Merge branch 'upstream' into test
[ckermit.git] / ckcplm.txt
diff --git a/ckcplm.txt b/ckcplm.txt
deleted file mode 100644 (file)
index 9c2f44f..0000000
+++ /dev/null
@@ -1,3076 +0,0 @@
-
-                         C-Kermit Program Logic Manual
-
-     Frank da Cruz
-     [1]The Kermit Project
-     [2]Columbia University
-
-   As of: C-Kermit 8.0.211, 10 April 2004
-   This page last updated: Sat Apr 10 16:45:30 2004 (New York USA Time)
-
-     IF YOU ARE READING A PLAIN-TEXT version of this document, note that
-     this file is a plain-text dump of a Web page. You can visit the
-     original (and possibly more up-to-date) Web page here:
-
-  [3]http://www.columbia.edu/kermit/ckcplm.html
-
-   [ [4]C-Kermit Home ] [ [5]Kermit Home ]
-    ________________________________________________________________________
-
-  CONTENTS
-
-  1. [6]INTRODUCTION
-  2. [7]FILES
-  3. [8]SOURCE CODE PORTABILITY AND STYLE
-  4. [9]MODULES
-     4.A. [10]Group A: Library Routines
-     4.B. [11]Group B: Kermit File Transfer
-     4.C. [12]Group C: Character-Set Conversion
-     4.D. [13]Group D: User Interface
-     4.E. [14]Group E: Platform-Dependent I/O
-     4.F. [15]Group F: Network Support
-     4.G. [16]Group G: Formatted Screen Support
-     4.H. [17]Group H: Pseudoterminal Support
-     4.I. [18]Group I: Security
-  I. [19]APPENDIX I: FILE PERMISSIONS
-    ________________________________________________________________________
-
-  1. INTRODUCTION
-
-   The Kermit Protocol is specified in the book Kermit, A File Transfer
-   Protocol by Frank da Cruz, Digital Press / Butterworth Heinemann,
-   Newton, MA, USA (1987), 379 pages, ISBN 0-932376-88-6. It is assumed
-   the reader is familiar with the Kermit protocol specification.
-
-   This file describes the relationship among the modules and functions
-   of C-Kermit 5A and later, and other programming considerations.
-   C-Kermit is designed to be portable to any kind of computer that has a
-   C compiler. The source code is broken into many files that are grouped
-   according to their function, as shown in the [20]Contents.
-
-   C-Kermit has seen constant development since 1985. Throughout its
-   history, there has been a neverending tug-of-war among:
-
-    a. Functionality: adding new features, fixing bugs, improving
-       performance.
-    b. Adding support for new platforms.
-    c. "Buzzword 1.0 compliance".
-
-   The latter category is the most frustrating, since it generally
-   involves massive changes just to keep the software doing what it did
-   before in some new setting: e.g. the K&R-to-ANSIC conversion (which
-   had to be done, of course, without breaking K&R); Y2K (not a big deal
-   in our case); the many and varied UNIX and other API "standards";
-   IPv6.
-
-   [ [21]Contents ] [ [22]C-Kermit ] [ [23]Kermit Home ]
-    ________________________________________________________________________
-
-  2. FILES
-
-   C-Kermit source files begin with the two letters "ck", for example
-   ckutio.c. Filenames are kept short (6.3) for maximum portability and
-   (obviously I hope) do not contain spaces or more than one period. The
-   third character in the name denotes something about the function group
-   and the expected level of portability:
-
-     a     General descriptive material and documentation (text)
-     b     BOO file encoders and decoders (obsolete)
-     c     All platforms with C compilers (*)
-     d     Data General AOS/VS
-     e     Reserved for "ckermit" files, like ckermit.ini, ckermit2.txt
-     f     (reserved)
-     g     (reserved)
-     h     (reserved)
-     i     Commodore Amiga (Intuition)
-     j     (unused)
-     k     (unused)
-     l     Stratus VOS
-     m     Macintosh with Mac OS 1-9
-     n     Microsoft Windows NT/2000/XP
-     o     OS/2 and/or Microsoft Windows 9x/ME/NT/2000/XP
-     p     Plan 9 from Bell Labs
-     q     (reserved)
-     r     DEC PDP-11 with RSTS/E (never used, open for reassigment)
-     s     Atari ST GEMDOS (last supported in version 5A(189))
-     t     DEC PDP-11 with RT-11 (never used, open for reassigment)
-     u     Unix-based operating systems (*)
-     v     VMS and OpenVMS
-     w     Wart (Lex-like preprocessor, platform independent)
-     x     (reserved)
-     y     (reserved)
-     z     (reserved)
-     0-3   (reserved)
-     4     IBM AS/400
-     5-8   (reserved)
-     9     Microware OS-9
-     _     Encryption modules
-
-   (*) In fact there is little distinction between the ckc*.* and cku*.*
-   categories. It would make more sense for all cku*.* modules to be
-   ckc*.* ones, except ckufio.c, ckutio.c, ckucon.c, ckucns.c, and
-   ckupty.c, which truly are specific to Unix. The rest (ckuus*.c,
-   ckucmd.c, etc) are quite portable.
-
-   One hint before proceeding: functions are scattered all over the
-   ckc*.c and cku*.c modules, where function size has begun to take
-   precedence over the desirability of grouping related functions
-   together, the aim being to keep any particular module from growing
-   disproportionately large. The easiest way (in UNIX) to find out in
-   what source file a given function is defined is like this (where the
-   desired function is foo()...):
-
-  grep ^foo\( ck*.c
-
-   This works because the coding convention has been to make function
-   names always start on the left margin with their contents indented,
-   for example:
-
-static char *
-foo(x,y) int x, y; {
-    ...
-}
-
-   Also note the style for bracket placement. This allows
-   bracket-matching text editors (such as EMACS) to help you make sure
-   you know which opening bracket a closing bracket matches, particularly
-   when the opening bracket is above the visible screen, and it also
-   makes it easy to find the end of a function (search for '}' on the
-   left margin).
-
-   Of course EMACS tags work nicely with this format too:
-
-  $ cd kermit-source-directory
-  $ etags ck[cu]*.c
-  $ emacs
-  Esc-X Visit-Tags-Table<CR><CR>
-
-   (but remember that the source file for ckcpro.c is [24]ckcpro.w!)
-
-   Also:
-
-     * Tabs should be set every 8 spaces, as on a VT100.
-     * All lines must no more than 79 characters wide after tab
-       expansion.
-     * Note the distinction between physical tabs (ASCII 9) and the
-       indentation conventions, which are: 4 for block contents, 2 for
-       most other stuff (obviously this is not a portability issue, just
-       style).
-
-   [ [25]Contents ] [ [26]C-Kermit ] [ [27]Kermit Home ]
-    ________________________________________________________________________
-
-  3. SOURCE CODE PORTABILITY AND STYLE
-
-   C-Kermit was designed in 1985 as a platform-independent replacement
-   for the earlier Unix Kermit. c-Kermit's design was expected to promote
-   portability, and judging from the number of platforms to which it has
-   been adapted since then, the model is effective, if not ideal
-   (obviously if we had it all to do over, we'd change a few things). To
-   answer the oft-repeated question: "Why are there so many #ifdefs?",
-   it's because:
-
-     * Many of them are related to feature selection and program size,
-       and so need to be there anyway.
-     * Those that treat compiler, library, platform, header-file, and
-       similar differences have built up over time as hundreds of people
-       all over the world adapted C-Kermit to their particular
-       environments and sent back their changes. There might be more
-       politically-correct ways to achieve portability, but this one is
-       natural and proven. The basic idea is to introduce changes that
-       can be selected by defining a symbol, which, if not defined,
-       leaves the program exactly as it was before the changes.
-     * Although it might be possible to "clean up" the "#ifdef mess",
-       nobody has access to all the hundreds of platforms served by the
-       #ifdefs to check the results.
-
-   And to answer the second-most-oft-repeated question: "Why don't you
-   just use GNU autoconfig / automake / autowhatever instead of
-   hard-coding all those #ifdefs?" Answers:
-
-     * The GNU tools are not available on all the platforms where
-       C-Kermit must be built and I wouldn't necessarily trust them if
-       they were.
-     * Each platform is a moving target, so the tools themselves would
-       need to updated before Kermit could be updated.
-     * It would only add another layer of complexity to an already
-       complex process.
-     * Conversion at this point would not be practical unless there was a
-       way to test the results on all the hundreds of platforms where
-       C-Kermit is supposed to build.
-
-   When writing code for the system-indendent C-Kermit modules, please
-   stick to the following coding conventions to ensure portability to the
-   widest possible variety of C preprocessors, compilers, and linkers, as
-   well as certain network and/or email transports. The same holds true
-   for many of the "system dependent" modules too; particularly the Unix
-   ones, since they must be buildable by a wide variety of compilers and
-   linkers, new and old.
-
-   This list does not purport to be comprehensive, and although some
-   items on it might seem far-fetched, they would not be listed unless I
-   had encountered them somewhere, some time. I wish I had kept better
-   records so I could cite specific platforms and compilers.
-
-     * Try to keep variable and function names unique within 6
-       characters, especially if they are used across modules, since 6 is
-       the maximum for some old linkers (actually, this goes back to
-       TOPS-10 and -20 and other old DEC OS's where C-Kermit never ran
-       anyway; a more realistic maximum is probably somewhere between 8
-       and 16). We know for certain that VAX C has a 31-character max
-       because it complains -- others might not complain, but just
-       silently truncate, thus folding two or more routines/variables
-       into one.
-     * Keep preprocessor symbols unique within 8 characters; that's the
-       max for some preprocessors (sorry, I can't give a specific
-       example, but in 1988 or thereabouts, I had to change character-set
-       symbols like TC_LATIN1 and TC_LATIN2 to TC_1LATIN and TC_2LATIN
-       because the digits were being truncated and ignored on a platform
-       where I actually had to build C-Kermit 5A; unfortunately I didn't
-       note which platform -- maybe some early Ultrix version?)
-     * Don't create preprocessor symbols, or variable or function names,
-       that start with underscore (_). These are usually reserved for
-       internal use by the compiler and header files.
-     * Don't put #include directives inside functions or { blocks }.
-     * Don't use the #if or #elif preprocessor constructions, only use
-       #ifdef, #ifndef, #define, #undef, and #endif.
-     * Put tokens after #endif in comment brackets, e.g.
-       #endif /* FOO */.
-     * Don't indent preprocessor statements - # must always be first char
-       on line.
-     * Don't put whitespace after # in preprocessor statements.
-     * Don't use #pragma, even within #ifdefs -- it makes some
-       preprocessors give up.
-     * Same goes for #module, #if, etc - #ifdefs do NOT protect them.
-     * Don't use logical operators in preprocessor constructions.
-     * Avoid #ifdefs inside argument list to function calls (I can't
-       remember why this one is here, but probably needn't be; we do this
-       all the time).
-     * Always cast strlen() in expressions to int:
-       if ((int)strlen(foo) < x)...
-     * Any variable whose value might exceed 16383 should be declared as
-       long, or if that is not possible, then as unsigned.
-     * Avoid typedefs; they might be portable but they are very confusing
-       and there's no way to test for their presence or absence at
-       compile time. Use preprocessor symbols instead if possible; at
-       least you can test their definitions.
-     * Unsigned long is not portable; use a preprocessor symbol (Kermit
-       uses ULONG for this).
-     * Long long is not portable. If you really need it, be creative.
-     * Similarly 1234LL is not portable, nor almost any other constant
-       modifier other than L.
-     * Unsigned char is not portable, use CHAR (a preprocessor symbol
-       defined in the Kermit header files) and always take precautions
-       against character signage (more about this [28]below).
-     * Don't use initializers with automatic arrays or structs: it's not
-       portable.
-     * Don't use big automatic arrays or structs in functions that might
-       be called recursively; some platforms have fixed-size stacks (e.g.
-       Windows 9x: 256K) and recursive functions crash with stack
-       overflow. Even when there is not a compiler limitation, this
-       causes memory to be consumed without bound, and can end up filling
-       swap space.
-     * Don't assume that struct assignment performs a copy, or that it
-       even exists.
-     * Don't use sizeof to get the size of an array; someone might come
-       along later and and change it from static to malloc'd. Always use
-       a symbol to refer to the array's size.
-     * Don't put prototypes for static functions into header files that
-       are used by modules that don't contain that function; the link
-       step can fail with unresolved references (e.g. on AOS/VS).
-     * Avoid the construction *++p (the order of evaluation varies; it
-       shouldn't but at least one compiler had a bug that made me include
-       this item).
-     * Don't use triple assignments, like a = b = c = 0; (or quadruple,
-       etc). Some compilers generate bad code for these, or crash, etc
-       (some version of DEC C as I recall).
-     * Some compilers don't allow structure members to have the same
-       names as other identifiers. Try to give structure members unique
-       names.
-     * Don't assume anything about order of evaluation in boolean
-       expressions, or that they will stop early if a required condition
-       is not true, e.g.:
-  if (i > 0 && p[i-1] == blah)
-       can still dump core if i == 0 (hopefully this is not true of any
-       modern compiler, but I would not have said this if it did not
-       actually happen somewhere).
-     * Don't have a switch() statement with no cases (e.g. because of
-       #ifdefs); this is a fatal error in some compilers.
-     * Don't put lots of code in a switch case; move it out to a separate
-       function; some compilers run out of memory when presented with a
-       huge switch() statement -- it's not the number of cases that
-       matters; it's the overall amount of code.
-     * Some compilers might also limit the number of switch() cases, e.g.
-       to 254.
-     * Don't put anything between "switch() {" and "case:" -- switch
-       blocks are not like other blocks.
-     * Don't jump into or out of switches.
-     * Don't make character-string constants longer than about 250 bytes.
-       Longer strings should be broken up into arrays of strings.
-     * Don't write into character-string constants (obviously). Even when
-       you know you are not writing past the end; the compiler or linker
-       might have put them into read-only and/or shared memory, and/or
-       coalesced multiple equal constants so if you change one you change
-       them all.
-     * Don't depend on '\r' being carriage return.
-     * Don't depend on '\n' being linefeed or for that matter any SINGLE
-       character.
-     * Don't depend on '\r' and '\n' being different (e.g. as separate
-       switch() cases).
-     * In other words, don't use \n or \r to stand for specific
-       characters; use \012 and \015 instead.
-     * Don't code for "buzzword 1.0 compliance", unless "buzzword" is K&R
-       and "1.0" is the first edition.
-     * Don't use or depend on anything_t (size_t, pid_t, etc), except
-       time_t, without #ifdef protection (time_t is the only one I've
-       found that is accepted everywhere). This is a tough one because
-       the same function might require (say) a size_t arg on one
-       platform, whereas size_t is unheard of on another; or worse, it
-       might require a totally different data type, like int or long or
-       some other typedef'd thing. It has often proved necessary to
-       define a symbol to stand for the type of a particular argument to
-       a particular library or system function to get around this
-       problem.
-     * Don't use or depend on internationalization ("i18n") features,
-       wchar_t, locales, etc, in portable code; they are not portable.
-       Anyway, locales are not the right model for Kermit's
-       multi-character-set support. Kermit does all character-set
-       conversion itself and does not use any external libraries or
-       functions.
-     * In particular, don't use any library functions that deal with wide
-       characters or Unicode in any form. These are not only nonportable,
-       but a constantly shifting target (e.g. the ones in glibc).
-     * Don't make any assumption about signal handler type. It can be
-       void, int, long, or anything else. Always declare signal handlers
-       as SIGTYP (see definition in ckcdeb.h and augment it if necessary)
-       and always use SIGRETURN at exit points from signal handlers.
-     * Signals should always be re-armed to be used again (this barely
-       scratches the surface -- the differences between BSD/V7 and System
-       V and POSIX signal handling are numerous, and some platforms do
-       not even support signals, alarms, or longjmps correctly or at all
-       -- avoid all of this if you can).
-     * On the other hand, don't assume that signals are disarmed after
-       being raised. In some platforms you have to re-arm them, in others
-       they stay armed.
-     * Don't call malloc() and friends from a signal handler; don't do
-       anything but setting integer global variables in a signal handler.
-     * malloc() does not initialize allocated memory -- it never said it
-       did. Don't expect it to be all 0's.
-     * Did You Know: malloc() can succeed and the program can still dump
-       core later when it attempts to use the malloc'd memory? (This
-       happens when allocation is deferred until use and swap space is
-       full.)
-     * memset(), memmove(), and memcpy() are not portable, don't use them
-       without protecting them in ifdefs (we have USE_MEMCPY for this).
-       bzero()/bcopy() too, except we're guaranteed to have
-       bzero()/bcopy() when using the sockets library (not really). See
-       examples in the source.
-     * Don't assume that strncpy() stops on the first null byte -- most
-       versions always copy the number of bytes given in arg 3, padding
-       out with 0's and overwriting whatever was there before. Use
-       C-Kermit ckstrncpy() if you want predictable non-padding behavior,
-       guaranteed NUL-termination, and a useful return code.
-     * DID YOU KNOW.. that some versions of inet_blah() routines return
-       IP addresses in network byte order, while others return them local
-       machine byte order? So passing them to ntohs() or whatever is not
-       always the right thing to do.
-     * Don't use ANSI-format function declarations without #ifdef
-       CK_ANSIC, and always provide an #else for the non-ANSI case.
-     * Use the Kermit _PROTOTYP() macro for declaring function
-       prototypes; it works in both the ANSI and non-ANSI cases.
-     * Don't depend on any other ANSI preprocessor features like
-       "pasting" -- they are often missing or nonoperational.
-     * Don't assume any C++ syntax or semantics.
-     * Don't use // as a comment introducer. C is not C++.
-     * Don't declare a string as "char foo[]" in one module and "extern
-       char * foo" in another, or vice-versa: this causes core dumps.
-     * With compiler makers falling all over themselves trying to outdo
-       each other in ANSI strictness, it has become increasingly
-       necessary to cast EVERYTHING. This is increasingly true for char
-       vs unsigned char. We need to use unsigned chars if we want to deal
-       with 8-bit character sets, but most character- and string-oriented
-       APIs want (signed) char arguments, so explicit casts are
-       necessary. It would be nice if every compiler had a
-       -funsigned-char option (as gcc does), but they don't.
-     * a[x], where x is an unsigned char, can produce a wild memory
-       reference if x, when promoted to an int, becomes negative. Cast it
-       to (unsigned), even though it ALREADY IS unsigned.
-     * Be careful how you declare functions that have char or long
-       arguments; for ANSI compilers you MUST use ANSI declarations to
-       avoid promotion problems, but you can't use ANSI declarations with
-       non-ANSI compilers. Thus declarations of such functions must be
-       hideously entwined in #ifdefs. Example: latter:
-  int                          /*  Put character in server command buffer  */
-  #ifdef CK_ANSIC
-  putsrv(char c)
-  #else
-  putsrv(c) char c;
-  #endif /* CK_ANSIC */
-  /* putsrv */ {
-      *srvptr++ = c;
-      *srvptr = '\0';           /* Make sure buffer is null-terminated */
-      return(0);
-  }
-     * Be careful how you return characters from functions that return
-       int values -- "getc-like functions" -- in the ANSI world. Unless
-       you explicitly cast the return value to (unsigned), it is likely
-       to be "promoted" to an int and have its sign extended.
-     * At least one compiler (the one on DEC OSF/1 1.3) treats "/*" and
-       "*/" within string constants as comment begin and end. No amount
-       of #ifdefs will get around this one. You simply can't put these
-       sequences in a string constant, e.g. "/usr/local/doc/*.*".
-     * Avoid putting multiple macro references on a single line, e.g.:
-  putchar(BS); putchar(SP); putchar(BS)
-
-   This overflows the CPP output buffer of more than a few C
-   preprocessors (this happened, for example, with SunOS 4.1 cc, which
-   evidently has a 1K macro expansion buffer).
-
-   C-Kermit needs constant adjustment to new OS and compiler releases.
-   Every new OS release shuffles header files or their contents, or
-   prototypes, or data types, or levels of ANSI strictness, etc. Every
-   time you make an adjustment to remove a new compilation error, BE VERY
-   CAREFUL to #ifdef it on a symbol unique to the new configuration so
-   that the previous configuration (and all other configurations on all
-   other platforms) remain as before.
-
-   Assume nothing. Don't assume header files are where they are supposed
-   to be, that they contain what you think they contain, that they define
-   specific symbols to have certain values -- or define them at all!
-   Don't assume system header files protect themselves against multiple
-   inclusion. Don't assume that particular system or library calls are
-   available, or that the arguments are what you think they are -- order,
-   data type, passed by reference vs value, etc. Be conservative when
-   attempting to write portable code. Avoid all advanced features.
-
-   If you see something that does not make sense, don't assume it's a
-   mistake -- it might be there for a reason, and changing it or removing
-   is likely to cause compilation, linking, or runtime failures sometime,
-   somewhere. Some huge percentage of the code, especially in the
-   platform-dependent modules, is workarounds for compiler, linker, or
-   API bugs.
-
-   But finally... feel free to violate any or all of these rules in
-   platform-specific modules for environments in which the rules are
-   certain not to apply. For example, in VMS-specific code, it is OK to
-   use #if, because VAX C, DEC C, and VMS GCC all support it.
-
-   [ [29]Contents ] [ [30]C-Kermit ] [ [31]Kermit Home ]
-    ________________________________________________________________________
-
-  3.1. Memory Leaks
-
-   The C language and standard C library are notoriously inadequate and
-   unsafe. Strings are arrays of characters, usually referenced through
-   pointers. There is no native string datatype. Buffers are fixed size,
-   and C provides no runtime bounds checking, thus allowing overwriting
-   of other data or even program code. With the popularization of the
-   Internet, the "buffer exploit" has become a preferred method for
-   hackers to hijack privileged programs; long data strings are fed to a
-   program in hopes that it uses unsafe C library calls such as strcpy()
-   or sprintf() to copy strings into automatic arrays, thus overwriting
-   the call stack, and therefore the routine's return address. When such
-   a hole is discovered, a "string" can be constructed that contains
-   machine code to hijack the program's privileges and penetrate the
-   system.
-
-   This problem is partially addressed by the strn...() routines, which
-   should always be used in preference to their str...() equivalents
-   (except when the copy operation has already been prechecked, or there
-   is a good reason for not using them, e.g. the sometimes undesirable
-   side effect of strncpy() zeroing the remainder of the buffer). The
-   most gaping whole, however, is sprintf(), which performs no length
-   checking on its destination buffer, and is not easy to replace.
-   Although snprintf() routines are starting to appear, they are not yet
-   widespread, and certainly not universal, nor are they especially
-   portable, or even full-featured.
-
-   For these reasons, we have started to build up our own little library
-   of C Library replacements, ckclib.[ch]. These are safe and highly
-   portable primitives for memory management and string manipulation,
-   such as:
-
-   ckstrncpy()
-          Like strncpy but returns a useful value, doesn't zero buffer.
-
-   ckitoa()
-          Opposite of atoi()
-
-   ckltoa()
-          Opposite of atol()
-
-   ckctoa()
-          Returns character as string
-
-   ckmakmsg()
-          Used with ck?to?() as a safe sprintf() replacement for up to 4
-          items
-
-   ckmakxmsg()
-          Like ckmakmsg() but accepts up to 12 items
-
-   More about library functions in [32]Section 4.A.
-
-   [ [33]Contents ] [ [34]C-Kermit ] [ [35]Kermit Home ]
-    ________________________________________________________________________
-
-  3.2. The "char" vs "unsigned char" Dilemma
-
-   This is one of the most aggravating and vexing characteristics of the
-   C language. By design, chars (and char *'s) are SIGNED. But in the
-   modern era, however, we need to process characters that can have (or
-   include) 8-bit values, as in the ISO Latin-1, IBM CP 850, or UTF-8
-   character sets, so this data must be treated as unsigned. But some C
-   compilers (such as those based on the Bell UNIX V7 compiler) do not
-   support "unsigned char" as a data type. Therefore we have the macro or
-   typedef CHAR, which we use when we need chars to be unsigned, but
-   which, unfortunately, resolves itself to "char" on those compilers
-   that don't support "unsigned char". AND SO... We have to do a lot of
-   fiddling at runtime to avoid sign extension and so forth.
-
-   Some modern compilers (e.g. IBM, DEC, Microsoft) have options that say
-   "make all chars be unsigned" (e.g. GCC "-funsigned-char") and we use
-   them when they are available. Other compilers don't have this option,
-   and at the same time, are becoming increasingly strict about type
-   mismatches, and spew out torrents of warnings when we use a CHAR where
-   a char is expected, or vice versa. We fix these one by one using
-   casts, and the code becomes increasingly ugly. But there remains a
-   serious problem, namely that certain library and kernel functions have
-   arguments that are declared as signed chars (or pointers to them),
-   whereas our character data is unsigned. Fine, we can can use casts
-   here too -- but who knows what happens inside these routines.
-
-   [ [36]Contents ] [ [37]C-Kermit ] [ [38]Kermit Home ]
-    ________________________________________________________________________
-
-  4. MODULES
-
-   When C-Kermit is on the far end of a connection, it is said to be in
-   remote mode. When C-Kermit has made a connection to another computer,
-   it is in local mode. (If C-Kermit is "in the middle" of a multihop
-   connection, it is still in local mode.)
-
-   On another axis, C-Kermit can be in any of several major states:
-
-   Command State
-          Reading and writing from the job's controlling terminal or
-          "console". In this mode, all i/o is handled by the Group E
-          conxxx() (console i/o) routines.
-
-   Protocol State
-          Reading and writing from the communicatons device. In this
-          mode, all i/o is handled by the Group E ttxxx() (terminal i/o)
-          routines.
-
-   Terminal State
-          Reading from the keyboard with conxxx() routines and writing to
-          the communications device with ttxxx() routines AND vice-versa.
-
-   When in local mode, the console and communications device are
-   distinct. During file transfer, Kermit may put up a file-transfer
-   display on the console and sample the console for interruption
-   signals.
-
-   When in remote mode, the console and communications device are the
-   same, and therefore there can be no file-transfer display on the
-   console or interruptions from it (except for "in-band" interruptions
-   such as ^C^C^C).
-
-   [ [39]Contents ] [ [40]C-Kermit ] [ [41]Kermit Home ]
-    ________________________________________________________________________
-
-  4.A. Group A: Library Functions
-
-   Library functions, strictly portable, can be used by all modules on
-   all platforms: [42]ckclib.h, [43]ckclib.c.
-
-   (To be filled in... For now, see [44]Section 3.1 and the comments in
-   ckclib.c.)
-
-   [ [45]Contents ] [ [46]C-Kermit ] [ [47]Kermit Home ]
-    ________________________________________________________________________
-
-  4.B. Group B: Kermit File Transfer
-
-   The Kermit protocol kernel. These files, whose names start with "ckc
-   are supposed to be totally portable C, and are expected to compile
-   correctly on any platform with any C compiler. "Portable" does not
-   mean the same as as "ANSI" -- these modules must compile on 10- and
-   20-year old computers, with C preprocessors, compilers, and/or linkers
-   that have all sorts of restrictions. The Group B modules do not
-   include any header files other than those that come with Kermit
-   itself. They do not contain any library calls except from the standard
-   C library (e.g. printf()). They most certainly do not contain any
-   system calls. Files:
-
-   [48]ckcsym.h
-          For use by C compilers that don't allow -D on the command line.
-
-   [49]ckcasc.h
-          ASCII character symbol definitions.
-
-   [50]ckcsig.h
-          System-independent signal-handling definitions and prototypes.
-
-   [51]ckcdeb.h
-          Originally, debugging definitions. Now this file also contains
-          all definitions and prototypes that are shared by all modules
-          in all groups.
-
-   [52]ckcker.h
-          Kermit protocol symbol definitions.
-
-   [53]ckcxla.h
-          Character-set-related symbol definitions (see next section).
-
-   [54]ckcmai.c
-          The main program. This module contains the declarations of all
-          the protocol-related global variables that are shared among the
-          other modules.
-
-   [55]ckcpro.w
-          The protocol module itself, written in "wart", a lex-like
-          preprocessor that is distributed with Kermit under the name
-          CKWART.C.
-
-   [56]ckcfns.c, [57]ckcfn2.c, [58]ckcfn3.c
-          The protocol support functions used by the protocol module.
-
-   [59]Group B modules may call upon functions from [60]Group E, but not
-   from [61]Group D modules (with the single exception that the main
-   program invokes the user interface, which is in Group D). (This last
-   assertion is really only a conjecture.)
-
-   [ [62]Contents ] [ [63]C-Kermit ] [ [64]Kermit Home ]
-    ________________________________________________________________________
-
-  4.C. Group C: Character-Set Conversion
-
-   Character set translation tables and functions. Used by the [65]Group
-   B, protocol modules, but may be specific to different computers. (So
-   far, all character character sets supported by C-Kermit are supported
-   in [66]ckuxla.c and [67]ckuxla.h, including Macintosh and IBM
-   character sets). These modules should be completely portable, and not
-   rely on any kind of system or library services.
-
-   [68]ckcxla.h
-          Character-set definitions usable by all versions of C-Kermit.
-
-   ck?xla.h
-          Character-set definitions for computer "?", e.g. [69]ckuxla.h
-          for UNIX, [70]ckmxla.h for Macintosh.
-
-   [71]ck?xla
-          Character-set translation tables and functions for computer
-          "?", For example, CKUXLA.C for UNIX, CKMXLA.C for Macintosh. So
-          far, these are the only two such modules. The UNIX module is
-          used for all versions of C-Kermit except the Macintosh version.
-
-   [72]ckcuni.h
-          Unicode definitions
-
-   [73]ckcuni.c
-          Unicode module
-
-   Here's how to add a new file character set in the original
-   (non-Unicode modules). Assuming it is based on the Roman (Latin)
-   alphabet. Let's call it "Barbarian". First, in ck?xla.h, add a
-   definition for FC_BARBA (8 chars maximum length) and increase
-   MAXFCSETS by 1. Then, in ck?xla.c:
-
-     * Add a barbarian entry into the fcsinfo array.
-     * Add a "barbarian" entry to file character set keyword table,
-       fcstab.
-     * Add a "barbarian" entry to terminal character set keyword table,
-       ttcstab.
-     * Add a translation table from Latin-1 to barbarian: yl1ba[].
-     * Add a translation table from barbarian to Latin-1: ybal1[].
-     * Add a translation function from Barbarian to ASCII: xbaas().
-     * Add a translation function from Barbarian to Latin-1: xbal1().
-     * Add a translation function from Latin-1 to Barbarian: xl1ba().
-     * etc etc for each transfer character set...
-     * Add translation function pointers to the xls and xlr tables.
-
-   Other translations involving Barbarian (e.g. from Barbarian to
-   Latin-Cyrillic) are performed through these tables and functions. See
-   ckuxla.h and ckuxla.c for extensive examples.
-
-   To add a new Transfer Character Set, e.g. Latin Alphabet 9 (for the
-   Euro symbol), again in the "old" character-set modules:
-
-   In ckcxla.h:
-
-          + Add a TC_xxxx definition and increase MAXTCSETS accordingly.
-
-   In ck?xla.h (since any transfer charset is also a file charset):
-
-          + Add an FC_xxxx definition and increase MAXFCSETS accordingly.
-
-   In ck?xla.c:
-
-          + Add a tcsinfo[] entry.
-          + Make a tcstab[] keyword table entry.
-          + Make an fcsinfo[] table entry.
-          + Make an fcstab[] keyword table entry.
-          + Make a tcstab[] keyword table entry.
-          + If necessary, make a langinfo[] table entry.
-          + Make entries in the function pointer arrays.
-          + Provide any needed functions.
-
-   As of C-Kermit 7.0, character sets are also handled in parallel by the
-   new (and very large) Unicode module, ckcuni.[ch]. Eventually we should
-   phase out the old way, described just above, and operate entirely in
-   (and through) Unicode. The advantages are many. The disadvantages are
-   size and performance. To add a character to the Unicode modules:
-
-   In ckcuni.h:
-
-          + (To be filled in...)
-
-   In ckcuni.c:
-
-          + (To be filled in...)
-
-   [ [74]Contents ] [ [75]C-Kermit ] [ [76]Kermit Home ]
-    ________________________________________________________________________
-
-  4.D. Group D: User Interface
-
-   This is the code that communicates with the user, gets her commands,
-   informs her of the results. It may be command-line oriented,
-   interactive prompting dialog, menus and arrow keys, windows and mice,
-   speech recognition, telepathy, etc. The one provided is command-and
-   prompt, with the ability to read commands from various sources: the
-   console keyboard, a file, or a macro definition. The user interface
-   has three major functions:
-
-    1. Sets the parameters for the file transfer and then starts it. This
-       is done by setting certain (many) global variables, such as the
-       protocol machine start state, the file specification, file type,
-       communication parameters, packet length, window size, character
-       set, etc.
-    2. Displays messages on the user's screen during the file transfer,
-       using the screen() function, which is called by the group-1
-       modules.
-    3. Executes any commands directly that do not require Kermit
-       protocol, such as the CONNECT command, local file management
-       commands, parameter-setting commands, FTP client commands, etc.
-
-   If you plan to imbed the [77]Group B, files into a program with a
-   different user interface, your interface must supply an appropriate
-   screen() function, plus a couple related ones like chkint() and
-   intmsg() for handling keyboard (or mouse, etc) interruptions during
-   file transfer. The best way to find out about this is to link all the
-   C-Kermit modules together except the ckuu*.o and ckucon.o modules, and
-   see which missing symbols turn up.
-
-   C-Kermit's character-oriented user interface (as opposed to the
-   Macintosh version's graphical user interface) consists of the
-   following modules. C-Kermit can be built with an interactive command
-   parser, a command-line-option-only parser, a graphical user interface,
-   or any combination, and it can even be built with no user interface at
-   all (in which case it runs as a remote-mode Kermit server).
-
-   [78]ckucmd.h
-   [79]ckucmd.c
-          The command parsing primitives used by the interactive command
-          parser to parse keywords, numbers, filenames, etc, and to give
-          help, complete fields, supply defaults, allow abbreviations and
-          editing, etc. This package is totally independent of Kermit,
-          but does depend on the [80]Group E functions.
-
-   [81]ckuusr.h
-          Definitions of symbols used in Kermit's commands.
-
-   ckuus*.c
-          Kermit's interactive command parser, including the script
-          programming language: [82]ckuusr.c (includes top-level keyword
-          tables); [83]ckuus2.c (HELP command text); [84]ckuus3.c (most
-          of the SET command); [85]ckuus4.c (includes variables and
-          functions); ckuus[567].c (miscellaneous);
-
-   [86]ckuusy.c
-          The command-line-option parser.
-
-   [87]ckuusx.c
-          User interface functions common to both the interactive and
-          command-line parsers.
-
-   [88]ckuver.h
-          Version heralds for different implementations.
-
-   [89]ckuscr.c
-          The (old, uucp-like) SCRIPT command
-
-   [90]ckudia.c
-          The DIAL command. Includes specific knowledge of many types of
-          modems.
-
-   Note that none of the above files is actually Unix-specific. Over time
-   they have proven to be portable among all platforms where C-Kermit is
-   built: Unix, VMS, AOS/VS, Amiga, OS-9, VOS, etc etc. Thus the third
-   letter should more properly be "c", but changing it would be too
-   confusing.
-
-   ck?con.c, ckucns.c
-          The CONNECT command. Terminal connection, and in some cases
-          (Macintosh, Windows) also terminal emulation. NOTE: As of
-          C-Kermit 7.0, there are two different CONNECT modules for UNIX:
-          [91]ckucon.c -- the traditional, portable, fork()-based version
-          -- and [92]ckucns.c, a new version that uses select() rather
-          than forks so it can handle encryption. ckucns.c is the
-          preferred version for Unix; ckucon.c is not likely to keep pace
-          with it in terms of upgrades, etc. However, since select() is
-          not portable to every platform, ckucon.c will be kept
-          indefinitely for those platforms that can't use ckucns.c. NOTE:
-          SunLink X.25 support is available only in ckucon.c.
-
-   ck_*.*, ckuat*.*
-          Modules having to do with authentication and encryption. Since
-          the relaxation of USA export laws, they are included with the
-          general source-code distribution. Secure C-Kermit binaries can
-          be built using special targets in the standard makefile.
-          However, secure prebuilt binaries may not be distributed.
-
-   For other implementations, the files may, and probably do, have
-   different names. For example, the Macintosh graphical user interface
-   filenames start with "ckm". Kermit 95 uses the ckucmd and ckuus*
-   modules, but has its own CONNECT command modules. And so on.
-
-   Here is a brief description of C-Kermit's "user interface interface",
-   from ckuusr.c. It is nowhere near complete; in particular, hundreds of
-   global variables are shared among the many modules. These should, some
-   day, be collected into classes or structures that can be passed around
-   as needed; not only for purity's sake, but also to allow for multiple
-   simultaneous communication sessions and or user interfaces. Our list
-   of things to do is endless, and reorganizing the source is almost
-   always at the bottom.
-
-   The ckuus*.c modules (like many of the ckc*.c modules) depend on the
-   existence of C library features like fopen, fgets, feof, (f)printf,
-   argv/argc, etc. Other functions that are likely to vary among
-   operating systems -- like setting terminal modes or interrupts -- are
-   invoked via calls to functions that are defined in the [93]Group E
-   platform-dependent modules, ck?[ft]io.c. The command line parser
-   processes any arguments found on the command line, as passed to main()
-   via argv/argc. The interactive parser uses the facilities of the cmd
-   package (developed for this program, but, in theory, usable by any
-   program). Any command parser may be substituted for this one. The only
-   requirements for the Kermit command parser are these:
-
-    1. Set parameters via global variables like duplex, speed, ttname,
-       etc. See [94]ckcmai.c for the declarations and descriptions of
-       these variables.
-    2. If a command can be executed without the use of Kermit protocol,
-       then execute the command directly and set the sstate (start state)
-       variable to 0. Examples include SET commands, local directory
-       listings, the CONNECT command.
-    3. If a command requires the Kermit protocol, set the following
-       variables:
- sstate                             string data
-   'x' (enter server mode)            (none)
-   'r' (send a 'get' command)         cmarg, cmarg2
-   'v' (enter receive mode)           cmarg2
-   'g' (send a generic command)       cmarg
-   's' (send files)                   nfils, cmarg & cmarg2 OR cmlist
-   'c' (send a remote host command)   cmarg
-
-       cmlist is an array of pointers to strings.
-       cmarg, cmarg2 are pointers to strings.
-       nfils is an integer (hmmm, probably should be an unsigned long).
-
-        cmarg can be:
-                A filename string (possibly wild), or:
-                a pointer to a prefabricated generic command string, or:
-                a pointer to a host command string.
-
-        cmarg2 is:
-                The name to send a single file under, or:
-                the name under which to store an incoming file; must not
-                be wild.
-                If it's the name for receiving, a null value means to
-                store the file under the name it arrives with.
-
-        cmlist is:
-                A list of nonwild filenames, such as passed via argv.
-
-        nfils is an integer, interpreted as follows:
-                -1: filespec (possibly wild) in cmarg, must be expanded
-                internally.
-                0: send from stdin (standard input).
-                >0: number of files to send, from cmlist.
-
-   The screen() function is used to update the screen during file
-   transfer. The tlog() function writes to a transaction log (if TLOG is
-   defined). The debug() function writes to a debugging log (if DEBUG is
-   defined). The intmsg() and chkint() functions provide the user i/o for
-   interrupting file transfers.
-
-   [ [95]Contents ] [ [96]C-Kermit ] [ [97]Kermit Home ]
-    ________________________________________________________________________
-
-  4.E. Group E: Platform-Dependent I/O
-
-   Platform-dependent function definitions. All the Kermit modules,
-   including the command package, call upon these functions, which are
-   designed to provide system-independent primitives for controlling and
-   manipulating devices and files. For Unix, these functions are defined
-   in the files [98]ckufio.c (files), [99]ckutio.c (communications), and
-   [100]ckusig.c (signal handling).
-
-   For VMS, the files are [101]ckvfio.c, ckvtio.c, and [102]ckusig.c (VMS
-   can use the same signal handling routines as Unix). It doesn't really
-   matter what the files are called, except for Kermit distribution
-   purposes (grouping related files together alphabetically), only that
-   each function is provided with the name indicated, observes the same
-   calling and return conventions, and has the same type.
-
-   The Group E modules contain both functions and global variables that
-   are accessed by modules in the other groups. These are now described.
-
-   (By the way, I got this list by linking all the C-Kermit modules
-   together except ckutio and ckufio. These are the symbols that ld
-   reported as undefined. But that was a long time ago, probably circa
-   Version 6.)
-
-  4.E.1. Global Variables
-
-   char *DELCMD;
-          Pointer to string containing command for deleting files.
-          Example: char *DELCMD = "rm -f "; (UNIX)
-          Example: char *DELCMD = "delete "; (VMS)
-          Note trailing space. Filename is concatenated to end of this
-          string. NOTE: DELCMD is used only in versions that do not
-          provide their own built-in DELETE command.
-
-   char *DIRCMD;
-          Pointer to string containing command for listing files when a
-          filespec is given.
-          Example: char *DIRCMD = "/bin/ls -l "; (UNIX)
-          Example: char *DIRCMD = "directory "; (VMS)
-          Note trailing space. Filename is concatenated to end of this
-          string. NOTE: DIRCMD is used only in versions that do not
-          provide their own built-in DIRECTORY command.
-
-   char *DIRCM2;
-          Pointer to string containing command for listing files when a
-          filespec is not given. (currently not used, handled in another
-          way.)
-          Example: char *DIRCMD2 = "/bin/ls -ld *";
-          NOTE: DIRCMD2 is used only in versions that do not provide
-          their own built-in DIRECTORY command.
-
-   char *PWDCMD;
-          Pointer to string containing command to display current
-          directory.
-          Example: char *PWDCMD = "pwd ";
-          NOTE: PWDCMD is used only in versions that do not provide their
-          own built-in PWD command.
-
-   char *SPACMD;
-          Pointer to command to display free disk space in current
-          device/directory.
-          Example: char *SPACMD = "df .";
-          NOTE: SPACMD is used only in versions that do not provide their
-          own built-in SPACE command.
-
-   char *SPACM2;
-          Pointer to command to display free disk space in another
-          device/directory.
-          Example: char *SPACM2 = "df ";
-          Note trailing space. Device or directory name is added to this
-          string. NOTE: SPACMD2 is used only in versions that do not
-          provide their own built-in SPACE command.
-
-   char *TYPCMD;
-          Pointer to command for displaying the contents of a file.
-          Example: char *TYPCMD = "cat ";
-          Note trailing space. Device or directory name is added to this
-          string. NOTE: TYPCMD is used only in versions that do not
-          provide their own built-in TYPE command.
-
-   char *WHOCMD;
-          Pointer to command for displaying logged-in users.
-          Example: char *WHOCMD = "who ";
-          Note trailing space. Specific user name may be added to this
-          string.
-
-   int backgrd = 0;
-          Flag for whether program is running in foreground (0) or
-          background (nonzero). Background operation implies that screen
-          output should not be done and that all errors should be fatal.
-
-   int ckxech;
-          Flag for who is to echo console typein:
-          1: The program (system is not echoing).
-          0: The OS, front end, terminal, etc (not this program).
-
-   char *ckxsys;
-          Pointer to string that names the computer and operating system.
-          Example: char *ckxsys = " NeXT Mach 1.0";
-          Tells what computer system ckxv applies to. In UNIX Kermit,
-          this variable is also used to print the program herald, and in
-          the SHOW VERSION command.
-
-   char *ckxv;
-          Pointer to version/edit info of ck?tio.c module.
-          Example: char *ckxv = "UNIX Communications Support, 6.0.169, 6
-          Sep 96";
-          Used by SHOW VERSION command.
-
-   char *ckzsys;
-          Like ckxsys, but briefer.
-          Example: char *ckzsys = " 4.3 BSD";
-          Tells what platform ckzv applies to. Used by the SHOW VERSION
-          command.
-
-   char *ckzv;
-          Pointer to version/edit info of ck?fio.c module.
-          Example: char *ckzv = "UNIX File support, 6.0.113, 6 Sep 96";
-          Used by SHOW VERSION command.
-
-   int dfflow;
-          Default flow control. 0 = none, 1 = Xon/Xoff, ... (see FLO_xxx
-          symbols in ckcdeb.h)
-          Set by Group E module. Used by [103]ckcmai.c to initialize flow
-          control variable.
-
-   int dfloc;
-          Default location. 0 = remote, 1 = local. Set by Group E module.
-          Used by ckcmai.c to initialize local variable. Used in various
-          places in the user interface.
-
-   int dfprty;
-          Default parity. 0 = none, 'e' = even, 'o' = odd, 'm' = mark,
-          's' = space. Set by Group E module. Used by ckcmai.c to
-          initialize parity variable.
-
-   char *dftty;
-          Default communication device. Set by Group E module. Used in
-          many places. This variable should be initialized the the symbol
-          CTTNAM, which is defined in ckcdeb.h, e.g. as "/dev/tty" for
-          UNIX, "TT:" for VMS, etc. Example: char *dftty = CTTNAM;
-
-   char *mtchs[];
-          Array of string pointers to filenames that matched the most
-          recent wildcard match, i.e. the most recent call to zxpand().
-          Used (at least) by command parsing package for partial filename
-          completion.
-
-   int tilde_expand;
-          Flag for whether to attempt to expand leading tildes in
-          directory names (used in UNIX only, and then only when the
-          symbol DTILDE is defined.
-
-   int ttnproto;
-          The protocol being used to communicate over a network device.
-          Values are defined in ckcnet.h. Example: NP_TELNET is network
-          protocol "telnet".
-
-   int maxnam;
-          The maximum length for a filename, exclusive of any device or
-          directory information, in the format of the host operating
-          system.
-
-   int maxpath;
-          The maximum length for a fully specified filename, including
-          device designator, directory name, network node name, etc, in
-          the format of the host operating system, and including all
-          punctuation.
-
-   int ttyfd;
-          File descriptor of the communication device. -1 if there is no
-          open or usable connection, including when C-Kermit is in remote
-          mode. Since this is not implemented everywhere, references to
-          it are in #ifdef CK_TTYFD..#endif.
-
-   [ [104]Contents ] [ [105]C-Kermit ] [ [106]Kermit Home ]
-    ________________________________________________________________________
-
-  4.E.2. Functions
-
-   These are divided into three categories: file-related functions (B.1),
-   communication functions (B.2), and miscellaneous functions (B.3).
-
-    4.E.2.1. File-Related Functions
-
-   In most implementations, these are collected together into a module
-   called ck?fio.c, where ? = "u" ([107]ckutio.c for Unix), "v"
-   ([108]ckvtio.c for VMS), [109]etc. To be totally platform-independent,
-   C-Kermit maintains its own file numbers, and provides the functions
-   described in this section to deal with the files associated with them.
-   The file numbers are referred to symbolically, and are defined as
-   follows in ckcker.h:
-
-  #define ZCTERM      0           /* Console terminal */
-  #define ZSTDIO      1           /* Standard input/output */
-  #define ZIFILE      2           /* Current input file for SEND command */
-  #define ZOFILE      3           /* Current output file for RECEIVE command */
-  #define ZDFILE      4           /* Current debugging log file */
-  #define ZTFILE      5           /* Current transaction log file */
-  #define ZPFILE      6           /* Current packet log file */
-  #define ZSFILE      7           /* Current session log file */
-  #define ZSYSFN      8           /* Input from a system function (pipe) */
-  #define ZRFILE      9           /* Local file for READ command */  (NEW)
-  #define ZWFILE     10           /* Local file for WRITE command */ (NEW)
-  #define ZMFILE     11           /* Auxilliary file for internal use */ (NEW)
-  #define ZNFILS     12           /* How many defined file numbers */
-
-   In the descriptions below, fn refers to a filename, and n refers to
-   one of these file numbers. Functions are of type int unless otherwise
-   noted, and are listed mostly alphabetically.
-
-   int
-          chkfn(n) int n;
-          Checks the file number n. Returns:
-           -1: File number n is out of range
-            0: n is in range, but file is not open
-            1: n in range and file is open
-
-   int
-          iswild(filspec) char *filespec;
-          Checks if the file specification is "wild", i.e. contains
-          metacharacters or other notations intended to match multiple
-          filenames. Returns:
-            0: not wild
-            1: wild.
-
-   int
-          isdir(string) char *string;
-          Checks if the string is the name of an existing directory. The
-          idea is to check whether the string can be "cd'd" to, so in
-          some cases (e.g. DOS) it might also indicate any file
-          structured device, such as a disk drive (like A:). Other
-          nonzero returns indicate system-dependent information; e.g. in
-          VMS isdir("[.FOO]") returns 1 but isdir("FOO.DIR;1") returns 2
-          to indicate the directory-file name is in a format that needs
-          conversion before it can be combined with a filename. Returns:
-            0: not a directory (including any kind of error)
-            1: it is an existing directory
-
-   char *
-          zfcdat(name) char *name;
-          Returns modification (preferably, otherwise creation) date/time
-          of file whose name is given in the argument string. Return
-          value is a pointer to a string of the form yyyymmdd hh:mm:ss,
-          for example 19931231 23:59:59, which represents the local time
-          (no timezone or daylight savings time finagling required).
-          Returns the null string ("") on failure. The text pointed to by
-          the string pointer might be in a static buffer, and so should
-          be copied to a safe place by the caller before any subsequent
-          calls to this function.
-
-   struct zfnfp *
-          zfnqfp(fn, buflen, buf) char * fn; int buflen; char * buf;
-          Given the filename fn, the corresponding fully qualified,
-          absolute filename is placed into the buffer buf, whose length
-          is buflen. On failure returns a NULL pointer. On success
-          returns a pointer to a struct zfnfp containing pointers to the
-          full pathname and to just the filename, and an int giving the
-          length of the full pathname. All references to this function in
-          mainline code must be protected by #ifdef ZFNQFP..#endif,
-          because it is not present in all of the ck*fio.c modules. So if
-          you implement this function in a version that did not have it
-          before, be sure to add #define ZFNQFP in the appropriate spot
-          in ckcdeb.h or in the build-procedure CFLAGS.
-
-   int
-          zcmpfn(s1,s2) char * s2, * s2;
-          Compares two filenames to see if they refer to the same.
-          Internally, the arguments can be converted to fully qualified
-          pathnames, e.g. with zfnqfp(), realpath(), or somesuch. In Unix
-          or other systems where symbolic links exist, the link should be
-          resolved before making the comparison or looking at the inodes.
-          Returns:
-            0: Files are not identical.
-            1: Files are identical.
-
-   int
-          zfseek(pos) long pos;
-          Positions the input pointer on the current input file to the
-          given position. The pos argument is 0-based, the offset
-          (distance in bytes) from beginning of the file. Needed for
-          RESEND, PSEND, and other recovery operations. This function is
-          not necessarily possible on all systems, e.g. record-oriented
-          systems. It should only be used on binary files (i.e. files we
-          are sending in binary mode) and stream-oriented file systems.
-          Returns:
-           -1: on failure.
-            0: On success.
-
-   int
-          zchdir(dirnam) char *dirnam;
-          Changes current or default directory to the one given in
-          dirnam. Returns:
-            0: On failure.
-            1: on success.
-
-   long
-          zchki(fn) char *fn;
-          Check to see if file with name fn is a regular, readable,
-          existing file, suitable for Kermit to send -- not a directory,
-          not a symbolic link, etc. Returns:
-           -3: if file exists but is not accessible (e.g.
-          read-protected);
-           -2: if file exists but is not of a readable type (e.g. a
-          directory);
-           -1: on error (e.g. file does not exist, or fn is garbage);
-          >=0: (length of file) if file exists and is readable.
-          Also see isdir(), zgetfs().
-
-   int
-          zchkpid(pid) unsigned long pid;
-          Returns:
-            1: If the given process ID (e.g. pid in UNIX) is valid and
-          active
-            0: otherwise.
-
-   long
-          zgetfs(fn) char *fn;
-          Gets the size of the given file, regardless of accessibility.
-          Used for directory listings. Unlike zchki(), should return the
-          size of any kind of file, even a directory. zgetfs() also
-          should serve as a mini "get file info" function that can be
-          used until we design a better one, by also setting some global
-          variables:
-            int zgfs_link   = 1/0 = file is (not) a symbolic link.
-            int zgfs_dir    = 1/0 = file is (not) a directory.
-            char linkname[] = if zgfs_link != 0, name of file link points
-          to.
-          Returns:
-           -1: on error (e.g. file does not exist, or fn is garbage);
-          >=0: (length of file) if file exists and is readable.
-
-   int
-          zchko(fn) char *fn;
-          Checks to see if a file of the given name can be created.
-          Returns:
-           -1: if file cannot be created, or on any kind of error.
-            0: if file can be created.
-
-   int
-          zchkspa(fn,len) char *f; long len;
-          Checks to see if there is sufficient space to store the file
-          named fn, which is len bytes long. If you can't write a
-          function to do this, then just make a dummy that always returns
-          1; higher level code will recover from disk-full errors. The
-          receiving Kermit uses this function to refuse an incoming file
-          based on its size, via the attribute mechanism. Returns:
-           -1: on error.
-            0: if there is not enough space.
-            1: if there is enough space.
-
-   int
-          zchin(n,c) int n; int *c;
-          Gets a character from file number n, return it in c (call with
-          &c). Returns:
-           -1: on failure, including EOF.
-            0: on success with character in c.
-
-   int
-          zchout(n,c) int n; char c;
-          Writes the character c to file number n. Returns:
-           -1: on error.
-            0: on success.
-
-   int
-          zclose(n) int n;
-          Closes file number n. Returns:
-           -1: on error.
-            1: on success.
-
-   int
-          zdelet(fn) char *name;
-          Attempts to delete (remove, erase) the named file. Returns:
-           -1: on error.
-            1: if file was deleted successfully.
-
-   char *
-          zgperm(char * f)
-          Returns a pointer to the system-dependent numeric
-          permissions/protection string for file f, or NULL upon failure.
-          Used if CK_PERMS is defined.
-
-   char *
-          ziperm(char * f)
-          Returns a pointer to the system-dependent symbolic
-          permissions/protection string for file f, or NULL upon failure.
-          Used if CK_PERMS is defined. Example: In UNIX zgperm(f) might
-          return "100770", but ziperm() might return "-rwxrwx---". In
-          VMS, zgperm() would return a hexadecimal string, but ziperm()
-          would return something like "(RWED,RWED,RE,)".
-
-   char *
-          zgtdir()
-          Returns a pointer to the name of the current directory, folder,
-          etc, or a NULL pointer if the current directory cannot be
-          determined. If possible, the directory specification should be
-          (a) fully specified, e.g. as a complete pathname, and (b) be
-          suitable for appending a filename. Thus, for example, Unix
-          directory names should end with '/'. VMS directory names should
-          look like DEV:[NAME] (rather than, say, NAME.DIR;1).
-
-   char *
-          zhome()
-          Returns a pointer to a string containing the user's home
-          directory, or NULL upon error. Should be formatted like
-          zgtdir() (q.v.).
-
-   int
-          zinfill()
-          Fill buffer from input file. This function is used by the macro
-          zminchar(), which is defined in ckcker.h. zminchar() manages
-          its own buffer, and calls zinfill() to fill it whenever it
-          becomes empty. It is used only for sending files, and reads
-          characters only from file number ZIFILE. zinfill() returns -1
-          upon end of file, -2 upon fatal error, and -3 upon timeout
-          (e.g. when reading from a pipe); otherwise it returns the first
-          character from the buffer it just read.
-
-   int
-          zkself()
-          Kills the current job, session, process, etc, logs out,
-          disappears. Used by the Kermit server when it receives a BYE
-          command. On failure, returns -1. On success, does not return at
-          all! This function should not be called until all other steps
-          have been taken to close files, etc.
-
-   VOID
-          zstrip(fn,&fn2) char *fn1, **fn2;
-          Strips device and directory, etc, from file specification fn,
-          leaving only the filename (including "extension" or "filetype"
-          -- the part after the dot). For example DUA0:[PROGRAMS]OOFA.C;3
-          becomes OOFA.C, or /usr/fdc/oofa.c becomes oofa.c. Returns a
-          pointer to result in fn2.
-
-   int
-          zsetperm(char * file, unsigned int code)
-          Set permissions of file to given system-dependent code.   0: On
-          failure.
-            1: on success.
-
-   int
-          zsetroot(char * dir)
-          Sets the root for the user's file access, like Unix chroot(),
-          but does not require privilege. In Unix, this must be
-          implemented entirely by Kermit's own file access routines.
-          Returns:
-            1: Success
-           -1: Invalid argument
-           -2:
-           -3: Internal error
-           -4: Access to given directory denied
-           -5: New root not within old root
-
-   int
-          zinroot(char * file)
-          If no root is set (zsetroot()), returns 1.
-          Otherwise, if given file is in the root, returns 1.
-          Otherwise, returns 0.
-
-   VOID
-          zltor(fn,fn2) char *fn1, *fn2;
-          Local-To-Remote filename translation. OBSOLETE: replaced by
-          nzltor() (q.v.). Translates the local filename fn into a format
-          suitable for transmission to an arbitrary type of computer, and
-          copies the result into the buffer pointed to by fn2.
-          Translation may involve (a) stripping the device and/or
-          directory/path name, (b) converting lowercase to uppercase, (c)
-          removing spaces and strange characters, or converting them to
-          some innocuous alphabetic character like X, (d) discarding or
-          converting extra periods (there should not be more than one).
-          Does its best. Returns no value. name2 is a pointer to a
-          buffer, furnished by the caller, into which zltor() writes the
-          resulting name. No length checking is done.
-
-   #ifdef NZLTOR
-          VOID
-          nzltor(fn,fn2,convert,pathnames,max) char *fn1,*fn2; int
-          convert,pathnames,max;
-          Replaces zltor(). This new version handles pathnames and checks
-          length. fn1 and fn2 are as in zltor(). This version is called
-          unconditionally for each file, rather than only when filename
-          conversion is enabled. Pathnames can have the following values:
-
-            PATH_OFF: Pathname, if any, is to be stripped
-            PATH_REL: The relative pathname is to be included
-            PATH_ABS: The full pathname is to be included
-
-          After handling pathnames, conversion is done to the result as
-          in the zltor() description if convert != 0; if relative or
-          absolute pathnames are included, they are converted to UNIX
-          format, i.e. with slash (/) as the directory separator. The max
-          parameter specifies the maximum size of fn2. If convert > 0,
-          the regular conversions are done; if convert < 0, minimal
-          conversions are done (we skip uppercasing the letters, we allow
-          more than one period, etc; this can be used when we know our
-          partner is UNIX or similar).
-
-   #endif /* NZLTOR */
-
-   int
-          nzxpand(fn,flags) char *fn; int flags;
-          Replaces zxpand(), which is obsolete as of C-Kermit 7.0.
-          Call with:
-            fn = Pointer to filename or pattern.
-            flags = option bits:
-              flags & ZX_FILONLY  Match regular files
-              flags & ZX_DIRONLY  Match directories
-              flags & ZX_RECURSE  Descend through directory tree
-              flags & ZX_MATCHDOT Match "dot files"
-              flags & ZX_NOBACKUP Don't match "backup files"
-              flags & ZX_NOLINKS  Don't follow symlinks.
-
-          Returns the number of files that match fn, with data structures
-          set up so the first file (if any) will be returned by the next
-          znext() call. If ZX_FILONLY and ZX_DIRONLY are both set, or
-          neither one is set, files and directories are matched. Notes:
-
-         1. It is essential that the number returned by nzxpand() reflect
-            the actual number of filenames that will be returned by
-            znext() calls. In other words:
-  for (n = nzxpand(string,flags); n > 0; n--) {
-      znext(buf);
-      printf("%s\n", buf);
-  }
-            should print all the file names; no more, no less.
-         2. In UNIX, DOS, OS-9, etc, where directories contain entries
-            for themselves (.) and the superior directory (..), these
-            should NOT be included in the list under any circumstances,
-            including when ZX_MATCHDOT is set.
-         3. Additional option bits might be added in the future, e.g. for
-            sorting (sort by date/name/size, reverse/ascending, etc).
-            Currently this is done only in higher level code (through a
-            hack in which the nzxpand() exports its filename array, which
-            is not portable because not all OS's can use this mechanism).
-
-   int
-          zmail(addr,fn) char *addr, fn;
-          Send the local, existing file fn as e-mail to the address addr.
-          Returns:
-            0: on success
-            2: if mail delivered but temp file can't be deleted
-           -2: if mail can't be delivered
-
-   int
-          zmkdir(path) char *path;
-          The path can be a file specification that might contain
-          directory information, in which the filename is expected to be
-          included, or an unambiguous directory specification (e.g. in
-          UNIX it must end with "/"). This routine attempts to create any
-          directories in the given path that don't already exist. Returns
-          0 or greater success: no directories needed creation, or else
-          all directories that needed creation were created successfully;
-          the return code is the number of directories that were created.
-          Returns -1 on failure to create any of the needed directories.
-
-   int
-          zrmdir(path) char *path;
-          Attempts to remove the given directory. Returns 0 on success,
-          -1 on failure. The detailed semantics are open -- should it
-          fail if the directory contains any files or subdirectories,
-          etc. It is probably best for this routine to behave in whatever
-          manner is customary on the underlying platform; e.g. in UNIX,
-          VMS, DOS, etc, where directories can not be removed unless they
-          are empty.
-
-   VOID
-          znewn(fn,s) char *fn, **s;
-          Transforms the name fn into a filename that is guaranteed to be
-          unique. If the file fn does not exist, then the new name is the
-          same as fn; Otherwise, it's different. this function does its
-          best, returns no value. New name is created in caller's space.
-          Call like this: znewn(old,&new);. The second parameter is a
-          pointer to the new name. This pointer is set by znewn() to
-          point to a static string in its own space, so be sure to the
-          result to a safe place before calling this function again.
-
-   int
-          znext(fn) char *fn;
-          Copies the next file name from a file list created by zxpand()
-          into the string pointed to by fn (see zxpand). If no more
-          files, then the null string is placed there. Returns 0 if there
-          are no more filenames, with 0th element the array pointed to by
-          fn set to NUL. If there is a filename, it is stored in the
-          array pointed to by fn and a positive number is returned. NOTE:
-          This is a change from earlier definitions of this function
-          (pre-1999), which returned the number of files remaining; thus
-          0 was the return value when returning the final file. However,
-          no mainline code ever depended on the return value, so this
-          change should be safe.
-
-   int
-          zopeni(n,fn) int n; char *fn;
-          Opens the file named fn for input as file number n. Returns:
-            0: on failure.
-            1: on success.
-
-   int
-          zopeno(n,fn,zz,fcb) int n; char *name; struct zattr *zz; struct
-          filinfo *fcb;
-          Attempts to open the named file for output as file number n. zz
-          is a Kermit file attribute structure as defined in ckcdeb.h,
-          containing various information about the file, including its
-          size, creation date, and so forth. This function should attempt
-          to honor as many of these as possible. fcb is a "file control
-          block" in the traditional sense, defined in ckcdeb.h,
-          containing information relevant to complicated file systems
-          like VMS (RMS), IBM MVS, etc, like blocksize, record length,
-          organization, record format, carriage control, etc. Returns:
-            0: on failure.
-            1: on success.
-
-   int
-          zoutdump()
-          Dumps a file output buffer. Used with the macro zmchout()
-          defined in ckcker.h. Used only with file number ZOFILE, i.e.
-          the file that is being received by Kermit during file transfer.
-          Returns:
-           -1: on failure.
-            0: on success.
-
-   int
-          zprint(p,fn) char *p, *f;
-          Prints the file with name fn on a local printer, with options
-          p. Returns:
-            0: on success
-            3: if file sent to printer but can't be deleted
-           -3: if file can't be printed
-
-   int
-          zrename(fn,fn2) char *fn, *fn2;
-          Changes the name of file fn to fn2. If fn2 is the name of an
-          existing directory, or a file-structured device, then file fn
-          is moved to that directory or device, keeping its original
-          name. If fn2 lacks a directory separator when passed to this
-          function, an appropriate one is supplied. Returns:
-           -1: on failure.
-            0: on success.
-
-   int
-          zcopy(source,dest) char * source, * dest;
-          Copies the source file to the destination. One file only. No
-          wildcards. The destination string may be a filename or a
-          directory name. Returns:
-            0: on success.
-           <0: on failure:
-            -2: source file is not a regular file.
-            -3: source file not found.
-            -4: permission denied.
-            -5: source and destination are the same file.
-            -6: i/o error.
-            -1: other error.
-
-   char *
-          zlocaltime(char *)
-          Call with: "yyyymmdd hh:mm:ss" GMT/UTC date-time. Returns
-          pointer to local date-time string "yyyymmdd hh:mm:ss" on
-          success, NULL on failure.
-
-   VOID
-          zrtol(fn,fn2) char *fn, *fn2;
-          Remote-To-Local filename translation. OBSOLETE: replaced by
-          nzrtol(). Translates a "standard" filename to a local filename.
-          For example, in Unix this function might convert an
-          all-uppercase name to lowercase, but leave lower- or mix-case
-          names alone. Does its best, returns no value. New name is in
-          string pointed to by fn2. No length checking is done.
-
-   #ifdef NZLTOR
-   int
-          nzrtol(fn,fn2,convert,pathnames,max) char *fn1,*fn2; int
-          convert,pathnames,max;
-          Replaces zrtol. Like zrtol but handles pathnames and checks
-          length. See nzltor for detailed description of parameters.
-
-   #endif /* NZLTOR */
-
-   int
-          zsattr(xx) struct zattr *xx;
-          Fills in a Kermit file attribute structure for the file which
-          is to be sent, namely the currently open ZIFILE. Note that this
-          is not a very good design, but we're stuck with it. Callers
-          must ensure that zsattr() is called only on real files, not on
-          pipes, internally generated file-like objects such as server
-          REMOTE command responses, etc. Returns:
-           -1: on failure.
-            0: on success with the structure filled in.
-          If any string member is null, it should be ignored by the
-          caller.
-          If any numeric member is -1, it should be ignored by the
-          caller.
-
-   int
-          zshcmd(s) char *s;
-          s contains to pointer to a command to be executed by the host
-          computer's shell, command parser, or operating system. If the
-          system allows the user to choose from a variety of command
-          processors (shells), then this function should employ the
-          user's preferred shell. If possible, the user's job
-          (environment, process, etc) should be set up to catch keyboard
-          interruption signals to allow the user to halt the system
-          command and return to Kermit. The command must run in ordinary,
-          unprivileged user mode. If possible, this function should
-          return -1 on failure to start the command, or else it should
-          return 1 if the command succeeded and 0 if it failed.
-
-   int
-          pexitstatus
-          zshcmd() and zsyscmd() should set this to the command's actual
-          exit status code if possible.
-
-   int
-          zsyscmd(s) char *s;
-          s contains to pointer to a command to be executed by the host
-          computer's shell, command parser, or operating system. If the
-          system allows the user to choose from a variety of command
-          processors (shells), then this function should employ the
-          system standard shell (e.g. /bin/sh for Unix), so that the
-          results will always be the same for everybody. If possible, the
-          user's job (environment, process, etc) should be set up to
-          catch keyboard interruption signals to allow the user to halt
-          the system command and return to Kermit. The command must run
-          in ordinary, unprivileged user mode. If possible, this function
-          should return -1 on failure to start the command, or else it
-          should return 1 if the command succeeded and 0 if it failed.
-
-   VOID
-          z_exec(s,args) char * s; char * args[];
-          This one executes the command s (which is searched for using
-          the system's normal searching mechanism, such as PATH in UNIX),
-          with the given argument vector, which follows the conventions
-          of UNIX argv[]: the name of the command pointed to by element
-          0, the first arg by element 1, and so on. A null args[] pointer
-          indicates the end of the arugment list. All open files must
-          remain open so the exec'd process can use them. Returns only if
-          unsuccessful.
-
-   int
-          zsinl(n,s,x) int n, x; char *s;
-          Reads a line from file number n. Writes the line into the
-          address s provided by the caller. Writing terminates when
-          newline is read, but with newline discarded. Writing also
-          terminates upon EOF or if length x is exhausted. Returns:
-           -1: on EOF or error.
-            0: on success.
-
-   int
-          zsout(n,s) int n; char *s;
-          Writes the string s out to file number n. Returns:
-           -1: on failure.
-            0: on success.
-
-   int
-          zsoutl(n,s) int n; char *s;
-          Writes the string s out to file number n and adds a line
-          (record) terminator (boundary) appropriate for the system and
-          the file format. Returns:
-           -1: on failure.
-            0: on success.
-
-   int
-          zsoutx(n,s,x) int n, x; char *s;
-          Writes exactly x characters from string s to file number n. If
-          s has fewer than x characters, then the entire string s is
-          written. Returns:
-           -1: on failure.
-          >= 0: on success, the number of characters actually written.
-
-   int
-          zstime(fn,yy,x) char *fn; struct zattr *yy; int x;
-          Sets the creation date (and other attributes) of an existing
-          file, or compares a file's creation date with a given date.
-          Call with:
-
-   fn: pointer to name of existing file.
-   yy: Pointer to a Kermit file attribute structure in which yy->date.val
-   is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00, which
-   is to be used for setting or comparing the file date. Other attributes
-   in the struct can also be set, such as the protection/permission (See
-   [110]Appendix I), when it makes sense (e.g. "yy->lprotect.val" can be
-   set if the remote system ID matches the local one).
-    x: A function code: 0 means to set the file's creation date as given.
-   1 means compare the date from the yy struct with the file's date.
-
-          Returns:
-           -1: on any kind of error.
-            0: if x is 0 and the file date was set successfully.
-            0: if x is 1 and date from attribute structure > file
-          creation date.
-            1: if x is 1 and date from attribute structure <= file
-          creation date.
-
-   VOID
-          zstrip(name,name2) char *name, **name2;
-          Strips pathname from filename "name". Constructs the resulting
-          string in a static buffer in its own space and returns a
-          pointer to it in name2. Also strips device name, file version
-          numbers, and other "non-name" material.
-
-   int
-          zxcmd(n,s) char *s;
-          Runs a system command so its output can be accessed as if it
-          were file n. The command is run in ordinary, unprivileged user
-          mode.
-          If n is ZSTDIO or ZCTERM, returns -1.
-          If n is ZIFILE or ZRFILE, then Kermit reads from the command,
-          otherwise Kermit writes to the command.
-          Returns 0 on error, 1 on success.
-
-   int
-          zxpand(fn) char *fn;
-          OBSOLETE: Replaced by nzxpand(), q.v.
-
-   #ifdef ZXREWIND
-   int
-          zxrewind()
-          Returns the number of files returned by the most recent
-          nzxpand() call, and resets the list to the beginning so the
-          next znext() call returns the first file. Returns -1 if zxpand
-          has not yet been called. If this function is available,
-          ZXREWIND should be defined; otherwise it should not be
-          referenced.
-
-   #endif /* ZXREWIND */
-
-   int
-          xsystem(cmd) char *cmd;
-          Executes the system command without redirecting any of its i/o,
-          similar (well, identical) to system() in Unix. But before
-          passing the command to the system, xsystem() ensures that all
-          privileges are turned off, so that the system command executes
-          in ordinary unprivileged user mode. If possible, xsystem()
-          returns the return code of the command that was executed.
-
-    4.E.2.2. IKSD Variables and Functions
-
-   These must be implemented in any C-Kermit version that is to be
-   installed as an Internet Kermit Service Daemon (IKSD). IKSD is
-   expected to be started by the Internet Daemon (e.g. inetd) with its
-   standard i/o redirected to the incoming connection.
-
-   int ckxanon;
-          Nonzero if anonymous logins allowed.
-
-   extern int inserver;
-          Nonzero if started in IKSD mode.
-
-   extern int isguest;
-          Nonzero if IKSD and user logged in anonymously.
-
-   extern char * homdir;
-          Pointer to user's home directory.
-
-   extern char * anonroot;
-          Pointer to file-system root for anonymous users.
-
-   Existing functions must make "if (inserver && isguest)" checks for
-   actions that would not be legal for guests: zdelete(), zrmdir(),
-   zprint(), zmail(), etc.
-
-   int
-          zvuser(name) char * name;
-          Verifies that user "name" exists and is allowed to log in. If
-          the name is "ftp" or "anonymous" and ckxanon != 0, a guest
-          login is set up. Returns 0 if user not allowed to log in,
-          nonzero if user may log in.
-
-   int
-          zvpass(string) char * string;
-          Verifies password of the user from the most recent zvuser()
-          call. Returns nonzero if password is valid for user, 0 if it
-          isn't. Makes any appropriate system log entries (IKSD logins,
-          failed login attempts, etc). If password is valid, logs the
-          user in as herself (if real user), or sets up restricted
-          anonymous access if user is guest (e.g. changes file-system
-          root to anonroot and sets isguest = 1).
-
-   VOID
-          zsyslog()
-          Begins any desired system logging of an IKSD session.
-
-   VOID
-          zvlogout()
-          Terminates an IKSD session. In most cases this is simply a
-          wrapper for exit() or doexit(), with some system logging added.
-
-    4.E.2.3. Privilege Functions
-
-   These functions are used by C-Kermit to adapt itself to operating
-   systems where the program can be made to run in a "privileged" mode,
-   e.g. setuid or setgid in Unix. C-Kermit should NOT read and write
-   files or start subprocesses as a privileged program. This would
-   present a serious threat to system security. The security package has
-   been installed to prevent such security breaches by turning off the
-   program's special privileges at all times except when they are needed.
-
-   In UNIX, the only need Kermit has for privileged status is access to
-   the UUCP lockfile directory, in order to read, create, and destroy
-   lockfiles, and to open communication devices that are normally
-   protected against the user (see the [111]Unix C-Kermit Installation
-   Instructions for discussion). Therefore, privileges should only be
-   enabled for these operations and disabled at all other times. This
-   relieves the programmer of the responsibility of putting expensive and
-   unreliable access checks around every file access and subprocess
-   creation.
-
-   Strictly speaking, these functions are not required in all C-Kermit
-   implementations, because their use (so far, at least) is internal to
-   the Group E modules. However, they should be included in all C-Kermit
-   implementations for operating systems that support the notion of a
-   privileged program (UNIX, RSTS/E, what others?).
-
-   int
-          priv_ini()
-          Determine whether the program is running in privileged status.
-          If so, turn off the privileges, in such a way that they can be
-          turned on again when needed. Called from sysinit() at program
-          startup time. Returns:
-            0 on success
-            nonzero on failure, in which case the program should halt
-          immediately.
-
-   int
-          priv_on()
-          If the program is not privileged, this function does nothing.
-          If the program is privileged, this function returns it to
-          privileged status. priv_ini() must have been called first.
-          Returns:
-            0 on success
-            nonzero on failure
-
-   int
-          priv_off()
-          Turns privileges off (if they are on) in such a way that they
-          can be turned back on again. Returns:
-            0 on success
-            nonzero on failure
-
-   int
-          priv_can()
-          Turns privileges off in such a way that they cannot be turned
-          back on. Returns:
-            0 on success
-            nonzero on failure
-
-   int
-          priv_chk()
-          Attempts to turns privileges off in such a way that they can be
-          turned on again later. Then checks to make sure that they were
-          really turned off. If they were not really turned off, then
-          they are cancelled permanently. Returns:
-            0 on success
-            nonzero on failure
-
-    4.E.2.4. Console-Related Functions
-
-   These relate to the program's "console", or controlling terminal, i.e.
-   the terminal that the user is logged in on and types commands at, or
-   on a PC or workstation, the actual keyboard and screen.
-
-   int
-          conbin(esc) char esc;
-          Puts the console into "binary" mode, so that Kermit's command
-          parser can control echoing and other treatment of characters
-          that the user types. esc is the character that will be used to
-          get Kermit's attention during packet mode; puts this in a
-          global place. Sets the ckxech variable. Returns:
-           -1: on error.
-            0: on success.
-
-   int
-          concb(esc) char esc;
-          Put console in "cbreak" (single-character wakeup) mode. That
-          is, ensure that each console character is available to the
-          program immediately when the user types it. Otherwise just like
-          conbin(). Returns:
-           -1: on error.
-            0: on success.
-
-   int
-          conchk()
-          Returns a number, 0 or greater, the number of characters
-          waiting to be read from the console, i.e. the number of
-          characters that the user has typed that have not been read yet
-          by Kermit.
-
-   long
-          congspd();
-          Returns the speed ("baud rate") of the controlling terminal, if
-          known, otherwise -1L.
-
-   int
-          congks(timo) int timo;
-          Get Keyboard Scancode. Reads a keyboard scan code from the
-          physical console keyboard. If the timo parameter is greater
-          than zero, then times out and returns -2 if no character
-          appears within the given number of seconds. Upon any other kind
-          of error, returns -1. Upon success returns a scan code, which
-          may be any positive integer. For situations where scan codes
-          cannot be read (for example, when an ASCII terminal is used as
-          the job's controlling terminal), this function is identical to
-          coninc(), i.e. it returns an 8-bit character value. congks() is
-          for use with workstations whose keyboards have Alternate,
-          Command, Option, and similar modifier keys, and Function keys
-          that generate codes greater than 255.
-
-   int
-          congm()
-          Console get modes. Gets the current console terminal modes and
-          saves them so that conres() can restore them later. Returns 1
-          if it got the modes OK, 0 if it did nothing (e.g. because
-          Kermit is not connected with any terminal), -1 on error.
-
-   int
-          coninc(timo) int timo;
-          Console Input Character. Reads a character from the console. If
-          the timo parameter is greater than zero, then coninc() times
-          out and returns -2 if no character appears within the given
-          number of seconds. Upon any other kind of error, returns -1.
-          Upon success, returns the character itself, with a value in the
-          range 0-255 decimal.
-
-   VOID
-          conint(f,s) SIGTYP (*f)(), (*s)();
-          Sets the console to generate an interrupt if the user types a
-          keyboard interrupt character, and to transfer control the
-          signal-handling function f. For systems with job control, s is
-          the address of the function that suspends the job. Sets the
-          global variable "backgrd" to zero if Kermit is running in the
-          foreground, and to nonzero if Kermit is running in the
-          background. See ckcdeb.h for the definition of SIGTYP. No
-          return value.
-
-   VOID
-          connoi()
-          Console no interrupts. Disable keyboard interrupts on the
-          console. No return value.
-
-   int
-          conoc(c) char c;
-          Writes character c to the console terminal. Returns:
-          0 on failure, 1 on success.
-
-   int
-          conol(s) char *s;
-          Writes string s to the console. Returns -1 on error, 0 or
-          greater on success.
-
-   int
-          conola(s) char *s[]; {
-          Writes an array of strings to the console. Returns -1 on error,
-          0 or greater on success.
-
-   int
-          conoll(s) char *s;
-          Writes string s to the console, followed by the necessary line
-          termination characters to put the console cursor at the
-          beginning of the next line. Returns -1 on error, 0 or greater
-          on success.
-
-   int
-          conres()
-          Restores the console terminal to the modes obtained by congm().
-          Returns: -1 on error, 0 on success.
-
-   int
-          conxo(x,s) int x; char *s;
-          Write x characters from string s to the console. Returns 0 or
-          greater on success, -1 on error.
-
-   char *
-          conkbg();
-          Returns a pointer to the designator of the console keyboard
-          type. For example, on a PC, this function would return "88",
-          "101", etc. Upon failure, returns a pointer to the empty
-          string.
-
-    4.E.2.5. Communications Functions
-
-   The communication device is the device used for terminal emulation and
-   file transfer. It may or may not be the same device as the console,
-   and it may or may not be a terminal (serial-port) device; it could
-   also be a network connection. For brevity, the communication device is
-   referred to here as the "tty". When the communication device is the
-   same as the console device, Kermit is said to be in remote mode. When
-   the two devices are different, Kermit is in local mode.
-
-   int
-          ttchk()
-          Returns the number of characters that have arrived at the
-          communication device but have not yet been read by ttinc(),
-          ttinl(), and friends. If communication input is buffered (and
-          it should be), this is the sum of the number of unread
-          characters in Kermit's buffer PLUS the number of unread
-          characters in the operating system's internal buffer. The call
-          must be nondestructive and nonblocking, and as inexpensive as
-          possible. Returns:
-            0: or greater on success,
-            0: in case of internal error,
-           -1: or less when it determines the connection has been broken,
-          or there is no connection.
-
-          That is, a negative return from ttchk() should reliably
-          indicate that there is no usable connection. Furthermore,
-          ttchk() should be callable at any time to see if the connection
-          is open. When the connection is open, every effort must be made
-          to ensure that ttchk returns an accurate number of characters
-          waiting to be read, rather than just 0 (no characters) or 1 (1
-          or more characters), as would be the case when we use select().
-          This aspect of ttchk's operation is critical to successful
-          operation of sliding windows and streaming, but "nondestructive
-          buffer peeking" is an obscure operating system feature, and so
-          when it is not available, we have to do it ourselves by
-          managing our own internal buffer at a level below ttinc(),
-          ttinl(), etc, as in the UNIX version (non-FIONREAD case).
-
-          An external global variable, clsondisc, if nonzero, means that
-          if a serial connection drops (carrier on-to-off transition
-          detected by ttchk()), the device should be closed and released
-          automatically.
-
-   int
-          ttclos()
-          Closes the communication device (tty or network). If there were
-          any kind of exclusive access locks connected with the tty,
-          these are released. If the tty has a modem connection, it is
-          hung up. For true tty devices, the original tty device modes
-          are restored. Returns:
-           -1: on failure.
-            0: on success.
-
-   int
-          ttflui()
-          Flush communications input buffer. If any characters have
-          arrived but have not yet been read, discard these characters.
-          If communications input is buffered by Kermit (and it should
-          be), this function flushes Kermit's buffer as well as the
-          operating system's internal input buffer. Returns:
-           -1: on failure.
-            0: on success.
-
-   int
-          ttfluo()
-          Flush tty output buffer. If any characters have been written
-          but not actually transmitted (e.g. because the system has been
-          flow-controlled), remove them from the system's output buffer.
-          (Note, this function is not actually used, but it is
-          recommended that all C-Kermit programmers add it for future
-          use, even if it is only a dummy function that returns 0
-          always.)
-
-   int
-          ttgmdm()
-          Looks for the modem signals CTS, DSR, and CTS, and returns
-          those that are on in as its return value, in a bit mask as
-          described for ttwmdm, in which a bit is on (1) or off (0)
-          according to whether the corresponding signal is on (asserted)
-          or off (not asserted). Return values:
-           -3: Not implemented
-           -2: if the line does not have modem control
-           -1: on error
-          >=0: on success, with bit mask containing the modem signals.
-
-   long
-          ttgspd()
-          Returns the current tty speed in BITS (not CHARACTERS) per
-          second, or -1 if it is not known or if the tty is really a
-          network, or upon any kind of error. On success, the speed
-          returned is the actual number of bits per second, like 1200,
-          9600, 19200, etc.
-
-   int
-          ttgwsiz()
-          Get terminal window size. Returns -1 on error, 0 if the window
-          size can't be obtained, 1 if the window size has been
-          successfully obtained. Upon success, the external global
-          variables tt_rows and tt_cols are set to the number of screen
-          rows and number of screen columns, respectively. As this
-          function is not implemented in all ck*tio.c modules, calls to
-          it must be wrapped in #ifdef CK_TTGWSIZ..#endif. NOTE: This
-          function must be available to use the TELNET NAWS feature
-          (Negotiate About Window Size) as well as Rlogin.
-
-   int
-          tthang()
-          Hang up the current tty device. For real tty devices, turn off
-          DTR for about 1/3-1/2 second (or other length of time,
-          depending on the system). If the tty is really a network
-          connection, close it. Returns:
-           -1: on failure.
-            0: if it does not even try to hang up.
-            1: if it believes it hung up successfully.
-
-   VOID
-          ttimoff()
-          Turns off all pending timer interrupts.
-
-   int
-          ttinc(timo) int timo; (function is old, return codes are new)
-          Reads one character from the communication device. If timo is
-          greater than zero, wait the given number of seconds and then
-          time out if no character arrives, otherwise wait forever for a
-          character. Returns:
-           -3: internal error (e.g. tty modes set wrong)
-           -2: communications disconnect
-           -1: timeout or other error
-          >=0: the character that was read.
-          It is HIGHLY RECOMMENDED that ttinc() be internally buffered so
-          that calls to it are relatively inexpensive. If it is possible
-          to to implement ttinc() as a macro, all the better, for example
-          something like:
-
-  #define ttinc(t) ( (--txbufn >= 0) ? txbuf[ttbufp++] : txbufr(t) )
-
-          (see description of txbufr() below)
-
-   int
-          ttinl(dest,max,timo,eol,start,turn) int max,timo,turn; CHAR
-          *dest, eol, start;
-          ttinl() is Kermit's packet reader. Reads a packet from the
-          communications device, or up to max characters, whichever
-          occurs first. A line is a string of characters starting with
-          the start character up to and including the character given in
-          eol or until the length is exhausted, or, if turn != 0, until
-          the line turnaround character (turn) is read. If turn is 0,
-          ttinl() *should* use the packet length field to detect the end,
-          to allow for the possibility that the eol character appears
-          unprefixed in the packet data. (The turnaround character is for
-          half-duplex linemode connections.)
-
-          If timo is greater than zero, ttinl() times out if the eol
-          character is not encountered within the given number of seconds
-          and returns -1.
-
-          The characters that were input are copied into "dest" with
-          their parity bits stripped if parity is not none. The first
-          character copied into dest should be the start character, and
-          the last should be the final character of the packet (the last
-          block check character). ttinl() should also absorb and discard
-          the eol and turn characters, and any other characters that are
-          waiting to be read, up until the next start character, so that
-          subsequent calls to ttchk() will not succeed simply because
-          there are some terminators still sitting in the buffer that
-          ttinl() didn't read. This operation, if performed, MUST NOT
-          BLOCK (so if it can't be performed in a guaranteed nonblocking
-          way, don't do it).
-
-          On success, ttinl() returns the number of characters read.
-          Optionally, ttinl() can sense the parity of incoming packets.
-          If it does this, then it should set the global variable ttprty
-          accordingly. ttinl() should be coded to be as efficient as
-          possible, since it is at the "inner loop" of packet reception.
-          ttinl() returns:
-           -1: Timeout or other possibly correctable error.
-           -2: Interrupted from keyboard.
-           -3: Uncorrectable i/o error -- connection lost, configuration
-          problem, etc.
-          >=0: on success, the number of characters that were actually
-          read and placed in the dest buffer, not counting the trailing
-          null.
-
-   int
-          ttoc(c) char c;
-          Outputs the character c to the communication line. If the
-          operation fails to complete within two seconds, this function
-          returns -1. Otherwise it returns the number of characters
-          actually written to the tty (0 or 1). This function should only
-          be used for interactive, character-mode operations, like
-          terminal connection, script execution, dialer i/o, where the
-          overhead of the signals and alarms does not create a
-          bottleneck. (THIS DESCRIPTION NEEDS IMPROVEMENT -- If the
-          operation fails within a "certain amount of time"... which
-          might be dependent on the communication method, speed, etc. In
-          particular, flow-control deadlocks must be accounted for and
-          broken out of to prevent the program from hanging indefinitely,
-          etc.)
-
-   int
-          ttol(s,n) int n; char *s;
-          Kermit's packet writer. Writes the n characters of the string
-          pointed to to by s. NOTE: It is ttol's responsibility to write
-          ALL of the characters, not just some of them. Returns:
-           -1: on a possibly correctable error (so it can be retried).
-           -3: on a fatal error, e.g. connection lost.
-          >=0: on success, the actual number of characters written (the
-          specific number is not actually used for anything).
-
-   int
-          ttopen(ttname,lcl,modem,timo) char *ttname; int *lcl, modem,
-          timo;
-          Opens a tty device, if it is not already open. ttopen must
-          check to make sure the SAME device is not already open; if it
-          is, ttopen returns successfully without doing anything. If a
-          DIFFERENT device is currently open, ttopen() must call ttclos()
-          to close it before opening the new one.
-
-        Parameters:
-
-              ttname:
-                      character string - device name or network host
-                      name.
-
-              lcl:
-                      If called with lcl < 0, sets value of lcl as
-                      follows:
-                      0: the terminal named by ttname is the job's
-                      controlling terminal.
-                      1: the terminal named by ttname is not the job's
-                      controlling terminal.
-                      If the device is already open, or if the requested
-                      device can't be opened, then lcl remains (and is
-                      returned as) -1.
-
-              modem:
-                      Less than zero: this is the negative of the network
-                      type, and ttname is a network host name. Network
-                      types (from [112]ckcnet.h:
-
-  NET_TCPB 1   TCP/IP Berkeley (socket)  (implemented in [113]ckutio.c)
-  NET_TCPA 2   TCP/IP AT&T (streams)     (not yet implemented)
-  NET_DEC  3   DECnet                    (not yet implemented)
-
-                      Zero or greater: ttname is a terminal device name.
-                      Zero means a direct connection (don't use modem
-                      signals). Positive means use modem signals
-                      depending on the current setting of ttcarr (see
-                      ttscarr()).
-
-              timo:
-                      > 0: number of seconds to wait for open() to return
-                      before timing out.
-                      <=0: no timer, wait forever (e.g. for incoming
-                      call).
-                      For real tty devices, ttopen() attempts to gain
-                      exclusive access to the tty device, for example in
-                      UNIX by creating a "lockfile" (in other operating
-                      systems, like VMS, exclusive access probably
-                      requires no special action).
-
-        Side effects:
-                Copies its arguments and the tty file descriptor to
-                global variables that are available to the other
-                tty-related functions, with the lcl value altered as
-                described above. Gets all parameters and settings
-                associated with the line and puts them in a global area,
-                so that they can be restored by ttres(), e.g. when the
-                device is closed.
-
-        Returns:
-                  0: on success
-                 -5: if device is in use
-                 -4: if access to device is denied
-                 -3: if access to lock mechanism denied
-                 -2: upon timeout waiting for device to open
-                 -1: on other error
-
-   int
-          ttpkt(speed,flow,parity) long speed; int flow, parity;
-          Puts the currently open tty device into the appropriate modes
-          for transmitting and receiving Kermit packets.
-
-        Arguments:
-
-              speed:
-                      if speed > -1, and the device is a true tty device,
-                      and Kermit is in local mode, ttpkt also sets the
-                      speed.
-
-              flow:
-                      if in the range 0-3, ttpkt selects the
-                      corresponding type of flow control. Currently 0 is
-                      defined as no flow control, 1 is Xon/Xoff, and no
-                      other types are defined. If (and this is a horrible
-                      hack, but it goes back many years and will be hard
-                      to eradicate) flow is 4, then the appropriate tty
-                      modes are set for modem dialing, a special case in
-                      which we talk to a modem-controlled line without
-                      requiring carrier. If flow is 5, then we require
-                      carrier.
-
-              parity:
-                      This is simply copied into a global variable so
-                      that other functions (like ttinl, ttinc, etc) can
-                      use it.
-
-        Side effects:
-                Copies its arguments to global variables, flushes the
-                terminal device input buffer.
-
-        Returns:
-                 -1: on error.
-                  0: on success.
-
-   int
-          ttsetflow(int)
-          Enables the given type of flow control on the open serial
-          communications device immediately. Arguments are the FLO_xxx
-          values from ckcdeb.h, except FLO_DIAL, FLO_DIAX, or FLO_AUTO,
-          which are not actual flow-control types. Returns 0 on success,
-          -1 on failure.
-
-   #ifdef TTSPDLIST
-   long *
-          ttspdlist()
-          Returns a pointer to an array of longs, or NULL on failure. On
-          success, element 0 of the array contains number, n, indicating
-          how many follow. Elements 1-n are serial speeds, expressed in
-          bits per second, that are legal on this platform. The user
-          interface may use this list to construct a menu, keyword table,
-          etc.
-
-   #endif /* TTSPDLIST */
-
-   int
-          ttres()
-          Restores the tty device to the modes and settings that were in
-          effect at the time it was opened (see ttopen). Returns:
-           -1: on error.
-            0: on success.
-
-   int
-          ttruncmd(string) char * string;
-          Runs the given command on the local system, but redirects its
-          input and output to the communication (SET LINE, SET PORT, or
-          SET HOST) device. Returns:
-            0: on failure.
-            1: on success.
-
-   int
-          ttscarr(carrier) int carrier;
-          Copies its argument to a variable that is global to the other
-          tty-related functions, and then returns it. The values for
-          carrier are defined in ckcdeb.h: CAR_ON, CAR_OFF, CAR_AUTO.
-          ttopen(), ttpkt(), and ttvt() use this variable when deciding
-          how to open the tty device and what modes to select. The
-          meanings are these:
-
-   CAR_OFF: Ignore carrier at all times.
-   CAR_ON: Require carrier at all times, except when dialing. This means,
-   for example, that ttopen() could hang forever waiting for carrier if
-   it is not present.
-   CAR_AUTO: If the modem type is zero (i.e. the connection is direct),
-   this is the same as CAR_OFF. If the modem type is positive, then heed
-   carrier during CONNECT (ttvt mode), but ignore it at other times
-   (packet mode, during SET LINE, etc). Compatible with pre-5A versions
-   of C-Kermit. This should be the default carrier mode.
-
-          Kermit's DIAL command ignores the carrier setting, but
-          ttopen(), ttvt(), and ttpkt() all honor the carrier option in
-          effect at the time they are called. None of this applies to
-          remote mode (the tty device is the job's controlling terminal)
-          or to network host connections (modem type is negative).
-
-   int
-          ttsndb()
-          Sends a BREAK signal on the tty device. On a real tty device,
-          send a real BREAK lasting approximately 275 milliseconds. If
-          this is not possible, simulate a BREAK by (for example)
-          dropping down some very low baud rate, like 50, and sending a
-          bunch of null characters. On a network connection, do the
-          appropriate network protocol for BREAK. Returns:
-           -1: on error.
-            0: on success.
-
-   int
-          ttsndlb()
-          Like ttsndb(), but sends a "Long BREAK" (approx 1.5 seconds).
-          For network connections, it is identical to ttsndb().
-          Currently, this function is used only if CK_LBRK is defined (as
-          it is for UNIX and VMS).
-
-   int
-          ttsspd(cps) int cps;
-          For serial devices only, set the device transmission speed to
-          (note carefully) TEN TIMES the argument. The argument is in
-          characters per second, but transmission speeds are in bits per
-          second. cps are used rather than bps because high speeds like
-          38400 are not expressible in a 16-bit int but longs cannot be
-          used because keyword-table values are ints and not longs. If
-          the argument is 7, then the bps is 75, not 70. If the argument
-          is 888, this is a special code for 75/1200 split-speed
-          operation (75 bps out, 1200 bps in). Returns:
-           -1: on error, meaning the requested speed is not valid or
-          available.
-          >=0: on success (don't try to use this value for anything).
-
-   int
-          ttvt(speed,flow) long speed; int flow;
-          Puts the currently open tty device into the appropriate modes
-          for terminal emulation. The arguments are interpreted as in
-          ttpkt(). Side effects: ttvt() stores its arguments in global
-          variables, and sets a flag that it has been called so that
-          subsequent calls can be ignored so long as the arguments are
-          the same as in the last effective call. Other functions, such
-          as ttopen(), ttclose(), ttres(), ttvt(), etc, that change the
-          tty device in any way must unset this flag. In UNIX Kermit,
-          this flag is called tvtflg.
-
-   int
-          ttwmdm(mdmsig,timo) int mdmsig, timo;
-          Waits up to timo seconds for all of the given modem signals to
-          appear. mdmsig is a bit mask, in which a bit is on (1) or off
-          (0) according to whether the corresponding signal is to be
-          waited for. These symbols are defined in ckcdeb.h:
-            BM_CTS (bit 0) means wait for Clear To Send
-            BM_DSR (bit 1) means wait for Data Set Ready
-            BM_DCD (bit 2) means wait for Carrier Detect
-          Returns:
-           -3: Not implemented.
-           -2: This line does not have modem control.
-           -1: Timeout: time limit exceeded before all signals were
-          detected.
-            1: Success.
-
-   int
-          ttxin(n,buf) int n; CHAR *buf;
-          Reads x characters from the tty device into the specified buf,
-          stripping parity if parity is not none. This call waits
-          forever, there is no timeout. This function is designed to be
-          called only when you know that at least x characters are
-          waiting to be read (as determined, for example, by ttchk()).
-          This function should use the same buffer as ttinc().
-
-   int
-          txbufr(timo) int timo;
-          Reads characters into the internal communications input buffer.
-          timo is a timeout interval, in seconds. 0 means no timeout,
-          wait forever. Called by ttinc() (and possibly ttxin() and
-          ttinl()) when the communications input buffer is empty. The
-          buffer should be called ttxbuf[], its length is defined by the
-          symbol TXBUFL. The global variable txbufn is the number of
-          characters available to be read from ttxbuf[], and txbufp is
-          the index of the next character to be read. Should not be
-          called if txbufn > 0, in which case the buffer does not need
-          refilling. This routine returns:
-            -2: Communications disconnect
-            -1: Timeout
-          >=0: A character (0 - 255) On success, the first character that
-          was read, with the variables txbufn and txbufp set
-          appropriately for any remaining characters.
-          NOTE: Currently this routine is used internally only by the
-          UNIX and VMS versions. The aim is to make it available to all
-          versions so there is one single coherent and efficient way of
-          reading from the communications device or network.
-
-    4.E.2.6. Miscellaneous system-dependent functions
-
-   VOID
-          ztime(s) char **s;
-          Returns a pointer, s, to the current date-and-time string in s.
-          This string must be in the fixed-field format associated with
-          the C runtime asctime() function, like: "Sun Sep 16 13:23:45
-          1973\n" so that callers of this function can extract the
-          different fields. The pointer value is filled in by ztime, and
-          the data it points to is not safe, so should be copied to a
-          safe place before use. ztime() has no return value. As a side
-          effect, this routine can also fill in the following two
-          external variables (which must be defined in the
-          system-dependendent modules for each platform):
-            long ztusec: Fraction of seconds of clock time, microseconds.
-            long ztmsec: Fraction of seconds of clock time, milliseconds.
-          If these variables are not set by zstime(), they remain at
-          their initial value of -1L.
-
-   int
-          gtimer()
-          Returns the current value of the elapsed time counter in
-          seconds (see rtimer), or 0 on any kind of error.
-
-   #ifdef GFTIMER
-          CKFLOAT
-          gftimer()
-          Returns the current value of the elapsed time counter in
-          seconds, as a floating point number, capable of representing
-          not only whole seconds, but also the fractional part, to the
-          millisecond or microsecond level, whatever precision is
-          available. Requires a function to get times at subsecond
-          precision, as well as floating-point support. That's why it's
-          #ifdef'd.
-
-   #endif /* GFTIMER */
-
-   int
-          msleep(m) int m;
-          Sleeps (pauses, does nothing) for m milliseconds (a millisecond
-          is one thousandth of a second). Returns:
-           -1: on failure.
-            0: on success.
-
-   VOID
-          rtimer()
-          Sets the elapsed time counter to zero. If you want to time how
-          long an operation takes, call rtimer() when it starts and
-          gtimer when it ends. rtimer() has no return value.
-
-   #ifdef GFTIMER
-          VOID
-          rftimer()
-          Sets the elapsed time counter to zero. If you want to time how
-          long an operation takes, call rftimer() when it starts and
-          gftimer when it ends. rftimer() has no return value. Note:
-          rftimer() is to be used with gftimer() and rtimer() is to be
-          used with gtimer(). See the rftimer() description.
-
-   #endif /* GFTIMER */
-
-   int
-          sysinit()
-          Does whatever needs doing upon program start. In particular, if
-          the program is running in any kind of privileged mode, turns
-          off the privileges (see priv_ini()). Returns:
-           -1: on error.
-            0: on success.
-
-   int
-          syscleanup()
-          Does whatever needs doing upon program exit. Returns:
-           -1: on error.
-            0: on success.
-
-   int
-          psuspend()
-          Suspends the Kermit process, puts it in the background so it
-          can be continued ("foregrounded") later. Returns:
-           -1: if this function is not supported.
-            0: on success.
-
-   [ [114]Contents ] [ [115]C-Kermit ] [ [116]Kermit Home ]
-    ________________________________________________________________________
-
-  4.F. Group F: Network Support
-
-   As of version 5A, C-Kermit includes support for several networks.
-   Originally, this was just worked into the ttopen(), ttclos(), ttinc(),
-   ttinl(), and similar routines in [117]ckutio.c. But this made it
-   impossible to share this code with non-UNIX versions, like VMS,
-   AOS/VS, OS/2, etc. So as of edit 168, network code has been separated
-   out into its own module and header file, ckcnet.c and ckcnet.h:
-
-     [118]ckcnet.h: Network-related symbol definitions.
-     [119]ckcnet.c: Network i/o (TCP/IP, X.25, etc), shared by most
-   platforms.
-     [120]cklnet.c: Network i/o (TCP/IP, X.25, etc) specific to Stratus
-   VOS.
-
-   The routines and variables in these modules fall into two categories:
-
-    1. Support for specific network packages like SunLink X.25 and TGV
-       MultiNet, and:
-    2. support for specific network virtual terminal protocols like CCITT
-       X.3 and TCP/IP Telnet.
-
-   Category (1) functions are analogs to the tt*() functions, and have
-   names like netopen, netclos, nettinc, etc. Group A-D modules do not
-   (and must not) know anything about these functions -- they continue to
-   call the old Group E functions (ttopen, ttinc, etc). Category (2)
-   functions are protocol specific and have names prefixed by a protocol
-   identifier, like tn for telnet x25 for X.25.
-
-   ckcnet.h contains prototypes for all these functions, as well as
-   symbol definitions for network types, protocols, and network- and
-   protocol- specific symbols, as well as #includes for the header files
-   necessary for each network and protocol.
-
-   The following functions are to be provided for networks that do not
-   use normal system i/o (open, read, write, close):
-
-   int
-          netopen()
-          To be called from within ttopen() when a network connection is
-          requested. Calling conventions and purpose same as Group E
-          ttopen().
-
-   int
-          netclos()
-          To be called from within ttclos() when a network connection is
-          being closed. Calling conventions and purpose same as Group E
-          ttclos().
-
-   int
-          nettchk()
-          To be called from within ttchk(). Calling conventions and
-          purpose same as Group E ttchk().
-
-   int
-          netflui()
-          To be called from within ttflui(). Calling conventions and
-          purpose same as Group E ttflui().
-
-   int
-          netbreak()
-          To send a network break (attention) signal. Calling conventions
-          and purpose same as Group E ttsndbrk().
-
-   int
-          netinc()
-          To get a character from the network. Calling conventions same
-          as Group E ttsndbrk().
-
-   int
-          nettoc()
-          Send a "character" (byte) to the network. Calling conventions
-          same as Group E ttoc().
-
-   int
-          nettol()
-          Send a "line" (sequence of bytes) to the network. Calling
-          conventions same as Group E ttol().
-
-   Conceivably, some systems support network connections simply by
-   letting you open a device of a certain name and letting you do i/o to
-   it. Others (like the Berkeley sockets TCP/IP library on UNIX) require
-   you to open the connection in a special way, but then do normal i/o
-   (read, write). In such a case, you would use netopen(), but you would
-   not use nettinc, nettoc, etc.
-
-   VMS TCP/IP products have their own set of functions for all network
-   operations, so in that case the full range of netxxx() functions is
-   used.
-
-   The technique is to put a test in each corresponding ttxxx() function
-   to see if a network connection is active (or is being requested), test
-   for which kind of network it is, and if necessary route the call to
-   the corresponding netxxx() function. The netxxx() function must also
-   contain code to test for the network type, which is available via the
-   global variable ttnet.
-
-   [ [121]Contents ] [ [122]C-Kermit ] [ [123]Kermit Home ]
-      ______________________________________________________________________
-
-    4.F.1. Telnet Protocol
-
-   (This section needs a great deal of updating...)
-
-   As of edit 195, Telnet protocol is split out into its own files, since
-   it can be implemented in remote mode, which does not have a network
-   connection:
-
-      [124]ckctel.h: Telnet protocol symbol definitions.
-      [125]ckctel.c: Telnet protocol.
-
-   The Telnet protocol is supported by the following variables and
-   routines:
-
-   int tn_init
-          Nonzero if telnet protocol initialized, zero otherwise.
-
-   int
-          tn_init()
-          Initialize the telnet protocol (send initial options).
-
-   int
-          tn_sopt()
-          Send a telnet option.
-
-   int
-          tn_doop()
-          Receive and act on a telnet option from the remote.
-
-   int
-          tn_sttyp()
-          Send terminal type using telnet protocol.
-      ______________________________________________________________________
-
-    4.F.2. FTP Protocol
-
-   (To be filled in...)
-      ______________________________________________________________________
-
-    4.F.3. HTTP Protocol
-
-   (To be filled in...)
-      ______________________________________________________________________
-
-    4.F.4. X.25 Networks
-
-   These routines were written SunLink X.25 and have since been adapted
-   to at least on one other: IBM AIXLink/X.25.
-
-   int
-          x25diag()
-          Reads and prints X.25 diagnostics
-
-   int
-          x25oobh()
-          X.25 out of band signal handler
-
-   int
-          x25intr()
-          Sends X.25 interrupt packet
-
-   int
-          x25reset()
-          Resets X.25 virtual circuit
-
-   int
-          x25clear()
-          Clear X.25 virtual circuit
-
-   int
-          x25stat()
-          X.25 status
-
-   int
-          setqbit()
-          Sets X.25 Q-bit
-
-   int
-          resetqbit()
-          Resets X.25 Q-bit
-
-   int
-          x25xin()
-          Reads n characters from X.25 circuit.
-
-   int
-          x25inl()
-          Read a Kermit packet from X.25 circuit.
-
-   [ [126]Contents ] [ [127]C-Kermit ] [ [128]Kermit Home ]
-      ______________________________________________________________________
-
-    4.F.5. Adding New Network Types
-
-   Example: Adding support for IBM X.25 and Hewlett Packard X.25. First,
-   add new network type symbols for each one. There are already some
-   network types defined for other X.25 packages:
-
-  NET_SX25 is the network-type ID for SunLink X.25.
-  NET_VX25 is the network-type ID for VOS X.25.
-
-   So first you should new symbols for the new network types, giving them
-   the next numbers in the sequence, e.g.:
-
-#define NET_HX25 11                     /* Hewlett-Packard X.25 */
-#define NET_IX25 12                     /* IBM X.25 */
-
-   This is in ckcnet.h.
-
-   Then we need symbols to say that we are actually compiling in the code
-   for these platforms. These would be defined on the cc command line:
-
-  -DIBMX25  (for IBM)
-  -DHPX25   (for HP)
-
-   So we can build C-Kermit versions for AIX and HP-UX both with and
-   without X.25 support (since not all AIX and IBM systems have the
-   needed libraries, and so an executable that was linked with them might
-   no load).
-
-   Then in ckcnet.h:
-
-#ifdef IBMX25
-#define ANYX25
-#endif /* IBMX25 */
-
-#ifdef HPX25
-#define ANYX25
-#endif /* HPX25 */
-
-   And then use ANYX25 for code that is common to all of them, and IBMX25
-   or HPX25 for code specific to IBM or HP.
-
-   It might also happen that some code can be shared between two or more
-   of these, but not the others. Suppose, for example, that you write
-   code that applies to both IBM and HP, but not Sun or VOS X.25. Then
-   you add the following definition to ckcnet.h:
-
-#ifndef HPORIBMX25
-#ifdef HPX25
-#define HPORIBMX25
-#else
-#ifdef IBMX25
-#define HPORIBMX25
-#endif /* IBMX25 */
-#endif /* HPX25 */
-#endif /* HPORIBMX25 */
-
-   You can NOT use constructions like "#if defined (HPX25 || IBMX25)";
-   they are not portable.
-
-   [ [129]Contents ] [ [130]C-Kermit ] [ [131]Kermit Home ]
-    ________________________________________________________________________
-
-  4.G. Group G: Formatted Screen Support
-
-   So far, this is used only for the fullscreen local-mode file transfer
-   display. In the future, it might be extended to other uses. The
-   fullscreen display code is in and around the routine screenc() in
-   [132]ckuusx.c.
-
-   In the UNIX version, we use the curses library, plus one call from the
-   termcap library. In other versions (OS/2, VMS, etc) we insert dummy
-   routines that have the same names as curses routines. So far, there
-   are two methods for simulating curses routines:
-
-    1. In VMS, we use the Screen Management Library (SMG), and insert
-       stubs to convert curses calls into SMG calls.
-    2. In OS/2, we use the MYCURSES code, in which the stub routines
-       actually emit the appropriate escape sequences themselves.
-
-   Here are the stub routines:
-
-   int
-          tgetent(char *buf, char *term)
-          Arguments are ignored. Returns 1 if the user has a supported
-          terminal type, 0 otherwise. Sets a global variable (for
-          example, "isvt52" or "isdasher") to indicate the terminal type.
-
-   VOID
-          move(int row, int col)
-          Sends the escape sequence to position the cursor at the
-          indicated row and column. The numbers are 0-based, e.g. the
-          home position is 0,0.
-
-   int
-          clear()
-          Sends the escape sequence to clear the screen.
-
-   int
-          clrtoeol()
-          Sends the escape sequence to clear from the current cursor
-          position to the end of the line.
-
-   In the MYCURSES case, code must be added to each of the last three
-   routines to emit the appropriate escape sequences for a new terminal
-   type.
-
-   clearok(curscr), wrefresh()
-          In real curses, these two calls are required to refresh the
-          screen, for example after it was fractured by a broadcast
-          message. These are useful only if the underlying screen
-          management service keeps a copy of the entire screen, as curses
-          and SMG do. C-Kermit does not do this itself.
-
-   [ [133]Contents ] [ [134]C-Kermit ] [ [135]Kermit Home ]
-    ________________________________________________________________________
-
-  4.H. Group H: Pseudoterminal Support
-
-   (To be filled in...)
-    ________________________________________________________________________
-
-  4.I. Group I: Security
-
-   (To be filled in...)
-
-   [ [136]Contents ] [ [137]C-Kermit ] [ [138]Kermit Home ]
-    ________________________________________________________________________
-
-  APPENDIX I. FILE PERMISSIONS
-
-  I.1. Format of System-Dependent File Permissions in A-Packets
-
-   The format of this field (the "," attribute) is interpreted according
-   to the System ID ("." Attribute).
-
-   For UNIX (System ID = U1), it's the familiar 3-digit octal number, the
-   low-order 9 bits of the filemode: Owner, Group, World, e.g. 660 =
-   read/write access for owner and group, none for world, recorded as a
-   3-digit octal string. High-order UNIX permission bits are not
-   transmitted.
-
-   For VMS (System ID = D7), it's a 4-digit hex string, representing the
-   16-bit file protection WGOS fields (World,Group,Owner,System), in that
-   order (which is the reverse of how they're shown in a directory
-   listing); in each field, Bit 0 = Read, 1 = Write, 2 = Execute, 3 =
-   Delete. A bit value of 0 means permission is granted, 1 means
-   permission is denied. Sample:
-
-  r-01-00-^A/!FWERMIT.EXE'"
-  s-01-00-^AE!Y/amd/watsun/w/fdc/new/wermit.exe.DV
-  r-02-01-^A]"A."D7""B8#119980101 18:14:05!#8531&872960,$A20B-!7(#512@ #.Y
-  s-02-01-^A%"Y.5!
-
-   A VMS directory listing shows the file's protection as (E,RWED,RED,RE)
-   which really means (S=E,O=RWED,G=RED,W=RE), which is reverse order
-   from the internal storage, so (RE,RED,RWED,E). Now translate each
-   letter to its corresponding bit:
-
-  RE=0101, RED=1101, RWED=1111, E=0010
-
-   Now reverse the bits:
-
-  RE=1010, RED=0010, RWED=0000, E=1101
-
-   This gives the 16-bit quantity:
-
-  1010001000001101
-
-   This is the internal representation of the VMS file permission; in
-   hex:
-
-  A20B
-
-   as shown in the sample packet above.
-
-   The VMS format probably would also apply to RSX or any other FILES-11
-   system.
-
-  I.2. Handling of Generic Protection
-
-   To be used when the two systems are different (and/or do not recognize
-   or understand each other's local protection codes).
-
-   First of all, the book is wrong. This should not be the World
-   protection, but the Owner protection. The other fields should be set
-   according to system defaults (e.g. UNIX umask, VMS default protection,
-   etc), except that no non-Owner field should give more permissions than
-   the Owner field.
-
-   [ [139]Top ] [ [140]Contents ] [ [141]C-Kermit Home ] [ [142]Kermit
-   Home ]
-     _________________________________________________________________
-
-
-    C-Kermit Program Logic Manual / [143]The Kermit Project /
-    [144]Columbia University / [145]kermit@columbia.edu / 10 April 2004
-
-References
-
-   1. http://www.columbia.edu/kermit/
-   2. http://www.columbia.edu/
-   3. http://www.columbia.edu/kermit/ckcplm.html
-   4. http://www.columbia.edu/kermit/ckermit.html
-   5. http://www.columbia.edu/kermit/index.html
-   6. http://www.columbia.edu/kermit/ckcplm.html#x1
-   7. http://www.columbia.edu/kermit/ckcplm.html#x2
-   8. http://www.columbia.edu/kermit/ckcplm.html#x3
-   9. http://www.columbia.edu/kermit/ckcplm.html#x4
-  10. http://www.columbia.edu/kermit/ckcplm.html#x4.A
-  11. http://www.columbia.edu/kermit/ckcplm.html#x4.B
-  12. http://www.columbia.edu/kermit/ckcplm.html#x4.C
-  13. http://www.columbia.edu/kermit/ckcplm.html#x4.D
-  14. http://www.columbia.edu/kermit/ckcplm.html#x4.E
-  15. http://www.columbia.edu/kermit/ckcplm.html#x4.F
-  16. http://www.columbia.edu/kermit/ckcplm.html#x4.G
-  17. http://www.columbia.edu/kermit/ckcplm.html#x4.H
-  18. http://www.columbia.edu/kermit/ckcplm.html#x4.I
-  19. http://www.columbia.edu/kermit/ckcplm.html#xa1
-  20. http://www.columbia.edu/kermit/ckcplm.html#contents
-  21. http://www.columbia.edu/kermit/ckcplm.html#contents
-  22. http://www.columbia.edu/kermit/ckermit.html
-  23. http://www.columbia.edu/kermit/index.html
-  24. ftp://kermit.columbia.edu/kermit/c-kermit/ckcpro.w
-  25. http://www.columbia.edu/kermit/ckcplm.html#contents
-  26. http://www.columbia.edu/kermit/ckermit.html
-  27. http://www.columbia.edu/kermit/index.html
-  28. http://www.columbia.edu/kermit/ckcplm.html#x3.2
-  29. http://www.columbia.edu/kermit/ckcplm.html#contents
-  30. http://www.columbia.edu/kermit/ckermit.html
-  31. http://www.columbia.edu/kermit/index.html
-  32. http://www.columbia.edu/kermit/ckcplm.html#x4.A
-  33. http://www.columbia.edu/kermit/ckcplm.html#contents
-  34. http://www.columbia.edu/kermit/ckermit.html
-  35. http://www.columbia.edu/kermit/index.html
-  36. http://www.columbia.edu/kermit/ckcplm.html#contents
-  37. http://www.columbia.edu/kermit/ckermit.html
-  38. http://www.columbia.edu/kermit/index.html
-  39. http://www.columbia.edu/kermit/ckcplm.html#contents
-  40. http://www.columbia.edu/kermit/ckermit.html
-  41. http://www.columbia.edu/kermit/index.html
-  42. ftp://kermit.columbia.edu/kermit/c-kermit/ckclib.h
-  43. ftp://kermit.columbia.edu/kermit/c-kermit/ckclib.c
-  44. http://www.columbia.edu/kermit/ckcplm.html#x3.1
-  45. http://www.columbia.edu/kermit/ckcplm.html#contents
-  46. http://www.columbia.edu/kermit/ckermit.html
-  47. http://www.columbia.edu/kermit/index.html
-  48. ftp://kermit.columbia.edu/kermit/c-kermit/ckcsym.h
-  49. ftp://kermit.columbia.edu/kermit/c-kermit/ckcasc.h
-  50. ftp://kermit.columbia.edu/kermit/c-kermit/ckcsig.h
-  51. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h
-  52. ftp://kermit.columbia.edu/kermit/c-kermit/ckcker.h
-  53. ftp://kermit.columbia.edu/kermit/c-kermit/ckcxla.h
-  54. ftp://kermit.columbia.edu/kermit/c-kermit/ckcmai.c
-  55. ftp://kermit.columbia.edu/kermit/c-kermit/ckcpro.w
-  56. ftp://kermit.columbia.edu/kermit/c-kermit/ckcfns.c
-  57. ftp://kermit.columbia.edu/kermit/c-kermit/ckcfn2.c
-  58. ftp://kermit.columbia.edu/kermit/c-kermit/ckcfn3.c
-  59. http://www.columbia.edu/kermit/ckcplm.html#x4.B
-  60. http://www.columbia.edu/kermit/ckcplm.html#x4.E
-  61. http://www.columbia.edu/kermit/ckcplm.html#x4.D
-  62. http://www.columbia.edu/kermit/ckcplm.html#contents
-  63. http://www.columbia.edu/kermit/ckermit.html
-  64. http://www.columbia.edu/kermit/index.html
-  65. http://www.columbia.edu/kermit/ckcplm.html#x4.B
-  66. ftp://kermit.columbia.edu/kermit/c-kermit/ckuxla.c
-  67. ftp://kermit.columbia.edu/kermit/c-kermit/ckuxla.h
-  68. ftp://kermit.columbia.edu/kermit/c-kermit/ckcxla.h
-  69. ftp://kermit.columbia.edu/kermit/c-kermit/ckuxla.h
-  70. ftp://kermit.columbia.edu/kermit/c-kermit/ckmxla.h
-  71. ftp://kermit.columbia.edu/kermit/c-kermit/ck?xla
-  72. ftp://kermit.columbia.edu/kermit/c-kermit/ckcuni.h
-  73. ftp://kermit.columbia.edu/kermit/c-kermit/ckcuni.c
-  74. http://www.columbia.edu/kermit/ckcplm.html#contents
-  75. http://www.columbia.edu/kermit/ckermit.html
-  76. http://www.columbia.edu/kermit/index.html
-  77. http://www.columbia.edu/kermit/ckcplm.html#x4.B
-  78. ftp://kermit.columbia.edu/kermit/c-kermit/ckucmd.h
-  79. ftp://kermit.columbia.edu/kermit/c-kermit/ckucmd.c
-  80. http://www.columbia.edu/kermit/ckcplm.html#x4.E
-  81. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusr.h
-  82. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusr.c
-  83. ftp://kermit.columbia.edu/kermit/c-kermit/ckuus2.c
-  84. ftp://kermit.columbia.edu/kermit/c-kermit/ckuus3.c
-  85. ftp://kermit.columbia.edu/kermit/c-kermit/ckuus4.c
-  86. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusy.c
-  87. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusx.c
-  88. ftp://kermit.columbia.edu/kermit/c-kermit/ckuver.h
-  89. ftp://kermit.columbia.edu/kermit/c-kermit/ckuscr.c
-  90. ftp://kermit.columbia.edu/kermit/c-kermit/ckudia.c
-  91. ftp://kermit.columbia.edu/kermit/c-kermit/ckucon.c
-  92. ftp://kermit.columbia.edu/kermit/c-kermit/ckucns.c
-  93. http://www.columbia.edu/kermit/ckcplm.html#x4.E
-  94. ftp://kermit.columbia.edu/kermit/c-kermit/ckcmai.c
-  95. http://www.columbia.edu/kermit/ckcplm.html#contents
-  96. http://www.columbia.edu/kermit/ckermit.html
-  97. http://www.columbia.edu/kermit/index.html
-  98. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c
-  99. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c
- 100. ftp://kermit.columbia.edu/kermit/c-kermit/ckusig.c
- 101. ftp://kermit.columbia.edu/kermit/c-kermit/ckvfio.c
- 102. ftp://kermit.columbia.edu/kermit/c-kermit/ckusig.c
- 103. ftp://kermit.columbia.edu/kermit/c-kermit/ckcmai.c
- 104. http://www.columbia.edu/kermit/ckcplm.html#contents
- 105. http://www.columbia.edu/kermit/ckermit.html
- 106. http://www.columbia.edu/kermit/index.html
- 107. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c
- 108. ftp://kermit.columbia.edu/kermit/c-kermit/ckvtio.c
- 109. http://www.columbia.edu/kermit/ckcplm.html#x2
- 110. http://www.columbia.edu/kermit/ckcplm.html#xa1
- 111. http://www.columbia.edu/kermit/ckuins.html
- 112. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.h
- 113. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c
- 114. http://www.columbia.edu/kermit/ckcplm.html#contents
- 115. http://www.columbia.edu/kermit/ckermit.html
- 116. http://www.columbia.edu/kermit/index.html
- 117. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c
- 118. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.h
- 119. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.c
- 120. ftp://kermit.columbia.edu/kermit/c-kermit/cklnet.c
- 121. http://www.columbia.edu/kermit/ckcplm.html#contents
- 122. http://www.columbia.edu/kermit/ckermit.html
- 123. http://www.columbia.edu/kermit/index.html
- 124. ftp://kermit.columbia.edu/kermit/c-kermit/ckctel.h
- 125. ftp://kermit.columbia.edu/kermit/c-kermit/ckctel.c
- 126. http://www.columbia.edu/kermit/ckcplm.html#contents
- 127. http://www.columbia.edu/kermit/ckermit.html
- 128. http://www.columbia.edu/kermit/index.html
- 129. http://www.columbia.edu/kermit/ckcplm.html#contents
- 130. http://www.columbia.edu/kermit/ckermit.html
- 131. http://www.columbia.edu/kermit/index.html
- 132. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusx.c
- 133. http://www.columbia.edu/kermit/ckcplm.html#contents
- 134. http://www.columbia.edu/kermit/ckermit.html
- 135. http://www.columbia.edu/kermit/index.html
- 136. http://www.columbia.edu/kermit/ckcplm.html#contents
- 137. http://www.columbia.edu/kermit/ckermit.html
- 138. http://www.columbia.edu/kermit/index.html
- 139. http://www.columbia.edu/kermit/ckcplm.html#top
- 140. http://www.columbia.edu/kermit/ckcplm.html#contents
- 141. http://www.columbia.edu/kermit/ckermit.html
- 142. http://www.columbia.edu/kermit/index.html
- 143. http://www.columbia.edu/kermit/index.html
- 144. http://www.columbia.edu/
- 145. mailto:kermit@columbia.edu