package nxos;
##
## $Id: nxos.pm.in 3843 2018-07-19 01:21:29Z heas $
##
## rancid 3.8
## Copyright (c) 1997-2018 by Henry Kilmer and John Heasley
## All rights reserved.
##
## This code is derived from software contributed to and maintained by
## Henry Kilmer, John Heasley, Andrew Partan,
## Pete Whiting, Austin Schutz, and Andrew Fort.
##
## Redistribution and use in source and binary forms, with or without
## modification, are permitted provided that the following conditions
## are met:
## 1. Redistributions of source code must retain the above copyright
##    notice, this list of conditions and the following disclaimer.
## 2. Redistributions in binary form must reproduce the above copyright
##    notice, this list of conditions and the following disclaimer in the
##    documentation and/or other materials provided with the distribution.
## 3. Neither the name of RANCID nor the names of its
##    contributors may be used to endorse or promote products derived from
##    this software without specific prior written permission.
##
## THIS SOFTWARE IS PROVIDED BY Henry Kilmer, John Heasley AND CONTRIBUTORS
## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
## PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
## POSSIBILITY OF SUCH DAMAGE.
##
## It is the request of the authors, but not a condition of license, that
## parties packaging or redistributing RANCID NOT distribute altered versions
## of the etc/rancid.types.base file nor alter how this file is processed nor
## when in relation to etc/rancid.types.conf.  The goal of this is to help
## suppress our support costs.  If it becomes a problem, this could become a
## condition of license.
# 
#  The expect login scripts were based on Erik Sherk's gwtn, by permission.
# 
#  The original looking glass software was written by Ed Kern, provided by
#  permission and modified beyond recognition.
#
#  RANCID - Really Awesome New Cisco confIg Differ
#
#  nxos.pm - Cisco NX-OS rancid procedures

use 5.010;
use strict 'vars';
use warnings;
no warnings 'uninitialized';
require(Exporter);
our @ISA = qw(Exporter);

use rancid 3.8;

our $proc;
our $ios;
our $found_version;
our $found_env;
our $found_diag;
our $config_register;			# configuration register value

our $type;				# device model, from ShowVersion

our $C0;				# output formatting control
our $E0;
our $H0;
our $DO_SHOW_VLAN;

@ISA = qw(Exporter rancid main);
#XXX @Exporter::EXPORT = qw($VERSION @commandtable %commands @commands);

# load-time initialization
sub import {
    0;
}

# post-open(collection file) initialization
sub init {
    $proc = "";
    $found_version = 0;
    $found_env = 0;
    $found_diag = 0;
    $config_register = undef;		# configuration register value

    $type = undef;			# device model, from ShowVersion

    $C0 = 0;				# output formatting control
    $E0 = 0;
    $H0 = 0;
    $DO_SHOW_VLAN = 1;

    # add content lines and separators
    ProcessHistory("","","","!RANCID-CONTENT-TYPE: $devtype\n!\n");
    ProcessHistory("COMMENTS","keysort","B0","!\n");
    ProcessHistory("COMMENTS","keysort","D0","!\n");
    ProcessHistory("COMMENTS","keysort","F0","!\n");
    ProcessHistory("COMMENTS","keysort","G0","!\n");

    0;
}

# main loop of input of device output
sub inloop {
    my($INPUT, $OUTPUT) = @_;
    my($cmd, $rval);

TOP: while(<$INPUT>) {
	tr/\015//d;

	if (/[>#]\s?exit$/) {
	    print STDERR ("$host: found exit\n") if ($debug);
	    $clean_run = 1;
	    last;
	}
	if (/^Error:/) {
	    print STDOUT ("$host clogin error: $_");
	    print STDERR ("$host clogin error: $_") if ($debug);
	    $clean_run = 0;
	    last;
	}
	while (/#\s*($cmds_regexp)\s*$/) {
	    $cmd = $1;
	    if (!defined($prompt)) {
		$prompt = ($_ =~ /^([^#]+#)/)[0];
		$prompt =~ s/([][}{)(\\])/\\$1/g;
		print STDERR ("PROMPT MATCH: $prompt\n") if ($debug);
	    }
	    print STDERR ("HIT COMMAND:$_") if ($debug);
	    if (! defined($commands{$cmd})) {
		print STDERR "$host: found unexpected command - \"$cmd\"\n";
		$clean_run = 0;
		last TOP;
	    }
	    if (! defined(&{$commands{$cmd}})) {
		printf(STDERR "$host: undefined function - \"%s\"\n",
		       $commands{$cmd});
		$clean_run = 0;
		last TOP;
	    }
	    $rval = &{$commands{$cmd}}($INPUT, $OUTPUT, $cmd);
	    delete($commands{$cmd});
	    if ($rval == -1) {
		$clean_run = 0;
		print STDERR ("$host: $cmd failed: $rval\n") if ($debug);
		last TOP;
	    }
	    if (/[>#]\s?exit$/) {
		print STDERR ("$host: found exit\n") if ($debug);
		$clean_run = 1;
		last TOP;
	    }
	}
    }
}

# This routine parses "show version"
sub ShowVersion {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowVersion: $_" if ($debug);

    while (<$INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(-1) if (/\% Invalid command at /);
	return(-1) if (/No token match at /);	# 1000v
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);

	if (/^Cisco Nexus Operating System/) { $type = "NXOS";}
	if (/^Software$/) {
	    while (<$INPUT>) {
		tr/\015//d;
		last if (/^$prompt/);
		next if (/^\s*$cmd\s*$/);

		if (/^$/) {
		    goto EndSoftware;
		}
		/\s*([^:]*:)\s*(.*)$/ &&
		    ProcessHistory("COMMENTS","keysort","C1",
				   "!Software: $1 $2\n") && next;
	    }
	}
EndSoftware:
	if (/^Hardware$/) {
	    while (<$INPUT>) {
		tr/\015//d;
		last if (/^$prompt/);
		next if (/^\s*$cmd\s*$/);

		if (/^\s*cisco (Nexus[ \d]+) /) {
		    # cisco Nexus7000 C7010 (10 Slot) Chassis ("Supervisor module-1X")
		    # cisco Nexus7000 C7018 (18 Slot) Chassis ("Supervisor module-1X")
		    # cisco Nexus5548 Chassis ("O2 32X10GE/Modular Supervisor")
		    # cisco Nexus 4000 Chassis ("20x10GE/supervisor")
		    $proc = $1;
		}

		if (/^$/) {
		    goto EndHardware;
		}
		if (/^\s*(.*) CPU\s*with (\d*) kB(.*)$/) {
		    my($tmp) = int($2 / 1024);
		    ProcessHistory("COMMENTS","keysort","A2",
				   "!Hardware: $1 CPU with $tmp MB$3\n");
		    next;
		}
		if (/^\s*(.*)\s*with (\d*) kB(.*)$/) {
		    my($tmp) = int($2 / 1024);
		    ProcessHistory("COMMENTS","keysort","A2",
				   "!Hardware: $1with $tmp MB$3\n");
		    next;
		}
		/^\s*(.*)$/ &&
		    ProcessHistory("COMMENTS","keysort","A2",
				   "!Hardware: $1\n") && next;
	    }
	}
EndHardware:
	if (/^\s+(bootflash|slot0):\s+(\d+) kB(.*)$/) {
	    my($tmp) = int($2 / 1024);
	    ProcessHistory("COMMENTS","keysort","B1",
			   "!Memory: $1: $tmp MB$3\n");
	    next;
	}

    }

    print STDERR "TYPE = $type\n" if ($debug);
    # XXX $proc is never set
    ProcessHistory("COMMENTS","keysort","A1",
		   "!Chassis type: $proc - a $type router\n");
    ProcessHistory("COMMENTS","keysort","B0", "!\n");
    ProcessHistory("COMMENTS","keysort","C0", "!\n");
    ProcessHistory("COMMENTS","","", "!\n");
    return(0);
}

# This routine parses "show version build-info"
sub ShowVersionBuild {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowVersionBuild: $_" if ($debug);

    while (<$INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if (/\% Invalid command at /);
	return(1) if (/No token match at /);	# 1000v
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);

	/^Built By / && ProcessHistory("COMMENTS","","", "!Build: $_");
	/^On Date / && ProcessHistory("COMMENTS","","", "!Build: $_");
	/^From Tree / && ProcessHistory("COMMENTS","","", "!Build: $_");
	/^Base Tag / && ProcessHistory("COMMENTS","","", "!Build: $_");
	/^Release for / && ProcessHistory("COMMENTS","","", "!Build: $_");
    }
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine parses "show license *"
sub ShowLicense {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowLicense: $_" if ($debug);

    while (<$INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if (/\% Invalid command at /);
	return(-1) if (/No token match at /);	# 1000v
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);

	/^-+$/ && next;		# Skip lines of all dashes.
	s/ Grace .+$/ Grace/;	# Drop anything after Grace.
	ProcessHistory("COMMENTS","","", "!LIC: $_");
    }
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine parses "show system redundancy status"
sub ShowRedundancy {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowRedundancy: $_" if ($debug);

    while (<$INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if (/\% Invalid command at /);
	return(1) if (/.* command is not support/i);
	return(-1) if (/No token match at /);	# 1000v
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);

	s/ +$//;	# Drop trailing ' '
	ProcessHistory("COMMENTS","","","!Red: $_");
    }
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine parses "show environment"
sub ShowEnv {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowEnv: $_" if ($debug);

    while (<$INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	next if (/^\s*\^\s*$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if (/\% Invalid command at /);
	return(1) if (/ .* command is not support/i);
	return(1) if (/No token match at /);	# 1000v
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);

	s/ +$//;	# Drop trailing ' '
	next if (/Fan Zone Speed:/);
	if (/(control temperature|monitor temperature)/i) {
	    ProcessHistory("COMMENTS","","","!Env: $_");
	    while (<$INPUT>) {
		tr/\015//d;
		goto EndShowEnv if (/^$prompt/);
		if (/(.*\s+\d+\s+\d+\s+)(\d+)(.*$)/) {
		    $_ = sprintf("%s%-". length($2)."s%s\n", $1, "", $3);
		}
		ProcessHistory("COMMENTS","","","!Env: $_");
		last if (/^\s*$/);
	    }
	    next;
	}
	ProcessHistory("COMMENTS","","","!Env: $_");
    }
EndShowEnv:
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine parses "show environment temperature"
sub ShowEnvTemp {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowEnvTemp: $_" if ($debug);

    while (<$INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if (/No token match at /);	# 1000v
	return(1) if (/\% Invalid command at /);# 1000v has no support
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);

	s/ +$//;	# Drop trailing ' '

# Cut out CurTemp - drop the 2nd to last field.
#--------------------------------------------------------------------
#Module   Sensor        MajorThresh   MinorThres   CurTemp     Status
#                       (Celsius)     (Celsius)    (Celsius)         
#5        Outlet1 (s1)    125             125         33         Ok
#5        QEng1Sn1(s10)   115             105         39         Ok
	if (/(control temperature|monitor temperature)/i) {
	    ProcessHistory("COMMENTS","","","!Env: $_");
	    while (<$INPUT>) {
		if (/(.*\s+\d+\s+\d+\s+)(\d+)(.*$)/) {
		    $_ = sprintf("%s%-". length($2)."s%s\n", $1, "", $3);
		}
		ProcessHistory("COMMENTS","","","!Env: $_");
		last if (/^\s*$/);
	    }
	    next;
	} else {
	    next if (/INTAKE/);
	    next if (/ASIC/);
	    s/^(.+\s)(\S+\s+)(\S+\s*)$/$1$3/;
	    s/ +$//;	# Drop trailing ' '
	    ProcessHistory("COMMENTS","","","!Env: $_");
	}
    }
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine parses "show environment power"
sub ShowEnvPower {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowEnvPower: $_" if ($debug);

    while (<$INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	next if (/^\s*\^\s*$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if (/\% Invalid command at /);
	return(1) if (/No token match at /);	# 1000v
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);

# Filter Actual Output/Draw.
#Power                              Actual        Total
#Supply    Model                    Output     Capacity    Status
#                                 (Watts )     (Watts )
#-------  -------------------  -----------  -----------  --------------
#1        ------------                 0 W          0 W     Absent    
#3                                   749 W       5480 W     Ok        
#                                  Actual        Power      
#Module    Model                     Draw    Allocated    Status
#                                 (Watts )     (Watts )     
#-------  -------------------  -----------  -----------  --------------
#2        NURBURGRING                N/A          573 W    Powered-Up
#fan1                                N/A          720 W    Powered-Up
#
# ELSE
#
# nexus# sh environment power
# Power Supply:
# Voltage: 12 Volts
# Power                             Actual      Actual        Total
# Supply    Model                   Output      Input      Capacity    Status
#                                 (Watts )    (Watts )     (Watts )  
# -------  -------------------  ----------  ----------  ----------
# --------------
# 1        N9K-PAC-650W-B             65 W        76 W       649 W     Ok
# 2        N9K-PAC-650W-B             70 W        84 W       649 W     Ok
#  
#  
# Power Usage Summary:
# --------------------
# Power Supply redundancy mode (configured)                PS-Redundant
# Power Supply redundancy mode (operational)               PS-Redundant
#          
# Total Power Capacity (based on configured mode)             649.92 W
# Total Grid-A (first half of PS slots) Power Capacity        649.92 W 
# Total Grid-B (second half of PS slots) Power Capacity       649.92 W
# Total Power of all Inputs (cumulative)                     1299.84 W
# Total Power Output (actual draw)                            136.00 W
# Total Power Input (actual draw)                             161.00 W
# Total Power Allocated (budget)                                N/A
# Total Power Available for additional modules                  N/A  
# 
# nexus#
	if (/(.* +)(\d+ W +\d+ W)( +\d+ W.+)/) {
	    $_ = sprintf("%s%-". length($2)."s%s\n", $1, "", $3);
	} elsif (/(.* +)(\d+(\.\d+)? W)( +\d+(\.\d+)? W.+)/) {
	    $_ = sprintf("%s%-". length($2)."s%s\n", $1, "", $4);
	}

	/actual draw/ && next;	# Drop changing total power output.

	s/ +$//;	# Drop trailing ' '
	ProcessHistory("COMMENTS","","","!Env: $_");
    }
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine parses "show boot"
sub ShowBoot {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowBoot: $_" if ($debug);

    while (<$INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /^\s*\^\s*$/;
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if /Ambiguous command/i;
	return(-1) if (/\% Invalid command at /);
	return(-1) if (/No token match at /);	# 1000v
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);

	s/ variable = / = /;
	ProcessHistory("COMMENTS","","","!Variable: $_");
    }
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine parses "dir /all ((disk|slot)N|bootflash|nvram):"
sub DirSlotN {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In DirSlotN: $_" if ($debug);
    my($free);

    my($dev) = (/\s([^\s]+):/);

    while (<$INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /^\s*\^\s*$/;
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if (/\% Invalid command at /);
	return(1) if /(No such device|Error Sending Request)/i;
	return(1) if /No such file or directory/;
	return(1) if /No space information available/;
	return(1) if / is either not present or not formatted/;
	return(-1) if /\%Error calling/;
	return(-1) if /(: device being squeezed|ATA_Status time out)/i; # busy
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);
	return(1) if /(Open device \S+ failed|Error opening \S+:)/;

	# Drop ee.log
	/\s+ee\.log(?:\..*)?$/ && next;

	# Drop bootvar_debug log files
	/\s+bootvar_debug\./ && next;

	# Drop accounting.log
	/\s+accounting\.log$/ && next;

	# Drop vtp_debug.log and vtp_debug_old.log CDETS bug CSCuy87611
	/\s+vtp_debug(_old)?\.log$/ && next;

	next if (/BufferMonitor-1HourData/);

	if (/( debug_logs| log)\/$/) {
	    # change
	    #         8192    Jan 08 14:05:05 2015  log/
	    # to
	    #                                       log/
	    if (/(\s*\d+\s+)(\S+ \d+\s+\d+:\d+:\d+ \d+)(.*)/) {
		my($a, $dt, $rem) = ($1, $2, $3);
		my($dtl) = length($dt);
		my($fmt) = "%s%-". $dtl ."s%s\n";
		$_ = sprintf($fmt, $a, "", $rem);
	    }
	}

	if (/^\s*(\d+) bytes free/i) {
	    $free = $1;
	    next;
	} elsif (/^\s*(\d+) bytes used/i) {
	    next;
	} elsif (/^\s*(\d+) bytes total/i) {
	    $_ = diskszsummary($1, $free, undef) . "\n";
	}

	ProcessHistory("COMMENTS","","","!Flash: $dev: $_");
    }
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine parses "show module".
sub ShowModule {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowModule: $_" if ($debug);

    while (<$INPUT>) {
	tr/\015//d;
	return if (/^\s*\^$/);
	last if (/online diag status/i);
	last if (/^$prompt/);
	next if (/^\s*$cmd\s*$/);
	return(1) if (/\% Invalid command at /);
	return(1) if (/\% Invalid number at /);
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);

	s/(.*) \*$/$1/;	# Drop a trailing '*'
	/^\* this terminal session/ && next;
	s/ +$//;	# Drop trailing ' '
	ProcessHistory("COMMENTS","","","!Mod: $_");
    }
    ProcessHistory("COMMENTS","","","!\n");

    return(0);
}

# This routine parses "show interface transceiver".
sub ShowIntTransceiver {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowtTransceiver: $_" if ($debug);

    while (<$INPUT>) {
	tr/\015//d;
	return if (/^\s*\^$/);
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if (/\% Invalid command at /);
	return(-1) if (/No token match at /);	# 1000v
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);

	ProcessHistory("COMMENTS","","","!$_");
    }
    ProcessHistory("COMMENTS","","","!\n");

    return(0);
}

# This routine parses "show inventory".
sub ShowInventory {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowInventory: $_" if ($debug);

    while (<$INPUT>) {
	tr/\015//d;
	return if (/^\s*\^$/);
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if (/\% Invalid command at /);
	return(-1) if (/No token match at /);	# 1000v
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);

	if (/^(NAME: "[^"]*",)\s+(DESCR: "[^"]+")/) {
	    ProcessHistory("COMMENTS","","", sprintf("!%-30s %s\n", $1, $2));
	    next;
	}
	# split PID/VID/SN line
	if (/^PID: (\S*)\s*,\s+VID: (\S*)\s*,\s+SN: (\S*)\s*$/) {
	    my($entries) = "";
	    $entries .= "!PID: $1\n" if ($1);
	    $entries .= "!VID: $2\n" if ($2);
	    $entries .= "!SN: $3\n" if ($3);
	    ProcessHistory("COMMENTS","","", "$entries");
	    next;
	}
	# split broken PID/VID/SN lines.
	if (/^PID: (\S*)\s*,\s+VID: (\S*)\s*$/) {
	    my($entries) = "";
	    $entries .= "!PID: $1\n" if ($1);
	    $entries .= "!VID: $2\n" if ($2);
	    <$INPUT>;
	    tr/\015//d;
	    /^\s*,\s+SN: (\S*)\s*$/;
	    $entries .= "!SN: $1\n" if ($1);
	    ProcessHistory("COMMENTS","","", "$entries");
	    next;
	}
	ProcessHistory("COMMENTS","","","!$_");
    }
    ProcessHistory("COMMENTS","","","!\n");

    return(0);
}

# This routine parses "show vtp status"
sub ShowVTP {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowVTP: $_" if ($debug);

    while (<$INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /^\s*\^\s*$/;
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if (/\% Invalid command at /);
	return(1) if (/No token match at /);	# 1000v
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);
	next if (/^Configuration last modified by/);
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	# Nexus 5k and 1000v do not support vtp
	if (!/^VTP Operating Mode\s+:\s+(Transparent|Server)/) {
	    $DO_SHOW_VLAN = 0;
	}
	ProcessHistory("COMMENTS","","","!VTP: $_");
    }
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine parses "show vlan"
sub ShowVLAN {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowVLAN: $_" if ($debug);

    ($_ = <$INPUT>, return(1)) if (!$DO_SHOW_VLAN);

    while (<$INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /^\s*\^\s*$/;
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(-1) if (/\% Invalid command at /);
	return(-1) if (/No token match at /);	# 1000v
	return(1) if /Ambiguous command/i;
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);
	# the pager can not be disabled per-session on the PIX
	if (/^(<-+ More -+>)/) {
	    my($len) = length($1);
	    s/^$1\s{$len}//;
	}

	ProcessHistory("COMMENTS","","","!VLAN: $_");
    }
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine parses "show debug"
sub ShowDebug {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowDebug: $_" if ($debug);
    my($lines) = 0;

    while (<$INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(-1) if (/\% Invalid command at /);
	return(-1) if (/No token match at /);	# 1000v
	return(-1) if (/command authorization failed/i);
	return(-1) if (/could not retrieve info/i);
	# XXX return(-1) if (/\% Permission denied/);
	# NX 5000 bug? "show debug" generates
	# "Permission denied" when using command authorization. -Per-Olof Olsson
	next if (/\% Permission denied/);

	/^No matching debug flags set$/ && next;
	/^No debug flags set$/ && next;
	ProcessHistory("COMMENTS","","","!DEBUG: $_");
	$lines++;
    }
    if ($lines) {
	ProcessHistory("COMMENTS","","","!\n");
    }
    return(0);
}

# This routine parses "show cores"
sub ShowCores {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowCores: $_" if ($debug);

    while (<$INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if (/\% Invalid command at /);
	return(1) if (/No token match at /);	# 1000v
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);

	ProcessHistory("COMMENTS","","","!CORES: $_");
    }
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine parses "show fex" and "show module fex"
sub ShowFex {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowFex: $_" if ($debug);

    while (<$INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if (/^\s*\^\s*$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if (/\% Invalid command at /);
	return(1) if (/% Invalid number at /);	# 1000v
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);

	ProcessHistory("COMMENTS","","","!FEX: $_");
    }
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine parses "show processes log"
sub ShowProcLog {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In ShowProcLog: $_" if ($debug);

    while (<$INPUT>) {
	tr/\015//d;
	last if (/^$prompt/);
	next if (/^(\s*|\s*$cmd\s*)$/);
	return(1) if /Line has invalid autocommand /;
	return(1) if /(Invalid input detected|Type help or )/;
	return(1) if (/\% Invalid command at /);
	return(-1) if (/No token match at /);	# 1000v
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);

	ProcessHistory("COMMENTS","","","!PROC_LOGS: $_");
    }
    ProcessHistory("COMMENTS","","","!\n");
    return(0);
}

# This routine processes a "write term"
sub WriteTerm {
    my($INPUT, $OUTPUT, $cmd) = @_;
    print STDERR "    In WriteTerm: $_" if ($debug);
    my($lineauto,$comment,$linecnt) = (0,0,0);

    while (<$INPUT>) {
REPEAT:	tr/\015//d;
	last if (/^$prompt/);
	return(1) if /Line has invalid autocommand /;
	return(1) if (/(Invalid input detected|Type help or )/i);
	return(-1) if (/\% Invalid command at /);
	return(-1) if (/No token match at /);	# 1000v
	return(-1) if (/\% Permission denied/);
	return(-1) if (/command authorization failed/i);
	return(0) if ($found_end);		# Only do this routine once

	$linecnt++;
	$lineauto = 0 if (/^[^ ]/);

	# Dog gone Cool matches to process the rest of the config
	/^!Command: show running-config/ && next; # kill this junk
	/^!Time: / && next; # kill this junk

	# Sort username and delete passwords.
	if (/^username (\S+) password (\d) (\S+)(\s.*)$/) {
	    if ($filter_pwds >= 2) {
		ProcessHistory("USER","keysort","$1",
			       "!username $1 password <removed>$4\n");
	    } elsif ($filter_pwds >= 1 && $2 ne "5") {
		ProcessHistory("USER","keysort","$1",
			       "!username $1 password <removed>$4\n");
	    } else {
		ProcessHistory("USER","keysort","$1","$_");
	    }
	    next;
	}
	# Sort any other username info.
	/^username (\S+) .*$/ &&
	    ProcessHistory("USER","keysort","$1","$_") && next;
	if (/^\s*(.*?neighbor \S*) password / && $filter_pwds >= 1) {
	    ProcessHistory("","","","! $1 password <removed>\n");
	    next;
	}
	# sort ipv{4,6} access-lists
	if ($aclfilterseq && /^ip(v4|v6)? access-list (\S+)\s*$/) {
	    my($nlri, $key) = ($1, $2);
	    my($seq, $cmd);
	    if (length($nlri) eq 0) {
		$nlri = "ipv4";	# assume ip will become ipv4 at some point
	    }
	    ProcessHistory("ACL $nlri $key","","","$_");
	    while (<$INPUT>) {
		tr/\015//d;
		goto REPEAT if (/^$prompt/ || !/^\s/);
		if (/^\s*!/) {
		    ProcessHistory("ACL $nlri $key !", "", "", "$_");
		    next;
		}
		# ip access-list copp-system-acl-bgp
		#   10 permit tcp any gt 1024 any eq bgp
		#   20 permit tcp any eq bgp any gt 1024
		# ipv6 access-list copp-system-acl-bgp6
		#   10 permit tcp any gt 1024 any eq bgp
		#   20 permit tcp any eq bgp any gt 1024
		# XXX this may need more format handling; going off email with
		#     a user.
		my($seq, $cmd, $resid) = ($_ =~ /^\s*(\d+) (\w+) (.+)/);
		if ($cmd =~ /(permit|deny)/) {
		    my($ip);
		    my(@w) = ($resid =~ /(\S+) (\S+) (\S+\s)?(.+)/);
		    for (my($i) = 0; $i < $#w; $i++) {
			if ($w[$i] eq "any") {
			    if ($nlri eq "ipv4") {
				$ip = "255.255.255.255/32";
			    } else {
				$ip = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128";
			    }
			    last;
			} elsif ($w[$i] =~ /^[:0-9]/ ||
				 $2[$i] =~ /^[a-fA-F]{1,4}:/) {
			    $ip = $w[$i];
			    $ip =~ s/\s+$//;		# trim trailing WS
			    last;
			}
		    }
		    ProcessHistory("ACL $nlri $key $cmd", "$aclsort", "$ip",
				   " $cmd $resid\n");
		} else {
		    ProcessHistory("ACL $nlri $key $cmd", "", "",
				   " $cmd $resid\n");
		}
	    }
	}

	# Why was this commented out? It shows up in the raw text...
	if (/^(snmp-server community) (\S+)/) {
	    if ($filter_commstr) {
		ProcessHistory("SNMPSERVERCOMM","keysort","$_",
			       "!$1 <removed>$'") && next;
	    } else {
		ProcessHistory("SNMPSERVERCOMM","keysort","$_","$_") && next;
	    }
	}
	# order/prune snmp-server host statements
	# we only prune lines of the form
	# snmp-server host a.b.c.d <community>
	if (/^snmp-server host (\d+\.\d+\.\d+\.\d+) /) {
	    if ($filter_commstr) {
		my($ip) = $1;
		my($line) = "snmp-server host $ip";
		my(@tokens) = split(' ', $');
		my($token);
		while ($token = shift(@tokens)) {
		    if ($token eq 'version') {
			$line .= " " . join(' ', ($token, shift(@tokens)));
			if ($token eq '3') {
			    $line .= " " . join(' ', ($token, shift(@tokens)));
			}
		    } elsif ($token eq 'vrf') {
			$line .= " " . join(' ', ($token, shift(@tokens)));
		    } elsif ($token =~ /^(informs?|traps?|(no)?auth)$/) {
			$line .= " " . $token;
		    } else {
			$line = "!$line " . join(' ', ("<removed>",
						 join(' ',@tokens)));
			last;
		    }
		}
		ProcessHistory("SNMPSERVERHOST","ipsort","$ip","$line\n");
	    } else {
		ProcessHistory("SNMPSERVERHOST","ipsort","$1","$_");
	    }
	    next;
	}
	# Sort snmp user and delete passwords.
	if (/^snmp-server user (\S+) (\S+) auth md5 (\S+) priv (\S+) localizedkey$/) {
	    if ($filter_pwds >= 2) {
		ProcessHistory("SNMP-USER","keysort","$1",
			       "!snmp-server user $1 $2 auth md5 <removed> priv <removed> localizedkey\n");
	    } else {
		ProcessHistory("SNMP-USER","keysort","$1","$_");
	    }
	    next;
	}
	# Sort any other snmp user info.
	/^snmp-server user (\S+) .*$/ &&
	    ProcessHistory("SNMP-USER","keysort","$1","$_") && next;
	# Delete bgp passwords.
	if (/^(\s*)password (\d) (\S+)(\s.*)?$/) {
	    if ($filter_pwds >= 2) {
		ProcessHistory("","","","!$1password <removed>$4");
	    } elsif ($filter_pwds >= 1 && $2 ne "5") {
		ProcessHistory("","","","!$1password <removed>$4");
	    } else {
		ProcessHistory("","","","$_");
	    }
	    next;
	}

	# prune tacacs/radius server keys:
	# tacacs-server host 196.23.0.13 key 7 "xxxxxxx" port 50 timeout 10
	if (/^((tacacs|radius)-server.*?\bkey\b.*?) ".*?"(.*)/
	    && $filter_pwds >= 1) {
	    ProcessHistory("","","","!$1 <removed>$3\n"); next;
	}

	# order cli alias names
	/^cli alias name (\S+) .*$/ &&
	    ProcessHistory("CLI-ALIAS","keysort","$1","$_") && next;
	# order snmp-server enable trap statements
	/^snmp-server enable traps (.*)$/ &&
	    ProcessHistory("SNMP-TRAPS","keysort","$1","$_") && next;

	# catch anything that wasnt matched above.
	ProcessHistory("","","","$_");
	# end of config.  the ": " game is for the PIX
	if (/^(: +)?end$/) {
	    $found_end = 1;
	    return(0);
	}
    }
    # The ContentEngine lacks a definitive "end of config" marker.  If we
    # know that it is NXOS and we have seen at least 5 lines
    # of write term output, we can be reasonably sure that we got the config.
    if (($type eq "NXOS") && $linecnt > 5) {
	$found_end = 1;
	return(0);
    }

    return(0);
}

1;
