1package ios;
2##
3## @PACKAGE@ @VERSION@
4@copyright@
5#
6#  RANCID - Really Awesome New Cisco confIg Differ
7#
8#  ios.pm - Cisco IOS rancid procedures
9
10use 5.010;
11use strict 'vars';
12use warnings;
13no warnings 'uninitialized';
14require(Exporter);
15our @ISA = qw(Exporter);
16
17use rancid @VERSION@;
18
19our $proc;
20our $ios;
21our $found_version;
22our $found_env;
23our $found_diag;
24our $found_inventory;
25our $config_register;			# configuration register value
26
27our %hwbuf;				# defined in ShowContCbus
28our %hwmemc;				# defined in ShowContCbus
29our %hwmemd;				# defined in ShowContCbus
30our %hwucode;				# defined in ShowContCbus
31our $supbootdisk;			# skip sup-bootflash if sup-bootdisk
32					# worked
33our $type;				# device model, from ShowVersion
34our %ucode;				# defined in ShowContCbus
35
36our $ssp;				# SSP/SSE info, from ShowVersion
37our $sspmem;				# SSP/SSE info, from ShowVersion
38
39our $C0;				# output formatting control
40our $E0;
41our $H0;
42our $I0;
43our $DO_SHOW_VLAN;
44
45our $vss_show_module;			 # Use "show module switch" on 6k VSS systems
46
47@ISA = qw(Exporter rancid main);
48#XXX @Exporter::EXPORT = qw($VERSION @commandtable %commands @commands);
49
50# load-time initialization
51sub import {
52    0;
53}
54
55# post-open(collection file) initialization
56sub init {
57    $proc = "";
58    $ios = "IOS";
59    $found_version = 0;
60    $found_env = 0;
61    $found_diag = 0;
62    $found_inventory = 0;
63    $config_register = undef;		# configuration register value
64
65    $supbootdisk = 0;			# skip sup-bootflash if sup-bootdisk
66					# worked
67    $type = undef;			# device model, from ShowVersion
68
69    $ssp = 0;				# SSP/SSE info, from ShowVersion
70    $sspmem = undef;			# SSP/SSE info, from ShowVersion
71
72    $C0 = 0;				# output formatting control
73    $E0 = 0;
74    $H0 = 0;
75    $I0 = 0;
76    $DO_SHOW_VLAN = 0;
77
78    $vss_show_module = 0;		# Use "show module switch" on 6k VSS systems
79    # add content lines and separators
80    ProcessHistory("","","","!RANCID-CONTENT-TYPE: $devtype\n!\n");
81    ProcessHistory("COMMENTS","keysort","B0","!\n");
82    ProcessHistory("COMMENTS","keysort","D0","!\n");
83    ProcessHistory("COMMENTS","keysort","F0","!\n");
84    ProcessHistory("COMMENTS","keysort","G0","!\n");
85
86    0;
87}
88
89# main loop of input of device output
90sub inloop {
91    my($INPUT, $OUTPUT) = @_;
92    my($cmd, $rval);
93
94TOP: while(<$INPUT>) {
95	tr/\015//d;
96	if (/[>#]\s?exit$/) {
97	    $clean_run = 1;
98	    last;
99	}
100	if (/^Error:/) {
101	    print STDOUT ("$host clogin error: $_");
102	    print STDERR ("$host clogin error: $_") if ($debug);
103	    $clean_run = 0;
104	    last;
105	}
106	while (/[>#]\s*($cmds_regexp)\s*$/) {
107	    $cmd = $1;
108	    if (!defined($prompt)) {
109		$prompt = ($_ =~ /^([^#>]+[#>])/)[0];
110		$prompt =~ s/([][}{)(+\\])/\\$1/g;
111		print STDERR ("PROMPT MATCH: $prompt\n") if ($debug);
112	    }
113	    print STDERR ("HIT COMMAND:$_") if ($debug);
114	    if (! defined($commands{$cmd})) {
115		print STDERR "$host: found unexpected command - \"$cmd\"\n";
116		$clean_run = 0;
117		last TOP;
118	    }
119	    if (! defined(&{$commands{$cmd}})) {
120		printf(STDERR "$host: undefined function - \"%s\"\n",
121		       $commands{$cmd});
122		$clean_run = 0;
123		last TOP;
124	    }
125	    $rval = &{$commands{$cmd}}($INPUT, $OUTPUT, $cmd);
126	    delete($commands{$cmd});
127	    if ($rval == -1) {
128		$clean_run = 0;
129		last TOP;
130	    }
131	}
132    }
133}
134
135# This routine parses "show version"
136sub ShowVersion {
137    my($INPUT, $OUTPUT, $cmd) = @_;
138    my($slave, $slaveslot);
139    print STDERR "    In ShowVersion: $_" if ($debug);
140
141    while (<$INPUT>) {
142	tr/\015//d;
143	if (/^$prompt/) { $found_version = 1; last};
144	next if (/^(\s*|\s*$cmd\s*)$/);
145	next if (/^\s+\^$/);
146	next if (/^Load for five/);
147	next if (/^Time source is/);
148	return(1) if (/Line has invalid autocommand /);
149	return(1) if (/(invalid (input|command) detected|type help or )/i);
150	return(0) if ($found_version);		# Only do this routine once
151	return(-1) if (/(?:%|command)? authorization failed/i);
152	# the pager can not be disabled per-session on the PIX
153	if (/^(<-+ More -+>)/) {
154	    my($len) = length($1);
155	    s/^$1\s{$len}//;
156	}
157
158	if (/^Slave in slot (\d+) is running/) {
159	    $slave = " Slave:";
160	    $slaveslot = ", slot $1";
161	    next;
162	}
163	if (/cisco ios.*(IOS-)?XE/i) { $ios = "XE"; }
164	if (/^Application and Content Networking .*Software/) { $type = "CE"; }
165	# treat the ACE like the Content Engines for matching endofconfig
166	if (/^Cisco Application Control Software/) { $type = "CE"; }
167	if (/^Cisco Storage Area Networking Operating System/) { $type = "SAN";}
168	if (/^Cisco Nexus Operating System/) { $type = "NXOS";}
169	/^Application and Content Networking Software Release /i &&
170	    ProcessHistory("COMMENTS","keysort","F1", "!Image: $_") && next;
171	/^Cisco Secure PIX /i &&
172	    ProcessHistory("COMMENTS","keysort","F1", "!Image: $_") && next;
173	# ASA "time-based licenses" - eg: bot-net
174	/^This (PIX|platform) has a time-based license that will expire in\s+(\d{2,})\s+day.*$/ &&
175	    ProcessHistory("COMMENTS","keysort","D1",
176			   "!This $1 has a time-based license\n") && next;
177	# PIX 6 fail-over license, as in "This PIX has an Unrestricted (UR)
178	# license."  PIX 7 as "his platform has ..."
179	/^This (PIX|platform) has an?\s+(.*)$/ &&
180	    ProcessHistory("COMMENTS","keysort","D1", "!$_") && next;
181	/^(Cisco )?IOS .* Software,? \(([A-Za-z0-9_-]*)\), .*Version\s+(.*)$/ &&
182	    ProcessHistory("COMMENTS","keysort","F1",
183		"!Image:$slave Software: $2, $3\n") && next;
184	/^([A-Za-z-0-9_]*) Synced to mainline version: (.*)$/ &&
185	    ProcessHistory("COMMENTS","keysort","F2",
186		"!Image:$slave $1 Synced to mainline version: $2\n") && next;
187	/^Compiled (.*)$/ &&
188	    ProcessHistory("COMMENTS","keysort","F3",
189		"!Image:$slave Compiled: $1\n") && next;
190	/^ROM: (IOS \S+ )?(System )?Bootstrap.*(Version.*)$/ &&
191	    ProcessHistory("COMMENTS","keysort","G1",
192		"!ROM Bootstrap: $3\n") && next;
193	if (/^Hardware:\s+(.*), (.* RAM), CPU (.*)$/) {
194	    ProcessHistory("COMMENTS","keysort","A1",
195		"!Chassis type: $1 - a PIX\n");
196	    ProcessHistory("COMMENTS","keysort","A2",
197		"!CPU: $3\n");
198	    ProcessHistory("COMMENTS","keysort","B1", "!Memory: $2\n");
199	}
200	/^serial number:\s+(.*)$/i &&
201	    ProcessHistory("COMMENTS","keysort","C1", "!Serial Number: $1\n") &&
202	    next;
203	# More PIX stuff
204	/^Encryption hardware device\s+:\s+(.*)/ &&
205	    ProcessHistory("COMMENTS","keysort","A3", "!Encryption: $1\n") &&
206	    next;
207	/^running activation key\s*:\s+(.*)/i &&
208	    ProcessHistory("COMMENTS","keysort","D2", "!Key: $1\n") &&
209	    next;
210	# Flash on the PIX or FWSM (FireWall Switch Module)
211	/^Flash(\s+\S+)+ \@ 0x\S+,\s+(\S+)/ &&
212	    ProcessHistory("COMMENTS","keysort","B2", "!Memory: Flash $2\n") &&
213	    next;
214	# 3750 switch stacks
215	next if (/^Model revision number/ && $type eq "3750");
216	next if (/^Motherboard/ && $type eq "3750");
217	next if (/^Power supply/ && $type eq "3750");
218	/^Model number                    : (.*)$/ && $type eq "3750" &&
219	ProcessHistory("COMMENTS","keysort","C2", "!Model number:  $1\n") &&
220 	    next;
221	/^System serial number            : (.*)$/ && $type eq "3750" &&
222	ProcessHistory("COMMENTS","keysort","C2", "!Serial number: $1\n") &&
223 	    next;
224	# CatOS 3500xl stuff
225	/^system serial number\s*:\s+(.*)$/i &&
226	    ProcessHistory("COMMENTS","keysort","C1", "!Serial Number: $1\n") &&
227	    next;
228	/^Model / &&
229	    ProcessHistory("COMMENTS","keysort","C2", "!$_") && next;
230	/^Motherboard / &&
231	    ProcessHistory("COMMENTS","keysort","C3", "!$_") && next;
232	/^Power supply / &&
233	    ProcessHistory("COMMENTS","keysort","C4", "!$_") && next;
234
235	/^Activation Key:\s+(.*)$/ &&
236	    ProcessHistory("COMMENTS","keysort","C2", "!$_") && next;
237	/^ROM: \d+ Bootstrap .*(Version.*)$/ &&
238	    ProcessHistory("COMMENTS","keysort","G2",
239		"!ROM Image: Bootstrap $1\n!\n") && next;
240	/^ROM: .*(Version.*)$/ &&
241	    ProcessHistory("COMMENTS","keysort","G3","!ROM Image: $1\n") && next;
242	/^BOOTFLASH: .*(Version.*)$/ &&
243	    ProcessHistory("COMMENTS","keysort","G4","!BOOTFLASH: $1\n") && next;
244	/^BOOTLDR: .*(Version.*)$/ &&
245	    ProcessHistory("COMMENTS","keysort","G4","!BOOTLDR: $1\n") && next;
246	/^System image file is "([^\"]*)", booted via (\S*)/ &&
247# removed the booted source due to
248# CSCdk28131: cycling info in 'sh ver'
249#	ProcessHistory("COMMENTS","keysort","F4","!Image: booted via $2, $1\n") &&
250	    ProcessHistory("COMMENTS","keysort","F4","!Image: booted $1\n") &&
251	    next;
252	/^System image file is "([^\"]*)"$/ &&
253	    ProcessHistory("COMMENTS","keysort","F5","!Image: $1\n") && next;
254	# 2800 at least don't have a processor line
255	if ((/(\S+(?:\sseries)?)\s+(?:\(([^)]+)\)\s+processor|\(revision[^)]+\)).*\s+with (\S+k) bytes/i) ||
256	    (/isco\s+(\S+)\s+\(.+\)\s+with (\S+[kK]) bytes/)) {
257	    $proc = $1;
258	    my($cpu) = $2;
259	    my($mem) = $3;
260
261	    # the next line ought to be the more specific cpu info, grab it.
262	    # yet, some boards/IOS vers have a processor ID line between these
263	    # two.  grrr.  make sure we dont grab the "software" junk that
264	    # follows these lines by looking for "CPU at " or the 2600s
265	    # "processor: " unique string.  there are undoubtedly many other
266	    # incantations.  for a slave, we dont get this info, its just a
267	    # blank line.
268	    $_ = <$INPUT>;
269	    if (/processor board id/i) {
270		my($sn);
271
272		if (/processor board id (\S+)/i) {
273		    $sn = $1;
274		    $sn =~ s/,$//;
275		    ProcessHistory("COMMENTS","keysort","D9",
276				   "!Processor ID: $sn\n");
277		}
278		$_ = <$INPUT>;
279	    }
280	    # for 6500 sup-2t
281	    if ($cpu =~ /M8572/) {
282		if (defined($cpu)) {
283		    s/^ CPU://;
284		    ProcessHistory("COMMENTS","keysort","A3", "!CPU: $cpu, $_");
285		}
286LINE:		while (<$INPUT>) {
287		    last LINE if /^\s*$/;
288		    ProcessHistory("COMMENTS","keysort","A3", "!CPU: $_");
289		    last LINE if /^\s*I-cache/;
290		}
291		undef ($cpu);
292	    }
293	    $_ = "" if (! /(cpu at |processor: |$cpu processor,)/i);
294	    tr/\015//d;
295	    s/implementation/impl/i;
296	    if ($_ !~ /^\s*$/) {
297		chomp;
298		s/^/, /;
299	    }
300
301	    if ($proc eq "CSC") {
302		$type = "AGS";
303	    } elsif ($proc eq "CSC4") {
304		$type = "AGS+";
305	    } elsif ($proc =~ /1900/) {
306		$type = "1900";
307	    } elsif ($proc =~ /2811/) {
308		$type = "2800";
309            } elsif ($proc =~ /^ME-3400/) {
310                $type = "ME3400";
311            } elsif ($proc =~ /^ME-C37/) {
312                $type = "ME3700";
313            } elsif ($proc =~ /^ME-C65/) {
314                $type = "ME6500";
315	    } elsif ($proc =~ /C3750/) {
316		$type = "3750";
317	    } elsif ($proc =~ /^(AS)?25[12][12]/) {
318		$type = "2500";
319	    } elsif ($proc =~ /261[01]/ || $proc =~ /262[01]/) {
320		$type = "2600";
321	    } elsif ($proc =~ /WS-C29/) {
322		$type = "2900XL";
323	    } elsif ($proc =~ /WS-C355/) {
324		$type = "3550";
325	    } elsif ($proc =~ /WS-C35/) {
326		$type = "3500XL";
327	    } elsif ($proc =~ /^36[0246][0-9]/) {
328		$type = "3600";
329	    } elsif ($proc =~ /^37/) {
330		$type = "3700";
331            } elsif ($proc =~ /WS-C375/) {
332                $type = "3750";
333	    } elsif ($proc =~ /^38/) {
334		$type = "3800";
335	    } elsif ($proc =~ /WS-C45/) {
336		$type = "4500";
337	    } elsif ($proc =~ /^AS5300/) {
338		$type = "AS5300";
339	    } elsif ($proc =~ /^AS5350/) {
340		$type = "AS5350";
341	    } elsif ($proc =~ /^AS5400/) {
342		$type = "AS5400";
343	    } elsif ($proc =~ /^ASR920/) {
344		$type = "ASR920";
345	    } elsif ($proc =~ /6000/) {
346		$type = "6000";
347	    } elsif ($proc eq "WK-C65") {
348		$type = "6500";
349            } elsif ($proc =~ /WS-C6509/) {
350                $type = "6500";
351	    } elsif ($proc eq "RP") {
352		$type = "7000";
353	    } elsif ($proc eq "RP1") {
354		$type = "7000";
355	    } elsif ($proc =~ /720[246]/) {
356		$type = "7200";
357	    } elsif ($proc =~ /^73/) {
358		$type = "7300";
359	    } elsif ($proc eq "RSP7000") {
360		$type = "7500";
361	    } elsif ($proc =~ /RSP\d/) {
362		$type = "7500";
363	    } elsif ($proc =~ /OSR-76/) {
364		$type = "7600";
365	    } elsif ($proc =~ /CISCO76/) {
366		$type = "7600";
367	    } elsif ($proc =~ /1200[48]\/(GRP|PRP)/ || $proc =~ /1201[26]\/(GRP|PRP)/) {
368		$type = "12000";
369	    } elsif ($proc =~ /1201[26]-8R\/(GRP|PRP)/) {
370		$type = "12000";
371	    } elsif ($proc =~ /1240[48]\/(GRP|PRP)/ || $proc =~ /1241[06]\/(GRP|PRP)/) {
372		$type = "12400";
373	    } elsif ($proc =~ /AIR-L?AP1[12][1234][[1234]/) {
374		$type="Aironet";
375	    } else {
376		$type = $proc;
377	    }
378
379	    print STDERR "TYPE = $type\n" if ($debug);
380	    ProcessHistory("COMMENTS","keysort","A1",
381		"!Chassis type:$slave $proc\n");
382	    ProcessHistory("COMMENTS","keysort","B1",
383		"!Memory:$slave main $mem\n");
384	    if (defined($cpu)) {
385		ProcessHistory("COMMENTS","keysort","A3",
386			       "!CPU:$slave $cpu$_$slaveslot\n");
387	    }
388	    next;
389	}
390	if (/(\S+) Silicon\s*Switch Processor/) {
391	    if (!$C0) {
392		$C0 = 1; ProcessHistory("COMMENTS","keysort","C0","!\n");
393	    }
394	    ProcessHistory("COMMENTS","keysort","C2","!SSP: $1\n");
395	    $ssp = 1;
396	    $sspmem = $1;
397	    next;
398	}
399	/^(\d+[kK]) bytes of multibus/ &&
400	    ProcessHistory("COMMENTS","keysort","B2",
401		"!Memory: multibus $1\n") && next;
402	/^(\d+[kK]) bytes of (non-volatile|NVRAM)/ &&
403	    ProcessHistory("COMMENTS","keysort","B3",
404		"!Memory: nvram $1\n") && next;
405	/^(\d+[kK]) bytes of (flash memory|processor board System flash|ATA CompactFlash)/ &&
406	    ProcessHistory("COMMENTS","keysort","B5","!Memory: flash $1\n") &&
407	    next;
408	/^(\d+[kK]) bytes of .*flash partition/ &&
409	    ProcessHistory("COMMENTS","keysort","B6",
410		"!Memory: flash partition $1\n") && next;
411	/^(\d+[kK]) bytes of Flash internal/ &&
412	    ProcessHistory("COMMENTS","keysort","B4",
413		"!Memory: bootflash $1\n") && next;
414	if (/^(\d+[kK]) bytes of (Flash|ATA)?.*PCMCIA .*(slot|disk) ?(\d)/i) {
415	    ProcessHistory("COMMENTS","keysort","B7",
416		"!Memory: pcmcia $2 $3$4 $1\n");
417	    next;
418	}
419	if (/^(\d+[kK]) bytes of (slot|disk)(\d)/i) {
420	    ProcessHistory("COMMENTS","keysort","B7",
421		"!Memory: pcmcia $2$3 $1\n");
422	    next;
423	}
424	if (/^(\d+[kK]) bytes of physical memory/i) {
425	    ProcessHistory("COMMENTS","keysort","B1", "!Memory: physical $1\n");
426	    next;
427	}
428	if (/^WARNING/) {
429	    if (!$I0) {
430		$I0 = 1;
431		ProcessHistory("COMMENTS","keysort","I0","!\n");
432	    }
433	    ProcessHistory("COMMENTS","keysort","I1","! $_");
434	}
435	if (/^Configuration register is (.*)$/) {
436	    $config_register = $1;
437	    next;
438	}
439	if (/^Configuration register on node \S+ is (.*)$/) {
440	    $config_register = $1 if (length($config_register) < 1);
441	    next;
442	}
443    }
444    return(0);
445}
446
447# This routine parses "show activation-key" on ASA
448sub ShowActivationKey {
449    my($INPUT, $OUTPUT, $cmd) = @_;
450    print STDERR "    In ShowMTU: $_" if ($debug);
451
452    while (<$INPUT>) {
453	tr/\015//d;
454	last if (/^$prompt/);
455	next if (/^(\s*|\s*$cmd\s*)$/);
456	next if (/^\s+\^$/);
457	next if (/^Load for five/);
458	next if (/^Time source is/);
459	return(1) if (/Line has invalid autocommand /);
460	return(1) if (/(invalid (input|command) detected|type help or )/i);
461	# the pager can not be disabled per-session on the PIX
462	if (/^(<-+ More -+>)/) {
463	    my($len) = length($1);
464	    s/^$1\s{$len}//;
465	}
466
467	if (/^(failover|licensed) .* for this platform:/i ||
468	    /^active .* activation key/i) {
469	    ProcessHistory("COMMENTS","keysort","LICENSE","! $_");
470	    # parse license features/permanents/etc until an empty line
471	    while (<$INPUT>) {
472		tr/\015//d;
473		goto OUT if (/^$prompt/);	# should not occur
474		s/\s*$//;			# trim trailing WS
475		# the pager can not be disabled per-session on the PIX
476		if (/^(<-+ More -+>)/) {
477		    my($len) = length($1);
478		    s/^$1\s{$len}//;
479		}
480		if (/^\s*$/) {
481		    ProcessHistory("COMMENTS","keysort","LICENSE","!\n");
482		    last;
483		}
484		if (/^([^:]+: \S+\s+)(.*)/) {
485		    my($L) = $1;
486		    my($T) = $2;
487		    if ($T !~ /perpetual/i) {
488			$T = "<limited>";
489		    }
490	    	    ProcessHistory("COMMENTS","keysort","LICENSE","! $L $T\n");
491		} else {
492		    ProcessHistory("COMMENTS","keysort","LICENSE","! $_\n");
493		}
494	    }
495	    next;
496	}
497	ProcessHistory("COMMENTS","keysort","LICENSE","! $_");
498    }
499OUT:ProcessHistory("COMMENTS","keysort","LICENSE","!\n");
500    return(0);
501}
502
503# This routine parses "show cellular 0 profile"
504sub ShowCellular {
505    my($INPUT, $OUTPUT, $cmd) = @_;
506    print STDERR "    In ShowCellular: $_" if ($debug);
507
508    while (<$INPUT>) {
509	tr/\015//d;
510	last if (/^$prompt/);
511	next if (/^(\s*|\s*$cmd\s*)$/);
512	next if (/^\s+\^$/);
513	return(1) if (/Line has invalid autocommand /);
514	return(1) if (/(invalid (input|command) detected|type help or )/i);
515
516	# Ignore the PDP address and assigned DNS servers
517	next if (/^pdp (ipv6 )?address/i);
518	next if (/^\s*(primary|secondary) DNS (ipv6 )?address/i);
519
520	ProcessHistory("COMMENTS","keysort","CELL","!CELL: $_");
521    }
522    ProcessHistory("COMMENTS","keysort","CELL","!\n");
523    return(0);
524}
525
526# This routine parses "show switch detail"
527sub ShowDetail {
528    my($INPUT, $OUTPUT, $cmd) = @_;
529    print STDERR "    In ShowDetail: $_" if ($debug);
530
531    while (<$INPUT>) {
532	tr/\015//d;
533	last if (/^$prompt/);
534	next if (/^(\s*|\s*$cmd\s*)$/);
535	next if (/^\s+\^$/);
536	next if (/^Load for five/);
537	next if (/^Time source is/);
538
539	next if (/^Load for five/);
540	next if (/^Time source is/);
541	return(1) if (/Line has invalid autocommand /);
542	return(1) if (/(invalid (input|command) detected|type help or )/i);
543
544	ProcessHistory("COMMENTS","keysort","IO","!STACK: $_");
545    }
546    ProcessHistory("COMMENTS","keysort","IO","!\n");
547    return(0);
548}
549
550# This routine parses "show license" & "show license udi"
551sub ShowLicense {
552    my($INPUT, $OUTPUT, $cmd) = @_;
553    print STDERR "    In ShowLicense: $_" if ($debug);
554
555    while (<$INPUT>) {
556	tr/\015//d;
557	last if (/^$prompt/);
558	next if (/^(\s*|\s*$cmd\s*)$/);
559	next if (/^\s+\^$/);
560	next if (/^Load for five/);
561	next if (/^Time source is/);
562	return(0) if (/% no licensable udi in the system/i);	# show udi on old box
563	return(0) if (/% license not supported on this device/i);# show lic on old box
564	return(0) if (/% incomplete command/i);                 # show lic on old XE box
565	return(1) if (/Line has invalid autocommand /);
566	return(1) if (/(invalid (input|command) detected|type help or )/i);
567	return(-1) if (/(?:%|command)? authorization failed/i);
568	return(-1) if (/unable to retrieve license info/i);
569
570	# filter the BS from license broker
571	next if (/(renewal attempt|communication attempt):/i);
572	next if (/next registration attempt:/i);		# timestamp
573	next if (/(period used:|requested time:)/i);		# show lic feature
574	if (/(^\s*(evaluation )?period (left|remaining):\s*)\d+/i) {
575	    ProcessHistory("COMMENTS","keysort","LICENSE","! $1<limited>\n");
576	    next;
577	}
578
579	# drop license counts
580	next if (/license usage:/i);
581	if (/(.*)count status/i) {
582	    my($hdr) = $1;
583	    my($len) = length($hdr);
584
585	    $hdr =~ s/\s*$//;
586	    ProcessHistory("COMMENTS", "keysort", "LICENSE", "! $hdr\n");
587	    while (<$INPUT>) {
588		tr/\015//d;
589		return(0) if (/^$prompt/);
590
591		s/^(.{1,$len}).*/$1/;
592		ProcessHistory("COMMENTS", "keysort", "LICENSE", "! $_");
593	    }
594	    next;
595	}
596
597	ProcessHistory("COMMENTS","keysort","LICENSE","! $_");
598    }
599    ProcessHistory("COMMENTS","keysort","LICENSE","!\n");
600    return(0);
601}
602
603# This routine parses "showMTU"
604sub ShowMTU {
605    my($INPUT, $OUTPUT, $cmd) = @_;
606    print STDERR "    In ShowMTU: $_" if ($debug);
607
608    while (<$INPUT>) {
609	tr/\015//d;
610	last if (/^$prompt/);
611	next if (/^(\s*|\s*$cmd\s*)$/);
612	next if (/^\s+\^$/);
613	return(1) if (/Line has invalid autocommand /);
614	return(1) if (/(invalid (input|command) detected|type help or )/i);
615
616        if (/System MTU size is (\d+) bytes/) {
617            ProcessHistory("COMMENTS","keysort","IO","!MTU: $1\n");
618            next;
619        }
620        if (/System Jumbo MTU size is (\d+) bytes/) {
621            ProcessHistory("COMMENTS","keysort","IO","!MTU-Jumbo: $1\n");
622            next;
623        }
624        if (/Routing MTU size is (\d+) bytes/) {
625            ProcessHistory("COMMENTS","keysort","IO","!MTU-Routing: $1\n");
626            next;
627        }
628	# XE version
629        if (/Global Ethernet MTU is (\d+) bytes./) {
630            ProcessHistory("COMMENTS","keysort","IO","!MTU-Global: $1\n");
631            next;
632        }
633        if (/On next reload, (.*)/) {
634            ProcessHistory("COMMENTS","keysort","IO","!MTU-Reload: $1\n");
635            next;
636        }
637    }
638    ProcessHistory("COMMENTS","keysort","IO","!\n");
639    return(0);
640}
641
642# This routine parses "show sdm prefer"
643sub ShowSDM {
644    my($INPUT, $OUTPUT, $cmd) = @_;
645    print STDERR "    In ShowSDM: $_" if ($debug);
646
647    while (<$INPUT>) {
648	tr/\015//d;
649	last if (/^$prompt/);
650	next if (/^(\s*|\s*$cmd\s*)$/);
651	next if (/^\s+\^$/);
652	next if (/^Load for five/);
653	next if (/^Time source is/);
654	next if (/^Load for five/);
655	next if (/^Time source is/);
656	return(1) if (/Line has invalid autocommand /);
657	return(1) if (/(invalid (input|command) detected|type help or )/i);
658
659        if (/current template is "(.+)" template/) {
660            ProcessHistory("COMMENTS","keysort","IO","!SDM Template: $1\n");
661            next;
662        }
663        if (/current template is the (\S+) template/) {
664            ProcessHistory("COMMENTS","keysort","IO","!SDM Template: $1\n");
665            next;
666        }
667	# XE version
668        if (/This is the (\S+.*) template/) {
669            ProcessHistory("COMMENTS","keysort","IO","!SDM Template: $1\n");
670            next;
671        }
672        if (/On next reload, template will be "(.+)" template/) {
673            ProcessHistory("COMMENTS","keysort","IO","!SDM Template-Reload: $1\n");
674            next;
675        }
676	if (/(current template is|next reload)/) {
677	    ProcessHistory("COMMENTS","keysort","IO","!SDM: $_");
678	}
679    }
680    ProcessHistory("COMMENTS","keysort","IO","!\n");
681    return(0);
682}
683
684# This routine parses "show redundancy"
685sub ShowRedundancy {
686    my($INPUT, $OUTPUT, $cmd) = @_;
687    my($slave, $slaveslot);
688    print STDERR "    In ShowRedundancy: $_" if ($debug);
689
690    while (<$INPUT>) {
691	tr/\015//d;
692	last if (/^$prompt/);
693	next if (/^(\s*|\s*$cmd\s*)$/);
694	next if (/^\s+\^$/);
695	next if (/^Load for five/);
696	next if (/^Time source is/);
697	return(1) if (/Line has invalid autocommand /);
698	return(1) if (/(invalid (input|command) detected|type help or )/i);
699	# the pager can not be disabled per-session on the PIX
700	if (/^(<-+ More -+>)/) {
701	    my($len) = length($1);
702	    s/^$1\s{$len}//;
703	}
704
705	if (/^Version information for secondary in slot (\d+):/) {
706	    $slave = " Slave:";
707	    $slaveslot = ", slot $1";
708	    next;
709	}
710
711	/^IOS .* Software \(([A-Za-z0-9_-]*)\), .*Version\s+(.*)$/ &&
712	    ProcessHistory("COMMENTS","keysort","F1",
713		"!Image:$slave Software: $1, $2\n") && next;
714	/^Compiled (.*)$/ &&
715	    ProcessHistory("COMMENTS","keysort","F3",
716		"!Image:$slave Compiled: $1\n") && next;
717    }
718    return(0);
719}
720
721# This routine parses "show IDprom"
722sub ShowIDprom {
723    my($INPUT, $OUTPUT, $cmd) = @_;
724    my($tmp);
725
726    print STDERR "    In ShowIDprom: $_" if ($debug);
727
728    while (<$INPUT>) {
729	tr/\015//d;
730	last if (/^$prompt/);
731	next if (/^(\s*|\s*$cmd\s*)$/);
732	next if (/^\s+\^$/);
733	next if (/^Load for five/);
734	next if (/^Time source is/);
735	return(1) if (/Line has invalid autocommand /);
736	return(1) if (/(invalid (input|command) detected|type help or )/i);
737	# the pager can not be disabled per-session on the PIX
738	if (/^(<-+ More -+>)/) {
739	    my($len) = length($1);
740	    s/^$1\s{$len}//;
741	}
742
743	/FRU is .(.*)\'/ && ($tmp = $1);
744	/Product Number = .(.*)\'/ &&
745		ProcessHistory("COMMENTS","keysort","D0",
746				"!Catalyst Chassis type: $1, $tmp\n");
747	/Serial Number = .([0-9A-Za-z]+)/ &&
748		ProcessHistory("COMMENTS","keysort","D1",
749				"!Catalyst Chassis S/N: $1\n");
750	/Manufacturing Assembly Number = .([-0-9]+)/ && ($tmp = $1);
751	/Manufacturing Assembly Revision = .(.*)\'/ && ($tmp .= ", rev " . $1);
752	/Hardware Revision = ([0-9.]+)/ &&
753		ProcessHistory("COMMENTS","keysort","D2",
754				"!Catalyst Chassis assembly: $tmp, ver $1\n");
755    }
756    return(0);
757}
758
759# This routine parses "show install active"
760sub ShowInstallActive {
761    my($INPUT, $OUTPUT, $cmd) = @_;
762    print STDERR "    In ShowInstallActive: $_" if ($debug);
763
764    while (<$INPUT>) {
765	tr/\015//d;
766	last if (/^$prompt/);
767	next if (/^(\s*|\s*$cmd\s*)$/);
768	next if (/^\s+\^$/);
769	next if (/^Load for five/);
770	next if (/^Time source is/);
771	return(1) if (/Line has invalid autocommand /);
772	return(1) if (/(invalid (input|command) detected|type help or )/i);
773	return(-1) if (/(?:%|command)? authorization failed/i);
774	# the pager can not be disabled per-session on the PIX
775	if (/^(<-+ More -+>)/) {
776	    my($len) = length($1);
777	    s/^$1\s{$len}//;
778	}
779
780	ProcessHistory("COMMENTS","keysort","F5","!Image: $_") && next;
781    }
782    return(0);
783}
784
785# This routine parses "show env all"
786sub ShowEnv {
787    my($INPUT, $OUTPUT, $cmd) = @_;
788    print STDERR "    In ShowEnv: $_" if ($debug);
789
790    while (<$INPUT>) {
791	tr/\015//d;
792	if (/^$prompt/) { $found_env = 1; last};
793	next if (/^(\s*|\s*$cmd\s*)$/);
794	next if (/^\s+\^$/);
795	next if (/^Load for five/);
796	next if (/^Time source is/);
797	return(1) if (/Line has invalid autocommand /);
798	return(1) if (/(invalid (input|command) detected|type help or )/i);
799	return(0) if ($found_env);		# Only do this routine once
800	return(-1) if (/(?:%|command)? authorization failed/i);
801	# the pager can not be disabled per-session on the PIX
802	if (/^(<-+ More -+>)/) {
803	    my($len) = length($1);
804	    s/^$1\s{$len}//;
805	}
806
807	# remove "Fan n RPM is #" on 7201, 7301
808	next if (/ RPM is /);
809
810	if (!$E0) {
811	    $E0 = 1;
812	    ProcessHistory("COMMENTS","keysort","E0","!\n");
813	}
814	if (/^Arbiter type (\d), backplane type (\S+)/) {
815	    if (!$C0) {
816		$C0 = 1; ProcessHistory("COMMENTS","keysort","C0","!\n");
817	    }
818	    ProcessHistory("COMMENTS","keysort","C1",
819		"!Enviromental Arbiter Type: $1\n");
820	    ProcessHistory("COMMENTS","keysort","A2",
821		"!Chassis type: $2 backplane\n");
822	    next;
823	}
824	# AC revision from UBRs and some others fluctuates
825	s/is AC Revision [A-F]0\./is AC./;
826	/^Power Supply Information$/ && next;
827	/^\s*Power Module\s+Voltage\s+Current$/ && next;
828	/^\s*(Power [^:\n]+)$/ &&
829	    ProcessHistory("COMMENTS","keysort","E1","!Power: $1\n") && next;
830	/^\s*(Lower Power .*)/i &&
831	    ProcessHistory("COMMENTS","keysort","E2","!Power: $1\n") && next;
832	/^\s*(redundant .*)/i &&
833	    ProcessHistory("COMMENTS","keysort","E2","!Power: $1\n") && next;
834	/^\s*((RPS|power-supply) (\d|is) .*)/i &&
835	    ProcessHistory("COMMENTS","keysort","E2","!Power: $1\n") && next;
836	/^\s*FAN \d RPM is \d+$/ && next;
837	# Fan speed on ASR901
838	# Fan 1 Operation: Normal, is running at 40   percent speed
839	/^(\s*Fan \d Operation: \S+), .*$/ &&
840	    ProcessHistory("COMMENTS","keysort","E3","!FAN: $1\n") && next;
841
842	if (/^\s*((FAN|fan-tray) (\d|is) .*)/i) {
843	    my($tmp) = ($1);
844	    $tmp =~ s/, \S+ speed setting//;
845	    $tmp =~ s/, is running at \d{1,3}\s* percent speed//;
846	    ProcessHistory("COMMENTS","keysort","E3","!FAN: $tmp\n");
847	    next;
848	}
849    }
850    ProcessHistory("COMMENTS","","","!\n");
851    return(0);
852}
853
854# This routine parses "show hw-programmable all"
855sub ShowHWProgrammable {
856    my($INPUT, $OUTPUT, $cmd) = @_;
857    print STDERR "    In ShowPlatform: $_" if ($debug);
858
859    while (<$INPUT>) {
860	tr/\015//d;
861	last if (/^$prompt/);
862	next if (/^(\s*|\s*$cmd\s*)$/);
863	next if (/^\s+\^$/);
864	next if (/^Load for five/);
865        next if (/^Time source is/);
866	return(0) if (/% incomplete command/i); # every platform is different
867	return(1) if (/(invalid (input|command) detected|type help or )/i);
868	return(-1) if (/(?:%|command)? authorization failed/i);
869	# return(1) if ($type !~ /^12[40]/);
870	# the pager can not be disabled per-session on the PIX
871	if (/^(<-+ More -+>)/) {
872	    my($len) = length($1);
873	    s/^$1\s{$len}//;
874	}
875	/^$/ && next;
876
877	# XXX parsing for show platform, which has CPLD info on some platforms
878	#     but is inconsistent across platforms.
879	#if (/^(.*) insert time.*$/i) {
880	#    my($len) = length($1);
881	#    ProcessHistory("PLATFORM","","", "! $1\n");
882	#
883	#    while (<$INPUT>) {
884	#	tr/\015//d;
885	#	return(0) if (/^$prompt/);
886	#
887	#	s/^(.{1,$len}).*/$1/;
888	#	ProcessHistory("PLATFORM","","", "! $_");
889	#	last if (/^$/);
890	#    }
891	#    next;
892	#}
893
894	ProcessHistory("HWP","","", "! $_");
895    }
896    ProcessHistory("HWP","","", "!\n");
897
898    return(0);
899}
900
901# This routine parses "show rsp chassis-info" for the rsp
902# This will create arrays for hw info.
903sub ShowRSP {
904    my($INPUT, $OUTPUT, $cmd) = @_;
905    print STDERR "    In ShowRSP: $_" if ($debug);
906
907    while (<$INPUT>) {
908	tr/\015//d;
909	last if (/^$prompt/);
910	next if (/^(\s*|\s*$cmd\s*)$/);
911	next if (/^\s+\^$/);
912	return(1) if (/(invalid (input|command) detected|type help or )/i);
913	return(-1) if (/(?:%|command)? authorization failed/i);
914	# return(1) if ($type !~ /^12[40]/);
915	# the pager can not be disabled per-session on the PIX
916	if (/^(<-+ More -+>)/) {
917	    my($len) = length($1);
918	    s/^$1\s{$len}//;
919	}
920	/^$/ && next;
921
922	/^\s+Chassis model: (\S+)/ &&
923	    ProcessHistory("COMMENTS","keysort","D1",
924				"!RSP Chassis model: $1\n") &&
925	    next;
926	/^\s+Chassis S\/N: (.*)$/ &&
927	    ProcessHistory("COMMENTS","keysort","D2",
928				"!RSP Chassis S/N: $1\n") &&
929	    next;
930    }
931
932    return(0);
933}
934
935# This routine parses "show gsr chassis-info" for the gsr
936# This will create arrays for hw info.
937sub ShowGSR {
938    my($INPUT, $OUTPUT, $cmd) = @_;
939    # Skip if this is not a 1200n.
940    print STDERR "    In ShowGSR: $_" if ($debug);
941
942    while (<$INPUT>) {
943	tr/\015//d;
944	last if (/^$prompt/);
945	next if (/^(\s*|\s*$cmd\s*)$/);
946	next if (/^\s+\^$/);
947	next if (/^Load for five/);
948	next if (/^Time source is/);
949	next if (/^Load for five/);
950	next if (/^Time source is/);
951	return(1) if (/(invalid (input|command) detected|type help or )/i);
952	return(-1) if (/(?:%|command)? authorization failed/i);
953	# return(1) if ($type !~ /^12[40]/);
954	# the pager can not be disabled per-session on the PIX
955	if (/^(<-+ More -+>)/) {
956	    my($len) = length($1);
957	    s/^$1\s{$len}//;
958	}
959	/^$/ && next;
960
961	/^\s+Chassis: type (\S+) Fab Ver: (\S+)/ &&
962	    ProcessHistory("COMMENTS","keysort","D1",
963				"!GSR Chassis type: $1 Fab Ver: $2\n") &&
964	    next;
965	/^\s+Chassis S\/N: (.*)$/ &&
966	    ProcessHistory("COMMENTS","keysort","D2",
967				"!GSR Chassis S/N: $1\n") &&
968	    next;
969	/^\s+PCA: (\S+)\s*rev: (\S+)\s*dev: \S+\s*HW ver: (\S+)$/ &&
970	    ProcessHistory("COMMENTS","keysort","D3",
971				"!GSR Backplane PCA: $1, rev $2, ver $3\n") &&
972	    next;
973	/^\s+Backplane S\/N: (\S+)$/ &&
974	    ProcessHistory("COMMENTS","keysort","D4",
975				"!GSR Backplane S/N: $1\n") &&
976	    next;
977    }
978    ProcessHistory("COMMENTS","","","!\n");
979    return(0);
980}
981
982# This routine parses "show boot"
983sub ShowBoot {
984    my($INPUT, $OUTPUT, $cmd) = @_;
985    # Pick up boot variables if 7000/7200/7500/12000/2900/3500;
986    # otherwise pick up bootflash.
987    print STDERR "    In ShowBoot: $_" if ($debug);
988
989    while (<$INPUT>) {
990	tr/\015//d;
991	last if (/^$prompt/);
992	next if (/^(\s*|\s*$cmd\s*)$/);
993	next if (/^\s+\^$/);
994	next if (/^Load for five/);
995	next if (/^Time source is/);
996	return(1) if (/Line has invalid autocommand /);
997	return(1) if (/(invalid (input|command) detected|type help or )/i);
998	return(1) if (/Ambiguous command/i);
999	return(1) if (/(Open device \S+ failed|Error opening \S+:)/);
1000	return(-1) if (/(?:%|command)? authorization failed/i);
1001	# the pager can not be disabled per-session on the PIX
1002	if (/^(<-+ More -+>)/) {
1003	    my($len) = length($1);
1004	    s/^$1\s{$len}//;
1005	}
1006
1007	next if /CONFGEN variable/;
1008	if (!$H0) {
1009	    $H0 = 1; ProcessHistory("COMMENTS","keysort","H0","!\n");
1010	}
1011	if ($type !~ /^(12[04]|7)/) {
1012	    if ($type !~ /^(29|35)00/) {
1013		ProcessHistory("COMMENTS","keysort","H2","!BootFlash: $_");
1014	    } else {
1015		ProcessHistory("COMMENTS","keysort","H1","!Variable: $_");
1016	    }
1017	} elsif (/(variable|register)/) {
1018	    ProcessHistory("COMMENTS","keysort","H1","!Variable: $_");
1019	}
1020    }
1021    ProcessHistory("COMMENTS","","","!\n");
1022    return(0);
1023}
1024
1025# This routine parses "show flash"
1026sub ShowFlash {
1027    my($INPUT, $OUTPUT, $cmd) = @_;
1028    print STDERR "    In ShowFlash: $_" if ($debug);
1029
1030    while (<$INPUT>) {
1031	tr/\015//d;
1032	last if (/^$prompt/);
1033	next if (/^(\s*|\s*$cmd\s*)$/);
1034	# skip if this is 7000, 7200, 7500, 12000, or IOS-XE; else we have
1035	# redundant data from dir /all slot0:
1036	return(1) if ($type =~ /^(12[40]|7)/);
1037	return(1) if ($ios eq "XE");
1038	next if (/^\s+\^$/);
1039
1040	next if (/^Load for five/);
1041	next if (/^Time source is/);
1042	return(1) if (/Line has invalid autocommand /);
1043	return(1) if (/(invalid (input|command) detected|type help or )/i);
1044	return(-1) if (/(?:%|command)? authorization failed/i);
1045	# the pager can not be disabled per-session on the PIX
1046	if (/^(<-+ More -+>)/) {
1047	    my($len) = length($1);
1048	    s/^$1\s{$len}//;
1049	}
1050
1051	# Drop these files entirely.
1052	/\s+(private-multiple-fs|multiple-fs|LISP-MapCache-IPv\S+|nv_hdr)$/ &&
1053	    next;
1054
1055	if (/(dhcp_[^. ]*\.txt|license_evlog|vlan\.dat|sflog|snooping)/ ||
1056		 /(LOCAL-CA-SERVER(?:[^\s]*))\s*$/ ||
1057		 /(smart-log\/agentlog|syslog)\s*$/ ||
1058		 /(log\/(?:ssp_tz\/)?[^. ]+\.log(?:\.[0-9]+\.gz)?)\s*$/) {
1059	    # filter frequently changing files (dhcp & vlan database, logs) from flash
1060	    # change from:
1061	    # 537549598  38354       Feb 19 2019 20:59:32  log/ssp_tz/ssp_tz.log.1.gz
1062	    # 9          660 Jan 15 2011 20:43:54 vlan.dat
1063	    # 9          660 Jan 15 2011 20:43:54 +00:00 vlan.dat
1064	    # to:
1065	    #                                              log/ssp_tz/ssp_tz.log.1.gz
1066	    #                                     vlan.dat
1067	    #                                            vlan.dat
1068	    if (/(\s*\d+)(\s+[-drwx]+\s+)(\d+)(\s+)(\w+ \d+\s+\d+ \d+:\d+:\d+ .\d+:\d+)/) {
1069		my($fn, $a, $sz, $c, $dt, $rem) = ($1, $2, $3, $4, $5, $');
1070		my($fnl, $szl, $dtl) = (length($fn), length($sz), length($dt));
1071		my($fmt) = "%-". $fnl ."s%s%-". $szl ."s%s%-". $dtl ."s%s";
1072		$_ = sprintf($fmt, "", $a, "", $c, "", $rem);
1073	    } elsif (/(\s*\d+)(\s+[-drwx]+\s+)(\d+)(\s+)(\w+ \d+\s+\d+ \d+:\d+:\d+)/) {
1074		my($fn, $a, $sz, $c, $dt, $rem) = ($1, $2, $3, $4, $5, $');
1075		my($fnl, $szl, $dtl) = (length($fn), length($sz), length($dt));
1076		my($fmt) = "%-". $fnl ."s%s%-". $szl ."s%s%-". $dtl ."s%s";
1077		$_ = sprintf($fmt, "", $a, "", $c, "", $rem);
1078	    } elsif (/(\s*\d+)(\s+)(\d+)(\s+)(\w+ \d+\s+\d+ \d+:\d+:\d+ .\d+:\d+)/) {
1079		# System flash directory:
1080		# File  Length   Name/status
1081		#   1   12138448  c3640-ik9s-mz.122-40.bin
1082		my($fn, $a, $sz, $c, $dt, $rem) = ($1, $2, $3, $4, $5, $');
1083		my($fnl, $szl, $dtl) = (length($fn), length($sz), length($dt));
1084		my($fmt) = "%-". $fnl ."s%s%-". $szl ."s%s%-". $dtl ."s%s";
1085		$_ = sprintf($fmt, "", $a, "", $c, "", $rem);
1086	    } elsif (/(\s*\d+)(\s+)(\d+)(\s+)(\w+ \d+\s+\d+ \d+:\d+:\d+)/) {
1087		my($fn, $a, $sz, $c, $dt, $rem) = ($1, $2, $3, $4, $5, $');
1088		my($fnl, $szl, $dtl) = (length($fn), length($sz), length($dt));
1089		my($fmt) = "%-". $fnl ."s%s%-". $szl ."s%s%-". $dtl ."s%s";
1090		$_ = sprintf($fmt, "", $a, "", $c, "", $rem);
1091	    }
1092	    /\s+(\S+)\s*$/ &&
1093		ProcessHistory("FLASH","keysort","$1","!Flash: $_") && next;
1094	} elsif (/(running-config-archive-)\S+\s*$/) {
1095	    # filter config archives from flash
1096	    # change from:
1097	    # 9          660 Jan 15 2011 20:43:54 running-config-archive-Jul--1-16-50-27.123-113
1098	    # 9          660 Jan 15 2011 20:43:54 +00:00 running-config-archive-Jul--1-16-50-27.123-113
1099	    # to:
1100	    #                                     running-config-archive-<removed>
1101	    #                                     running-config-archive-<removed>
1102	    my($arc) = $1;
1103	    if (/(\s*\d+)(\s+[-drwx]+\s+)(\d+)(\s+)(\w+ \d+\s+\d+ \d+:\d+:\d+ .\d+:\d+)/) {
1104		my($fn, $a, $sz, $c, $dt, $rem) = ($1, $2, $3, $4, $5, $');
1105		my($fnl, $szl, $dtl) = (length($fn), length($sz), length($dt));
1106		my($fmt) = "%-". $fnl ."s%s%-". $szl ."s%s%-". $dtl ."s%s%s\n";
1107		$_ = sprintf($fmt, "", $a, "", $c, "", $arc , "<removed>");
1108	    } elsif (/(\s*\d+)(\s+[-drwx]+\s+)(\d+)(\s+)(\w+ \d+\s+\d+ \d+:\d+:\d+)/) {
1109		my($fn, $a, $sz, $c, $dt, $rem) = ($1, $2, $3, $4, $5, $');
1110		my($fnl, $szl, $dtl) = (length($fn), length($sz), length($dt));
1111		my($fmt) = "%-". $fnl ."s%s%-". $szl ."s%s%-". $dtl ."s%s%s\n";
1112		$_ = sprintf($fmt, "", $a, "", $c, "", $arc, "<removed>");
1113	    } elsif (/(\s*\d+)(\s+)(\d+)(\s+)(\w+ \d+\s+\d+ \d+:\d+:\d+ .\d+:\d+)/) {
1114		my($fn, $a, $sz, $c, $dt, $rem) = ($1, $2, $3, $4, $5, $');
1115		my($fnl, $szl, $dtl) = (length($fn), length($sz), length($dt));
1116		my($fmt) = "%-". $fnl ."s%s%-". $szl ."s%s%-". $dtl ."s%s%s\n";
1117		$_ = sprintf($fmt, "", $a, "", $c, "", $arc, "<removed>");
1118	    } elsif (/(\s*\d+)(\s+)(\d+)(\s+)(\w+ \d+\s+\d+ \d+:\d+:\d+)/) {
1119		my($fn, $a, $sz, $c, $dt, $rem) = ($1, $2, $3, $4, $5, $');
1120		my($fnl, $szl, $dtl) = (length($fn), length($sz), length($dt));
1121		my($fmt) = "%-". $fnl ."s%s%-". $szl ."s%s%-". $dtl ."s%s%s\n";
1122		$_ = sprintf($fmt, "", $a, "", $c, "", $arc, "<removed>");
1123	    }
1124	    /\s+(\S+)\s*$/ &&
1125		ProcessHistory("FLASH","keysort","$1","!Flash: $_") && next;
1126	} elsif (/^(\s*\d+)(\s+[-drwx]+\s+\d+\s+\w+ \d+\s+\d+ \d+:\d+:\d+ .\d+:\d+\s+)(\S+)/ ||
1127		 /^(\s*\d+)(\s+[-drwx]+\s+\d+\s+\w+ \d+\s+\d+ \d+:\d+:\d+\s+)(\S+)/ ||
1128		 /^(\s*\d+)(\s+\d+\s+\w+ \d+\s+\d+ \d+:\d+:\d+ .\d+:\d+\s+)(\S+)/ ||
1129		 /^(\s*\d+)(\s+\d+\s+\w+ \d+\s+\d+ \d+:\d+:\d+\s+)(\S+)/) {
1130	    my($fmt) = "%-". length($1) ."s%s%s\n";
1131	    $_ = sprintf($fmt, "", $2, $3);
1132	    ProcessHistory("FLASH","keysort","$3","!Flash: $_") && next;
1133        }
1134
1135	if (/(\d+) bytes (available|total) \((\d+) bytes (free|used)(\/\s+% free)?\)/) {
1136	    my($avail);
1137	    my($preamble) = "";
1138	    if ($2 eq "available") {
1139		$avail = $1;
1140	    } else {
1141		$preamble = "$1 bytes total";
1142		if ($4 eq "free") {
1143		    $avail = $3;
1144		} else {
1145		    $avail = $1 - $3;
1146		}
1147	    }
1148	    if ($avail >= (1024 * 1024 * 1024)) {
1149		$avail = int($avail / (1024 * 1024 * 1024));
1150		$_ = "$avail GB free\n";
1151	    } elsif ($avail >= (1024 * 1024)) {
1152		$avail = int($avail / (1024 * 1024));
1153		$_ = "$avail MB free\n";
1154	    } elsif ($avail >= (1024)) {
1155		$avail = int($avail / 1024);
1156		$_ = "$avail KB free\n";
1157	    } elsif ($avail > 0) {
1158		$_ = "< 1KB free\n";
1159	    } else {
1160		$_ = "0 bytes free\n";
1161	    }
1162	    if (length($preamble)) {
1163		chomp($_);
1164		$_ = "$preamble ($_)\n";
1165	    }
1166	}
1167	ProcessHistory("FLASH","","","!Flash: $_");
1168    }
1169    ProcessHistory("","","","!\n");
1170    return;
1171}
1172
1173# This routine parses "dir /all ((disk|slot)N|bootflash|nvram):"
1174sub DirSlotN {
1175    my($INPUT, $OUTPUT, $cmd) = @_;
1176    print STDERR "    In DirSlotN: $_" if ($debug);
1177
1178    my($dev) = (/\s([^\s]+):/);
1179
1180    while (<$INPUT>) {
1181	tr/\015//d;
1182	last if (/^$prompt/);
1183	next if (/^(\s*|\s*$cmd\s*)$/);
1184	next if (/^\s+\^$/);
1185	next if (/^Load for five/);
1186	next if (/^Time source is/);
1187	return(1) if (/Line has invalid autocommand /);
1188	return(1) if (/(invalid (input|command) detected|type help or )/i);
1189	return(1) if (/(No such device|Error Sending Request)/i);
1190	return(1) if (/\%Error: No such file or directory/);
1191	return(1) if (/No space information available/);
1192	# Corrupt flash
1193	/\%Error calling getdents / &&
1194	    ProcessHistory("FLASH","","","!Flash: $dev: $_") && next;
1195	return(-1) if (/\%Error calling/);
1196	return(-1) if (/(: device being squeezed|ATA_Status time out)/i); # busy
1197	return(-1) if (/\%Error opening \S+:\S+ \(Device or resource busy\)/i);
1198	return(-1) if (/(?:%|command)? authorization failed/i);
1199	return(1) if (/(Open device \S+ failed|Error opening \S+:)/);
1200	# the pager can not be disabled per-session on the PIX
1201	if (/^(<-+ More -+>)/) {
1202	    my($len) = length($1);
1203	    s/^$1\s{$len}//;
1204	}
1205
1206	# skip dir sup-bootflash if dir sup-bootdisk was successful, duplicates
1207	if ($cmd =~ / sup-bootdisk/) {
1208	    $supbootdisk++;
1209	} elsif ($supbootdisk && $cmd =~ / sup-bootflash/) {
1210	    return(0);
1211	}
1212
1213	# Drop LISP cache.
1214	/\s+LISP-MapCache-IPv\S+$/ && next;
1215
1216	# Filter internal file used by ISSU (In-Service Software Upgrade)
1217	# on dual RP ASR systems
1218	next if (/\.issu_loc_lock\s*$/);
1219
1220	# vASA nonsense
1221	# 9 file(s) total size: 252854822 bytes
1222	next if (/\d+ file\S+ total size: \d+ bytes/i);
1223
1224	# filter frequently changing files (dhcp & vlan database)
1225	# change from:
1226	#    9  -rw-         660  Jan 15 2011 20:43:54 vlan.dat
1227	#    9  -rw-         660  Jan 15 2011 20:43:54 +00:00  vlan.dat
1228	# to:
1229	#       -rw-                                   vlan.dat
1230	#       -rw-                                           vlan.dat
1231	if (/(dhcp_[^. ]*\.txt|vlan\.dat|sflog|snooping|syslog)\s*$/ ||
1232	    /(tracelogs|throughput_monitor_params|underlying-config)\s*$/) {
1233	    if (/(\s*\d+\s+)(\S+\s+)(\d+)(\s+)(\w+ \d+\s+\d+ \d+:\d+:\d+ .\d+:\d+)/) {
1234		my($fn, $a, $sz, $c, $dt, $rem) = ($1, $2, $3, $4, $5, $');
1235		my($fnl, $szl, $dtl) = (length($fn), length($sz), length($dt));
1236		my($fmt) = "%s%-". $szl ."s%s%-". $dtl ."s%s";
1237		$_ = sprintf($fmt, $a, "", $c, "", $rem);
1238	    } elsif (/(\s*\d+\s+)(\S+\s+)(\d+)(\s+)(\w+ \d+\s+\d+ \d+:\d+:\d+)/) {
1239		my($fn, $a, $sz, $c, $dt, $rem) = ($1, $2, $3, $4, $5, $');
1240		my($fnl, $szl, $dtl) = (length($fn), length($sz), length($dt));
1241		my($fmt) = "%s%-". $szl ."s%s%-". $dtl ."s%s";
1242		$_ = sprintf($fmt, $a, "", $c, "", $rem);
1243	    } elsif (/(\s*\d+\s+)(\S+\s+)(\d+)(\s+<no date>)/i) {
1244		# 32771  -rw-            24520                    <no date>  underlying-config
1245		my($fn, $a, $sz, $dt, $rem) = ($1, $2, $3, $4, $');
1246		my($fnl, $szl) = (length($fn), length($sz));
1247		my($fmt) = "%s%-". $szl ."s%s%s";
1248		$_ = sprintf($fmt, $a, "", $dt, $rem);
1249	    }
1250	    /\s+(\S+)\s*$/ &&
1251		ProcessHistory("FLASH","keysort","$1","!Flash: $dev: $_") &&
1252		next;
1253	} elsif (/(running-config-archive-)\S+\s*$/) {
1254	    my($arc) = $1;
1255
1256	    # filter frequently changing files of the config archive feature
1257	    # change from:
1258	    #    9  -rw-         660  Jan 15 2011 20:43:54 running-config-archive-Jul--1-16-50-27.123-113
1259	    #    9  -rw-         660  Jan 15 2011 20:43:54 +00:00  running-config-archive-Jul--1-16-50-27.123-113
1260	    # to:
1261	    #       -rw-                                   running-config-archive-Jul--1-16-50-27.123-113
1262	    #       -rw-                                           running-config-archive-Jul--1-16-50-27.123-113
1263	    if (/(\s*\d+\s+)(\S+\s+)(\d+)(\s+)(\w+ \d+\s+\d+ \d+:\d+:\d+ .\d+:\d+)/) {
1264		my($fn, $a, $sz, $c, $dt, $rem) = ($1, $2, $3, $4, $5, $');
1265		my($fnl, $szl, $dtl) = (length($fn), length($sz), length($dt));
1266		my($fmt) = "%s%-". $szl ."s%s%-". $dtl ."s%s%s\n";
1267		$_ = sprintf($fmt, $a, "", $c, "", $arc, "<removed>");
1268	    } elsif (/(\s*\d+\s+)(\S+\s+)(\d+)(\s+)(\w+ \d+\s+\d+ \d+:\d+:\d+)/) {
1269		my($fn, $a, $sz, $c, $dt, $rem) = ($1, $2, $3, $4, $5, $');
1270		my($fnl, $szl, $dtl) = (length($fn), length($sz), length($dt));
1271		my($fmt) = "%s%-". $szl ."s%s%-". $dtl ."s%s%s\n";
1272		$_ = sprintf($fmt, $a, "", $c, "", $arc, "<removed>");
1273	    }
1274	    /\s+(\S+)\s*$/ &&
1275		ProcessHistory("FLASH","keysort","$1","!Flash: $dev: $_") &&
1276		next;
1277	} else {
1278	    # drop file number (from the various formats):
1279	    #     3  -rw-             1011                    <no date>  ifIndex-table
1280	    #    9  -rw-         660  Jan 15 2011 20:43:54 vlan.dat
1281	    #   16  -rw-             5437  Jan 16 2016 02:22:32 +00:00  licenses
1282	    #  114    -rwx  92           13:22:08 Aug 15 2019  .boot_string (ASA)
1283	    if (/(\s*\d+\s+)(\S+\s+\d+\s+\w+ \d+\s+\d+ \d+:\d+:\d+ .\d+:\d+)/ ||
1284	        /(\s*\d+\s+)(\S+\s+\d+\s+\w+ \d+\s+\d+ \d+:\d+:\d+)/ ||
1285	        /(\s*\d+\s+)(\S+\s+\d+\s+<no date>\s+\S+)/ ||
1286	        /(\s*\d+\s+)(\S+\s+\d+\s+\d+:\d+:\d+ \w+ \d+\s+\d+\s+\S+)/) {
1287		#my($fn, $a, $rem) = ($1, $2, $');
1288		#my($fnl) = length($fn);
1289		#my($fmt) = "%-". $fnl ."s%s%s\n";
1290		#$_ = sprintf($fmt, "", $a, $rem);
1291		$_ = $2 . $';
1292		/\s+(\S+)\s*$/ &&
1293		    ProcessHistory("FLASH","keysort","$1","!Flash: $dev: $_") &&
1294		    next;
1295	    }
1296	}
1297
1298	# XE: 822083584 bytes total (821081600 bytes free)
1299	if (/^\s*(\d+) bytes total\s+\((\d+) bytes free\)/i) {
1300	    ProcessHistory("FLASH","","","!Flash: $dev: " .
1301			   diskszsummary($1, $2) . "\n");
1302	    next;
1303	}
1304	# vASA: 8571076608 bytes total (8306561024 bytes free/96% free)
1305	if (/^\s*(\d+) bytes total \((\d+) bytes free\/\d+% free\)/) {
1306	    ProcessHistory("FLASH","","","!Flash: $dev: " .
1307			   diskszsummary($1, $2) . "\n");
1308	    next;
1309	}
1310
1311	ProcessHistory("FLASH","","","!Flash: $dev: $_");
1312    }
1313    ProcessHistory("","","","!\n");
1314    return(0);
1315}
1316
1317# This routine parses "show controllers"
1318sub ShowContAll {
1319    my($INPUT, $OUTPUT, $cmd) = @_;
1320    my($INT);
1321    # Skip if this is a 70[01]0, 7500, or 12000.
1322    print STDERR "    In ShowContAll: $_" if ($debug);
1323
1324    while (<$INPUT>) {
1325	tr/\015//d;
1326	last if (/^$prompt/);
1327	next if (/^(\s*|\s*$cmd\s*)$/);
1328	next if (/^\s+\^$/);
1329	next if (/^Load for five/);
1330	next if (/^Time source is/);
1331	return(1) if (/(invalid (input|command) detected|type help or )/i);
1332	# return(1) if ($type =~ /^(12[40]|7[05])/);
1333	return(-1) if (/(?:%|command)? authorization failed/i);
1334	# the pager can not be disabled per-session on the PIX
1335	if (/^(<-+ More -+>)/) {
1336	    my($len) = length($1);
1337	    s/^$1\s{$len}//;
1338	}
1339
1340	if (/^Interface ([^ \n(]*)/) { $INT = "$1, "; next; }
1341	/^(BRI unit \d)/ &&
1342	    ProcessHistory("INT","","","!Interface: $1\n") && next;
1343	/^LANCE unit \d, NIM/ &&
1344	    ProcessHistory("INT","","","!Interface: $_") && next;
1345	/^(LANCE unit \d)/ &&
1346	    ProcessHistory("INT","","","!Interface: $1\n") && next;
1347	/(Media Type is \S+),/ &&
1348	    ProcessHistory("INT","","","!\t$1\n");
1349	if (/(M\dT[^ :]*:) show controller:$/) {
1350	    my($ctlr) = $1;
1351	    $_ = <$INPUT>; tr/\015//d; s/ subunit \d,//;
1352	    ProcessHistory("INT","","","!Interface: $ctlr $_");
1353	}
1354	if (/^(\S+) : show controller:$/) {
1355	    my($ctlr) = $1;
1356	    $_ = <$INPUT>; tr/\015//d; s/ subunit \d,//;
1357	    ProcessHistory("INT","","","!Interface: $ctlr: $_");
1358	}
1359	/^(HD unit \d), idb/ &&
1360	    ProcessHistory("INT","","","!Interface: $1\n") && next;
1361	/^HD unit \d, NIM/ &&
1362	    ProcessHistory("INT","","","!Interface: $_") && next;
1363	/^buffer size \d+  HD unit \d, (.*)/ &&
1364	    ProcessHistory("INT","","","!\t$1\n") && next;
1365	/^AM79970 / && ProcessHistory("INT","","","!Interface: $_") && next;
1366	/^buffer size \d+  (Universal Serial: .*)/ &&
1367	    ProcessHistory("INT","","","!\t$1\n") && next;
1368	# Remove dynamic addresses like:
1369	# !Interface: FastEthernet0/0, GT96K FE ADDR: 62AFB684, FASTSEND: 6 1579E4C, MCI_INDEX: 0
1370	/^ *Hardware is (.*?)($| ADDR: .*| at 0x.*)/ &&
1371	    ProcessHistory("INT","","","!Interface: $INT$1\n") && next;
1372	/^Hardware is (.*)/ &&
1373	    ProcessHistory("INT","","","!Interface: $INT$1\n") && next;
1374	/^(QUICC Serial unit \d),/ &&
1375	    ProcessHistory("INT","","","!$1\n") && next;
1376	/^QUICC Ethernet .*/ &&
1377	    ProcessHistory("INT","","","!$_") && next;
1378	/^DTE .*\.$/ && next;
1379	/^(cable type :.*),/ &&
1380	    ProcessHistory("INT","","","!\t$1\n") && next;
1381	/^(.* cable.*), received clockrate \d+$/ &&
1382	    ProcessHistory("INT","","","!\t$1\n") && next;
1383	/^.* cable.*$/ &&
1384	    ProcessHistory("INT","","","!\t$_") && next;
1385    }
1386    return(0);
1387}
1388
1389# This routine parses "show controllers cbus"
1390# Some of this is printed out in ShowDiagbus.
1391sub ShowContCbus {
1392    my($INPUT, $OUTPUT, $cmd) = @_;
1393    my($interface, $slot);
1394    # Skip if this is not a 7000 or 7500.
1395    print STDERR "    In ShowContCbus: $_" if ($debug);
1396
1397    while (<$INPUT>) {
1398	my(%board, %hwver);
1399	tr/\015//d;
1400	last if (/^$prompt/);
1401	next if (/^(\s*|\s*$cmd\s*)$/);
1402	next if (/^\s+\^$/);
1403	next if (/^Load for five/);
1404	next if (/^Time source is/);
1405	return(1) if (/(invalid (input|command) detected|type help or )/i);
1406	#return(1) if ($type !~ /^7[05]0/);
1407	return(-1) if (/(?:%|command)? authorization failed/i);
1408	# the pager can not be disabled per-session on the PIX
1409	if (/^(<-+ More -+>)/) {
1410	    my($len) = length($1);
1411	    s/^$1\s{$len}//;
1412	}
1413
1414	if (/^\s*slot(\d+): ([^,]+), hw (\S+), sw (\S+), ccb/) {
1415	    $slot = $1;
1416	    $board{$slot} = $2;
1417	    $hwver{$slot} = $3;
1418	    $hwucode{$slot} = $4;
1419	} elsif (/^\s*(\S+) (\d+), hardware version (\S+), microcode version (\S+)/) {
1420	    $slot = $2;
1421	    $board{$slot} = $1;
1422	    $hwver{$slot} = $3;
1423	    $hwucode{$slot} = $4;
1424	} elsif (/(Microcode .*)/) {
1425	    $ucode{$slot} = $1;
1426	} elsif (/(software loaded .*)/) {
1427	    $ucode{$slot} = $1;
1428	} elsif (/(\d+) Kbytes of main memory, (\d+) Kbytes cache memory/) {
1429	    $hwmemd{$slot} = $1;
1430	    $hwmemc{$slot} = $2;
1431	} elsif (/byte buffers/) {
1432	    chop;
1433	    s/^\s*//;
1434	    $hwbuf{$slot} = $_;
1435	} elsif (/Interface (\d+) - (\S+ \S+),/) {
1436	    $interface = $1;
1437	    ProcessHistory("HW","","",
1438		"!\n!Int $interface: in slot $slot, named $2\n"); next;
1439	} elsif (/(\d+) buffer RX queue threshold, (\d+) buffer TX queue limit, buffer size (\d+)/) {
1440	    ProcessHistory("HW","","","!Int $interface: rxq $1, txq $2, bufsize $3\n");
1441	    next;
1442	}
1443    }
1444    return(0);
1445}
1446
1447# This routine parses "show debug"
1448sub ShowDebug {
1449    my($INPUT, $OUTPUT, $cmd) = @_;
1450    print STDERR "    In ShowDebug: $_" if ($debug);
1451    my($lines) = 0;
1452
1453    while (<$INPUT>) {
1454	tr/\015//d;
1455	last if (/^$prompt/);
1456	next if (/^(\s*|\s*$cmd\s*)$/);
1457	next if (/^\s+\^$/);
1458	next if (/^Load for five/);
1459	next if (/^Time source is/);
1460	return(1) if (/Line has invalid autocommand /);
1461	return(1) if (/(invalid (input|command) detected|type help or )/i);
1462	return(-1) if (/(?:%|command)? authorization failed/i);
1463	# ASAv produce this error occasionally
1464	return(-1) if (/unable to retrieve licensing debug info/i);
1465	# the pager can not be disabled per-session on the PIX
1466	if (/^(<-+ More -+>)/) {
1467	    my($len) = length($1);
1468	    s/^$1\s{$len}//;
1469	}
1470
1471	/Load for / && next;
1472	/^Time source is / && next;
1473	/^No matching debug flags set$/ && next;
1474	/^No debug flags set$/ && next;
1475	ProcessHistory("COMMENTS","keysort","J1","!DEBUG: $_");
1476	$lines++;
1477    }
1478    if ($lines) {
1479	ProcessHistory("COMMENTS","keysort","J0","!\n");
1480    }
1481    return(0);
1482}
1483
1484# This routine parses "show diagbus"
1485# This will create arrays for hw info.
1486sub ShowDiagbus {
1487    my($INPUT, $OUTPUT, $cmd) = @_;
1488    my($board, $slot);
1489    # Skip if this is not a 7000, 70[01]0, or 7500.
1490    print STDERR "    In ShowDiagbus: $_" if ($debug);
1491
1492    while (<$INPUT>) {
1493	tr/\015//d;
1494	last if (/^$prompt/);
1495	next if (/^(\s*|\s*$cmd\s*)$/);
1496	#return(1) if ($type !~ /^7[05]/);
1497	next if (/^\s+\^$/);
1498	next if (/^Load for five/);
1499	next if (/^Time source is/);
1500	return(1) if (/Line has invalid autocommand /);
1501	return(1) if (/(invalid (input|command) detected|type help or )/i);
1502	return(-1) if (/(?:%|command)? authorization failed/i);
1503	# the pager can not be disabled per-session on the PIX
1504	if (/^(<-+ More -+>)/) {
1505	    my($len) = length($1);
1506	    s/^$1\s{$len}//;
1507	}
1508
1509	if (/^\s*Slot (\d+):/i) {
1510	    $slot = $1;
1511	    next;
1512	} elsif (/^\s*Slot (\d+) \(virtual\):/i) {
1513	    $slot = $1;
1514	    next;
1515	} elsif (/^\s*(.*Processor.*|.*controller|.*controler|.*Chassis Interface)(, FRU\s?:.*)?, HW rev (\S+), board revision (\S+)/i) {
1516	    $board = $1;
1517	    my($hwver) = $3;
1518	    my($boardrev) = $4;
1519	    if ($board =~ /Processor/) {
1520		if ($board =~ /7000 Route\/Switch/) {
1521		    $board = "RSP7000";
1522		} elsif ($board =~ /Route\/Switch Processor (\d)/) {
1523		    $board = "RSP$1";
1524		} elsif ($board =~ /Route/) {
1525		    $board = "RP";
1526		} elsif ($board =~ /Silicon Switch/) {
1527		    $board = "SSP";
1528		} elsif ($board =~ /Switch/) {
1529		    $board = "SP";
1530		    $board = "SSP $sspmem" if $ssp;
1531		} elsif ($board =~ /ATM/) {
1532		    $board = "AIP";
1533		}
1534	    } elsif ($board =~ /(.*) controller/i) {
1535		$board = $1;
1536	    }
1537	    # hwucode{$slot} defined in ShowContCbus
1538	    if (defined($hwucode{$slot})) {
1539		ProcessHistory("SLOT","","","!\n!Slot $slot/$board: hvers $hwver rev $boardrev ucode $hwucode{$slot}\n");
1540	    } else {
1541		ProcessHistory("SLOT","","","!\n!Slot $slot/$board: hvers $hwver rev $boardrev\n");
1542	    }
1543	    # These are also from the ShowContCbus
1544	    ProcessHistory("SLOT","","","!Slot $slot/$board: $ucode{$slot}\n") if (defined $ucode{$slot});
1545	    ProcessHistory("SLOT","","","!Slot $slot/$board: memd $hwmemd{$slot}, cache $hwmemc{$slot}\n")
1546	    if ((defined $hwmemd{$slot}) && (defined $hwmemc{$slot}));
1547	    ProcessHistory("SLOT","","","!Slot $slot/$board: $hwbuf{$slot}\n") if (defined $hwbuf{$slot});
1548	    next;
1549	}
1550	/Serial number: (\S+)\s*Part number: (\S+)/ &&
1551	    ProcessHistory("SLOT","","",
1552			"!Slot $slot/$board: part $2, serial $1\n") &&
1553	    next;
1554	/^\s*Controller Memory Size: (.*)$/ &&
1555	    ProcessHistory("SLOT","","","!Slot $slot/$board: $1\n") &&
1556	    next;
1557	if (/PA Bay (\d) Information/) {
1558	    my($pano) = $1;
1559	    if ("PA" =~ /$board/) {
1560		my($s,$c) = split(/\//,$board);
1561		$board = "$s/$c/PA $pano";
1562	    } else {
1563		$board =~ s/\/PA \d//;
1564		$board = "$board/PA $pano";
1565	    }
1566	    next;
1567	}
1568	/\s+(.*) (IP|PA), (\d) ports?,( \S+,)? (FRU\s?: )?(\S+)/ &&
1569	    ProcessHistory("SLOT","","","!Slot $slot/$board: type $6, $3 ports\n") &&
1570	    next;
1571	/\s+(.*) (IP|PA)( \(\S+\))?, (\d) ports?/ &&
1572	    ProcessHistory("SLOT","","","!Slot $slot/$board: type $1$3, $4 ports\n") &&
1573	    next;
1574	/^\s*HW rev (\S+), Board revision (\S+)/ &&
1575	    ProcessHistory("SLOT","","","!Slot $slot/$board: hvers $1 rev $2\n") &&
1576	    next;
1577	/Serial number: (\S+)\s*Part number: (\S+)/ &&
1578	    ProcessHistory("SLOT","","","!Slot $slot/$board: part $2, serial $1\n") && next;
1579    }
1580    return(0);
1581}
1582
1583# This routine parses "show diag" for the gsr, 7200, 3700, 3600, 2600.
1584# This will create arrays for hw info.
1585sub ShowDiag {
1586    my($INPUT, $OUTPUT, $cmd) = @_;
1587    my($fn, $slot, $WIC);
1588    print STDERR "    In ShowDiag: $_" if ($debug);
1589
1590    while (<$INPUT>) {
1591REDUX:	tr/\015//d;
1592	if (/^$prompt/) { $found_diag = 1; last};
1593	next if (/^(\s*|\s*$cmd\s*)$/);
1594	return(1) if (/Line has invalid autocommand /);
1595	next if (/^\s+\^\s*$/);
1596	next if (/^Load for five/);
1597	next if (/^Time source is/);
1598	return(1) if (/(invalid (input|command) detected|type help or )/i);
1599	return(0) if ($found_diag);		# Only do this routine once
1600	return(-1) if (/(?:%|command)? authorization failed/i);
1601	/^$/ && next;
1602	# the pager can not be disabled per-session on the PIX
1603	if (/^(<-+ More -+>)/) {
1604	    my($len) = length($1);
1605	    s/^$1\s{$len}//;
1606	}
1607
1608	s/Port Packet Over SONET/POS/;
1609	if (/^\s*SLOT\s+(\d+)\s+\((.*)\): (.*)/) {
1610	    $slot = $1;
1611	    ProcessHistory("SLOT","","","!\n");
1612	    ProcessHistory("SLOT","keysort","A","!Slot $slot: $3\n");
1613	    next;
1614	}
1615	if (/^\s*NODE\s+(\S+) : (.*)/) {
1616	    $slot = $1;
1617	    ProcessHistory("SLOT","","","!\n");
1618	    ProcessHistory("SLOT","keysort","A","!Slot $slot: $2\n");
1619	    next;
1620	}
1621	if (/^\s*PLIM\s+(\S+) : (.*)/) {
1622	    $slot = $1 . " PLIM";
1623	    ProcessHistory("SLOT","","","!\n");
1624	    ProcessHistory("SLOT","keysort","A","!Slot $slot: $2\n");
1625	    next;
1626	}
1627	if (/^\s*RACK\s+(\S+) : (.*)/) {
1628	    $slot = "Rack/" . $1;
1629	    ProcessHistory("SLOT","","","!\n");
1630	    ProcessHistory("SLOT","keysort","A","!Slot $slot: $2\n");
1631	    next;
1632	}
1633	if (/^\s+MAIN:\s* type \S+,\s+(.*)/) {
1634	    my($part) = $1;
1635	    $_ = <$INPUT>;
1636	    if (/^\s+(HW version|Design Release) (\S+)\s+S\/N (\S+)/i) {
1637		ProcessHistory("SLOT","keysort","AM","!Slot $slot/MAIN: part $part, serial $3\n");
1638		ProcessHistory("SLOT","keysort","AM","!Slot $slot/MAIN: hvers $2\n");
1639	    } else {
1640		ProcessHistory("SLOT","keysort","AM","!Slot $slot/MAIN: part $part\n");
1641		goto REDUX;
1642	    }
1643	    next;
1644	}
1645	if (/^\s+MAIN:\s* board type \S+$/) {
1646	    $_ = <$INPUT>;
1647	    tr/\015//d;
1648	    if (/^\s+(.+)$/) {
1649		my($part) = $1;
1650		$_ = <$INPUT>;
1651		tr/\015//d;
1652		if (/^\s+dev (.*)$/) {
1653		    my($dev) = $1;
1654		    $_ = <$INPUT>;
1655		    if (/^\s+S\/N (\S+)/) {
1656			ProcessHistory("SLOT","keysort","AM","!Slot $slot/MAIN: part $part, dev $dev, serial $1\n");
1657		    } else {
1658			ProcessHistory("SLOT","keysort","AM","!Slot $slot/MAIN: part $part, dev $dev\n");
1659			goto REDUX;
1660		   }
1661		} else {
1662		    ProcessHistory("SLOT","keysort","AM","!Slot $slot/MAIN: part $part\n");
1663		    goto REDUX;
1664		}
1665	    } else {
1666		goto REDUX;
1667	    }
1668	    next;
1669	}
1670	if (/^c3700\s+(io-board|mid-plane)/i) {
1671	    $slot = $1;
1672	    ProcessHistory("SLOT","","","!\n");
1673	    ProcessHistory("SLOT","keysort","A","!Slot $slot: part $1\n");
1674	    next;
1675	}
1676	if (/ Engine:\s+(.*)/) {
1677	    ProcessHistory("SLOT","keysort","AE","!Slot $slot/Engine: $1\n");
1678	}
1679	if (/FRU:\s+Linecard\/Module:\s+(\S+)/) {
1680	    ProcessHistory("SLOT","keysort","AF","!Slot $slot/FRU: Linecard/Module: $1\n");
1681	    next;
1682	}
1683	if (/\s+Processor Memory:\s+(\S+)/) {
1684	    ProcessHistory("SLOT","keysort","AF","!Slot $slot/FRU: Processor Memory: $1\n");
1685	    next;
1686	}
1687	if (/\s+Packet Memory:\s+(\S+)/) {
1688	    ProcessHistory("SLOT","keysort","AF","!Slot $slot/FRU: Packet Memory: $1\n");
1689	    next;
1690	}
1691	if (/\s+Route Memory:\s+(\S+)/) {
1692	    ProcessHistory("SLOT","keysort","AF","!Slot $slot/FRU: Route Memory: $1\n");
1693	    next;
1694	}
1695	if (/^\s+PCA:\s+(.*)/) {
1696	    my($part) = $1;
1697	    $_ = <$INPUT>;
1698	    if (/^\s+(HW version|design release) (\S+)\s+S\/N (\S+)/i) {
1699		ProcessHistory("SLOT","keysort","C1","!Slot $slot/PCA: part $part, serial $3\n");
1700		ProcessHistory("SLOT","keysort","C2","!Slot $slot/PCA: hvers $2\n");
1701	    } else {
1702		ProcessHistory("SLOT","keysort","C1","!Slot $slot/PCA: part $part\n");
1703		goto REDUX;
1704	    }
1705	    next;
1706	}
1707	if (/^\s+MBUS: .*\)\s+(.*)/) {
1708	    my($tmp) = "!Slot $slot/MBUS: part $1";
1709	    $_ = <$INPUT>;
1710	    /^\s+HW version (\S+)\s+S\/N (\S+)/ &&
1711		ProcessHistory("SLOT","keysort","MB1","$tmp, serial $2\n") &&
1712		ProcessHistory("SLOT","keysort","MB2","!Slot $slot/MBUS: hvers $1\n");
1713	    next;
1714	}
1715	if (/^\s+MBUS Agent Software version (.*)/) {
1716	    ProcessHistory("SLOT","keysort","MB3","!Slot $slot/MBUS: software $1\n");
1717	    next;
1718	}
1719	if (/^\s+PLD: (.*)/) {
1720	    ProcessHistory("SLOT","keysort","P","!Slot $slot/PLD: $1\n");
1721	    next;
1722	}
1723	if (/^\s+MONLIB: (.*)/) {
1724	    ProcessHistory("SLOT","keysort","Q","!Slot $slot/MONLIB: $1\n");
1725	    next;
1726	}
1727	if (/^\s+ROM Monitor version (.*)/) {
1728	    ProcessHistory("SLOT","keysort","R","!Slot $slot/ROM Monitor: version $1\n");
1729	    next;
1730	}
1731	if (/^\s+ROMMON: Version (.*)/) {
1732	    ProcessHistory("SLOT","keysort","R","!Slot $slot/ROMMON: version $1\n");
1733	    next;
1734	}
1735	if (/^\s+Fabric Downloader version used (.*)/) {
1736	    ProcessHistory("SLOT","keysort","Z","!Slot $slot/Fabric Downloader: version $1\n");
1737	    next;
1738	}
1739	if (/^\s+DRAM size: (\d+)/) {
1740	    my($dram) = $1 / 1048576;
1741	    $_ = <$INPUT>;
1742	    if (/^\s+FrFab SDRAM size: (\d+)/) {
1743		ProcessHistory("SLOT","keysort","MB4","!Slot $slot/MBUS: $dram Mbytes DRAM, "
1744			   . $1 / 1024 . " Kbytes SDRAM\n");
1745	    } else {
1746		ProcessHistory("SLOT","keysort","MB4","!Slot $slot/MBUS: $dram Mbytes DRAM\n");
1747		goto REDUX;
1748	    }
1749	    next;
1750	}
1751	# 7200, 3800, 3600, 2600, and 1700 stuff
1752	if (/^(Slot)\s+(\d+(\/\d+)?):/
1753	    || /^\s+(PVDM|WIC|VIC|WIC\/VIC|WIC\/VIC\/HWIC) Slot (\d):/
1754	    || /^(Encryption AIM) (\d):/
1755	    || /^(AIM Module in slot:) (\d)/) {
1756	    if ($1 eq "PVDM") {
1757		$WIC = "/$2";
1758	    } elsif ($1 eq "WIC") {
1759		$WIC = "/$2";
1760	    } elsif ($1 eq "VIC") {
1761		$WIC = "/$2";
1762	    } elsif ($1 eq "WIC/VIC") {
1763		$WIC = "/$2";
1764	    } elsif ($1 eq "WIC/VIC/HWIC") {
1765		$WIC = "/$2";
1766	    } elsif ($1 eq "DSP") {
1767		$WIC = "/$2";
1768	    } elsif ($1 eq "Encryption AIM") {
1769		$slot = "$2";
1770		$WIC = undef;
1771		ProcessHistory("SLOT","","","!\n");
1772		ProcessHistory("SLOT","keysort","B","!Slot $slot: type $1\n");
1773		next;
1774	    } elsif ($1 eq "AIM Module in slot:") {
1775		$slot = "AIM $2";
1776		$WIC = undef;
1777		ProcessHistory("SLOT","","","!\n");
1778		ProcessHistory("SLOT","keysort","B",
1779			       "!Slot $slot: type AIM Module\n");
1780		next;
1781	    } else {
1782		$slot = $2;
1783		$WIC = undef;
1784	    }
1785	    $_ = <$INPUT>; tr/\015//d;
1786
1787	    # clean up hideous 7200/etc formats to look more like 7500 output
1788	    s/Fast-ethernet on C7200 I\/O card/FE-IO/;
1789	    s/ with MII or RJ45/-TX/;
1790	    s/Fast-ethernet /100Base/; s/[)(]//g;
1791	    s/intermediate reach/IR/i;
1792
1793	    ProcessHistory("SLOT","","","!\n");
1794	    /\s+(.*) port adapter,?\s+(\d+)\s+/i &&
1795		ProcessHistory("SLOT","keysort","B",
1796			       "!Slot $slot: type $1, $2 ports\n") && next;
1797	    # I/O controller with no interfaces
1798	    /\s+(.*)\s+port adapter\s*$/i &&
1799		ProcessHistory("SLOT","keysort","B",
1800			       "!Slot $slot: type $1, 0 ports\n") && next;
1801	    /\s+(.*)\s+daughter card(.*)$/ &&
1802		ProcessHistory("SLOT","keysort","B",
1803			       "!Slot $slot$WIC: type $1$2\n") && next;
1804	    /\s+(FT1)$/ &&
1805		ProcessHistory("SLOT","keysort","B",
1806			       "!Slot $slot$WIC: type $1\n") && next;
1807	    # AS5300/5400 handling
1808	    /^Hardware is\s+(.*)$/i &&
1809		ProcessHistory("SLOT","keysort","B","!Slot $slot: type $1\n")
1810		&& next;
1811	    /^DFC type is\s+(.*)$/i &&
1812		ProcessHistory("SLOT","keysort","B","!Slot $slot: type $1\n")
1813		&& next;
1814	    #
1815	    # handle WICs lacking "daughter card" in the 2nd line of their
1816	    # show diag o/p
1817	    if (length($WIC)) {
1818		s/^\s+//;
1819		ProcessHistory("SLOT","keysort","B","!Slot $slot$WIC: type $_");
1820	    }
1821	    next;
1822	} elsif (/^\s+(.* (DSP) Module) Slot (\d):/) {
1823	    # The 1760 (at least) has yet another format...where it has two
1824	    # dedicated DSP slots, and thus two slot 0s.
1825	    my($TYPE) = $1;
1826	    $WIC = "/$3";
1827	    ProcessHistory("SLOT","","","!\n");
1828	    ProcessHistory("SLOT","keysort","B",
1829					"!Slot $slot$WIC: type $TYPE\n");
1830	    next;
1831	}
1832	# yet another format.  seen on 2600s w/ 12.1, but appears to be all
1833	# 12.1, including 7200s & 3700s.  Sometimes the PCB serial appears
1834	# before the hardware revision.
1835	if (/(pcb serial number|hardware revision)\s+:\s+(\S+)$/i) {
1836	    my($hw, $pn, $rev, $sn);
1837	    if ($1 =~ /^pcb/i) {
1838		$sn = $2;
1839	    } else {
1840		$hw = $2;
1841	    }
1842	    while (<$INPUT>) {
1843		tr/\015//d;
1844
1845		# Sometimes "show diag" just ends while we are
1846		# trying to process this pcb stuff.  Check for a
1847		# prompt so we can get out.
1848		if (/^$prompt/) {
1849		    $found_diag = 1;
1850		    goto PerlSucks;
1851		}
1852
1853		if (/0x..: / || /^$/) {
1854		    # no effing idea why break does not work there
1855		    goto PerlSucks;
1856		}
1857		if (/hardware revision\s+:\s+(\S+)/i) { $hw = $1; }
1858		if (/part number\s+:\s+(\S+)/i) { $pn = $1; }
1859		if (/board revision\s+:\s+(\S+)/i) { $rev = $1; }
1860		if (/pcb serial number\s+:\s+(\S+)/i) { $sn = $1; }
1861		# fru/pid bits, true Cisco evolving "standard", hopefully
1862		# "show inventory" will be "the way" soon.
1863		#
1864		if (/product \(fru\) number\s+:\s+(\S+)/i) { $fn = $1; }
1865		if (/product number\s+:\s+(\S+)/i) { $fn = $1; }
1866		if (/product\s+identifier\s+\(PID\)\s+:\s+(\S+)/i) { $fn = $1; }
1867		if (/fru\s+part\s+number\s+(\S+)/i) { $fn = $1; }
1868	    }
1869PerlSucks:
1870	    # fru/pid bits
1871	    # If slot is blank, call it "Chassis"
1872	    if ($slot eq "") {
1873		$slot = "Chassis";
1874	    }
1875	    ProcessHistory("SLOT","keysort","AG","!Slot $slot$WIC: fru $fn\n");
1876	    #
1877	    ProcessHistory("SLOT","keysort","B","!Slot $slot$WIC: hvers $hw rev $rev\n");
1878	    ProcessHistory("SLOT","keysort","C","!Slot $slot$WIC: part $pn, serial $sn\n");
1879	    # If we saw the prompt, then we are done.
1880	    last if $found_diag;
1881	}
1882	/revision\s+(\S+).*revision\s+(\S+)/ &&
1883	    ProcessHistory("SLOT","keysort","C","!Slot $slot$WIC: hvers $1 rev $2\n") &&
1884	    next;
1885	/number\s+(\S+)\s+Part number\s+(\S+)/ &&
1886	    ProcessHistory("SLOT","keysort","D","!Slot $slot$WIC: part $2, serial $1\n") &&
1887	    next;
1888	# AS5x00 bits
1889	/^\ Board Revision\s+(\S+),\s+Serial Number\s+(\S+),/ &&
1890	    ProcessHistory("SLOT","keysort","D",
1891			   "!Slot $slot$WIC: rev $1, serial $2\n") && next;
1892	/^\ Board Hardware Version\s+(\S+),\s+Item Number\s+(\S+),/ &&
1893	    ProcessHistory("SLOT","keysort","D",
1894			   "!Slot $slot$WIC: hvers $1, part $2\n") && next;
1895	/^Motherboard Info:/ &&
1896	    ProcessHistory("SLOT","keysort","D",
1897			   "!Slot $slot$WIC: Motherboard\n") && next;
1898	#
1899    }
1900    ProcessHistory("SLOT","","","!\n");
1901    return(0);
1902}
1903
1904# This routine parses "show inventory".
1905sub ShowInventory {
1906    my($INPUT, $OUTPUT, $cmd) = @_;
1907    print STDERR "    In ShowInventory: $_" if ($debug);
1908
1909    while (<$INPUT>) {
1910	tr/\015//d;
1911	return if (/^\s*\^$/);
1912	if (/^$prompt/) { $found_inventory = 1; last};
1913	next if (/^(\s*|\s*$cmd\s*)$/);
1914	return(1) if (/Line has invalid autocommand /);
1915	next if (/^\s+\^\s*$/);
1916	next if (/^Load for five/);
1917	next if (/^Time source is/);
1918	return(1) if (/(invalid (input|command) detected|type help or )/i);
1919	return(-1) if (/(?:%|command)? authorization failed/i);
1920	return(0) if ($found_inventory);	# Only do this routine once
1921	# the pager can not be disabled per-session on the PIX
1922	if (/^(<-+ More -+>)/) {
1923	    my($len) = length($1);
1924	    s/^$1\s{$len}//;
1925	}
1926
1927	next if (/^Load for /);
1928	next if (/^Time source is /);
1929	if (/^(NAME: "[^"]*",) (DESCR: "[^"]+")/) {
1930	    ProcessHistory("INVENTORY","","", sprintf("!%-30s %s\n", $1, $2));
1931	    next;
1932	}
1933	# split PID/VID/SN line
1934	if (/^PID: (\S*)\s*,\s*VID: (\S*)\s*,\s*SN: (\S*)\s*$/) {
1935	    my($pid,$vid,$sn) = ($1, $2, $3);
1936	    my($entries) = "";
1937	    # filter <empty>, "0x" and "N/A" lines
1938	    if ($pid !~ /^(|0x|N\/A)$/) {
1939		$entries .= "!PID: $pid\n";
1940	    }
1941	    if ($vid !~ /^(|0x|N\/A)$/) {
1942		$entries .= "!VID: $vid\n";
1943	    }
1944	    if ($sn !~ /^(|0x|N\/A)$/) {
1945		$entries .= "!SN: $sn\n";
1946	    }
1947	    ProcessHistory("INVENTORY","","", "$entries");
1948	    next;
1949	}
1950	ProcessHistory("INVENTORY","","","!$_");
1951    }
1952    ProcessHistory("INVENTORY","","","!\n");
1953
1954    return(0);
1955}
1956
1957# This routine parses "show module".
1958sub ShowModule {
1959    my($INPUT, $OUTPUT, $cmd) = @_;
1960    print STDERR "    In ShowModule: $_" if ($debug);
1961
1962    my(@lines);
1963    my($slot, $pa);
1964    my($switch, $switch_n);
1965
1966    if ($vss_show_module == 1) {
1967	while (<$INPUT>) {
1968	    last if (/^$prompt/);
1969	}
1970	return(0);
1971    }
1972    while (<$INPUT>) {
1973	tr/\015//d;
1974	next if (/^\s*\^$/);
1975	next if (/^Load for five/);
1976	next if (/^Time source is/);
1977	if (/online diag status/i) {
1978	    $vss_show_module = 1;
1979	    next;
1980	}
1981	last if (/^$prompt/);
1982	next if (/^(\s*|\s*$cmd\s*)$/);
1983	return(-1) if (/(?:%|command)? authorization failed/i);
1984	# the pager can not be disabled per-session on the PIX
1985	if (/^(<-+ More -+>)/) {
1986	    my($len) = length($1);
1987	    s/^$1\s{$len}//;
1988	}
1989
1990	# match  Switch Number:     2   Role:  Virtual Switch Active/Standby
1991	if (/^ *Switch Number: *(\d) .*Virtual Switch\s+(\S+)/) {
1992	    $switch_n = $1;
1993	    $switch = "Sw$1 ";
1994	    ProcessHistory("Module","","","!Virtual Switch $1 is $2\n");
1995	    next;
1996	}
1997
1998	# match slot/card info line
1999	if (/^ *(\d+)\s+(\d+)\s+(.*)\s+(\S+)\s+(\S+)\s*$/) {
2000	    $lines[$switch_n * 10000 + $1 * 1000] .= "!Slot ${switch}$1: type $3, $2 ports\n!Slot ${switch}$1: part $4, serial $5\n";
2001	    $lines[$switch_n * 10000 + $1 * 1000] =~ s/\s+,/,/g;
2002	    next;
2003	}
2004	# now match the Revs in the second paragraph of o/p and stick it in
2005	# the array with the previous bits...grumble.
2006	if (/^ *(\d+)\s+\S+\s+to\s+\S+\s+(\S+)\s+(\S*)\s+(\S+)(\s+\S+)?\s*$/) {
2007	    $lines[$switch_n * 10000 + $1 * 1000] .= "!Slot ${switch}$1: hvers $2, firmware $3, sw $4\n";
2008	    $lines[$switch_n * 10000 + $1 * 1000] =~ s/\s+,/,/g;
2009	    next;
2010	}
2011	# grab the sub-modules, if any
2012	if (/^\s+(\d+)\s(.*)\s+(\S+)\s+(\S+)\s+(\S+)\s+\S+\s*$/) {
2013	    my($idx);
2014	    $pa = 0 if ($1 != $slot);
2015	    $slot = $1;
2016	    $idx = $switch_n * 10000 + $1 * 1000 + $1 * 10 + $pa;
2017	    $lines[$idx] .= "!Slot ${switch}$1/$pa: type $2\n";
2018	    $lines[$idx] .= "!Slot ${switch}$slot/$pa: part $3, serial $4\n";
2019	    $lines[$idx] .= "!Slot ${switch}$slot/$pa: hvers $5\n";
2020	    $pa++;
2021	}
2022    }
2023    if ($switch_n != 0) {
2024	ProcessHistory("Module","","","!\n");
2025    }
2026    foreach $slot (@lines) {
2027	next if ($slot =~ /^\s*$/);
2028	ProcessHistory("Module","","","$slot!\n");
2029    }
2030
2031    return(0);
2032}
2033
2034# This routine parses "show spe version".
2035sub ShowSpeVersion {
2036    my($INPUT, $OUTPUT, $cmd) = @_;
2037    print STDERR "    In ShowSpeVersion: $_" if ($debug);
2038
2039    while (<$INPUT>) {
2040	tr/\015//d;
2041	last if (/^$prompt/);
2042	next if (/^(\s*|\s*$cmd\s*)$/);
2043	return(1) if /^\s*\^\s*$/;
2044	return(1) if (/Line has invalid autocommand /);
2045	next if (/^\s+\^\s*$/);
2046	next if (/^Load for five/);
2047	next if (/^Time source is/);
2048	return(1) if (/(invalid (input|command) detected|type help or )/i);
2049	return(-1) if (/(?:%|command)? authorization failed/i);
2050
2051	ProcessHistory("MODEM","","","!Modem: $_") && next;
2052    }
2053    ProcessHistory("MODEM","","","!\n");
2054    return(0);
2055}
2056
2057# This routine parses "show c7200" for the 7200
2058# This will create arrays for hw info.
2059sub ShowC7200 {
2060    my($INPUT, $OUTPUT, $cmd) = @_;
2061    # Skip if this is not a 7200.
2062    print STDERR "    In ShowC7200: $_" if ($debug);
2063
2064    while (<$INPUT>) {
2065	tr/\015//d;
2066	last if (/^$prompt/);
2067	next if (/^(\s*|\s*$cmd\s*)$/);
2068	next if (/^\s+\^\s*$/);
2069	next if (/^Load for five/);
2070	next if (/^Time source is/);
2071	return(1) if (/(invalid (input|command) detected|type help or )/i);
2072	#return(1) if ($type !~ /^72/);
2073	return(-1) if (/(?:%|command)? authorization failed/i);
2074	/^$/ && next;
2075	# the pager can not be disabled per-session on the PIX
2076	if (/^(<-+ More -+>)/) {
2077	    my($len) = length($1);
2078	    s/^$1\s{$len}//;
2079	}
2080
2081	if (/^(C7200 )?Midplane EEPROM:/) {
2082	    $_ = <$INPUT>;
2083	    /revision\s+(\S+).*revision\s+(\S+)/;
2084	    ProcessHistory("SLOT","","","!Slot Midplane: hvers $1 rev $2\n");
2085	    $_ = <$INPUT>;
2086	    /number\s+(\S+)\s+Part number\s+(\S+)/;
2087	    ProcessHistory("SLOT","","","!Slot Midplane: part $2, serial $1\n!\n");
2088	    next;
2089	}
2090	if (/C720\d(VXR)? CPU EEPROM:/) {
2091	    my ($hvers,$rev,$part,$serial);
2092	    # npe400s report their cpu eeprom info differently w/ 12.0.21S
2093	    while (<$INPUT>) {
2094		/Hardware Revision\s+: (\S+)/ && ($hvers = $1) && next;
2095		/Board Revision\s+: (\S+)/ && ($rev = $1) && next;
2096		/Part Number\s+: (\S+)/ && ($part = $1) && next;
2097		/Serial Number\s+: (\S+)/ && ($serial = $1) && next;
2098		/revision\s+(\S+).*revision\s+(\S+)/ &&
2099		    ($hvers = $1, $rev = $2) && next;
2100		/number\s+(\S+)\s+Part number\s+(\S+)/ &&
2101		    ($serial = $1, $part = $2) && next;
2102		/^\s*$/ && last;
2103	    }
2104	    ProcessHistory("SLOT","","","!Slot CPU: hvers $hvers rev $rev\n");
2105	    ProcessHistory("SLOT","","","!Slot CPU: part $part, serial $serial\n!\n");
2106	    next;
2107	}
2108    }
2109    return(0);
2110}
2111
2112# This routine parses "show capture".  Intended for ASA/PIXes.
2113sub ShowCapture {
2114    my($INPUT, $OUTPUT, $cmd) = @_;
2115    print STDERR "    In ShowCapture: $_" if ($debug);
2116    my $capture_found = 0;
2117    while (<$INPUT>) {
2118	tr/\015//d;
2119	last if (/^$prompt/);
2120	return(1) if (/^(\s*|\s*$cmd\s*)$/);
2121	return(1) if /^\s*\^\s*$/;
2122	return(1) if (/Line has invalid autocommand /);
2123	next if (/^\s+\^\s*$/);
2124	next if (/^Load for five/);
2125	next if (/^Time source is/);
2126	return(1) if (/(invalid (input|command) detected|type help or )/i);
2127	return(-1) if (/(?:%|command)? authorization failed/i);
2128	# the pager can not be disabled per-session on the PIX
2129	if (/^(<-+ More -+>)/) {
2130	    my($len) = length($1);
2131	    s/^$1\s{$len}//;
2132	}
2133
2134	if (/capture (.*) type/) {
2135	    my $cap_name = $1;
2136	    s/\d+ bytes/<COUNTER> bytes/;
2137	    ProcessHistory("CAPTURE","","","!Capture: $cap_name\n");
2138	    ProcessHistory("CAPTURE","","","!Capture: $_");
2139	} else {
2140	    ProcessHistory("CAPTURE","","","!Capture: $_");
2141	}
2142        $capture_found = 1
2143    }
2144    ProcessHistory("CAPTURE","","","!\n") if ($capture_found == 1);
2145    return(0);
2146}
2147
2148# This routine parses "show dot1x"
2149sub ShowDot1x {
2150    my($INPUT, $OUTPUT, $cmd) = @_;
2151    print STDERR "    In ShowVTP: $_" if ($debug);
2152
2153    while (<$INPUT>) {
2154	tr/\015//d;
2155	last if (/^$prompt/);
2156	next if (/^(\s*|\s*$cmd\s*)$/);
2157	return(1) if /^\s*\^\s*$/;
2158	return(1) if (/Line has invalid autocommand /);
2159	next if (/^\s+\^\s*$/);
2160	next if (/^Load for five/);
2161        next if (/^Time source is/);
2162	return(1) if (/(invalid (input|command) detected|type help or )/i);
2163	return(-1) if (/(?:%|command)? authorization failed/i);
2164	next if (/^Configuration last modified by/);
2165	# the pager can not be disabled per-session on the PIX
2166	if (/^(<-+ More -+>)/) {
2167	    my($len) = length($1);
2168	    s/^$1\s{$len}//;
2169	}
2170
2171	# if dot1x is enabled, do not collect show vlan output
2172	if (/^sysauthcontrol\s+(?:=\s+)?(\S+)/i) {
2173	    if ($1 !~ /disabled/i && $filter_osc > 1) {
2174		$DO_SHOW_VLAN = 1;
2175	    }
2176	}
2177	ProcessHistory("COMMENTS","keysort","I0","!DOT1x: $_");
2178    }
2179    ProcessHistory("COMMENTS","keysort","I0","!\n");
2180    return(0);
2181}
2182
2183
2184# This routine parses "show vtp status"
2185sub ShowVTP {
2186    my($INPUT, $OUTPUT, $cmd) = @_;
2187    print STDERR "    In ShowVTP: $_" if ($debug);
2188
2189    while (<$INPUT>) {
2190	tr/\015//d;
2191	last if (/^$prompt/);
2192	next if (/^(\s*|\s*$cmd\s*)$/);
2193	return(1) if /^\s*\^\s*$/;
2194	return(1) if (/Line has invalid autocommand /);
2195	next if (/^\s+\^\s*$/);
2196	next if (/^Load for five/);
2197	next if (/^Time source is/);
2198	return(1) if (/(invalid (input|command) detected|type help or )/i);
2199	#return(1) if ($type !~ /^(2900XL|3500XL|6000)$/);
2200	return(-1) if (/(?:%|command)? authorization failed/i);
2201	next if (/^Configuration last modified by/);
2202	# the pager can not be disabled per-session on the PIX
2203	if (/^(<-+ More -+>)/) {
2204	    my($len) = length($1);
2205	    s/^$1\s{$len}//;
2206	}
2207
2208	if (/^VTP Operating Mode\s+:\s+(Client)/) {
2209	    $DO_SHOW_VLAN = 1;
2210	}
2211	ProcessHistory("COMMENTS","keysort","I0","!VTP: $_");
2212    }
2213    ProcessHistory("COMMENTS","keysort","I0","!\n");
2214    return(0);
2215}
2216
2217# This routine parses "show vlan"
2218sub ShowVLAN {
2219    my($INPUT, $OUTPUT, $cmd) = @_;
2220    print STDERR "    In ShowVLAN: $_" if ($debug);
2221
2222    ($_ = <$INPUT>, return(1)) if ($DO_SHOW_VLAN);
2223
2224    while (<$INPUT>) {
2225	tr/\015//d;
2226	last if (/^$prompt/);
2227	next if (/^(\s*|\s*$cmd\s*)$/);
2228	return(1) if /^\s*\^\s*$/;
2229	return(1) if (/Line has invalid autocommand /);
2230	next if (/^\s+\^\s*$/);
2231	next if (/^Load for five/);
2232	next if (/^Time source is/);
2233	return(1) if (/(invalid (input|command) detected|type help or )/i);
2234	return(1) if (/ambiguous command/i);
2235	return(1) if (/incomplete command/i);
2236	return(-1) if (/(?:%|command)? authorization failed/i);
2237	# the pager can not be disabled per-session on the PIX
2238	if (/^(<-+ More -+>)/) {
2239	    my($len) = length($1);
2240	    s/^$1\s{$len}//;
2241	}
2242	return(0) if (/no virtual lans configured/i);
2243	# some ASAs do not support show vlan
2244	return(0) if (/use .show switch vlan. to view the vlans that have /i);
2245
2246	# GSR-specific, i think, filter
2247	if (/received:\s+transmitted:/i) {
2248	    while (<$INPUT>) {
2249		last if (/^\s*$/);
2250		goto OUT if (/^$prompt/);
2251	    }
2252	}
2253
2254	next if (/total.*packets.*(input|output)/i);
2255
2256	# Aironet AP's traffic counters
2257	next if (/\d+\s+bytes.*(input|output)/i);
2258	next if (/^\s*Other\s+\d+\s+\d+\s*$/i);
2259	next if (/^\s*Bridging\s+Bridge.Group.\d+\s+\d+\s+\d+\s*$/i);
2260
2261	ProcessHistory("COMMENTS","keysort","IO","!VLAN: $_");
2262    }
2263OUT:ProcessHistory("COMMENTS","keysort","IO","!\n");
2264    return(0);
2265}
2266
2267# This routine processes a "show shun".  Intended for ASA/PIXes.
2268sub ShowShun {
2269    my($INPUT, $OUTPUT, $cmd) = @_;
2270    print STDERR "    In ShowShun: $_" if ($debug);
2271    my $shun_found = 0;
2272
2273    while (<$INPUT>) {
2274	tr/\015//d;
2275	last if (/^$prompt/);
2276	return(1) if (/^(\s*|\s*$cmd\s*)$/);
2277	return(1) if /^\s*\^\s*$/;
2278	return(1) if (/Line has invalid autocommand /);
2279	return(1) if (/(invalid (input|command) detected|type help or )/i);
2280	return(-1) if (/(?:%|command)? authorization failed/i);
2281
2282	next if (/^\s+\^\s*$/);
2283	next if (/^Load for five/);
2284	next if (/^Time source is/);
2285	# the pager can not be disabled per-session on the PIX
2286	if (/^(<-+ More -+>)/) {
2287	    my($len) = length($1);
2288	    s/^$1\s{$len}//;
2289	}
2290
2291	ProcessHistory("SHUN","","","!Shun: $_");
2292	$shun_found = 1;
2293    }
2294    ProcessHistory("SHUN","","","!\n") if ($shun_found == 1);
2295    return(0);
2296}
2297
2298# This routine processes a "write term"
2299sub WriteTerm {
2300    my($INPUT, $OUTPUT, $cmd) = @_;
2301    print STDERR "    In WriteTerm: $_" if ($debug);
2302    my($comment, $linecnt) = (0,0);
2303
2304    while (<$INPUT>) {
2305TOP:
2306	tr/\015//d;
2307	last if (/^$prompt/);
2308	return(1) if (!$linecnt && /^\s+\^\s*$/);
2309	next if (/^\s*$cmd\s*$/);
2310	return(1) if (/Line has invalid autocommand /);
2311	next if (/^\s+\^\s*$/);
2312	next if (/^Load for five/);
2313	next if (/^Time source is/);
2314	return(1) if (/(invalid (input|command) detected|type help or )/i);
2315	return(1) if (/\%Error: No such file or directory/);
2316	return(1) if (/(Open device \S+ failed|Error opening \S+:)/);
2317	return(0) if ($found_end);		# Only do this routine once
2318	return(-1) if (/(?:%|command)? authorization failed/i);
2319	return(-1) if (/% ?configuration buffer full/i);
2320	# the pager can not be disabled per-session on the PIX
2321	if (/^(<-+ More -+>)/) {
2322	    my($len) = length($1);
2323	    s/^$1\s{$len}//;
2324	}
2325	/^! no configuration change since last restart/i && next;
2326	# skip emtpy lines at the beginning
2327	if (!$linecnt && /^\s*$/) {
2328	    next;
2329	}
2330	if (!$linecnt && defined($config_register)) {
2331	    ProcessHistory("","","", "!\nconfig-register $config_register\n");
2332	}
2333
2334	/Non-Volatile memory is in use/ && return(-1); # NvRAM is locked
2335	/% Configuration buffer full, / && return(-1); # buffer is in use
2336	$linecnt++;
2337	# skip the crap
2338	if (/^(##+|(building|current) configuration)/i) {
2339	    while (<$INPUT>) {
2340		next if (/^Current configuration\s*:/i);
2341		next if (/^:/);
2342		next if (/^([%!].*|\s*)$/);
2343		next if (/^ip add.*ipv4:/);	# band-aid for 3620 12.0S
2344		last;
2345	    }
2346	    tr/\015//d;
2347	}
2348	# config timestamp on MDS/NX-OS
2349	/Time: / && next;
2350	# skip ASA 5520 configuration author line
2351	/^: written by /i && next;
2352	# some versions have other crap mixed in with the bits in the
2353	# block above
2354	/^! (Last configuration|NVRAM config last)/ && next;
2355	# and for the ASA
2356	/^: (Written by \S+ at|Saved)/ && next;
2357
2358	# skip consecutive comment lines to avoid oscillating extra comment
2359	# line on some access servers.  grrr.
2360	if (/^!\s*$/) {
2361	    next if ($comment);
2362	    ProcessHistory("","","",$_);
2363	    $comment++;
2364	    next;
2365	}
2366	$comment = 0;
2367
2368	# Dog gone Cool matches to process the rest of the config
2369	/^tftp-server flash /   && next; # kill any tftp remains
2370	/^ntp clock-period /    && next; # kill ntp clock-period
2371	/^ clockrate /		&& next; # kill clockrate on serial interfaces
2372	# kill rx/txspeed (particularly on cellular modem cards)
2373	if (/^(line (\d+(\/\d+\/\d+)?|con|aux|vty))/) {
2374	    my($key) = $1;
2375	    my($lineauto) = (0);
2376	    if ($key =~ /con/) {
2377		$key = -1;
2378	    } elsif ($key =~ /aux/) {
2379		$key = -2;
2380	    } elsif ($key =~ /vty/) {
2381		$key = -3;
2382	    }
2383	    ProcessHistory("LINE","keysort","$key","$_");
2384	    while (<$INPUT>) {
2385		tr/\015//d;
2386		last if (/^$prompt/);
2387		goto TOP if (! /^ /);
2388		next if (/\s*(rx|tx)speed \d+/);
2389		next if (/^ length /);	# kill length on serial lines
2390		next if (/^ width /);	# kill width on serial lines
2391		$lineauto = 0 if (/^[^ ]/);
2392		$lineauto = 1 if /^ modem auto/;
2393		/^ speed / && $lineauto	&& next; # kill speed on serial lines
2394		if (/^(\s+password) \d+ / && $filter_pwds >= 1) {
2395		    $_ = "!$1 <removed>\n";
2396		}
2397		ProcessHistory("LINE","keysort","$key","$_");
2398	    }
2399	}
2400	if (/^(enable )?(password|passwd)( level \d+)? / && $filter_pwds >= 1) {
2401	    ProcessHistory("ENABLE","","","!$1$2$3 <removed>\n");
2402	    next;
2403	}
2404	if (/^(enable secret) / && $filter_pwds >= 2) {
2405	    ProcessHistory("ENABLE","","","!$1 <removed>\n");
2406	    next;
2407	}
2408	if (/^username (\S+)(\s.*)? secret /) {
2409	    if ($filter_pwds >= 2) {
2410		ProcessHistory("USER","keysort","$1",
2411			       "!username $1$2 secret <removed>\n");
2412	    } else {
2413		ProcessHistory("USER","keysort","$1","$_");
2414	    }
2415	    next;
2416	}
2417	if (/^username (\S+)(\s.*)? password ((\d) \S+|\S+)/) {
2418	    if ($filter_pwds >= 2) {
2419		ProcessHistory("USER","keysort","$1",
2420			       "!username $1$2 password <removed>\n");
2421	    } elsif ($filter_pwds >= 1 && $4 ne "5"){
2422		ProcessHistory("USER","keysort","$1",
2423			       "!username $1$2 password <removed>\n");
2424	    } else {
2425		ProcessHistory("USER","keysort","$1","$_");
2426	    }
2427	    next;
2428	}
2429	# cisco AP w/ IOS
2430	if (/^(wlccp \S+ username (\S+)(\s.*)? password) (\d \S+|\S+)/) {
2431	    if ($filter_pwds >= 1) {
2432		ProcessHistory("USER","keysort","$2","!$1 <removed>\n");
2433	    } else {
2434		ProcessHistory("USER","keysort","$2","$_");
2435	    }
2436	    next;
2437	}
2438	# filter auto "rogue ap" configuration lines
2439	/^rogue ap classify / && next;
2440	if (/^( set session-key (in|out)bound ah \d+ )/ && $filter_pwds >= 1) {
2441	    ProcessHistory("","","","!$1<removed>\n");
2442	    next;
2443	}
2444	if (/^( set session-key (in|out)bound esp \d+ (authenticator|cypher) )/
2445	    && $filter_pwds >= 1) {
2446	    ProcessHistory("","","","!$1<removed>\n");
2447	    next;
2448	}
2449	if (/^(\s*)password / && $filter_pwds >= 1) {
2450	    ProcessHistory("LINE-PASS","","","!$1password <removed>\n");
2451	    next;
2452	}
2453	if (/^(\s*)secret / && $filter_pwds >= 2) {
2454	    ProcessHistory("LINE-PASS","","","!$1secret <removed>\n");
2455	    next;
2456	}
2457	if (/^\s*(.*?neighbor.*?) (\S*) password / && $filter_pwds >= 1) {
2458	    ProcessHistory("","","","! $1 $2 password <removed>\n");
2459	    next;
2460	}
2461	if (/^(\s*ppp .* hostname) .*/ && $filter_pwds >= 1) {
2462	    ProcessHistory("","","","!$1 <removed>\n"); next;
2463	}
2464	if (/^(\s*ppp .* password) \d .*/ && $filter_pwds >= 1) {
2465	    ProcessHistory("","","","!$1 <removed>\n"); next;
2466	}
2467	if (/^(ip ftp password) / && $filter_pwds >= 1) {
2468	    ProcessHistory("","","","!$1 <removed>\n"); next;
2469	}
2470	if (/^( ip ospf authentication-key) / && $filter_pwds >= 1) {
2471	    ProcessHistory("","","","!$1 <removed>\n"); next;
2472	}
2473	# isis passwords appear to be completely plain-text
2474	if (/^\s+isis password (\S+)( .*)?/ && $filter_pwds >= 1) {
2475	    ProcessHistory("","","","!isis password <removed>$2\n"); next;
2476	}
2477	if (/^\s+(domain-password|area-password) (\S+)( .*)?/
2478							&& $filter_pwds >= 1) {
2479	    ProcessHistory("","","","!$1 <removed>$3\n"); next;
2480	}
2481	# this is reversable, despite 'md5' in the cmd
2482	if (/^( ip ospf message-digest-key \d+ md5) / && $filter_pwds >= 1) {
2483	    ProcessHistory("","","","!$1 <removed>\n"); next;
2484	}
2485	# this is also reversable, despite 'md5 encrypted' in the cmd
2486	if (/^(  message-digest-key \d+ md5 (7|encrypted)) /
2487	    && $filter_pwds >= 1) {
2488	    ProcessHistory("","","","!$1 <removed>\n"); next;
2489	}
2490	if (/^((crypto )?isakmp key) (\d )?\S+ / && $filter_pwds >= 1) {
2491	    ProcessHistory("","","","!$1 <removed> $'"); next;
2492	}
2493	# filter HSRP passwords
2494	if (/^(\s+standby \d+ authentication) / && $filter_pwds >= 1) {
2495	    ProcessHistory("","","","!$1 <removed>\n"); next;
2496	}
2497	# this appears in "measurement/sla" images
2498	if (/^(\s+key-string \d?)/ && $filter_pwds >= 1) {
2499	    ProcessHistory("","","","!$1 <removed>\n"); next;
2500	}
2501	if (/^( l2tp tunnel \S+ password)/ && $filter_pwds >= 1) {
2502	    ProcessHistory("","","","!$1 <removed>\n"); next;
2503	}
2504	# l2tp-class secret
2505	if (/^( digest secret 7?)/ && $filter_pwds >= 1) {
2506	    ProcessHistory("","","","!$1 <removed>\n"); next;
2507	}
2508	# i am told these are plain-text on the PIX
2509	if (/^(vpdn username (\S+) password)/) {
2510	    if ($filter_pwds >= 1) {
2511		ProcessHistory("USER","keysort","$2","!$1 <removed>\n");
2512	    } else {
2513		ProcessHistory("USER","keysort","$2","$_");
2514	    }
2515	    next;
2516	}
2517	# ASA/PIX keys in more system:running-config
2518	if (/^(( ikev2)? (local|remote)-authentication pre-shared-key ).*/ &&
2519	    $filter_pwds >= 1) {
2520	    ProcessHistory("","","","!$1 <removed> $'"); next;
2521	}
2522	# ASA/PIX keys in more system:running-config
2523	if (/^(( ikev1)? pre-shared-key | key |failover key ).*/ &&
2524	    $filter_pwds >= 1) {
2525	    ProcessHistory("","","","!$1 <removed> $'"); next;
2526	}
2527	# ASA/PIX keys in more system:running-config
2528	if (/(\s+ldap-login-password )\S+(.*)/ && $filter_pwds >= 1) {
2529	    ProcessHistory("","","","!$1 <removed> $'"); next;
2530	}
2531	# filter WPA password such as on cisco 877W ISR
2532	if (/^\s+(wpa-psk ascii|hex \d) / && $filter_pwds >= 1) {
2533	    ProcessHistory("","","","!$1 <removed>\n"); next;
2534	}
2535	#
2536	if (/^( cable shared-secret )/ && $filter_pwds >= 1) {
2537	    ProcessHistory("","","","!$1 <removed>\n");
2538	    next;
2539	}
2540	/fair-queue individual-limit/ && next;
2541	# sort ip explicit-paths.
2542	if (/^ip explicit-path name (\S+)/) {
2543	    my($key) = $1;
2544	    my($expath) = $_;
2545	    while (<$INPUT>) {
2546		tr/\015//d;
2547		last if (/^$prompt/ || ! /^(ip explicit-path name |[ !])/);
2548		if (/^ip explicit-path name (\S+)/) {
2549		    ProcessHistory("EXPATH","keysort","$key","$expath");
2550		    $key = $1;
2551		    $expath = $_;
2552		} else  {
2553		    $expath .= $_;
2554		}
2555	    }
2556	    ProcessHistory("EXPATH","keysort","$key","$expath");
2557	}
2558	# sort route-maps
2559	if (/^route-map (\S+)/) {
2560	    my($key) = $1;
2561	    my($routemap) = $_;
2562	    while (<$INPUT>) {
2563		tr/\015//d;
2564		last if (/^$prompt/ || ! /^(route-map |[ !])/);
2565		if (/^route-map (\S+)/) {
2566		    ProcessHistory("ROUTEMAP","keysort","$key","$routemap");
2567		    $key = $1;
2568		    $routemap = $_;
2569		} else  {
2570		    $routemap .= $_;
2571		}
2572	    }
2573	    ProcessHistory("ROUTEMAP","keysort","$key","$routemap");
2574	}
2575	# filter out any RCS/CVS tags to avoid confusing local CVS storage
2576	s/\$(Revision|Id):/ $1:/;
2577	# order access-lists
2578	/^access-list\s+(\d\d?)\s+(\S+)\s+(\S+)/ &&
2579	    ProcessHistory("ACL $1 $2","$aclsort","$3","$_") && next;
2580	# order extended access-lists
2581	if ($aclfilterseq) {
2582	/^access-list\s+(\d\d\d)\s+(\S+)\s+(\S+)\s+host\s+(\S+)/ &&
2583	    ProcessHistory("EACL $1 $2","$aclsort","$4","$_") && next;
2584	/^access-list\s+(\d\d\d)\s+(\S+)\s+(\S+)\s+(\d\S+)/ &&
2585	    ProcessHistory("EACL $1 $2","$aclsort","$4","$_") && next;
2586	/^access-list\s+(\d\d\d)\s+(\S+)\s+(\S+)\s+any/ &&
2587	    ProcessHistory("EACL $1 $2","$aclsort","0.0.0.0","$_") && next;
2588	}
2589	if ($aclfilterseq) {
2590	    /^ip(v6)? prefix-list\s+(\S+)\s+seq\s+(\d+)\s+(permit|deny)\s+(\S+)(.*)/
2591		&& ProcessHistory("PACL $2 $4","$aclsort","$5",
2592				  "ip$1 prefix-list $2 $4 $5$6\n")
2593		&& next;
2594	}
2595	# sort ipv{4,6} access-lists
2596	if ($aclfilterseq && /^ipv(4|6) access-list (\S+)\s*$/) {
2597	    my($nlri, $key) = ($1, $2);
2598	    my($seq, $cmd);
2599	    ProcessHistory("ACL $nlri $key","","","$_");
2600	    while (<$INPUT>) {
2601		tr/\015//d;
2602		last if (/^$prompt/ || /^\S/);
2603		# ipv4 access-list name
2604		#  remark NTP
2605   		#  deny ipv4 host 224.0.1.1 any
2606		#  deny ipv4 239.0.0.0 0.255.255.255 any
2607		#  permit udp any eq 123 any
2608		#  permit ipv4 nnn.nnn.nnn.nnn/nn any
2609		#  permit nnn.nnn.nnn.nnn/nn
2610		# ipv6 access-list name
2611		#  permit ipv6 host 2001:nnnn::nnnn any
2612		#  permit ipv6 2001:nnn::/nnn any
2613		#  permit 2001:nnnn::/64 any
2614		#  permit udp any eq 123 any
2615		#
2616		# line might begin with " sequence nnn permit ..."
2617		s/^\s+(sequence (\d+)) / /;
2618		my($seq) = $1;
2619		my($cmd, $resid) = ($_ =~ /^\s+(\w+) (.+)/);
2620		if ($cmd =~ /(permit|deny)/) {
2621		    my($ip);
2622		    my(@w) = ($resid =~ /(\S+) (\S+) (\S+\s)?(.+)/);
2623		    for (my($i) = 0; $i < $#w; $i++) {
2624			if ($w[$i] eq "any") {
2625			    if ($nlri eq "ipv4") {
2626				$ip = "255.255.255.255/32";
2627			    } else {
2628				$ip = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128";
2629			    }
2630			    last;
2631			} elsif ($w[$i] =~ /^[:0-9]/ ||
2632				 $2[$i] =~ /^[a-fA-F]{1,4}:/) {
2633			    $ip = $w[$i];
2634			    $ip =~ s/\s+$//;		# trim trailing WS
2635			    last;
2636			}
2637		    }
2638		    ProcessHistory("ACL $nlri $key $cmd", "$aclsort", "$ip",
2639				   " $cmd $resid\n");
2640		} else {
2641		    ProcessHistory("ACL $nlri $key $cmd", "", "",
2642				   " $cmd $resid\n");
2643		}
2644	    }
2645	}
2646	# order arp lists
2647	/^arp\s+(\d+\.\d+\.\d+\.\d+)\s+/ &&
2648	    ProcessHistory("ARP","$aclsort","$1","$_") && next;
2649	# order logging statements
2650	/^logging (\d+\.\d+\.\d+\.\d+)/ &&
2651	    ProcessHistory("LOGGING","ipsort","$1","$_") && next;
2652	# order/prune snmp-server host statements
2653	# we only prune lines of the form
2654	# snmp-server host a.b.c.d <community>
2655	if (/^snmp-server host (\d+\.\d+\.\d+\.\d+) /) {
2656	    if ($filter_commstr) {
2657		my($ip) = $1;
2658		my($line) = "snmp-server host $ip";
2659		my(@tokens) = split(' ', $');
2660		my($token);
2661		while ($token = shift(@tokens)) {
2662		    if ($token eq 'version') {
2663			$line .= " " . join(' ', ($token, shift(@tokens)));
2664			if ($token eq '3') {
2665			    $line .= " " . join(' ', ($token, shift(@tokens)));
2666			}
2667		    } elsif ($token eq 'vrf') {
2668			$line .= " " . join(' ', ($token, shift(@tokens)));
2669		    } elsif ($token =~ /^(informs?|traps?|(no)?auth)$/) {
2670			$line .= " " . $token;
2671		    } else {
2672			$line = "!$line " . join(' ', ("<removed>",
2673						 join(' ',@tokens)));
2674			last;
2675		    }
2676		}
2677		ProcessHistory("SNMPSERVERHOST","ipsort","$ip","$line\n");
2678	    } else {
2679		ProcessHistory("SNMPSERVERHOST","ipsort","$1","$_");
2680	    }
2681	    next;
2682	}
2683	# For ASA version 8.x and higher, the format changed a little. It is
2684	# 'snmp-server host {interface {hostname | ip_address}} [trap | poll]
2685	# [community  0 | 8 community-string] [version {1 | 2c | 3 username}]
2686	# [udp-port port] '
2687	if (/^(snmp-server .*community) ([08] )?(\S+)/) {
2688	    if ($filter_commstr) {
2689		ProcessHistory("SNMPSERVERCOMM","keysort","$_",
2690			       "!$1 <removed>$'") && next;
2691	    } else {
2692		ProcessHistory("SNMPSERVERCOMM","keysort","$_","$_") && next;
2693	    }
2694	}
2695	# prune tacacs/radius server keys
2696	if (/^((tacacs|radius)-server\s(\w*[-\s(\s\S+])*\s?key) (\d )?\S+/
2697	    && $filter_pwds >= 1) {
2698	    ProcessHistory("","","","!$1 <removed>$'"); next;
2699	}
2700	# order clns host statements
2701	/^clns host \S+ (\S+)/ &&
2702	    ProcessHistory("CLNS","keysort","$1","$_") && next;
2703	# order alias statements
2704	/^alias / && ProcessHistory("ALIAS","keysort","$_","$_") && next;
2705	# delete ntp auth password - this md5 is a reversable too
2706	if (/^(ntp authentication-key \d+ md5) / && $filter_pwds >= 1) {
2707	    ProcessHistory("","","","!$1 <removed>\n"); next;
2708	}
2709	# order ntp peers/servers
2710	if (/^ntp (server|peer) (\d+)\.(\d+)\.(\d+)\.(\d+)/) {
2711	    my($sortkey) = sprintf("$1 %03d%03d%03d%03d",$2,$3,$4,$5);
2712	    ProcessHistory("NTP","keysort",$sortkey,"$_");
2713	    next;
2714	}
2715	# order ip host statements
2716	/^ip host (\S+) / &&
2717	    ProcessHistory("IPHOST","keysort","$1","$_") && next;
2718	# order ip nat source static statements
2719	/^ip nat (\S+) source static (\S+)/ &&
2720	    ProcessHistory("IP NAT $1","ipsort","$2","$_") && next;
2721	# order atm map-list statements
2722	/^\s+ip\s+(\d+\.\d+\.\d+\.\d+)\s+atm-vc/ &&
2723	    ProcessHistory("ATM map-list","ipsort","$1","$_") && next;
2724	# order ip rcmd lines
2725	/^ip rcmd/ && ProcessHistory("RCMD","keysort","$_","$_") && next;
2726
2727	# system controller
2728	/^syscon address (\S*) (\S*)/ &&
2729	    ProcessHistory("","","","!syscon address $1 <removed>\n") &&
2730	    next;
2731	if (/^syscon password (\S*)/ && $filter_pwds >= 1) {
2732	    ProcessHistory("","","","!syscon password <removed>\n");
2733	    next;
2734	}
2735
2736	/^ *Cryptochecksum:/ && next;
2737
2738	# catch anything that wasnt matched above.
2739	ProcessHistory("","","","$_");
2740	# end of config.  the ": " game is for the PIX
2741	if (/^(: +)?end$/) {
2742	    $found_end = 1;
2743	    return(0);
2744	}
2745    }
2746    # The ContentEngine lacks a definitive "end of config" marker.  If we
2747    # know that it is a CE, SAN, or NXOS and we have seen at least 5 lines
2748    # of write term output, we can be reasonably sure that we have the config.
2749    if (($type eq "CE" || $type eq "SAN" || $type eq "NXOS") && $linecnt > 5) {
2750	$found_end = 1;
2751	return(0);
2752    }
2753
2754    return(0);
2755}
2756
27571;
2758