Mailing List Archive

string compare vs string equal
This is a follow-up to my previous the other day regarding the $platform selection, but it's a general tcl question more than anything (I just about muddle through with tcl, rancid is my only exposure to it).

I commented that there was rudimentary detection for "extreme" as a platform. In clogin, there are tests this like:

if { [string compare "extreme" "$platform"] } {

(string compare is used in lots of other places too, see below).

Should this not be "string equal"? If I do this:

set platform "cmw"

if { [string compare "extreme" "$platform"] } {
send_user "true $platform\n"
} else {
send_user "false $platform\n"

then string compare looks at "extreme" and "cmw" and returns "true cmw" - not what I would expect.

Docs say "string compare" returns 0 if equal, -1 if string1 comes before string2, else 1. But in tcl false is 0, and any other non-zero is true. So it doesn't seem that string compare is suitable, since it will return true in circumstances when the strings are not equal (like when returning -1). Do I misunderstand why compare is being used here?

I fell over this when my platform selection was not working. To make the code work, I replaced:

if { [string compare "extreme" "$platform"] } {
send -h "exit\r"
} else {
send -h "quit\r"


if {[string equal "extreme" "$platform"]} {
send -h "exit\r"
} elseif {[string equal "cmw" "$platform"] || [string equal "cmwbaseline" "$platform"]} {
send -h "$exitcmd\r"
} else {
send -h "quit\r"

The other common appearance of "string compare" across the expect scripts is in lines like below, but I haven't studied these or the other "extreme" cases to see if they make sense or just work by chance ... I do not have cisco or extreme to test against.

} elseif ![string compare $prog "rsh"] {

} elseif ![string compare $prog "ssh"] {

Ultimately I'm working towards a set of changes to better support Comware-based gear which is often requested (e.g and on this list), which ideally would be fully incorporated into clogin (if acceptable, using the platform selection), or otherwise be as few changes as needed to it and maintained alongside. Which means tidying up these bits and pieces first to make life easier for tracking.

I'm personally in favour of clogin being a bit more complex, but thereby being able to support more platforms with a few modifications, so there need be fewer *login scripts to maintain. As shown above, I've also abstracted the exit command to a variable set by platform selection. The code could be tidied further incorporating this.

Happy for me to submit pull requests via github for consideration, heasley?


. . . . . . . . . . . . . . . . . . . . . . . . .

Jethro R Binks, Network Manager,

Information Services Directorate, University Of Strathclyde, Glasgow, UK

The University of Strathclyde is a charitable body, registered in Scotland, number SC015263.

From: Jethro Binks
Sent: 23 January 2022 12:57
To: <>
Subject: $platform value

There's some rudimentary code in clogin/etc that attempts to do things slightly differently depending on the value of $platform. The setting for $platform appears to be magically divined in some cases (primarily noting "extreme" in clogin).

I'd like this to be user-controllable, so below patch against rancid 3.13 adds -P as an option for clogin, and it is also setable via cloginrc. But the patch doesn't make any changes to any current magic divination behaviour.

My use case for this is some very similar switches, but some particular submodels need a bunch of extra commands sending before rancid will work properly, and it isn't possible/reliable to magically divine which they are. So I just want to be able to add lines to cloginrc to their entries to give rancid a helping hand, eg:

add platform 10.x.y.z cmwbaseline

(or via command line).

If this can be adopted, it would make the changes I need to maintain locally a lot easier and I might be able to contribute back more.

Also, I think it might start to lead to other benefits. One of the problems I find with rancid is that there are so many *login scripts, and if you want to develop support for some new hardware, it's difficult to know which to pick to start from. I assume clogin is the "most" maintained, but that's also the most complicated and if you need something simpler it is hard to strip back from there. It isn't always clear if generic changes in clogin get transferred to the other ones .. and let's face it most of them are 80% the same content, reading the command-line and cloginrc, running ssh, etc. By introducing a bit more intelligence, it might be possible to reduce the number, by adding a few platform-dependent conditional operations to some common ones where are just a few tweaks need making.


--- clogin.orig
+++ clogin 2022-01-23 12:50:26.007313000 +0000
@@ -59,12 +59,16 @@
# Some CLIs having problems if we write too fast (Extreme, PIX, Cat)
set send_human {.2 .1 .4 .2 1}

+# Initialise the platform to a reasonable default for this type of hardware
+# "" is a reasonable default if you don't need to use platform switching
+set defaultplatform ""
# Usage line
set usage "Usage: $argv0 \[-dhiSV\] \[-m|M\] \[-autoenable\] \[-noenable\] \
\[-c command\] \[-Evar=x\] \[-e enable-password\] \[-f cloginrc-file\] \
\[-p user-password\] \[-r passphrase\] \[-s script-file\] \[-t timeout\] \
\[-u username\] \[-v vty-password\] \[-w enable-username\] \[-x command-file\] \
-\[-y ssh_cypher_type\] router \[router...\]\n"
+\[-y ssh_cypher_type\] \[-P platform\] router \[router...\]\n"

# Password file
set password_file $env(HOME)/.cloginrc
@@ -175,7 +179,7 @@
set do_interact 1
# user Password
} -p* {
- if {! [regexp .\[pP\](.+) $arg ignore userpasswd]} {
+ if {! [regexp .\[p\](.+) $arg ignore userpasswd]} {
incr i
set userpasswd [lindex $argv $i]
@@ -259,6 +263,12 @@
incr i
set cypher [lindex $argv $i]
+ # Platform (device type refinement)
+ } -P* {
+ if {! [regexp .\[P\](.+) $arg ignore platform]} {
+ incr i
+ set plat [lindex $argv $i]
+ }
# Do we enable?
} -noenable {
set avenable 0
@@ -391,8 +401,8 @@

# 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 identfile } {
- global command spawn_id in_proc do_command do_script platform passphrase
+proc login { router user userpswd passwd enapasswd cmethod cyphertype identfile platform } {
+ global command spawn_id in_proc do_command do_script defaultplatform passphrase
global prompt prompt_match u_prompt p_prompt e_prompt sshcmd telnetcmd
set in_proc 1
set uprompt_seen 0
@@ -880,8 +890,8 @@
foreach router [lrange $argv $i end] {
set router [string tolower $router]
# attempt at platform switching.
- set platform ""
- send_user -- "$router\n"
+ set platform $defaultplatform
+ send_user -- "$router (initial platform: \"$platform\")\n"

# device timeout
set timeout [find timeout $router]
@@ -1024,11 +1034,21 @@
set telnetcmd [join [lindex [find telnetcmd $router] 0] ""]
if { "$telnetcmd" == "" } { set telnetcmd "telnet -K" }

+ # Figure out if we have a platform hint
+ if {[info exists plat]} {
+ # command line platform
+ set platform $plat
+ } else {
+ set plat [find platform $router]
+ if { "$plat" ne "" } { set platform $plat }
+ }
+ send_user -- "$router (active platform: \"$platform\")\n"
# if [-mM], skip do not login
if { $do_cloginrcdbg > 0 } { continue; }

# Login to the router
- if {[login $router $ruser $userpswd $passwd $enapasswd $cmethod $cyphertype $identfile]} {
+ if {[login $router $ruser $userpswd $passwd $enapasswd $cmethod $cyphertype $identfile $platform]} {
incr exitval
# if login failed or rsh was unsuccessful, move on to the next device

. . . . . . . . . . . . . . . . . . . . . . . . .

Jethro R Binks, Network Manager,

Information Services Directorate, University Of Strathclyde, Glasgow, UK

The University of Strathclyde is a charitable body, registered in Scotland, number SC015263.