--- /dev/null
+
+ [1]The Columbia Crown The Kermit Project | Columbia University
+ 612 West 115th Street, New York NY 10025 USA o [2]kermit@columbia.edu
+ ...since 1981
+ [3]Home [4]Kermit 95 [5]C-Kermit [6]Scripts [7]Current [8]New [9]FAQ
+ [10]Support
+
+
+ [11]CLICK HERE to read about some of these items.
+
+ [12]Table of platforms [13]Book: Using C-Kermit [14]Download
+ C-Kermit 9.0
+
+C-Kermit 9.0 Update Notes
+
+ Note: C-Kermit 9.0.301 contains a correction that applies only to
+ Solaris 10 and 11.
+ C-Kermit 9.0.302 contains corrections that apply only to FreeBSD 8
+ and 9.
+ * [15]Large Files
+ * [16]How to Test Large-File Transfer
+ * [17]Arithmetic with Large Integers
+ * [18]FORCE-3 Packet Protocol
+ * [19]Variable Evaluation
+
+ * [20]The RENAME Command You Always Wanted
+ * [21]Other New Features
+ * [22]Incompatibilities
+ * [23]What's Not In C-Kermit 9.0
+ * [24]And a Loose End
+
+ * [25]Demonstration: Secure POP mail fetcher
+ * [26]Demonstration: HP Switch Configuration Backup
+ * [27]Demonstration: HP iLO Blade Configuration
+ * [28]Demonstration: IBM/Rolm/Siemens CBX Management
+ * [29]Demonstration: CSV and TSV Files
+ * [30]Demonstration Scripts for Webmasters
+
+ This is the third supplement to [31]Using C-Kermit, Second Edition. I
+ apologize for the scattered nature of the information and I hope I can
+ organize it and gather it all into one place for easy and definitive
+ reference some day. It's a big job so it depends on the demand. For the
+ time being the definitive reference and introduction is the book (which
+ is now available also in a [32]Kindle Edition), plus the [33]C-Kermit
+ 7.0 update, [34]C-Kermit 8.0 update, and now this one. Plus tons of
+ other web pages on this site, sample script programs, and so on.
+
+ In version 6.0, C-Kermit was a pretty powerful and flexible
+ communication program with scripting capabilities. By version 9.0, I'd
+ like to think of it more as a scripting language with built-in
+ communications. You can get an idea of the kinds of programs you can
+ write in Kermit language [35]here. You can develop programs quickly
+ because it's an interactive program, not a compiler. The scripting
+ language is the command language. Kind of like the Unix shell but
+ "somewhat" less cryptic, including concepts not only from C but from
+ PL/I, Snobol, LISP, Bliss, and Smalltalk. The language itself is built
+ upon the command language of the much-loved [36]DECSYSTEM-20 from the
+ 1970s and 80s, the Clipper Ship of the Text Era. (Text is not a bad
+ word. Those of us who can touch-type and who are proficient in
+ text-based computing environments like Unix shell or VMS DCL are likely
+ to be orders of magnitude more productive than users of GUIs.)
+
+ Thanks to (at least) Jeff Altman, William Bader, Ian Beckwith, Nelson
+ Beebe, Gerry Belanger, Joop Boonen, Rob Brown, Christian Corti, Alexey
+ Dokuchaev, John Dunlap, Peter Eichhorn, Carl Friedberg, Terry Kennedy,
+ Günter Knauf, Jason Lehr, Arthur Marsh, Lewis McCarthy, Gary Mills, Ed
+ Ravin, Jonathan Reams, Mike Rechtman, Mark Sapiro, Steven Schweda
+ (SMS), Kinjal Shah, Michael Sokolov, Andy Tanenbaum, Seth Theriault,
+ Zach A. Thomas, Martin Vorländer, and Eric Weaver for assistance, and
+ to Hewlett-Packard Company for support.
+
+ - Frank da Cruz [37]fdc@columbia.edu, 30 June 2011
+
+ P.S. It occurred to me just before the end of the day that maybe I
+ should back up the Kermit website on DVD, just in case. Using
+ [38]Kermit 95 on the desktop over an SSH connection to the Unix file
+ system where the website resides, I made a fresh directory on the PC,
+ CD'd to it, and on Unix cd'd to the Website directory, and told
+ C-Kermit 9.0 to:
+
+C-Kermit> send /recursive /dotfiles /nobackup *
+
+ and it re-created the website directory tree in the PC directory, text
+ files correctly converted to Windows format and binary files correctly
+ left as-is. The /dotfiles switch means to include files such as
+ .htaccess whose names start with a dot (period), and the /nobackup
+ switch means to skip backup files created by EMACs (such as
+ index.html.~243~). And then I did the same with the FTP sites, about
+ 8GB in all. Watching the file-transfer display was kind of like having
+ 30 years of my life flash before my eyes in a few minutes. Then I
+ copied the two directories to DVD (the FTP site had to be split over 2
+ DVDs). The whole operation took under half an hour. The directory tree
+ on the CD is directly usable in Windows, Unix, or any other operating
+ system (unlike if I had transferred the files all in binary mode or all
+ in text mode, or if I had made, say, a gzipped tar archive or a zip
+ archive). I believe that, to this day, Kermit is the only software that
+ can do this. If someday I have to upload from these DVDs to Unix, VMS,
+ or any other operating system, it can be done exactly the same way,
+ with any necessary conversions on text files done automatically, and
+ binary files left intact, recursively through a whole very large
+ directory tree.
+
+What's New in General
+
+ Very briefly, the major items:
+ * [39]Open Source license.
+ * [40]64-bit file access and transfer and 64-bit integer arithmetic
+ on most common platforms.
+ * Support for recent releases of Linux, Mac OS X, *BSD, etc ([41]see
+ table).
+ * Support for newer OpenSSL releases up to and including 1.0.0d
+ ([42]see table).
+ * [43]Strengthened error checking for file transfer under extremely
+ harsh conditions.
+ * [44]Simplified semantics for variables used in scripts.
+ * Super-handy [45]extensions to the RENAME command.
+ * Other scripting improvements including support for reading and
+ writing [46]CSV and TSV files.
+ * [47]MIME character-set names are now recognized.
+ * Improved logging and debugging (see demo [48]here).
+ * Lots more described or listed below, and [49]here.
+
+Open Source License
+
+ C-Kermit 9.0 has the [50]Revised 3-Clause BSD License, an open source
+ license approved by OSI, the [51]Open Source Initiative.
+
+Large Files
+
+ Kermit is, first and foremost, a file-transfer program. One might
+ expect it to be able to transfer any kind of file, but that has been
+ decreasingly the case as file sizes began to cross the 2 gigabyte
+ threshold.
+
+ The biggest change since C-Kermit 8.0.211 is support for large files on
+ platforms that support them. A "large file" is one whose size is
+ greater than 2^31-1 (2,147,483,647) bytes (2GB-1); that is, one whose
+ size requires more than 31 bits to represent. Before now, Kermit was
+ able to access such files only on 100% 64-bit platforms such as Digital
+ Unix, later known as Tru64 Unix. In the new release, Kermit takes
+ advantage of the X/Open Single UNIX Specification Version 2 (UNIX 98)
+ Large File Support (LFS) specification, which allows 32-bit platforms
+ to create, access, and manage files larger than 2GB.
+
+ Accommodating large files required code changes in many modules,
+ affecting not only file transfer, but also file management functions
+ from directory listings to local file manipulation, plus the user
+ interface itself to allow entry and display of large numbers. All this
+ had to be done in a way that would not affect pure 32-bit builds on
+ platforms that do not support large files. Large file support is
+ summarized in the [52]Table of Platforms; entries in Yellow (32-bit
+ builds that support 64-bit integers) and Green (64-bit builds) support
+ large files.
+
+ Note that VMS C-Kermit and Kermit 95 for Windows have always been able
+ to transfer large files. However their user interface used 32-bit
+ integers for statistics and the file transfer display. In C-Kermit 9.0
+ Alpha.03, VMS C-Kermit on 64-bit platforms (Alpha and Itanium) should
+ now give correct statistics and progress displays. (We'll see about
+ Kermit 95 later.)
+
+How to Test Large-File Transfer
+
+ Several methods are available for testing large-file transfers:
+ * By transferring a real file that is more than 2147483648 bytes long
+ (a file whose length requires more than 31 bits to express); or to
+ be totally sure, that is longer than 4294967296 bytes (32 bits or
+ more). Or to be double super sure, longer than 8589934592 (33
+ bits).
+ * If you don't have such a file or there is not sufficient disk space
+ for such a file, you can create a special kind of file that takes
+ up one block on the disk but appears to be 4.3GB long by compiling
+ and running [53]THIS C PROGRAM on Linux, Solaris, HP-UX, or other
+ Unix platform that supports large files. Kermit or FTP or any other
+ file transfer program will transfer the result (BIGFILE) in such a
+ way as to actually put 4.3GB (or other desired size; see source) on
+ the wire.
+ * You can use Kermit's CALIBRATE feature to transfer a large file
+ that doesn't exist. At the receiver, use RECEIVE /CALIBRATE. At the
+ sender, use SEND /CALIBRATE:length, e.g.:
+
+ (At remote kermit...)
+ $ kermit -Y
+ C-Kermit> receive /calibrate
+ (Return to local kermit...)
+ Ctrl-\c
+ C-Kermit> send /calibrate:4300000000
+ This sends a simulated file 4.3GB in length, that does not exist on
+ the sender and will not take up any disk space on the receiver.
+ SEND /CALIBRATE: accepts big numbers only in Kermit versions that
+ support them (this does not include Kermit 95 on Windows). This
+ method tests only Kermit's ability to express and understand large
+ file sizes, but does not test Kermit's file-system interface, since
+ no files are involved.
+
+Arithmetic with Large Integers
+
+ Because large file support requires the availability of a 64-bit signed
+ integer data type, other aspects of C-Kermit were adapted to use it
+ too, most notably Kermit's algebraic expression evaluator and its
+ [54]S-Expression interpreter, on all platforms that support large files
+ (those listed as 64 or 32/64 in the Word column of the [55]table). In
+ fact, every Kermit command that parses a number in any field can now
+ parse a large number on those platforms.
+
+ S-Expressions can now be forced to operate with integers only, without
+ floating-point conversion or having to explicitly truncate each result;
+ as an example. see the revised [56]Easter date calculation script.
+
+FORCE-3 Packet Protocol
+
+ The Kermit protocol has proven itself over the past 30 years to be
+ robust in terms of surviving harsh transmission environments and
+ delivering the data correctly and completely. In these times of
+ Internet everywhere and error-correcting modems in the few places where
+ the Internet isn't, few people even recall the kinds of difficult
+ conditions that were common when the Kermit protocol was first
+ developed: noisy telephone lines, serial interfaces that drop
+ characters, lack of transparency to control or 8-bit characters,
+ absence of flow control, "bare" modems without error correction.
+
+ But the Internet is not everywhere, and not all modems are
+ error-correcting. Perhaps the most difficult trial so far for Kermit or
+ any other protocol is the [57]EM-APEX project, in which floats are
+ dropped into the ocean from an aircraft into the path of a hurricane;
+ these floats dive into the water measuring current, temperature, and
+ salinity at different depths and then surface to phone home, sending
+ the data to land stations using Kermit protocol over
+ non-error-correcting 300bps [58]Iridium satellite modems, with high
+ seas and winds battering the floats and heavy ([59]sometimes
+ electrical) storms between the modem and the satellite.
+
+ Because of the transmission speed and long distances involved, the
+ transfers were very slow. The Kermit software in the floats is
+ [60]Embedded Kermit, which did not implement sliding windows, which
+ would have sped up the flow considerably. John Dunlap, engineer at the
+ University of Washington's Applied Physics Laboratory, undertook the
+ task of adding sliding windows to E-Kermit. For testing, he rigged up a
+ [61]simulator in which Kermit transfers take place over a connection
+ with different amounts of noise and delay. He found that occasionally,
+ a transfer would appear to succeed, but the received file would be
+ corrupt.
+
+ According to the Kermit protocol definition, the first packet always
+ has block-check type 1, a 6-bit checksum, which is the only block check
+ type that all Kermit implementations are required to support; thus any
+ Kermit partner can process this packet. This packet itself can
+ negotiate a higher level of checking, such that subsequent packets have
+ (say) block-check type 3, a 16-bit cyclic redundancy check (CRC)
+ encoded as three printable 7-bit ASCII characters. The 16-bit CRC can
+ catch all errors of certain kinds (single-bit, double-bit, bursts of 16
+ bits or less), and more than 99.9984741210937% of all other possible
+ errors.
+
+ John's simulations revealed that file corruption could occur undetected
+ when the initial packet was corrupted in such a way that a parameter or
+ capability byte was changed and the checksum also changed to make the
+ packet appear to be correct, thus allowing the transfer to proceed with
+ the two Kermit partners out of sync as to packet encoding and
+ interpretation (the chances of two such errors producing a seemingly
+ valid packet are about 1 in 6000 when using the 6-bit checksum). For
+ example, the compression technique might be misnegotiated and then the
+ receiver might store incoming data without decompressing it.
+
+ The solution is a new option, selected by:
+
+ BLOCK-CHECK TYPE 5
+
+ to require a type 3 block check (16-bit CRC) on every packet, including
+ the initial ones, thus reducing the probability of a misnegotiation by
+ many orders of magnitude. THIS PARAMETER CAN NOT BE NEGOTIATED. Each
+ Kermit program must be given the "set block 5" command prior to
+ transfer. That's because normally every Kermit program expects the
+ first packet to have a 6-bit checksum, and if the first packet has a
+ 3-byte, 16-bit CRC, the packet receiver will think it is corrupted.
+
+ In practice, however, it is possible to code the packet receiver
+ "cheat" by reading the packet data before verifying the block check.
+ Thus when the receiver is C-Kermit 9.0 or later or E-Kermit 1.7 or
+ later, it is only necessary to give the "set block 5" command to the
+ file sender, and the receiver will check for a FORCE-3 first packet. If
+ the receiver does not support this feature, however, the initial packet
+ will be be rejected (after several retries) and the file transfer will
+ not take place. There is no attempt to "back off" to normal behavior.
+
+ CAPTION: Table 4. Kermit Protocol Packet Block Check Types
+
+ Type Command Bytes Status Explanation
+ 1 SET BLOCK 1 1 Required in all Kermit implementations. Negotiated.
+ 6-bit checksum, suitable for good connections.
+ 2 SET BLOCK 2 2 Optional, negotiated. 12-bit checksum. 64 times
+ stronger than type 1.
+ 3 SET BLOCK 3 3 Optional, negotiated. 16-bit CRC.
+ BLANK-FREE-2 SET BLOCK 4 2 Optional, negotiated. 12-bit checksum, two
+ nonblank bytes.
+ FORCE-3 SET BLOCK 5 3 Optional, not negotiated. 16-bit CRC forced all
+ packets.
+
+ BLANK-FREE-2 is for environments where Kermit packets are treated as
+ lines of text, and in which trailing blanks can be stripped; for
+ example, when transferring files with an IBM mainframe through a 3270
+ protocol converter.
+ [62]E-Kermit 1.7
+
+Variable Evaluation
+
+ Does the strange behavior of Kermit's \%x variables puzzle or annoy
+ you?
+
+ Kermit software development has been a collaborative project over the
+ years, with contributions coming in from almost every country and every
+ sector of the economy - academic, corporate, government. Thus not all
+ versions, and not all features of a given version, are a product of
+ systematic design.
+
+ One example was the introduction of variables for text substitution,
+ first in a version of MS-DOS Kermit that was sent in by someone
+ somewhere (I could look it up, but no time...) Although the design of
+ the notation for variable names (table below) is mine, the underlying
+ code was contributed. In that code there was only one kind of variable,
+ and if I recall correctly the variable name was a backslash followed by
+ a single letter, for example \a, \b, etc. The contributed code
+ evaluated these variables recursively, meaning if the definition of a
+ variable contained variable references, then these were resolved when
+ dereferencing the variable, and the process would continue as deep down
+ as necessary to resolve the thing fully.
+
+ This was sometimes handy, but it had one severe drawback: There was no
+ way to use variables in a straightforward way to represent strings that
+ contained literal backslashes; for example, DOS or Windows pathnames.
+ This gave rise to all kinds of quoting rules and conventions (e.g.
+ doubling backslashes or forcing single-level evaluation with
+ \\fcontents()), and also to the introduction of other kinds of
+ variables that were evaluated one level deep, rather than recursively.
+
+ To accommodate coexistence of different kinds of variables as well as
+ "escape sequences" for representing control and 8-bit characters, the
+ syntax for variable names was extended to include three elements: the
+ leading backslash, then a single character indicating the type of
+ variable, and then the name of the variable in a format corresponding
+ to the type designator, as shown in this somewhat simplified table:
+
+ CAPTION: Table 1. Variable-name Syntax in Kermit
+
+ Notation Meaning
+ \000 - \255 8-bit character constant (decimal)
+ \d000 - \d255 Alternative notation for 8-bit character (byte) constant
+ (decimal)
+ \o000 - \o377 8-bit character constant (octal)
+ \x00 - \xff 8-bit character constant (hexadecimal)
+ \%a - \%z Scalar variable, evaluated recursively.
+ \%0 - \%9 Macro argument, scalar, evaluated recursively.
+ \&a - \%& Array name
+ \&a[x] Array reference, evaluated recursively (x is any constant or
+ variable)
+ \v(name) Built-in scalar variable, evaluated one level deep.
+ \m(name) User-defined scalar variable, evaluated one level deep.
+ \$(name) An environment variable, evaluated one level deep.
+ \s(name[n:m]) Compact substring notation, evaluated one level deep.
+ \fname(args...) Built-in function with zero or more arguments.
+ \\ Literal backslash
+ \N OUTPUT command only: NUL, ASCII 0
+ \B OUTPUT command only: BREAK (250ms, for serial connections)
+ \L OUTPUT command only: Long BREAK (1.5sec, ditto)
+
+ Variable names in Kermit are case-independent. The simplifications in
+ the table are that the notation for decimal and octal bytes can have
+ from one to three digits, and can include braces to separate them from
+ text digits, e.g. \7, \{123}, \o{50}. Hex bytes too, except they must
+ always have exactly two hex digits, 0-9a-f. Array indices must be, or
+ must evaluate to, numbers (floating point numbers are truncated).
+ Associative arrays are also available (dynamic arrays with arbitrary
+ text as subscript), but they are really just a variation on \m()
+ variables (read about associative arrays [63]here). Also, there are
+ some alternative notations for compact substring notation.
+
+ We didn't want to have lots of "distinguished" characters, as the UNIX
+ shell does; one is enough, clarity over brevity. Although the notation
+ can be a bit cumbersome, we can use the \m(name) form to circumvent the
+ overevaluation in most contexts. But macro arguments are always
+ assigned to the \%0-9 variables, and thus always evaluated recursively,
+ making it difficult and confusing to pass (e.g.) Windows pathnames as
+ arguments to macros. The same is true for array elements, especially in
+ contexts where they are used to return results from built-in functions
+ (for example, \fsplit() used to return the elements of a
+ [64]comma-separated value list if any of the values contained
+ backslashes). An even worse scenario is when macro arguments are passed
+ from one macro to another; for some graphic illustrations see
+ [65]Taming the Wild Backslash - Part Deux from the [66]C-Kermit 7.0
+ Update Notes.
+
+ We can't just change how variables are evaluated because that would
+ break existing scripts. But we can always add Yet Another SET Command:
+
+ SET COMMAND VARIABLE-EVALUATION { RECURSIVE, SIMPLE }
+
+ This applies only to \%a-z and \%0-9 variables and to \&a-z[] arrays
+ (since all other kinds of variables are evaluated only one level deep).
+ The default, of course, for backwards compatibility, is RECURSIVE.
+ SIMPLE forces the evaluation of these variables to return their literal
+ contents, without further evaluation:
+
+ * An exception is made in the case of array subscripts, because
+ changing how they are evaluated could break a lot of scripts, and
+ anyway there should never be any harm in evaluating them
+ recursively because their final value is always (or should be)
+ numeric, not some string that might contain backslashes.
+ * The VARIABLE-EVALUATION setting is on the command stack. Thus you
+ can give this command in a macro, command file, or user-defined
+ function without affecting the calling environment.
+ * The new \frecurse() function forces recursive evaluation of its
+ argument regardless of the VARIABLE-EVALUATION setting. The
+ argument can be any string (or nothing at all); all the variables
+ in the string, even \m() ones, are evaluated recursively:
+
+def \%a 1 \%b 3
+def \%b 2
+def xx easy as \%a
+show mac xx
+echo \frecurse(\m(xx))
+easy as 1 2 3
+echo \frecurse(it's as easy as \m(xx))
+it's as easy as easy as 1 2 3
+
+ * The new \v(vareval) built-in variable contains the current setting
+ (recursive or simple) at the current command-stack level.
+
+ Here's a short script for illustration:
+
+define path c:\users\fdc\somefile.txt
+define test1 { # Normal recursive argument evaluation
+ echo \%0: arg=\%1
+}
+define test2 { # Simple argument evaluation
+ set var simple
+ echo \%0: arg=\%1
+}
+test1 \m(path)
+test2 \m(path)
+exit
+
+ And here's the result:
+
+?<ERROR:NO_SUCH_FUNCTION:\fdc\somefile.txt()>
+test2: arg=c:\users\fdc\somefile.txt
+
+ The first line might seem surprising, but under the normal rules (see
+ table above) \f indicates a function call, with the letters following
+ the 'f' being the name of the function. But there is no function by
+ that name... and if there were, you probably didn't intend to call it!
+
+ SET COMMAND VARIABLE-EVALUATION SIMPLE has no effect on constants, only
+ on variables. Note how \m(path) is defined. The DEFINE command assigns
+ the literal value of its argument to the named variable (see Table 3
+ below), thus in this case no special syntax is needed. But in other
+ contexts, you must double the backslashes or use the \fliteral()
+ function to use literal backslashes in data:
+
+test2 c:\\users\\fdc\\somefile.txt
+test2 \fliteral(c:\users\fdc\somefile.txt)
+
+ C-Kermit 9.0 adds a new notation for \fliteral() which also has certain
+ advantages over it: \q(string):
+
+test2 \q(c:\users\fdc\somefile.txt)
+
+ Since \fliteral() is a function, its argument list (the text within
+ parentheses) has special syntax of its own, in which commas and braces
+ are treated specially and introduce another set of quoting problems.
+ \q(string) doesn't have these problems. The only consideration is that
+ parentheses must be balanced or else quoted (preceded by backslash), or
+ represented as numeric character entities (left paren = \40, (right
+ paren = \41).
+
+ Or else hold the value in a simple variable as we did with \\m(path)
+ above.
+
+ SET COMMAND VARIABLE-EVALUATION SIMPLE is a big change and might have
+ repercussions that didn't show up in the initial tests; a lot more
+ testing is needed.
+
+ On the topic of variables, let's summarize in one place the ways in
+ which values can be explicitly assigned to variables. There is nothing
+ new here except the table itself:
+
+ CAPTION: Table 2. Variable Assignment in Kermit
+
+ Command Shorthand Explanation
+ DEFINE name value .name = value The literal value becomes the contents
+ of the named variable; variables names in the value are copied without
+ evaluation. This command is for defining macros that take parameters,
+ as well as for defining simple variables, especially if the values
+ contain backslashes.
+ _DEFINE name value Like DEFINE but the name is evaluated before use.
+ ASSIGN name value .name := value The value is evaluated and the result
+ becomes the contents of the named variable.
+ _ASSIGN name value Like ASSIGN but the name is evaluated before use.
+ EVALUATE name expression .name ::= value The expression (in regular
+ algebraic notation) is evaluated arithmetically and the result becomes
+ the contents of the named variable. If the expression contains any
+ variables they are evaluated first.
+ _EVALUATE name expression Like EVALUATE but the name is evaluated
+ before use.
+ INCREMENT name expression Evaluates the variables in the expression,
+ then evaluates the expression arithmetically, and then adds the value
+ to the contents of the named variable, which must be a number or an
+ algebraic expression. If the expression is empty, a value of 1 is used.
+ _INCREMENT name expression Like INCREMENT but the name is evaluated
+ before use.
+ DECREMENT name expression Evaluates the variables in the expression,
+ then evaluates the expression arithmetically, and then subtracts the
+ value from the contents of the named variable, which must be a number
+ or an algebraic expression. If the expression is empty, a value of 1 is
+ used.
+ _DECREMENT name expression Like DECREMENT but the name is evaluated
+ before use.
+ DECLARE name = list An array declaration can include an initializer
+ list; items in the list are evaluated before assignment. This can be
+ defeated by doubling any backslashes or enclosing individual arguments
+ in \fliteral().
+ DO name arguments name arguments When invoking a macro with a DO
+ command (or an implied one), the arguments are evaluated, then assigned
+ to \%1, \%2, etc, and the macro's name to \%0.
+ (SETQ name value) Kermit also includes a mini-[67]LISP interpreter
+
+ Variables are evaluated automatically in Kermit commands simply by
+ referencing them, according to rules given in Table 1. The following
+ functions can be used to change how a a particular variable is
+ evaluated:
+
+ CAPTION: Table 3. Kermit Functions for Evaluating Variables
+
+ Function Argument Description
+ \fcontents() \%x or \&x[y] Evaluates the variable or array element
+ (which normally would be evaluated recursively) one level deep.
+ \fdefinition() name If the argument is a \%x variable or an array
+ element, it is evaluated to get the name; otherwise the argument is the
+ name. Its definition is returned with no recursion.
+ \m() name Equivalent to \fdefinition().
+ \frecurse() \m(name) Forces recursive evaluation of a macro definition
+ (a.k.a. long variable name). NOTE: \frecurse() can operate on any kind
+ of variable as well as on any string containing any mixture of
+ variables.
+
+C-Kermit's RENAME Command
+
+ C-Kermit's RENAME command, which is used for changing the names of
+ local files or for moving files locally, has two basic forms:
+
+ RENAME [ optional-switches ] oldfilename newfilename
+ This form lets you change the name of a single file from
+ oldfilename to newfilename. Example:
+ rename thismonth.log lastmonth.log
+
+ RENAME [ optional-switches ] filespec directoryname
+ This form lets you move (without renaming) one or more files
+ (all the files that match the filespec, which may contain
+ wildcard characters such as "*") to the given directory.
+ Example:
+ rename *.txt ~/textfiles/
+
+ Traditionally, the optional switches have been:
+
+ RENAME /LIST oldname newname
+ Display the old and new name for each file while renaming.
+ Synonyms: /LOG, /VERBOSE. Example:
+ rename /list *.txt ~/textfiles/
+
+ RENAME /NOLIST oldname newname
+ Don't display the old and new name for each file while renaming.
+ This is the default behavior. Synonyms: /NOLOG, /QUIET. Example:
+ rename /nolist *.txt ~/textfiles/
+
+ Reminder: Every switch starts with a slash (/) and must be preceded by
+ a space.
+
+New RENAME Features for C-Kermit 9.0
+
+ A series of new options (switches) have been added to let you change
+ the names of multiple files at once by case conversion, string
+ substitution, or character-set conversion, and optionally also move
+ them to a different directory:
+
+ /LOWER: Convert the filename to lowercase
+ /UPPER: Convert the filename to uppercase
+ /CONVERT: Change the filename's character encoding
+ /REPLACE: Do string substitutions on the filename
+
+ If the source-file specification includes a path or directory, any
+ changes are applied to the filenames only, not to the directory or path
+ specification.
+
+ Since name changes, when applied to many files at once, can have
+ consequences that are not easily undone, there are also some new
+ controls, safeguards, and conveniences:
+
+ RENAME /SIMULATE
+ This switch tells Kermit to show you what the RENAME command
+ would do without actually doing it. /SIMULATE implies /LIST.
+
+ RENAME /COLLISION:{FAIL,SKIP,OVERWRITE}
+ This switch governs Kermit's behavior when renaming multiple
+ files, and any of the names would collide with the name of a
+ file that already exists. The default, for compatibility with
+ earlier releases of C-Kermit, is OVERWRITE, i.e. write over the
+ existing file. The other two protect existing files. SKIP means
+ to skip (not rename) the file that would cause the collision,
+ and proceed to the next file, if any. FAIL means that no files
+ will be renamed if there would be any collisions; for this
+ Kermit makes two passes, checking each new name it constructs
+ for existence before starting the second pass (however, there is
+ no guarantee that in the second pass, it won't create the same
+ new name for more than one file; in that case, it will stop
+ before executing the second rename). Example:
+ rename /simulate /collision:proceed * ~/tmp/
+
+ Reminder: In switches such as /COLLISION that take arguments
+ (operands), the switch name and its argument(s) are separated by a
+ colon (:) with no intervening spaces. Also remember that Kermit
+ keywords can always be abbreviated by leaving off characters from the
+ right, as long as the result is still unique in its context. Thus "ren
+ /col:f" would be equivalent to "rename /collision:fail".
+
+ You can change the following preferences for the RENAME command with
+ the new SET RENAME command:
+
+ SET RENAME LIST { ON, OFF }
+ Tells the RENAME command whether to list its actions if you
+ don't include a /LIST or /NOLIST or equivalent switch.
+
+ SET RENAME COLLISION { FAIL, OVERWRITE, SKIP }
+ Tells the RENAME command how to handle filename collisions in
+ the absence of a /COLLISION switch. That is, it replaces the
+ default action of OVERWRITE with action of your choosing, which
+ is then used in any RENAME command that does not include an
+ explicit /COLLISION switch.
+
+ SHOW RENAME
+ Displays the current SET RENAME settings.
+
+Changing the Case of Filenames
+
+ RENAME /UPPER:{ALL,LOWER} filespec [ directory ]
+ RENAME /LOWER:{ALL,UPPER} filespec [ directory ]
+ These switches let you change the alphabetic case of letters in
+ all the files whose names match the filespec. If a directory
+ name is given after the filespec, then the files are also moved
+ to the given directory.
+
+ By default, all files that match the given filespec have their names
+ changed (if necessary). This is what the ALL argument means, e.g.:
+
+ RENAME /LOWER:ALL *
+ RENAME /LOWER *
+
+ You can use either form: RENAME /LOWER is equivalent to RENAME
+ /LOWER:ALL. The other argument (/LOWER:UPPER or /UPPER:LOWER) means to
+ leave mixed-case filenames alone, and rename only those files whose
+ names contain letters of only the given case. Examples:
+
+ RENAME /UPPER:ALL foo.bar
+ Changes the filename to FOO.BAR.
+
+ RENAME /UPPER foo.bar
+ Same as "rename /upper:all foo.bar".
+
+ RENAME /UPPER foo.bar ~/old/
+ Renames foo.bar to FOO.BAR and moves it to the user's old
+ directory (Unix).
+
+ RENAME /LOWER *
+ Changes the names of all files to have only lowercase letters.
+
+ RENAME /LOWER:UPPER *
+ Changes the names of only those files whose names contain no
+ lowercase letters to have only lowercase letters. For example,
+ FOO.BAR would be changed, Foo.Bar would not be changed. foo.bar
+ would not be changed either because it's already all lowercase.
+
+ RENAME /LOWER:UPPER * ~/new/
+ Same as the previous example, but also moves each file to the
+ user's new directory (whether it was renamed or not).
+
+ Case conversion works reliably for ASCII characters only. Kermit uses
+ the C library for this, which on any given platform might or might not
+ handle non-ASCII letters, and if it does, then how it works would
+ normally depend on your locale definitions (the LC_CTYPE and/or LANG
+ environment variable in Unix). When non-ASCII letters are not handled
+ by the C library, the RENAME command does change their case. For
+ example, Olga_Tañón.txt might become OLGA_TAñóN.TXT.
+
+String Replacement in Filenames
+
+ The RENAME command also lets you change filenames by string
+ substitution.
+
+ RENAME /FIXSPACES[:String] filespec [ directory ]
+ Replaces all spaces in each matching filename by the given
+ string, if any, or if none is given, by underscore. Examples:
+
+ RENAME /FIX *
+ RENAME /FIXSPACES:_ *
+ RENAME /FIXSPACES:"" *
+ RENAME /FIXSPACES:<040> *
+
+ The first two are equivalent, replacing each space with
+ underscore; a file called "My Favorite Photo.jpg" becomes
+ "My_Favorite_Photo.jpg". The third example removes all spaces
+ ("MyFavoritePhoto.jpg"). The fourth replaces each space with the
+ string "<040>" ("My<040>Favorite<040>Photo.jpg").
+
+ RENAME /REPLACE:{{String1}{String2}} filespec [ directory ]
+ Renames each matching file by changing occurrences of String1 in
+ its name to String2. If a directory specification is included,
+ the file is also moved to the given directory (even if the name
+ was not changed). Note that in this case, the curly braces are
+ part of the command. Example:
+
+ RENAME /REPLACE:{{.jpeg}{.jpg}} *
+
+ changes all *.jpeg files to *.jpg.
+
+ By default, RENAME /REPLACE changes all occurrences of String1 in each
+ filename to String2 so, for example, if you had a file called
+ abcjpegxyz.jpeg, the command just shown would change its name to
+ abcjpgxyz.jpg.
+
+ For greater control and flexibility, the /REPLACE: switch argument can
+ take several distinct forms:
+
+ RENAME /REPLACE:String1 filespec [ directory ]
+ This means to remove all occurrences of String1 from the given
+ filenames name. It is equivalent to /REPLACE:{{String1}{}}. A
+ handy use for this option is to remove spaces from filenames.
+
+ RENAME /REPLACE:{{String1}{String2}} filespec [ directory ]
+ As already noted, this replaces every occurrence of String1 with
+ String2 in each filename. Alphabetic case in string matching is
+ done according to the current SET CASE setting.
+
+ RENAME /REPLACE:{{ }{_}} filespec [ directory ]
+ This replaces all spaces in the given filenames with underscore,
+ equivalent to RENAME /FIXSPACES.
+
+ RENAME /REPLACE:{{String1}{String2}{Options}} filespec [ directory ]
+ Options can be included that add more control to the process.
+ The option string is a sequence of characters; each character in
+ the string is an option. The choices are:
+
+ A String matching is to be case-sensitive, regardless of SET CASE.
+ a String matching is to be case-independent, regardless of SET CASE.
+ ^ String replacement will occur only at the beginning of the filename.
+ $ String replacement will occur only at the end of the filename.
+ 1 Only the first occurrence of the string will be replaced.
+ 2 Only the second occurrence of the string will be replaced.
+ 3 4 5 6 7 8 ...
+ 9 Only the ninth occurrence of the string will be replaced.
+ - (hyphen, minus sign) Before a digit: occurrences will be counted from
+ the right.
+ ~ (tilde) Before digit or minus sign: all occurrences but the given one
+ will be replaced.
+
+ The tilde modifier works only with single-byte character sets such as
+ ASCII, CP437, ISO 8859-1, etc, but not with multibyte character sets
+ such as UCS2, UTF8, or any of the Japanese Kanji sets.
+
+ Here are some examples showing how to use the /REPLACE options:
+
+ RENAME /REPLACE:{{foo}{bar}{^}} *
+ For all files whose names start with "foo", replaces the "foo"
+ at the beginning with "bar".
+
+ RENAME /REPLACE:{{}{New-}{^}} *
+ Prepends "New-" to the name of each file.
+
+ RENAME /REPLACE:{{.jpeg}{.jpg}{$}} *
+ Replaces ".jpeg" at the end of each filename with ".jpg".
+
+ RENAME /REPLACE:{{}{-Old}{$}} *
+ Appends "-Old" to the name of each file.
+
+ RENAME /REPLACE:{{foo}{bar}{a}} *
+ Replaces "foo", "FOO", "Foo", "fOO", etc, with "bar" in each
+ filename.
+
+ RENAME /REPLACE:{{foo}{bar}{A}} *
+ Replaces only (lowercase) "foo" in filenames with "bar".
+
+ RENAME /REPLACE:{{a}{XX}} *
+ Changes every "a" to "XX". For example a file called "a.a.a.a"
+ would become "XX.XX.XX.XX".
+
+ RENAME /REPLACE:{{a}{X}{2}}
+ Changes only the second "a" to "X". For example a file called
+ "a.a.a.a" would become "a.X.a.a".
+
+ RENAME /REPLACE:{{a}{X}{-1}}
+ Changes only the final "a" in the filename (it doesn't have to
+ be at the end) to "X". For example a file called "a.b.a.c.a.d"
+ would become "a.b.a.c.X.d".
+
+ RENAME /REPLACE:{{foo}{NOTFOO}{-2}}
+ Changes the second-to-last "foo" (if any) in the filename to
+ "NOTFOO".
+
+ RENAME /REPLACE:{{foo}{}{-2}}
+ Deletes the second-to-last "foo" (if any) from the filename.
+
+ RENAME /REPLACE:{{.}{_}{~1}}
+ Changes all but the first period to an underscore; for example,
+ "a.b.c.d.e" would become "a.b_c_d_e".
+
+ RENAME /REPLACE:{{.}{_}{~-1}}
+ Changes all but the final period to an underscore; for example,
+ "a.b.c.d.e" would become "a_b_c_d.e".
+
+ In the Options field, digits (and their modifiers), ^, and $ are
+ mutually exclusive. If you include more than one of these in the option
+ string, only the last one is used. Similarly for 'a' and 'A':
+
+ RENAME /REPLACE:{{foo}{bar}{Aa2$^}} *
+ This replaces "foo" with "bar" no matter what combination of
+ upper and lower case letters are used in "foo" ('a' overrides
+ 'A' in the option string), but only if "foo" is at the beginning
+ of the filename ('^' overrides '$' and '2').
+
+ If you give an /UPPER or /LOWER switch and a /REPLACE switch in the
+ same RENAME command, the /REPLACE action occurs first, then the case
+ conversion:
+
+ RENAME /REPLACE:{{foo}{bar}} /UPPER * /tmp
+ For each file: changes all occurrences of "foo" in the name to
+ "bar", then converts the result to uppercase, and then moves the
+ file to the /tmp directory. So (for example) "foot.txt" would
+ become "/tmp/BART.TXT".
+
+Changing the Character Encoding of Filenames
+
+ As you know, text is represented on the computer as a series of
+ numbers, with a given number corresponding to a given character
+ according to some convention or standard. Filenames are represented the
+ same way. The trouble is, different computers, or even different
+ applications on the same computer, might use different standards or
+ conventions ("character sets") for representing the same characters.
+ Usually ASCII is safe, but anything beyond that -- non-ASCII characters
+ such as accented or non-Roman letters -- is likely to vary. Sometimes
+ you have text that's in the "wrong" character set and you need to
+ convert it to something you can can use. Kermit has always been able to
+ handle this as part of file transfer and terminal emulation, as well as
+ being able to convert text files locally with its TRANSLATE command.
+ Now there's a way to convert filenames too, for example after copying
+ files from a CD that uses a different encoding:
+
+ RENAME /CONVERT:charset1:charset2 filespec [ directory ]
+ Converts filenames from the first character set to the second
+ one. The two character sets can be chosen from the SET FILE
+ CHARACTER-SET list; for complete details see [68]this page. For
+ example suppose you have a file called "Olga_Tañón.txt" on a
+ computer where ISO 8859-1 Latin Alphabet 1 is used, and you have
+ transported it (e.g. on CDROM) to another computer where the
+ text encoding is UTF8. Maybe you also have a lot of other files
+ with similar names in the same directory. You can convert the
+ filenames to UTF8 like this:
+
+ RENAME /CONVERT:latin1:utf8 *
+
+ /CONVERT can not be combined with /UPPER, /LOWER, or /REPLACE.
+
+ You should NOT use UCS2 for filenames since this encoding is not
+ compatible with C strings used in Unix and elsewhere.
+
+ RENAME /CONVERT affects only the filename, not the file's contents. You
+ can use the TRANSLATE command to convert the encoding of the contents
+ of a text file.
+
+Other New Features
+
+ See the [69]C-Kermit Daily Builds page for details. Very briefly:
+
+ * Perhaps most important, modernized makefile targets for the major
+ Unix platforms: Linux, Mac OS X, AIX, Solaris, etc. These are
+ somewhat automated; not autoconf exactly, but they cut down
+ significantly on redundant targets. For example, one single "linux"
+ target works on many (hopefully all) different Linux
+ configurations, where before different targets were required for
+ different combinations of (e.g.) curses / ncurses / no curses;
+ 32-bit / 64-bit; different feature sets and library locations.
+ (Separate targets are still required for Kerberos and/or SSL
+ builds, but they are "subroutinized".)
+ * Bigger buffers, more storage for commands, macros, scripts,
+ strings, and filename expansion in 64-bit versions and in 32-bit
+ versions that support large files.
+ * User-settable FTP timeout, works on both the data and control
+ connection.
+ * FTP access to ports higher than 16383.
+ * Built-in FTP client for VMS. This is the [70]same FTP client Unix
+ C-Kermit has had since version 8.0, minimally adapted to VMS by
+ SMS, supporting binary and Stream_LF file transfer only (in other
+ words, nothing to handle RMS files), but otherwise fully functional
+ (and scriptable) and theoretically capable of making connections
+ secured by SSL (at least it compiles and links OK with SSL - HP SSL
+ 1.3 in this case).
+ * Large file support in VMS, also by SMS. Alpha and Itanium only (not
+ VAX). VMS C-Kermit was already able to transfer large files, but
+ the file-transfer display (numbers and progress bar) and statistics
+ were wrong because they used ints. In the present Alpha test
+ release, this is an optional feature requested by including the "f"
+ option in P1.
+ * New PUTENV command that allows Kermit to pass environment variables
+ to subprocesses (Unix only, "help putenv").
+ * New TOUCH command, many file selection options ("help touch").
+ * New DIRECTORY command options and switches (/TOP, /COUNT;
+ HDIRECTORY, WDIRECTORY...). To see the ten biggest files in the
+ current directory: "dir /top:10 /sort:size /reverse *" or
+ equivalently, "hdir /top:10 *". WDIR lists files in reverse
+ chronological order, shorthand for "dir /sort:date /reverse".
+ * New command FSEEK /FIND:string-or-pattern, seeks to the first line
+ in an FOPEN'd file that contains the given string or matches the
+ given pattern. Example: Suppose you have a file of lines like this:
+
+ quantity description...
+ in which the first "word" is a number, followed by a description
+ (for example, the name of an item). Here is how to use FSEEK to
+ quickly get the total quantity of any given item, which is passed
+ as a parameter (either a literal string or a pattern) on the
+ command line:
+
+#!/usr/local/bin/kermit +
+if not def \%1 exit 1 Usage: \fbasename(\%0) string-or-pattern
+
+.filename = /usr/local/data/items.log # Substitute the actual filename
+set case off # Searches are case-independent
+fopen /read \%c \m(filename) # Open the file
+if fail exit 1 "\m(filename): \v(errstring)" # Fail: exit with error message
+.total = 0 # OK: Initialize the total
+echo Searching "\%1"...
+
+while true {
+ fseek /line /relative /find:\%1 \%c 0 # Get next line that has target
+ if fail break # Failure indicates EOF
+ fread /line \%c line # Read it
+ if fail break # (shouldn't happen)
+ increment total \fword(\m(line),1) # Increment the total
+}
+fclose \%c # Close the file
+echo Total for "\%1" : \m(total) # Print the result
+exit 0
+
+ The syntax of the FSEEK command in this example indicates that each
+ search should start relative to the current file line. Since Kermit
+ is an interpretive language, FSEEK is a lot faster than FREAD'ing
+ each line and checking it for the target, especially for big files.
+ An especially handy use for FSEEK is for use with potentially huge
+ sequentially timestamped logs, to seek directly to the date-time
+ where you want to start processing. Some other improvements for the
+ FOPEN/FREAD/FWRITE/FCLOSE family of commands are included also
+ (performance, bug fixes, convenience features), listed in the
+ [71]change log. (Prior to 9.0.299 Alpha.02, the FSEEK /FIND:
+ command always started from the top.)
+ * MIME synonyms for character-set names: A new equivalence between
+ MIME names and Kermit names for character sets, with a new table
+ showing the supported sets [72]HERE (this feature is also
+ illustrated in the [73]Weblog script).
+ * Unix C-Kermit SET TERMINAL TYPE now passes its arguments to
+ subprocesses as an environment variable.
+ * SET SESSION-LOG TEXT now strips out ANSI escape sequences from the
+ session log.
+ * For interacting with POP servers over clear-text or SSL-secured
+ connections:
+ + New SSL and TLS "raw" connections (no Telnet protocol).
+ + New INPUT command options for reading and capturing (perhaps
+ while scanning) continuous incoming text, such as INPUT
+ /NOWRAP (explained [74]HERE).
+ + New \femailaddress() command to extract the e-mail address
+ from an Internet mail message To: or From: line, used in
+ fetching mail from POP servers.
+ + Improved date parsing commands and functions for parsing the
+ different date formats that can appear in e-mail.
+ + Production scripts for fetching mail from a secure POP server,
+ available [75]HERE.
+ * Various features added to make Kermit more useful for writing CGI
+ scripts such as INPUT /COUNT:n to INPUT exactly n characters
+ (useful for reading form data).
+ * New \fpictureinfo() function for getting orientation and dimensions
+ of JPG and GIF images, described [76]HERE.
+ * New \fgetpidinfo() function for testing whether a given process
+ exists.
+ * \fkwdvalue() function fixed to allow multiword values.
+ * New function \fcount(s1,s2) to tell the number of occurrences of s1
+ in s2.
+ * New \flopx() function returns rightmost field from string (such as
+ a file's extension).
+ * New function \ffunction(s1) to tell whether a built-in s1 function
+ exists.
+ * New \fsqueeze(s1) function removes leading and trailing whitespace
+ from string s1, changes tabs to spaces, squeezing each run of
+ repeated whitespace characters to a single space.
+ * Compact substring notation: \s(somestring[12:18]) is the same as
+ \fsubstring(\m(somestring),12,18), i.e. the substring starting at
+ position 12, 18 characters long. \s(somestring[12_18]) means
+ characters 12 through 18 of the string (7 characters). Also,
+ \s(somestring[17.]) returns character number 17 of somestring.
+ * The string indexing functions now accept an optional trailing
+ argument specifying the occurrence number of the target string.
+ Likewise, \fword() can fetch words from the right as well as the
+ left.
+ * The COPY command in Unix C-Kermit has a new /PRESERVE switch,
+ equivalent to Unix "cp -p".
+ * ASKQ /ECHO:c can be used to make the characters the user types echo
+ as the character c, e.g. asterisk when typing a password.
+ * IF LINK filename to test if the filename is a symlink.
+ * Ctrl-K, when typed at the command parser, replaces itself with most
+ recently entered file specification.
+ * In Unix, the ability to log a terminal session to a serial port,
+ for use with speaking devices or serial printers; described
+ [77]HERE. Also for the same purpose, SET SESSION-LOG
+ NULL-PADDED-LINES for a speech synthesizer than needed this.
+ * Adaptation to OpenSSL 0.9.8 and 1.0.0.
+ * Lifted the restriction on having a remote Kermit program send
+ REMOTE commands to the local. A very big ex-client needed to be
+ able to do this (branches would connect to headquarters and upload
+ files; HQ would then download patches, a REMOTE HOST command was
+ necessary to allow the remote headquarters machines to install the
+ patches on the local client; of course the client first has to
+ ENABLE HOST because this is a risky scenario). The reason for the
+ restriction was that the server, upon receiving any REMOTE command
+ would send the results (output) back to the client as a file
+ transfer with "destination screen", but of course the remote has no
+ screen.
+ * Added XMESSAGE, which is to [78]MESSAGE as XECHO is ECHO: it
+ outputs a string with no line terminator DEBUG MESSAGE is ON.
+ * Fixed \frecurse() to not dump core when invoked with no arguments.
+ * Improved text for HELP FUNCTION SPLIT and HELP FUNCTION WORD.
+ * Patches for Debian 6.0 "Squeeze" from Ian Beckwith.
+ * \fcontents(\&a[3]) got an error if the array was declared but its
+ dimension was less than 3. Now it simply returns and empty string.
+ * \fsplit(), when parsing lines from CSV and TSV files, was treating
+ backslash in the data the same way it treats backslash in Kermit
+ commands. This was fixed to treat backslash like any other
+ character.
+ * Builds for Solaris 9 and later now use streams ptys rather then the
+ old BSD-style ptys. Thanks to Gary Mills for this one, who noticed
+ that he couldn't have more than 48 C-Kermit SSH sessions going at
+ once and figured out why.
+ * As noted [79]below DES encryption is being retired from many
+ platforms and libraries that once used it. I changed the Solaris
+ and Linux OpenSSL builds to account for this by testing for it. I
+ probably should also add a OMITDES option to omit DES even if it is
+ installed, but "KFLAGS=-UCK_DES" seems to do the job for now.
+ * I changed the Linux build to test for the OpenSSL version (like the
+ Solaris version already did), rather than assuming OpenSSL 0.9.7.
+ * A couple minor changes for Tru64 Unix 5.1B from Steven Schweda but
+ we still have some trouble on that platform. As a workaround "make
+ osf1" can be used there.
+ * Unix makefile and man page are now included in the Zip
+ distribution.
+ * \fjoin(), which is the inverse function of fsplit() now accepts CSV
+ and TSV as a second argument, to transform an array into a
+ comma-separated or tab-separated value list, as described [80]HERE.
+ * Even in 2010, Unix distributions continue to change their UUCP
+ lockfile conventions. C-Kermit 9.0 contains support from Joop
+ Boonen for OpenSuSE >= 11.3 and recent Debian, which no longer have
+ baudboy.h, which first appeared in Red Hat 7.2 in 2003.
+ * From Lewis McCarthy:
+
+ Based on code inspection, C-Kermit appears to have an SSL-related
+ security vulnerability analogous to that identified as CVE-2009-3767
+ (see e.g.
+ [81]http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-3767).
+
+ I'm attaching a patch for this issue relative to the revision of
+ ck_ssl.c obtained from a copy of
+ [82]http://www.columbia.edu/kermit/ftp/test/tar/x.zip downloaded on
+ 2010/07/30, which I believe is the latest.
+ When this flaw was first widely publicized at last year's Black Hat
+ conference, it was claimed that some public certificate authorities
+ had indeed issued certificates that could be used to exploit this
+ class of vulnerability. As far as I know they have not revealed
+ specifically which public CA(s) had been found issuing such
+ certificates. Some references:
+ + [83]http://www.mseclab.com/?p=180
+ + [84]http://www.theregister.co.uk/2009/07/30/universal_ssl_cert
+ ificate/
+
+ * Peter Eichhorn reported that "RENAME ../x ." didn't work; fixed
+ now.
+ * If only one file is FOPEN'd, FCLOSE given with no arguments would
+ close it; this was a "convenience feature" that turned out to be
+ dangerous. For safety FCLOSE has to require a specific channel
+ number or the word ALL.
+ * Added \fstrcmp(s1,s2,case,start,length), which has the advantage
+ over IF EQU,LGT,LLT that case sensitivity can be specified as a
+ function arg, and also substrings can be specified.
+ * New built-in functions:
+
+ \fcvtcsets(string,cs1,cs2)
+ Function to convert a string from one character set to
+ another.
+
+ \fdecodehex(string[,prefix])
+ Function to decode a string containing hex escapes.
+
+ \fstringtype(string)
+ Function to tell whether a string is 7-bit, 8-bit, or
+ UTF-8.
+
+ For the motivation for these features and an application that uses
+ them to analyze web logs, see the Weblog script below.
+ *
+
+ Lazy IF Conditions: Now you can do this:
+ define foo some number
+ if foo command
+
+ instead of this:
+ define foo some number
+ if \m(foo) command
+
+ Of course the old way still works too. But watch out because if the
+ variable name is the same as a symbolic IF condition (for example
+ COUNT), it won't do what you expected. (IF COUNT was used for loop
+ control in early versions of MS-DOS Kermit, before it got real FOR
+ and WHILE loops; it was added to C-Kermit for compatibility, and it
+ can't be removed because that could break existing scripts).
+ * Escape sequences are now stripped from text-mode session logs not
+ only in CONNECT sessions but also in whatever is logged by the
+ INPUT command; described in the [85]next section.
+ * New commands for selectively issuing progress or debugging messages
+ from scripts, also described in the next section.
+ * Fix from [86]John Dunlap to prevent the fixed packet-timeout
+ interval from going to an unexpected value.
+ * Alpha.04 fixes a problem with FTP connections made from 64-bit Unix
+ platforms. All the other changes in this section were to Alpha.03.
+ * Relaunching a closed SSH connection with the CONNECT command is now
+ possible, as it always has been with Telnet and other connection
+ types; suggested by Peter Eichhorn (needs testing).
+ * A symbol conflict fixed that prevented successful build on
+ [87]FreeBSD 8.0.
+ * Fixes from Christian Corti for building on SunOS 4.1.
+ * New aixg target for building on AIX with gcc.
+ * New aix+ibmssl target. This is nice because the IBM-supplied SSL
+ libraries and header files are in a known location; no need to
+ [88]set environment variables giving their locations.
+ * "Large File Support" is now included by default on Alpha and IA64
+ hardware on VMS 7.3 and later, and it should work much better than
+ before.
+ * Kermit's internal FTP client is now included by default in any
+ build that also includes TCP/IP networking. At present, the FTP
+ client seems to work well for binary-mode transfers; text (ASCII)
+ mode transfers still need some work. In builds that also include
+ Secure Sockets Layer (SSL) security (next item) the FTP client
+ should be able to make securely authenticated and encrypted
+ connections.
+ * In network builds that request OpenSSL support, e.g.:
+
+ $ @ckvker "" "" "CK_SSL"
+ the OpenSSL version is detected automatically and the appropriate
+ compile-time options are emitted (such as
+ OPENSSL_DISABLE_OLD_DES_SUPPORT).
+ * Preliminary / limited support for the ODS-5 file system on VMS 7.2
+ and later, Alpha and Itanium only (needs testing): Filenames can be
+ mixed case and can be longer.
+ * Support for older and older VMS versions.
+ * In the VMS build procedure, CKVKER.COM, the "i" option in P1 now
+ means don't include the internal FTP client, and the "f" option
+ means do not include "Large File" support. Large File support in
+ VMS really only applies to the file-transfer display and
+ statistics, which would go out of whack as soon as the byte count
+ overflowed 31 bits because this is C-Kermit, built with the C
+ compiler and the C library (runtime system), which did not support
+ long integers until VMS 7.3.
+ * The [89]LISP Operator ROUND now takes an optional second argument
+ that specifies the number of places to round to, e.g.
+ (ROUND dollars 2) rounds dollars to 2 decimal places.
+ * Improved pattern matching in many commands for both strings and
+ filenames.
+ * Various minor new features, plus numerous bug fixes and speedups.
+
+Incompatibilities
+
+ A top priority for new Kermit software releases has always been
+ backwards compatibility. A script written for a previous Kermit release
+ should run the same way in the new release.
+
+ There's one exception this time. The [90]\fsplit() function is
+ incredibly handy, it can do almost anything, up to and including
+ parsing a LISP program (the underlying code is the basis of the
+ [91]S-Expression interpreter). But did you ever try to use it to parse
+ (say) a Tab-Separated-List (TSV file) or Comma-Separated-List (CSV)? It
+ works as expected as long as the data contains only 7-bit characters.
+ But if your data contains (say) Spanish or German or Russian text
+ written in an 8-bit character set such as ISO 8859-1, every 8-bit
+ character (any value 128-255) is treated as a break character. This is
+ fixed in C-Kermit 9.0 by treating all 8-bit bytes as "include"
+ characters rather than break characters, a total reversal of past
+ behavior. I don't think it will affect anyone though, because if this
+ had happened to anyone, I would have heard about it!
+
+ Since most standard 8-bit character sets have control characters in
+ positions 128-160, it might have made sense to keep 128-160 in the
+ break set, but with the proliferation of Microsoft Windows code pages,
+ there is no telling which 8-bit character is likely to be some kind of
+ text, e.g. "smart quotes" or East European or Turkish accented letters.
+
+What's Not In C-Kermit 9.0
+
+ Some large projects that were contemplated have not been done,
+ including:
+ * IPv6. Honestly, there has been zero demand for this, and it would
+ be a lot of work and disruption to the code base. Volunteers
+ welcome, I guess. It could be a CS project.
+ * A database interface - MySQL or ODBC. For this one, there is some
+ demand but I haven't had a chance to even look into it.
+ * There's a looming issue with DES encryption; major vendors are
+ removing it from their platforms, starting with Apple in Mac OS X
+ 10.6, with Microsoft to follow suit. A secure version of Kermit can
+ be built without DES, but in limited testing successful connections
+ were spotty (e.g. with Kerberos 5).
+ * Cleaning up the Unix makefile. It has 25 years' worth of targets in
+ it. It is very likely safe to remove most of them, since (a) most
+ old platforms have gone away by now, or have been upgraded, due to
+ hacking vulnerabilities; (b) the market has consolidated
+ considerably; and (c) most of the new features of C-Kermit 9.0,
+ such as large files, won't be of any use on older platforms and
+ previous C-Kermit versions will remain available.
+ * Packages. Everybody wants an install package custom made for their
+ own computer, Linux RPMs being the prime example but far from the
+ only one. These will come, I suppose (especially with some Linux
+ sites having a policy against installing any application that does
+ not come as an RPM). In the meantime, here's a page that describes
+ some Kermit-specific issues in package construction:
+ [92]ckpackages.html.
+
+And a Loose End...
+Using External File-Transfer Protocols on Secure Connections
+
+ After C-Kermit 8.0.212 Dev.27 (2006/12/22), I spent a big chunk of time
+ trying to solve a particular problem that some of you have complained
+ about and others might be familiar with: If you use C-Kermit to make a
+ secure Telnet connection to another host (e.g. with Telnet SSL/TLS,
+ Kerberos, or SRP) and then attempt to transfer a file using an external
+ protocol such as Zmodem, it doesn't work.
+
+ That's because as coded (through 8.0.211), C-Kermit simply starts the
+ external protocol in a fork with its standard i/o redirected to the
+ connection. This completely bypasses the encryption and decryption that
+ is done by C-Kermit itself, and of course it doesn't work. The same
+ thing occurs if you use the REDIRECT command. The routine that handles
+ this is ttruncmd() in ckutio.c.
+
+ In order to allow (say) Zmodem transfers on secure connections, it is
+ necessary for C-Kermit to interpose itself between the external Zmodem
+ program and the connection, decrypting the incoming stream before
+ feeding it to Zmodem and encrypting Zmodem's output before sending out
+ the connection.
+
+ In principal, this is simple enough. We open a pseudoterminal pair
+ ("master" and "slave") for Zmodem's i/o and we create a fork and start
+ Zmodem in it; we read from the fork pty's standard output, encrypt, and
+ send to the net; we read from the net, decrypt, and write to the fork
+ pty's standard input.
+
+ In practice, it's not so simple. First of all, pseudoterminals (ptys)
+ don't seem to interface correctly with certain crucial APIs, at least
+ not in the OS's I have tried (Mac OS X, Linux, NetBSD, etc), such as
+ select(). And i/o with the pty often - perhaps always - fails to
+ indicate errors when they occur; for example, when the fork has exited.
+
+ But, even after coding around the apparent uselessness of select() for
+ multiplexing pty and net, and using various tricks to detect when the
+ external protocol exits and what its exit status is, I'm still left
+ with a show-stopping problem: I just simply can not download (receive)
+ a file with Zmodem, which is the main thing that people would probably
+ want to do. I can send files just fine, but not receive. The incoming
+ stream is delivered to Zmodem (to the pty slave) but upon arrival at
+ the Zmodem process itself, pieces are always missing and/or corrupt.
+ Yet I can receive files just fine if I use Kermit itself (C-Kermit or
+ G-Kermit) as the external protocol, rather than Zmodem.
+
+ I can think of two reasons why this might be the case:
+
+ 1. Zmodem sends all 8-bit bytes and control codes in the clear, and
+ maybe the pty is choking on them because it thinks it is a real
+ terminal.
+
+ But Zmodem puts its controlling terminal into raw mode. And C-Kermit
+ puts the pty into raw mode too, just for good measure. If any 0xFF
+ codes are in the Zmodem data stream, and it's a Telnet session, Kermit
+ does any needed byte stuffing/unstuffing automatically. Anyway, if I
+ tell Zmodem to prefix everything, it makes no difference.
+
+ 2. Zmodem is a streaming protocol and perhaps the pty driver can't
+ keep up with a sustained stream of input at network speeds. What
+ would be the method of flow control?
+
+ I can vary the size of the i/o buffers used for writing to the pty, and
+ get different effects, but I am not able to get a clean download, no
+ matter what buffer size I use. write()'ing to the pty does not return
+ an error, and I can't see the errors because they happen on the master
+ side. It's as if the path between the pty slave and master lacks flow
+ control; I deliver a valid data stream to the pty slave and the master
+ gets bits and pieces. This impression is bolstered somewhat by the
+ "[93]man 7 pty" page in HP-UX, which talks about some special modes for
+ ptys that turn off all termio processing and guarantee a
+ flow-controlled reliable stream of bytes in both directions - a feature
+ that seems to be specific to HP-UX, and exactly the one we need
+ everywhere.
+
+ Well, in Pass One I used C-Kermit's existing pty routines from
+ ckupty.[ch], which are well-proven in terms of portability and of
+ actually working. They are currently used by SET HOST /PTY for making
+ terminal connections to external processes. But these routines are
+ written on the assumption that the pty is to be accessed interactively,
+ and maybe they are setting the fork/pty arrangement up in such a way
+ that that's not suitable for file transfer. The Pass One routine is
+ called xttptycmd() in ckutio.c.
+
+ So in Pass Two I made a second copy of the routine, yttptycmd(), that
+ manages the pty and fork itself, so all the code is in one place and
+ it's simple and understandable. But it still doesn't work for Zmodem
+ downloads. In this routine, I use openpty() to get the pty pair, which
+ is not portable, so I can have access to both the master and slave pty
+ file descriptors. This version can be used only a platforms that have
+ openpty(): Linux, Mac OS X, NetBSD, etc.
+
+ In Pass Three, zttptycmd(), I tried using pipes instead of ptys, in
+ case ptys are simply not up to this task (but that can't be true
+ because if I make a Telnet or SSH connection into a host, I can send
+ files to it with Zmodem, and the remote Zmodem receiver is, indeed,
+ running on a pty). But pipes didn't work either.
+
+ In Pass Four, I extracted the relevant routines into a standalone
+ program based on yttptycmd() (the openpty() version, for simplicity),
+ which I tested on Mac OS X, the idea being to rule out any
+ "environmental" effects of running inside the C-Kermit process. There
+ was no difference -- Kermit transfers (with C-Kermit itself as the
+ external protocol) worked; Zmodem transfers (neither sz or lsz) did
+ not.
+
+ Well, it's a much longer story. As the external protocol, I've tried
+ rzsz, crzsz, and lrzsz. We know that some of these have quirks
+ regarding standard i/o, etc, which is one of the reasons for using ptys
+ in the first place, and i/o does work - just not reliably. Anyway, the
+ 1100 lines or so of [94]ckc299.txt, starting just below where it says
+ "--- Dev.27 ---" tell the full story. At this point I have to give up
+ and move on; it might be more productive to let somebody else who has
+ more experience with ptys take a look at it - if indeed anyone still
+ cares about being able to do Zmodem transfers over secure Telnet
+ connections.
+
+ C-Kermit 9.0 contains the three new routines (and some auxiliary ones),
+ but they are not compiled or called unless you build it specially:
+
+ make targetname KFLAGS=-DXTTPTYCMD (builds with xttptycmd())
+ make targetname KFLAGS=-DYTTPTYCMD (builds with yttptycmd())
+ make targetname KFLAGS=-DZTTPTYCMD (builds with zttptycmd())
+
+ These are all in [95]ckutio.c. As noted, the second one works only for
+ Linux, FreeBSD, NetBSD, and Mac OS X, because it uses non-POSIX,
+ non-portable openpty(). If you want to try it on some other platform
+ that has openpty(), you can build it like this:
+
+ make targetname "KFLAGS=-DYTTPTYCMD -DHAVE_OPENPTY"
+
+ (and let me know, so I can have HAVE_OPENPTY predefined for that
+ platform too). The best strategy to get this working, I think, would be
+ to concentrate on yttptycmd(), which is the simpler of the two
+ pty-based routines. If it can be made to work, then we'll see if we can
+ retrofit it to use the ckupty.c routines so it will be portable to
+ non-BSD platforms.
+
+ By the way, if you build with any of [XYZ]TTPTYCMD defined, then the
+ selected routine will always be used in place of ttruncmd(). This is to
+ allow testing on all kinds of connections, not just secure ones, in
+ both local and remote mode. Once the thing works, if it ever does, I'll
+ add the appropriate tests and/or commands.
+
+ By default, in the initial test release, C-Kermit 9.0 uses ttruncmd()
+ on serial connections and ttyptycmd() on network connections. Even when
+ a network connection is not encrypted, Kermit still needs to handle the
+ network protocol, e.g. the quoting of 0xff bytes on Telnet connections.
+
+Demonstration: Fetch Mail from POP Server Secured by SSL
+
+ [96]pop.ksc is a fully elaborated production script for fetching one's
+ mail from a POP3 server over a connection secured by SSL. For
+ explanation and documentation, [97]CLICK HERE. [98]mailcheck is a
+ wrapper for the pop.ksc script, which collects your password one time,
+ and then checks for new mail every 5 minutes (or other selected
+ interval) and calls pop.ksc to fetch it if there is any.
+
+Demonstration: HP Switch Configuration Backup
+
+ A common use for Kermit software is to make automated backups of the
+ configuration of network switches and routers, such as those made by
+ Cisco or Hewlett-Packard (although [99]tftp can be used for this, it is
+ not available in all such devices; Kermit, however, works with those
+ that have tftp as well as those that don't).
+
+ Typically a backup can be done by making a Telnet, SSH, or serial
+ connection to the device with Kermit and giving a command such as "show
+ config" at the command-line prompt of the device with Kermit's session
+ log activated. The result is a list of the commands that were used to
+ establish the current configuration, suitable for feeding back to the
+ device's console (e.g. with C-Kermit's TRANSMIT command) to reestablish
+ the same configuration or to duplicate it on another device.
+
+ At an HP installation it was noted, however, that while the HP switches
+ (various ProCurve models) produced the desired list of commands, they
+ were interspersed with escape sequences for special effects, thus
+ rendering the recorded sessions unsuitable for feeding back into the
+ switches.
+
+ C-Kermit 9.0 introduces a new feature to strip the offending sequences
+ out of a session log, leaving just the text. The command SET
+ SESSION-LOG TEXT activates this feature. In C-Kermit 9.0 Alpha.02 and
+ earlier, escape sequence stripping occurred only while logging
+ interactive (CONNECT) sessions; beginning with Alpha.03 it is done also
+ for data that is read by INPUT commands and therefore works for scripts
+ too.
+
+ A sample HP Switch Configuration Backup script is [100]HERE, and its
+ data file is [101]HERE. This script also illustrates some other new
+ features of Alpha.03:
+
+ MESSAGE text
+ This lets you put debugging messages in your script that can be
+ displayed or not, according to SET DEBUG MESSAGE (below). This
+ way you don't have to change your script for debugging. Hint:
+ In Unix, invoke the script like this:
+
+ $ DEBUG=1 scriptname arg1 arg2...
+
+ and then include the following command in your script:
+
+ if defined \$(DEBUG) set debug message on
+
+ XMESSAGE text
+ Like MESSAGE but prints the text with no line terminator, so it
+ can be continued by subsequent messages.
+
+ SET DEBUG MESSAGE { ON, OFF, STDERR }
+ ON means MESSAGE commands should print to standard output; OFF
+ means they shouldn't print anything; STDERR means the messages
+ should be printed to [102]stderr. DEBUG MESSAGE is OFF by
+ default, i.e. unless you SET it to ON or STDERR.
+
+ IF DEBUG command
+ Executes the command if SET DEBUG MESSAGE is not OFF.
+
+ The \v(lastcommand) variable
+ This variable contains the previous command. You can use it in
+ debugging and error message to show (for example) exactly what
+ the command was that just failed, without having to make a copy
+ of the command:
+
+set host somehost.somecompany.com
+if fail exit 1 "FATAL - \v(lastcommand)"
+
+ which, if the SET HOST command fails, prints "FATAL - set host
+ somehost.somecompany.com" and then exits with status 1 (which
+ normally indicates failure).
+
+Demonstration: HP iLO Blade Configuration
+
+ [103]THIS DOCUMENT describes a script in production use at Columbia
+ University for configuring and deploying racks full of HP blade servers
+ through their "integrated Lights Out" (iLO) management interface,
+ bypassing the tedious and error-prone process of configuring the
+ servers one by one through the vendor-provided point-and-click Web
+ interface, which is ill-suited to configuring large numbers of blades.
+ The script illustrates some of C-Kermit 9.0's new features; source code
+ is available through the link. The code is apt to change from time to
+ time as new requirements surface.
+
+Demonstration: IBM/Rolm/Siemens CBX Management
+
+ [104]THIS DOCUMENT describes a suite of scripts (some in production,
+ some in development) used to manage the Columbia campus 20,000-line
+ main telephone switch, along with about 10 satellite switches at
+ off-campus locations. These switches are 1980s technology*, their
+ management consoles are serial ports. Access is via Telnet to reverse
+ terminal servers. The scripts allow for interactive sessions as well as
+ automatic production (and in some cases formatting) of different
+ reports required by different groups at different intervals. These
+ scripts replace a whole assortment of ad-hoc ProComm ASPECT scripts
+ that were scattered all over the place, with passwords embedded. The
+ new scripts are intended to be run from a centralized server where
+ there is a single well-secured configuration file, and where they can
+ be used on demand, or in cron jobs. They are modular so code
+ duplication is minimal.
+ __________________________
+ * Of course the University is deploying new technology but the but the
+ old system will be used in parallel for some time to come.
+
+Demonstration: CSV and TSV Files
+
+ Contents
+
+ * [105]Reading a CSV or TSV Record and Converting it to an Array
+ * [106]Using \fjoin() to create a Comma- or Tab-Separated Value List
+ from an Array
+ * [107]Using CSV or TSV Files
+
+ Comma-Separated Value (CSV) format is commonly output by spreadsheets
+ and databases when exporting data into plain-text files for import into
+ other applications. Here are the details:
+
+ Comma-Separated List Syntax
+
+ 1. Each record is a series of fields.
+ 2. Records are in whatever format is used by the underlying file
+ system for lines of text.
+ 3. Fields within records are separated by commas, with zero or more
+ whitespace characters (space or tab) before and/or after the comma;
+ such whitespace is considered part of the separator.
+ 4. Fields with embedded commas must be enclosed in ASCII doublequote
+ characters.
+ 5. Fields with leading or trailing spaces must be enclosed in ASCII
+ doublequotes.
+ 6. Any field may be enclosed in ASCII doublequotes.
+ 7. Fields with embedded doublequotes must be enclosed in doublequotes
+ and each interior doublequote is doubled.
+
+ Here is an example:
+
+aaa, bbb, has spaces,,"ddd,eee,fff", " has spaces ","Muhammad ""The Greatest"" A
+li"
+
+ The first two are regular fields. The second is a field that has an
+ embedded space but in which any leading or trailing spaces are to be
+ ignored. The fourth is an empty field, but still a field. The fifth is
+ a field that contains embedded commas. The sixth has leading and
+ trailing spaces. The last field has embedded quotation marks.
+
+ Prior to C-Kermit 9.0 Alpha.06, C-Kermit did not handle CSV files
+ according to the specification above. Most seriously, there was no
+ provision for a separator to be surrounded by whitespace that was to be
+ considered part of the separator. Also there was no provision for
+ quoting doublequotes inside of a quoted string.
+
+Reading a CSV record
+
+ Now the \fsplit() function can handle any CSV-format string if you
+ include the symbolic include set "CSV" as the 4th parameter. To
+ illustrate, this program:
+
+def xx {
+ echo [\fcontents(\%1)]
+ .\%9 := \fsplit(\fcontents(\%1), &a, \44, CSV)
+ for \%i 1 \%9 1 { echo "\flpad(\%i,3). [\&a[\%i]]" }
+ echo "-----------"
+}
+xx {a,b,c}
+xx { a , b , c }
+xx { aaa,,ccc," with spaces ",zzz }
+xx { "1","2","3","","5" }
+xx { this is a single field }
+xx { this is one field, " and this is another " }
+xx { name,"Mohammad ""The Greatest"" Ali", age, 67 }
+xx { """field enclosed in doublequotes""" }
+exit
+
+ gives the following results:
+
+[a,b,c]
+ 1. [a]
+ 2. [b]
+ 3. [c]
+-----------
+[ a , b , c ]
+ 1. [a]
+ 2. [b]
+ 3. [c]
+-----------
+[ aaa,,ccc," with spaces ",zzz ]
+ 1. [aaa]
+ 2. []
+ 3. [ccc]
+ 4. [ with spaces ]
+ 5. [zzz]
+-----------
+[ "1","2","3","","5" ]
+ 1. [1]
+ 2. [2]
+ 3. [3]
+ 4. []
+ 5. [5]
+-----------
+[ this is a single field ]
+ 1. [this is a single field]
+-----------
+[ this is one field, " and this is another " ]
+ 1. [this is one field]
+ 2. [ and this is another ]
+-----------
+[ name,"Mohammad ""The Greatest"" Ali", age, 67 ]
+ 1. [name]
+ 2. [Mohammad "The Greatest" Ali]
+ 3. [age]
+ 4. [67]
+-----------
+[ """field enclosed in doublequotes""" ]
+ 1. ["field enclosed in doublequotes"]
+-----------
+
+ The separator \44 (comma) must still be specified as the break set (3rd
+ \fsplit() parameter). When "CSV" is specified as the include set:
+ * The Grouping Mask is automatically set to 1 (which specifies that
+ the ASCII doublequote character (") is used for grouping;
+ * The Separator Flag is automatically set to 1 so that adjacent field
+ separators will not be collapsed;
+ * All bytes (values 0 through 255) other than the break character are
+ added to the include set;
+ * Any leading whitespace is stripped from the first element unless it
+ is enclosed in doublequotes;
+ * Any trailing whitespace is trimmed from the end of the last element
+ unless it is enclosed in doublequotes;
+ * If the separator character has any spaces or tabs preceding it or
+ following it, they are ignored and discarded;
+ * The separator character is treated as an ordinary data character if
+ it appears in a quoted field;
+ * A sequence of two doublequote characters ("") within a quoted field
+ is converted to a single doublequote.
+
+ There is also a new TSV symbolic include set, which is like CSV except
+ without the quoting rules or the stripping of whitespace around the
+ separator because, by definition, TSV fields do not contain tabs.
+
+ Of course you can specify any separator(s) you want with either the
+ CSV, TSV, or ALL symbolic include sets. For example, if you have a TSV
+ file in which you want the spaces around each Tab to be discarded, you
+ can use:
+
+\fsplit(variable, &a, \9, CSV)
+
+ \9 is Tab.
+
+ The new symbolic include sets can also be used with \fword(), which is
+ just like \fsplit() except that it retrieves the nth word from the
+ argument string, rather than an array of all the words. In C-Kermit you
+ can get information about these or any other functions with the HELP
+ FUNCTION command, e.g.:
+
+C-Kermit> help func word
+
+Function \fword(s1,n1,s2,s3,n2,n3) - Extracts a word from a string.
+ s1 = source string.
+ n1 = word number (1-based) counting from left; if negative, from right.
+ s2 = optional break set.
+ s3 = optional include set (or ALL, CSV, or TSV).
+ n2 = optional grouping mask.
+ n3 = optional separator flag:
+ 0 = collapse adjacent separators;
+ 1 = don't collapse adjacent separators.
+
+ \fword() returns the n1th "word" of the string s1, according to the
+ criteria specified by the other parameters.
+
+ The BREAK SET is the set of all characters that separate words. The
+ default break set is all characters except ASCII letters and digits.
+ ASCII (C0) control characters are treated as break characters by default,
+ as are spacing and punctuation characters, brackets, and so on, and
+ all 8-bit characters.
+
+ The INCLUDE SET is the set of characters that are to be treated as
+ parts of words even though they normally would be separators. The
+ default include set is empty. Three special symbolic include sets are
+ also allowed:
+
+ ALL (meaning include all bytes that are not in the break set)
+ CSV (special treatment for Comma-Separated-Value records)
+ TSV (special treatment for Tab-Separated-Value records)
+
+ For operating on 8-bit character sets, the include set should be ALL.
+
+ If the GROUPING MASK is given and is nonzero, words can be grouped by
+ quotes or brackets selected by the sum of the following:
+
+ 1 = doublequotes: "a b c"
+ 2 = braces: {a b c}
+ 4 = apostrophes: 'a b c'
+ 8 = parentheses: (a b c)
+ 16 = square brackets: [a b c]
+ 32 = angle brackets: <a b c>
+
+ Nesting is possible with {}()[]<> but not with quotes or apostrophes.
+
+Returns string:
+ Word number n1, if there is one, otherwise an empty string.
+
+Also see:
+ HELP FUNCTION SPLIT
+
+C-Kermit>
+
+Using \fjoin() to create Comma- or Tab-Separated Value Lists from Arrays
+
+ In C-Kermit 9.0, \fsplit()'s inverse function, [108]\fjoin() received
+ the capability of converting an array into a comma-separated or a
+ tab-separated value list. Thus, given a CSV, if you split it into an
+ array with \fsplit() and then join the array with \fjoin(), giving each
+ function the new CSV parameter in the appropriate argument position,
+ the result will be will be equivalent to the original, according to the
+ CSV definition. It might not be identical, because if the result had
+ extraneous spaces before or after the separating commas, these are
+ discarded, but that does not affect the elements themselves. The new
+ syntax for \fjoin() is:
+
+ \fjoin(&a,CSV)
+ Given the array \&a[] or any other valid array designator, joins
+ its elements into a comma-separated list according to the
+ [109]rules listed above.
+
+ \fjoin(&a,TSV)
+ Joins the elements of the given array into a tab-separated list,
+ also described above.
+
+ [110]Previous calling conventions for \fjoin() are undisturbed,
+ including the ability to specify a portion of an array, rather than the
+ whole array:
+
+declare \&a[] = 1 2 3 4 5 6 7 8 9
+echo \fjoin(&a[3:7],CSV)
+3,4,5,6,7
+
+ Using \fsplit() and \fjoin() it is now possible to convert a
+ comma-separated value list into a tab-separated value list, and vice
+ versa (which is not a simple matter of changing commas to tabs or vice
+ versa).
+
+Applications for CSV Files
+
+ Databases such as MS Access or MySQL can export tables or reports in
+ CSV format, and then Kermit can read the resulting CSV file and do
+ whatever you like with it; typically something that could not be done
+ with the database query language itself (or that you didn't know how to
+ do that way): create reports or datasets based on complex criteria or
+ procedures, edit or modify some fields, etc, and then use \fjoin() to
+ put each record back in CSV form so it can be reimported into a
+ spreadsheet or database.
+
+ Here is a simple example in which we purge all records of customers who
+ have two or more unpaid bills. The file is sorted so that each license
+ purchase record is followed by its annual maintenance payment records
+ in chronological order.
+
+#!/usr/local/bin/kermit
+.filename = somefile.csv # Input file in CSV format
+fopen /read \%c \m(filename) # Open it
+if fail exit # Don't go on if open failed
+copy \m(filename) ./new # Make a copy of the file
+
+.oldserial = 00000000000 # Multiple records for each serial number
+.zeros = 0 # Unpaid bill counter
+
+while true { # Loop
+ fread /line \%c line # Get a record
+ if fail exit # End of file
+ .n := \fsplit(\m(line),&a,\44,CSV) # Split the fields into an array
+ if not equ "\m(oldserial)" "\&a[6]" { # Have new serial number?
+ # Remove all records for previous serial number
+ # if two or more bills were not paid...
+ if > \m(zeros) 1 {
+ grep /nomatch \m(oldserial) /output:./new2 ./new
+ rename ./new2 ./new
+ }
+ .oldserial := \&a[6] # To detect next time serial number changes
+ .zeros = 0 # Reset unpaid bill counter
+ }
+ if equ "\&a[5]" "$0.00" { # Element 5 is amount paid
+ increment zeros # If it's zero, count it.
+ }
+}
+fclose \%c
+
+ Rewriting the file multiple times is inelegant, but this is a quick and
+ dirty use-once-and-discard script, so elegance doesn't count. The
+ example is interesting in that it purges certain records based on the
+ contents of other records. Maybe there is a way to do this directly
+ with SQL, but why use SQL when you can use Kermit?
+
+ Here is the same task but this time no shelling out, and this time we
+ do change and add some fields and then join the result back into a CSV
+ record and write it out to a new file. The object is to create a record
+ for each license that shows not only the date and purchase price of the
+ license but also the date and amount of the last maintenance payment,
+ and to add new fields for sorting by anniversary (month and day):
+
+#!usr/local/bin/kermit +
+cd ~/somedirectory # CD to appropriate directory
+if fail exit 1 # Make sure we did
+.filename := \%1 # Filename from command line
+if not def filename { # If none give usage message
+ exit 1 "Usage: \%0: infile [ outfile ]"
+}
+fopen /read \%c \m(filename) # Open the input CSV file
+if fail exit # Make sure we did
+
+.output := \%2 # Output filename from command line
+if not def output { # Supply one if not given
+ .output := New_\m(filename)
+}
+fopen /write \%o \m(output) # Open output file
+if fail exit # Check that we did
+
+.serial = 00000000000 # Initialize serial number
+.licenses = 0 # and license counter
+
+fread /line \%c line # First line is column labels
+if fail exit # Check
+fwrite /line \%o "\m(line),AMM_DD,AYYYY" # Write new labels line
+
+# Remaining lines are license purchases (K95B) followed by zero or more
+# maintenance invoices (K95BM) for each license.
+
+.datepaid = 00/00/0000 # Initialize last maint payment date
+.amtpaid = $0.00 # Initialize last maint payment amount
+set flag off # For remembering we're at end of file
+while not flag { # Loop to read all records
+ fread /line \%c line # Read a record
+ if fail set flag on # If EOF set flag for later
+ .n := \fsplit(\m(line),&a,\44,CSV) # Break record into array
+ if ( flag || equ "\&a[3]" "K95B" ) { # License or EOF
+ if fail exit 1 "FAILED: \v(lastcommand)"
+ if licenses { # If this is not the first license
+ .\&x[5] := \m(amtpaid) # Substitute most recent amount paid
+ .\&x[21] := \m(datepaid) # Substitute most recent date paid
+ void \fsplit(\&x[18],&d,/) # Break up original (anniversary) date
+ # and put mm_dd and yyyy in separate fields for sorting...
+ fwrite /line \%o "\fjoin(&x,CSV),\flpad(\&d[1],2,0)_\flpad(\&d[2],2,
+0),\&d[3]"
+ if fail exit 1 WRITE # Check for error
+ xecho . # Show progress as one dot per record
+ }
+ if flag break # We're at EOF so we're finished
+ increment licenses # New license - count it
+ array copy &a &x # Keep this record while reading next
+ .serial := \&a[6] # Remember serial number
+ .datepaid = 00/00/0000 # Initial maintenance payment date
+ .amtpaid = $0.00 # and amount
+ continue # and go back to read next record
+ }
+ if not eq "\m(serial)" "\&a[6]" { # Catch out-of-sequence record
+ echo
+ echo "SEQUENCE: \m(serial)..\&a[6]: \&a[7] [\&a[1]]"
+ continue
+ }
+ if equ "\&a[5]" "" .\&a[5] = $0.00 # If amount is empty make it $0.00
+ if not equ "\&a[5]" "$0.00" { # If amount is not $0.00
+ .datepaid := \&a[21] # remember date paid
+ .amtpaid := \&a[5] # and amount paid
+ }
+}
+fclose ALL # Done - close all files and exit
+exit 0 Done.
+
+
+ The result imports back into Excel, where it can be sorted, formatted,
+ or otherwise manipulated as desired.
+
+Using CSV Files: Extending Kermit's Data Structures
+
+ Now that we can parse a CSV record, what would we do with a CSV file -
+ that is, a sequence of records? If we needed all the data available at
+ once, we would want to load it into a matrix of (row,column) values.
+ But Kermit doesn't have matrices. Or does it?
+
+ Kermit has several built-in data types, but you can invent your own
+ data types as needed using Kermit's macro feature:
+
+define variablename value
+
+ For example:
+
+define alphabet abcdefghijklmnopqrstuvwxyz
+
+ This defines a macro named alphabet and gives it the value
+ abcdefghijklmnopqrstuvwxyz. A more convenient notation (added in
+ C-Kermit 7.0, see [111]Table 2) for this is:
+
+.alphabet = abcdefghijklmnopqrstuvwxyz
+
+ The two are exactly equivalent: they make a literal copy the "right
+ hand side" as the value of the macro. Then you can refer to the macro
+ anywhere in a Kermit command as "\m(macroname)":
+
+echo "Alphabet = \m(alphabet)"
+
+ There is a second way to define a macro, which is like the first except
+ that the right-hand side is evaluated first; that is, any variable
+ references or function calls in the right-hand side are replaced by
+ their values before the result is assigned to the macro. The command
+ for this is ASSIGN rather than DEFINE:
+
+define alphabet abcdefghijklmnopqrstuvwxyz
+assign backwards \freverse(\m(alphabet))
+echo "Alphabet backwards = \m(backwards)"
+
+ which prints:
+
+Alphabet backwards = zyxwvutsrqponmlkjihgfedcba
+
+ This kind of assignment can also be done like this:
+
+.alphabet = abcdefghijklmnopqrstuvwxyz
+.backwards := \freverse(\m(alphabet))
+
+ [112]Any command starting with a period is an assignment, and the
+ operator (= or :=) tells what to do with the right-hand side before
+ making the assignment.
+
+ In both the DEFINE and ASSIGN commands, the variable name itself is
+ taken literally. It is also possible, however, to have Kermit compute
+ the variable name. This is done (as described in [113]Using C-Kermit,
+ 2nd Ed., p.457), using parallel commands that start with underscore:
+ _DEFINE and _ASSIGN (alias _DEF and _ASG). These are just like DEFINE
+ and ASSIGN except they evaluate the variable name before making the
+ assignment. For example:
+
+define \%a one
+_define \%a\%a\%a 111
+
+ would create a macro named ONEONEONE with a value of 111, and:
+
+define \%a one
+define number 111
+_assign \%a\%a\%a \m(number)
+
+ would create the same macro with the same value, but:
+
+define \%a one
+define number 111
+_define \%a\%a\%a \m(number)
+
+ would give the macro a value of "\m(number)".
+
+ You can use the _ASSIGN command to create any kind of data structure
+ you want; you can find some examples in the [114]Object-Oriented
+ Programming section of the [115]Kermit Script Library. In the following
+ program we use this capability to create a two-dimensional array, or
+ matrix, to hold the all the elements of the CSV file, and then to
+ display the matrix:
+
+fopen /read \%c data.csv # Open CSV file
+if fail exit 1
+
+.\%r = 0 # Row
+.\%m = 0 # Maximum columns
+while true {
+ fread /line \%c line # Read a record
+ if fail break # End of file
+ .\%n := \fsplit(\m(line),&a,\44,CSV) # Split record into items
+ incr \%r # Count this row
+ for \%i 1 \%n 1 { # Assign items to this row of matrix
+ _asg a[\%r][\%i] \&a[\%i]
+ }
+ if > \%i \%m { .\%m := \%i } # Remember width of widest row
+}
+fclose \%c # Close CSV file
+decrement \%m # (because of how FOR loop works)
+echo MATRIX A ROWS: \%r COLUMNS: \%m # Show the matrix
+
+for \%i 1 \%r 1 { # Loop through rows
+ for \%j 1 \%m 1 { # Loop through columns of each row
+ xecho "\flpad(\m(a[\%i][\%j]),6)"
+ }
+ echo
+}
+exit 0
+
+ The matrix is called a and its elements are a[1][1], a[1][2], a[1][3],
+ ... a[2][1], etc, and you can treat this data structure exactly like a
+ two-dimensional array, in which you can refer to any element by its "X
+ and Y coordinates". For example, if the CSV file contained numeric data
+ you could compute row and column sums using simple FOR loops and
+ Kermit's built-in one-dimensional array data type:
+
+declare \&r[\%r] # Make an array for the row sums
+declare \&c[\%m] # Make an array for the column sums
+for \%i 1 \%r 1 { # Loop through rows
+ for \%j 1 \%m 1 { # Loop through columns of each row
+ increment \&r[\%i] \m(a[\%i][\%j]) # Accumulate row sum
+ increment \&c[\%j] \m(a[\%i][\%j]) # Accumulate column sum
+ }
+}
+
+ Note that the sum arrays don't have to be initialized to zero because
+ Kermit's INCREMENT command treats empty definitions as zero.
+
+Demonstration Scripts for Webmasters
+
+ These scripts all use new features of C-Kermit 9.0.
+
+ [116]ksitemap
+ A C-Kermit 9.0 script to build sitemap.xml for a website,
+ complete with Google image extensions (this is the file used by
+ webmasters to get their sites crawled and indexed optimally).
+
+ [117]The Weblog Script
+ Reads a web log, extracts the Google searches, normalizes the
+ search strings, and prints the top 20 searches, along with their
+ counts.
+
+ [118]The Amazon Script
+ Reads an Amazon Associate orders report and lists the products
+ according to the number of orders for each, or the number of
+ clicks on each.
+
+ [119]Photoalbum
+ Makes a website from a collection of JPG images.
+
+ [120]Home [121]Kermit 95 [122]C-Kermit [123]Scripts [124]Current
+ [125]New [126]FAQ [127]Support
+
+
+ C-Kermit 9.0 / [128]The Kermit Project / [129]Columbia University /
+ [130]kermit@columbia.edu / [131]validate
+
+References
+
+ 1. http://www.columbia.edu/
+ 2. mailto:kermit@columbia.edu
+ 3. http://www.columbia.edu/kermit/index.html
+ 4. http://www.columbia.edu/kermit/k95.html
+ 5. http://www.columbia.edu/kermit/ckermit.html
+ 6. http://www.columbia.edu/kermit/ckscripts.html
+ 7. http://www.columbia.edu/kermit/current.html
+ 8. http://www.columbia.edu/kermit/whatsnew.html
+ 9. http://www.columbia.edu/kermit/faq.html
+ 10. http://www.columbia.edu/kermit/support.html
+ 11. http://www.columbia.edu/cu/computinghistory/books/#menagerie
+ 12. http://www.columbia.edu/kermit/ck90tables.html
+ 13. http://www.amazon.com/gp/product/1555581641?ie=UTF8&tag=aleidmoreldom-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=1555581641
+ 14. http://www.columbia.edu/kermit/ckermit.html#download
+ 15. http://www.columbia.edu/kermit/ckermit90.html#LargeFiles
+ 16. http://www.columbia.edu/kermit/ckermit90.html#TestLargeFiles
+ 17. http://www.columbia.edu/kermit/ckermit90.html#Bignums
+ 18. http://www.columbia.edu/kermit/ckermit90.html#force3
+ 19. http://www.columbia.edu/kermit/ckermit90.html#Vareval
+ 20. http://www.columbia.edu/kermit/ckermit90.html#rename
+ 21. http://www.columbia.edu/kermit/ckermit90.html#Other
+ 22. http://www.columbia.edu/kermit/ckermit90.html#Incompatibilities
+ 23. http://www.columbia.edu/kermit/ckermit90.html#NotIn9.0
+ 24. http://www.columbia.edu/kermit/ckermit90.html#LooseEnd
+ 25. http://www.columbia.edu/kermit/ckermit90.html#pop
+ 26. http://www.columbia.edu/kermit/ckermit90.html#HPswitch
+ 27. http://www.columbia.edu/kermit/ckermit90.html#iLO
+ 28. http://www.columbia.edu/kermit/ckermit90.html#Rolm
+ 29. http://www.columbia.edu/kermit/ckermit90.html#CSV
+ 30. http://www.columbia.edu/kermit/ckermit90.html#Otherdemos
+ 31. http://www.columbia.edu/kermit/ck60manual.html
+ 32. http://www.amazon.com/gp/product/B002ACPF9M?ie=UTF8&tag=aleidmoreldom-20&linkCode=as2&camp=1789&creative=390957&creativeASIN=B002ACPF9M
+ 33. http://www.columbia.edu/kermit/ckermit70.html
+ 34. http://www.columbia.edu/kermit/ckermit80.html
+ 35. http://www.columbia.edu/kermit/ckscripts.html
+ 36. http://www.columbia.edu/cu/computinghistory/dec20.html
+ 37. mailto:fdc@columbia.edu
+ 38. http://www.columbia.edu/kermit/k95.html
+ 39. http://www.columbia.edu/kermit/cu-bsd-license.html
+ 40. http://www.columbia.edu/kermit/ckermit90.html#LargeFiles
+ 41. http://www.columbia.edu/kermit/ck90tables.html
+ 42. http://www.columbia.edu/kermit/ck90tables.html
+ 43. http://www.columbia.edu/kermit/ckermit90.html#force3
+ 44. http://www.columbia.edu/kermit/ckermit90.html#Vareval
+ 45. http://www.columbia.edu/kermit/ckrename.html
+ 46. http://www.columbia.edu/kermit/csv.html
+ 47. http://www.columbia.edu/kermit/csetnames.html
+ 48. http://www.columbia.edu/kermit/ckermit90.html#HPswitch
+ 49. http://www.columbia.edu/kermit/ckdaily.html
+ 50. http://www.columbia.edu/kermit/cu-bsd-license.html
+ 51. http://www.opensource.org/
+ 52. http://kermit.columbia.edu/ck90tables.html#LF
+ 53. ftp://kermit.columbia.edu/kermit/utils/bigfile.c
+ 54. http://www.columbia.edu/kermit/ckermit80.html#x9
+ 55. http://www.columbia.edu/kermit/ck90tables.html#LF
+ 56. ftp://kermit.columbia.edu/kermit/scripts/ckermit/easter2
+ 57. http://www.columbia.edu/kermit/em-apex.html
+ 58. http://www.iridium.com/
+ 59. http://science1.nasa.gov/science-news/science-at-nasa/2006/09jan_electrichurricanes/
+ 60. http://www.columbia.edu/kermit/ek.html
+ 61. ftp://kermit.columbia.edu/kermit/ek/simirid/
+ 62. http://www.columbia.edu/kermit/ek.html
+ 63. http://www.columbia.edu/kermit/ckermit70.html#x7.10.10
+ 64. http://www.columbia.edu/kermit/csv.html
+ 65. http://www.columbia.edu/kermit/ckermit70.html#x1.11
+ 66. http://www.columbia.edu/kermit/ckermit70.html
+ 67. http://www.columbia.edu/kermit/ckermit80.html#x9
+ 68. http://www.columbia.edu/kermit/csetnames.html
+ 69. http://www.columbia.edu/kermit/ckdaily.html
+ 70. http://www.columbia.edu/kermit/ftpclient.html
+ 71. http://www.columbia.edu/kermit/ckdaily.html
+ 72. http://www.columbia.edu/kermit/csetnames.html
+ 73. http://www.columbia.edu/kermit/ckermit90.html#Otherdemos
+ 74. http://www.columbia.edu/kermit/input_nowrap.html
+ 75. http://www.columbia.edu/~fdc/mm/index.html
+ 76. http://www.columbia.edu/kermit/photoalbum.html
+ 77. http://www.columbia.edu/~fdc/kermit/logserial.html
+ 78. http://www.columbia.edu/kermit/ckermit90.html#message
+ 79. http://www.columbia.edu/kermit/ckermit90.html#NotIn9.0
+ 80. http://www.columbia.edu/kermit/csv.html#join
+ 81. http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-3767
+ 82. http://www.columbia.edu/kermit/ftp/test/tar/x.zip
+ 83. http://www.mseclab.com/?p=180
+ 84. http://www.theregister.co.uk/2009/07/30/universal_ssl_certificate/
+ 85. http://www.columbia.edu/kermit/ckermit90.html#HPswitch
+ 86. http://www.columbia.edu/kermit/em-apex.html
+ 87. http://www.freebsd.org/releases/8.0R/announce.html
+ 88. http://www.columbia.edu/kermit/security81.html#x4.2.3
+ 89. http://www.columbia.edu/kermit/ckermit80.html#x9
+ 90. http://www.columbia.edu/kermit/ckermit80.html#x8.7.2
+ 91. http://www.columbia.edu/kermit/ckermit80.html#x9
+ 92. http://www.columbia.edu/kermit/ckpackages.html
+ 93. http://docs.hp.com/en/B9106-90013/pty.7.html
+ 94. http://www.columbia.edu/kermit/test/text/ckc299.txt
+ 95. http://www.columbia.edu/kermit/test/text/ckutio.c
+ 96. http://www.columbia.edu/~fdc/mm/pop
+ 97. http://www.columbia.edu/~fdc/mm/
+ 98. http://www.columbia.edu/~fdc/mm/mailcheck
+ 99. http://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol
+ 100. http://www.columbia.edu/kermit/ftp/scripts/ckermit/gethpconfig
+ 101. http://www.columbia.edu/kermit/ftp/scripts/ckermit/TestSwitches.txt
+ 102. http://en.wikipedia.org/wiki/Standard_streams
+ 103. http://kermit.columbia.edu/cudocs/ilosetup.html
+ 104. http://www.columbia.edu/kermit/cudocs/cbx.html
+ 105. http://www.columbia.edu/kermit/ckermit90.html#record
+ 106. http://www.columbia.edu/kermit/ckermit90.html#join
+ 107. http://www.columbia.edu/kermit/ckermit90.html#file
+ 108. http://www.columbia.edu/kermit/ckermit80.html#fjoin
+ 109. http://www.columbia.edu/kermit/ckermit90.html#rules
+ 110. http://www.columbia.edu/kermit/ckermit80.html#fjoin
+ 111. http://www.columbia.edu/kermit/ckermit90.html#varasg
+ 112. http://www.columbia.edu/kermit/ckermit70.html#x7.9
+ 113. http://www.amazon.com/gp/product/1555581641?ie=UTF8&tag=aleidmoreldom-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=1555581641
+ 114. http://www.columbia.edu/kermit/ckscripts.html#oops
+ 115. http://www.columbia.edu/kermit/ckscripts.html
+ 116. http://www.columbia.edu/kermit/ksitemap.html
+ 117. http://www.columbia.edu/kermit/weblog.html
+ 118. http://kermit.columbia.edu/ftp/scripts/ckermit/amazon
+ 119. http://www.columbia.edu/kermit/photoalbum.html
+ 120. http://www.columbia.edu/kermit/index.html
+ 121. http://www.columbia.edu/kermit/k95.html
+ 122. http://www.columbia.edu/kermit/ckermit.html
+ 123. http://www.columbia.edu/kermit/ckscripts.html
+ 124. http://www.columbia.edu/kermit/current.html
+ 125. http://www.columbia.edu/kermit/whatsnew.html
+ 126. http://www.columbia.edu/kermit/faq.html
+ 127. http://www.columbia.edu/kermit/support.html
+ 128. http://www.columbia.edu/kermit/index.html
+ 129. http://www.columbia.edu/
+ 130. mailto:kermit@columbia.edu
+ 131. http://validator.w3.org/check?uri=http%3A%2F%2Fkermit.columbia.edu%2Fckermit90.html