3com BLS switches (2948) from Diego Ercolani diego.ercolani at ssis.sm
in msg
http://www.shrubbery.net/pipermail/rancid-discuss/2009-June/004005.html

Files rancid-2.3.2.orig/bin/.3cblsrancid.in.swp and rancid-2.3.2/bin/.3cblsrancid.in.swp differ
diff -Nur rancid-2.3.2.orig/bin/3cblslogin.in rancid-2.3.2/bin/3cblslogin.in
--- rancid-2.3.2.orig/bin/3cblslogin.in	1970-01-01 01:00:00.000000000 +0100
+++ rancid-2.3.2/bin/3cblslogin.in	2009-06-12 15:02:43.000000000 +0200
@@ -0,0 +1,570 @@
+#! @EXPECT_PATH@ --
+##
+## $Id: 3cblslogin.in,v 1.0 2009/06/4 18:59:04 heas Exp $
+## based on clogin 1.136
+## 
+## @PACKAGE@ @VERSION@
+## Copyright (c) 1997-2009 by Terrapin Communications, Inc.
+## copyright (c) 2009 Diego Ercolani diego.ercolani at ssis.sm
+## All rights reserved.
+##
+## This code is derived from software contributed to and maintained by
+## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
+## Pete Whiting, Austin Schutz, and Andrew Fort.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions
+## are met:
+## 1. Redistributions of source code must retain the above copyright
+##    notice, this list of conditions and the following disclaimer.
+## 2. Redistributions in binary form must reproduce the above copyright
+##    notice, this list of conditions and the following disclaimer in the
+##    documentation and/or other materials provided with the distribution.
+## 3. All advertising materials mentioning features or use of this software
+##    must display the following acknowledgement:
+##        This product includes software developed by Terrapin Communications,
+##        Inc. and its contributors for RANCID.
+## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
+##    contributors may be used to endorse or promote products derived from
+##    this software without specific prior written permission.
+## 5. It is requested that non-binding fixes and modifications be contributed
+##    back to Terrapin Communications, Inc.
+##
+## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
+## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+## PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
+## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+## POSSIBILITY OF SUCH DAMAGE.
+# 
+#  The expect login scripts were based on Erik Sherk's gwtn, by permission.
+# 
+# 3cblslogin - 3com Basic Line Switches configuration dumper
+#
+# used to implement some glue to the 3comBLS2948 and probably
+# other Baseline Switches (?) and enable download of configuration and other
+# information that are normally reachable only via
+# web
+#
+# The default username password is the same as the vty password.
+#
+
+package require http
+
+# Usage line
+set usage "Usage: $argv0 \[-dSV\] \[-autoenable\] \[-noenable\] \[-c command\] \
+\[-Evar=x\] \[-e enable-password\] \[-f 3cblsloginrc-file\] \[-p user-password\] \
+\[-s script-file\] \[-t timeout\] \[-u username\] \
+\[-v vty-password\] \[-w enable-username\] \[-x command-file\] \
+\[-y ssh_cypher_type\] router \[router...\]\n"
+
+# env(CLOGIN) may contain:
+#	x == do not set xterm banner or name
+
+# Password file
+set password_file $env(HOME)/.cloginrc
+# Default is to login to the router
+set do_command 0
+set do_script 0
+# The default is to automatically enable
+set avenable 1
+# The default is that you login non-enabled (tacacs can have you login already
+# enabled)
+set avautoenable 0
+# The default is to look in the password file to find the passwords.  This
+# tracks if we receive them on the command line.
+set do_passwd 1
+set do_enapasswd 1
+# Save config, if prompted
+set do_saveconfig 0
+#
+# setta la velocità di "digitazione"
+#set send_human {default word variability min max}
+#viene applicato con send -h
+set send_human {.1 .2 1 .1 .3}
+# Error tag prefix recognized by 3cblsrancid in the output stream
+set ERRORTAG "Error:"
+# Command Prompt (virtual) recognized by 3cblsrancid to process command
+set COMMANDPROMPT "##PROMPT## :"
+
+proc GetCookie { host login } {
+set in_proc 1
+ # Get Login Cookie
+ set tok [::http::geturl http://$host/login.cgi -query $login]
+ upvar #0 $tok state
+ foreach { name value } $state(meta) {
+   if { $name eq "Set-Cookie" } {
+          lappend cookies [lindex [split $value {;}] 0]
+   }
+ }
+ ::http::cleanup $tok
+ #puts "$cookies"
+ set in_proc 0
+ return $cookies
+}
+
+proc GetConf { host cookies } {
+set in_proc 1
+  global COMMANDPROMPT
+  # Get Configuration
+  set tok2 [::http::geturl http://$host/3comranger48.cfg -headers [list Cookie [join $cookies {;}]]]
+  upvar #0 $tok2 state
+  #parray $tok2
+  #foreach keyitem [array names state] {
+  #  puts "$keyitem=$state($keyitem)"
+  #}
+  #puts $state(body)
+  set body $state(body)
+  #puts $body
+  ::http::cleanup $tok2
+  # $COMMANDPROMPT is opening and closing each
+  # configuration dump to permit parsing
+  # to 3cblsrancid these tags leave 3cblsrancid
+  # happy :-)
+  puts "$COMMANDPROMPT GetConf"
+  puts $body
+  puts ": end"
+  puts "$COMMANDPROMPT"
+  set in_proc 0
+  return $body
+}
+
+proc GetArpTable { host cookies } {
+  set in_proc 1
+  global COMMANDPROMPT
+  # Get ARP Table
+  set tok2 [::http::geturl http://$host/aos3com_addrtable_states.cgi -headers [list Cookie [join $cookies {;}]]]
+  upvar #0 $tok2 state
+  #parray $tok2
+  #foreach keyitem [array names state] {
+  #  puts "$keyitem=$state($keyitem)"
+  #}
+  set lines [split $state(body) "\r\n"]
+  foreach i $lines {
+   if [regexp {<option>(.*)</option>} $i ignore option] {
+     #puts "$option"
+     append options "$option\n"
+   }
+  }
+  ::http::cleanup $tok2
+  # $COMMANDPROMPT is opening and closing each
+  # configuration dump to permit parsing
+  # to 3cblsrancid
+  puts "$COMMANDPROMPT GetArpTable"
+  puts $options
+  puts "$COMMANDPROMPT"
+  set in_proc 0
+  return $options
+}
+
+proc ClearCookie { host cookies } {
+  set in_proc 1
+  # Logout Gacefully
+  set tok2 [::http::geturl http://$host/logout.cgi -headers [list Cookie [join $cookies {;}]]]
+  upvar #0 $tok2 state
+  #parray $tok2
+  #foreach keyitem [array names state] {
+  #  puts "$keyitem=$state($keyitem)"
+  #}
+  ::http::cleanup $tok2
+  set in_proc 0
+}
+
+
+
+
+
+# Find the user in the ENV, or use the unix userid.
+if {[ info exists env(CISCO_USER) ]} {
+    set default_user $env(CISCO_USER)
+} elseif {[ info exists env(USER) ]} {
+    set default_user $env(USER)
+} elseif {[ info exists env(LOGNAME) ]} {
+    set default_user $env(LOGNAME)
+} else {
+    # This uses "id" which I think is portable.  At least it has existed
+    # (without options) on all machines/OSes I've been on recently -
+    # unlike whoami or id -nu.
+    if [ catch {exec id} reason ] {
+	send_error "\n$ERRORTAG could not exec id: $reason\n"
+	exit 1
+    }
+    regexp {\(([^)]*)} "$reason" junk default_user
+}
+if {[ info exists env(CLOGINRC) ]} {
+    set password_file $env(CLOGINRC)
+}
+
+# Process the command line
+for {set i 0} {$i < $argc} {incr i} {
+    set arg [lindex $argv $i]
+
+    switch  -glob -- $arg {
+	# Expect debug mode
+	-d* {
+	    exp_internal 1
+	# Username
+	} -u* {
+	    if {! [regexp .\[uU\](.+) $arg ignore user]} {
+		incr i
+		set username [ lindex $argv $i ]
+	    }
+	# VTY Password
+	} -p* {
+	    if {! [regexp .\[pP\](.+) $arg ignore userpasswd]} {
+		incr i
+		set userpasswd [ lindex $argv $i ]
+	    }
+	    set do_passwd 0
+	# VTY Password
+	} -v* {
+	    if {! [regexp .\[vV\](.+) $arg ignore passwd]} {
+		incr i
+		set passwd [ lindex $argv $i ]
+	    }
+	    set do_passwd 0
+	# Version string
+	} -V* {
+	    send_user "@PACKAGE@ @VERSION@\n"
+	    exit 0
+	# Enable Username
+	} -w* {
+	    send_user "(-w) Not Implemented\n"
+	    exit 1
+	    if {! [regexp .\[wW\](.+) $arg ignore enauser]} {
+		incr i
+		set enausername [ lindex $argv $i ]
+	    }
+	# Environment variable to pass to -s scripts
+	} -E* {
+	    if {[regexp .\[E\](.+)=(.+) $arg ignore varname varvalue]} {
+		set E$varname $varvalue
+	    } else {
+		send_user "\n$ERRORTAG invalid format for -E in $arg\n"
+		exit 1
+	    }
+	# Enable Password
+	} -e* {
+	    send_user "(-e) Not Implemented\n"
+	    exit 1
+	    if {! [regexp .\[e\](.+) $arg ignore enapasswd]} {
+		incr i
+		set enapasswd [ lindex $argv $i ]
+	    }
+	    set do_enapasswd 0
+	# Command to run.
+	} -c* {
+	    if {! [regexp .\[cC\](.+) $arg ignore command]} {
+		incr i
+		set command [ lindex $argv $i ]
+	    }
+	    set do_command 1
+	# Expect script to run.
+	} -s* {
+	    if {! [regexp .\[sS\](.+) $arg ignore sfile]} {
+		incr i
+		set sfile [ lindex $argv $i ]
+	    }
+	    if { ! [ file readable $sfile ] } {
+		send_user "\n$ERRORTAG Can't read $sfile\n"
+		exit 1
+	    }
+	    set do_script 1
+	# save config on exit
+	} -S* {
+	    set do_saveconfig 1
+	# 'ssh -c' cypher type
+	} -y* {
+	    send_user "(-y) Not Implemented\n"
+	    exit 1
+	    if {! [regexp .\[eE\](.+) $arg ignore cypher]} {
+		incr i
+		set cypher [ lindex $argv $i ]
+	    }
+	# alternate 3cblsloginrc file
+	} -f* {
+	    if {! [regexp .\[fF\](.+) $arg ignore password_file]} {
+		incr i
+		set password_file [ lindex $argv $i ]
+	    }
+	# Timeout
+	} -t* {
+	    if {! [regexp .\[tT\](.+) $arg ignore timeout]} {
+		incr i
+	        set timeout [ lindex $argv $i ]
+	    }
+	# Command file
+	} -x* {
+	    if {! [regexp .\[xX\](.+) $arg ignore cmd_file]} {
+		incr i
+		set cmd_file [ lindex $argv $i ]
+	    }
+	    if [ catch {set cmd_fd [open $cmd_file r]} reason ] {
+		send_user "\n$ERRORTAG $reason\n"
+		exit 1
+	    }
+	    set cmd_text [read $cmd_fd]
+	    close $cmd_fd
+	    set command [join [split $cmd_text \n] \;]
+	    set do_command 1
+	# Do we enable?
+	} -noenable {
+	    set avenable 0
+	# Does tacacs automatically enable us?
+	} -autoenable {
+	    set avautoenable 1
+	    set avenable 0
+	} -* {
+	    send_user "\n$ERRORTAG Unknown argument! $arg\n"
+	    send_user $usage
+	    exit 1
+	} default {
+	    break
+	}
+    }
+}
+# Process routers...no routers listed is an error.
+if { $i == $argc } {
+    send_user "\n$ERRORTAG $usage"
+}
+
+# Only be quiet if we are running a script (it can log its output
+# on its own)
+if { $do_script } {
+    log_user 0
+} else {
+    log_user 1
+}
+
+#
+# Done configuration/variable setting.  Now run with it...
+#
+
+# Sets Xterm title if interactive...if its an xterm and the user cares
+proc label { host } {
+    global env
+    # if CLOGIN has an 'x' in it, don't set the xterm name/banner
+    if [info exists env(CLOGIN)] {
+	if {[string first "x" $env(CLOGIN)] != -1} { return }
+    }
+    # take host from ENV(TERM)
+    if [info exists env(TERM)] {
+	if [ regexp \^(xterm|vs) $env(TERM) ignore ] {
+	    send_user "\033]1;[lindex [split $host "."] 0]\a"
+	    send_user "\033]2;$host\a"
+	}
+    }
+}
+
+# This is a helper function to make the password file easier to
+# maintain.  Using this the password file has the form:
+# add password sl*	pete cow
+# add password at*	steve
+# add password *	hanky-pie
+proc add {var args} { global int_$var ; lappend int_$var $args}
+proc include {args} {
+    global env
+    regsub -all "(^{|}$)" $args {} args
+    if { [ regexp "^/" $args ignore ] == 0 } {
+	set args $env(HOME)/$args
+    }
+    source_password_file $args
+}
+
+proc find {var router} {
+    upvar int_$var list
+    if { [info exists list] } {
+	foreach line $list {
+	    if { [string match [lindex $line 0] $router ] } {
+		return [lrange $line 1 end]
+	    }
+	}
+    }
+    return {}
+}
+
+# Loads the password file.  Note that as this file is tcl, and that
+# it is sourced, the user better know what to put in there, as it
+# could install more than just password info...  I will assume however,
+# that a "bad guy" could just as easy put such code in the 3cblslogin
+# script, so I will leave .3cblsloginrc as just an extention of that script
+proc source_password_file { password_file } {
+    global env ERRORTAG
+    if { ! [file exists $password_file] } {
+	send_user "\n$ERRORTAG password file ($password_file) does not exist\n"
+	exit 1
+    }
+    file stat $password_file fileinfo
+    if { [expr ($fileinfo(mode) & 007)] != 0000 } {
+	send_user "\n$ERRORTAG $password_file must not be world readable/writable\n"
+	exit 1
+    }
+    if [ catch {source $password_file} reason ] {
+	send_user "\n$ERRORTAG $reason\n"
+	exit 1
+    }
+}
+
+## Log into the router.
+## returns: 0 on success, 1 on failure, -1 if rsh was used successfully
+#proc login { router user userpswd passwd enapasswd cmethod cyphertype } {
+# @@@@@@@@@@@@@@ substituted by GetCookie @@@@@@@@@@@@
+#
+#
+# Run commands given on the command line.
+# command are really "MetaCommand" as
+# switch doesn't have a CLI interface
+# Implemented Command are:
+# GetConf - Get current configuration
+# GetArpTable - Get current arp table without "LAG 1" as
+#               in our topology "LAG 1" is used for
+#               uplinks to root switches
+#
+proc run_commands { host cookie command } {
+    global in_proc platform COMMANDPROMPT ERRORTAG
+    set in_proc 1
+
+    if {[string length $cookie] < 10} {
+     # probably cookie is wrong
+     # I don't know how to check....
+     send_user "Cookie \"$cookie\" is wrong. "
+     exit -1
+    } 
+    set commands [split $command \;]
+    set num_commands [llength $commands]
+    # the pager can not be turned off on the PIX, so we have to look
+    # for the "More" prompt.  the extreme is equally obnoxious, with a
+    # global switch in the config.
+    for {set i 0} {$i < $num_commands} { incr i} {
+	# Main loop to send commands
+	# here we are probably just authenticated with a cookie
+        # $commands is a tcl list, not an array, we have to threat it as list
+        switch -nocase -glob [subst [lindex $commands $i]] {
+	 GetConf 	{ GetConf $host $cookie }
+	 GetArpTable 	{ GetArpTable $host $cookie }
+	 default	{ send_user "$ERRORTAG Command \"[subst [lindex $commands $i]]\" not implemented or Syntax Error in command" }
+	}
+    }
+    # give 3cblsrancid parser loop the end loop without errors
+    puts "$COMMANDPROMPT exit"
+    log_user 1
+    
+
+    ClearCookie $host $cookie
+    set in_proc 0
+}
+
+#
+# For each router... (this is main loop)
+#
+source_password_file $password_file
+set in_proc 0
+set exitval 0
+foreach router [lrange $argv $i end] {
+    set router [string tolower $router]
+    # attempt at platform switching.
+    set platform ""
+    #send_user -- "$router\n"
+
+    # Figure out passwords
+    if { $do_passwd } {
+      set pswd [find password $router]
+      if { [llength $pswd] == 0 } {
+	send_user -- "\n$ERRORTAG no password for $router in $password_file.\n"
+	continue
+      }
+      set passwd [join [lindex $pswd 0] ""]
+    } else {
+	set passwd $userpasswd
+    }
+
+    # Figure out username
+    if {[info exists username]} {
+      # command line username
+      set ruser $username
+    } else {
+      set ruser [join [find user $router] ""]
+      if { "$ruser" == "" } { set ruser $default_user }
+    }
+
+    # Figure out username's password (if different from the vty password)
+    if {[info exists userpasswd]} {
+      # command line username
+      set userpswd $userpasswd
+    } else {
+      set userpswd [join [find userpassword $router] ""]
+      if { "$userpswd" == "" } { set userpswd $passwd }
+    }
+
+
+    # Figure out connection method
+    set cmethod [find method $router]
+    if { "$cmethod" == "" } { set cmethod {{http}} 
+    } elseif { "$cmethod" == "http" } { set cmethod {{http}} 
+    } else { 
+      send_user "Method \"$cmethod\" to connect to switch $router not supported."
+      exit -1
+    }
+
+    # Login to the router
+    set rtime [expr {[clock seconds]*2}]
+    set login [::http::formatQuery username $ruser password $userpswd rtime $rtime]
+    catch { GetCookie $router $login } cookie err
+    #puts "->$cookie<-->$err<-"
+    if { [regexp errorcode $err ] } {
+      send_user "$ERRORTAG login to router $router\ndebug:$cookie\n"
+      continue
+    }
+
+    if { $do_command } {
+	if {[run_commands $router $cookie $command]} {
+	    incr exitval
+	    continue
+	}
+      } elseif { $do_script } {
+	send_user "$ERRORTAG Not verified: source file\n"
+	# If the prompt is (enable), then we are on a switch and the
+	# command is "set length 0"; otherwise its "terminal length 0".
+	source $sfile
+    } else {
+    	send_user "\033[32m$ERRORTAG Interactive console not implemented\033[m\n"
+	label $router
+	exit -1
+	#log_user 1
+	interact
+    }
+
+    # End of for each router
+    catch {wait};
+    sleep 0.3
+}
+exit $exitval
+
+#########################
+##########################
+##########################
+
+#set host ga2948b18.mgt.ssis
+#set cookie [GetCookie $host $login]
+##puts "Cookie=$cookie"
+#set configuration [GetConf $host $cookie]
+#set arptable [GetArp $host $cookie]
+#ClearCookie $host $cookie
+#
+#puts "Configuration:\n$configuration\nArpTable:"
+#foreach a $arptable {
+# if [regexp LAG\ 1 $a] {
+#  # if it is a "LAG 1" element in our topology
+#  # is an uplink versus root switches
+#  # so we don't consider as network clients
+#  # 
+#  continue
+# }
+# puts "$a"
+#}
+#
diff -Nur rancid-2.3.2.orig/bin/3cblsrancid.in rancid-2.3.2/bin/3cblsrancid.in
--- rancid-2.3.2.orig/bin/3cblsrancid.in	1970-01-01 01:00:00.000000000 +0100
+++ rancid-2.3.2/bin/3cblsrancid.in	2009-06-12 15:01:03.000000000 +0200
@@ -0,0 +1,329 @@
+#! @PERLV_PATH@
+##
+## $Id: 3cblsrancid.in,v 1.0 2009/06/10 15:19:00 heas Exp $
+## based on rancid 1.255 2009/04/20 19:56:27 heas Exp
+##
+## @PACKAGE@ @VERSION@
+## Copyright (c) 1997-2009 by Terrapin Communications, Inc.
+## All rights reserved.
+##
+## This code is derived from software contributed to and maintained by
+## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
+## Pete Whiting, Austin Schutz, and Andrew Fort.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions
+## are met:
+## 1. Redistributions of source code must retain the above copyright
+##    notice, this list of conditions and the following disclaimer.
+## 2. Redistributions in binary form must reproduce the above copyright
+##    notice, this list of conditions and the following disclaimer in the
+##    documentation and/or other materials provided with the distribution.
+## 3. All advertising materials mentioning features or use of this software
+##    must display the following acknowledgement:
+##        This product includes software developed by Terrapin Communications,
+##        Inc. and its contributors for RANCID.
+## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
+##    contributors may be used to endorse or promote products derived from
+##    this software without specific prior written permission.
+## 5. It is requested that non-binding fixes and modifications be contributed
+##    back to Terrapin Communications, Inc.
+##
+## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
+## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+## PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
+## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+## POSSIBILITY OF SUCH DAMAGE.
+#
+#  RANCID - Really Awesome New Cisco confIg Differ
+#
+# usage: 3cblsrancid [-dV] [-l] [-f filename | hostname]
+#
+use Getopt::Std;
+getopts('dflV');
+if ($opt_V) {
+    print "@PACKAGE@ @VERSION@\n";
+    exit(0);
+}
+$log = $opt_l;
+$debug = $opt_d;
+$file = $opt_f;
+$host = $ARGV[0];
+$clean_run = 0;
+$found_end = 0;
+$found_version = 0;
+$found_env = 0;
+$found_diag = 0;
+$timeo = 90;				# 3cblslogin timeout in seconds
+
+my(@commandtable, %commands, @commands);# command lists
+my($filter_pwds);			# password filtering mode
+
+# This routine is used to print out the router configuration
+sub ProcessHistory {
+    my($new_hist_tag,$new_command,$command_string,@string) = (@_);
+    if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command))
+	&& defined %history) {
+	print eval "$command \%history";
+	undef %history;
+    }
+    if (($new_hist_tag) && ($new_command) && ($command_string)) {
+	if ($history{$command_string}) {
+	    $history{$command_string} = "$history{$command_string}@string";
+	} else {
+	    $history{$command_string} = "@string";
+	}
+    } elsif (($new_hist_tag) && ($new_command)) {
+	$history{++$#history} = "@string";
+    } else {
+	print "@string";
+    }
+    $hist_tag = $new_hist_tag;
+    $command = $new_command;
+    1;
+}
+
+sub numerically { $a <=> $b; }
+
+# This is a sort routine that will sort numerically on the
+# keys of a hash as if it were a normal array.
+sub keynsort {
+    local(%lines) = @_;
+    local($i) = 0;
+    local(@sorted_lines);
+    foreach $key (sort numerically keys(%lines)) {
+	$sorted_lines[$i] = $lines{$key};
+	$i++;
+    }
+    @sorted_lines;
+}
+
+# This is a sort routine that will sort on the
+# keys of a hash as if it were a normal array.
+sub keysort {
+    local(%lines) = @_;
+    local($i) = 0;
+    local(@sorted_lines);
+    foreach $key (sort keys(%lines)) {
+	$sorted_lines[$i] = $lines{$key};
+	$i++;
+    }
+    @sorted_lines;
+}
+
+# This is a sort routine that will sort on the
+# values of a hash as if it were a normal array.
+sub valsort{
+    local(%lines) = @_;
+    local($i) = 0;
+    local(@sorted_lines);
+    foreach $key (sort values %lines) {
+	$sorted_lines[$i] = $key;
+	$i++;
+    }
+    @sorted_lines;
+}
+
+# This is a numerical sort routine (ascending).
+sub numsort {
+    local(%lines) = @_;
+    local($i) = 0;
+    local(@sorted_lines);
+    foreach $num (sort {$a <=> $b} keys %lines) {
+	$sorted_lines[$i] = $lines{$num};
+	$i++;
+    }
+    @sorted_lines;
+}
+
+# This is a sort routine that will sort on the
+# ip address when the ip address is anywhere in
+# the strings.
+sub ipsort {
+    local(%lines) = @_;
+    local($i) = 0;
+    local(@sorted_lines);
+    foreach $addr (sort sortbyipaddr keys %lines) {
+	$sorted_lines[$i] = $lines{$addr};
+	$i++;
+    }
+    @sorted_lines;
+}
+
+# These two routines will sort based upon IP addresses
+sub ipaddrval {
+    my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#);
+    $a[3] + 256 * ($a[2] + 256 * ($a[1] +256 * $a[0]));
+}
+sub sortbyipaddr {
+    &ipaddrval($a) <=> &ipaddrval($b);
+}
+
+# This routine parses "GetConf" (metacommand)
+sub GetConf {
+   print STDERR "    In GetConf: $_" if ($debug);
+   while (<INPUT>) {
+    tr/\015//d;
+    last if (/$prompt/);
+    next if (/^$/);
+    if (/^(: +)?end$/) {
+       $found_end = 1;
+       return(0);
+    }
+    ProcessHistory("","","","$_");
+   }
+   return(0);
+}
+
+# This routine parses "GetArpTable" (metacommand)
+sub GetArpTable {
+   print STDERR "    In GetArpTable: $_" if ($debug);
+   while (<INPUT>) {
+    tr/\015//d;
+    last if (/$prompt/);
+    next if (/LAG 1/);
+    next if (/^$/);
+    ProcessHistory("COMMENTS","keysort","IO","!ARP: $_");
+
+   }
+   return(0);
+}
+
+
+
+# dummy function
+sub DoNothing {print STDOUT;}
+
+# Main
+@commandtable = (
+	# router command       =>  Meta Command
+	{'GetConf'		=> 'GetConf'},
+	{'GetArpTable'			=> 'GetArpTable'},
+);
+# Use an array to preserve the order of the commands and a hash for mapping
+# commands to the subroutine and track commands that have been completed.
+@commands = map(keys(%$_), @commandtable);
+%commands = map(%$_, @commandtable);
+
+$cisco_cmds = join(";",@commands);
+$cmds_regexp = join("|",@commands);
+
+if (length($host) == 0) {
+    if ($file) {
+	print(STDERR "Too few arguments: file name required\n");
+	exit(1);
+    } else {
+	print(STDERR "Too few arguments: host name required\n");
+	exit(1);
+    }
+}
+open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";
+#Set OUTPUT as standard output stream
+select(OUTPUT);
+# make OUTPUT unbuffered if debugging
+if ($debug) { $| = 1; }
+
+if ($file) {
+    print STDERR "opening file $host\n" if ($debug);
+    print STDOUT "opening file $host\n" if ($log);
+    open(INPUT,"<$host") || die "open failed for $host: $!\n";
+} else {
+    print STDERR "executing 3cblslogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($debug);
+    print STDOUT "executing 3cblslogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($log);
+    if (defined($ENV{NOPIPE})) {
+	system "3cblslogin -t $timeo -c \"$cisco_cmds\" $host </dev/null > $host.raw 2>&1" || die "3cblslogin failed for $host: $!\n";
+	open(INPUT, "< $host.raw") || die "3cblslogin failed for $host: $!\n";
+    } else {
+	open(INPUT,"3cblslogin -t $timeo -c \"$cisco_cmds\" $host </dev/null |") || die "3cblslogin failed for $host: $!\n";
+    }
+}
+
+# determine ACL sorting mode
+if ($ENV{"ACLSORT"} =~ /no/i) {
+    $aclsort = "";
+}
+# determine community string filtering mode
+if (defined($ENV{"NOCOMMSTR"}) &&
+    ($ENV{"NOCOMMSTR"} =~ /yes/i || $ENV{"NOCOMMSTR"} =~ /^$/)) {
+    $filter_commstr = 1;
+} else {
+    $filter_commstr = 0;
+}
+# determine password filtering mode
+if ($ENV{"FILTER_PWDS"} =~ /no/i) {
+    $filter_pwds = 0;
+} elsif ($ENV{"FILTER_PWDS"} =~ /all/i) {
+    $filter_pwds = 2;
+} else {
+    $filter_pwds = 1;
+}
+
+ProcessHistory("COMMENTS","","","!RANCID-CONTENT-TYPE: 3com Baseline Switches\n!\n");
+TOP: while(<INPUT>) {
+    #print STDOUT "\033[32m$_\033[m" if ($debug);
+    tr/\015//d;
+    if (/^Error:/) {
+	#If encountered ERRORTAG then show reason and exit
+	print STDOUT ("$host 3cblslogin error: $_");
+	print STDERR ("$host 3cblslogin error: $_") if ($debug);
+	$clean_run = 0;
+	last;
+    }
+    if (/$prompt\s?exit$/) {
+        # if encountered (virtual) exit tag
+        $clean_run = 1;
+        last;
+    }
+
+    if (/\s*($cmds_regexp)\s*$/) {
+	$cmd = $1;
+	if (!defined($prompt)) {
+            # in rancid code prompt is extrapolated by a regular
+	    # expression: nice try but I'm not such an expert
+	    $prompt = "##PROMPT## :";
+	    print STDERR ("PROMPT MATCH: $prompt\n") if ($debug);
+	}
+	print STDERR ("HIT COMMAND:$_") if ($debug);
+	if (! defined($commands{$cmd})) {
+	    print STDERR "$host: found unexpected command - \"$cmd\"\n";
+	    $clean_run = 0;
+	    last TOP;
+	}
+	$rval = &{$commands{$cmd}};
+	delete($commands{$cmd});
+	if ($rval == -1) {
+	    $clean_run = 0;
+	    last TOP;
+	}
+    }
+}
+print STDOUT "Done $logincmd: $_\n" if ($log);
+# Flush History
+ProcessHistory("","","","");
+# Cleanup
+close(INPUT);
+close(OUTPUT);
+
+if (defined($ENV{NOPIPE})) {
+    unlink("$host.raw") if (! $debug);
+}
+
+# check for completeness
+if (scalar(%commands) || !$clean_run || !$found_end) {
+    if (scalar(%commands)) {
+	printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands)));
+	printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug);
+    }
+    if (!$clean_run || !$found_end) {
+	print STDOUT "$host: End of run not found\n";
+	print STDERR "$host: End of run not found\n" if ($debug);
+	system("/usr/bin/tail -1 $host.new");
+    }
+    unlink "$host.new" if (! $debug);
+}
diff -Nur rancid-2.3.2.orig/bin/Makefile.in rancid-2.3.2/bin/Makefile.in
--- rancid-2.3.2.orig/bin/Makefile.in	2009-06-12 14:56:39.000000000 +0200
+++ rancid-2.3.2/bin/Makefile.in	2009-06-12 15:01:03.000000000 +0200
@@ -60,7 +60,9 @@
 	$(srcdir)/rivrancid.in $(srcdir)/rrancid.in \
 	$(srcdir)/srancid.in $(srcdir)/tlogin.in $(srcdir)/tntlogin.in \
 	$(srcdir)/tntrancid.in $(srcdir)/trancid.in \
-	$(srcdir)/xrancid.in $(srcdir)/zrancid.in
+	$(srcdir)/xrancid.in $(srcdir)/zrancid.in \
+	$(srcdir)/3cblslogin.in $(srcdir)/3cblslogin.in 
+
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
 	$(top_srcdir)/configure.in
@@ -74,7 +76,8 @@
 	francid fnrancid hlogin hrancid htlogin htrancid jlogin \
 	jrancid jerancid mrancid mrvlogin mrvrancid fnlogin nlogin nrancid \
 	nslogin nsrancid nxrancid prancid rivlogin rivrancid rrancid \
-	srancid tlogin tntlogin tntrancid trancid xrancid zrancid
+	srancid tlogin tntlogin tntrancid trancid xrancid zrancid \
+	3cblslogin 3cblsrancid
 am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)"
 binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
 PROGRAMS = $(bin_PROGRAMS)
@@ -224,6 +227,7 @@
 	mrvlogin mrvrancid fnlogin nlogin nrancid nslogin nsrancid nxrancid \
 	par prancid rancid-fe rancid rivlogin rivrancid rrancid \
 	srancid tlogin tntlogin tntrancid trancid xrancid zrancid \
+	3cblslogin 3cblsrancid \
 	lg.cgi lgform.cgi rancid-cvs rancid-run
 EXTRA_DIST = lg.cgi.in lgform.cgi.in rancid-cvs.in rancid-run.in
 #dist_bin_SCRIPTS= $(bin_SCRIPTS:%=%.in)
@@ -388,6 +392,11 @@
 	cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
 zrancid: $(top_builddir)/config.status $(srcdir)/zrancid.in
 	cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+3cblslogin: $(top_builddir)/config.status $(srcdir)/3cblslogin.in
+	cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+3cblsrancid: $(top_builddir)/config.status $(srcdir)/3cblsrancid.in
+	cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
 install-binPROGRAMS: $(bin_PROGRAMS)
 	@$(NORMAL_INSTALL)
 	test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
diff -Nur rancid-2.3.2.orig/bin/rancid-fe.in rancid-2.3.2/bin/rancid-fe.in
--- rancid-2.3.2.orig/bin/rancid-fe.in	2009-04-17 00:05:51.000000000 +0200
+++ rancid-2.3.2/bin/rancid-fe.in	2009-06-12 15:01:03.000000000 +0200
@@ -80,7 +80,8 @@
     'riverstone'	=> 'rivrancid',
     'smc'		=> 'srancid',
     'tnt'		=> 'tntrancid',
-    'zebra'		=> 'zrancid'
+    'zebra'		=> 'zrancid',
+    '3combls'		=> '3cblsrancid'
 );
 
 if ($vendortable{$vendor} eq "") {
diff -Nur rancid-2.3.2.orig/configure rancid-2.3.2/configure
--- rancid-2.3.2.orig/configure	2009-06-12 14:56:39.000000000 +0200
+++ rancid-2.3.2/configure	2009-06-12 15:01:03.000000000 +0200
@@ -7290,6 +7290,8 @@
 
 ac_config_files="$ac_config_files bin/zrancid"
 
+ac_config_files="$ac_config_files bin/3cblslogin bin/3cblsrancid"
+
 ac_config_files="$ac_config_files share/rtrfilter"
 
 
