|
The Kermit Project |
Now hosted by
Panix.com
New York City USA •
kermit@kermitproject.org
| ||||||||
|
C-Kermit 9.0 was released in 2011.
C-Kermit 10.0 is in Beta test
as of 16 May 2022.
This file last updated: Sun Aug 21 15:48:16 2022
Authors: Frank da Cruz and Christine M. Gianone
Address: The Kermit Project
Columbia University
612 West 115th Street
New York NY 10025-7799
USA
E-Mail: support@kermitproject.org
Web: http://www.kermitproject.org/
Or: http://www.kermit-project.org/
For the full text of the third-party copyright notices, see Appendix V.
until the third edition of Using C-Kermit is published. We apologize for the scattered documentation and will consolidate it when we are able.
[ Top ] [ C-Kermit ] [ Kermit Home ]
[ Top ] [ C-Kermit ] [ Kermit Home ]
echo {this is a string}
would print:
this is a string
whereas:
echo "this is a string"
printed:
"this is a string"
In C-Kermit 8.0, both print:
this is a string
To force the doublequotes to be treated as part of the string, use either of the following forms:
echo {"this is a string"}
echo ""this is a string""
Similarly, to force braces to be treated as part of the string:
echo "{this is a string}"
echo {{this is a string}}
Other incompatibilities:
[ Top ] [ Contents ] [ C-Kermit ] [ Kermit Home ]
There were no functional changes from 196 to 197.
Fixes applied after C-Kermit 7.0.197 was released:
Source code: Big flexelint and "gcc -Wall" audit and cleanup.
Configuration:
Connections:
Commands and scripts:
File transfer:
Character sets:
The following bugs in C-Kermit 8.0.200 were fixed in 8.0.201:
Other fixes are listed in the previous section.
[ Top ] [ Contents ] [ C-Kermit ] [ Kermit Home ]
This section does not apply to Kermit 95 2.0, which has its own built-in SSH client, which is documented SEPARATELY.On most UNIX platforms, C-Kermit can make SSH (Secure SHell) connection by running the external SSH command or program through its pseudoterminal interface. The command is:
Kermit's SSH command gives you all the features of Kermit on an SSH connection: command language, file transfer, character-set translation, scripting, and all the rest. By default, C-Kermit invokes SSH with "-e none", which disables the ssh escape character and makes the connection transparent for purposes of file transfer. You can, however, change the SSH invocation to whatever else you might need (an explicit path, additional command-line arguments, etc) with:
In most cases, these connections work quite well. They can be scripted like any other connection, and file transfer goes as fast as, or faster than, on a regular Telnet connection. In some cases, however, the underlying pseudoterminal driver is a limiting factor, resulting in slow or failed file transfers. Sometimes you can work around such problems by reducing the Kermit packet length. Note that Kermit does not consider SSH connections to be reliable, so it does not offer to use streaming in Kermit protocol transfers (but you can force it with SET RELIABLE or SET STREAMING if you wish).
The SSH command is like the TELNET command: it enters CONNECT mode automatically when the connection is made. Therefore, to script an SSH connection, use:
set host /pty ssh -e none [ other-options ] host if fail ...to make the connection.
Here's a sequence that can be used to make a connection to a given host using Telnet if the host accepts it, otherwise SSH:
if not defined \%1 exit 1 Usage: \%0 host
set quiet on
set host \%1 23 /telnet
if fail {
set host /pty ssh -l \m(user) -e none \%1
if fail exit 1 \%1: Telnet and SSH both fail
echo SSH connection to \%1 successful
} else {
echo Telnet connection to \%1 successful
}
In SSH v2, it is possible to make an SSH connection direct to a Kermit server system if the host administrator has configured the SSH server to allow this; CLICK HERE for details.
Since Kermit uses external ssh client software, and since there are different ssh clients (and different releases of each one), the exact command to be used to make an SSH/Kermit connection can vary. Here is the command for the OpenSSH 3.0.2p1 client:
set host /pipe ssh -e none [ -l username ] -T -s hostname kermit
Example:
set host /pipe ssh -e none -l olga -T -s hq.xyzcorp.com kermit
The SSH client might or might not prompt you for a password or other information before it makes the connection; this depends on your SSH configuration (your public and private keys, your authorized hosts file, etc). Here's a brief synopsis of the OpenSSH client command syntax ("man ssh" for details):
You might want to include other or additional ssh command-line options; "man ssh" explains what they are. Here are some examples for the OpenSSH 3.0.2p1 client:
Once you have an SSH connection to a Kermit server, it's just like any other connection to a Kermit server (and very similar to a connection to an FTP server). You give the client file transfer and management commands for the server, and the server executes them. Of course you can also give the client any other commands you wish.
[ SSH Kermit Server Subsystem ] [ Kermit 95 Built-in SSH Client ]
Although C-Kermit 7.0 could make HTTP connections to Web servers, it could do so only when no other connection was open, and the procedure was somewhat awkward. C-Kermit 8.0 improves matters by:
Persistent HTTP connections are managed with the following commands:
A URL starts with a protocol name, which must be http or https in this case; optionally includes a username and password; and must contain a host name or address:
protocol://[user[.password]]@host[:port][URI]
HTTP is Hypertext Transfer Protocol. HTTPS is the secure (SSL/TLS) version of HTTP. The TCP service port is derived from the protocol prefix (so normally the ":port" field is omitted). Thus the URL protocol name specifies a default TCP service port and the URL user and password fields can take the place of the /USER and /PASSWORD switches (Section 2.2.1). The optional URI is a "compact string of characters for identifying an abstract or physical resource" (RFC 2396), such as a file. It must begin with a slash (/); if the URI is omitted, "/" is supplied. Examples:
Persistence is accomplished unilaterally by C-Kermit 8.0. An HTTP 1.0 server closes the connection after each action. Although HTTP 1.1 allows multiple actions on the same connection, an HTTP 1.1 server tends to close the connection if it is idle for more than a few seconds, to defend itself against denial-of-service attacks. But when you use Kermit's HTTP OPEN command to create a connection, Kermit reopens it automatically (if necessary) for each HTTP action until you close it with HTTP CLOSE, regardless of the server's HTTP protocol version, or how many times it closes the connection.
Firewalls can be negotiated through proxies with the following commands:
If you include switches with an HTTP action command (such as GET or PUT), they apply only to that command.
/HEADER:tag:value
To send more than one header, use braces for grouping:
/HEADER:{{tag:value}{tag:value}...}
For a list of valid tags and value formats see RFC 2616, "Hypertext Transfer Protocol -- HTTP/1.1". A maximum of eight headers may be specified.
http open www.columbia.edu if failure stop 1 HTTP OPEN failed: \v(http_message) http get kermit/index.html if failure stop 1 HTTP GET failed: \v(http_message) (more actions possible here...) http close
A self-contained HTTP action occurs when a URL is given instead of a remote file name to an HTTP action command. In this case, Kermit makes the HTTP connection, takes the action, and then closes the connection. If an HTTP connection was already open, it is closed silently and automatically.
http get http://www.kermitproject.org/index.html
Kermit's HTTP action commands are as follows. Switches may be included with any of these to override switch (or default) values given in the HTTP OPEN command.
In the remaining HTTP action commands, the distinction between a remote filename and a URL are the same as in the HTTP GET command.
Note the limitations of HTTP protocol compared to (say) FTP or Kermit. There is no command for changing directories, no standard way to get file or directory lists, no way to transfer file groups by using wildcard notation, etc, and therefore no good way to (say) fetch all pages, descend through subdirectories, perform automatic updates, etc. There is no assurance a connection will stay open and, as noted, there is no provision for data conversion between unlike platforms. The data's MIME headers can be used for postprocessing.
GET /index.html HTTP/1.1 Host: www.columbia.edu:80 User-agent: C-Kermit 8.0 Authorization: Basic base64-encoded-username-password
These might be followed by any others specified with a /HEADERS: switch:
Accept: image/gif, image/x-xbitmap, image/jpeg, *.* Accept-Encoding: gzip Accept-Language: en Accept-Charset: iso-8859-1,utf-8 Cookie: cookie-data
The server sends back a short report about the file prior to sending the file contents. Example:
HTTP/1.1 200 OK Date: Fri, 24 Aug 2001 21:09:39 GMT Server: Apache/1.3.4 (Unix) Last-Modified: Mon, 06 Aug 2001 21:16:13 GMT ETag: "1fa137-10d7-3b6f091d" Accept-Ranges: bytes Content-Length: 4311 Content-Type: text/html
If you want to have this information available to a Kermit script you can use the /ARRAY switch to have Kermit put it in array, one line per array element. Example:
set exit warning off
http open www.columbia.edu
if fail exit 1 Can't reach server
http /array:&a get /index.html
if fail exit 1 Can't get file
echo Header lines: \fdim(&a)
for \%i 1 \fdim(&a) 1 {
echo \%i. \&a[\%i]
}
Note that the "Date:" item is the current date and time; the "Last-Modified:" item is the file's modification date and time. An example showing how to use this information is presented in Section 8.13.7.
And you must make a connection to the secure HTTP port: service name HTTPS, port number 443 (as opposed to service HTTP, port 80). You can also make secure connections to other ports by including the /TLS or /SSL switch with the HTTP OPEN command, if the host supports SSL/TLS on the given port:
The quality of the SSL/TLS connection depends on the cipher suite. There are several possibilities:
If the verification succeeded, the connection would be encrypted with one-way (server-to-client) authentication. This connection is not subject to a MITM attack.
If a username and password are transmitted over this connection, they are not subject to interception. However, the standard risks associated with passing the password to the host for verification apply; for example, if the host has been compromised, the password will be compromised.
An HTTP connection is made with the HTTP OPEN command:
Certificates are covered in the separate Kermit Security Reference for C-Kermit 8.0. You should let Kermit know to verify certificates with the SET AUTHENTICATION TLS command. For example:
There are many other options; see the security document for details.
Now suppose you need need to fetch the file denoted by the following URL:
https://myuserid:mypassword@wwws.xyzcorp.com/clients/info/secret.html
Once you have set up the handling of certificates as desired, you can use the following Kermit commands:
http /user:myuserid /password:mypassword open www1.xyzcorp.com https
if success {
http get /clients/info/secret.html
http close
}
As another example, let's say that you have a web form you need to
populate with three fields: red,white and blue.
<FORM ACTION="http://www.xyzcorp.com/cgi-bin/form.cgi" METHOD="POST"> <INPUT NAME="Red"> <INPUT NAME="White"> <INPUT NAME="Blue"> </FORM>
You can handle this with the HTTP POST command. The data to be posted is stored in the local file data.txt.
Red=seven stripes&White=six stripes&Blue=fifty stars
and the response from the server will be stored into response.txt.
http open www.xyzcorp.com http
if success {
http /array:c post data.txt /cgi-bin/form.cgi response.txt
http close
}
In this scenario, the Common Gateway Interface (CGI) sends a
response whether it succeeds or fails in a script-dependent manner. The
script can either report success and enclose the response data; or it might
send a 302 Found error which indicates that the "Location:" header should be
used to determine the URL at which the data can be found.
To display all the HTTP variables at once, type SHOW VAR HTTP:
C-Kermit> http open www.columbia.edu C-Kermit> http get lkjlkjlkjlkj C-Kermit> sho var http \v(http_code) = 404 \v(http_connected) = 1 \v(http_host) = www.columbia.edu:80 \v(http_message) = Not Found \v(http_security) = NULL C-Kermit>
Usage: ./http host [ options... ]
-h This message.
-d Debug to debug.log.
-S Stay (issue command prompt when done).
-Y Do not execute Kermit initialization file.
-q Quiet (suppress most messages).
-u name Username.
-P password Password.
-g pathname Get remote pathname.
-p pathname Put remote pathname.
-H pathname Head remote pathname.
-l pathname Local path for -g, -p, and -H.
-z opt[=value] Security options...
cert=file Client certificate file
certsok Accept all certificates
key=file Client private key file
secure Use SSL
verify=n 0 = none, 1 = peer , 2 = certificate required
The "host" argument is the name of a Web host, e.g. www.columbia.edu. The action options are -p, -g, and -H. If you give an action option, Kermit does the action and then exits. If you give a host without an action option, Kermit makes an HTTP connection to the host and then gives you the C-Kermit prompt. Here's a simple example that fetches a publicly readable Web page:
http www.columbia.edu -g kermit/index.html
If you need to access a website for which a username and password are required, you can supply them on the command line with -u and -P. If you include a username but omit the password, Kermit prompts you for it:
http www.columbia.edu -u olga -p kermit/index.html -l index.html Password:
Note that when PUT'ing files to websites, you have to supply both the -p (remote pathname) and -l (local path) options.
If your version of Kermit is built with SSL/TLS security, you can also use the -z option to make secure HTTP (https) connections.
Finally, as noted in Section 16, you can also give a URL instead of a host name and options.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
3.1. Making and Managing FTP Connections
3.2. Making Secure FTP Connections
3.3. Setting FTP Preferences
3.4. Managing Directories and Files
3.5. Uploading Files With FTP
3.6. Downloading Files With FTP
3.7. Translating Character Sets
3.8. FTP Command Shortcuts
3.9. Dual Sessions
3.10. Automating FTP Sessions
3.11. Advanced FTP Protocol Features
Earlier versions of C-Kermit and K95 included an FTP command, but it simply invoked an external FTP client. Now, by popular demand, Kermit includes its own built-in FTP client that offers the following advantages over traditional FTP clients (and its previous interface to them):
And best of all:
[ Top ] [ FTP Top ] [ FTP Client Overview ] [ FTP Script Tutorial ] [ C-Kermit Home ] [ Kermit Home ]
A Kermit FTP session can be established by command-line options, by URL, or by interactive commands.
kermit -9 hostname
or if a non-default FTP port is needed:
kermit -9 hostname:port
You can also specify the username on the command line with the -M ("My User ID") option that was already there for other connection types:
kermit -9 hostname -M olga
If you specify the username on the command line, Kermit uses it when making the connection and does not prompt you for it (but it does prompt you for the password if one is required).
Once the connection is made, you get the regular Kermit prompt, and can give interactive commands such as the ones described below. When you give a BYE command, Kermit closes the session and exits, just as a regular FTP client would do. If you don't want Kermit to exit when you give a BYE command, include the -S ("Stay") option on the command line.
Other Kermit command-line options that are not specific to non-FTP connections should affect the FTP session in the expected ways; for example, -i and -T force binary and text mode transfers, respectively.
File transfers can not be initiated on the "kermit -9" command line; for that you need to use Kermit's FTP personality (next section) or you can use URLs (Section 3.1.3).
ftp [ options ] hostname [ port ]
In this case the options are like those of a regular FTP client:
-d Debug: enables debug messages and creates a debug.log file. -n No autologin: Kermit should not send your user ID automatically. -t Packet trace: accepted but is treated the same as -d. -v Verbose: accepted but ignored (operation is verbose by default). -i Not interactive: accepted but ignored.
and the hostname can also be a URL (explained in Section 3.1.3). To specify a non-default TCP port for the FTP server, include the port number or name after the hostname.
There are also some bonus options that allow you to execute an entire FTP session from the shell command line, as long as you don't include the -n option. These are not available with regular FTP clients, and at least one of these options (-g) conflicts with UNIX ftp (where -g means "no globbing", which does not apply to Kermit), and some of them (like the options above) also conflict with regular Kermit command-line options:
-m mode = "passive" (default) or "active"
-Y Don't execute the Kermit initialization file [1]
-q Quiet, suppresses all but error messages [1]
-S Stay, don't exit automatically [1]
-A Autologin anonymously [2]
-u name Username for autologin [2] (synonym: -M [1])
-P password Password for autologin (see cautions below) [2]
-D directory cd after autologin [2]
-b Binary mode [2]
-a Text ("ascii") mode [2] (synonym: -T [1])
-R Recursive (works with -p) [4]
-p files Files to put (upload) after autologin [2] (synonym: -s [1])
-g files Files to get (download) after autologin [3]
[1] Same as Kermit, not available in regular FTP clients.
[2] Conflicts with Kermit, not available in regular FTP clients.
[3] Same as Kermit, conflicts with regular FTP clients.
[4] Conflicts with Kermit, available in some FTP clients.
Fancier options such as restart, character-set translation, filename collision selection, automatic move/rename/delete, etc, are not available from the command line; for these you can use the commands described in the following sections. The -R option might also work with -g (GET) but that depends on the server.
The following security options are also available, explained in Section 3.2:
-k realm Kerberos 4 realm [4] -f Kerberos 5 credentials forwarding [4] -x autoencryption mode [4] -c cipher SRP cipher type [4] -H hash SRP encryption hash [4] -z option Security options [4]
If you include -A or specify a name of "anonymous" or "ftp", you are logged in anonymously and, in the absence of -P, Kermit automatically supplies a password of "user@host", where "user" is your local user ID, and "host" is the hostname of the computer where Kermit is running. If you do not include -p or -g, Kermit enters command mode so you can type commands or execute them from a script.
If you include -p or -g, Kermit attempts to transfer the specified files and then exits automatically at the end of the transfer unless you also included -S (Stay). It uses the "brief" file transfer display (one line per file) unless you include the -q option to suppress it.
When uploading files with -p, Kermit switches automatically between text and binary mode for each file.
When downloading, you can either specify a particular mode (text or binary) to be used for all the files, or you can let Kermit select the type for each file automatically, based on its name (see Sections 3.5 and 3.6 for greater detail). In UNIX be sure to quote any wildcard characters to prevent the shell from expanding them, as shown in the examples just below. Filename collisions are handled according Kermit's FILE COLLISION setting (if specified in your Kermit customization file; otherwise the default, which is BACKUP).
It should go without saying that the -P option should be used with caution. In addition to the well-known risks of transmitting plaintext passwords over the Internet, in this case the password also echos to the screen if you type it, and can be seen in ps and w listings that show the user's currently active command and command-line arguments. Thus command-line FTP sessions are most appropriate for secure or anonymous connections (those that do not require passwords).
Here's an example in which you download the latest C-Kermit "tarball" from the Kermit Project FTP archive:
ftp -A ftp.kermitproject.edu -bg kermit/archives/ckermit.tar.gz
This assumes that "ftp" is a symbolic link to C-Kermit. It logs you in anonymously and gets the ckermit.tar.gz file in binary mode from the kermit/archives directory.
Here's a slightly more ambitious example that illustrates CD'ing to the desired server directory to get a group of files in text mode (in this case the C-Kermit source files):
ftp -A ftp.kermitproject.org -D kermit/f -ag "ck[cuw]*.[cwh]" makefile
In this case we CD to the kermit/f directory so we don't have to include it in each file specification, and we quote the ck[cuw]*.[cwh] specification so the shell doesn't expand it, since we have to pass it as-is to the server. Note also that the quotes don't go around the entire file list; only around each file specification that needs to be quoted.
Here's one more example, that uploads a debug log file in binary mode to the Kermit incoming directory (as we might ask you to do when following up on a problem report):
ftp -A ftp.kermitproject.org -D kermit/incoming -bp debug.log
In this case the -D option is required to tell the server where to put the incoming file.
Unless the -Y option is included, your Kermit initialization file (.mykermrc in UNIX, K95.INI in Windows) is executed before the command line options, so you can set any FTP-related preferences there, as described in the subsequent sections.
If the FTP personality is used, the service must be "ftp". In all cases, a hostname or address must be included. If a user is included but no password, you are prompted for the password. If a path (filename) is included:
If a path is included, but a username is not included, "anonymous" is used and an appropriate user@host password is supplied automatically. If authentication is successful, Kermit attempts to GET the file indicated by the path or, if the path is the name of a directory, it asks the server for a directory listing. In both cases, Kermit disconnects from the server and exits after the operation is complete (unless you have included the -S option on the command line).
This scheme allows Kermit to be used as the FTP helper of other applications, such as Web browsers, with all its advantages over other FTP clients (especially the ones that are built in to most Web browsers), e.g. that it can be given wildcards, and it can pick text and binary mode automatically for each file.
HINT: suppose somebody sends you an FTP URL in email, or you see it in some text. If your terminal screen supports copy/paste, copy the url, and then at the shell prompt type "kermit", a space, and then paste the URL, e.g.:
$ kermit ftp://alpha.greenie.net/pub/mgetty/source/1.1/mgetty1.1.27-O
"$ is the shell prompt; the part you type is underlined, the rest is pasted in. Kermit does the rest.
An FTP session is established with the FTP OPEN command:
The hostname can be an IP host name, numeric IP address, or if you have a network directory active (SET NETWORK DIRECTORY; see Chapter 6 of Using C-Kermit), an entry name in the directory. In the latter case, if the given hostname matches exactly one entry, the associated name or address is used; if it matches more than one, Kermit cycles through them until one is found that can be opened; if it matches none, then the hostname is used as-is. If a directory is active but you want to bypass directory lookup, include an "=" sign at the beginning of the hostname, and/or use a numeric IP address.
When an FTP connection is opened, the default file-transfer mode is set to binary if the client and server platforms are alike (e.g. both of them are some kind of UNIX), and to text ("ascii") if they are not alike. This has no particular effect for uploading since Kermit automatically switches between text and binary mode for each file, but might be important for downloading. The connection is also set to Stream mode and File structure. Record- or page-oriented file transfers are not supported by C-Kermit's FTP client.
The optional FTP OPEN switches are:
When a username or password is missing, a prompt is issued at the controlling terminal and you must type the response; the response can not be scripted. Use the switches to avoid prompts, or one of the secure authentication methods described in the next section, or see SET FTP AUTOLOGIN and the FTP USER and similar commands described later in this section.
Examples:
ftp open ftp.kermitproject.org /anonymous ; Open and log in anonymously ftp ftp.kermitproject.org /anonymous ; The OPEN keyword can be omitted ftp xyzcorp.com ; Open and maybe prompt for username ftp xyzcorp.com /user:olga ; Open and log in as olga ftp testing.abccorp.com 449 ; Specify a special TCP port number ftp testing.abccorp.com /user:olaf /password:secret 449
The FTP OPEN command succeeds if a connection was opened to the server (even if the given username and password were not valid) and fails otherwise (see Section 3.8 for details).
When your FTP session is complete, you can terminate it as follows:
The following commands can be used to achieve greater control over the connection and login process:
Example:
set ftp autologin off ; One thing at a time please ftp xyzcorp.com ; Try to make the connection if fail exit 1 FTP connection failed ; Check that it was made ftp user olga secret ; Now log in to the server if fail exit 1 FTP login failed ; Check that it worked ftp account 103896854 ; Login OK - send account if fail echo WARNING - FTP ACCT failed ; Warn if problem ... ; (have session here) bye ; Log out and disconnect
The following commands are used to control or get information about the FTP connection. Any particular FTP server does not necessarily support all of them.
[ Top ] [ FTP Top ] [ C-Kermit Home ] [ Kermit Home ]
Many web servers support the CONNECT command and it can be configured to allow outgoing connections to authenticated user to any TCP/IP hostname/port combination accessible to the web server. The limitations of HTTP CONNECT is that it can only be used for outgoing connections for protocols that are implemented using TCP/IP. Protocols such as Kerberos authentication that use UDP/IP cannot be tunneled using HTTP CONNECT.
Kermit provides support for the use of HTTP CONNECT proxy services with the command:
SET TCP HTTP-PROXY [/USER:username /PASSWORD:password] hostname/ip-address[:port]
When a port is not specified the default port configured on the HTTP server is used. This is frequently port 443. When a hostname is specified, it is resolved using the DNS available to the web server.
In the previous section, you can see several examples of traditional insecure authentication: username and password sent across the network in clear text. Of course this is bad practice on at least two counts: (1) storing passwords in files (such as script files) gives access to the target systems to anybody who can obtain read access to your scripts; and (2) sending this information over the network leaves it open to interception by network sniffers or compromised hosts.
Because of the increasing need for security on the Internet, FTP servers are beginning to appear that offer secure forms of authentication, in which no information is sent over the network that would allow anyone who intercepts it to usurp your identity and gain your access rights.
Kermit provides an equivalent form of FTP security for each type of IETF standard security implemented in Telnet. These include GSSAPI-KERBEROS5, KERBEROS4, Secure Remote Password (SRP), and Transport Layer Security (SSL and TLS). It does not presently include SSL tunneling nor any form of SSH v1 or v2. When Kermit is built with the necessary libraries, secure FTP connections are attempted by default, in which all connections are authenticated and the command and data channels are private.
The use of authentication and encryption for FTP connections can be adjusted with the commands listed below, which are available only if your version of Kermit was built with the corresponding security options and libraries:
CLEAR Data is sent in plaintext and not protected against tampering. CONFIDENTIAL Data is encrypted but not protected against tampering. PRIVATE Data is encrypted and is protected against tampering. SAFE Data is sent in plaintext but protected against tampering.
The default is PRIVATE.
BLOWFISH_ECB CAST5_ECB DES_ECB DES3_ECB BLOWFISH_CBC CAST5_CBC DES_CBC DES3_CBC BLOWFISH_CFB64 CAST5_CFB64 DES_CFB64 DES3_CFB64 BLOWFISH_OFB64 CAST5_OFB64 DES_OFB64 DES3_OFB64
The default is DES3_ECB.
Command-line options:
Caution: If your FTP connection is secured via AUTH TLS, it is not possible to interrupt a file transfer. This is a limitation of all known FTP servers that support AUTH TLS.
Note that when using certain security methods, such as SSL or TLS, you may be prompted to confirm or verify certain actions or conditions, for example, whether to accept self-signed certificates. This can interfere with unattended operation of scripts; see Section 3.10.
[ Top ] [ FTP Top ] [ C-Kermit Home ] [ Kermit Home ]
FTP screen messages and displays are controlled by the following commands:
Set all of these to OFF when silent running is desired.
If you want want specific FTP preference settings to be in effect for all your Kermit FTP sessions, put the desired SET FTP commands in your Kermit customization file (~/.mykermrc in UNIX, K95CUSTOM.INI in Windows).
[ Top ] [ FTP Top ] [ C-Kermit Home ] [ Kermit Home ]
(There can also be an HTTP connection, but the commands in this section don't apply to HTTP connections.)
Thus in general, each such command comes in three forms:
Kermit's FTP file and directory management commands are as follows. When an R-command is included in the Synonyms list, be sure to read Section 3.8 about rules for use of R-commands.
ftp dir ; Show listing of all files on screen
ftp dir *.txt ; List *.txt files on screen
ftp dir *.txt > somefile ; Put listing in somefile
ftp dir *.txt >> somefile ; Append listing to somefile
ftp dir *.txt | sort > somefile ; Put sorted listing in somefile
ftp dir | more ; Runs list through "more"
ftp dir | sort | more ; Runs list through "sort" and "more"
C-Kermit> ftp modtime signpost C-Kermit> echo \v(ftp_message) 20010807113542.014 C-Kermit> ftp mput /after:\v(ftp_message)GMT *
Note that we must append "GMT" to the date-time string to let the /AFTER switch know the time is GMT rather than local.
ftp chmod 664 oofa.txt
Not all servers support this command. For non-UNIX-based servers, you might need to use FTP QUOTE or FTP SITE and the appropriate platform-specific FTP server command.
/ERROR-ACTION: /FILENAMES: /NOBACKUPFILES /QUIET /EXCEPT: /LARGER-THAN: /NODOTFILES /NOPAGE /PAGE /RECURSIVE /SMALLER-THAN:
When used with FTP DELETE, the /RECURSIVE switch deletes files but not directories, and furthermore depends on the server providing recursive file lists, which is not the normal behavior. For further details, see the descriptions of these switches in Section 3.6. Synonyms: FTP MDELETE (Kermit makes no distinction between DELETE and MDELETE); RDELETE.
[ Top ] [ FTP Top ] [ C-Kermit Home ] [ Kermit Home ]
Unlike normal FTP clients, Kermit does not prompt you by default (or at all) for each file; it just sends them, just as it does with Kermit protocol. The filespec can be a literal filename or a Kermit pattern, described in:
http://www.kermitproject.org/ckermit70.html#x4.9
Kermit patterns are equivalent to C-Shell patterns and provide a fair amount of flexibility in selecting which files to send, which is augmented by the file-selection switches presented in Section 3.5.1.
If a PUT or MPUT command results in one file being uploaded, it succeeds if the file is uploaded completely and fails otherwise. If more than one file is selected for upload, success or failure depends on the FTP ERROR-ACTION setting; if it is PROCEED (the default setting), then the [M]PUT command succeeds if at least one of the files was completely uploaded, and fails otherwise, If FTP ERROR-ACTION is QUIT, the [M]PUT command succeeds if all selected files were uploaded successfully, and fails if any file failed.
FTP uploads may be interrupted just like Kermit uploads. While the transfer is in progress, type:
X to interrupt the current file and go on to the next file. Z to cancel the current file and all remaining files. ^C (Control-C): Like Z, but might act more quickly.
MPUT may be used as in regular FTP clients, but it is not required to send multiple files; in Kermit it is required only if you want to give multiple file specifications. Examples:
ftp put oofa.txt ; Send a single file oofa.txt ftp put oofa.txt budget.txt ; Send single file oofa.txt as budget.txt ftp put *.txt ; Send all *.txt files ftp mput *.txt ; Send all *.txt files (same as "put *.txt") ftp mput *.txt foo.bar ; Send all *.txt files plus foo.bar
The distinction between PUT and MPUT is important only when more than one filespec is given, just like the distinction between Kermit SEND and MSEND:
ftp put oofa.txt budget.txt ; Send oofa.txt AS budget.txt ftp mput oofa.txt budget.txt ; Send oofa.txt AND budget.txt
If the source file specification includes any path segments, for example:
put /tmp/oofa.txt put subdir/another/andanother/oofa.txt
the path portion is stripped from the filename that is sent to the server. However, if an as-name contains a path, it is retained. Examples:
ftp put /usr/doc/oofa.txt ; Send as "oofa.txt". ftp put oofa.txt /tmp/oofa.txt ; Send as "/tmp/oofa.txt"
The latter example sends the file oofa.txt from your current local directory to the server's /tmp directory. This works only if the server uses the same directory notation that you used in the as-name AND the given directory already exists on the server AND if you have write access to it.
Use caution when uploading from a case-sensitive file system, such as UNIX, to a file system that is not case sensitive, such as Windows or VMS. If you have two files in UNIX, AA and aa and upload both of them, the second one will overwrite the first. The only way around this provided by FTP protocol is its "unique server names" feature (SET FTP UNIQUE-SERVER-NAMES or the /UNIQUE switch described below).
C-Kermit>ftp put ? Filename, or switch, one of the following: /after: /larger-than: /rename-to: /array: /listfile: /server-character-set: /as-name: /local-character-set: /server-rename-to: /before: /move-to: /simulate /binary /nobackupfiles /smaller-than: /command /nodotfiles /tenex /delete /nofollowlinks /text /dotfiles /not-after: /transparent /error-action: /not-before: /type: /except: /permissions: /update /filenames: /quiet /unique-server-names /filter: /recover /followlinks /recursive
Since most of these switches are common to Kermit's SEND and MSEND commands, they described only briefly here. For greater detail see:
http://www.kermitproject.org/ckermit70.html#x1.5 (explanation of switches)
http://www.kermitproject.org/ckermit70.html#x4.7 (file-transfer switches)
First the file-selection switches:
ftp put /after:{1 jan 2000 0:00:00} *
ftp put /after:-5days *
ftp put /as-name:array.txt /array:&a(or, to send a segment of the array, /array:&a[100:199]). If you don't include an /AS-NAME, a name of "_array_x_" is used (where x is the array letter). If you include this switch, most other switches are meaningless and ignored.
ftp put /command /as-name:{userlist} {finger | sort -r}
Now the other switches:
ftp put /binary /filter:{gzip -c \v(filename)} /as-name:\v(filename).gz *
cd source-directory ftp computerb.xyzcorp.com ( authentication details... ) ftp cd target-directory ftp put [ switches ] *
But if the total size is large or the network slow, this would be unnecessarily time-consuming. Worse, if other users or sites had to update whenever new files appeared in B's directory, this would cause them unnecessary work. By including the /UPDATE switch:
ftp put /update [ other-switches ] *
only those files that changed since last time are uploaded. Here's how it works. For each local file that is selected for uploading:
All time comparisons take place in Coordinated Universal Time (UTC)(1), also known as GMT or Zulu time: Timezone 0; standard time, without daylight savings.
WARNING: Some FTP servers, such as Novell NWFTPD.NLM, ignore or misimplement the FTP specification and send local time rather than UTC.
Update mode is useful only when always used in the same direction. When you upload (PUT) a file with FTP, the destination file receives the current timestamp on the server's computer, not the original file's timestamp (2). If you try to FTP PUT /UPDATE the same file again, it will be skipped (as expected) since the remote copy is newer. However, if you try to FTP GET /UPDATE the same file (Section 3.6), it will be transferred for the same reason.
To check the availability of PUT /UPDATE on a particular connection, issue an FTP MODTIME command for a file that is known to exist on the server. If it succeeds, PUT /UPDATE should work and in that case, you can run a procedure like the one above every day: the first time, it sends all the files; after that, it sends only the ones that changed. If a transaction log is active, a notation is included for any files that are skipped.
Notes:
Here's how it works. When you include the /RECOVER switch:
If the switch is accepted, then for each selected file:
To safeguard file integrity, recovery is not attempted unless all the preconditions are met. For the widest possible usefulness, APPEND is used rather than RESTART. For stream transfers (the only kind that Kermit supports) the results are the same.
By design, the /RECOVER switch can be included with any FTP PUT or MPUT command, even if it specifies a group of files. This allows you to resume an interrupted batch transfer from where it left off. The files that were already completely sent are skipped, the file that was interrupted is recovered, and the remaining files are uploaded.
By the way, it doesn't matter how the original partial file was uploaded -- FTP, Kermit, Zmodem, etc: as long as the preconditions are met, it can be recovered with FTP PUT /RECOVER, or for that matter also using Kermit protocol and SEND /RECOVER.
A word of caution, however, when the original upload was in text mode with character-set translation (Section 3.7):
Kermit has no way of knowing anything about the previous upload. As a safeguard, an error occurs if you include /RECOVER and also specify a character-set of UCS2 or UTF8, since recovery can't possibly work in that situation. Otherwise, it's up to you to avoid unsafe recovery operations.
[ Top ] [ FTP Top ] [ C-Kermit Home ] [ Kermit Home ]
/PERMISSIONS (FTP protocol has no mechanism for this).
/[NOT-]BEFORE, /[NOT-]AFTER (because of the timezone problem).
/RECOVER works only in binary mode.
/RECURSIVE has limited utility.
The commands for downloading are:
In both the FTP GET and MGET commands, any filenames that contain spaces must be enclosed in braces or doublequotes (see Section 5 for details).
FTP downloads may be interrupted just like Kermit transfers. While the transfer is in progress, type:
Before proceeding, a brief word about temporary files. In FTP protocol, the MGET command works by requesting a file list from the server, and then (internally) issuing a GET command (FTP RETR protocol directive) for each file. The file list returned by the server can be any size at all, so in case it is huge, we don't store it in memory; instead we put it in a temporary file. For troubleshooting purposes, you should be aware of two points:
mget /text /as-name:\v(filename).new *.c
This gets all ".c" files and stores them with ".new" appended to their names. See the C-Kermit 7.0 Update Notes for details.
The file selection switches are:
Even when the server does not provide recursive file lists, [M]GET /RECURSIVE
forces Kermit to replicate any directory structure implied or expressed by
the server's file list. For example:
Gets the file named somefile from the server's somepath
directory and puts it Kermit's current (or download) directory, whereas:
creates the path locally and then puts the file in it. Similarly for MGET:
downloads all the files in all the data subdirectories of all the
subdirectories of the server's current directory and stores them locally
in Kermit's current (or download) directory, whereas:
re-creates the server's directory structure locally.
get somepath/somefile
get /recursive somepath/somefile
mget */data/*
mget /recursive */data/*
The FTP protocol does not include explicit mechanisms for recursion, so Kermit builds upon what is available. Although an Internet draft describes a mechanism ("MLSD") that would allow protocol-driven recursion, similar to Kermit's File Attribute packets (circa 1984), it has not yet attained RFC or standard status, and servers are not yet widely available that offer this feature. In the meantime, the effectiveness of MGET /RECURSIVE depends on the FTP server implementation. If the server returns a recursive list in response to the standard NLST command (whose behavior is ill-defined), Kermit's FTP MGET /RECURSIVE command uses it to re-create the remote directory tree locally. If the server supports MLSD, C-Kermit 8.0.206 and Kermit 95 2.1 and later are able to sense it automatically and use it, as described below in Section 3.11.
The /BEFORE:, /AFTER:, /NOT-BEFORE:, and /NOT-AFTER: switches are not available for downloading because of the confusion with timezones. Would the given times be in the local timezone, the server's timezone, or GMT? The FTP server's directory listings show its own local times but since we don't know what timezone the server is in, there's no way to reconcile our local times with the server's. Similarly, /PERMISSIONS can't be preserved in downloads because FTP protocol provides no means of querying the server for a file's permission.
Source-file disposition switches:
Destination-file disposition switches:
get /text /to-screen /filter:more oofa.txt
In fact, you should always use /TO-SCREEN with /FILTER or /COMMAND when the command would result in displaying the incoming file on the screen; otherwise C-Kermit would have no way of knowing to suppress its file transfer display (since it can't be expected to know what the command or filter does).
ftp mget /rename-to:\v(filename).ok *
causes each file that is successfully downloaded to have ".ok" appended to its name. For details see Section 4.1 of the C-Kermit 7.0 Update Notes.
The file transfer display does not show the /MOVE-TO or /RENAME-TO value, since the incoming file has not yet been moved or renamed.
The name under which an incoming file is to be stored is determined as follows:
If the resulting name coincides with the name of a local file that already exists, we have a filename collision. Collisions are handled according to the currently selected collision action:
FTP GET /UPDATE and /COLLISION:UPDATE mean to download only those files whose
modification dates on the server are later than those on the client.
Date-time comparisons are done in Coordinated Universal Time (UTC, GMT, ZULU).
The command:
Downloads all matching remote files into a single local file (in whatever
order the server sends them).
FTP MGET /COLLISION:APPEND /AS-NAME:newfilename *.*
Here's how download recovery works:
If the /RECOVER switch is accepted, then for each selected file:
The /RECOVER switch can be included with any FTP GET or MGET command, even if it specifies a group of files. This lets you resume an interrupted batch transfer from where it left off. The files that were already completely sent are skipped, the file that was interrupted is recovered, and the remaining files are uploaded. BUT... unlike with uploading, where this can be done with any mixture of text and binary files, when downloading, it can only be done if all the files are binary.
It doesn't matter how the original partial file was downloaded -- FTP, Kermit, HTTP, Zmodem, etc: as long as the preconditions are met, it can be recovered with FTP [M]GET /RECOVER, or for that matter also with GET /RECOVER (using Kermit protocol).
[ Top ] [ FTP Top ] [ C-Kermit Home ] [ Kermit Home ]
TO REITERATE: If you SET FTP CHARACTER-SET TRANSLATION ON but do not specify an FTP SERVER-CHARACTER-SET, outbound text files are converted to UTF-8 and inbound text files are assumed to be UTF-8. If this is not appropriate, be sure to also specify the desired FTP SERVER-CHARACTER-SET.
You can use "special" (non-ASCII) characters in filenames in all the client / server file management commands (FTP MKDIR, RMDIR, DIRECTORY, VDIRECTORY, DELETE, etc), and also in file-transfer commands. When giving commands such as FTP DIR (RDIR) and FTP PWD (RPWD), the reply is translated too, so you can read it. In this example, the client and server use entirely different codes to represent the special characters of German:
C-Kermit> ftp xyzcorp.de /anonymous C-Kermit> set ftp server-character-set latin1 C-Kermit> set file character-set german C-Kermit> rcd Städte C-Kermit> rpwd "/pub/ftp/Städte is current directory" C-Kermit> rdir -rw-rw---- 1 olaf 54018 Jan 6 17:58 Adenbüttel.txt -rw-rw---- 1 ursula 373 Jan 5 15:19 Aßlar.txt -rw-rw---- 1 gisbert 482 Jan 5 15:20 Blowatz.txt -rw-rw---- 1 gudrun 124 Jan 5 15:19 Böblingen.txt -rw-rw---- 1 olga 14348 Jan 7 14:23 Köln.txt
When the client and server file systems use different character sets, you should take care to use only those characters that the two sets share in common when creating filenames or text-file contents. For example, PC code pages contain a lot line- and box-drawing characters, and sometimes "smart quotes", etc, that are not found in ISO standard 8-bit character sets. You should be especially careful to avoid using such characters in filenames.
WARNING: It is not advisable to use UCS-2 (or any Unicode transformation other than UTF-8) "on the wire", i.e. as a server character set. Most FTP servers are not able to cope with it, since it contains lots of 0 (NUL) characters. If you do use it, Kermit does not translate filenames to or from UCS-2, for reasons well known to C programmers (for example, UNIX APIs assume filename strings are NUL-terminated). UTF-8 is the preferred (and standard) Unicode format for the Internet.
FTP character-set translations differ from the regular Kermit ones by not restricting translations to a file-character-set / transfer-character-set pair. You can have Kermit's FTP client translate between any pair of character sets it knows about. You can see the list of supported character sets by typing either of the following:
set ftp server-character-set ? set file character-set ?
A typical list looks like this (CLICK HERE for an explanation of the names):
C-Kermit>set file char ? One of the following: ascii cp869-greek hebrew-7 mazovia-pc british cyrillic-iso hebrew-iso next-multinational bulgaria-pc danish hp-roman8 norwegian canadian-french dec-kanji hungarian portuguese cp1250 dec-multinational iso2022jp-kanji shift-jis-kanji cp1251-cyrillic dg-international italian short-koi cp1252 dutch jis7-kanji spanish cp437 elot927-greek koi8 swedish cp850 elot928-greek koi8r swiss cp852 euc-jp koi8u ucs2 cp855-cyrillic finnish latin1-iso utf8 cp858 french latin2-iso cp862-hebrew german latin9-iso cp866-cyrillic greek-iso macintosh-latin C-Kermit>
Thus you can translate not only between private sets (like PC code pages) and standard ones (like Latin-1) as in Kermit protocol, but also between any given pair of private sets (e.g. CP852 and Mazovia). All conversions go through Unicode as the intermediate character set, resulting in a minimum of character loss, since Unicode is a superset of all other character sets known to Kermit.
In addition to the SET commands listed above, the FTP PUT and MPUT commands include switches that apply only to the current command:
In other words, if you include one or both of these switches with a PUT or MPUT command, they are used. Similarly, the /TRANSPARENT switch disables character-set translation for the PUT or MPUT command despite the prevailing FTP CHARACTER-SET-TRANSLATION and SERVER-CHARACTER-SET settings.
When uploading, the FILE CHARACTER-SET setting is ignored unless you have forced Kermit not to scan local files by including a /TEXT or /BINARY switch with your [M]PUT command, or by disabling automatic text/binary switching in some other way.
Examples:
ftp put /local-char:cp852 /server-char:latin2 magyar.txt
set ftp server-character-set latin2
in your Kermit customization file, and then you can omit the /SERVER-CHARACTER-SET: switch from your FTP PUT commands:
ftp put /local-char:cp852 magyar.txt
set ftp server-character-set latin2 ; ISO 8859-2 set file default 7-bit-character-set hungarian ; ISO 646 Hungarian set file default 8-bit-character-set cp852 ; PC East European Code Page
and now PUT and MPUT will automatically detect and switch among ISO 646 Hungarian, Code Page 852, UTF-8, and UCS-2 encodings, translating each one to Latin-2 for uploading:
ftp put *.txt
And since binary files are also detected automatically, the latter can be simplified to:
ftp put *
even when "*" matches a diverse collection of binary and text files, because translations are skipped automatically for binary files.
In C-Kermit 8.0, the pattern lists used with FTP GET are not the same lists used with Kermit transfers, and can not be viewed with SHOW PATTERNS, nor adjusted with ADD and REMOVE TEXT-PATTERNS and BINARY-PATTERNS, or SET FILE TEXT-PATTERNS and BINARY-PATTERNS. Configuration of the FTP patterns list will be added in a future release.
Examples:
Note that any pair of 8-bit character sets is likely to have some incompatibilities. Any characters in the source file that do not have equivalents in the destination file's character set are converted to question marks. This applies to both filenames and to text file contents.
Also note that the server's ability to accept special characters in filenames depends on the particular server. For example:
get Grüße.txt
works with WU-FTPD, but:
mget Grüß*.txt
does not.
[ Top ] [ FTP Top ] [ C-Kermit Home ] [ Kermit Home ]
Here is a complete list of affected commands:
Kermit Command FTP Equivalent (none) FTP [ OPEN ] LOGIN FTP USER LOGOUT FTP RESET BYE FTP BYE FINISH FTP BYE CLOSE FTP BYE HANGUP FTP BYE BINARY FTP TYPE BINARY TEXT (or ASCII) FTP TYPE ASCII SEND (or PUT) FTP PUT MSEND (or MPUT) FTP MPUT RESEND FTP PUT /RECOVER CSEND FTP PUT /COMMAND GET FTP GET MGET FTP MGET REGET FTP GET /RECOVER REMOTE HELP (RHELP) FTP HELP REMOTE CD (RCD) FTP CD (CWD) REMOTE PWD (RPWD) FTP PWD REMOTE DIRECTORY (RDIR) FTP DIRECTORY REMOTE DELETE (RDEL) FTP DELETE REMOTE MKDIR (RMKDIR) FTP MKDIR REMOTE RMDIR (RRMDIR) FTP RMDIR REMOTE RENAME (RRENAME) FTP RENAME REMOTE TYPE (RTYPE) FTP TYPE REMOTE EXIT (REXIT) FTP BYE
The commands in the right-hand column always access FTP. The commands in the left column can access either Kermit protocol or FTP:
Note that file-management commands such as DIRECTORY, DELETE, CD, PWD, MKDIR, RMDIR, HELP, RENAME, COPY, TYPE, and so on, always apply locally, no matter what kind of connection you have. This is the opposite of most FTP clients, where these commands are intended for the server, and require an "L" prefix for local execution (e.g. "dir" gets a directory listing from the server, "ldir" gets a local directory listing). To illustrate with the CD command and a typical UNIX FTP client:
Client Server Change Local Directory Change Remote Directory FTP FTP lcd cd (cwd) Kermit Kermit cd rcd, remote cd Kermit FTP cd ftp cd, rcd, remote cd
Also note that not all REMOTE commands are useful with FTP, since FTP servers do not offer the corresponding functions. These include:
Finally note that command shortcuts do not apply to the HELP command. For help about an FTP command, use (for example) "help ftp delete", not "help delete" or "help rdelete".
[ Top ] [ FTP Top ] [ C-Kermit Home ] [ Kermit Home ]
ftp foo.bar.baz.com if fail ... (log in) set host foo.bar.baz.com if fail ... (log in)
Now you have both an FTP and Telnet connection to the same host (of course they could also be to different hosts, and you could also have a direct or dialed serial connection instead of a Telnet connection). Now assuming you have a Kermit server on the far end of the Kermit connection:
rcd incoming ; Changes Kermit server's directory (= REMOTE CD) ftp cd incoming ; Changes FTP server's directory put oofa.txt ; Sends a file on the Kermit connection ftp put oofa.txt ; Sends a file on the FTP connection bye ; Shuts down the Kermit connection ftp bye ; Shuts down the FTP connection
Note that PUT and SEND are synonyms for both FTP and Kermit connections.
You can also establish dual sessions on the Kermit command line:
kermit -j host1 -9 host2
This makes a Telnet connection to host1 and an FTP connection to host2.
[ Top ] [ FTP Top ] [ C-Kermit Home ] [ Kermit Home ]
CLICK HERE for an FTP scripting tutorial.
The FTP OPEN command sets:
The FTP USER command (or FTP OPEN /USER:, or FTP with automatic login) sets:
The current COMMAND-PROTECTION-LEVEL and DATA-PROTECTION-LEVEL values are reflected in:
The FTP GET-PUT-REMOTE setting is reflected in:
Every FTP command sets the \v(success) variable, as well as the following two FTP-specific variables:
1xx = Positive Preliminary Reply
2xx = Positive Completion Reply
3xx = Positive Intermediate Reply
4xx = Transient Negative Completion Reply
5xx = Permanent Negative Completion Reply
\v(cps) Characters per second of most recent transfer. \v(filespec) File specification used in most recent transfer. \v(fsize) Size of file most recently transferred. \v(tfsize) Total size of file group most recently transferred. \v(xferstatus) Status of most recent transfer (0 = success, 1 = failure). \v(tftime) Elapsed time of most recent transfer, in seconds.
During an FTP transfer, the per-file variables are:
\v(filename) Name of current file. \v(filenumber) Ordinal file number in group (1, 2, 3, ...)
define error if fail { ftp bye, stop 1 Error: \%1 }
set transact brief
log t
ftp ftp.xyzcorp.com /anonymous
if fail stop 1 Connection failed
if not \v(ftp_loggedin) stop 1 Login failed
ftp cd incoming
error {ftp cd}
cd upload
error {local cd}
ftp put /delete *
error {put}
ftp bye
First we define an error handling macro to be used after the connection is made. Then we set up a brief-format transaction log to keep a record of our file transfers. Then we make a connection to the host and log in anonymously. The "if fail" command checks whether the connection was made. The "if not" command checks whether login was successful. Obviously the script should not continue unless both tests succeed.
Next we change to the server's 'incoming' directory and to our own 'upload' directory, and send all the files that are in it (they can be any mixture of text and binary files), deleting each source file automatically after it is successfully uploaded. Each of these operations is checked with the ERROR macro, which prevents the script from continuing past a failure.
Finally we close the FTP session with the "bye" command.
Just like any other Kermit script, this one can be used in many ways:
We could have used command shortcuts like "rcd", "put", and "bye", but since they can be ambiguous under certain circumstances, it is better to avoid them in scripts; they are intended mainly for convenience during interactive use. However, if you wish to use the shortcuts in a script, you can do it this way (error handling omitted for brevity):
local \%t ; Declare a local temporary variable assign \%t \v(ftp_getputremote) ; Save current FTP GET-PUT-REMOTE setting set ftp get-put-remote ftp ; Choose FTP orientation ftp xyzcorp.com /anonymous ; Open an FTP connection get oofa.txt ; GET a file put foo.bar ; PUT a file rdel yesterday.log ; Delete a file on the server bye ; Log out and disconnect from server. set ftp get-put-remote \%t ; Restore previous GET-PUT-REMOTE setting
Of course, FTP scripts can also be written as macros. This lets you pass parameters such as hostnames, usernames, and filenames to them:
define doftpget {
if < \v(argc) 4 end 1 Usage: \%0 host user remotefile [ localfile ]
ftp \%1 /user:\%2
if fail end 1 FTP OPEN \%1 failed
if not \v(ftp_loggedin) end 1 FTP LOGIN failed
ftp get {\%3} {\%4}
if fail end 1 FTP GET \%3 failed
ftp bye
}
Add this definition to your Kermit customization file, and it will always be available when you start Kermit. This macro lets you download a file with FTP by giving a single command, e.g.:
doftpget xyzcorp.com anonymous oofa.txt
[ Top ] [ FTP Top ] [ FTP Script Tutorial ] [ C-Kermit Home ] [ Kermit Home ]
The new releases of C-Kermit (8.0.206) and Kermit 95 (2.1) support new FTP protocol features from RFC 2389 as well as most of what's in the Elz and Hethmon Extensions to FTP Internet Draft (see References). Some of these features, such as SIZE (request a file's size), MDTM (request file's modification time), and REST (restart interrupted transfer) have been widely implemented in FTP clients and servers for years (as well as in the initial release of the Kermit FTP clients). Others such as FEAT and MLSD are rarely seen and are new to the upcoming Kermit releases. TVFS (Trivial Virtual File Store) is supported implicitly, and the UTF-8 character-set is already fully supported at the protocol and data-interchange level.
For Kermit users, the main benefit of the new FTP protocol extensions is the ability to do recursive downloads. But the extensions also introduce complications and tradeoffs that you should be aware of. Of course Kermit tries to "do the right thing" automatically in every case for backwards compatibility. But (as noted later) some cases are inherently ambiguous and/or can result in nasty surprises, and for those situations new commands and switches are available to give you precise control over Kermit's behavior, in case the defaults don't produce the desired results.
Guessing is nice when it works, but sometimes it doesn't, and some FTP servers become confused when you send them a directive they don't understand, or they do something you didn't want, sometimes to the point of closing the connection. For this reason, Kermit lets you override default or negotiated features with the following new commands:
500 'FEAT': command not understood
which is normally harmless (but you never know). (In C-Kermit 8.0.208, this error message is suppressed unless you SET FTP DEBUG ON.)
To enable or disable more than one feature, use multiple FTP ENABLE or FTP DISABLE commands. The SHOW FTP command shows which features are currently enabled and disabled.
Whenever a SIZE or MDTM directive is sent implicitly and rejected by the server because it is unknown, Kermit automatically disables it.
With the new FTP protocol extensions, now there are two ways to get the list of files: the NLST directive, which has been part of FTP protocol since the beginning, and the new MLSD directive, which is new and not yet widely implemented. When NLST is used and you give a command like "mget *.txt", the FTP client sends:
NLST *.txt
and the server sends back a list of the files whose names match, e.g.
foo.txt bar.txt baz.txt
Then when downloading each file, the client sends SIZE (if it wants have a percent-done display) and MDTM (if it wants to set the downloaded file's timestamp to match that of the original), as well as RETR (to retrieve the file).
But when MLSD is used, the client is not supposed to send the filename or wildcard to the server; instead it sends an MLSD directive with no argument (or the name of a directory), and the server sends back a list of all the files in the current or given directory; then the client goes through the list and checks each file to see if it matches the given pattern, the rationale being that the user knows only the local conventions for wildcards and not necessarily the server's conventions. So with NLST the server interprets wildcards; with MLSD the client does.
The interpretation of NLST wildcards by the server is not necessarily required or even envisioned by the FTP protocol definition (RFC 959), but in practice most clients and servers work this way.
The principal advantage of MLSD is that instead of sending back a simple list of filenames, it sends back a kind of database in which each entry contains a filename together with information about the file: type, size, timestamp, and so on; for example:
size=0;type=dir;perm=el;modify=20020409191530; bin size=3919312;type=file;perm=r;modify=20000310140400; bar.txt size=6686176;type=file;perm=r;modify=20001215181000; baz.txt size=3820092;type=file;perm=r;modify=20000310140300; foo.txt size=27439;type=file;perm=r;modify=20020923151312; foo.zip (etc etc...)
(If the format of the file list were the only difference between NLST and MLSD, the discussion would be finished: it would always be better to use MLSD when available, and the MGET user interface would need no changes. But there's a lot more to MLSD than the file-list format; read on…)
The client learns whether the server supports MLSD in FEAT exchange. But the fact that the server supports MLSD doesn't mean the client should always use it. It is better to use MLSD:
But it is better to use NLST:
But when using MLSD there are complications:
To further complicate matters, NLST can (in theory) work just like MLSD: if sent with a blank argument or a directory name, it is supposed to return a complete list of files in the current or given directory, which the client can match locally against some pattern. It is not known if any FTP server or client does this but nevertheless, it should be possible since this behavior can be inferred from RFC 959.
In view of these considerations, and given the need to preserve the traditional FTP client command structure and behavior so the software will be usable by most people:
By default, Kermit's MGET command uses MLSD if MLST is reported by the server in its FEAT list. When MLSD is used, the filespec is sent to the server if it is not wild (according to Kermit's own definition of "wild" since it can't possibly know the server's definition). If the filespec is wild it is held for local use to select files from the list returned by the server. If MLST is not reported by the server or is disabled, Kermit sends the MGET filespec with the NLST directive.
The default behavior can be overridden globally with FTP DISABLE MLST, which forces Kermit to use NLST to get file lists. And then for situations in which MLSD is enabled, the following MGET switches can be used to override the defaults for a specific MGET operation:
mget /nlst foo.*
mget /mlsd foo.*
Command: With NLST: With MLSD:
mget NLST MLSD
mget *.txt NLST *.txt MLSD
mget foo NLST foo MLSD foo
mget /match:*.txt NLST MLSD
mget /match:*.txt foo NLST foo MLSD foo
In other words, the pattern is always interpreted locally unless MGET uses NLST and no /MATCH switch was given.
mget /nlst t[1234].h
In all cases in which the /RECURSIVE switch is included, the server's tree is duplicated locally.
Although MLSD allows recursion and NLST does not, the MLSD specification places a heavy burden on the client; the obvious, straightforward, and elegant implementation (depth-first, the one that Kermit currently uses) requires as many open temporary files as the server's directory tree is deep, and therefore client resource exhaustion -- e.g. exceeding the maximum number of open files -- is a danger. Unfortunately MLSD was not designed with recursion in mind. (Breadth-first traversal could be problematic due to lack of sufficient navigation information.)
Of course all of Kermit's other MGET switches can be used too, e.g. for finer-grained file selection (by date, size, etc), for moving or renaming files as they arrive, to override Kermit's automatic per-file text/binary mode switching, to pass the incoming files through a filter, to convert text-file character sets, and so on.
User's Command FTP Sends Remarks mget /nlst NLST Gets a list of all the files in the server's current and downloads each file. The list includes names only, so Kermit also must send SIZE and MDTM directives if size and timestamp information is required (this is always true of NLST). Sending NLST without an argument is allowed by the RFC959 NLST definition and by the Kermit FTP client, but might not work with other clients, and also might not work with every server. mget /nlst foo NLST foo If "foo" is a directory, this gets a list of all the files from the server's "foo" directory and downloads each file; otherwise this downloads the file named "foo" (if any) from the server's current directory. mget /nlst *.txt NLST *.txt Gets a list of the files in the server's current directory whose names match the pattern *.txt, and then downloads each file from the list. Because we are using NLST, we send the filespec (*.txt) to the server and the server interprets any wildcards. mget /nlst foo/*.txt NLST foo/*.txt Gets a list of the files in the server's "foo" directory whose names match the pattern *.txt, and then downloads each file from the list (server interprets wildcards). mget /nlst /match:*.txt NLST Gets a list of all the files in the server's current directory and then downloads each one whose name matches the pattern *.txt (client interprets wildcards). mget /nlst /match:*.txt foo NLST foo Gets a list of all the files in the server's "foo" directory and then downloads each one whose name matches the pattern *.txt (client interprets wildcards). mget /mlsd MLSD Gets a list of all the files from the server's current directory and then downloads each one. The list might include size and timestamp information, in which case Kermit does not need to send SIZE and MDTM directives for each file (this is always true of MLSD). mget /mlsd foo MLSD foo Gets a list of all the files from the server's "foo" directory (where the string "foo" does not contain wildcards) and then downloads each one. If "foo" is a regular file and not a directory, this command is supposed to fail, but some servers have been observed that send the file. mget /mlsd *.txt MLSD Gets a list of all the files from the server's current directory and then downloads only the ones whose names match the pattern "*.txt". Because we are using MLSD and the MGET filespec is wild, we do not send the filespec to the server, but treat it as though it had been given in a /MATCH: switch and use it locally to match the names in the list. mget /mlsd foo/*.txt MLSD This one won't work because MLSD requires that the notions of server directory and filename-matching pattern be separated. However, the client, which can't be expected to know the server's file-system syntax, winds up sending a request that the server will (or should) reject. mget /mlsd /match:*.txt MLSD Gets a list of all the files from the server's current directory and then downloads only the ones whose names match the pattern "*.txt" (client interprets wildcards). mget /mlsd /match:*.txt foo MLSD foo If "foo" is a directory on the server, this gets a list of all the files from the server's "foo" directory and then downloads only the ones whose names match the pattern "*.txt" (client interprets wildcards). This leaves the server CD'd to the "foo" directory; there's no way the client can restore the server's original directory because MLSD doesn't give that information, and since the client can not be expected to know the server's file-system syntax, it would not be safe to guess. If "foo" is a regular file, MLSD fails. mget /mlsd foo bar MLSD This one is problematic. You're supposed to be able to give MGET a list a filespecs; in this case we name two directories. The client must change the server's directory to "foo" to get the list of files, and then the files themselves. But then it has no way to return to the server's previous directory in order to do the same for "bar", as explained in the previous example. mget /mlsd /match:* [abc] MLSD [abc] Including a /MATCH: switch forces [abc] to be sent to the server even though the client would normally think it was a wildcard and hold it for local interpretation. In this example, [abc] might be a VMS directory name. mget /mlsd /match:* t*.h MLSD t*.h Contrary to the MLSD specification, some MLSD-capable FTP servers do interpret wildcards. This form of the MGET command can be used to force a wildcard to be sent to the server for interpretation.
When MLSD is used implicitly (that is, without an /MLSD switch given to force the use of MLSD) and an MGET command such as "mget foo/*.txt" fails, Kermit automatically falls back to NLST and tries again.
[ Top ] [ FTP Top ] [ C-Kermit Home ] [ Kermit Home ]
The primary benefit of file scanning is in file transfer. For all practical purposes, now you can stop worrying about whether a file should be sent in binary or text mode, or about sending mixtures of text and binary files in a single operation, or configuring and fine-tuning your lists of binary-file and text-file name patterns: now it all just works.
File scanning is done by the file sender, which determines the type of each file before it sends it and informs the receiver (Kermit or FTP server) of the type. File scanning is NOT done by the receiver, because it is the sender's responsibility to determine each file's type, send the file in the right mode, and inform the receiver of the mode. If both transfer partners are capable of this (or any other) form of automatic text/binary mode switching, then files can be sent in both directions with no worries about corruption due to inappropriate transfer mode. (As noted in Section 3, FTP servers don't do this, so this discussion does not apply when using Kermit to download from an FTP server.)
The rest of this section is mainly for the curious. If you don't read it and simply accept all defaults, every file you send should go in the appropriate mode automatically. As always, however, for character-set translation to work for 7- and 8-bit character-set files, the appropriate SET FILE CHARACTER-SET command(s) must have been executed to identify their encoding (Kermit's default file character-set is neutral ASCII except on platforms like HP-UX or DG/UX, where the default file character-set is known). And of course, receiving is another matter -- obviously the other Kermit must also send each file in the appropriate mode.
Scanning is more reliable than filename patterns simply because filenames are not reliable indicators of the file's contents. Classic examples include ".doc" files, which are binary if Microsoft Word documents but text on most other platforms, and ".com" files, which are binary on DOS and Windows but text on VMS. Anyway, nobody knows the naming conventions (if any) of all the applications (and persons!) on your computer. Scanning, on the other hand, determines each file's type by inspecting its contents rather than just looking at its name.
Also, file patterns -- even when they work as intended -- categorize each file only as text or binary, whereas file scanning can make finer distinctions:
File scanning is available in UNIX C-Kermit, in K-95, and to a limited extent, in VMS C-Kermit (full scanning is problematic in VMS because even plain-text files might contain binary record-format information). The relevant commands are:
(B) Binary (T)(7BIT) Text: 7-bit (T)(8BIT) Text: 8-bit (T)(UTF8) Text: Unicode UTF8 (T)(UCS2BE) Text: Unicode UCS2 Big Endian (T)(UCS2LE) Text: Unicode UCS2 Little Endian
So you can use DIR /XFER to get a preview of how each file in a selected group will be transferred. Everything to the right of the (B) or (T) is new. If FILE SCAN is OFF, you only get the (B) or (T) as before.
Note: Big and Little Endian refer to the ordering of bytes within a computer word. Big Endian architecture is standard and is used on most non-PC computers. Little Endian architecture is used on PCs.
To illustrate file-transfer with scanning, suppose you have a directory containing a mixture of text and binary files, and each text file can be 7-bit German ISO 646, 8-bit Latin-1, or Unicode in any of the following forms: UCS2 Little Endian, UCS2 Big Endian, or UTF8 (UTF-16 is not supported yet). Assuming all the built-in defaults are in effect, the following three commands do the job:
set file char german ; This sets the default for 7-bit text files set file char latin1 ; This sets the default for 8-bit text files send *
Each file is sent in the appropriate mode (text or binary), with text files converted to the appropriate transfer character-set and labeled so the receiver can convert them according to its own local conventions.
By the way, what if you want to inhibit character-set translation but still allow automatic text/binary mode switching? Previously, you could simply SET TRANSFER CHARACTER-SET TRANSPARENT. But now with file scanning, the file and transfer character-sets are set automatically per file. A new command was added for this purpose:
When TRANSFER TRANSLATION is OFF but FILE SCAN is ON, files are still scanned to see if they are text or binary, but no character-set translation is done when they text: only the normal record-format conversion.
Like all SET commands, SET TRANSFER TRANSLATION is global and persistent. You can also force a particular file-transfer command (SEND, MSEND, GET, RECEIVE, TRANSMIT, etc) to not translate without affecting the global translation settings by including the new /TRANSPARENT switch, e.g.
send /transparent oofa.txt
As of C-Kermit 8.0.206, SET TRANSFER CHARACTER-SET TRANSPARENT implies SET TRANSFER TRANSLATION OFF.
File scanning is also used in the TYPE command. The source file type and character set are determined as above, and then the file is automatically converted to your display character-set, line by line. In Kermit 95, the display character-set is Unicode, perhaps converted to your current console code page; in other versions of C-Kermit, it is your current file character-set. Thus if you have the following set appropriately:
SET FILE CHARACTER-SET (necessary in Unix but not K95) SET FILE DEFAULT 7BIT CHARACTER-SET SET FILE DEFAULT 8BIT CHARACTER-SET
then you should be able to TYPE any text file and see something reasonable. For example, in Unix, if your DEFAULT 7BIT-CHARACTER-SET is ITALIAN and your DEFAULT 8BIT-CHARACTER-SET is LATIN1, and your FILE CHARACTER-SET is LATIN1, you can TYPE an Italian ISO 646 file, a Latin-1 file, or any kind of Unicode file, and have it translated automatically to Latin-1 for your display.
In the GUI version of Kermit 95, you can see mixtures of many different scripts if the file is UTF8 or UCS2: Roman, Cyrillic, Hebrew, Greek, Armenian, Georgian, etc, all on the same screen at once.
File scanning also adds a new criterion for file selection, i.e. to select only text (or binary) files. Several commands now include a new switch, /TYPE:{BINARY,TEXT,ALL}. BINARY means select only binary regular files (not directories). TEXT means select only text files. ALL means don't scan; select all files. Examples:
NOTE: File scanning is NOT done when using external protocols (because the external protocol programs, such as sz, are processing each file, not Kermit).
When FILE SCAN is OFF and FILE PATTERNS are ON, behavior is as before with PATTERNS ON, but with some improvements:
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
Most command shells address these problems by allowing such names to be enclosed in doublequotes, e.g.:
cd "c:\Program Files"
C-Kermit previously used braces for this:
cd {c:\Program Files}
which was not what most people expected. And even when braces were used, Kermit had difficulties with completion, file menus, and so forth, within braced fields.
C-Kermit 8.0 allows either doublequotes or braces to be used for grouping:
send "this file"
send {this file}
rename "this file" "that file"
rename {this file} "that file"
rename "this file" {that file}
cd {Program Files}
cd "Program Files"
Note that the doublequotes or brackets must enclose the whole file or directory specification:
"c:\My Directory"
not:
c:\"My Directory"
In C-Kermit 8.0, you can also use completion on these filenames, in which case Kermit supplies the quotes (or braces) automatically. Example (in which the current directory contains only one file whose name starts with "th" and its full name is "this file" (without the quotes, but with the space)):
cat th<Tab>
Kermit repaints the filename field like this:
cat "this file"
That is, it backspaces over the original "th" and then writes the filename in doublequotes.
If completion is only partial, Kermit still supplies the quotes, but in this case also beeps. To continue the filename, you must first backspace over the closing quote. The closing quote is supplied in this case to make sure that you can see the spaces, especially if they are trailing. For example, if the current directory contains two files whose names start with "th", and their fill names are "this file" and "this other file":
cat th<Tab>
Kermit prints:
cat "this "<Beep>
If it didn't print the closing quote, you would probably wonder why it was beeping.
Also, if you begin a filename field with a doublequote or opening brace, now you can use completion or get ?-help; this was never possible before.
C-Kermit>type "thi? Input file specification, one of the following: this file this other file C-Kermit>type "thi_
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
define xx show args xx one "this is two" three
Result:
Macro arguments at level 0 (\v(argc) = 4): \%0 = xx \%1 = one \%2 = this is two \%3 = three
Also, you can now quote braces and quotes in macro args (this didn't work before). Examples:
xx "{" ; The argument is a single left brace
xx {"} ; The argument is a doublequote character
In case this new behavior interferes with your scripts, you can restore the previous behavior with:
SET COMMAND DOUBLEQUOTING OFF
Completion has also been improved for file and directory names that contain not only spaces (as described above) but also "metacharacters" such as asterisk (*) and tilde (~): now the field is repainted if necessary. For example, if the current directory contains only one file whose name contains "blah", then in:
type *blah<Tab>
"*blah" is replaced by the filename. In earlier releases, the part typed so far was left on the command line (and in the history buffer), so even when the original command worked, the recalled version would not. Similarly for ~ (the nearly-universal Unix notation for username):
type ~olga/x<Tab>
is repainted as (e.g.):
type /users/home/olga/x(Beep)
Speaking of command history, the new SHOW HISTORY command shows your command history and recall buffer. SAVE COMMAND HISTORY saves it into a file of your choice.
take commandfile arg1 arg2 ...
This was accomplished by replacing the current \%1, \%2, etc, with the given arguments, since a new set of macro argument variables is created only when a macro is executed, not a command file. It is much more intuitive, however, if arguments to command files worked like those to macros: the command file sees the arguments as its own \%1, \%2, etc, but the caller's variables are not disturbed. C-Kermit 8.0 accomplishes this by automatically creating an intermediate temporary macro to start the command file (if any arguments were given), thus creating a new level of arguments as expected.
define foo {
(some command)
if fail echo Sorry, blah failed...
}
This would result in Kermit trying to execute a "blah" command. This could always be handled by enclosing the text in braces:
define foo {
(some command)
if fail echo {Sorry, blah failed...}
}
but doublequotes (more intuitive) should have worked too. Now they do:
define foo {
(some command)
if fail echo "Sorry, blah failed..."
}
This change was made to facilitate command recall in Linux-based PDAs that don't have a Control key, or at least not one that's easily (or always) accessible, such as the Sharp Zaurus SL5500.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
Version 8.0.211 adds the /DEFAULT:text switch for ASK-Class commands (ASK, ASKQ, and GETOK). This lets you supply a default answer in case the user supplies an empty answer or the /TIMEOUT: switch was included and the time limit expired without an answer. In both these cases, the command succeeds.
Synonyms: FIND, SEARCH.
If you need to process a bigger list of files than your computer has memory for, you might be able use an external file list. The Kermit SEND and the FTP PUT and GET commands accept a /LISTFILE: switch, which gives the name of a file that contains the list of files to be transferred. Example for UNIX:
!find . -print | grep / > /tmp/names ftp put /update /recursive /listfile:/tmp/names
Unprefixed Remote Local Description CD (CWD) RCD LCD Change (Working) Directory CDUP RCDUP LCDUP CD Up PWD RPWD LPWD Print Working Directory DIRECTORY RDIR LDIR Request a directory listing DELETE RDEL LDEL Delete (a) file(s) RENAME RREN LREN Rename a file MKDIR RMKDIR LMKDIR Create a directory RMDIR RRMDIR LRMDIR Remove a directory
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
The new PROMPT command can be used to set breakpoints for debugging scripts. If executed in a command file or macro, it gives you an interactive command prompt in the current context of the script, with all its variables, arguments, command stack, etc, available for examination or change, and the ability to resume the script at any point (END resumes it, Ctrl-C or STOP cancels it and returns to top level).
The new Ctrl-C trapping feature (Section 8.14) lets you intercept interruption of scripts. This can be used in combination with the PROMPT command to debug scripts. Example:
define ON_CTRLC {
echo INTERRUPTED BY CTRL-C...
echo The command stack has not yet been rolled back:
show stack
echo Type Ctrl-C again or use the END command to return to top level.
prompt Debug>
}
Adding this ON_CTRL definition to your script lets you interrupt it at any point and get prompt that is issued at the current command level, so you can query local variables, etc.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
When a macro is to be executed, its name is given as if it were a C-Kermit command, optionally preceded by the word "do". When a macro is used as a variable, it must be "escaped" with \m(xxx) (or equivalent function, e.g. \s(xxx), \:(xxx), \fdefinition(xxx)), where xxx is the macro name, for example:
define filename /usr/olga/oofa.txt send \m(filename)
Of course variables can also hold numbers:
define size 17 declare \&a[\m(size)] ... define index 3 if ( == \m(index) 3 ) echo The third value is: \&a[\m(index)] evaluate index (\m(index) * 4) if ( > \m(index) \m(size) ) echo Out of range!
But these are contexts in which only numbers are valid. C-Kermit 8.0 has been changed to treat non-escaped non-numeric items in strictly numeric contexts as macro names. So it is now possible (but not required) to omit the \m(...) notation and just use the macro name in these contexts:
define size 17 declare \&a[size] ... define index 3 if ( == index 3 ) echo The third value is: \&a[index] evaluate index (index * 4) if ( > index size ) echo Out of range!
This is especially nice for loops that deal with arrays. Here, for example, is a loop that reverses the order of the elements in an array. Whereas formerly it was necessary to write:
.\%n ::= \fdim(&a)
for \%i 1 \%n/2 1 {
.tmp := \&a[\%n-\%i+1]
.\&a[\%n-\%i+1] := \&a[\%i]
.\&a[\%i] := \m(tmp)
}
Recoding this to use macro names "i" and "n" instead of the backslash variables \%i and \%n, we have:
.n ::= \fdim(&a)
for i 1 n/2 1 {
.tmp := \&a[n-i+1]
.\&a[n-i+1] := \&a[i]
.\&a[i] := \m(tmp)
}
which reduces the backslash count to less than half. The final statement in the loop could be written ".\&a[i] ::= tmp" if the array contained only numbers (since ::= indicates arithmetic expression evaluation).
Also, now you can use floating-point numbers in integer contexts (such as array subscripts), in which case they are truncated to an integer value (i.e. the fractional part is discarded).
Examples of numeric contexts include:
and:
Macro names used in numeric contexts must not include mathematical operators. Although it is legal to create a macro called "foo+bar", in a numeric context this would be taken as the sum of the values of "foo" and "bar". Any such conflict can be avoided, of course, by enclosing the macro name in \m(...).
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
By contrast, IF FLOAT n succeeds if n is a floating-point number OR an integer (or a variable with floating-point or integer value). Therefore, IF FLOAT should be used whenever any kind of number is acceptable, whereas IF INTEGER (or IF NUMERIC) when only an integer can be used.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
DEF ON_UNKNOWN_COMMAND telnet \%1 ; Treat unknown commands as hostnames DEF ON_UNKNOWN_COMMAND dial \%1 ; Treat unknown commands phone numbers DEF ON_UNKNOWN_COMMAND take \%1 ; Treat unknown commands as filenames DEF ON_UNKNOWN_COMMAND !\%* ; Treat unknown commands as shell commands
The ON_CD macro, if defined, is executed whenever Kermit is given a CD (change directory) command (8.0.211). Upon entry to this macro, the directory has already changed and the new directory string is available in the \v(directory) variable, and also as the first argument (\%1).
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
(setq a 1 b 2 c 3) show mac a b c a = 1 b = 2 c = 3
An exact match is required for each name (except that case doesn't matter). If you include wildcard characters, however, a pattern match is performed:
show mac [a-c]*x
shows all macros whose names start with a, b, or c, and end with x.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
In function arguments, however, you MUST use the abbreviated form: \fsplit(\%a,&a) or \fsplit(\%a,&a[]). If you include the backslash (as in "\fsplit(\%a,\&a[])") a parse error occurs.
Here are the new array-related commands:
Array links let you pass array names as arguments to macros. For example, suppose you had a program that needed to uppercase all the elements of different arrays at different times. You could write a macro to do this, with the array name as an argument. But without array links, there would be no way to refer to the argument array within the macro. Array links make it easy:
define arrayupper {
local \&e[] \%i
array link \&e[] \%1
for i 1 \fdim(&e) 1 { .\&e[i] := \fupper(\&e[i]) }
}
declare \&a[] = these are some words
arrayupper &a
show array &a
The macro declares the array link LOCAL, which means it doesn't conflict with any array of the same name that might exist outside the macro, and that the link is destroyed automatically when the macro exits. This works, by the way, even if the link name and the macro argument name are the same, as long as the link is declared LOCAL.
As noted, you can't make a link to a nonexistent array. So when writing a macro whose job is to create an array whose name is passed as an argument, you must declare the array first (the size doesn't matter as long as it's greater than 0). Example:
define tryme { ; Demonstration macro
local \&e[] ; We only need this inside the macro
array link \&e[] \%1 ; Make local link
shift ; Shift argument list
void \fsplit(\%*,&e) ; Split remainder of arg list into array
}
declare \&a[1] ; Declare target array in advance
tryme &a here are some words ; Invoke the macro with array name and words
show array a ; See the results
One final improvement allows the macro itself to declare the array (this was not possible in earlier Kermit releases): if the array name in the DECLARE command is a variable (and not an array name), or includes variables, the resulting value is used as the array name. So:
define tryme { ; Demonstration macro
declare \%1[1] ; Preliminary declaration for target array
local \&e[] ; We only need this inside the macro
array link \&e[] \%1 ; Make local link
shift ; Shift argument list
void \fsplit(\%*,&e) ; Split remainder of arg list into array
}
tryme &a here are some words ; Invoke the macro with array name and words
show array a ; See the results
The SHOW ARRAY command now indicates whether an array name is a link.
Also see the descriptions of \fjoin() and \fsplit(), plus Section 8.10 on the MINPUT command, which shows how an entire array (or segment of it) can be used as the MINPUT target list.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
\v(buildid) A date string like "20000808" indicating when C-Kermit was built. \v(ftime) Current time, secs since midnight, including fraction of second. \v(iprompt) The current SET PROMPT value \v(sexp) The most recent S-Expression (see Section 9) \v(sdepth) The current S-Expression invocation depth (Section 9) \v(svalue) The value of the most recent S-Expression (Section 9)\v(ftp_code) Most recent FTP response code (Section 3) \v(ftp_connected) FTP connection status (Section 3) \v(ftp_cpl) FTP Command Protection Level (Section 3.2) \v(ftp_dpl) FTP Data Protection Level (Section 3.2) \v(ftp_getputremote) The current SET GET-PUT-REMOTE setting (Section 3.8) \v(ftp_host) Name or IP address of FTP server (Section 3) \v(ftp_loggedin) FTP login status (Section 3) \v(ftp_message) Most recent FTP response message (Section 3) \v(ftp_security) FTP Security method (Section 3.2) \v(ftp_server) OS type of FTP server (Section 3)
\v(http_code) Most recent HTTP response code \v(http_connected) HTTP connection status \v(http_host) Name or IP address of HTTP server \v(http_message) Most recent HTTP response message \v(http_security) TLS cipher used to secure the HTTP session
\v(hour) Hour of the day, 0 to 23. \v(timestamp) Equivalent to "\v(ndate) \v(time)". \v(log_debug) Current debug log file, if any. \v(log_packet) Current packet log file, if any. \v(log_session) Current session log file, if any. \v(log_transaction) Current transaction log file, if any. \v(log_connection) Current connection log file, if any.
The following new or improved built-in functions are available:
\fcmdstack() Allows programmatic access to the command stack. \fcvtdate() Section 8.13, format options added \fdelta2secs() Section 8.13 \fdostounixpath(s1) Converts a DOS filename to Unix format. \fsplit() Now allows grouping/nesting in source string. \fword() Allows the same grouping and nesting. \fjoin(&a,s1,n1,n2) Copies an array into a single string. \fsubstitute(s1,s2,s3) Substitutes characters within a string. \freplace() Has new 4th "occurrence" argument. \fsexpression() Evaluates an S-Expression (explained in Section 9). \ftrim(), \fltrim() Now trim CR and LF by default, as well as SP and Tab. \funixtodospath(s1) Converts a Unix filename to DOS format. \fkeywordval(s1,c1) Assigns values to keywords (macros) (explained below).
Most functions that have "2" in their names to stand for the word "to" can now also be written with "to", e.g. "\fdelta2secs()," \fdeltatosecs()."
define MYDIAL {
local \%i modem hangup method device speed number
def number 5551234 ; Assign default parameter values
def speed 57600
def modem usrobotics
def hangup rs232
def method tone
def country 1
for \%i 1 \v(argc)-1 1 { ; Parse any keyword parameters...
if not \fkeywordval(\&_[\%i]) end 1 Bad parameter: "\&_[\%i]"
}
set dial country \m(country)
set modem type \m(modem)
set modem hang \m(hangup)
set dial method \m(tone)
set line \m(device)
if fail stop 1
set speed \m(speed)
if fail stop 1
show comm
set dial display on
dial \m(number)
if success connect
}
In this example, all the defaults are set up inside the macro, and therefore it can be invoked with no parameters at all. But if you want to have the macro dial a different number, you can supply it as follows:
mydial number=7654321
You can supply any number of keyword parameters, and you can give them in any order:
mydial number=7654321 hangup=modem speed=115200
declare \&a[] = 0 1 2 3 4 5 6 7 8 9
you can get effects like this:
\fjoin(&a) 0 1 2 3 4 5 6 7 8 9
\fjoin(&a,:) 0:1:2:3:4:5:6:7:8:9
\fjoin(&a,{,}) 0,1,2,3,4,5,6,7,8,9
\fjoin(&a,...) 0...1...2...3...4...5...6...7...8...9
\fjoin(&a,,,1) 0123456789
\fsplit(), \fword(), \fstripb(), and \fjoin() accept a "grouping mask" argument, n1, which is a number from 0 to 63, in which:
1 = "" doublequotes
2 = {} braces
4 = '' singlequotes
8 = () parentheses
16 = [] square brackets
32 = <> angle brackets
These can be OR'd (added) together to make any number 0-63 (-1 is treated the same as 63, 0 means no grouping). If a bit is on, the corresponding kind of grouping is selected. (If more than 1 bit is set for \fjoin(), only the lowest-order one is used.)
If you include the same character in the grouping mask and the include list, the grouping mask takes precedence. Example:
def \%a a "b c d" e \fsplit(\%a,&a[],,,-1) = 3 <-- doublequote used for grouping \fsplit(\%a,&a[],,",-1) = 3 <-- doublequote still used for grouping
Nesting of matched left and right grouping characters (parentheses, braces, and brackets, but not quotes) is recognized. Example:
def \%a a (b c <d e [f g {h i} j k] l m> n o) p
\fsplit(\%a,&a,,,0) = 16 (no grouping)
\fsplit(\%a,&a,,,2) = 15 (braces only)
\fsplit(\%a,&a,,,16) = 11 (square brackets only)
\fsplit(\%a,&a,,,32) = 7 (angle brackets only)
\fsplit(\%a,&a,,,63) = 3 (all)
\fsplit(\%a,&a,,,-1) = 3 (all)
\fsplit() and \fjoin() are "reciprocal" functions. You can split a string up into an array and join it back into a new string that is equivalent, as long as \fsplit() and \fjoin() are given equivalent grouping masks, except that the type of braces might change. Example:
def \%a a {b c [d e] f g} "h i" j <k l> m
echo STRING=[\%a]
echo WORDS=\fsplit(\%a,&a,,,-1)
show array a
asg \%b \fjoin(&a,{ },2)
echo JOIN =[\%b]
echo WORDS=\fsplit(\%b,&b,,,-1)
show array b
The arrays a and b are identical. The strings a and b are as follows:
\%a: a {b c [d e] f g} "h i" j <k l> m
\%b: a {b c [d e] f g} {h i} j {k l} m
It is possible to quote separator grouping characters with backslash to override their grouping function. And of course to include backslash itself in the string, it must be quoted too. Furthermore, each backslash must be doubled, so the command parser will still pass one backslash to \fsplit() for each two that it sees. Here are some examples using \fsplit() with a grouping mask of 8 (treat parentheses as grouping characters).
String Result
a b c d e f 6
a b\\ c d e f 5
a b (c d e) f 4
a b \\(c d e\\) f 6
a b \\\\(c d e\\\\) f 7
\fsplit() has also been changed to create its array (if one is given) each time it is called, so now it can be conveniently called in a loop without having to redeclare the array each time.
Incidentally... Sometimes you might want to invoke \fsplit() in a situation where you don't care about its return value, e.g. when you just want to fill the array. Now you can "call" \fsplit() or any other function with the new VOID command:
void \fsplit(\%a,&a)
\fsplit() and \fjoin() also accept a new, optional 6th argument, an options flag, a number that can specify a number of options. So far there is just one option, whose value is 1:
\fword(Three little words,2)
returns "little" (the second word). Space is a separator, but there are multiple spaces between each word. If the value 1 is included in the option flag, however, each separator counts. If two separators are adjacent, an empty word is produced between them. This is useful for parsing (e.g.) comma-separated lists exported from databases or spreadsheets.
0 (default) = name of object at level n1. 1 (nonzero) = object type (0 = prompt; 1 = command file; 2 = macro).The default for n2 is 0.
The name associated with prompt is "(prompt)". Here's a loop that can be included in a macro or command file to show the stack (similar to what the SHOW STACK command does):
for \%i \v(cmdlevel) 0 -1 {
echo \%i. [\fcmdstack(\%i,1)] \fcmdstack(\%i,0)
}
In this connection, note that \v(cmdfile) always indicates the most recently invoked active command file (if any), even if that file is executing a macro. Similarly, \v(macro) indicates the most recently invoked macro (if any), even if the current command source is not a macro. The name of the "caller" of the currently executing object (command file or macro) is:
\fcmdstack(\v(cmdlevel)-1)
and its type is:
\fcmdstack(\v(cmdlevel)-1,1)
To find the name of the macro that invoked the currently executing object, even if one or more intermediate command files (or prompting levels) are involved, use a loop like this:
for \%i \v(cmdlevel)-1 0 -1 {
if = \fcmdstack(\%i,1) 2 echo CALLER = \fcmdstack(\%i,0)
}
Of course if you make a macro to do this, the macro must account for its own additional level:
define CALLER {
for \%i \v(cmdlevel)-2 0 -1 {
if = \fcmdstack(\%i,1) 2 return \fcmdstack(\%i,0)
}
return "(none)"
}
The built-in variable \v(cmdsource) gives the current command source as a word ("prompt", "file", or "macro").
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
The same considerations apply to command files invoked by the TAKE command.
If a macro does not execute any commands that set success or failure, then invoking the macro does not change the current SUCCESS/FAILURE status. It follows, then, that the mere invocation of a macro does not change the SUCCESS/FAILURE status either. This makes it possible to write macros to react to the status of other commands (or macros), for example:
define CHKLINE {
if success end 0
stop 1 SET LINE failed - please try another device.
}
set modem type usrobotics
set line /dev/cua0
chkline
set speed 57600
dial 7654321
By the way, none of this is news. But it was not explicitly documented before, and C-Kermit 7.0 and earlier did not always handle the RETURN statement as it should have.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
If you omit the switches and include only one name, the UNDEFINE command works as before.
Switches include:
The UNDEFINE command fails if there were any errors and succeeds otherwise.
The new _UNDEFINE command is like UNDEFINE, except the names are assumed to be variable names themselves, which contain the names (or parts of them) of the variables to be undefined. For example, if you have the following definitions:
define \%a foo define foo This is some text
then:
undef \%a
undefines the variable \%a, but:
_undef \%a
undefines the macro foo.
Normal Kermit patterns are used for matching; metacharacters include asterisk, question mark, braces, and square brackets. Thus, when using the /MATCHING switch, if the names of the macros you want to undefine contain any of these characters, you must quote them with backslash to force them to be taken literally. Also note that \%* is not the name of a variable; it is a special notation used within a macro for "all my arguments". The command "undef /match \%*" deletes all \%x variables, where x is 0..9 and a..z. Use "undef /match \%[0-9]" to delete macro argument variables or "undef /match \%[i-n]" to delete a range of \%x variables.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
Also in version 8.0.211, there is a new way to apply a scale factor to [M]INPUT timeouts:
The MINPUT command can be used to search the incoming data stream for several targets simultaneously. For example:
MINPUT 8 one two three
waits up to 8 seconds for one of the words "one", "two", or "three" to arrive. Words can be grouped to indicate targets that contain spaces:
MINPUT 8 nineteen twenty "twenty one"
And of course you can also use variables in place of (or as part of) the target names:
MINPUT 8 \%a \&x[3] \m(foo)
Until now you had to know the number of targets in advance when writing the MINPUT statement. Each of the examples above has exactly three targets.
But suppose your script needs to look for a variable number of targets. For this you can use arrays and \fjoin(), described in Section 8.7. Any number of \fjoin() invocations can be included in the MINPUT target list, and each one is expanded into the appropriate number of separate targets each time the MINPUT command is executed. Example:
declare \&a[10] = one two three minput 10 foo \fjoin(&a) bar
This declares an array of ten elements, and assigns values to the first three of them. The MINPUT command looks for these three (as well as the words "foo" and "bar"). Later, if you assign additional elements to the array, the same MINPUT command also looks for the new elements.
If an array element contains spaces, each word becomes a separate target. To create one target per array element, use \fjoin()'s grouping feature:
dcl \&a[] = {aaa bbb} {ccc ddd} {xxx yyy zzz}
minput 10 \fjoin(&a) <-- 7 targets
minput 10 \fjoin(&a,,2) <-- 3 targets
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
The /OFF and /ON switches let you turn recording off and on during a session without closing the file.
When recording:
Thus the script recorder is inherently line-oriented. It can't be used to script character-oriented interactions like typing Space to a "More?" prompt or editing a text file with VI or EMACS.
But it has advantages too; for example it takes control characters into account that might not be visible to you otherwise, and it automatically converts control characters in both the input and output streams to the appropriate notation. It can tell, for example that the "$ " prompt on the left margin in UNIX is really {\{13}\{10}$ }, whereas in VMS it might be {\{13}\{10}\{13}$ }. These sequences are detected and recorded automatically.
A learned script should execute correctly when you give a TAKE command for it. However, it is usually appropriate to edit the script a bit. The most important change would be to remove any passwords from it. For example, if the script contains:
INPUT 9 {\{13}\{10}Password: }
IF FAIL STOP 1 INPUT timeout
PAUSE 1
OUTPUT bigsecret\{13}
you should replace this by something like:
INPUT 9 {\{13}\{10}Password: }
IF FAIL STOP 1 INPUT timeout
ASKQ pswd Please type your password:
PAUSE 1
OUTPUT \m(pswd)\{13}
The LEARN command can't do this for you since it knows nothing about "content"; it only knows about lines and can't be expected to parse or understand them -- after all, the Password prompt might be in some other language. So remember: if you use the LEARN command to record a login script, be sure edit the resulting file to remove any passwords. Also be sure to delete any backup copies your editor or OS might have made of the file.
Other manual adjustments might also be appropriate:
INPUT 10 ts-23> ; or whatever
to:
INPUT 10 \fpattern(ts-[0-9][0-9]>)
Here is a sample script generated by Kermit ("learn vms.ksc") in which a Telnet connection is made to a VMS computer, the user logs in, starts Kermit on VMS, sends it a file, and then logs out:
; Scriptfile: vms.ksc
; Directory: /usr/olga
; Recorded: 20001124 15:21:23
SET HOST /NETWORK:TCP vms.xyzcorp.com
IF FAIL STOP 1 Connection failed
INPUT 7 {\{13}\{10}\{13}Username: }
IF FAIL STOP 1 INPUT timeout
PAUSE 1
OUTPUT olga\{13}
INPUT 3 {\{13}\{10}\{13}Password: }
IF FAIL STOP 1 INPUT timeout
PAUSE 1
OUTPUT secret\{13}
INPUT 18 {\{13}\{10}\{13}$ }
IF FAIL STOP 1 INPUT timeout
PAUSE 1
OUTPUT set default [.incoming]\{13}
INPUT 12 {\{13}\{10}\{13}$ }
IF FAIL STOP 1 INPUT timeout
PAUSE 1
OUTPUT kermit\{13}
INPUT 15 {\{13}\{10}\{13}ALTO:[OLGA.INCOMING] C-Kermit>}
IF FAIL STOP 1 INPUT timeout
PAUSE 1
OUTPUT receive\{13}
send myfile.txt
INPUT 18 {\{13}\{10}\{13}ALTO:[OLGA.INCOMING] C-Kermit>}
IF FAIL STOP 1 INPUT timeout
PAUSE 1
OUTPUT exit\{13}
INPUT 6 {\{13}\{10}\{13}$ }
IF FAIL STOP 1 INPUT timeout
PAUSE 1
OUTPUT logout\{13}
close
exit
The commands generated by Kermit during CONNECT (INPUT, IF FAIL, PAUSE, and OUTPUT) have uppercase keywords; the commands typed by the user are in whatever form the user typed them (in this case, lowercase).
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
Patterns are also used in increasingly many other ways, to the extent it is useful to point out certain important distinctions in the ways in which they are used:
This terminology lets us describe Kermit's commands with a bit more precision. When a pattern is used for matching filenames, it is a wildcard, except in the TEXT-PATTERNS and BINARY-PATTERNS lists and /EXCEPT: clauses, in which case directory separators are not significant (for example, a BINARY-PATTERN of "*.exe" matches any file whose name ends in .exe, no matter how deeply it might be buried in subdirectories). When Kermit parses a file specification directly, however, it uses the strict wildcard definition. For example, "send a*b" sends all files whose names start with "a" and end with "b" in the current directory, and not any files whose names end with "b" that happen to be in subdirectories whose names start with "a". And as noted, wildcards are anchored, so "delete foo" deletes the file named "foo", and not all files whose names happen to contain "foo".
Most other patterns are anchored. For example:
if match abc bc ...
does not succeed (and you would be surprised if it did!). In fact, the only floating patterns are the ones used by commands or functions that search for patterns in files, arrays, or strings. These include:
Thus these are the only contexts in which explicit anchors ("^" and "$") may be used:
Similarly for TYPE /PAGE, /fsearch(), /frsearch(), and \farraylook().
Here is a brief summary of anchored and floating pattern equivalences:
Anchored Floating
abc ^abc$
*abc abc$
abc* ^abc
*abc* abc
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
Furthermore, C-Kermit 8.0 includes a new and easy-to-use form of date-time arithmetic, in which any date or time can be combined with a "delta time", to add or subtract the desired time interval (years, months, weeks, days, hours, minutes, seconds) to/from the given date. And new functions are available to compare dates and to compute their differences.
As you can imagine, all this requires quite a bit of "syntax". The basic format is:
[ date ] [ time ] [ delta ]
Each field is optional, but in most cases (depending on the context) there must be at least one field. If a date is given, it must come first. If no date is given, the current date is assumed. If no time is given, an appropriate time is supplied depending on whether a date was supplied. If no delta is given, no arithmetic is done. If a delta is given without a date or time, the current date and time are used as the base.
Date-time-delta fields are likely to contain spaces (although they need not; space-free forms are always available). Therefore, in most contexts -- and notably as switch arguments -- date-time information must be enclosed in braces or doublequotes, for example:
send /after:"8-Aug-2001 12:00 UTC" *.txt
Kermit's standard internal format for dates and times is:
yyyymmdd hh:mm:ss
for example:
20010208 10:28:01
Date-times can always be given in this format. yyyy is the 4-digit year, mm is the two-digit month (1-12; supply leading zero for Jan-Sep), dd is the 2-digit day (leading zero for 1-9), hh is the hour (0-23), mm the minute (0-59), ss the second (0-59), each with leading zero if less than the field width. The date and time can be separated by a space, an underscore, a colon, or the letter T. The time is in 24-hour format. Thus the various quantities are at the following fixed positions:
Position Contents 1-4 Year (4 digits, 0000-9999) 5-6 Month (2 digits, 1-12) 7-8 Day (2 digits, 1-31) 9 Date-Time Separator (space, :, _, or the letter T) 10-11 Hour (2 digits, 0-23) 12 Hour-Minute Separator (colon) 13-14 Minute (2 digits, 0-59) 15 Minute-Second Separator (colon) 16-17 Second (2 digits, 0-59)
Example:
19800526 13:07:12 26 May 1980, 13:07:12 (1:07:12PM)
This is the format produced by the DATE command and by any function that returns a date-time. It is suitable for lexical comparison and sorting, and for use as a date-time in any Kermit command. When this format is given as input to a command or function, various date-time separators (as noted) are accepted:
19800526 13:07:12 26 May 1980, 13:07:12 (1:07:12PM) 20010208_10:28:35 2 February 2001, 10:28:35 AM 18580101:12:00:00 1 January 1858, noon 20110208T00:00:00 2 February 2011, midnight
Certain other special date-time formats that are encountered on computer networks are recognized:
Sat, 14 Jul 2001 11:49:29 (No timezone) Fri, 24 Mar 2000 14:19:59 EST (Symbolic timezone) Tue, 26 Jun 2001 10:19:45 -0400 (EDT) (GMT Offset + comment)
yyyymmddhhmmss[.ffff]
where yyyy is the 4-digit year, mm is the 2-digit month, and so on, exactly 14 digits long. An optional fractional part (fraction of second) may also be included, separated by a decimal point (period). Kermit rounds to the nearest second. Example:
20020208102835.515 (8 February 2002 10:28:36 AM)
You can give an explicit date in almost any conceivable format, but there are some rules:
Various date-field separators are accepted: hyphen, slash, space, underscore, period. The same field separator (if any) must be used in both places; for example 18-Sep-2001 but not 18-Sep/2001. Months can be numeric (1-12) or English names or abbreviations. Month name abbreviations are normally three letters, e.g. Apr, May, Jun, Jul. Capitalization doesn't matter.
Here are a few examples:
18 Sep 2001 (English month, abbreviated) 18 September 2001 (English month, spelled out) 2001 Sept 18 (Year, month, day) 18-Sep-2001 (With hyphens) 18/09/2001 (All numeric with slashes) 18.09.2001 (Ditto, with periods) 18_09_2001 (Ditto, with underscores) 09/18/2001 (See below) 2001/09/18 (See below) September 18, 2001 (Correspondence style) Sep-18-2001 (Month-day-year) 20010918 (Numeric, no separators)
You can also include the day of the week with a specific date, in which case it is accepted (if it is a valid day name), but not verified to agree with the given date:
Tue, 18 Sep 2001 (Abbreviated, with comma) Tue,18 Sep 2001 (Comma but no space) Tue 18 Sep 2001 (Abbreviated, no comma) Tuesday 18 Sep 2001 (Spelled out) Tuesday, 18 Sep 2001 (etc) Friday, 18 Sep 2001 (Accepted even if not Friday)
In all-numeric dates with the year last, such as 18/09/2001, Kermit identifies the year because it's 4 digits, then decides which of the other two numbers is the month or day based on its value. If both are 12 or less and are unequal, the date is ambiguous and is rejected. In all-numeric dates with the year first, the second field is always the month and the third is the day. The month never comes last. A date with no separators is accepted only if it is all numeric and has exactly eight digits, and is assumed to be in yyyymmdd format.
20010918 (18-Sep-2001 00:00:00)or 14 digits (as in FTP MDTM format):
20010918123456 (18-Sep-2001 12:34:56)
You can always avoid ambiguity by putting the year first, or by using an English, rather than numeric, month. A date such as 09/08/2001 would be ambiguous but 2001/09/08 is not, nor is 09-Aug-2001.
Until the late 1990s, it was common to encounter 2-digit years, and these are found to this day in old e-mails and other documents. Kermit accepts these dates if they have English months, and interprets them according to the windowing rules of RFC 2822: "If a two digit year is encountered whose value is between 00 and 49, the year is interpreted by adding 2000, ending up with a value between 2000 and 2049. If a two digit year is encountered with a value between 50 and 99, or any three digit year is encountered, the year is interpreted by adding 1900."
If you need to specify a year prior to 1000, use leading zeros to ensure it is not misinterpreted as a "non-Y2K-compliant" modern year:
7-Oct-77 (19771007 00:00:00) 7-Oct-0077 (00771007 00:00:00)
11:59:59 (11:59:59 AM) 11:59 (11:59:00 AM) 11 (11:00:00 AM) 11:59:59.33 (11:59:59 AM) 11:59:59.66 (Noon) 03:21:00 (3:21:00 AM) 3:21:00 (3:21:00 AM) 15:21:00 (3:21:00 PM) :21:00 (00:21:00 AM) ::01 (00:00:01 AM) 11::59 (11:00:59 AM)
Leading zeros can be omitted, but it is customary and more readable to keep them in the minute and second fields:
03:02:01 (03:02:01 AM) 3:02:01 (03:02:01 AM) 3:2:1 (03:02:01 AM)
AM/PM notation is accepted if you wish to use it:
11:59:59 (11:59:59 AM) 11:59:59AM (11:59:59 AM) 11:59:59A.M. (11:59:59 AM) 11:59:59am (11:59:59 AM) 11:59:59a.m. (11:59:59 AM) 11:59:59PM (11:59:59 PM = 23:59:59) 11:59:59P.M. (11:59:59 PM = 23:59:59) 11:59:59pm (11:59:59 PM = 23:59:59) 11:59:59p.m. (11:59:59 PM = 23:59:59)
You can omit the colons if you wish, in which case Kermit uses the following rules to interpret the time:
Examples:
1 (01:00:00 AM) 10 (10:00:00 AM) 230 (02:30:00 AM) 230pm (02:30:00 PM = 14:30:00) 1115 (11:15:00 AM) 2315 (11:15:00 PM = 23:15:00 PM) 23150 (02:31:50 AM) 231500 (23:15:00 PM)
The preferred time zone designator is the UTC Offset, as specified in RFC 2822: a plus sign or minus sign immediately followed by exactly four decimal digits, signifying the difference in hh (hours) and mm (minutes) from Universal Coordinated Time (UTC, also known as Greenwich Mean Time, or GMT), with negative numbers to the West and positive numbers to the East. For example:
Fri, 13 Jul 2001 12:54:29 -0700
indicates a local time of 12:54:29 that is 07 hours and 00 minutes behind (less than, East of) Universal Time. The space is optional, so the example could also be written as:
Fri, 13 Jul 2001 12:54:29-0700
The following symbolic time zones are also accepted, as specified by RFC 2822 and/or in ISO 8601:
GMT = +0000 Greenwich Mean Time Z = +0000 Zulu (Zero Meridian) Time UTC = +0000 Universal Coordinated Time UT = +0000 Universal Time EDT = -0400 Eastern (USA) Daylight Time EST = -0500 Eastern (USA) Standard Time CDT = -0500 Central (USA) Daylight Time CST = -0600 Central (USA) Standard Time MDT = -0600 Mountain (USA) Daylight Time MST = -0700 Mountain (USA) Standard Time PDT = -0700 Pacific (USA) Daylight Time PST = -0800 Pacific (USA) Standard Time
Note that GMT, Z, UTC, and UT all express the same concept: standard (not daylight) time at the Zero Meridian. UTC, by the way, is an international standard symbol and does not correspond to the order of the English words, Universal Coordinated Time, but it happens to have the same initial letters as these words. Of course hundreds of other symbolic timezones and variations exist, but they are not standardized, and are therefore not supported by Kermit.
When a time zone is included with a time, the time is converted to local time. In case the conversion crosses a midnight boundary, the date is adjusted accordingly. Examples converting to EST (Eastern USA Standard Time = -0500):
11:30:00 = 11:30:00 11:30:00 EST = 11:30:00 11:30:00 GMT = 06:30:00 11:30:00 PST = 14:30:00 11:30:00Z = 06:30:00 11:30PM GMT = 18:30:00 11:30 -0500 = 11:30:00 11:30 -0800 = 08:30:00 11:30 +0200 = 04:30:00
Unlike most of Kermit's other date-time conversions, timezone knowledge (specifically, the offset of local time from UTC) is embodied in the underlying operating system, not in Kermit itself, and any conversion errors in this department are the fault of the OS. For example, most UNIX platforms do not perform conversions for years prior to 1970.
{+,-}[number units][hh[:mm[:ss]]]
In other words, a delta time always starts with a plus or minus sign, which is followed by a "part1", a "part2", or both. The "part1", if given, specifies a number of days, weeks, months, or years; "part2" specifies a time in hh:mm:ss notation. In arithmetic terms, these represents some number of days or other big time units, and then a fraction of a day expressed as hours, minutes, and seconds; these are to be added to or subtracted from the given (or implied) date and time. The syntax is somewhat flexible, as shown by the following examples:
+1 day (Plus one day) +1day (Ditto) +1d (Ditto) + 1 day (Ditto) + 1 day 3:00 (Plus one day and 3 hours) +1d3:00 (Ditto) +1d3 (Ditto) +3:00:00 (Plus 3 hours) +3:00 (Ditto) +3 (Ditto) +2 days (Plus 2 days) -12 days 7:14:22 (Minus 12 days, 7 hours, 14 minutes, and 22 seconds)
The words "week", "month", and "year" can be used like "day" in the examples above. A week is exactly equivalent to 7 days. When months are specified, the numeric month number of the date is incremented or decremented by the given number, and the year and day adjusted accordingly if necessary (for example, 31-Jan-2001 +1month = 03-Mar-2001 because February does not have 31 days). When years are specified, they are added or subtracted to the base year. Examples (assuming the current date is 10-Aug-2001 and the current time is 19:21:11):
18-Sep-2001 +1day (20010918 00:00:00) today +1day (20010811 00:00:00) now+1d (20010811 19:21:11) + 1 day (20010811 19:21:11) + 1 day 3:14:42 (20010811 22:35:54) + 7 weeks (20010928 19:21:11) +1d3:14:42 (20010811 22:35:54) +1w3:14:42 (20010817 22:35:54) +1m3:14:42 (20010910 22:35:54) +1y3:14:42 (20020810 22:35:54) 2 feb 2001 + 10 years (20110208 00:00:00) 2001-02-08 +10y12 (20110208 12:00:00) 31-dec-1999 23:59:59+00:00:01 (20000101 00:00:00) 28-feb-1996 +1day (19960229 00:00:00) (leap year) 28-feb-1997 +1day (19970301 00:00:00) (nonleap year) 28-feb-1997 +1month (19970328 00:00:00) 28-feb-1997 +1month 11:59:59 (19970328 11:59:59) 28-feb-1997 +20years (20170228 00:00:00) 28-feb-1997 +8000years (99970228 00:00:00)
For compatibility with VMS, the following special delta-time format is also accepted:
+number-hh:mm:ss -number-hh:mm:ss
(no spaces). The hyphen after the number indicates days. It corresponds exactly to the Kermit notation (note the 'd' between number and hh:mm:ss):
+numberdhh:mm:ss -numberdhh:mm:ss
The following forms all indicate exactly the same date and time:
18-Sep-2001 12:34:56 +1-3:23:01 18-Sep-2001 12:34:56 +1d3:23:01 18-Sep-2001 12:34:56 +1 day 3:23:01
and mean "add a day plus 3 hours, 23 minutes, and 1 second" to the given date.
Note that delta times are not at all the same as UTC offsets; the former specifies an adjustment to the given date/time and the latter specifies that the local time is a particular distance from Universal Time, for example:
11-Aug-2001 12:34:56 -0800 (20010811 16:34:56 -- UTC Offset) 11-Aug-2001 12:34:56 -08:00 (20010811 04:34:56 -- Delta time)
If you give a time followed by a modifier that starts with a + or - sign, how does Kermit know whether it's a UTC offset or a delta time? It is treated as a UTC offset if the sign is followed by exactly four decimal digits; otherwise it is a delta time. Examples (for USA Eastern Daylight Time):
11-Aug-2001 12:34:56 -0800 (20010811 16:34:56 -- UTC Offset) 11-Aug-2001 12:34:56 -08:00 (20010811 04:34:56 -- Delta time) 11-Aug-2001 12:34:56 -800 (20010811 04:34:56 -- Delta time) 11-Aug-2001 12:34:56 -8 (20010811 04:34:56 -- Delta time)
The first example says that at some unknown place which is 8 hours ahead of Universal Time, the time is 12:34:56, and this corresponds to 16:34:56 in Eastern Daylight time. The second example says to subtract 8 hours from the local time. The third and fourth are delta times because, even though a colon is not included, the time does not consist of exactly 4 digits.
When a delta time is written after a timezone, however, there is no ambiguity and no syntax distinction is required:
11-Aug-2001 12:34:56 -0800 -0800 (20010811 08:34:56) 11-Aug-2001 12:34:56 -0800 -08:00 (Ditto) 11-Aug-2001 12:34:56 -08:00 -08:00 (Illegal)
You can test any date-and/or-time format with the DATE command, which converts it to standard yyyymmdd hh:mm:ss format if it is understood, or else gives an explicit error message (rather than just "BAD DATE" as in previous C-Kermit releases) to indicate what is wrong with it. Examples (on Tuesday, 31 July 2001 in New York City, Eastern Daylight Time, UTC -0400):
DATE command argument Result 12:30 20010731 12:30:00 12:30:01 20010731 12:30:01 12:30:01.5 20010731 12:30:02 1230 20010731 12:30:00 230 20010731 02:30:00 230+1d 20010801 02:30:00 230+1d3:00 20010801 05:30:00 20010718 19:21:15 20010718 19:21:15 20010718_192115 20010718 19:21:15 20010718T192115 20010718 19:21:15 18 Jul 2001 +0400 20010717 23:59:59 18 Jul 2001 192115 20010718 19:21:15 18 Jul 2001 192115.8 20010718 19:21:16 18-Jul-2001T1921 20010718 19:21:00 18-Jul-2001 1921Z 20010718 15:21:00 18-Jul-2001 1921 GMT 20010718 15:21:00 18-Jul-2001 1921 UTC 20010718 15:21:00 18-Jul-2001 1921 Z 20010718 15:21:00 18-Jul-2001 1921Z 20010718 15:21:00 18-Jul-2001 1921 -04:00:00 20010718 19:21:00 21-Jul-2001_08:20:00am 20010721 08:20:00 21-Jul-2001_8:20:00P.M. 20010721 20:20:00 Fri Jul 20 11:26:25 2001 20010720 11:26:25 Fri Jul 20 11:26:25 GMT 2001 20010720 07:26:25 Sun, 9 Apr 2000 06:46:46 +0100 20000409 01:46:46 Sunday, 9 Apr 2000 06:46:46 +0100 20000409 01:46:46 now 20010731 19:41:12 today 20010731 00:00:00 today 09:00 20010731 09:00:00 tomorrow 20010801 00:00:00 tomorrow 09:00 20010801 09:00:00 tomorrow 09:00 GMT 20010801 05:00:00 yesterday 20010730 00:00:00 yesterday 09:00 20010730 09:00:00 + 3 days 20010803 00:00:00 +3 days 20010803 00:00:00 +3days 20010803 00:00:00 + 3days 20010803 00:00:00 + 3 days 09:00 20010803 09:00:00 + 2 weeks 20010814 00:00:00 + 1 month 20010831 00:00:00 - 7 months 20001231 00:00:00 + 10 years 20110731 00:00:00 friday 20010803 00:00:00 saturday 20010804 00:00:00 sunday 20010805 00:00:00 monday 20010806 00:00:00 tuesday 20010731 00:00:00 wednesday 20010801 00:00:00 thursday 20010802 00:00:00 friday 07:00 20010803 07:00:00 thursday 1:00pm 20010802 13:00:00 thursday 1:00pm GMT 20010802 09:00:00 Thu, 10 Nov 94 10:50:47 EST 19941110 10:50:47 Fri, 20 Oct 1995 18:35:15 -0400 (EDT) 19951020 18:35:15 31/12/2001 20011231 00:00:00 12/31/2001 20011231 00:00:00 2001-July-20 20010720 00:00:00 2001-September-30 20010930 00:00:00 30-September-2001 20010930 00:00:00 Sep 30, 2001 12:34:56 20010930 12:34:56 September 30, 2001 20010930 00:00:00 September 30, 2001 630 20010930 06:30:00 September 30 2001 630 20010930 06:30:00 Sep-30-2001 12:34:59 20010930 12:34:59 20010807113542.014 20010807 11:35.42 20010807113542.014Z 20010807 07:35:42
n1 = 1: yyyy-mmm-dd hh:mm:ss (mmm = English 3-letter month abbreviation)
n1 = 2: dd-mmm-yyyy hh:mm:ss (ditto)
n1 = 3: yyyymmddhhmmss (all numeric)
HINT: Although Kermit has a number of built-in date and time variables, it doesn't have a single one suitable for writing a timestamp. For this you would normally use something like "\v(ndate) \v(time)". But \fcvtdate() (with no arguments) is equivalent: it returns the current date and time in yyyymmdd hh:mm:ss format, suitable for time stamping.
define utcdate {
.local := \fcvtdate(\%*) ; 1.
.tmp := \fcvtdate(\m(local)UTC) ; 2.
.offset := \fdiffdate(\m(local),\m(tmp)) ; 3.
.utc := \fcvtdate(\m(local)\m(offset)) ; 4.
sho mac utc ; 5.
}
Brief explanation: Line 1 converts the macro argument, a free-format date-time, to standard-format local time. Line 2 appends the "UTC" timezone to the local time and converts the result to local time. In other words, we take the same time as the local time, but pretend it's UTC time, and convert it to local time. For example, if New York time is 4 hours ahead of UTC, then 6:00pm New York time is 2:00pm UTC. Line 3 gets the difference of the two results (e.g. "+04:00"). Line 4 appends the difference (delta time) to the local time, and converts it again, which adds (or subtracts) the UTC offset to the given time. Line 5 displays the result.
Here's a script that opens a web page, gets its headers into an array, scans the array for the "Last-Modified:" header, and interprets it:
http open www.columbia.edu
if fail stop 1 HTTP OPEN failed
http /array:a head index.html /dev/null
if fail stop 1 HTTP GET failed
show array a
for \%i 1 \fdim(&a) 1 {
.\%x := \findex(:,\&a[\%i])
if not \%x continue
.tag := \fleft(\&a[\%i],\%x-1)
.val := \fltrim(\fsubstr(\&a[\%i],\%x+1))
if ( eq "\m(tag)" "Last-Modified" ) {
echo HTTP Date: \m(val)
.rdate := \fcvtdate(\m(val))
echo {Standard Date (local): \m(rdate)}
echo {Standard Date (UTC): \futcdate(\m(rdate))}
break
}
}
http close
The result:
HTTP Date: Mon, 13 Aug 2001 20:05:42 GMT Standard Date (local): 20010813 16:05:42 Standard Date (UTC): 20010813 20:05:42
As you can see, Kermit had no trouble decoding the date-time-string from the website, converting to local time, and converting back to UTC with no conflicts or loss of information. If it had been in any other known format, the result would have been the same.
Now suppose we want to download the web page only if it is newer than our local copy. The \fdate(filename) function (which returns the modification date-time of the given file) and the new \fcmpdates() function make it easy. Insert the following just before the BREAK statement:
if ( < 0 \fcmpdates(\m(rdate),\fdate(index.html)) ) {
echo GETTING index.html...
http get index.html index.html
if success echo HTTP GET OK
} else {
echo index.html: no update needed
}
http close
exit
This says, "if 0 is less than the comparison of the remote file date and the local file date, get the remote file, otherwise skip it." And it automatically reconciles the time-zone difference (if any).
It would be nice to be able to extend this script into a general-purpose website updater, but unfortunately HTTP protocol doesn't provide any mechanism for the client to ask the server for a list of files, recursive or otherwise.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
Suppose, however, you want certain actions to occur when a script is interrupted; for example, closing open files, writing log entries, or displaying summary results. You can do this by defining a macro named ON_CTRLC. When Ctrl-C is detected, and a macro with this name is defined, Kermit executes it from the current command level, thus giving it full access to the environment in which the interruption occurred, including local variables and open files. Only when the ON_CTRLC macro completes execution is the command stack rolled back to top level.
Once the ON_CTRLC macro is defined, it can be executed only once. This is to prevent recursion if the user types Ctrl-C while the ON_CTRLC macro is executing. If you type Ctrl-C while the Ctrl-C macro is active, this does not start a new copy of ON_CTRLC; rather, it returns to the top-level command prompt. After the ON_CTRLC macro returns, it has been removed from the macro table so if you want to use it again or install a different Ctrl-C trap, you must execute a new DEFINE ON_CTRLC command. In any case, as always when you interrupt a script with Ctrl-C, its completion status is FAILURE.
Normally the ON_CTRLC macro would be defined in the command file or macro to which it applies, and should be declared LOCAL. This way, if the command file or macro completes successfully without being interrupted, the ON_CTRLC definition disappears automatically. Otherwise the definition would still be valid and the macro would be executed, probably out of context, the next time you typed Ctrl-C.
Here's a simple example of a command file that sets a Ctrl-C trap for itself:
local on_ctrlc ; Make Ctrl-C trap local to this command file.
define on_ctrlc { ; Define the ON_CTRLC macro.
echo Interrupted at \v(time).
echo Iterations: \%n
}
xecho Type Ctrl-C to quit
for \%n 1 999 1 { ; Prints a dot every second until interrupted.
sleep 1
xecho .
}
echo Finished normally at \v(time) ; Get here only if not interrupted.
decrement \%n
echo Iterations: \%n
This prints a summary no matter whether it completes normally or is interrupted from the keyboard. In both cases the trap is automatically removed afterwards.
For an example of how to use ON_CTRLC to debug scripts, see Section 8.1.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
Ever since C-Kermit version 5 was released in 1988, scripting has been one of its major attractions, and arithmetic is a key part of it. Versions 5 and 6 included integer arithmetic only, using traditional algebraic notation, e.g.:
echo \fevaluate(3*(2+7)/2) 13
C-Kermit 7.0 added support for floating-point arithmetic, but only through function calls:
echo \ffpdivide(\ffpmultiply(3.0,\ffpadd(2.0,7.0)),2.0) 13.5
C-Kermit 8.0 introduces a third form of arithmetic that treats integers and floating-point numbers uniformly, is easier to read and write, and executes very quickly:
(/ (* 3 (+ 2 7)) 2) 13.5
But first some background.
The Kermit command and scripting language differs from true programming languages (such as C or Fortran) in many ways; one of the most prominent differences is the way in which variables are distinguished from constants. In a command language, words are taken literally; for example, the Unix shell:
cat foo.bar
displays the file named foo.bar. Whereas in a programming language like C, words are assumed to be variables:
s = foo.bar; /* Assigns the value of foo.bar to the variable s */
To make a programming language take words literally, you have to quote or "escape" them:
s = "foo.bar"; /* Assigns a pointer to the string "foo.bar" to the variable s */
The opposite holds for command languages: to get them to treat a word as a variable rather than a constant, you have to escape them. For example, in the Unix shell:
foo=123 ; Assign value 123 to variable foo. echo foo ; Prints "foo" echo $foo ; Prints "123"
And in Kermit:
define foo 123 ; Assign value 123 to variable foo. echo 123 ; This prints "123". echo foo ; This prints "foo". echo \m(foo) ; This prints "123".
In other words, character strings (such as "foo" above) are interpreted as literal strings, rather than variable names, except in special commands like DEFINE that deal specifically with variable names (or in numeric contexts as explained in Section 8.2). The special "escape" character (dollar sign ($) for the shell, backslash (\) for Kermit) indicates that a variable is to be replaced by its value.
The requirement to escape variable names in command languages normally does not impose any special hardship, but can add a considerable notational burden to arithmetic expressions, which are typically full of variables. Especially in Kermit when floating point numbers are involved, where you must use special \ffpxxx() functions, e.g. "\ffpadd(\m(a),\m(b))" rather than the simple "+" operator to add two floating-point numbers together, because the original arithmetic handler doesn't support floating point (this might change in the future). To illustrate, the general formula for the area of a triangle is:
sqrt(s * (s - a) * (s - b) * (s - c))
where a, b, and c are the lengths of the triangle's three sides and:
s = (a + b + c) / 2
Except in special cases (e.g. a = 3, b = 4, c = 5), the result has a fractional part so the computation must be done using floating-point arithmetic. We can create a Kermit 7.0 function for this as follows:
def area {
local s t1 t2 t3
assign s \ffpdiv(\ffpadd(\ffpadd(\%1,\%2),\%3),2.0)
assign t1 \ffpsub(\m(s),\%1)
assign t2 \ffpsub(\m(s),\%2)
assign t3 \ffpsub(\m(s),\%3)
return \ffpsqrt(\ffpmul(\m(s),\ffpmul(\m(t1),\ffpmul(\m(t2),\m(t3)))))
}
But as you can see, this is rather cumbersome. Note, in particular, that arithmetic functions like \ffpadd(), \ffpmul(), etc, take exactly two operands (like their symbolic counterparts + and *), so obtaining the product of three or more numbers (as we do in this case) is awkward.
Using the alternative S-Expression notation, we can reduce this to a form that is both easier to read and executes faster (the details are explained later):
def newarea {
(let s (/ (+ \%1 \%2 \%3) 2.0))
(sqrt (* s (- s \%1) (- s \%2) (- s \%3)))
}
In both examples, the \%1..3 variables are the normal Kermit macro arguments, referenced by the normal escaping mechanism. For increased readability, we can also assign the macro arguments \%1, \%2, and \%3 to the letters a, b, and c corresponding to our formula:
def newarea {
(let a \%1 b \%2 c \%3)
(let s (/ (+ a b c) 2.0))
(sqrt (* s (- s a) (- s b) (- s c)))
}
And now the Kermit function reads almost like the original formula. Here Kermit behaves more like a regular programming language. In an S-Expression, macro names need not be escaped when they are used as the names of numeric variables.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
C-Kermit does not pretend to be a full Lisp interpreter; only the arithmetic parts of Lisp have been incorporated: S-Expressions that operate on numbers and return numeric values (plus extensibility features described in Section 9.8, which allow some degree of string processing).
An S-Expression is a list of zero or more items, separated by spaces, within parentheses. Examples:
() (1) (a) (+ a 1) (* 2 a b)
If the S-Expression is empty, it has the NIL (empty) value. If it is not empty and the first item is an operator (such as + or *), there can be zero or more subsequent items, called the operands:
(+ 1 2)
Here the operator is "+" and the operands are "1" and "2", and the value of the S-Expression is the value of the operation (in this case 3). The operator always comes first, which is different from the familiar algebraic notation; this because S-Expression operators can have different numbers of operands:
(+ 1) (+ 1 2) (+ 1 2 3 4 5 6 7 8 9)
If the first item in the S-Expression is not an operator, then it must be a variable or a number (or a macro; see Section 9.8), and the S-Expression can only contain one item; in this case, the S-Expression's value is the value of the variable or number:
(a) (3)
Operands can be numbers, variables that have numeric values, functions that return numbers, or other S-Expressions. To illustrate an S-Expression within an S-Expression, observe that:
(+ 1 2)
is equivalent to any of the following (plus an infinite number of others):
(+ 1 (+ 1 1)) (+ (- 3 2) (/ 14 (+ 3 4)))
S-Expressions can be nested to any reasonable level; for example, the value of the following S-Expression is 64:
(- (* (+ 2 (* 3 4)) (- 9 (* 2 2))) 6)
Operators have no precedence, implied or otherwise, since they can't be mixed. The only exceptions are unary + and -, which simply indicate the sign of a number:
(* 3 -1)
Order of evaluation is specified entirely by parentheses, which are required around each operator and its operands: (+ a (* b c)) instead of (a + b * c).
S-Expressions provide a simple and isolated environment in which Kermit's macro names can be used without the \m(...) escaping that is normally required. Given:
define a 1 define b 2 define c 3
Then:
(+ \m(a) \m(b) \m(c))
is equivalent to:
(+ a b c)
Within an S-Expression, as in other strictly numeric contexts (Section 8.2), any operand that starts with a letter is treated as a Kermit macro name. In this context, abbreviations are not accepted; variable names must be spelled out in full. Alphabetic case is not significant; "a" and "A" are the same variable, but both are different from "area".
Of course, regular Kermit variables and functions can be used in S-Expressions in the normal ways:
(* \v(math_pi) (^ \%r 2)) ; Area of a circle with radius \%r (+ \fjoin(&a)) ; Sum of all elements of array \&a[]
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
(+ 1 1) ; Result is 2 (/ 9 3) ; Result is 3
If any of the operands is floating point, however, the result is also floating point:
(+ 1 1.0) ; Result is 2.0 (/ 9.0 3) ; Result is 3.0
If all the operands are integers but the result has a fractional part, the result is floating point:
(/ 10 3) ; Result is 3.333333333333333
To force an integer result in such cases, use the TRUNCATE operator:
(truncate (/ 10 3)) ; Result is 3
Similarly, to force a computation to occur in floating point, you can coerce one of its operands to FLOAT:
(+ 1 (float 1)) ; Result is 2.0
The result is also floating point if the magnitude of any integer operand, intermediate result, or the result itself, is larger than the maximum for the underlying machine architecture:
(^ 100 100)
If the result is too large even for floating-point representation, "Infinity" is printed; if it is too small to be distinguished from 0, 0.0 is returned.
Large numbers can be used and large results generated, but they are accurate only to the precision of the underlying machine. For example, the result of:
(+ 111111111111111111111 222222222222222222222)
should be 333333333333333333333, but 333333333333333300000.0 is produced instead if the machine is accurate to only about 16 decimal digits, even with coercion to floating-point. The order of magnitude is correct but the least significant digits are wrong. The imprecise nature of the result is indicated by the ".0" at the end. Contrast with:
(+ 111111111 222222222)
which produces an exact integer result.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
If you enter an S-Expression at the C-Kermit> prompt, its result is printed:
C-Kermit>(/ 10.0 3) 3.333333333333333 C-Kermit>
If an S-Expression is executed within a macro or command file, its value is not printed. However, you can control the printing action with:
In any case, the value of the most recent S-Expression (and the S-Expression itself) may be accessed programmatically through the following variables:
Besides issuing S-Expressions as commands in themselves, you can also execute them anywhere within a Kermit command, but in this case they must be enclosed in a function call (otherwise they are taken literally):
echo \fsexpression((+ 1 1)) ; Outer parentheses may be included
echo \fsexpr(+ 1 1) ; Outer parentheses may be omitted
echo Value = "\fsexp(+ 1 a)" ; Can be embedded in strings
echo Value = \&a[\fsexp(/ b 2)] ; Can be used in array subscripts
if = {\fsexp(+ 1 1)} 2 { ; Braces needed here for grouping
echo One plus one still equals two
}
The IF statement illustrates how to use S-Expressions as (or in) IF or WHILE conditions:
If an S-Expression is the last command executed in a macro, its value becomes the return value of the macro; no RETURN command is needed. Example:
def newarea {
(let s (/ (+ \%1 \%2 \%3) 2.0))
(sqrt (* s (- s \%1) (- s \%2) (- s \%3)))
}
This is equivalent to (but more efficient than):
def newarea {
(let s (/ (+ \%1 \%2 \%3) 2.0))
return \fsexp(sqrt (* s (- s \%1) (- s \%2) (- s \%3)))
}
When an S-Expression is entered as a command -- that is, the first nonblank character of the command is a left parenthesis -- then it is allowed to span multiple lines, as many as you like, until the first left parenthesis is matched:
(let s (/
(+
\%1
\%2
\%3
)
2.0
)
)
(sqrt (*
s
(- s \%1)
(- s \%2)
(- s \%3)
)
)
The S-Expression concept lends itself easily to embedding and recursion, but the depth to which recursion can occur is limited by the resources of the computer (memory size, address space, swap space on disk) and other factors. There is no way that C-Kermit can know what this limit is, since it varies not only from computer to computer, but also from moment to moment. If resources are exhausted by recursion, C-Kermit simply crashes; there's no way to trap this error. However, you can set a depth limit on S-Expressions:
You can also test the depth programmatically:
Help, completion, and syntax checking are not available within an S-Expression. If you type ? within an S-Expression, it says:
C-Kermit>(? S-Expression ("help sexp" for details)
As it says, typing "help sexp" will display a brief help text.
The SHOW SEXPRESSION command displays current SET SEXPRESSION settings and related information.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
These constants are specific to S-Expressions and are not visible outside them. They may not be used as the target of an assignment. So, for example:
(setq t 0) Fails assign t 0 Succeeds but this is not the same T!
E (the base of natural logarithms, 2.7182818184...) is not built in since it is not intrinsic in most Lisp dialects. If you want E to be the base of natural logarithms you can:
(setq e (exp 1))
Operators are either symbols (such as "+") or words. Words must be spelled out in full, not abbreviated. Differences of alphabetic case are ignored.
The most basic operation in S-Expressions is evaluation:
(eval) 0 (eval 1) 1 (eval a) value of a (eval (+ 1 a)) value of a+1 (eval (setq a 1) (setq b (+ a 0.5))) value of b (= a+0.5)
You can use "." as a shorthand for EVAL:
(.) (. 1) (. a) (. (+ 1 a)) (. (setq a 1) (setq b (+ a 0.5)))
Opposite of EVAL is the operator that suppresses evaluation of its operand:
(quote) (illegal) (quote a) a (quote hello) hello (quote (this is a string)) this is a string (quote this is a string) (illegal)
A shorthand notation is also accepted for quoting:
'a is equivalent to (quote a). And therefore:
'(a b c) is equivalent to (quote (a b c)).
More about quoting in Section 9.8.
The following operators assign values to variables:
(setq) Does nothing, returns NIL. (setq a) Undefines a, returns NIL. (setq a 1) Assigns 1 to a, returns 1. (setq a 1 b 2) Assigns 1 to a, 2 to b, returns 2. (setq a 1 b 2 c) Assigns 1 to a, 2 to b, undefines c, returns NIL.
To undefine a variable that is not the final one in the list, give it a value of "()" or NIL:
(setq a () b 2) Undefines a, assigns 2 to b, returns 2. (setq a nil b 2) Ditto.
Note that a variable can be used right away once it has a value:
(setq a 1 b a) Assigns 1 to a, the value of a (1) to b, returns 1.
The results of SETQ (when used with macro names) can be checked conveniently with SHOW MACRO, e.g:
show mac a b c
If you want to use SETQ or LET to assign a value to a backslash variable such as \%a or \&a[2], you must double the backslash:
(setq \\%a 3) (setq \\%b (+ \%a 1)) (setq \\&a[2] (setq (\\%c (+ \%a \%b))))
In other words:
See Section 9.6 for a fuller explanation of variable syntax and scope.
Here's a summary table of arithmetic operators; in the examples, a is 2 and b is -1.3:
Operator Description Example Result + Adds all operands (0 or more) (+ a b) 0.7 - Subtracts all operands (0 or more) (- 9 5 2 1) 1 * Multiplies all operands (0 or more) (* a (+ b 1) 3) -1.80 / Divides all operands (2 or more) (/ b a 2) -0.325 ^ Raise given number to given power (^ 3 2) 9 ++ Increments variables (++ a 1.2) 3.2 -- Decrements variables (-- a) 1 ABS Absolute value of 1 operand (abs (* a b 3)) 7.8 MAX Maximum of all operands (1 or more) (max 1 2 3 4) 4 MIN Minimum of all operands (1 or more) (min 1 2 3 4) 1 MOD (%) Modulus of all operands (1 or more) (mod 7 4 2) 1 FLOAT Convert an integer to floating-point (float 1) 1.0 TRUNCATE Integer part of floating-point operand (truncate 3.333) 3 CEILING Ceiling of floating-point operand (ceiling 1.25) 2 FLOOR Floor of floating-point operand (floor 1.25) 1 ROUND Operand rounded to nearest integer (round 1.75) 2 SQRT Square root of 1 operand (sqrt 2) 1.414.. EXP e (2.71828..) to the given power (exp -1) 0.367.. SIN Sine of angle-in-radians (sin (/ pi 2)) 1.0 COS Cosine of angle-in-radians (cos pi) -1.0 TAN Tangent of angle-in-radians (tan pi) 0.0 LOG Natural log (base e) of given number (log 2.7183) 1.000.. LOG10 Log base 10 of given number (log10 1000) 3.0
The ++ and -- operators are also assignment operators and work just like SETQ and LET in their interpretations of operators and operands, but:
If you include more than one variable-value pair in a ++ or -- expression, every variable (except, optionally, the last) must be followed by a value. Examples:
(++ a) Equivalent to (setq a (+ a 1)) and to (++ a 1) (++ a 2) Equivalent to (setq a (+ a 2)) (-- a (* 2 pi)) Equivalent to (setq a (- a (* 2 pi))) (++ a 1 b 1 c 1 d) Equivalent to four SETQs incrementing a,b,c,d by 1.
Another group of operators forms the predicates. These return a "truth value", in which 0 (or NIL) is false, and 1 or any other nonzero number is true.
Operator Description Example Result = (or ==) Operands are equal (= 1 1.0) 1 != Operands are not equal (!= 1 1.0) 0 < Operands in strictly ascending order (< 1 2 3) 1 <= Operands in ascending order (<= 1 1 2 3) 1 > Operands in strictly descending order (> 3 2 1) 1 >= Operands in descending order (<= 3 3 2 1) 1 AND (&&) Operands are all true (and 1 1 1 1 0) 0 OR (||) At least one operand is true (or 1 1 1 1 0) 1 XOR Logical Exclusive OR (xor 3 1) 0 NOT (!) Reverses truth value of operand (not 3) 0
The Exclusive OR of two values is true if one value is true and the other value is false.
And another group operates on bits within an integer word:
Operator Description Example Result & Bitwise AND (& 7 2) 2 | Bitwise OR (| 1 2 3 4) 7 # Bitwise Exclusive OR (# 3 1) 2 ~ Reverses all bits (~ 3) -4
These operators coerce their operands to integer by truncation if necessary. The result of bit reversal is hardware dependent.
The final category of operator works on truth values:
Operator Description Example Result IF Conditional evaluation (if (1) 2 3) 2
You can group multiple expressions in the s1 and s2 expressions using EVAL (or "."):
(if (< a 0) (eval (setq x 0) (setq y 0)) (eval (setq x a) (setq y b)))
or equivalently:
(if (< a 0) (. (setq x 0) (setq y 0)) (. (setq x a) (setq y b)))
Each operator has its own requirement as to number and type of operands. In the following table, "number" means any kind of number -- integer or floating-point -- or a variable, function, macro, or S-Expression that returns a number; "vname" means variable name, "fpnumber" means a floating-point number (or anything that resolves to one), and "integer" means integer (or anything that resolves to one). "truthvalue" means anything that resolves to a value of zero or an empty value (which indicates false) or a nonzero value (which indicates true). "any" means any kind of value, including none at all.
Operator Number of operands Type of operands Returns
EVAL (.) 0 or more S-Expression Last value (default NIL)
STRING 1 S-Expression string
QUOTE (') 1 word(s) string
SETQ 0 or more vname value pairs Last value (default NIL)
LET 0 or more vname value pairs Last value (default NIL)
+ 0 or more number number (default 0)
- 0 or more number number (default 0)
* 0 or more number number (see note (1))
/ 2 or more number number
^ 2 or more number number
++ 1 or more vname value pairs Result of last increment
-- 1 or more vname value pairs Result of last decrement
ABS 1 number number
MAX 1 or more number number
MIN 1 or more number number
MOD (%) 2 number number
FLOAT 1 number fpnumber
TRUNCATE 1 number integer
CEILING 1 number integer
FLOOR 1 number integer
ROUND 1 number integer
SQRT 1 number fpnumber
EXP 1 number fpnumber
SIN 1 number fpnumber
COS 1 number fpnumber
TAN 1 number fpnumber
LOG 1 number fpnumber
LOG10 1 number fpnumber
= (==) 1 or more number truthvalue
!= 1 or more number truthvalue
< 1 or more number truthvalue
<= 1 or more number truthvalue
> 1 or more number truthvalue
>= 1 or more number truthvalue
AND (&&) 1 or more truthvalue truthvalue
OR (||) 1 or more truthvalue truthvalue
XOR 2 truthvalue truthvalue
NOT (!) 1 truthvalue truthvalue
& 1 or more number (see note 2) integer
| 1 or more number (see note 2) integer
# 2 number (see note 2) integer
~ 1 number (see note 2) integer
IF 2 or 3 truthvalue,any,any any
Operators that don't require any arguments return the default values shown.
C-Kermit>(* 13272.42 0.40) 5308.968 C-Kermit>(/ * 2) 2654.4840 C-Kermit>
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
Thus S-Expression reader generally deals only with macro names (not backslash items) as variables. It is important to understand how the reader handles macro names. There are fundamentally two kinds of S-Expressions: those that contain a single element, such as:
(foo)
and those that contain more than one element:
(foo a b c)
If an S-Expression contains only one element, and it is the name of a macro, the macro's definition is examined. If the definition is a number (integer or floating-point, positive or negative), then this becomes the value of the expression. If the definition starts with ' (apostrophe), then the quoted word or string is the value of the expression (explained in Section 9.8). Otherwise, the macro is assumed to be composed of Kermit commands (possibly including S-Expressions), which are executed. If the macro has a RETURN value, or it executes an S-Expression as its last command, the result becomes the value of the S-Expression; otherwise the result is empty.
For S-Expressions that contain more than one element, and the first element is the name of a macro, then this macro is executed with the arguments that are given, after the arguments are evaluated by the S-Expression reader. Likewise, If the first element is a built-in operator, then it is applied to the operands after they are evaluated. In both cases, each operand is fed to the S-Expression reader recursively for evaluation. If an operand is a number or a quoted string, it is used as-is. But if it's a macro name, this degenerates into the first case, and the previous paragraph applies.
Examples:
define foo 123 (foo) Result: 123 define foo 'abc (foo) Result: abc define foo '(one two three) (foo) Result: one two three define foo return \frandom(1000) (foo) Result: 713 (or other number) define foo (+ a b) (foo) Result: The sum of a and b
A more difficult example:
define foo abc (foo) Result: ???
The result in the last example depends on the definition of abc:
The use of macros as S-Expression operators is described in Section 9.8.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
(setq a (+ 1 2)) evaluate a 1 + 2
When the operand is a string, SETQ is equivalent to DEFINE:
(setq a '(this is a string)) define a this is a string
In the first case, both statements create a macro named "a" with a value of 3. But in neither case is the macro "a" necessarily global. If either of these commands executes in an environment (i.e. macro invocation level) where a "local a" command has been given, the "a" macro is global to that environment, but is not visible outside it.
LET is equivalent to the Kermit LOCAL command, followed by the corresponding EVALUATE:
(let a (+ 1 2))
is equivalent to:
local a evaluate a 1 + 2
Again, "local" in this context applies to the Kermit macro invocation stack, not to the S-Expression nesting level. To illustrate, recall our "newarea" macro:
def newarea {
(let a \%1 b \%2 c \%3)
(let s (/ (+ a b c) 2.0))
(sqrt (* s (- s a) (- s b) (- s c)))
}
Because SETQ and LET expressions return a value, they can be placed within a larger S-Expression. In this case we can replace the first reference to the "s" variable by its defining expression:
def newarea {
(let a \%1 b \%2 c \%3)
(sqrt (* (let s (/ (+ a b c) 2.0)) (- s a) (- s b) (- s c)))
}
This would not work if LET were local to the S-Expression, but it works nicely in the context of Kermit macros. The previous definition is equivalent to:
def newarea {
local a b c s
(setq a \%1 b \%2 c \%3)
(sqrt (* (setq s (/ (+ a b c) 2.0)) (- s a) (- s b) (- s c)))
}
In both cases, the variables a, b, c, and s are local to the "newarea" macro, and global within it.
Multiple assignments can be handled in several ways. Here is the obvious way to initialize a series of variables to the same value:
(setq a 0) (setq b 0) (setq c 0) (setq s 0)
Here is a more compact and efficient way of doing the same thing:
(setq a 0 b 0 c 0 s 0)
However, in case the value was more complex, it's better to put only one copy of it in the S-Expression; in this case we rely on the fact that SETQ returns the value of its last assignment:
(setq a (setq b (setq c (setq s (* x (^ y 2))))))
Similarly, to set a series of variables to x, x+1, x+2, ...
(setq c (+ (setq b (+ (setq a (+ (setq s x) 1)) 1)) 1))
In the last example, you can see why "last" does not always correspond to "rightmost" (the leftmost variable "c" is assigned last).
If you are working with backslash variables like \%a or array elements like \&a[1], remember two rules:
Examples of assigning to a backslash variable:
(setq x 1) (setq \\%a 0) (setq \\&a[x+1] 1) (++ \\%x) (-- \\&a[x+2])
Examples of referring to a backslash variable's value:
(setq a (+ \%a 1)) (setq b (+ \%a \&a[1])) (++ a \%x) (-- b \&a[1])
The special notation is required because all backslashed items (\%x variables, array elements, built-in \v(xxx) variables, and \fxxx() function invocations) are evaluated in a single pass BEFORE the S-Expression is executed; any other approach would result in unacceptable performance. So, for example, in:
declare \&a[] = 1 2 3 define \%x 4 define \%y 0 (setq \\%y (+ \%x \&a[1]))
the S-Expression becomes:
(setq \%y (+ 4 1))
before it is sent to the S-Expression evaluator. If the backslash had not been doubled on the assignment target, the result would have been:
(setq 0 (+ 4 1))
which is illegal because you can't assign a value to a number. Conversely, if backslashes were doubled on right-hand-side values:
(setq \\%y (+ \\%x \\&a[1])
this too, would give an error (not numeric - "\%x").
If you omit the double backslash in the assignment target, the result depends on whether the variable already has a value:
(setq \%a (* 3 3))
If \%a has a non-numeric single-word value, then this becomes the name of the variable that is assigned by SETQ. To illustrate:
define \%a foo echo \%a foo (setq \%a (* 3 3)) echo \%a foo show macro foo foo = 9
If \%a has no value, a numeric value, or a multiword value, an "invalid assignment" error occurs.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
(/ (+ \fjoin(&a)) (float \fdim(&a)))
This results in a "Divide by zero" error if the array is empty. If you want to define the average value of an empty array to be 0 instead of getting an error, you can use IF to check the array size:
(if \fdim(&a) (/ (+ \fjoin(&a)) (float \fdim(&a))) 0)
or equivalently:
(if (not \fdim(&a)) 0 (/ (+ \fjoin(&a)) (float \fdim(&a))))
Of course, IF can fit anywhere else into an S-Expression:
(setq a (+ b (if (< c 0) 0 c)))
and the IF expression can be as complex as you like:
(setq a (+ b (if (and (or (> x 0) (> y 0)) (< c 0) (> d 1) (!= e 0)) 1 0)))
and the "then" and "else" parts can contain multiple S-Expressions enclosed within (EVAL ...):
(if x (eval (...) (...) (...)) (eval (...) (...) (...)))
AND and OR operators are guaranteed to "short circuit". If any operand of AND is false, none of the subsequent operands is evaluated; likewise, if an OR operand is true, no further operands are evaluated.
Bear in mind that the S-Expression IF is not the same as Kermit IF; the condition is only allowed to be an S-Expression or a variable or number, not the whole list of possibilities you see when you type "if ?" at the C-Kermit> prompt. But keep reading...
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
And with the following enhancement:
define bump (++ \%1)
is equivalent to:
define bump return \fsexpression(++ \%1)
Here's an example in which we define a FIBONACCI operator that returns the nth element, n >= 0, of the Fibonacci series, 0 1 1 2 3 5 8 13 21 34 55, . . ., in which the first element is 0, the second is 1, and each subsequent element is the sum of the two before it. This series was devised by Leonardo Pisano, Filius Bonacci (Fibonacci for short) in 1202 to describe how fast rabbits can breed, and also forms the basis for the Golden Mean, the branching behavior of plants, the spiral of a nautilus shell, etc. (Thanks to Dat Thuc Nguyen for December 2003 corrections to this section!)
We can write a FIBONACCI function as a macro easily with S-Expressions:
define FIBONACCI {
(if (== \%1 0) 0
(if (== \%1 1) 1 (+ (fibonacci (- \%1 2)) (fibonacci (- \%1 1)))))
}
You can read this as:
If the argument (\%1) is 0, return a result of 0; if it is 1, return 1; otherwise:
return the sum of fibonacci(argument - 2) and fibonacci(argument - 1)
Note that a RETURN statement is not needed, since S-Expressions automatically set the return value of their containing macros.
For comparison, here's how it would be coded without S-Expressions:
define FIBONACCI {
if == \%1 0 {
return 0
} else if == \%1 1 {
return 1
} else {
return \feval(\fexec(fibonacci \feval(\%1-2)) -
+ \fexec(fibonacci \feval(\%1-1)))
}
}
Now we can use the FIBONACCI function (whichever way you write it) just as if it were a built-in operator:
(fibonacci 6)
Or:
(setq a 10) (fibonacci a)
Within S-Expressions only (not outside them), S-Expressions themselves can be used as macro arguments:
(setq a 2 b 4) (setq x (fibonacci (* a b )))
The value of the S-Expression (in this case "8"), and not the S-Expression itself, is sent to the macro.
Your macro is responsible for argument validation and error handling. A robust Fibonacci macro would be more like this:
define FIBONACCI {
if < \v(argc) 2 end 1 ?\%0: Missing argument
if > \v(argc) 2 end 1 ?\%0: Too many arguments
if not integer \%1 end 1 ?\%0: Integers only
if < \%1 1 end 1 ?\%0: Argument out of range
(if (== \%1 0) 0
(if (== \%1 1) 1 (+ (fibonacci (- \%1 2)) (fibonacci (- \%1 1)))))
}
Recall that "END nonzero-number [ message ]" causes a macro invocation to fail. When the macro is the operator in an S-Expression, this makes the S-Expression fail too. Also note that our Fibonacci macro is just an illustration, not a practical example. Since it is recursive (calls itself), it won't work for large arguments because the call stack can exceed available memory. See Section 9.9.2 for a practical alternative.
Kermit macros, when used as S-Expression operators, can do anything at all except initiate file transfers: they can print messages on the screen, read and write files, interact with the user, and so on. For example, here's a macro ASKME that asks you to enter a number, makes sure that you did, and then returns its value for use in the S-Expression:
define ASKME {
local \%n
while true {
ask \%n { Number: }
if not def \%n continue
if not numeric \%n {
echo Not numeric - "\%n"
continue
}
break
}
return \%n
}
(setq a (* 2 (askme))) ; Get number from user, double it, assign result to a.
Here's a macro you can use to validate that a number is in a given range:
define inrange {
if != \v(argc) 4 end 1 ?\%0: Wrong number of arguments
if ( < \%1 \%2 || > \%1 \%3 ) return 0
return 1
}
The first argument is the number to be checked, the second is the minimum acceptable value, the third is the maximum. You can use this (for example) in IF conditions:
define yes echo \%1 IS OK define no echo \%1 IS NOT OK (setq a -1 b 999) (if (inrange a 0 100) (yes a) (no a)) (if (inrange b -1000 +1000) (yes b) (no b))
This is just an illustration, of course; there's already a built-in operator to let you do range checking without help from macros:
(if (<= 0 a 100) (yes a) (no a)) (if (<= -1000 b +1000) (yes b) (no b))
To send string parameters to a macro, some kind of quoting is required to tell the S-Expression parser to take a given "word" literally rather than replacing it by its value. For this we use the Lisp QUOTE operator:
define length return \flength(\%1) (length (quote abcdefghijklmnopqrstuvwxyz)) 26
This causes the string "abcdefghijklmnopqrstuvwxyz" to be sent literally to the LENGTH macro. Kermit, like Lisp, also offers a shortcut for QUOTE, that lets us quote a word by prefixing it with a single quote (') character, also called apostrophe (ASCII 39):
(length 'abcdefghijklmnopqrstuvwxyz) 26
The two forms are equivalent.
How the macro treats its arguments is up to the macro. In the example above, the argument is treated as a literal string. However, it can also be treated as a variable name:
define string This is a string define length return \flength(\m(\%1)) (length 'string) 16
Note the construct \m(\%1). This means "the value of the macro whose name is the value of \%1". The value of \%1 in this case is the word "string", and the value of the macro whose name is "string" is "This is a string".
What if the macro takes multiple arguments, or a variable number of them? Here's a simple macro that prints a phrase that includes its arguments:
define complain echo It's too \%*!
(Recall that \%* means "all arguments".)
It can be called in the traditional way:
complain hot Result: "It's too hot!" complain cold and wet Result: "It's too cold and wet!"
Or from an S-Expression if you quote the arguments:
(complain 'hot) Result: "It's too hot!" (complain 'cold 'and 'wet) Result: "It's too cold and wet!"
To group multiple words into a single argument, use parentheses:
(complain (quote (cold and wet))) Result: "It's too cold and wet!" (complain '(cold and wet)) Result: "It's too cold and wet!"
Note the difference:
(complain 'cold 'and 'wet) Three arguments (complain '(cold and wet)) One argument
Since the COMPLAIN macro uses \%* to refer to all its arguments, no matter how many, it doesn't care which form you use. But it makes a difference in cases where the macro refers to its arguments individually.
To illustrate, let's consider a macro that receives the name of a macro and its argument list and executes it with its arguments, without knowing how many arguments there are. The following LOOP macro is used to execute the given macro with the given argument list the requested number of times:
def loop { local i, for i 1 \%1 1 do \%2 \%3 }
Within the LOOP macro, the first argument (\%1) is the loop count, \%2 is the macro name, and \%3 is the argument list. When the LOOP macro is invoked traditionally like this:
loop 3 complain hot
it prints "It's too hot!" three times. To invoke it from an S-Expression, you must quote both the macro name as well as the argument, since in this case the macro name itself is an argument:
(loop 3 'complain 'hot)
Now what if you need to send different or variable numbers of arguments to the LOOP macro? The LOOP macro can handle it already, provided you group the arguments into LOOP's third argument (\%3). In Kermit syntax, without grouping:
loop 3 complain cold and wet
prints "It's too cold!" three times ("and wet" is lost); but with grouping (either of the following two forms):
loop 3 complain {cold and wet}
loop 3 complain "cold and wet"
the LOOP macro prints "It's too cold and wet!" three times as desired.
To do the same thing in an S-Expression, just use the Lisp forms of quoting instead of the Kermit forms; the following two are equivalent:
(loop 3 'complain (quote (cold and wet))) (loop 3 'complain '(cold and wet))
Here's a similar example in which we write a macro that shows both the name and the value of one or more other macros, whose names are given as arguments (similar to "show macro"):
define display {
local \%i
for \%i 1 \v(argc)-1 1 {
echo \&_[\%i] = \m(\&_[\%i])
}
}
(Recall that \&_[] is the macro's argument vector array, equivalent to \%1, \%2, ...) The DISPLAY macro can be used in S-Expressions like this:
(setq a 1 b 2 c 3) (display 'a 'b 'c 'd)
which prints:
a = 1 b = 2 c = 3 d =
The names must be quoted to prevent their evaluation before they are sent to the macro. This ability to pass variables "by name" to macros, rather than by value, lets you write macros that change the values of argument variables. For example, here's a macro that doubles the value of its argument variable:
define double (++ \%1 \%1)
which you can call like this:
(setq a 12) (double 'a)
In the macro, \%1 is replace by the variable name "a"; (++ a a)" adds "a to itself, and sets the value of "a" to the result.
There are no built-in operators other than QUOTE, ', and STRING for handling strings in S-Expressions, but using just these, plus macros that use Kermit's regular string-handling features, you can easily extend S-Expressions to do string manipulation:
define len return \flen(\%1) Returns length of argument string define cap return \fupper(\%1) Uppercase argument string define rev return \freverse(\%1) Reverses argument string define sub return \fsubstr(\%1,\%2,\%3) Returns substring of arg string (len '(this is a string)) Result: 16 (rev '(this is a string)) Result: gnirts a si siht (rev (cap '(this is a string))) Result: GNIRTS A SI SIHT (sub (rev (cap '(this is a string))) 5 9) Result: TS A SI S
You can assign a string to a macro name as follows:
(setq foo '(this is a string)) (setq foo (quote (this is a string)))
The two are exactly equivalent. In both cases, the macro "foo" has the value:
'(this is a string)
so when it is retrieved it can be identified as a string rather than a number or commands to be executed. Thus:
(setq foo (quote (this is a string))) show macro foo foo = '(this is a string) (foo) this is a string
Note the different results for "show macro foo" and "(foo)". The former shows the internal definition; the latter evaluates the variable, which removes the quoting. And perhaps more important, note that if the apostrophe and surrounding parentheses were not stored as part of the definition, (foo) would try to execute "this is a string" as a command.
Given the assignment above, the following work as expected:
(len foo) Result: 16 (rev foo) Result: gnirts a si siht (rev (cap foo)) Result: GNIRTS A SI SIHT (sub (rev (cap foo)) 5 8) Result: TS A SI S
Note that, unlike built-in S-Expression operators that return numbers or truth values, these operators return strings. If you want to assign their return values to other variables, you can do so:
(setq bar (rev (cap foo))) Result: GNIRTS A SI SIHT
But now the S-Expression processor doesn't know the value of "bar" is supposed to be a string, rather than a macro to execute. For this you need one final special operator, STRING. The STRING operator takes an S-Expression as an operand, evaluates it, and then returns its value enclosed in '(), so you can use the value as a string is subsequent S-Expressions. Use STRING for referencing macros that return strings:
(setq bar (string (rev (cap foo)))) Result: '(GNIRTS A SI SIHT)
STRING is like QUOTE, except that it evaluates its operand before applying the quoting, rather than taking the operand literally.
To reference backslash variables or functions that return string values, you must use the regular quoting mechanisms:
(setq time '(\v(time))) (setq date '(\v(date))) assign \%r this is a string (setq s1 '(\%r))
That's because backslash items are evaluated BEFORE the S-Expression parser ever sees them, and the values of \v(time) and so on are not valid S-Expressions, so STRING won't like them.
Finally a brief word on the touchy topic of quoting. Suppose you want to include (say) literal parentheses in a string that will later be processed by the S-Expression reader (or \fsplit() or \fword()). Normally, you can't do this because parentheses are meaningful in these contexts. To defeat the normal parsing rules, you can quote the parentheses with backslash. However, due to the many levels of string processing involved, a surprisingly large amount of backslashes might be required, for example:
(setq s '(a b (c d) \\\\\\\\\\\\\\\\(e f (g h) x\\\\\\\\\\\\\\\\) j k))
This is nearly impossible to explain(*). Instead, just remember two points:
define s '(a b (c d) \\(e f (g h) x\\) j k)
Considerations like this apply in any scripting language (shell, Tcl, Perl, Python, etc). The situation is known as "Quoting Hell".
(*) If you really want an explanation, here it is:
Moral: To create string constants in which grouping characters must be quoted, use DEFINE rather than SETQ.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
; Initialize sums, maxima, minima, and number of elements
(setq xsum 0 ysum 0 xsum2 0 ysum2 0 xysum 0)
(setq xmin (setq xmax \&x[1]) ymin (setq ymax \&y[1]))
(setq n \fdim(&x))
; Loop through elements and accumulate sums, maxima, and minima
for i 1 n 1 {
(setq x \&x[i] y \&y[i]) ; Notational convenience
(setq xmax (max xmax x) ymax (max ymax y)) ; X and Y maxima
(setq xmin (min xmin x) ymin (min ymin y)) ; X and Y minima
(++ xsum x ysum y) ; X and Y sums
(++ xsum2 (^ x 2) ysum2 (^ y 2)) ; Sum of X and Y squares
(++ xysum (* x y)) ; Sum of XY products
}
; Calculate results
(setq xmean (/ xsum n) ymean (/ ysum n)) ; Mean X and Y
(setq xss (- xsum2 (/ (^ xsum 2) n))) ; Intermediate values
(setq yss (- ysum2 (/ (^ ysum 2) n)))
(setq xyss (- xysum (/ (* xsum ysum) n)))
(setq xvar (/ xss n) yvar (/ yss n)) ; X and Y variance
(setq sdx (sqrt xvar) sdy (sqrt yvar)) ; Std deviation in X and Y
(setq tmp (* xss yss))
(setq cc (if tmp (/ xyss (sqrt tmp)) 1.0)) ; Correlation coefficient
show macro xmean ymean xvar yvar sdx sdy cc ; Print the results
The final "if tmp" check accounts for the possibility that both arrays contain all 0's. Results can also be printed with "echo CC = \m(cc)", or any other desired way. Interestingly, if we had not needed the sum of the squares and products, we could have obtained the sums, maxima, and minima of the X's and Y's without a loop like this:
(setq xsum (+ \fjoin(&x)) ysum (+ \fjoin(&y))) (setq xmax (max \fjoin(&x)) ymax (max \fjoin(&y))) (setq xmin (min \fjoin(&x)) ymin (min \fjoin(&y)))
Any Kermit function that returns numbers or lists of numbers can be included in an S-Expression as an operand.
(setq t1 \v(ftime)) (setq result (fibonacci 17)) (setq t2 (- \v(ftime) t1)) echo FIBONACCI(17) = \m(result): TIME = \ffpround(t2,3)
prints (on a certain rather slow computer):
FIBONACCI(17) = 1597: TIME = 5.861
Any recursive function can be recoded iteratively. The result is not as pretty, but execution is far less expensive:
define FIBITER {
(if (== \%3 0) (\%2) (fibiter (+ \%1 \%2) \%1 (- \%3 1)))
}
define FIBONACCI {
(fibiter 1 0 \%1)
}
Here's the result on the same computer for the same argument of 17:
FIBONACCI(17) = 1597: TIME = 0.015
(47 times faster.) Execution time increases proportionally to the size of the argument in the iterative case, whereas in the recursive case it goes up geometrically, quickly reaching infinity.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
(a)
This expression can cause an error in Lisp (even if "a" has a value), but is acceptable in Kermit, where it returns the value of the variable "a". Similarly, (1) returns the value "1".
[13] USER(37): (/ (+ 1 2 3 4) 3) 10/3 [13] USER(38):
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
The following new "hot keys" are available when Kermit's file-transfer display is visible:
D: Turn on debugging, open "debug.log" if not already open.
d: Turn off debugging but leave log open (if it was open).
T: Turn on debug-log timestamps.
t: Turn off debug-log timestamps.
Other improvements:
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
In fact, Kermit assumes the modem is completely configured, and therefore does not send it an initialization string or any configuration commands. Instead, it sends only the simplest and most portable commands:
ATQ0V1 Give dial result codes.
ATDTnumber Dial the number.
(or ATD or ATDP, as appropriate).
The new defaults work for direct connections and for most modern modems on most platforms, and they work much faster than "full-treatment" dialing. If the new defaults don't work for you, or if you need to perform explicit modem configurations or interactions, then set a specific modem type and use the SET MODEM and SET DIAL commands as documented in Using C-Kermit.
WARNING: Don't use the generic modem on hosts that do not support RTS/CTS flow control. If Xon/Xoff is in use on the serial port, you'll need to select a particular modem type so Kermit knows what command to give it to enable Xon/Xoff flow control between itself and your serial port.
The following new modem types were added in C-Kermit 8.0:
lucent: Lucent Venus chipset
pctel: PCTel V.90 chipset
conexant: Conexant (ex-Rockwell) modem family
zoom-v32bis: New name for "Zoom"
zoom-v34 Zoom V.34
zoom-v90 Zoom V.90 56K
zoom-v92: Zoom V.92 with V.44 data compression
zoltrix-v34: New name for "zoltrix"
zoltrix-hsp-v90: Synonym for PCTel
zoltrix-hcf-v90: Synonym for ITU-T-V250
smartlink-v90: Synonym for usrobotics (same chipset)
acer-v90: Synonym for Rockwell-v90
New DIAL-related variables:
\v(dm_hf): Dial modifier: Wait for Hook-Flash.
\v(dm_wb): Dial modifier: Wait for Bong.
Finally, if dialing fails, Kermit now prints a context-sensitive hint suggesting possible reasons and remedies.
Added in C-Kermit 8.0.201: Rudimentary support for Caller ID, for use with the ANSWER command. If the modem reports Caller ID information, Kermit stores it in variables that you can access after the call is answered:
\v(callid_date) The date of the call \v(callid_time) The time of the call \v(callid_name) The name of the caller \v(callid_nmbr) The telephone number of the caller \v(callid_mesg) A message
The format of these items depends on the originating and answering phone companies and the modems and their configuration.
Not very many modems support Caller ID, and those that do (a) tend to have it disabled by default, and (b) use different commands to enable it. A quick survey shows of some current models shows:
- USR V.90: No - ITU-T V.250: No - Lucent Venus: No - Diamond Supra: #CID=1 - Rockwell 56K: #CID=1 - PCTEL: #CID=1 - Zoltrix: +VCID=1 - Conexant: +VCID=1
To use Kermit's Caller ID feature, you have to set the modem to wait for at least two rings before answering, and you have to give the command to enable Caller ID; for example (after choosing a modem with SET MODEM TYPE):
set modem command autoanswer on ATS0=2#CID=1\{13}
set modem command autoanswer on ATS0=2+VCID=1\{13}
These commands can be undone with:
set modem command autoanswer on ATS0=1#CID=0\{13}
set modem command autoanswer on ATS0=1+VCID=0\{13}
Kermit presently has no built-in knowledge of the Caller ID capabilities or commands of the modems in its database.
Since the variables can be accessed only after the call is answered, the only way to refuse a call is to answer it, inspect the variables, and then hang it up if desired.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
C-Kermit> set escape à C-Kermit> set escape 195 C-Kermit> show escape Escape character: Code 195 (Ã): enabled C-Kermit>
Both of these commands set the escape character value to 195 (decimal), which happens to be uppercase letter A with Tilde in Latin-1. SHOW ESCAPE and SHOW TERMINAL show the value, as does the CONNECT message.
A new command has been added to produce timestamped session logs:
SET TERMINAL IDLE-ACTION is useful for connections to hosts or services that automatically log you out after a certain amount of idle time, e.g.:
set term idle-timeout 300 set term idle-action output \32
sends a space (as if you had pressed the space bar) every 300 seconds (five minutes) while there is no activity (32 is the ASCII code for space).
When C-Kermit returns from CONNECT to command mode, the reason for the transition is given in a new variable, \v(cx_status):
0 No CONNECT command given yet.
1 User escaped back manually.
2 A trigger string was encountered.
3 IKSD entered server mode.
4 Application Program Command received from host.
5 Idle timeout.
6 Telnet protocol error.
7 Keystroke macro.
8 Time limit exceeded.
100 Internal error.
101 Carrier required by not detected.
102 I/O error on connection.
103 Disconnected by host.
104 Disconnected by user.
105 Session limit exceeded.
106 Rejected due to Telnet policy.
107 Received kill signal.
Values 100 and above indicate there is no connection.
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
But now, if you make a connection to a server that supports the Telnet Com Port Control Option, RFC 2217, you have the same degree of control as you would have over a serial port on the computer where Kermit is running: SET SPEED, SET FLOW, SET PARITY, SET STOP-BITS, SHOW COMM, WAIT, SET CARRIER-WATCH, the modem-signal variables, sending Break, and so on, apply to the connection between the terminal server and the modem.
For example, using a Cisco Access Server 2509, where specifying a TCP port in the 6000's selects a serial port that can be used for dialing out:
set host xxx 6001 ; xxx is the IP hostname or address of the server (log in if necessary) ; With a script or by hand set modem type usr ; Tell Kermit what kind of modem it has set speed 57600 ; This affects the server's port set flow rts/cts ; Ditto dial 7654321
The modem server might or might not require a login sequence. It might also allow for automatic authentication, e.g. via Kerberos tickets. NOTE: If the modem server requires a login sequence, then REDIAL might not work as expected.
When you have a Telnet Com Port connection, your SET SPEED and SET FLOW options change automatically to reflect the capabilities of the server, rather than those of your local computer.
See the configuration manual for your server for additional information. For example, how to set up the server to drop the Telnet connection automatically when the telephone call is hung up (e.g. "autohangup" on Cisco models).
For a Linux-based Telnet Com-Port server, click the Sredird link:
[ Top ] [ Contents ] [ Sredird ] [ C-Kermit Home ] [ Kermit Home ]
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
-dd Debug, like -d but adds timestamps
--version Shows C-Kermit version number.
--noperms Equivalent to SET ATTRIBUTE PROTECTION OFF.
Kermit now accepts a selection of URLs (Universal Resource Locators) as its first command-line argument. These are:
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]
As noted elsewhere, the new "-dd" command-line option selects a timestamped debug log (equivalent to "set debug timestamps on", "log debug debug.log").
C-Kermit 8.0 also supports a new timestamped session log via "set session-log timestamped-text", "log session".
There have been requests for other kinds of logs, for example a command log. These might be added at some point. One person wanted to be able to log commands with timestamps, but only commands issued at the prompt, not commands from files or macros, and also wanted a header line at the beginning showing the date, user, and host. This can be done as follows:
.filename := \v(home)commands.log ; (for example)
fopen /write \%c \m(filename)
if success {
fwrite /line \%c \v(date): User=\v(user) Host=\v(host)
fclose \%c
set debug timestamps on
log debug {| grep "CMD(P)" >> \m(filename)} append
}
[ Top ] [ Contents ] [ C-Kermit Home ] [ Kermit Home ]