[net.sources] C-Kermit

gregg@okstate.UUCP (03/08/85)

echo x - ckermi.mss part1
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckermi.mss
X@comment[	-*-Text-*-	]
X@Part(CKUNIX,root="KER:KUSER")
X@string(-ckversion="@q<4.2>")
X@define(exx=example,above 2,below 1)
X@Chapter<UNIX KERMIT>
X
X@Begin<Description,Leftmargin +12,Indent -12,spread 0>
X@i(Program:)@\Frank da Cruz, Bill Catchings, Jeff Damens, Columbia
XUniversity; Herm Fischer, Litton Data Systems, Van Nuys CA; contributions by
Xmany others.
X
X@i(Language:)@\C
X
X@i(Documentation:)@\Frank da Cruz, Columbia University; Herm
XFischer, Litton Data Systems, Van Nuys CA.
X
X@i(Version:)@\@value(-ckversion)
X
X@i(Date: )@\March 1985
X@end<Description>
X@Center(D R A F T)
X
X@begin(quotation)
XNOTE -- This source for this
Xdocumentation is written as input for the Scribe text formatter.
XIt needs to become input for two text formatters - Scribe and Nroff, the
Xformer for inclusion in the Kermit User Guide, and the latter for
XUnix man pages.  This might be done by defining formatting macros
Xin M4, which generate the appropriate Scribe and Nroff commands for
Xsectioning, itemization, description, etc.  (Volunteers?)
X@end(quotation)
X
XC-Kermit is a completely new implementation of Kermit, written modularly and
Xtransportably in C.  The protocol state transition table is written in
X@i'wart', a (not proprietary) lex-like preprocessor for C.  System-dependent
Xprimitive functions are isolated into separately compiled modules so that the
Xprogram should be easily portable among Unix systems and also to non-Unix
Xsystems that have C compilers.
X
X@subheading<Unix Kermit Capabilities At A Glance:>
X@begin<format,leftmargin +2,above 1,below 1>
X@tabclear()@tabset(3.5inches,4.0inches)
XLocal operation:@\Yes
XLocal operation:@\Yes
XRemote operation:@\Yes
XLogin scripts:@\Yes
XTransfer text files:@\Yes
XTransfer binary files:@\Yes
XWildcard send:@\Yes
XFile transfer interruption:@\Yes
XFilename collision avoidance:@\Yes
XCan time out:@\Yes
X8th-bit prefixing:@\Yes
XRepeat count prefixing:@\Yes
XAlternate block checks:@\Yes
XTerminal emulation:@\Yes
XCommunication settings:@\Yes
XTransmit BREAK:@\Yes
XSupport for dialout modems:@\Yes
XIBM mainframe communication:@\Yes
XTransaction logging:@\Yes
XSession logging:@\Yes
XDebug logging:@\Yes
XPacket logging:@\Yes
XAct as server:@\Yes
XTalk to server:@\Yes
XAdvanced server functions:@\Yes
XLocal file management:@\Yes
XCommand/Init files:@\Yes
XUUCP and multiuser line locking:@\Yes
XFile attributes:@\No
XCommand macros:@\No
XRaw file transmit:@\No
X@end<format>
X
X@index(C-Kermit)@index(Unix Kermit)
XC-Kermit provides traditional Unix command line operation as well as
Xinteractive command prompting and execution.  The command line options
Xprovide access to a minimal subset of C-Kermit's capabilities; the
Xinteractive command set is far richer.
X
XOn systems with dialout modems, C-Kermit can use command files and login
Xscripts to essentially duplicate the file transfer functionality of uucp among
Xheterogeneous operating systems, including by the use of scheduled (e.g. late
Xnight) unattended operation.
X
X@section(The Unix File System)
X
XConsult your Unix manual for details about the file system under your
Xversion of Unix.  For the purposes of Kermit, several things are worth
Xbriefly noting.  Unix files generally have lowercase names.  Unix
Xdirectories are tree-@|structured.  Directory levels are separated by
X"@q(/)" characters.  For example,
X@example(/usr/foo/bar)
Xdenotes the file @q(bar) in the directory @q(/usr/foo).  Wildcard or
X"meta" characters allow groups of files to be specified.  "@q(*)"
Xmatches any string; "@q(?)" matches any single character.
X
XWhen C-Kermit is invoked with files specified on the Unix command line,
Xthe Unix shell (Bourne Shell, C-Shell, etc) expands meta characters, and
Xin this case, a wider variety is available.  For example,
X@example(kermit -s ~/ck[x-z]*.[ch])
Xis expanded by the Berkeley C-Shell into a list of all the files in the
Xuser's home directory (@q[~/]) that start with the characters "@q(ck)",
Xfollowed by a single character @q(x), @q(y), or @q(z), followed by zero
Xor more characters, followed by a dot, followed by one of the characters
X@q(c) or @q(h).  Internally, the C-Kermit program itself expands only
Xthe @q("*") and @q("?") meta characters.
X
XUnix files are linear streams of 8-bit bytes.  Text files consist of
X7-bit ASCII characters, with the high bit off (0), and lines separated by
Xthe Unix newline character, which is linefeed (LF, ASCII 10).  This
Xdistinguishes Unix text files from those on most other ASCII systems, in
Xwhich lines are separated by a carriage-@|return linefeed sequence
X(CRLF, ASCII 13 followed by ASCII 10).  Binary files are likely to contain
Xdata in the high bits of the file bytes, and are not treated in terms of
Xlines.
X
XWhen transferring files, C-Kermit will convert between upper and lower
Xcase filenames and between LF and CRLF line terminators automatically,
Xunless told to do otherwise.  When binary files must be transferred, the
Xprogram must be instructed not to perform LF/CRLF conversion (@q[-i] on the
Xcommand line or "set file type" interactively; see below).
X
X@section(Command Line Operation)
X
XThe C-Kermit command line syntax has been changed from that of earlier
Xreleases of Unix Kermit to conform to the "Proposed Syntax Standards for
XUnix System Commands" put forth by Kathy Hemenway and Helene Armitage of
XAT&T Bell Laboratories in @ux(Unix/World), Vol.1, No.3, 1984.  The rules that
Xapply are:
X
X@begin(itemize,spread 0)
XCommand names must be between 2 and 9 characters ("kermit" is 6).
X
XCommand names must include lower case letters and digits only.
X
XAn option name is a single character.
X
XOptions are delimited by '@q(-)'.
X
XOptions with no arguments may be grouped (bundled) behind one delimiter.
X
XOption-arguments cannot be optional.
X
XArguments immediately follow options, separated by whitespace.
X
XThe order of options does not matter.
X
X'@q(-)' preceded and followed by whitespace means standard input.
X@end(itemize)
XA group of bundled options may end with an option that has an argument.
X
XThe following notation is used in command descriptions:
X@begin(description,leftmargin +8,indent -8)
X@i(fn)@\A Unix file specification, possibly containing the "wildcard"
Xcharacters '@q[*]' or '@q[?]' ('@q[*]' matches all character strings, '@q[?]'
Xmatches any single character).
X
X@i(fn1)@\A Unix file specification which may not contain '@q[*]' or '@q[?]'.
X
X@i(rfn)@\A remote file specification in the remote system's own syntax, which
Xmay denote a single file or a group of files.
X
X@i(rfn1)@\A remote file specification which should denote only a single file.
X
X@i(n)@\A decimal number between 0 and 94.
X
X@i(c)@\A decimal number between 0 and 127 representing the value of an
XASCII character.
X
X@i(cc)@\A decimal number between 0 and 31, or else exactly 127,
Xrepresenting the value of an ASCII control character.
X
X@q([ ])@\Any field in square braces is optional.
X
X@q({x,y,z})@\Alternatives are listed in curly braces.
X@end(description)
X
XC-Kermit command line options may specify either actions or settings.  If
XC-Kermit is invoked with a command line that specifies no actions, then it
Xwill issue a prompt and begin interactive dialog.
XAction options specify either protocol transactions or terminal connection.
X
X@begin<description,leftmargin +8,indent -8>
X@q(-s )@i(fn)@\Send the specified file or files.  If @i(fn) contains
Xwildcard (meta) characters, the Unix shell expands it into a list.  If @i(fn)
Xis '@q[-]' then kermit sends from standard input, which must
Xcome from a file:
X@example(kermit -s - < foo.bar)
Xor a parallel process:
X@example(ls -l | kermit -s -)
XYou cannot use this mechanism to send
Xterminal typein.  If you want to send a file whose name is "-"
Xyou can precede it with a path name, as in
X@example(kermit -s ./-)
X
X@q(-r)@\Receive a file or files.  Wait passively for files to arrive.
X
X@q(-k)@\Receive (passively) a file or files, sending them to standard
Xoutput.  This option can be used in several ways:
X@begin(description,leftmargin +4,indent -4)
X@q(kermit -k)@\Displays the incoming files on your screen; to be used only
Xin "local mode" (see below).
X
X@q(kermit -k > )@i(fn1)@\Sends the incoming file or files to the named file,
X@i(fn1).  If more than one file arrives, all are concatenated together
Xinto the single file @i(fn1).
X
X@q(kermit -k | command)@\Pipes the incoming data (single or multiple
Xfiles) to the indicated command, as in
X@example'kermit -k | sort > sorted.stuff'
X@end(description)
X
X@q(-a )@i(fn1)@\If you have specified a file transfer option, you may specify
Xan alternate name for a single file with the @q(-a) option.  For example,
X@example'kermit -s foo -a bar'
Xsends the file @q(foo) telling the receiver that its name is @q(bar).
XIf more than one file arrives or is sent, only the first file is
Xaffected by the @q(-a) option:
X@example'kermit -ra baz'
Xstores the first incoming file under the name @q(baz).
X
X@q(-x)@\Begin server operation.  May be used in either local or remote mode.
X@end(description)
X
XBefore proceeding, a few words about remote and local operation are
Xnecessary.  C-Kermit is "local" if it is running on PC or workstation that
Xyou are using directly, or if it is running on a multiuser system and
Xtransferring files over an external communication line -- not your job's
Xcontrolling terminal or console.  C-Kermit is remote if it is running on a
Xmultiuser system and transferring files over its own controlling terminal's
Xcommunication line, connected to your PC or workstation.
X
XIf you are running C-Kermit on a PC, it is in local mode by default,
Xwith the "back port" designated for file transfer and terminal connection.
XIf you are running C-Kermit on a multiuser (timesharing) system, it is
Xin remote mode unless you explicitly point it at an external line for
Xfile transfer or terminal connection.  The following command sets
XC-Kermit's "mode":
X
X@begin(description,leftmargin +8,indent -8)
X@q(-l )@i(dev)@\Line  -- Specify a terminal line to use for file
Xtransfer and terminal connection, as in
X@example'kermit -l /dev/ttyi5'
X@end(description)
X
XWhen an external line is being used, you might also need some additional
Xoptions for successful communication with the remote system:
X
X@begin(description,leftmargin +8,indent -8)
X@q(-b )@i(n)@\Baud  -- Specify the baud rate for the line given in the
X@q(-l) option, as in
X@example'kermit -l /dev/ttyi5 -b 9600'
XThis option should always be included with the @q(-l) option, since the
Xspeed of an external line is not necessarily what you expect.
X
X@q(-p )@i(x)@\Parity -- e,o,m,s,n (even, odd, mark, space, or none).  If parity
Xis other than none, then the 8th-bit prefixing mechanism will be
Xused for transferring 8-bit binary data, provided the opposite
XKermit agrees.  The default parity is none.
X
X@q(-t)@\Specifies half duplex, line turnaround with XON as the handshake
Xcharacter.
X@end(description)
X
XThe following commands may be used only with a C-Kermit which is local --
Xeither by default or else because the -l option has been specified.
X
X@begin(description,leftmargin +8,indent -8)
X@q(-g )@i(rfn)@\Actively request a remote server to send the named file
Xor files; @i(rfn) is a file specification in the remote host's own syntax.  If
X@i(fn) happens to contain any special shell characters, like '@q(*)',
Xthese must be quoted, as in
X@example'kermit -g x\*.\?'
X
X@q(-f)@\Send a 'finish' command to a remote server.
X
X@q(-c)@\Establish a terminal connection over the specified or default
Xcommunication line, before any protocol transaction takes place.
XGet back to the local system by typing the escape character
X(normally Control-Backslash) followed by the letter 'c'.
X
X@q(-n)@\Like @q(-c), but after a protocol transaction takes place;
X@q(-c) and @q(-n) may both be used in the same command.  The use of @q(-n)
Xand @q(-c) is illustrated below.
X@end(description)
XOn a timesharing system, the @q(-l) and @q(-b) options will also have to
Xbe included with the @q(-r), @q(-k), or @q(-s) options if the other
XKermit is on a remote system.
X
XIf C-Kermit is in local mode, the screen (stdout) is continously updated to
Xshow the progress of the file transer.  A dot is printed for every four data
Xpackets, other packets are shown by type (e.g. '@q(S)' for Send-Init),
X'@q(T)' is printed when there's a timeout, and '@q(%)' for each
Xretransmission.  In addition, you may type (to stdin) certain "interrupt"
Xcommands during file transfer:
X@begin(description,leftmargin +16,indent -12,spread 0)
XControl-F:@\Interrupt the current File, and go on to the next (if any).
X
XControl-B:@\Interrupt the entire Batch of files, terminate the transaction.
X
XControl-R:@\Resend the current packet
X
XControl-A:@\Display a status report for the current transaction.
X@end(description)
XThese interrupt characters differ from the ones used in other Kermit
Ximplementations to avoid conflict with Unix shell interrupt characters.
XWith System III and System V implementations of Unix, interrupt commands
Xmust be preceeded by the escape character (e.g. control-@q[\]).
X
XSeveral other command-line options are provided:
X
X@begin(description,leftmargin +8,indent -8)
X@q(-i)@\Specifies that files should be sent or received exactly "as is"
Xwith no conversions.  This option is necessary for transmitting
Xbinary files.  It may also be used to slightly boost efficiency
Xin Unix-to-Unix transfers of text files by eliminating CRLF/newline
Xconversion.
X
X@q(-w)@\Write-Protect -- Avoid filename collisions for incoming files.
X
X@q(-q)@\Quiet -- Suppress screen update during file transfer, for instance
Xto allow a file transfer to proceed in the background.
X
X@q(-d)@\Debug -- Record debugging information in the file @q(debug.log) in 
Xthe current directory.  Use this option if you believe the program
Xis misbehaving, and show the resulting log to your local
Xkermit maintainer.
X
X@q(-h)@\Help -- Display a brief synopsis of the command line options.
X@end(description)
XThe command line may contain no more than one protocol action option.
X
XFiles are sent with their own names, except that lowercase letters are
Xraised to upper, pathnames are stripped off, tilde ('@q[~]') characters
Xchanged to '@q(X)', and if the file name begins with a period, an
X'@q(X)' is inserted before it.  Incoming files are stored under their
Xown names except that uppercase letters are lowered, and, if @q(-w) was
Xspecified, a "generation number" is appended to the name if it has the
Xsame name as an existing file which would otherwise be overwritten.  If
Xthe @q(-a) option is included, then the same rules apply to its
Xargument.  The file transfer display shows any transformations performed
Xupon filenames.
X
XDuring transmission, files are encoded as follows:
X@begin(itemize)
XControl characters are converted to prefixed printables.
X
XSequences of repeated characters are collapsed via repeat counts, if
Xthe other Kermit is also capable of repeated-character compression.
X
XIf parity is being used on the communication line, data characters with 
Xthe 8th (parity) bit on are specially prefixed, provided the other Kermit
Xis capable of 8th-bit prefixing (if not, 8-bit binary files cannot be
Xsuccessfully transferred).
X
XConversion is done between Unix newlines and carriage-return-linefeed 
Xsequences unless the @q(-i) option was specified.
X@end(itemize)
X
X@subheading(Command Line Examples:)
X
X@example(kermit -l /dev/ttyi5 -b 1200 -cn -r)
XThis command connects you to the system on the other end of ttyi5 at
X1200 baud, where you presumably log in and run Kermit with a 'send'
Xcommand.  After you escape back, C-Kermit waits for a file (or files) to
Xarrive.  When the file transfer is completed, you are again connected to
Xthe remote system so that you can logout.
X
X@example(kermit -l /dev/ttyi4 -b 1800 -cntp m -r -a foo)
XThis command is like the preceding one, except the remote system in this
Xcase uses half duplex communication with mark parity.  The first file
Xthat arrives is stored under the name @q(foo).
X
X@exx(kermit -l /dev/ttyi6 -b 9600 -c | tek)
XThis example uses Kermit to connect your terminal to the system at the
Xother end of ttyi6.  The C-Kermit terminal connection does not
Xprovide any particular terminal emulation, so C-Kermit's standard i/o is
Xpiped through a (hypothetical) program called tek, which performs (say)
XTektronix emulation.
X
X@exx(kermit -l /dev/ttyi6 -b 9600 -nf)
XThis command would be used to shut down a remote server and then connect
Xto the remote system, in order to log out or to make further use of it.
XThe @q(-n) option is invoked @i(after) @q(-f) (@q[-c] would have been invoked
Xbefore).
X
X@exx(kermit -l /dev/ttyi6 -b 9600 -qg foo.\* &)
XThis command causes C-Kermit to be invoked in the background, getting a group
Xof files from a remote server (note the quoting of the '@q[*]' character).  No
Xdisplay occurs on the screen, and the keyboard is not sampled for
Xinterruption commands.  This allows other work to be done while file
Xtransfers proceed in the background.
X
X@exx(kermit -l /dev/ttyi6 -b 9600 -g foo.\* > foo.log < /dev/null &)
XThis command is like the previous one, except the file transfer display has
Xbeen redirected to the file @q(foo.log).  Standard input is also redirected, to
Xprevent C-Kermit from sampling it for interruption commands.
X
X@exx(kermit -iwx)
XThis command starts up C-Kermit as a server.  Files are transmitted with
Xno newline/@|carriage-@|return-@|linefeed conversion; the @q(-i) option is
Xnecessary for binary file transfer and useful for Unix-@|to-@|Unix transfers.
XIncoming files that have the same names as existing files are given new, unique
Xnames.
X
X@exx(kermit -l /dev/ttyi6 -b 9600)
XThis command sets the communication line and speed.  Since no action is
Xspecified, C-Kermit issues a prompt and enters an interactive dialog with
Xyou.  Any settings given on the command line remain in force during the
Xdialog, unless explicitly changed.
X
X@exx(kermit)
XThis command starts up Kermit interactively with all default settings.
X
XA final example shows how Unix Kermit might be used to send an entire directory
Xtree from one Unix system to another, using the tar program as Kermit's
Xstandard input and output.  On the orginating system, in this case the remote,
Xtype (for instance):@label(-uxtar)
X
X@exx(tar cf - /usr/fdc | kermit -is -)
XThis causes tar to send the directory @q(/usr/fdc) (and all its files and all
Xits subdirectories and all their files...) to standard output instead of to a
Xtape; kermit receives this as standard input and sends it as a binary file.
XOn the receiving system, in this case the local one, type (for instance):
X
X@exx(kermit -il /dev/ttyi5 -b 9600 -k | tar xf -)
XKermit receives the tar archive, and sends it via standard output to its own
Xcopy of tar, which extracts from it a replica of the original directory tree.
X
X@subheading(Exit Status Codes:)
X
XKermit returns an exit status of zero, except when a fatal error is
Xencountered, where the exit status is set to one.  With background
Xoperation (e.g., '@q(&)' on invoking command line), driven by scripted
Xinteractive commands (redirected standard input and/or take files),
Xany failed interactive command (such as failed dial or script attempt)
Xcauses the fatal error exit.
X
X
X@section(Interactive Operation)
X
XC-Kermit's interactive command prompt is "@q(C-Kermit>)".  In response to this
Xprompt, you may type any valid command.  C-Kermit executes the command and
Xthen prompts you for another command.  The process continues until you
Xinstruct the program to terminate.
X
XCommands begin with a keyword, normally an English verb, such as "send".
XYou may omit trailing characters from any keyword, so long as you specify
Xsufficient characters to distinguish it from any other keyword valid in that
Xfield.  Certain commonly-used keywords (such as "send", "receive", "connect")
Xhave special non-unique abbreviations ("s" for "send", "r" for "receive",
X"c" for "connect").
X
XCertain characters have special functions in interactive commands:
X@Begin(Description,leftmargin +8,indent -4)
X@q(?)@\Question mark, typed at any point in a command, will produce a
Xmessage explaining what is possible or expected at that point.  Depending on
Xthe context, the message may be a brief phrase, a menu of keywords, or a list
Xof files.
X
X@q(ESC)@\(The Escape or Altmode key) -- Request completion of the current
Xkeyword or filename, or insertion of a default value.  The result will be a
Xbeep if the requested operation fails.
X
X@q(DEL)@\(The Delete or Rubout key) -- Delete the previous character from the
Xcommand.  You may also use BS (Backspace, Control-H) for this function.
X
X@q(^W)@\(Control-W) -- Erase the rightmost word from the command line.
X
X@q(^U)@\(Control-U) -- Erase the entire command.
X
X@q(^R)@\(Control-R) -- Redisplay the current command.
X
X@q(SP)@\(Space) -- Delimits fields (keywords, filenames, numbers) within
Xa command.  HT (Horizontal Tab) may also be used for this purpose.
X
X@q(CR)@\(Carriage Return) -- Enters the command for execution.  LF (Linefeed)
Xor FF (formfeed) may also be used for this purpose.
X
X@q(\)@\(Backslash) -- Enter any of the above characters into the command,
Xliterally.  To enter a backslash, type two backslashes in a row (@q[\\]).
X@End(Description)
XYou may type the editing characters (@q[DEL], @q[^W], etc) repeatedly, to
Xdelete all the way back to the prompt.  No action will be performed until the
Xcommand is entered by typing carriage return, linefeed, or formfeed.  If you
Xmake any mistakes, you will receive an informative error message and a new
Xprompt -- make liberal use of '@q[?]' and ESC to feel your way through the
Xcommands.  One important command is "help" -- you should use it the first time
Xyou run C-Kermit.
X
XInteractive C-Kermit accepts commands from files as well as from the
Xkeyboard.  When you enter interactive mode, C-Kermit looks for the file
X@q(.kermrc) in your home or current directory (first it looks in the home
Xdirectory, then in the current one) and executes any commands it finds there.
XThese commands must be in interactive format, not Unix command-line format.
XA "take" command is also provided for use at any time during an interactive
Xsession.  Command files may be nested to any reasonable depth.
X
XHere is a brief list of C-Kermit interactive commands:
X@begin(format,spread 0)
X@tabclear()@tabset(1.5inches,2.0inches,2.5inches)
X@>!@\  Execute a Unix shell command.
X@>bye@\  Terminate and log out a remote Kermit server.
X@>close@\  Close a log file.
X@>connect@\  Establish a terminal connection to a remote system.
X@>cwd@\  Change Working Directory.
X@>dial@\  Dial a telephone number.
X@>directory@\  Display a directory listing.
X@>echo@\  Display arguments literally.
X@>exit@\  Exit from the program, closing any open logs.
X@>finish@\  Instruct a remote Kermit server to exit, but not log out.
X@>get@\  Get files from a remote Kermit server.
X@>help@\  Display a help message for a given command.
X@>log@\  Open a log file -- debugging, packet, session, transaction.
X@>quit@\  Same as 'exit'.
X@>receive@\  Passively wait for files to arrive.
X@>remote@\  Issue file management commands to a remote Kermit server.
X@>script@\  Execute a login script with a remote system.
X@>send@\  Send files.
X@>server@\  Begin server operation.
X@>set@\  Set various parameters.
X@>show@\  Display values of 'set' parameters.
X@>space@\  Display current disk space usage.
X@>statistics@\  Display statistics about most recent transaction.
X@>take@\  Execute commands from a file.
X@end(format)
X
XThe 'set' parameters are:
X@begin(format,spread 0)
X@tabclear()@tabset(1.5inches,2.0inches,2.5inches)
X@>block-check@\  Level of packet error detection.
X@>delay@\  How long to wait before sending first packet.
X@>duplex@\  Specify which side echoes during 'connect'.
X@>end-of-packet@\  Terminator for outbound packets.
X@>escape-character@\  Character to prefix "escape commands" during 'connect'.
X@>file@\  Set various file parameters.
X@>flow-control@\  Communication line full-duplex flow control.
X@>handshake@\  Communication line half-duplex turnaround character.
X@>line@\  Communication line device name.
X@>modem-dialer@\  Type of modem-dialer on communication line.
X@>packet-length@\  Maximum length for packets.
X@>pad-character@\  Character to use for inter-packet padding.
X@>padding@\  How much inter-packet padding to use.
X@>parity@\  Communication line character parity.
X@>prompt@\  Change the C-Kermit program's prompt.
X@>speed@\  Communication line speed.
X@>start-of-packet@\  Control character to mark beginning of packets.
X@>timeout@\  Timer interval to detect lost packets.
X@end(format)
X
XThe 'remote' commands are:
X@begin(format,spread 0)
X@tabclear()@tabset(1.5inches,2.0inches,2.5inches)
X@>cwd@\  Change remote working directory.
X@>delete@\  Delete remote files.
X@>directory@\  Display a listing of remote file names.
X@>help@\  Request help from a remote server.
X@>host@\  Issue a command to the remote host in its own command language.
X@>space@\  Display current disk space usage on remote system.
X@>type@\  Display a remote file on your screen.
X@>who@\  Display who's logged in, or get information about a user.
X@end(format)
X
XMost of these commands are described adequately in the Kermit User Guide.
XSpecial aspects of certain Unix Kermit commands are described below.
X
X@heading<The 'send' command>
X
XSyntax:  @q<send >@i(fn)@q<@ @ - >@i<or>@q< -@ @ >@q<send >@i(fn1)@q< >@i<rfn1>
X
XSend the file or files denoted by fn to the other Kermit, which should be
Xrunning as a server, or which should be given the 'receive' command.  Each
Xfile is sent under its own name (as described above, or as
Xspecified by the 'set file names' command).  If the second form is used,
Xi.e. with @i(fn1) denoting a single Unix file, @i(rfn1) may be specified as a
Xname to send it under.  The 'send' command may be abbreviated to 's', even
Xthough 's' is not a unique abbreviation for a top-level C-Kermit command.
X
XThe wildcard (meta) characters '@q[*]' and '@q[?]' are accepted in @i(fn).  If
X'@q[?]' is to be included, it must be prefixed by '@q[\]' to override its
Xnormal function of providing help.  '@q[*]' matches any string, '@q[?]' matches
Xany single character.  Other notations for file groups, like '@q([a-z]og)', are
Xnot available in interactive commands (though of course they are available on
Xthe command line).  When @i(fn) contains '@q[*]' or '@q[?]' characters, there
Xis a limit to the number of files that can be matched, which varies from system
Xto system.  If you get the message "Too many files match" then you'll have to
Xmake a more judicious selection.  If @i(fn) was of the form
X@example(usr/longname/anotherlongname/*)
Xthen C-Kermit's string space will fill up rapidly -- try doing a cwd (see
Xbelow) to the path in question and reissuing the command.
X
X@i<Note> -- C-Kermit sends only from the current or specified directory.
XIt does not traverse directory trees.  If the source directory contains
Xsubdirectories, they will be skipped.  Conversely, C-Kermit does not create
Xdirectories when receiving files.  If you have a need to do this, you can
Xpipe tar through C-Kermit, as shown in the example on page @pageref(-uxtar),
Xor under System III/V Unix you can use cpio.
X
X@i<Another Note> -- C-Kermit does not skip over "invisible" files that match
Xthe file specification; Unix systems usually treat files whose names start
Xwith a dot (like @q(.login), @q(.cshrc), and @q(.kermrc)) as invisible.
X
X@heading<The 'receive' command>
X
XSyntax:  @q<receive@ @ - >@i<or>@q< -@ @ receive >@i<fn1>
X
XPassively wait for files to arrive from the other Kermit, which must be given
Xthe 'send' command -- the 'receive' command does not work in conjunction with a
Xserver (use 'get' for that).  If @i(fn1) is specified, store the first incoming
Xfile under that name.  The 'receive' command may be abbreviated to 'r'.
X
X
X@heading<The 'get' command:>
X
XSyntax:@q<  get >@i<rfn>
X@begin(example)
X    @i<or>: get
X            @i(rfn)
X            @i(fn1)
X@end(example)
XRequest a remote Kermit server to send the named file or files.  Since a
Xremote file specification (or list) might contain spaces, which normally
Xdelimit fields of a C-Kermit command, an alternate form of the command is
Xprovided to allow the inbound file to be given a new name: type 'get' alone
Xon a line, and you will be prompted separately for the remote and local
Xfile specifications, for example
X@Begin(Example)
XC-Kermit>@ux(get)
X Remote file specification: @ux(foo)
X Local name to store it under: @ux(bar)
X@End(Example)
XAs with 'receive', if more than one file arrives as a result of the 'get'
Xcommand, only the first will be stored under the alternate name given by
X@i(fn1); the remaining files will be stored under their own names if possible.
XIf a '@q[?]' is to be included in the remote file specification, you must
Xprefix it with '@q[\]' to suppress its normal function of providing help.
X
X@heading(The 'server' command:)
X
XThe 'server' command places C-Kermit in "server mode" on the currently selected
Xcommunication line.  All further commands must arrive as valid Kermit packets
Xfrom the Kermit on the other end of the line.  The Unix Kermit server can
Xrespond to the following commands:
X@begin(format,spread 0,above 1,below 1)
X@tabclear()@tabset(2.25inches)
X@u<Command>@\@ux<Server Response>
X  get@\  Sends files
X  send@\  Receives files
X  bye@\  Attempts to log itself out
X  finish@\  Exits to level from which it was invoked
X  remote directory@\  Sends directory lising
X  remote delete@\  Removes files
X  remote cwd@\  Changes working directory
X  remote type@\  Sends files to your screen
X  remote space@\  Reports about its disk usage
X  remote who@\  Shows who's logged in
X  remote host@\  Executes a Unix shell command
X  remote help@\  Lists these capabilities
X@end(format)
XNote that the Unix Kermit server cannot always respond to a BYE command.
XIt will attempt to do so using "@q<kill(0,9)>", but this will not work
Xon all systems or under all conditions.  For instance, the C-Shell
Xchanges your process group, so that the process id of 0 does not refer
Xto what you might expect.
X
XIf the Kermit server is directed at an external line (i.e. it is in
X"local mode") then the console may be used for other work if you have
X'set file display off'; normally the program expects the
Xconsole to be used to observe file transfers and enter status queries or
Xinterruption commands.  The way to get C-Kermit into background operation from
Xinteractive command level varies from system to system (e.g. on Berkeley Unix
Xyou would halt the program with @q(^Z) and then use the C-Shell 'bg' command to
Xcontinue it in the background).  The more common method
Xis to invoke the program with the desired command line arguments, including
X"@q(-q)", and with a terminating "@q(&)".
X
XWhen the Unix Kermit server is given a 'remote host' command, it executes it
Xusing the shell invoked upon login to the remote system, e.g. the Bourne shell
Xor the Berkeley C-Shell.  (Note -- this is in distinction to the local "@q<!>"
Xshell escape, which always uses the Bourne shell; see below).
X
X@Heading(The 'remote', 'bye', and 'finish' commands:)
X
XC-Kermit may itself request services from a remote Kermit server.  In
Xaddition to the 'send' and 'get' commands, the following may also be used:
X
X@begin(description,leftmargin +8,indent -4)
Xremote cwd [@i(directory)]@\If the optional remote directory specification is
Xincluded, you will be prompted on a separate line for a password, which will
Xnot echo as you type it.
X@end(description)
X@begin(description,leftmargin +28, indent -24,spread 0,above 1)
Xremote delete rfn@\delete remote file or files.
X
Xremote directory [@i(rfn)]@\directory listing of remote files.
X
Xremote host @i(command)@\command in remote host's own command language.
X
Xremote space@\disk usage report from remote host.
X
Xremote type [@i(rfn)]@\display remote file or files on the screen.
X
Xremote who [@i(user)]@\display information about who's logged in.
X
Xremote help@\display remote server's capabilities.
X@end(description)
X@begin(description,leftmargin +8,indent -4)
Xbye @i(and) finish:@\When connected to a remote Kermit server, these commands
Xcause the remote server to terminate; 'finish' returns it to Kermit or system
Xcommand level (depending on the implementation or how the program was invoked);
X'bye' also requests it to log itself out.
X@end(description)
X@heading(The 'log' and 'close' commands:)
X
XSyntax: @q<log {debugging, packets, session, transactions} >[ @i(fn1) ]
X
XC-Kermit's progress may be logged in various ways.  The 'log' command
Xopens a log, the 'close' command closes it.  In addition, all open logs
Xare closed by the 'exit' and 'quit' commands.  A name may be specified for
Xa log file; if the name is omitted, the file is created with a default
Xname as shown below.
X
X@begin(description,leftmargin +4,indent -4)
Xlog debugging@\This produces a voluminous log of the internal workings of
XC-Kermit, of use to Kermit developers or maintainers in tracking down suspected
Xbugs in the C-Kermit program.  Use of this feature dramatically slows down the
XKermit protocol.  Default name: @q(debug.log).
X
Xlog packets@\This produces a record of all the packets that go in and out of
Xthe communication port.  This log is of use to Kermit maintainers who are
Xtracking down protocol problems in either C-Kermit or any Kermit that
XC-Kermit is connected to.  Default name:  @q(packet.log).
X
Xlog session@\This log will contain a copy of everything you see on your screen
Xduring the 'connect' command, except for local messages or interaction with
Xlocal escape commands.  Default name:  @q(session.log).
X
Xlog transactions@\The transaction log is a record of all the files that were
Xsent or received while transaction logging was in effect.  It includes time
Xstamps and statistics, filename transformations, and records of any
Xerrors that may have occurred.  The transaction log allows you to have
Xlong unattended file transfer sessions without fear of missing some
Xvital screen message.  Default name:  @q(transaction.log).
X@end(description)
XThe 'close' command explicitly closes a log, e.g. 'close debug'.
X
X@Heading(Local File Management Commands:)
X
XUnix Kermit allows some degree of local file management from interactive
Xcommand level:
X@begin(description,leftmargin +4,indent -4)
Xdirectory [@i(fn)]@\
XDisplays a listing of the names, modes, sizes, and dates of files
Xmatching @i(fn) (which defaults to '@q[*]').  Equivalent to '@q(ls -l)'.
X
Xcwd [directory-name]@\
XChanges Kermit's working directory to the one given, or to the your
Xdefault directory if the directory name is omitted.  Equivalent to 'cd'.
X
Xspace@\
XDisplay information about disk space and/or quota in the current
Xdirectory and device.
X
X@q(! )@i(command)@\
XThe command is executed by the Unix shell.  Use this for all other
Xfile management commands.  This command has certain peculiarities:
X@begin(itemize,spread 0)	
XThe Bourne shell is used.
X
XAt least one space must separate the '!' from the shell command.
X
XA 'cd' command executed in this manner will have no effect -- use the
XC-Kermit 'cwd' command instead. 
X@end(itemize)
X@end(description)
X
X@heading(The 'set' and 'show' Commands:)
X
XSince Kermit is designed to allow diverse systems to communicate, it is
Xoften necessary to issue special instructions to allow the program to adapt
Xto peculiarities of the another system or the communication path.  These
Xinstructions are accomplished by the 'set' command.  The 'show' command may
Xbe used to display current settings.  Here is a brief synopsis of settings
Xavailable in the current release of C-Kermit:
X
X@begin(description,leftmargin +4,indent -4)
Xblock-check {1, 2, 3}@\ Determines the level of per-packet error detection.
X"1" is a single-@|character 6-bit checksum, folded to include the values of all
Xbits from each character.  "2" is a 2-character, 12-bit checksum.  "3" is a
X3-character, 16-bit cyclic redundancy check (CRC).  The higher the block check,
Xthe better the error detection and correction and the higher the resulting
Xoverhead.  Type 1 is most commonly used; it is supported by all Kermit
Ximplementations, and it has proven adequate in most circumstances.  Types 2 or
X3 would be used to advantage when transferring 8-bit binary files over noisy
Xlines.
X
Xdelay @i(n)@\How many seconds to wait before sending the first packet after a
X'send' command.  Used in remote mode to give you time to escape back to your
!FUNKY!STUFF!

gregg@okstate.UUCP (03/08/85)

echo x - ckermi.mss part 2
sed '1,$s/^X//' <<\!FUNKY!STUFF! >> ckermi.mss
Xlocal Kermit and issue a 'receive' command.  Normally 5 seconds.
X
Xduplex {full, half}@\For use during 'connect'.  Specifies which side is doing
Xthe echoing; 'full' means the other side, 'half' means C-Kermit must echo
Xtypein itself.
X
Xend-of-packet @i(cc)@\Specifies the control character needed by the other
XKermit to recognize the end of a packet.  C-Kermit sends this character at the
Xend of each packet.  Normally 13 (carriage return), which most Kermit
Ximplementations require.  Other Kermits require no terminator at all, still
Xothers may require a different terminator, like linefeed (10).
X
Xescape-character @i(cc)@\For use during 'connect' to get C-Kermit's attention.
XThe escape character acts as a prefix to an 'escape command', for instance to
Xclose the connection and return to C-Kermit or Unix command level.
XThe normal escape character is Control-Backslash (28).
XThe escape character is also used in System III/V implementations,
Xto prefix interrupt commands during file transfers.
X
Xfile {display, names, type, warning}@\
XEstablish various file-related parameters:
X@begin(description,leftmargin +4,indent -4)
Xdisplay {on, off}@\Normally 'on'; when in local mode, display progress of file
Xtransfers on the screen (stdout), and listen to the keyboard (stdin)
Xfor interruptions.  If off (-q on command line) none of this is
Xdone, and the file transfer may proceed in the background oblivious
Xto any other work concurrently done at the console terminal.
X
Xnames {converted, literal}@\
XNormally converted, which mean that outbound filenames have path
Xspecifications stripped, lowercase letters raised to upper,
Xtildes and extra periods changed to X's, and an X inserted in
Xfront of any name that starts with period.  Incoming files have
Xuppercase letters lowered.  Literal means that none of these
Xconversions are done; therefore, any directory path appearing in a
Xreceived file specification must exist and be write-accessible.
XWhen literal naming is being used, the sender should not use path
Xnames in the file specification unless the same path exists on the
Xtarget system and is writable.
X
Xtype {binary, text}@\Normally text, which means that conversion is done between
XUnix newline characters and the carriage-@|return/@|linefeed sequences
Xrequired by the canonical Kermit file transmission format, and in
Xcommon use on non-@|Unix systems.  Binary means to transmit file
Xcontents without conversion.  Binary ('@q(-i)' in command line notation)
Xis necessary for binary files, and desirable in all Unix-@|to-@|Unix
Xtransactions to cut down on overhead.
X
Xwarning {on, off}@\Normally off, which means that incoming files will silently
Xoverwrite existing files of the same name.  When on ('@q(-w)' on command line)
XKermit will check if an arriving file would overwrite an existing file; if so,
Xit will construct a new name for the arriving file, of the form @q(foo~)@i(n),
Xwhere foo is the name they share and @i(n) is a "generation number"; if @i(foo)
Xexists, then the new file will be called @q(foo~1).  If @q(foo) and @q(foo~1)
Xexist, the new file will be @q(foo~2), and so on.
X@end(description)
X
Xflow-control {none, xon/xoff}@\Normally xon/xoff for full duplex flow control.
XShould be set to 'none' if the other system cannot do xon/xoff flow control.
X
Xhandshake {xon, xoff, cr, lf, bell, esc, none}@\
XNormally none.  Otherwise, half-duplex communication line turnaround
Xhandshaking is done, which means Unix Kermit will not reply to a packet
Xuntil it has received the indicated handshake character or has timed out
Xwaiting for it.
X
Xline [device-name]@\
XThe device name for the communication line to be used for file transfer
Xand terminal connection, e.g. @q(/dev/ttyi3).  If you specify a device name,
XKermit will be in local mode, and you should remember to issue any other
Xnecessary 'set' commands, such as 'set speed'.  If you omit the device
Xname, Kermit will revert to its default mode of operation.
X
Xmodem-dialer {direct, hayes, ventel}@\ The type of modem dialer on the
Xcommunication line.  "Direct" indicates either there is no dialout modem, or
Xthat if the line requires carrier detection to open, then 'set line' will hang
Xwaiting for an incoming call.  "Hayes" and "Ventel" indicate that the
Xsubsequent 'set line' (or the -l argument) will prepare for a subsequent 'dial'
Xcommand for Hayes and Ventel dialers, respectively.
X
Xpacket-length @i(n)@\Specify the maximum packet length to use.  Normally 90.
XShorter packet lengths can be useful on noisy lines, or with systems or front
Xends or networks that have small buffers.  The shorter the packet, the higher
Xthe overhead, but the lower the chance of a packet being corrupted by noise,
Xand the less time to retransmit corrupted packets.
X
Xpad-character @i(cc)@\C-Kermit normally does not need to have incoming packets
Xpreceded with pad characters.  This command allows C-Kermit to request the
Xother Kermit to use cc as a pad character.  Default cc is NUL, ASCII 0.
X
Xpadding @i(n)@\How many pad characters to ask for, normally 0.
X
Xparity {even, odd, mark, space, none}@\Specify character parity for use in
Xpackets and terminal connection, normally none.  If other than none, C-Kermit
Xwill seek to use the 8th-bit prefixing mechanism for transferring 8-bit binary
Xdata, which can be used successfully only if the other Kermit agrees; if not,
X8-bit binary data cannot be successfully transferred.
X
Xprompt [string]@\The given string will be substituted for "@q(C-Kermit)>" as
Xthis program's prompt.  If the string is omitted, the prompt will revert to
X"@q(C-Kermit>)".
X
Xspeed {0, 110, 150, 300, 600, 1200, 1800, 2400, 4800, 9600}@\The baud rate for
Xthe external communication line.  This command cannot be used to change the
Xspeed of your own console terminal.  Many Unix systems are set up in such a way
Xthat you must give this command after a 'set line' command before you can use
Xthe line.
X
Xstart-of-packet @i(cc)@\The Kermit packet prefix is Control-A (1).  The only
Xreasons it should ever be changed would be: Some piece of equipment somewhere
Xbetween the two Kermit programs will not pass through a Control-A; or, some
Xpiece of of equipment similarly placed is echoing its input.  In the latter
Xcase, the recipient of such an echo can change the packet prefix for outbound
Xpackets to be different from that of arriving packets, so that the echoed
Xpackets will be ignored.  The opposite Kermit must also be told to change the
Xprefix for its inbound packets.  Unix Kermit presently can be told to change
Xonly its outbound packet prefix.
X
Xtimeout @i(n)@\Normally, each Kermit partner sets its packet timeout interval
Xbased on what the opposite Kermit requests.  This command allows you to
Xoverride the normal procedure and specify a timeout interval.  If you specify
X0, then no timeouts will occur, and Unix Kermit will wait forever for expected
Xpackets to arrive.
X@end(description)
X
X@heading(The 'show' Command:)
X
XSyntax: @q<show {parameters, versions}>
X
XThe show command displays the values of all the 'set' parameters described
Xabove.  If you type 'show versions', then C-Kermit will display the version
Xnumbers and dates of all its internal modules.  You should use the 'show
Xversions' command to ascertain the vintage of your Kermit program before
Xreporting problems to Kermit maintainers.
X
X@heading(The 'statistics' Command:)
X
XThe statistics command displays information about the most recent Kermit
Xprotocol transaction, including file and communication line i/o, as well
Xas what encoding options were in effect (such as 8th-bit prefixing,
Xrepeat-@|count compression).
X
X@heading(The 'take' and 'echo' Commands:)
X
XSyntax: @q<take >@i<fn1>
X
XThe 'take' command instructs C-Kermit to execute commands from the named
Xfile.  The file may contain any interactive C-Kermit commands, including
X'take'; command files may be nested to any reasonable depth.  The 'echo'
Xcommand may be used within command files to issue greetings, announce
Xprogress, etc.
X
XCommand files are in exactly the same syntax as interactive commands.
XNote that this implies that if you want to include special characters like
Xquestion mark or backslash that you would have to quote with backslash when
Xtyping interactive commands, you must quote these characters the same way
Xin command files.
X
XCommand files may be used in lieu of command macros, which have not been
Ximplemented in this version of C-Kermit.  For instance, if you commonly
Xconnect to a system called 'B' that is connected to ttyh7 at 4800 baud,
Xyou could create a file called @q(b) containing the commands
X@begin(example)
Xset line /dev/ttyh7
Xset speed 4800
Xecho Connecting to System B...
Xconnect
X@end(example)
Xand then simply type 'take b' (or 't b' since no other commands begin with
Xthe letter 't') whenever you wished to connect to system B.
X
XFor connecting to IBM mainframes, a number of 'set' commands are required;
Xthese, too, are conveniently collected into a 'take' file like this one:
X@begin(example)
Xset speed 1200
Xset parity mark
Xset handshake xon
Xset flow-control none
Xset duplex half
X@end(example)
X
XAn implicit 'take' command is executed upon your @q(.kermrc) file upon
XC-Kermit's initial entry into interactive dialog.  The @q(.kermrc) file should
Xcontain 'set' or other commands you want to be in effect at all times.
XFor instance, you might want override the default action when incoming files
Xhave the same names as existing files -- in that case, put the command
X@example(set file warning on)
Xin your @q(.kermrc) file.
X
XCommands executed from take files are not echoed at the terminal.  If you
Xwant to see the commands as well as their output, you could feed the
Xcommand file to C-Kermit via redirected stdin, as in
X@example('kermit < cmdfile')
XErrors encountered during execution of take files (such as failure to complete
Xdial or script operations) cause termination of the current take file, popping
Xto the take file that invoked it, or to interactive level.  When kermit is
Xexecuted in the background, errors during execution of a take file are fatal.
X
X@heading(The 'connect' Command:)
X
XThe connect command links your terminal to another computer as if it were
Xa local terminal to that computer, through the device specified in the most
Xrecent 'set line' command, or through the default device if your system
Xis a PC or workstation.  All characters you type at your keyboard
Xare sent out the communication line, all characters arriving at the
Xcommunication port are displayed on your screen.  Current settings of speed,
Xparity, duplex, and flow-@|control are honored.  If you have issued a 'log
Xsession' command, everything you see on your screen will also be recorded
Xto your session log.  This provides a way to "capture" files from systems
Xthat don't have Kermit programs available.
X
XTo get back to your own system, you must type the escape character, which is
XControl-@|Backslash (@q[^\]) unless you have changed it with the 'set escape'
Xcommand, followed by a single-@|character command, such as 'c' for "close
Xconnection".  Single-@|character commands include:
X@begin(description,leftmargin +8,indent -6,spread 0.4)
Xc@\Close the connection
X
Xb@\Send a BREAK signal
X
X0@\(zero) send a null
X
Xs@\Give a status report about the connection
X
X@q[^\]@\Send Control-Backslash itself (whatever you have defined the
Xescape character to be, typed twice in a row sends one copy of it).
X@end(description)
XLowercase and control equivalents for these letters are also accepted.  A
Xspace typed after the escape character is ignored.  Any other character will
Xproduce a beep.
X
XThe connect command simply displays incoming characters on the screen.  It is
Xassumed any screen control sequences sent by the host will be handled by
Xthe firmware in your terminal or PC.  If terminal emulation is desired,
Xthen the connect command can invoked from the Unix command line (@q(-c) or
X@q(-n)), piped through a terminal emulation filter, e.g.
X@example(kermit -l /dev/acu -b 1200 -c | tek)
X'c' is an acceptable non-unique abbreviation for 'connect'.
X
X@heading(The 'dial' command:)
X
XSyntax: @q(dial )@i(telephone-number-string)
X
XThis command controls dialout modems.  The telephone-@|number-@|string may
Xcontain modem-@|dialer commands, such as comma for Hayes pause, or '@q(&)'
Xfor Ventel dialtone wait and '@q(%)' for Ventel pause.
X
XBecause modem dialers have strict requirements to override the carrier-@|detect
Xsignal most Unix implementations expect, the sequence for dialing is more rigid
Xthan with the rest of kermit's features.
X
XExample one:
X@begin(example)
X@ux<kermit -l /dev/cul0 -b 1200>
XC-Kermit>@ux<set modem-dialer hayes>	@i(hint: abbreviate) set m h
XC-Kermit>@ux<dial 9,5551212>
XConnected!
XC-Kermit>@u<connect>			@i(hint: abbreviate) c
X@i(logon, request remote server, etc.)
XC-Kermit> ...
XC-Kermit>@u<quit>			@i(hint: abbreviate) q
X@end(example)
Xthis disconnects modem, and unlocks line.
X
XExample two:
X@begin(example)
X@u(kermit)
XC-Kermit>@ux(set modem-dialer ventel)
XC-Kermit>@ux(set line /dev/cul0)
XC-Kermit>@ux(dial 9&5551212%)
XConnected!
XC-Kermit> ...
X@end(example)
XExample three:
X@begin(example)
Xkermit
XC-Kermit>@ux(take my-dial-procedure)
XConnected!
X
X@i(file my-dial-procedure):
Xset modem hayes
Xset line /dev/tty99
Xdial 5551212
Xconnect
X@end(example)
XFor Hayes dialers, two important switch settings are #1 and #6.  #1
Xshould be up so that the DTR is only asserted when the line is 'open'.
X#6 should be up so carrier-@|detect functions properly.  Switches #2
X(English versus digit result codes) and #4 (Hayes echoes modem commands)
Xmay be in either position.
X
X@heading(The 'script' Command:)
X
XSyntax: @q(script )@i(expect send [expect send] . . .)
X
X"expect" has the syntax: @i(expect[-send-expect[-send-expect[...]]])
X
XThis command facilitates logging into a remote system and/or invoking
Xprograms or other facilities after login on a remote system.
X
XThis login script facility operates in a manner similar to that commonly
Xused by the Unix uucp System's "@q(L.sys)" file entries.  A login script 
Xis a sequence of the form:
X@example(@i<expect send [expect send] . . .>)
Xwhere @i(expect) is a prompt or message to be issued by the remote site, and
X@i(send) is the string (names, numbers, etc) to return.  The send may also be
Xthe keyword EOT, to send Control-D, or BREAK, to send a break signal.  Letters
Xin send may be prefixed by '@q[~]' to send special characters.  These are:
X@begin(description,leftmargin +8,indent -4,spread 0)
X@q(~b)@\backspace
X
X@q(~s)@\space
X
X@q(~q)@\'@q[?]'(trapped by Kermit's command interpreter)
X
X@q(~n)@\linefeed
X
X@q(~r)@\carriage return
X
X@q(~t)@\tab
X
X@q(~')@\single quote
X
X@q(~~)@\tilde
X
X@q(~")@\double quote
X
X@q(~c)@\don't append a carriage return
X
X@q(~)@i(o[o[o]])@\an octal character
X@end(description)
XAs with some uucp systems, sent strings are followed by @q(~r) unless they have
Xa @q(~c).
X
XOnly the last 7 characters in each expect are matched.  A null @i(expect),
Xe.g. @q(~0) or two adjacent dashes, causes a short delay before proceeding
Xto the next send sequence.  A null expect always succeeds.
X
XAs with uucp, if the expect string does not arrive, the script attempt
Xfails.  If you expect that a sequence might not arrive, as with uucp, 
Xconditional sequences may be expressed in the form:
X@example(@i<-send-expect[-send-expect[...]]>)
Xwhere dashed sequences are followed as long as previous expects fail.
X
X@i(Expect/send) transactions can be easily be debugged by logging
Xtransactions.  This records all exchanges, both expected and actual.
X
XNote that '@q[\]' characters in login scripts, as in any other C-Kermit
Xinteractive commands, must be doubled up.
X
XExample one:
X
XUsing a modem, dial a unix host site.  Expect "login" (...gin), and if it
Xdoesn't come, simply send a null string with a @q(~r).  (Some Unixes
Xrequire either an EOT or a BREAK instead of the null sequence, depending
Xon the particular site's "logger" program.)  After providing user id
Xand password, respond "x" to a question-mark prompt, expect the Bourne
Xshell "@q($)" prompt (and send return if it doesn't arrive).  Then cd to
Xdirectory kermit, and run the program called "wermit", entering the
Xinteractive connect state after wermit is loaded.
X@begin(example)
Xset modem-dialer ventel
Xset line /dev/tty77
Xset baud 1200
Xdial 9&5551212
Xscript gin:--gin:--gin: smith ssword: mysecret ~q x $--$ 
X	cd~skermit $ wermit
Xconnect
X@end(example)
X
XExample two:
X
X@index(TELENET)
XUsing a modem, dial the Telenet network.  This network expects three
Xreturns with slight delays between them.  These are sent following
Xnull expects.  The single return is here sent as a null string, with
Xa return appended by default.  Four returns are sent to be safe before
Xlooking for the prompt.  Then the telenet id and password are entered.
XThen telenet is instructed to connect to a host site (c 12345).  The
Xhost has a data switch, and to "which system" it responds "myhost".
XThis is followed by a TOPS-20 logon, and a request to load Kermit,
Xset even parity, and enter the server mode.  Files
Xare then exchanged.  The commands are in a take file.  The
Xlogin command is split onto two lines for readability, though it
Xis a single long line in the take file.
X@begin(example)
Xset modem-dialer hayes
Xset line /dev/cul0
Xset baud 1200
Xdial 9,5551212
Xset parity even
Xscript ~0 ~0 ~0 ~0 ~0 ~0 ~0 ~0 @@--@@--@@ id~saa001122 = 002211 @@ 
X    c~s12345 ystem-c~s12345-ystem myhost @@ joe~ssecret @@ kermit 
X    > set~sparity~seven > server
Xsend some.stuff
Xget some.otherstuff
Xbye
Xquit
X@end(example)
XSince these commands may be executed totally in the background, they
Xcan also be scheduled.  A typical shell script, which might be scheduled
Xby cron, would be as follows (csh used for this example):
X
X@begin(example)
X#
X#keep trying to dial and log onto remote host and exchange files
X#wait 10 minutes before retrying if dial or script fail.
X# 
Xwhile ( 1 )
X	kermit < tonight.cmd &
X	if ( ! $status ) break
X	sleep 600
Xend
X@end(example)
XFile tonight.cmd might have two takes in it, for example, one to take
Xa file with the set modem, set line, set baud, dial, and script, and
Xa second take of a file with send/get commands for the remote server.
XThe last lines of @q(tonight.cmd) should be a bye and a quit.
X
X@heading(The 'help' Command:)
X
X@begin(example,leftmargin 0)
X@r(Syntax:) help
X@ @ @ @i(or): help @i(keyword)
X@ @ @ @i(or): help {set, remote} @i(keyword)
X@end(example)
X
XBrief help messages or menus are always available at interactive command
Xlevel by typing a question mark at any point.  A slightly more verbose form
Xof help is available through the 'help' command.  The 'help' command with
Xno arguments prints a brief summary of how to enter commands and how to
Xget further help.  'help' may be followed by one of the top-level C-Kermit
Xcommand keywords, such as 'send', to request information about a command.
XCommands such as 'set' and 'remote' have a further level of help.  Thus you
Xmay type 'help', 'help set', or 'help set parity'; each will provide a
Xsuccessively more detailed level of help.
X
X
X@heading(The 'exit' and 'quit' Commands:)
X
XThese two commands are identical.  Both of them do the following:
X
X@begin(itemize,spread 0)
XAttempt to insure that the terminal is returned to normal.
X
XRelinquish access to any communication line assigned via 'set line'.
X
XClose any open log files.
X
XRelinquish any uucp and multiuser locks on the communications line.
X
XHang up the modem, if the communications line supports data terminal ready.
X@end(itemize)
XAfter exit from C-Kermit, your default directory will be the same as when
Xyou started the program.  The 'exit' command is issued implicitly whenever
XC-Kermit halts normally, e.g. after a command line invocation, or after certain
Xkinds of interruptions.
X
X@section(C-Kermit under Berkeley or System III/V Unix:)
X
XC-Kermit may be interrupted at command level or during file transfer by
Xtyping Control-C.  The program will perform its normal exit function,
Xrestoring the terminal.  If a protocol transaction was in progress, an
Xerror packet will be sent to the opposite Kermit so that it can terminate
Xcleanly.
X
XC-Kermit may be invoked in the background ("&" on shell commmand line).
XIf a background process is "killed", the user will have to manually
Xremove any lock file and may need to restore the modem.  This is because 
Xthe kill signal (@q<kill(@i[x],9)>) cannot be trapped by Kermit.
X
XDuring execution of a system command, C-Kermit can often be returned to
Xcommand level by typing a single Control-C.  (With System III/V, the
Xusual interrupt function (often the DEL key) is replaced by Control-C.)
XOn detecting Control-C, C-Kermit takes its normal exit, removing
Xlock files and restoring the communication line, modem, and/or console
Xterminal.
X
X@begin(description,leftmargin +4, indent -4)
XUnder Berkeley Unix only:@\
XC-Kermit may also be interrupted by ^Z to put the process in the background.
XIn this case the terminal is not restored.  You will have to type
XControl-J followed by "reset" followed by another Control-J to get your
Xterminal back to normal.  C-Kermit can be halted in a similar manner by
Xtyping Control-@\Backslash, except that instead of moving it to the
Xbackground, a core dump is produced.
X
XUnder System III/V Unix:@\
XThe Control-@q(\) character (or whatever control character has been
Xselected via "set escape") at the C-Kermit command level
Xis ignored; it is trapped and will not core-@|dump or interrupt Kermit.
X@end(description)
X
XControl-C, Control-Z, and Control-@q(\) lose their normal functions during
Xterminal connection and also during file transfer when the controlling tty
Xline is being used for packet i/o.
X
XThe BSD implementation of C-Kermit has code to take advantage of a special
Xnonstandard kernel-mode line driver, which boosts the speed of packet i/o
Xsignificantly.  The problem is that "raw" mode, needed for packet i/o, also
Ximplies "cbreak" (character wakeup) mode, which is very expensive.  The new
Xline driver is a modification of the "berknet" driver, which allowed raw mode
Xi/o to take place with process wakeup only upon receipt of a linefeed.  The
XBerknet driver, unfortunately, strips off the high order bit of each
Xcharacter and does not allow the line terminator to be specified.  The
Xmodification allows all 8 bits to pass through unmolested, allows the wakeup
Xcharacter to be specified, and allows the buffer to be tested or cleared.
X
XThe System III/V implementation uses regular kernel drivers, but "gulps"
Xrawmode input in large blocks, thus overcomming the usual system call
Xoverheads.
X
XIf you are running C-Kermit in "quiet mode" in the foreground, then
Xinterrupting the program with a console interrupt like Control-C will not
Xrestore the terminal to normal conversational operation.  This is because
Xthe system call to enable console interrupt traps will cause the program to
Xblock if it's running in the background, and the primary reason for quiet
Xmode is to allow the program to run in the background without blocking, so
Xthat you can do other work in the foreground.
X
XIf C-Kermit is run in the background ("&" on shell commmand line), then
Xthe interrupt signal (Control-C) (and System III/V quit signal) are
Xignored.  This prevents an interrupt signal intended for a foreground
Xjob (say a compilation) from being trapped by a background Kermit session.
X
X
X@section(C-Kermit on the DEC Pro-3xx with Venix 1@q(.)0)
X
XThe DEC Professional 300 series are PDP-11/23 based personal computers.
XVenix 1@q(.)0 is a Unix v7 derivative.  It should not be confused with
XVenix 2@q(.)0, which resembles ATT System V.
XC-Kermit runs in local mode on the Pro-3@i(xx) when invoked from the
Xconsole; the default device is @q(/dev/com1).  When connected to a
Xremote system (using C-Kermit's 'connect' command), Pro/Venix itself (not
XKermit) provides VT52 terminal emulation.
X
XDuring file transfer, the interruption and status commands (Control-A,
XControl-F, etc) are not available.
X
X@section(C-Kermit Restrictions and Known Bugs)
X
X@begin(enumerate)
X@ux(File renaming):  When filename collision avoidance ("warning") is
Xselected, C-Kermit constructs unique names by appending a generation number
Xto the end of the file name.  Currently, no checking is done to ensure that
Xthe result is still within the maximum length for a file name.
X
X@ux(UUCP line locking):  C-Kermit locks lines, to prevent UUCP and
Xmultiuser conflicts, when it first opens a communications line.  This
Xoccurs either when 'set line' is issued, or if the '-l' argument is
Xused, when the first 'dial',
X'connect', or protocol operation occurs.  The lock is released if 
Xanother 'set line' is issued, or if the program quits, exits, or is
Xterminated by Control-C.
XIf a user connects and returns to the shell command level, for example
Xto initiate kermit by piped commands, on a communications line, the
Xline lock is released when returning to the shell.
XLocking is not needed, or used, if communications occur over the
Xlocal terminal line (e.g. @q[/dev/tty]).  In that case, there is no
Xdifficultly with "piped" operations releasing locks and lines.
X
X@begin(multiple)
X@ux(Removing stale lock files):
XFor various reasons, lock files sometimes get left about after
Xuucp or C-Kermit activities.  (The most common reason is that
Xthe uucp or C-Kermit activity was "killed" by a shell command.)
XIf the lock file is owned by yourself, clearly you may remove
Xit (presuming you are not running C-Kermit or uucp in the background
Xwhen you discovered it).
X
XUucp supports a function, called uuclean, which is customarily
Xused to remove these files after a predetermined age.  If in doubt
Xabout a lock file on the dial-out line you need, contact your
Xsystem's operator.
X@end(multiple)
X
X@ux(Modem controls):
XIf connection is made over a communication line (rather than on
Xthe controlling terminal line), and that line has modem controls,
X(e.g. data terminal ready and carrier detection implementation),
Xreturning to the shell level will disconnect the conversation.
XIn that case, one should use interactive mode commands, and
Xavoid use of piped shell-@|level operation (also see 'set modem-dialer'
Xand 'dial' commands.)
X
X@ux(Login Scripts):  The present login scripts implementation follows
Xthe Unix conventions of uucp's "@q(L.sys)" file, rather than the normal
XKermit "INPUT/@|OUTPUT" style.  Volunteers have indicated an intent to
Ximplement the Kermit standard for login scripts, and indeed even others
Xmay be implemented in the future as needed.
X
X@begin(multiple)
X@ux(Dial-out vs dial-in communications lines)
XC-Kermit requires a dial-out line for the "set line" or "-l" options.
XMost systems have some lines dedicated to dial-in, which they enable
X"loggers" on, and some lines available for dial-out.  Where a line
Xmust be shared between dial-in and dial-out, several options are
Xavailable (though they are, strictly speaking, outside the pervue of
XC-Kermit).
X
XA simple shell program can be used to change directionality of the line
Xif your Unix has the enable(8) and disable(8) commands.  In that
Xcase, the shell program could "grep" a "who" to see if anybody is
Xlogged onto the desired line;  if not, it could "disable" the line.
XThe shell program will need to be set-uID'ed to root.  The shell
Xprogram can be called from kermit prior to a dial command, e.g.,
X"! mydisable.shellprog".  Prior to the final "quit" from C-Kermit,
Xanother shell program could be executed to "enable" the line
Xagain.  This program also needs to be set-uID'ed to root.
X
XIf your Unix lacks the enable(8) and disable(8) commands, another
Xcommon technique works if your system supports the /etc/ttys file.
XA shell program could call up an awk program to find the line
Xin the file and set the enable byte to 0 (to directly disable the
Xline).  Likewise, it can be reenabled by a counterpart at the end.
XIt may be necessary to pause for 60 seconds after modifying that
Xfile before the logger sees it and actually disables the line.
X@end(multiple)
X
X@begin(multiple)
X@ux(Using C-Kermit on Local Area Networks):
XC-Kermit can successfully operate at speeds up to 9600 baud over
XLANs.  Testing has, however, shown that most processors, whether
XPC/XTs with PC/IX, or Vaxes, need flow control at these rates.
XA command, "set flow x" should be issued to each end of this form
Xof connection. 
X
XIf the LAN is the sort with an interface card (or box, like the
XSytek), then the interface card/box must be programmed to handle
Xthe flow control characters (xon and xoff) at the card/box level
X(rather than forwarding these characters to the remote site).  This
Xis because packetizing LANs will not deliver the xoff character
Xto the other end, after packetization, until long after the receive
Xbuffer has been overrun.
X@end(multiple)
X
X@ux(Resetting terminal after abnormal termination or kill): When C-Kermit
Xterminates abnormally (say, for example, by a kill
Xcommand issued by the operator) the user may need to reset the terminal state.
XIf commands do not seem to be accepted at the shell prompt, try
XControl-J "stty sane" Control-J (use "reset" on Berkeley Unix).
XThat should take the terminal out of "raw mode" if it was stuck there.
X
X@ux(Remote host commands may time-out on lengthy activity):
XUsing "remote host" to instruct the C-Kermit server to invoke Unix
Xfunctions (like "make") that might take a long time to produce output can cause
Xtimeout conditions.
X
X
X@ux(PC/IX Login Scripts -- Unfound Bug):
XThough login scripts appear to work properly on most processors, in the
Xcase of the PC/XT with PC/IX, it appears that longer scripts
Xneed to be broken up into shorter scripts (invoked sequentially from
Xthe take file).  This is because the portion of the script handler
Xwhich checks if an operation timed out seems to leave the processor
Xin a strange state (i.e. hung).
X@end(enumerate)
X
X@section(How to Build C-Kermit for a Unix System)
X
XThe C-Kermit files, as distributed from Columbia, all begin with the
Xprefix "ck".  You should make a directory for these files and then cd
Xto it.  A makefile is provided to build C-Kermit for various Unix systems.
XAs distributed, the makefile has the name "@q(ckermi.mak)".  You should
Xrename it to "@q(makefile)" and then type "make xxx", where xxx is the
Xsymbol for your system, for instance "make bsd" to make C-Kermit for
X4.2 BSD Unix.  The result will be a program called "wermit".  You should
Xtest this to make sure it works; if it does, then you can rename it to
X"kermit" and install it for general use.  See the makefile for a list of
Xthe systems supported and the corresponding "make" arguments.
X
X@section(Adapting C-Kermit to Other Systems)
X
XC-Kermit is designed for portability.  The level of portability is indicated
Xin parentheses after the module name: "C" means any system that has a C
Xcompiler that conforms to the description in "The C Programming Language" by
XKernighan & Ritchie (Prentice-Hall, 1978).  "Cf" is like "C", but also
Xrequires "standard" features like printf and fprintf, argument passing via
Xargv/argc, and so on, as described in Kernighan & Ritchie.  "Unix" means the
Xmodule should be useful under any Unix implementation; it requires features
Xsuch as fork() and pipes.  Anything else means that the module is particular
Xto the indicated system.  The modules are:
X
X@begin(description,leftmargin +4, indent -4)
X@q<ckmain.c, ckermi.h, ckdebu.h (Cf)>:@\This is the main program.  It contains
Xdeclarations for global variables and 
Xa small amount of code to initialize some variables and invoke the command
Xparser.  In its distributed form, it assumes that command line arguments are
Xpassed to it via argc and argv.  Since this portion of code is only several
Xlines long, it should be easy to replace for systems that have different
Xstyles of user interaction.  The header files define symbols and macros used
Xby the various modules of C-Kermit.  @q(ckdebu.h) is the only header file
Xthat is included by all the C-Kermit modules, so it contains not only the
Xdebug format definitions, but also any system-@|dependent typedefs.
X
X@q<wart.c (Cf), ckprot.w (C)>:@\The ckprot module embodies the Kermit protocol
Xstate table and the code to accomplish state switching.  It is written in
X"wart", a language which may be regarded as a subset of the Unix "lex" lexical
Xanalyzer generator.  Wart implements enough of lex to allow the ckprot module
Xto function.  Lex itself was not used because it is proprietary (but lex may be
Xused in place of wart with minor reformatting of @q(ckprot.w) -- removal of
X@q(#includes) and comments, which must be reinserted into lex's output C
Xprogram).  The protocol module @q(ckprot.w) is read by wart, and a
Xsystem-independent C program is produced.  The syntax of a Wart program is
Xillustrated by @q(ckprot.w), and is described in @q(ckwart.doc).
X
X@q<ckfns.c (C)>:@\The module contains all the Kermit protocol support functions
X-- packet formation, encoding, decoding, block check calculation, filename and
Xdata conversion, protocol parameter negotiation, and high-level interaction
Xwith the communication line and file system.
XTo accommodate small systems, this module has been split into two --
X@q(ckfns.c) and @q(ckfns2.c).
X
X@q(ckx???.c) (specific to system ???):@\For instance, @q(ckxunx.c)
X(Berkeley, Venix, ATT System III/V, and other Unix systems) -- The ckx
Xmodule contains the system-@|dependent primitives for communication line
Xi/o, timers, and interrupts.  Certain important variables are defined in
Xthis module, which determine whether C-Kermit is by default remote or
Xlocal, what the default communication device is, and so forth.  The ckx
Xmodule maintains its own private database of file descriptors and modes
Xfor the console terminal and the file transfer communication line so
Xthat other modules (like ckfns or the terminal connect module) need not
Xbe concerned with them.  This module will vary significantly among Unix
Ximplementations since the sgtty/@|ioctl/@|termio functions and their
Xarguments are among the least compatible areas of Unix.  The @q(ckxunx.c)
Xfile has conditional compilation for the variations of Unix.
X
X@q(ckz???.c) (specific to system ???):@\For instance, @q(ckzunx.c) (Berkeley,
XVenix, System III/V Unix) -- The ckz module contains system-dependent
Xprimitives for file i/o, wildcard (meta character) expansion, file existence
Xand access checking, and system command execution.  It maintains an internal
Xdatabase of i/o "channels" (file pointers in the case of Unix) for the files
XC-Kermit cares about -- the input file (the file which is being sent), the
Xoutput file (the file being received), the various logs, the screen, and so
Xforth.  This module will vary little among Unix implementations except for the
Xwildcard expansion code, which requires detailed knowledge of the directory
Xstructure.  The ckz module also defines variables containing the system command
Xstrings used to perform certain functions like directory listing, file
Xdeletion, etc.  The @q(ckzunx.c) file has conditional compilation for the
Xvariations of Unix.
X
X@begin(multiple)
X@q(ckuser.h, ckuser.c, ckusr2.c, ckusr3.c) (Unix):@\This is the "user
Xinterface" for C-Kermit.  It includes the command parser,
Xthe screen output functions, and console input functions.  The command
Xparser comes in two pieces -- the traditional Unix command line decoder
X(which is quite small and compact), and the interactive keyword parser
X(which is rather large).  This module is fully replacable; its interface to
Xthe other modules is very simple, and is explained at the beginning of the
Xsource file.  The ckuser module also includes code to execute any commands
Xdirectly which don't require the Kermit protocol -- local file management,
Xetc.  The module is rated "Unix" because it makes occasional use of the
Xsystem() function.
X
XNote that while ckuser is logically one module, it has been split up into
Xthree C source files, plus a header file for the symbols they share in common.
XThis is to accommodate small systems that cannot handle big modules.
X@q(ckuser.c) has the command line and top-level interactive command parser;
X@q(ckusr2.c) has the help command and strings; @q(ckusr3) has the set
Xand remote commands along with the logging, screen, and "interrupt" functions.
X@end(multiple)
X
X@q(ckcmd.c, ckcmd.h) (Cf):@\This is an interactive command parsing package
Xdeveloped for C-Kermit.
XIt is written portably enough to be usable on any system that has a C
Xcompiler that supports functions like printf.  The file name parsing
Xfunctions depend upon primitives defined in the ckz module; if these
Xprimitives cannot be supplied for a certain system, then the filename
Xparsing functions can be deleted, and the package will still be useful
Xfor parsing keywords, numbers, arbitrary text strings, and so forth.
XThe style of interaction is the same as that found on the DECSYSTEM-20.
X
X@q(ckconu.c) (Unix):@\This is the connect module.  As supplied, it should
Xoperate in any Unix environment, or any C-based environment that provides the
Xfork() function.  The module requires access to global variables that specify
Xline speed, parity, duplex, flow control, etc, but invokes functions from the
Xckx module to accomplish the desired settings and input/output, and functions
Xfrom the ckz module to perform session logging.  There is no code for
Xcontrolling modem signals.  No terminal emulation is performed, but since
Xstandard i/o is used for the console, this may be piped through a terminal
Xemulation filter.  The ckconu function may be entirely replaced, so long as
Xthe global settings are honored by its replacement.  PC implementations of
XC-Kermit may require the ckcon module to do screen control, escape sequence
Xinterpretation, etc, and may also wish to write special code to get the best
Xpossible performance.
X
X@q(ckdial.c) (Unix):@\This is the dialer module.  As supplied, it handles hayes
Xand ventel modems.
X
X@q(cklogi.c) (Unix):@\This is the login script module.  As supplied, it handles
Xuucp-@|style logins. 
X@end(description)
X
XMoving C-Kermit to a new system entails:
X@begin(enumerate)
XCreating a new ckx module in C, assembler, or whatever language is
Xmost appropriate for system programming on the new system.
X
XCreating a new ckz module, as above.
X
XIf the system is not Unix-like, then a new ckuser module may be required,
Xas well as a different invocation of it from ckmain.
X
XIf the distributed connect module doesn't work or performs poorly, then
Xit may be replaced.  For instance, interrupt-driven i/o may be required,
Xespecially if the system doesn't have forks.
X@end(enumerate)
XThose who favor a different style of user/program interaction from that
Xprovided in @q(ckuser.c) may replace the entire module, for instance with one
Xthat provides a mouse/@|window/@|icon environment, a menu/@|function-@|key
Xenvironment, etc.
X
XA few guidelines should be followed to maintain portability:
X@begin(itemize)
XKeep variable and function names to 6 characters or less.
X
XKeep modules small.  For instance, on a PDP-11 it is necessary to keep
Xthe code segment of each module below 8K in order to allow the segment
Xmapping to occur which is necessary to run programs larger than 64K on a
Xnon-I-&-D-space machine.
X
XKeep strings short; many compilers have restrictive maximum lengths.
X
XKeep (f,s)printf arguments short.  If these exceed some compiler dependent
Xmaximum (perhaps as short as 128, after expansion) memory will be overwritten
Xand the program will probably core dump.
X
XDo not introduce system dependencies into @q(ckprot.w) or @q(ckfns*.c).
X@End(Itemize)
XIn general, remember that this program will have to be compilable by old
Xcompilers and runnable on small systems.
X
X
!FUNKY!STUFF!

gregg@okstate.UUCP (03/08/85)

echo x - ckermi.doc part 1
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckermi.doc
X
X
X
X1. UNIX KERMIT
X
XProgram:    Frank  da  Cruz,  Bill Catchings, Jeff Damens, Columbia University;
X            Herm Fischer, Litton Data Systems, Van Nuys  CA;  contributions  by
X            many others.
XLanguage:   C
XDocumentation:
X            Frank da Cruz, Columbia University; Herm Fischer, Litton Data  Sys-
X            tems, Van Nuys CA.
XVersion:    4.2
XDate:       March 1985
X
X                                   D R A F T
X
X    NOTE  -- This source for this documentation is written as input for the
X    Scribe text formatter.  It needs to become input for two  text  format-
X    ters  -  Scribe  and Nroff, the former for inclusion in the Kermit User
X    Guide, and the latter for Unix man pages.  This might be done by defin-
X    ing  formatting macros in M4, which generate the appropriate Scribe and
X    Nroff  commands  for   sectioning,   itemization,   description,   etc.
X    (Volunteers?)
X
XC-Kermit  is  a  completely new implementation of Kermit, written modularly and
Xtransportably in C. The protocol state transition table is written in  wart,  a
X(not proprietary) lex-like preprocessor for C. System-dependent primitive func-
Xtions are isolated into separately compiled modules so that the program  should
Xbe  easily portable among Unix systems and also to non-Unix systems that have C
Xcompilers.
X
X
XUnix Kermit Capabilities At A Glance:
X
X  Local operation:                   Yes
X  Local operation:                   Yes
X  Remote operation:                  Yes
X  Login scripts:                     Yes
X  Transfer text files:               Yes
X  Transfer binary files:             Yes
X  Wildcard send:                     Yes
X  File transfer interruption:        Yes
X  Filename collision avoidance:      Yes
X  Can time out:                      Yes
X  8th-bit prefixing:                 Yes
X  Repeat count prefixing:            Yes
X  Alternate block checks:            Yes
X  Terminal emulation:                Yes
X  Communication settings:            Yes
X  Transmit BREAK:                    Yes
X  Support for dialout modems:        Yes
X  IBM mainframe communication:       Yes
X  Transaction logging:               Yes
X  Session logging:                   Yes
X  Debug logging:                     Yes
X  Packet logging:                    Yes
X  Act as server:                     Yes
X  Talk to server:                    Yes
X  Advanced server functions:         Yes
X  Local file management:             Yes
X  Command/Init files:                Yes
X  UUCP and multiuser line locking:   Yes
X  File attributes:                   No
X  Command macros:                    No
X  Raw file transmit:                 No
X
XC-Kermit provides traditional Unix command line operation  as  well  as  inter-
Xactive  command  prompting and execution.  The command line options provide ac-
Xcess to a minimal subset of C-Kermit's capabilities;  the  interactive  command
Xset is far richer.
X
XOn  systems  with  dialout  modems,  C-Kermit  can  use command files and login
Xscripts to essentially duplicate the file transfer functionality of uucp  among
Xheterogeneous  operating  systems, including by the use of scheduled (e.g. late
Xnight) unattended operation.
X
X
X1.1. The Unix File System
X
XConsult your Unix manual for details about the file system under  your  version
Xof  Unix.  For the purposes of Kermit, several things are worth briefly noting.
XUnix files generally have lowercase names.  Unix  directories  are  tree-struc-
Xtured.  Directory levels are separated by "/" characters.  For example, 
X
X    /usr/foo/bar
X
Xdenotes  the file bar in the directory /usr/foo.  Wildcard or "meta" characters
Xallow groups of files to be specified.  "*" matches any string; "?" matches any
Xsingle character.
X
XWhen  C-Kermit  is  invoked  with files specified on the Unix command line, the
XUnix shell (Bourne Shell, C-Shell, etc) expands meta characters,  and  in  this
Xcase, a wider variety is available.  For example, 
X
X    kermit -s ~/ck[x-z]*.[ch]
X
Xis  expanded by the Berkeley C-Shell into a list of all the files in the user's
Xhome directory (~/) that start with the characters "ck", followed by  a  single
Xcharacter  x,  y, or z, followed by zero or more characters, followed by a dot,
Xfollowed by one of the characters c or h.  Internally, the C-Kermit program it-
Xself expands only the "*" and "?" meta characters.
X
XUnix  files are linear streams of 8-bit bytes.  Text files consist of 7-bit AS-
XCII characters, with the high bit off (0), and  lines  separated  by  the  Unix
Xnewline  character,  which is linefeed (LF, ASCII 10).  This distinguishes Unix
Xtext files from those on most other ASCII systems, in which lines are separated
Xby  a  carriage-return linefeed sequence (CRLF, ASCII 13 followed by ASCII 10).
XBinary files are likely to contain data in the high bits of the file bytes, and
Xare not treated in terms of lines.
X
XWhen  transferring  files,  C-Kermit  will convert between upper and lower case
Xfilenames and between LF and CRLF line terminators automatically,  unless  told
Xto  do  otherwise.   When binary files must be transferred, the program must be
Xinstructed not to perform LF/CRLF conversion (-i on the command  line  or  "set
Xfile type" interactively; see below).
X
X
X1.2. Command Line Operation
X
XThe C-Kermit command line syntax has been changed from that of earlier releases
Xof Unix Kermit to conform to the "Proposed Syntax  Standards  for  Unix  System
XCommands"  put  forth  by  Kathy  Hemenway  and  Helene  Armitage  of AT&T Bell
XLaboratories in Unix/World, Vol.1, No.3, 1984.  The rules that apply are:
X
X   - Command names must be between 2 and 9 characters ("kermit" is 6).
X   - Command names must include lower case letters and digits only.
X   - An option name is a single character.
X   - Options are delimited by '-'.
X   - Options with  no  arguments  may  be  grouped  (bundled)  behind  one
X     delimiter.
X   - Option-arguments cannot be optional.
X   - Arguments immediately follow options, separated by whitespace.
X   - The order of options does not matter.
X   - '-' preceded and followed by whitespace means standard input.
X
XA group of bundled options may end with an option that has an argument.
X
XThe following notation is used in command descriptions:
X
Xfn      A  Unix  file specification, possibly containing the "wildcard" charac-
X        ters '*' or '?' ('*' matches all character strings, '?'    matches  any
X        single character).
X
Xfn1     A Unix file specification which may not contain '*' or '?'.
X
Xrfn     A  remote  file  specification in the remote system's own syntax, which
X        may denote a single file or a group of files.
X
Xrfn1    A remote file specification which should denote only a single file.
X
Xn       A decimal number between 0 and 94.
X
Xc       A decimal number between 0 and 127 representing the value of  an  ASCII
X        character.
X
Xcc      A  decimal  number  between 0 and 31, or else exactly 127, representing
X        the value of an ASCII control character.
X
X[ ]     Any field in square braces is optional.
X
X{x,y,z} Alternatives are listed in curly braces.
X
XC-Kermit command line options may specify  either  actions  or  settings.    If
XC-Kermit is invoked with a command line that specifies no actions, then it will
Xissue a prompt and begin interactive dialog.   Action  options  specify  either
Xprotocol transactions or terminal connection.
X
X-s fn   Send  the  specified  file  or  files.   If fn contains wildcard (meta)
X        characters, the Unix shell expands it into a list.  If fn is  '-'  then
X        kermit sends from standard input, which must come from a file:  
X
X            kermit -s - < foo.bar
X
X        or a parallel process:  
X
X            ls -l | kermit -s -
X
X        You  cannot use this mechanism to send terminal typein.  If you want to
X        send a file whose name is "-" you can precede it with a path  name,  as
X        in 
X
X            kermit -s ./-
X
X-r      Receive a file or files.  Wait passively for files to arrive.
X
X-k      Receive  (passively)  a file or files, sending them to standard output.
X        This option can be used in several ways:
X
X        kermit -k
X            Displays  the  incoming  files  on  your screen; to be used only in
X            "local mode" (see below).
X
X        kermit -k > fn1
X            Sends  the  incoming file or files to the named file, fn1.  If more
X            than one file arrives,  all  are  concatenated  together  into  the
X            single file fn1.
X
X        kermit -k | command
X            Pipes the incoming data (single or multiple files) to the indicated
X            command, as in 
X
X                kermit -k | sort > sorted.stuff
X
X-a fn1  If you have specified a file transfer option, you may specify an alter-
X        nate name for a single file with the -a option.  For example, 
X
X            kermit -s foo -a bar
X
X        sends the file foo telling the receiver that its name is bar.  If  more
X        than  one  file  arrives or is sent, only the first file is affected by
X        the -a option:  
X
X            kermit -ra baz
X
X        stores the first incoming file under the name baz.
X
X-x      Begin server operation.  May be used in either local or remote mode.
X
XBefore proceeding, a few words about remote and local operation are  necessary.
XC-Kermit  is  "local"  if it is running on PC or workstation that you are using
Xdirectly, or if it is running on a multiuser system and transferring files over
Xan  external  communication line -- not your job's controlling terminal or con-
Xsole.  C-Kermit is remote if it is running on a multiuser system and  transfer-
Xring files over its own controlling terminal's communication line, connected to
Xyour PC or workstation.
X
XIf you are running C-Kermit on a PC, it is in local mode by default,  with  the
X"back  port"  designated for file transfer and terminal connection.  If you are
Xrunning C-Kermit on a multiuser (timesharing) system, it is in remote mode  un-
Xless  you explicitly point it at an external line for file transfer or terminal
Xconnection.  The following command sets C-Kermit's "mode":
X
X-l dev  Line -- Specify a terminal line to use for file transfer  and  terminal
X        connection, as in 
X
X            kermit -l /dev/ttyi5
X
XWhen  an  external  line is being used, you might also need some additional op-
Xtions for successful communication with the remote system:
X
X-b n    Baud -- Specify the baud rate for the line given in the -l  option,  as
X        in 
X
X            kermit -l /dev/ttyi5 -b 9600
X
X        This  option  should  always  be included with the -l option, since the
X        speed of an external line is not necessarily what you expect.
X
X-p x    Parity -- e,o,m,s,n (even, odd, mark, space, or none).   If  parity  is
X        other  than none, then the 8th-bit prefixing mechanism will be used for
X        transferring 8-bit binary data, provided the  opposite  Kermit  agrees.
X        The default parity is none.
X
X-t      Specifies  half  duplex,  line  turnaround  with  XON  as the handshake
X        character.
X
XThe following commands may be used only with a C-Kermit which is local  --  ei-
Xther by default or else because the -l option has been specified.
X
X-g rfn  Actively  request  a remote server to send the named file or files; rfn
X        is a file specification in the remote host's own syntax.  If fn happens
X        to  contain  any  special  shell  characters,  like  '*', these must be
X        quoted, as in 
X
X            kermit -g x\*.\?
X
X-f      Send a 'finish' command to a remote server.
X
X-c      Establish a terminal connection over  the  specified  or  default  com-
X        munication line, before any protocol transaction takes place.  Get back
X        to  the  local  system  by  typing  the  escape   character   (normally
X        Control-Backslash) followed by the letter 'c'.
X
X-n      Like  -c,  but  after a protocol transaction takes place; -c and -n may
X        both be used in the same command.  The use of -n and -c is  illustrated
X        below.
X
XOn  a  timesharing  system, the -l and -b options will also have to be included
Xwith the -r, -k, or -s options if the other Kermit is on a remote system.
X
XIf C-Kermit is in local mode, the screen (stdout)  is  continously  updated  to
Xshow  the  progress  of the file transer.  A dot is printed for every four data
Xpackets, other packets are shown by type  (e.g.  'S'  for  Send-Init),  'T'  is
Xprinted  when there's a timeout, and '%' for each retransmission.  In addition,
Xyou may type (to stdin) certain "interrupt" commands during file transfer:
X
X    Control-F:  Interrupt the current File, and go on to the next (if any).
X    Control-B:  Interrupt the entire Batch of files, terminate the transaction.
X    Control-R:  Resend the current packet
X    Control-A:  Display a status report for the current transaction.
X
XThese interrupt characters differ from the ones used in other Kermit  implemen-
Xtations  to  avoid  conflict with Unix shell interrupt characters.  With System
XIII and System V implementations of Unix, interrupt commands must be  preceeded
Xby the escape character (e.g. control-\).
X
XSeveral other command-line options are provided:
X
X-i      Specifies that files should be sent or received exactly "as is" with no
X        conversions.  This option is necessary for transmitting  binary  files.
X        It may also be used to slightly boost efficiency in Unix-to-Unix trans-
X        fers of text files by eliminating CRLF/newline conversion.
X
X-w      Write-Protect -- Avoid filename collisions for incoming files.
X
X-q      Quiet -- Suppress screen update during file transfer, for  instance  to
X        allow a file transfer to proceed in the background.
X
X-d      Debug -- Record debugging information in the file debug.log in the cur-
X        rent directory.  Use this option if you believe  the  program  is  mis-
X        behaving, and show the resulting log to your local kermit maintainer.
X
X-h      Help -- Display a brief synopsis of the command line options.
X
XThe command line may contain no more than one protocol action option.
X
XFiles  are  sent with their own names, except that lowercase letters are raised
Xto upper, pathnames are stripped off, tilde ('~') characters  changed  to  'X',
Xand  if  the file name begins with a period, an 'X' is inserted before it.  In-
Xcoming files are stored under their own names except that uppercase letters are
Xlowered,  and,  if  -w  was specified, a "generation number" is appended to the
Xname if it has the same name as an existing file which would otherwise be over-
Xwritten.    If  the -a option is included, then the same rules apply to its ar-
Xgument.  The file transfer display shows  any  transformations  performed  upon
Xfilenames.
X
XDuring transmission, files are encoded as follows:
X
X   - Control characters are converted to prefixed printables.
X
X   - Sequences  of repeated characters are collapsed via repeat counts, if
X     the other Kermit is also capable of repeated-character compression.
X
X   - If parity is being used on the communication  line,  data  characters
X     with  the  8th  (parity)  bit on are specially prefixed, provided the
X     other Kermit is capable of 8th-bit prefixing (if  not,  8-bit  binary
X     files cannot be successfully transferred).
X
X   - Conversion is done between Unix newlines and carriage-return-linefeed
X     sequences unless the -i option was specified.
X
X
XCommand Line Examples:
X
X    kermit -l /dev/ttyi5 -b 1200 -cn -r
X
XThis command connects you to the system on the other end of ttyi5 at 1200 baud,
Xwhere  you  presumably  log in and run Kermit with a 'send' command.  After you
Xescape back, C-Kermit waits for a file (or files) to arrive.    When  the  file
Xtransfer is completed, you are again connected to the remote system so that you
Xcan logout.
X
X    kermit -l /dev/ttyi4 -b 1800 -cntp m -r -a foo
X
XThis command is like the preceding one, except the remote system in  this  case
Xuses  half  duplex communication with mark parity.  The first file that arrives
Xis stored under the name foo.
X
X
X    kermit -l /dev/ttyi6 -b 9600 -c | tek
X
XThis example uses Kermit to connect your terminal to the system  at  the  other
Xend of ttyi6.  The C-Kermit terminal connection does not provide any particular
Xterminal  emulation,  so  C-Kermit's  standard   i/o   is   piped   through   a
X(hypothetical) program called tek, which performs (say) Tektronix emulation.
X
X
X    kermit -l /dev/ttyi6 -b 9600 -nf
X
XThis command would be used to shut down a remote server and then connect to the
Xremote system, in order to log out or to make further use of it.  The -n option
Xis invoked after -f (-c would have been invoked before).
X
X
X    kermit -l /dev/ttyi6 -b 9600 -qg foo.\* &
X
XThis  command  causes C-Kermit to be invoked in the background, getting a group
Xof files from a remote server (note the quoting of the '*' character).  No dis-
Xplay  occurs  on  the  screen, and the keyboard is not sampled for interruption
Xcommands.  This allows other work to be done while file  transfers  proceed  in
Xthe background.
X
X
X    kermit -l /dev/ttyi6 -b 9600 -g foo.\* > foo.log < /dev/null &
X
XThis  command  is  like  the previous one, except the file transfer display has
Xbeen redirected to the file foo.log.  Standard input  is  also  redirected,  to
Xprevent C-Kermit from sampling it for interruption commands.
X
X
X    kermit -iwx
X
XThis  command  starts  up  C-Kermit as a server.  Files are transmitted with no
Xnewline/carriage-return-linefeed conversion; the -i option is necessary for bi-
Xnary  file transfer and useful for Unix-to-Unix transfers.  Incoming files that
Xhave the same names as existing files are given new, unique names.
X
X
X    kermit -l /dev/ttyi6 -b 9600
X
XThis command sets the communication  line  and  speed.    Since  no  action  is
Xspecified,  C-Kermit issues a prompt and enters an interactive dialog with you.
XAny settings given on the command line remain in force during the  dialog,  un-
Xless explicitly changed.
X
X
X    kermit
X
XThis command starts up Kermit interactively with all default settings.
X
XA final example shows how Unix Kermit might be used to send an entire directory
Xtree from one Unix system to another, using the tar program as  Kermit's  stan-
Xdard input and output.  On the orginating system, in this case the remote, type
X(for instance):
X
X
X    tar cf - /usr/fdc | kermit -is -
X
XThis causes tar to send the directory /usr/fdc (and all its files and  all  its
Xsubdirectories and all their files...) to standard output instead of to a tape;
Xkermit receives this as standard input and sends it as a binary file.   On  the
Xreceiving system, in this case the local one, type (for instance):
X
X
X    kermit -il /dev/ttyi5 -b 9600 -k | tar xf -
X
XKermit  receives  the  tar archive, and sends it via standard output to its own
Xcopy of tar, which extracts from it a replica of the original directory tree.
X
X
XExit Status Codes:
X
XKermit returns an exit status of zero, except when a  fatal  error  is  encoun-
Xtered,  where  the exit status is set to one.  With background operation (e.g.,
X'&'  on  invoking  command  line),  driven  by  scripted  interactive  commands
X(redirected  standard  input and/or take files), any failed interactive command
X(such as failed dial or script attempt) causes the fatal error exit.
X
X
X1.3. Interactive Operation
X
XC-Kermit's interactive command prompt is "C-Kermit>".    In  response  to  this
Xprompt, you may type any valid command.  C-Kermit executes the command and then
Xprompts you for another command.  The process continues until you instruct  the
Xprogram to terminate.
X
XCommands  begin  with a keyword, normally an English verb, such as "send".  You
Xmay omit trailing characters from any keyword, so  long  as  you  specify  suf-
Xficient  characters  to  distinguish  it  from  any other keyword valid in that
Xfield.  Certain commonly-used keywords (such as "send",  "receive",  "connect")
Xhave  special  non-unique abbreviations ("s" for "send", "r" for "receive", "c"
Xfor "connect").
X
XCertain characters have special functions in interactive commands:
X
X    ?   Question mark, typed at any point in a command, will produce a  message
X        explaining  what  is  possible or expected at that point.  Depending on
X        the context, the message may be a brief phrase, a menu of keywords,  or
X        a list of files.
X
X    ESC (The  Escape  or  Altmode  key)  --  Request  completion of the current
X        keyword or filename, or insertion of a default value.  The result  will
X        be a beep if the requested operation fails.
X
X    DEL (The  Delete  or  Rubout key) -- Delete the previous character from the
X        command.  You may also use BS (Backspace, Control-H) for this function.
X
X    ^W  (Control-W) -- Erase the rightmost word from the command line.
X
X    ^U  (Control-U) -- Erase the entire command.
X
X    ^R  (Control-R) -- Redisplay the current command.
X
X    SP  (Space) -- Delimits fields (keywords, filenames, numbers) within a com-
X        mand.  HT (Horizontal Tab) may also be used for this purpose.
X
X    CR  (Carriage  Return)  -- Enters the command for execution.  LF (Linefeed)
X        or FF (formfeed) may also be used for this purpose.
X
X    \   (Backslash) -- Enter any of the  above  characters  into  the  command,
X        literally.  To enter a backslash, type two backslashes in a row (\\).
X
XYou  may  type  the editing characters (DEL, ^W, etc) repeatedly, to delete all
Xthe way back to the prompt.  No action will be performed until the  command  is
Xentered by typing carriage return, linefeed, or formfeed.  If you make any mis-
Xtakes, you will receive an informative error message and a new prompt  --  make
Xliberal  use  of '?' and ESC to feel your way through the commands.  One impor-
Xtant command is "help" -- you should use it the first time you run C-Kermit.
X
XInteractive C-Kermit accepts commands from files as well as from the  keyboard.
XWhen  you  enter  interactive mode, C-Kermit looks for the file .kermrc in your
Xhome or current directory (first it looks in the home directory,  then  in  the
Xcurrent  one) and executes any commands it finds there.  These commands must be
Xin interactive format, not Unix command-line format.  A "take" command is  also
Xprovided  for use at any time during an interactive session.  Command files may
Xbe nested to any reasonable depth.
X
XHere is a brief list of C-Kermit interactive commands:
X              !  Execute a Unix shell command.
X            bye  Terminate and log out a remote Kermit server.
X          close  Close a log file.
X        connect  Establish a terminal connection to a remote system.
X            cwd  Change Working Directory.
X           dial  Dial a telephone number.
X      directory  Display a directory listing.
X           echo  Display arguments literally.
X           exit  Exit from the program, closing any open logs.
X         finish  Instruct a remote Kermit server to exit, but not log out.
X            get  Get files from a remote Kermit server.
X           help  Display a help message for a given command.
X            log  Open a log file -- debugging, packet, session, transaction.
X           quit  Same as 'exit'.
X        receive  Passively wait for files to arrive.
X         remote  Issue file management commands to a remote Kermit server.
X         script  Execute a login script with a remote system.
X           send  Send files.
X         server  Begin server operation.
X            set  Set various parameters.
X           show  Display values of 'set' parameters.
X          space  Display current disk space usage.
X     statistics  Display statistics about most recent transaction.
X           take  Execute commands from a file.
X
XThe 'set' parameters are:
X    block-check  Level of packet error detection.
X          delay  How long to wait before sending first packet.
X         duplex  Specify which side echoes during 'connect'.
X  end-of-packet  Terminator for outbound packets.
X    escape-character  Character to prefix "escape commands" during 'connect'.
X           file  Set various file parameters.
X   flow-control  Communication line full-duplex flow control.
X      handshake  Communication line half-duplex turnaround character.
X           line  Communication line device name.
X   modem-dialer  Type of modem-dialer on communication line.
X  packet-length  Maximum length for packets.
X  pad-character  Character to use for inter-packet padding.
X        padding  How much inter-packet padding to use.
X         parity  Communication line character parity.
X         prompt  Change the C-Kermit program's prompt.
X          speed  Communication line speed.
X     start-of-packet  Control character to mark beginning of packets.
X        timeout  Timer interval to detect lost packets.
X
XThe 'remote' commands are:
X            cwd  Change remote working directory.
X         delete  Delete remote files.
X      directory  Display a listing of remote file names.
X           help  Request help from a remote server.
X           host  Issue a command to the remote host in its own command language
X          space  Display current disk space usage on remote system.
X           type  Display a remote file on your screen.
X            who  Display who's logged in, or get information about a user.
X
XMost of these commands are described adequately in the Kermit User Guide.  Spe-
Xcial aspects of certain Unix Kermit commands are described below.
X
X
X                              THE 'SEND' COMMAND
X
XSyntax:  send fn  - or -  send fn1 rfn1
X
XSend  the file or files denoted by fn to the other Kermit, which should be run-
Xning as a server, or which should be given the 'receive' command.  Each file is
Xsent  under  its own name (as described above, or as specified by the 'set file
Xnames' command).  If the second form is used, i.e. with fn1 denoting  a  single
XUnix  file,  rfn1 may be specified as a name to send it under.  The 'send' com-
Xmand may be abbreviated to 's', even though 's' is not  a  unique  abbreviation
Xfor a top-level C-Kermit command.
X
XThe wildcard (meta) characters '*' and '?' are accepted in fn.  If '?' is to be
Xincluded, it must be prefixed by '\' to override its normal function of provid-
Xing  help.    '*'  matches any string, '?' matches any single character.  Other
Xnotations for file groups, like '[a-z]og', are  not  available  in  interactive
Xcommands  (though  of  course they are available on the command line).  When fn
Xcontains '*' or '?' characters, there is a limit to the number  of  files  that
Xcan  be  matched,  which  varies from system to system.  If you get the message
X"Too many files match" then you'll have to make a more judicious selection.  If
Xfn was of the form 
X
X    usr/longname/anotherlongname/*
X
Xthen  C-Kermit's  string  space  will  fill  up rapidly -- try doing a cwd (see
Xbelow) to the path in question and reissuing the command.
X
XNote -- C-Kermit sends only from the current or specified directory.   It  does
Xnot traverse directory trees.  If the source directory contains subdirectories,
Xthey will be skipped.  Conversely, C-Kermit does not  create  directories  when
Xreceiving  files.    If  you  have  a need to do this, you can pipe tar through
XC-Kermit, as shown in the example on page 3, or under System III/V Unix you can
Xuse cpio.
X
XAnother  Note  --  C-Kermit does not skip over "invisible" files that match the
Xfile specification; Unix systems usually treat files whose names start  with  a
Xdot (like .login, .cshrc, and .kermrc) as invisible.
X
X
X                             THE 'RECEIVE' COMMAND
X
XSyntax:  receive  - or -  receive fn1
X
XPassively  wait  for files to arrive from the other Kermit, which must be given
Xthe 'send' command -- the 'receive' command does not work in conjunction with a
Xserver  (use  'get'  for  that).  If fn1 is specified, store the first incoming
Xfile under that name.  The 'receive' command may be abbreviated to 'r'.
X
X
X                              THE 'GET' COMMAND:
X
XSyntax:  get rfn
X
X        or: get
X                rfn
X                fn1
X
XRequest a remote Kermit server to send the named file or files.  Since a remote
Xfile  specification  (or  list)  might  contain  spaces, which normally delimit
Xfields of a C-Kermit command, an alternate form of the command is  provided  to
Xallow  the inbound file to be given a new name: type 'get' alone on a line, and
Xyou will be prompted separately for the remote and local  file  specifications,
Xfor example
X
X    C-Kermit>get
X     Remote file specification: foo
X     Local name to store it under: bar
X
XAs  with 'receive', if more than one file arrives as a result of the 'get' com-
Xmand, only the first will be stored under the alternate name given by fn1;  the
Xremaining  files will be stored under their own names if possible.  If a '?' is
Xto be included in the remote file specification, you must prefix it with '\' to
Xsuppress its normal function of providing help.
X
X
X                             THE 'SERVER' COMMAND:
X
XThe 'server' command places C-Kermit in "server mode" on the currently selected
Xcommunication line.  All further commands must arrive as valid  Kermit  packets
Xfrom  the  Kermit  on  the  other  end of the line.  The Unix Kermit server can
Xrespond to the following commands:
X
XCommand                Server Response
X  get                    Sends files
X  send                   Receives files
X  bye                    Attempts to log itself out
X  finish                 Exits to level from which it was invoked
X  remote directory       Sends directory lising
X  remote delete          Removes files
X  remote cwd             Changes working directory
X  remote type            Sends files to your screen
X  remote space           Reports about its disk usage
X  remote who             Shows who's logged in
X  remote host            Executes a Unix shell command
X  remote help            Lists these capabilities
X
XNote that the Unix Kermit server cannot always respond to a BYE  command.    It
Xwill  attempt to do so using "kill(0,9)", but this will not work on all systems
Xor under all conditions.  For instance, the C-Shell changes your process group,
Xso that the process id of 0 does not refer to what you might expect.
X
XIf  the  Kermit  server  is  directed at an external line (i.e. it is in "local
Xmode") then the console may be used for other work if you have 'set  file  dis-
Xplay  off'; normally the program expects the console to be used to observe file
Xtransfers and enter status queries or interruption commands.  The  way  to  get
XC-Kermit  into  background operation from interactive command level varies from
Xsystem to system (e.g. on Berkeley Unix you would halt the program with ^Z  and
Xthen  use the C-Shell 'bg' command to continue it in the background).  The more
Xcommon method is to invoke the program with the desired command line arguments,
Xincluding "-q", and with a terminating "&".
X
XWhen  the  Unix  Kermit server is given a 'remote host' command, it executes it
Xusing the shell invoked upon login to the remote system, e.g. the Bourne  shell
Xor  the  Berkeley  C-Shell.    (Note -- this is in distinction to the local "!"
Xshell escape, which always uses the Bourne shell; see below).
X
X
X                  THE 'REMOTE', 'BYE', AND 'FINISH' COMMANDS:
X
XC-Kermit may itself request services from a remote Kermit server.  In  addition
Xto the 'send' and 'get' commands, the following may also be used:
X
X    remote cwd [directory]
X        If the optional remote directory specification is included, you will be
X        prompted  on a separate line for a password, which will not echo as you
X        type it.
X
X    remote delete rfn       delete remote file or files.
X    remote directory [rfn]  directory listing of remote files.
X    remote host command     command in remote host's own command language.
X    remote space            disk usage report from remote host.
X    remote type [rfn]       display remote file or files on the screen.
X    remote who [user]       display information about who's logged in.
X    remote help             display remote server's capabilities.
X
X    bye and finish:
X        When  connected  to  a  remote  Kermit server, these commands cause the
X        remote server to terminate; 'finish' returns it  to  Kermit  or  system
X        command  level  (depending on the implementation or how the program was
X        invoked); 'bye' also requests it to log itself out.
X
X
X                        THE 'LOG' AND 'CLOSE' COMMANDS:
X
XSyntax: log {debugging, packets, session, transactions} [ fn1 ]
X
XC-Kermit's progress may be logged in various ways.  The 'log' command  opens  a
Xlog,  the  'close' command closes it.  In addition, all open logs are closed by
Xthe 'exit' and 'quit' commands.  A name may be specified for a log file; if the
Xname is omitted, the file is created with a default name as shown below.
X
Xlog debugging
X    This produces a voluminous log of the internal workings of C-Kermit, of use
X    to  Kermit developers or maintainers in tracking down suspected bugs in the
X    C-Kermit program.  Use of this feature dramatically slows down  the  Kermit
X    protocol.  Default name: debug.log.
X
Xlog packets
X    This produces a record of all the packets that go in and out  of  the  com-
X    munication port.  This log is of use to Kermit maintainers who are tracking
X    down protocol problems in either C-Kermit or any Kermit  that  C-Kermit  is
X    connected to.  Default name:  packet.log.
X
Xlog session
X    This log will contain a copy of everything you see on  your  screen  during
X    the  'connect' command, except for local messages or interaction with local
X    escape commands.  Default name:  session.log.
X
Xlog transactions
X    The transaction log is a record of all the files that were sent or received
X    while transaction logging was in effect.    It  includes  time  stamps  and
X    statistics,  filename  transformations,  and records of any errors that may
X    have occurred.  The transaction log allows you to have long unattended file
X    transfer  sessions  without  fear  of  missing  some  vital screen message.
X    Default name:  transaction.log.
X
XThe 'close' command explicitly closes a log, e.g. 'close debug'.
X
X
X                        LOCAL FILE MANAGEMENT COMMANDS:
X
XUnix Kermit allows some degree of local file management from  interactive  com-
Xmand level:
X
Xdirectory [fn]
X    Displays a listing of the names, modes, sizes, and dates of files  matching
X    fn (which defaults to '*').  Equivalent to 'ls -l'.
X
Xcwd [directory-name]
X    Changes Kermit's working directory to the one given, or to the your default
X    directory if the directory name is omitted.  Equivalent to 'cd'.
X
Xspace
X    Display information about disk space and/or quota in the current  directory
X    and device.
X
X! command
X    The command is executed by the Unix shell.  Use this  for  all  other  file
X    management commands.  This command has certain peculiarities:
X
X       - The Bourne shell is used.
X       - At least one space must separate the '!' from the shell command.
X       - A 'cd' command executed in this manner will have no effect -- use
X         the C-Kermit 'cwd' command instead.
X
X
X                        THE 'SET' AND 'SHOW' COMMANDS:
X
XSince Kermit is designed to allow diverse systems to communicate, it  is  often
Xnecessary  to  issue  special  instructions  to  allow  the program to adapt to
Xpeculiarities of the another system or the communication path.  These  instruc-
Xtions are accomplished by the 'set' command.  The 'show' command may be used to
Xdisplay current settings.  Here is a brief synopsis of  settings  available  in
Xthe current release of C-Kermit:
X
Xblock-check {1, 2, 3}
X    Determines the level of per-packet error  detection.    "1"  is  a  single-
X    character  6-bit  checksum,  folded  to include the values of all bits from
X    each character.   "2"  is  a  2-character,  12-bit  checksum.    "3"  is  a
X    3-character,  16-bit  cyclic  redundancy check (CRC).  The higher the block
X    check, the better the error detection and correction  and  the  higher  the
X    resulting  overhead.   Type 1 is most commonly used; it is supported by all
X    Kermit implementations, and it has proven adequate in  most  circumstances.
X    Types  2  or  3  would  be used to advantage when transferring 8-bit binary
X    files over noisy lines.
X
Xdelay n
X    How  many  seconds  to  wait before sending the first packet after a 'send'
X    command.  Used in remote mode to give you time to escape back to your local
X    Kermit and issue a 'receive' command.  Normally 5 seconds.
X
Xduplex {full, half}
X    For use during 'connect'.  Specifies  which  side  is  doing  the  echoing;
X    'full' means the other side, 'half' means C-Kermit must echo typein itself.
X
Xend-of-packet cc
X    Specifies the control character needed by the other Kermit to recognize the
X    end  of a packet.  C-Kermit sends this character at the end of each packet.
X    Normally 13 (carriage return), which most Kermit  implementations  require.
X    Other Kermits require no terminator at all, still others may require a dif-
X    ferent terminator, like linefeed (10).
X
!FUNKY!STUFF!

gregg@okstate.UUCP (03/08/85)

echo x - ckermi.doc part 2
sed '1,$s/^X//' <<\!FUNKY!STUFF! >> ckermi.doc
Xescape-character cc
X    For use during 'connect' to get C-Kermit's attention.  The escape character
X    acts as a prefix to an 'escape command', for instance to close the  connec-
X    tion  and  return  to  C-Kermit  or  Unix command level.  The normal escape
X    character is Control-Backslash (28).  The escape character is also used  in
X    System  III/V  implementations,  to  prefix  interrupt commands during file
X    transfers.
X
Xfile {display, names, type, warning}
X    Establish various file-related parameters:
X
X    display {on, off}
X        Normally 'on'; when in local mode, display progress of  file  transfers
X        on  the  screen (stdout), and listen to the keyboard (stdin) for inter-
X        ruptions.  If off (-q on command line) none of this is  done,  and  the
X        file transfer may proceed in the background oblivious to any other work
X        concurrently done at the console terminal.
X
X    names {converted, literal}
X        Normally  converted,  which  mean  that  outbound  filenames  have path
X        specifications stripped, lowercase letters raised to upper, tildes  and
X        extra  periods  changed  to X's, and an X inserted in front of any name
X        that starts  with  period.    Incoming  files  have  uppercase  letters
X        lowered.  Literal means that none of these conversions are done; there-
X        fore, any directory path appearing in  a  received  file  specification
X        must exist and be write-accessible.  When literal naming is being used,
X        the sender should not use path names in the file  specification  unless
X        the same path exists on the target system and is writable.
X
X    type {binary, text}
X        Normally text, which means that conversion is done between Unix newline
X        characters  and  the carriage-return/linefeed sequences required by the
X        canonical Kermit file transmission format, and in common  use  on  non-
X        Unix  systems.   Binary means to transmit file contents without conver-
X        sion.  Binary ('-i' in command line notation) is necessary  for  binary
X        files,  and  desirable  in all Unix-to-Unix transactions to cut down on
X        overhead.
X
X    warning {on, off}
X        Normally  off,  which means that incoming files will silently overwrite
X        existing files of the same name.  When on ('-w' on command line) Kermit
X        will check if an arriving file would overwrite an existing file; if so,
X        it will construct a new name for the arriving file, of the form  foo~n,
X        where foo is the name they share and n is a "generation number"; if foo
X        exists, then the new file will be called foo~1.  If foo and  foo~1  ex-
X        ist, the new file will be foo~2, and so on.
X
Xflow-control {none, xon/xoff}
X    Normally xon/xoff for full duplex flow control.  Should be set to 'none' if
X    the other system cannot do xon/xoff flow control.
X
Xhandshake {xon, xoff, cr, lf, bell, esc, none}
X    Normally none.  Otherwise, half-duplex communication line turnaround  hand-
X    shaking  is  done, which means Unix Kermit will not reply to a packet until
X    it has received the indicated handshake character or has timed out  waiting
X    for it.
X
Xline [device-name]
X    The device name for the communication line to be used for file transfer and
X    terminal connection, e.g. /dev/ttyi3.  If you specify a device name, Kermit
X    will be in local mode, and you should remember to issue any other necessary
X    'set'  commands,  such as 'set speed'.  If you omit the device name, Kermit
X    will revert to its default mode of operation.
X
Xmodem-dialer {direct, hayes, ventel}
X    The type of modem dialer on the communication line.  "Direct" indicates ei-
X    ther there is no dialout modem, or that if the line requires carrier detec-
X    tion  to  open,  then  'set  line'  will hang waiting for an incoming call.
X    "Hayes" and "Ventel" indicate that the subsequent 'set  line'  (or  the  -l
X    argument) will prepare for a subsequent 'dial' command for Hayes and Ventel
X    dialers, respectively.
X
Xpacket-length n
X    Specify  the  maximum  packet  length to use.  Normally 90.  Shorter packet
X    lengths can be useful on noisy lines, or with systems or front ends or net-
X    works  that  have  small  buffers.   The shorter the packet, the higher the
X    overhead, but the lower the chance of a packet being  corrupted  by  noise,
X    and the less time to retransmit corrupted packets.
X
Xpad-character cc
X    C-Kermit normally does not need to have incoming packets preceded with  pad
X    characters.    This  command allows C-Kermit to request the other Kermit to
X    use cc as a pad character.  Default cc is NUL, ASCII 0.
X
Xpadding n
X    How many pad characters to ask for, normally 0.
X
Xparity {even, odd, mark, space, none}
X    Specify character parity for use in packets and terminal  connection,  nor-
X    mally  none.    If  other  than none, C-Kermit will seek to use the 8th-bit
X    prefixing mechanism for transferring 8-bit binary data, which can  be  used
X    successfully  only  if  the  other Kermit agrees; if not, 8-bit binary data
X    cannot be successfully transferred.
X
Xprompt [string]
X    The  given  string  will  be  substituted for "C-Kermit>" as this program's
X    prompt.  If the string is omitted, the prompt will revert to "C-Kermit>".
X
Xspeed {0, 110, 150, 300, 600, 1200, 1800, 2400, 4800, 9600}
X    The  baud rate for the external communication line.  This command cannot be
X    used to change the speed of your own console terminal.  Many  Unix  systems
X    are set up in such a way that you must give this command after a 'set line'
X    command before you can use the line.
X
Xstart-of-packet cc
X    The Kermit packet prefix is Control-A (1).  The only reasons it should ever
X    be changed would be: Some piece of equipment somewhere between the two Ker-
X    mit  programs will not pass through a Control-A; or, some piece of of equi-
X    pment similarly placed is echoing its input.    In  the  latter  case,  the
X    recipient of such an echo can change the packet prefix for outbound packets
X    to be different from that of arriving packets, so that the  echoed  packets
X    will  be  ignored.    The  opposite  Kermit must also be told to change the
X    prefix for its inbound packets.  Unix  Kermit  presently  can  be  told  to
X    change only its outbound packet prefix.
X
Xtimeout n
X    Normally, each Kermit partner sets its packet  timeout  interval  based  on
X    what the opposite Kermit requests.  This command allows you to override the
X    normal procedure and specify a timeout interval.  If you specify 0, then no
X    timeouts will occur, and Unix Kermit will wait forever for expected packets
X    to arrive.
X
X
X                              THE 'SHOW' COMMAND:
X
XSyntax: show {parameters, versions}
X
XThe show command displays the values of  all  the  'set'  parameters  described
Xabove.    If  you  type 'show versions', then C-Kermit will display the version
Xnumbers and dates of all its internal  modules.    You  should  use  the  'show
Xversions'  command  to  ascertain  the  vintage  of  your Kermit program before
Xreporting problems to Kermit maintainers.
X
X
X                           THE 'STATISTICS' COMMAND:
X
XThe statistics command  displays  information  about  the  most  recent  Kermit
Xprotocol  transaction,  including  file  and communication line i/o, as well as
Xwhat encoding options were in effect (such as 8th-bit  prefixing,  repeat-count
Xcompression).
X
X
X                        THE 'TAKE' AND 'ECHO' COMMANDS:
X
XSyntax: take fn1
X
XThe  'take' command instructs C-Kermit to execute commands from the named file.
XThe file may contain any interactive C-Kermit commands, including 'take';  com-
Xmand  files  may  be nested to any reasonable depth.  The 'echo' command may be
Xused within command files to issue greetings, announce progress, etc.
X
XCommand files are in exactly the same syntax as  interactive  commands.    Note
Xthat  this implies that if you want to include special characters like question
Xmark or backslash that you would have to quote with backslash when  typing  in-
Xteractive  commands,  you  must  quote these characters the same way in command
Xfiles.
X
XCommand files may be used in lieu of command macros, which have not been imple-
Xmented in this version of C-Kermit.  For instance, if you commonly connect to a
Xsystem called 'B' that is connected to ttyh7 at 4800 baud, you could  create  a
Xfile called b containing the commands
X
X    set line /dev/ttyh7
X    set speed 4800
X    echo Connecting to System B...
X    connect
X
Xand  then simply type 'take b' (or 't b' since no other commands begin with the
Xletter 't') whenever you wished to connect to system B.
X
XFor connecting to IBM mainframes, a number  of  'set'  commands  are  required;
Xthese, too, are conveniently collected into a 'take' file like this one:
X
X    set speed 1200
X    set parity mark
X    set handshake xon
X    set flow-control none
X    set duplex half
X
XAn  implicit  'take' command is executed upon your .kermrc file upon C-Kermit's
Xinitial entry into interactive dialog.  The .kermrc file should  contain  'set'
Xor  other  commands  you  want to be in effect at all times.  For instance, you
Xmight want override the default action when incoming files have the same  names
Xas existing files -- in that case, put the command 
X
X    set file warning on
X
Xin your .kermrc file.
X
XCommands  executed from take files are not echoed at the terminal.  If you want
Xto see the commands as well as their output, you could feed the command file to
XC-Kermit via redirected stdin, as in 
X
X    'kermit < cmdfile'
XErrors  encountered during execution of take files (such as failure to complete
Xdial or script operations) cause termination of the current take file,  popping
Xto  the take file that invoked it, or to interactive level.  When kermit is ex-
Xecuted in the background, errors during execution of a take file are fatal.
X
X
X                            THE 'CONNECT' COMMAND:
X
XThe connect command links your terminal to another computer as if it were a lo-
Xcal  terminal to that computer, through the device specified in the most recent
X'set line' command, or through the default device if your system  is  a  PC  or
Xworkstation.    All  characters you type at your keyboard are sent out the com-
Xmunication line, all characters arriving at the  communication  port  are  dis-
Xplayed  on  your  screen.  Current settings of speed, parity, duplex, and flow-
Xcontrol are honored.  If you have issued a 'log  session'  command,  everything
Xyou  see  on  your  screen  will  also  be  recorded to your session log.  This
Xprovides a way to "capture" files from systems that don't have Kermit  programs
Xavailable.
X
XTo  get  back  to your own system, you must type the escape character, which is
XControl-Backslash (^\) unless you have changed it with the  'set  escape'  com-
Xmand,   followed  by  a  single-character  command,  such  as  'c'  for  "close
Xconnection".  Single-character commands include:
X
X  c     Close the connection
X  b     Send a BREAK signal
X  0     (zero) send a null
X  s     Give a status report about the connection
X  ^\    Send Control-Backslash itself (whatever you  have  defined  the  escape
X        character to be, typed twice in a row sends one copy of it).
X
XLowercase and control equivalents for these letters are also accepted.  A space
Xtyped after the escape character is ignored.  Any other character will  produce
Xa beep.
X
XThe  connect  command simply displays incoming characters on the screen.  It is
Xassumed any screen control sequences sent by the host will be  handled  by  the
Xfirmware  in  your  terminal or PC.  If terminal emulation is desired, then the
Xconnect command can invoked from the  Unix  command  line  (-c  or  -n),  piped
Xthrough a terminal emulation filter, e.g.  
X
X    kermit -l /dev/acu -b 1200 -c | tek
X
X'c' is an acceptable non-unique abbreviation for 'connect'.
X
X
X                              THE 'DIAL' COMMAND:
X
XSyntax: dial telephone-number-string
X
XThis  command controls dialout modems.  The telephone-number-string may contain
Xmodem-dialer commands, such as comma for Hayes pause, or '&' for  Ventel  dial-
Xtone wait and '%' for Ventel pause.
X
XBecause  modem  dialers have strict requirements to override the carrier-detect
Xsignal most Unix implementations expect, the sequence for dialing is more rigid
Xthan with the rest of kermit's features.
X
XExample one:
X
X    kermit -l /dev/cul0 -b 1200
X    C-Kermit>set modem-dialer hayes    hint: abbreviate set m h
X    C-Kermit>dial 9,5551212
X    Connected!
X    C-Kermit>connect                    hint: abbreviate c
X    logon, request remote server, etc.
X    C-Kermit> ...
X    C-Kermit>quit                       hint: abbreviate q
X
Xthis disconnects modem, and unlocks line.
X
XExample two:
X
X    kermit
X    C-Kermit>set modem-dialer ventel
X    C-Kermit>set line /dev/cul0
X    C-Kermit>dial 9&5551212%
X    Connected!
X    C-Kermit> ...
X
XExample three:
X
X    kermit
X    C-Kermit>take my-dial-procedure
X    Connected!
X
X    file my-dial-procedure:
X    set modem hayes
X    set line /dev/tty99
X    dial 5551212
X    connect
X
XFor  Hayes  dialers, two important switch settings are #1 and #6.  #1 should be
Xup so that the DTR is only asserted when the line is 'open'.  #6 should  be  up
Xso carrier-detect functions properly.  Switches #2 (English versus digit result
Xcodes) and #4 (Hayes echoes modem commands) may be in either position.
X
X
X                             THE 'SCRIPT' COMMAND:
X
XSyntax: script expect send [expect send] . . .
X
X"expect" has the syntax: expect[-send-expect[-send-expect[...]]]
X
XThis command facilitates logging into a remote system and/or invoking  programs
Xor other facilities after login on a remote system.
X
XThis  login  script facility operates in a manner similar to that commonly used
Xby the Unix uucp System's "L.sys" file entries.  A login script is  a  sequence
Xof the form:  
X
X    expect send [expect send] . . .
X
Xwhere  expect  is a prompt or message to be issued by the remote site, and send
Xis the string (names, numbers, etc) to return.    The  send  may  also  be  the
Xkeyword  EOT,  to send Control-D, or BREAK, to send a break signal.  Letters in
Xsend may be prefixed by '~' to send special characters.  These are:
X
X    ~b  backspace
X    ~s  space
X    ~q  '?'(trapped by Kermit's command interpreter)
X    ~n  linefeed
X    ~r  carriage return
X    ~t  tab
X    ~'  single quote
X    ~~  tilde
X    ~"  double quote
X    ~c  don't append a carriage return
X    ~o[o[o]]
X        an octal character
X
XAs  with  some uucp systems, sent strings are followed by ~r unless they have a
X~c.
X
XOnly the last 7 characters in each expect are matched.  A null expect, e.g.  ~0
Xor two adjacent dashes, causes a short delay before proceeding to the next send
Xsequence.  A null expect always succeeds.
X
XAs with uucp, if the expect string does not arrive, the script  attempt  fails.
XIf  you  expect that a sequence might not arrive, as with uucp, conditional se-
Xquences may be expressed in the form:  
X
X    -send-expect[-send-expect[...]]
X
Xwhere dashed sequences are followed as long as previous expects fail.
X
XExpect/send transactions can be easily be  debugged  by  logging  transactions.
XThis records all exchanges, both expected and actual.
X
XNote that '\' characters in login scripts, as in any other C-Kermit interactive
Xcommands, must be doubled up.
X
XExample one:
X
XUsing a modem, dial a unix host site.   Expect  "login"  (...gin),  and  if  it
Xdoesn't come, simply send a null string with a ~r.  (Some Unixes require either
Xan EOT or a BREAK instead of the null sequence,  depending  on  the  particular
Xsite's "logger" program.)  After providing user id and password, respond "x" to
Xa question-mark prompt, expect the Bourne shell "$" prompt (and send return  if
Xit  doesn't  arrive).   Then cd to directory kermit, and run the program called
X"wermit", entering the interactive connect state after wermit is loaded.
X
X    set modem-dialer ventel
X    set line /dev/tty77
X    set baud 1200
X    dial 9&5551212
X    script gin:--gin:--gin: smith ssword: mysecret ~q x $--$
X            cd~skermit $ wermit
X    connect
X
XExample two:
X
XUsing a modem, dial the Telenet network.  This network  expects  three  returns
Xwith  slight  delays between them.  These are sent following null expects.  The
Xsingle return is here sent as a null string, with a return appended by default.
XFour  returns  are  sent  to  be  safe before looking for the prompt.  Then the
Xtelenet id and password are entered.  Then telenet is instructed to connect  to
Xa  host  site  (c 12345).  The host has a data switch, and to "which system" it
Xresponds "myhost".  This is followed by a TOPS-20 logon, and a request to  load
XKermit,  set even parity, and enter the server mode.  Files are then exchanged.
XThe commands are in a take file.  The login command is split onto two lines for
Xreadability, though it is a single long line in the take file.
X
X    set modem-dialer hayes
X    set line /dev/cul0
X    set baud 1200
X    dial 9,5551212
X    set parity even
X    script ~0 ~0 ~0 ~0 ~0 ~0 ~0 ~0 @--@--@ id~saa001122 = 002211 @
X        c~s12345 ystem-c~s12345-ystem myhost @ joe~ssecret @ kermit
X        > set~sparity~seven > server
X    send some.stuff
X    get some.otherstuff
X    bye
X    quit
X
XSince  these  commands may be executed totally in the background, they can also
Xbe scheduled.  A typical shell script, which might be scheduled by cron,  would
Xbe as follows (csh used for this example):
X    #
X    #keep trying to dial and log onto remote host and exchange files
X    #wait 10 minutes before retrying if dial or script fail.
X    #
X    while ( 1 )
X            kermit < tonight.cmd &
X            if ( ! $status ) break
X            sleep 600
X    end
X
XFile  tonight.cmd  might  have two takes in it, for example, one to take a file
Xwith the set modem, set line, set baud, dial, and script, and a second take  of
Xa  file  with  send/get  commands  for  the  remote  server.  The last lines of
Xtonight.cmd should be a bye and a quit.
X
X
X                              THE 'HELP' COMMAND:
X
XSyntax: help
X   or: help keyword
X   or: help {set, remote} keyword
X
XBrief help messages or menus are always available at interactive command  level
Xby  typing  a question mark at any point.  A slightly more verbose form of help
Xis available through the 'help' command.  The 'help' command with no  arguments
Xprints  a  brief  summary of how to enter commands and how to get further help.
X'help' may be followed by one of the top-level C-Kermit command keywords,  such
Xas  'send', to request information about a command.  Commands such as 'set' and
X'remote' have a further level of help.  Thus you may type 'help',  'help  set',
Xor  'help  set parity'; each will provide a successively more detailed level of
Xhelp.
X
X
X                        THE 'EXIT' AND 'QUIT' COMMANDS:
X
XThese two commands are identical.  Both of them do the following:
X
X   - Attempt to insure that the terminal is returned to normal.
X   - Relinquish access to any communication line assigned via 'set line'.
X   - Close any open log files.
X   - Relinquish any uucp and multiuser locks on the communications line.
X   - Hang up the modem, if the communications line supports data  terminal
X     ready.
X
XAfter  exit  from C-Kermit, your default directory will be the same as when you
Xstarted the program.  The 'exit' command is issued implicitly whenever C-Kermit
Xhalts normally, e.g. after a command line invocation, or after certain kinds of
Xinterruptions.
X
X
X1.4. C-Kermit under Berkeley or System III/V Unix:
X
XC-Kermit may be interrupted at command level or during file transfer by  typing
XControl-C.    The  program will perform its normal exit function, restoring the
Xterminal.  If a protocol transaction was in progress, an error packet  will  be
Xsent to the opposite Kermit so that it can terminate cleanly.
X
XC-Kermit  may  be invoked in the background ("&" on shell commmand line).  If a
Xbackground process is "killed", the user will have to manually remove any  lock
Xfile  and  may  need  to  restore  the  modem.  This is because the kill signal
X(kill(x,9)) cannot be trapped by Kermit.
X
XDuring execution of a system command, C-Kermit can often be returned to command
Xlevel  by  typing  a single Control-C.  (With System III/V, the usual interrupt
Xfunction (often  the  DEL  key)  is  replaced  by  Control-C.)    On  detecting
XControl-C,  C-Kermit  takes  its normal exit, removing lock files and restoring
Xthe communication line, modem, and/or console terminal.
X
XUnder Berkeley Unix only:
X    C-Kermit  may  also  be  interrupted  by ^Z to put the process in the back-
X    ground.  In this case the terminal is not restored.  You will have to  type
X    Control-J  followed  by  "reset"  followed by another Control-J to get your
X    terminal back to normal.  C-Kermit can be halted in  a  similar  manner  by
X    typing Control-
X    Backslash, except that instead of moving it to the background, a core  dump
X    is produced.
X
XUnder System III/V Unix:
X    The Control-\ character (or whatever control character  has  been  selected
X    via  "set  escape") at the C-Kermit command level is ignored; it is trapped
X    and will not core-dump or interrupt Kermit.
X
XControl-C, Control-Z, and Control-\ lose their normal functions during terminal
Xconnection and also during file transfer when the controlling tty line is being
Xused for packet i/o.
X
XThe BSD implementation of C-Kermit has code to take advantage of a special non-
Xstandard  kernel-mode  line  driver,  which boosts the speed of packet i/o sig-
Xnificantly.  The problem is that "raw" mode, needed for packet  i/o,  also  im-
Xplies  "cbreak" (character wakeup) mode, which is very expensive.  The new line
Xdriver is a modification of the "berknet" driver, which allowed raw mode i/o to
Xtake  place  with  process wakeup only upon receipt of a linefeed.  The Berknet
Xdriver, unfortunately, strips off the high order bit of each character and does
Xnot  allow  the line terminator to be specified.  The modification allows all 8
Xbits to pass through unmolested, allows the wakeup character to  be  specified,
Xand allows the buffer to be tested or cleared.
X
XThe  System  III/V implementation uses regular kernel drivers, but "gulps" raw-
Xmode input in large blocks, thus overcomming the usual system call overheads.
X
XIf you are running C-Kermit in "quiet mode" in the foreground, then  interrupt-
Xing  the  program  with a console interrupt like Control-C will not restore the
Xterminal to normal conversational operation.  This is because the  system  call
Xto  enable console interrupt traps will cause the program to block if it's run-
Xning in the background, and the primary reason for quiet mode is to  allow  the
Xprogram  to  run  in  the background without blocking, so that you can do other
Xwork in the foreground.
X
XIf C-Kermit is run in the background ("&" on shell commmand line), then the in-
Xterrupt  signal  (Control-C)  (and System III/V quit signal) are ignored.  This
Xprevents an interrupt signal intended for a foreground job (say a  compilation)
Xfrom being trapped by a background Kermit session.
X
X
X1.5. C-Kermit on the DEC Pro-3xx with Venix 1.0
X
XThe  DEC Professional 300 series are PDP-11/23 based personal computers.  Venix
X1.0 is a Unix v7 derivative.  It should not be confused with Venix  2.0,  which
Xresembles ATT System V. C-Kermit runs in local mode on the Pro-3xx when invoked
Xfrom the console; the default device is /dev/com1.  When connected to a  remote
Xsystem  (using  C-Kermit's  'connect'  command),  Pro/Venix itself (not Kermit)
Xprovides VT52 terminal emulation.
X
XDuring  file  transfer,  the  interruption  and  status  commands   (Control-A,
XControl-F, etc) are not available.
X
X
X1.6. C-Kermit Restrictions and Known Bugs
X
X   1. File  renaming:    When  filename collision avoidance ("warning") is
X      selected, C-Kermit constructs unique names by appending a generation
X      number  to the end of the file name.  Currently, no checking is done
X      to ensure that the result is still within the maximum length  for  a
X      file name.
X
X   2. UUCP  line  locking:  C-Kermit locks lines, to prevent UUCP and mul-
X      tiuser conflicts, when it first opens a communications line.    This
X      occurs  either when 'set line' is issued, or if the '-l' argument is
X      used, when the first 'dial', 'connect', or  protocol  operation  oc-
X      curs.    The lock is released if another 'set line' is issued, or if
X      the program quits, exits, or is terminated by Control-C.  If a  user
X      connects  and returns to the shell command level, for example to in-
X      itiate kermit by piped commands, on a communications line, the  line
X      lock  is  released  when  returning  to  the  shell.  Locking is not
X      needed, or used, if communications occur  over  the  local  terminal
X      line  (e.g.  /dev/tty).   In that case, there is no difficultly with
X      "piped" operations releasing locks and lines.
X
X   3. Removing stale lock files:  For various reasons,  lock  files  some-
X      times  get  left about after uucp or C-Kermit activities.  (The most
X      common reason is that the uucp or C-Kermit activity was "killed"  by
X      a  shell  command.)   If the lock file is owned by yourself, clearly
X      you may remove it (presuming you are not running C-Kermit or uucp in
X      the background when you discovered it).
X
X      Uucp  supports a function, called uuclean, which is customarily used
X      to remove these files after a predetermined age.  If in doubt  about
X      a  lock  file  on  the dial-out line you need, contact your system's
X      operator.
X
X   4. Modem controls:  If connection is made  over  a  communication  line
X      (rather  than  on  the controlling terminal line), and that line has
X      modem controls, (e.g. data  terminal  ready  and  carrier  detection
X      implementation),  returning  to  the shell level will disconnect the
X      conversation.  In that case, one should use  interactive  mode  com-
X      mands,  and  avoid use of piped shell-level operation (also see 'set
X      modem-dialer' and 'dial' commands.)
X
X   5. Login Scripts:  The present login scripts implementation follows the
X      Unix conventions of uucp's "L.sys" file, rather than the normal Ker-
X      mit "INPUT/OUTPUT" style.  Volunteers have indicated  an  intent  to
X      implement  the  Kermit  standard  for login scripts, and indeed even
X      others may be implemented in the future as needed.
X
X   6. Dial-out  vs  dial-in  communications  lines  C-Kermit  requires   a
X      dial-out line for the "set line" or "-l" options.  Most systems have
X      some lines dedicated to dial-in, which they enable "loggers" on, and
X      some  lines available for dial-out.  Where a line must be shared be-
X      tween dial-in and dial-out, several options  are  available  (though
X      they are, strictly speaking, outside the pervue of C-Kermit).
X
X      A  simple  shell program can be used to change directionality of the
X      line if your Unix has the enable(8) and  disable(8)  commands.    In
X      that  case, the shell program could "grep" a "who" to see if anybody
X      is logged onto the desired line; if  not,  it  could  "disable"  the
X      line.    The  shell program will need to be set-uID'ed to root.  The
X      shell program can be called from kermit prior  to  a  dial  command,
X      e.g.,  "!  mydisable.shellprog".    Prior  to  the final "quit" from
X      C-Kermit, another shell program could be executed  to  "enable"  the
X      line again.  This program also needs to be set-uID'ed to root.
X
X      If  your  Unix  lacks the enable(8) and disable(8) commands, another
X      common technique works if your system supports the  /etc/ttys  file.
X      A shell program could call up an awk program to find the line in the
X      file and set the enable byte to 0 (to directly  disable  the  line).
X      Likewise,  it  can be reenabled by a counterpart at the end.  It may
X      be necessary to pause for 60 seconds after modifying that  file  be-
X      fore the logger sees it and actually disables the line.
X
X   7. Using  C-Kermit  on  Local Area Networks:  C-Kermit can successfully
X      operate at speeds up to 9600 baud over LANs.  Testing has,  however,
X      shown  that  most  processors,  whether PC/XTs with PC/IX, or Vaxes,
X      need flow control at these rates.  A command, "set flow x" should be
X      issued to each end of this form of connection.
X
X      If  the  LAN  is  the  sort with an interface card (or box, like the
X      Sytek), then the interface card/box must be programmed to handle the
X      flow control characters (xon and xoff) at the card/box level (rather
X      than forwarding these characters to the remote site).  This  is  be-
X      cause  packetizing  LANs  will not deliver the xoff character to the
X      other end, after packetization, until long after the receive  buffer
X      has been overrun.
X
X   8. Resetting terminal after abnormal termination or kill: When C-Kermit
X      terminates abnormally (say, for example, by a kill command issued by
X      the  operator)  the  user  may need to reset the terminal state.  If
X      commands do not seem  to  be  accepted  at  the  shell  prompt,  try
X      Control-J  "stty  sane"  Control-J  (use  "reset" on Berkeley Unix).
X      That should take the terminal out of "raw  mode"  if  it  was  stuck
X      there.
X
X   9. Remote  host  commands  may  time-out  on  lengthy  activity:  Using
X      "remote host" to instruct the C-Kermit server to invoke  Unix  func-
X      tions  (like  "make")  that might take a long time to produce output
X      can cause timeout conditions.
X
X  10. PC/IX Login Scripts -- Unfound Bug:  Though login scripts appear  to
X      work  properly  on  most  processors,  in the case of the PC/XT with
X      PC/IX, it appears that longer scripts need  to  be  broken  up  into
X      shorter  scripts (invoked sequentially from the take file).  This is
X      because the portion of the script handler which checks if an  opera-
X      tion timed out seems to leave the processor in a strange state (i.e.
X      hung).
X
X
X1.7. How to Build C-Kermit for a Unix System
X
XThe C-Kermit files, as distributed from Columbia, all  begin  with  the  prefix
X"ck".    You  should  make  a  directory  for these files and then cd to it.  A
Xmakefile is provided to build C-Kermit for  various  Unix  systems.    As  dis-
Xtributed,  the  makefile  has  the  name "ckermi.mak".  You should rename it to
X"makefile" and then type "make xxx", where xxx is the symbol for  your  system,
Xfor  instance "make bsd" to make C-Kermit for 4.2 BSD Unix.  The result will be
Xa program called "wermit".  You should test this to make sure it works;  if  it
Xdoes,  then  you can rename it to "kermit" and install it for general use.  See
Xthe makefile for a list of the systems supported and the  corresponding  "make"
Xarguments.
X
X
X1.8. Adapting C-Kermit to Other Systems
X
XC-Kermit is designed for portability.  The level of portability is indicated in
Xparentheses after the module name: "C" means any system that has a  C  compiler
Xthat conforms to the description in "The C Programming Language" by Kernighan &
XRitchie (Prentice-Hall, 1978).  "Cf" is like "C", but also requires  "standard"
Xfeatures like printf and fprintf, argument passing via argv/argc, and so on, as
Xdescribed in Kernighan & Ritchie.  "Unix" means the module should be useful un-
Xder  any  Unix  implementation;  it requires features such as fork() and pipes.
XAnything else means that the module is particular to the indicated system.  The
Xmodules are:
X
Xckmain.c, ckermi.h, ckdebu.h (Cf):
X    This is the main program.  It contains declarations  for  global  variables
X    and a small amount of code to initialize some variables and invoke the com-
X    mand parser.  In its distributed form, it assumes  that  command  line  ar-
X    guments  are passed to it via argc and argv.  Since this portion of code is
X    only several lines long, it should be easy to replace for systems that have
X    different  styles of user interaction.  The header files define symbols and
X    macros used by the various modules of  C-Kermit.    ckdebu.h  is  the  only
X    header  file  that  is included by all the C-Kermit modules, so it contains
X    not only the  debug  format  definitions,  but  also  any  system-dependent
X    typedefs.
X
Xwart.c (Cf), ckprot.w (C):
X    The ckprot module embodies the Kermit protocol state table and the code  to
X    accomplish  state switching.  It is written in "wart", a language which may
X    be regarded as a subset of the Unix "lex" lexical analyzer generator.  Wart
X    implements  enough  of lex to allow the ckprot module to function.  Lex it-
X    self was not used because it is proprietary (but lex may be used  in  place
X    of  wart  with  minor  reformatting of ckprot.w -- removal of #includes and
X    comments, which must be reinserted into  lex's  output  C  program).    The
X    protocol  module  ckprot.w  is  read  by  wart,  and a system-independent C
X    program is produced.  The syntax  of  a  Wart  program  is  illustrated  by
X    ckprot.w, and is described in ckwart.doc.
X
Xckfns.c (C):
X    The module contains all the Kermit protocol  support  functions  --  packet
X    formation,  encoding,  decoding, block check calculation, filename and data
X    conversion, protocol parameter negotiation, and high-level interaction with
X    the communication line and file system.  To accommodate small systems, this
X    module has been split into two -- ckfns.c and ckfns2.c.
X
Xckx???.c (specific to system ???):
X    For  instance,  ckxunx.c (Berkeley, Venix, ATT System III/V, and other Unix
X    systems) -- The ckx module contains  the  system-dependent  primitives  for
X    communication  line  i/o,  timers, and interrupts.  Certain important vari-
X    ables are defined in this module, which determine whether  C-Kermit  is  by
X    default  remote  or local, what the default communication device is, and so
X    forth.  The ckx module maintains its own private database of file  descrip-
X    tors and modes for the console terminal and the file transfer communication
X    line so that other modules (like ckfns or the terminal connect module) need
X    not be concerned with them.  This module will vary significantly among Unix
X    implementations since the sgtty/ioctl/termio functions and their  arguments
X    are  among  the least compatible areas of Unix.  The ckxunx.c file has con-
X    ditional compilation for the variations of Unix.
X
Xckz???.c (specific to system ???):
X    For  instance,  ckzunx.c  (Berkeley,  Venix,  System III/V Unix) -- The ckz
X    module contains system-dependent primitives for file  i/o,  wildcard  (meta
X    character)  expansion,  file existence and access checking, and system com-
X    mand execution.  It maintains an internal database of i/o "channels"  (file
X    pointers in the case of Unix) for the files C-Kermit cares about -- the in-
X    put file (the file which is being sent), the output file  (the  file  being
X    received),  the  various  logs, the screen, and so forth.  This module will
X    vary little among Unix implementations except for  the  wildcard  expansion
X    code,  which  requires  detailed knowledge of the directory structure.  The
X    ckz module also defines variables containing  the  system  command  strings
X    used  to  perform  certain functions like directory listing, file deletion,
X    etc.  The ckzunx.c file has conditional compilation for the  variations  of
X    Unix.
X
Xckuser.h, ckuser.c, ckusr2.c, ckusr3.c (Unix):
X    This is the "user interface" for C-Kermit.  It includes the command parser,
X    the screen output functions, and console input functions.  The command par-
X    ser comes in two pieces -- the traditional Unix command line decoder (which
X    is  quite  small and compact), and the interactive keyword parser (which is
X    rather large).  This module is fully replacable; its interface to the other
X    modules  is  very  simple,  and is explained at the beginning of the source
X    file.  The ckuser  module  also  includes  code  to  execute  any  commands
X    directly  which don't require the Kermit protocol -- local file management,
X    etc.  The module is rated "Unix" because it makes  occasional  use  of  the
X    system() function.
X
X    Note  that  while ckuser is logically one module, it has been split up into
X    three C source files, plus a header file for the symbols they share in com-
X    mon.   This is to accommodate small systems that cannot handle big modules.
X    ckuser.c has the command line and  top-level  interactive  command  parser;
X    ckusr2.c  has  the  help command and strings; ckusr3 has the set and remote
X    commands along with the logging, screen, and "interrupt" functions.
X
Xckcmd.c, ckcmd.h (Cf):
X    This  is an interactive command parsing package developed for C-Kermit.  It
X    is written portably enough to be usable on any system that has a C compiler
X    that  supports  functions  like  printf.    The file name parsing functions
X    depend upon primitives defined in the ckz module; if these primitives  can-
X    not  be  supplied for a certain system, then the filename parsing functions
X    can be deleted, and the package will still be useful for parsing  keywords,
X    numbers, arbitrary text strings, and so forth.  The style of interaction is
X    the same as that found on the DECSYSTEM-20.
X
Xckconu.c (Unix):
X    This is the connect module.  As supplied, it should operate in any Unix en-
X    vironment, or any C-based environment that provides  the  fork()  function.
X    The  module  requires  access  to global variables that specify line speed,
X    parity, duplex, flow control, etc,  but  invokes  functions  from  the  ckx
X    module  to  accomplish the desired settings and input/output, and functions
X    from the ckz module to perform session logging.  There is no code for  con-
X    trolling  modem  signals.    No  terminal emulation is performed, but since
X    standard i/o is used for the console, this may be piped through a  terminal
X    emulation filter.  The ckconu function may be entirely replaced, so long as
X    the global settings are honored by its replacement.  PC implementations  of
X    C-Kermit may require the ckcon module to do screen control, escape sequence
X    interpretation, etc, and may also wish to write special  code  to  get  the
X    best possible performance.
X
Xckdial.c (Unix):
X    This is the dialer module.   As  supplied,  it  handles  hayes  and  ventel
X    modems.
X
Xcklogi.c (Unix):
X    This is the login script  module.    As  supplied,  it  handles  uucp-style
X    logins.
X
XMoving C-Kermit to a new system entails:
X
X   1. Creating  a  new ckx module in C, assembler, or whatever language is
X      most appropriate for system programming on the new system.
X
X   2. Creating a new ckz module, as above.
X
X   3. If the system is not Unix-like, then a new ckuser module may be  re-
X      quired, as well as a different invocation of it from ckmain.
X
X   4. If  the  distributed connect module doesn't work or performs poorly,
X      then it may be replaced.  For instance, interrupt-driven i/o may  be
X      required, especially if the system doesn't have forks.
X
XThose  who  favor  a  different  style  of  user/program  interaction from that
Xprovided in ckuser.c may replace the entire module, for instance with one  that
Xprovides a mouse/window/icon environment, a menu/function-key environment, etc.
X
XA few guidelines should be followed to maintain portability:
X
X   - Keep variable and function names to 6 characters or less.
X
X   - Keep  modules  small.    For instance, on a PDP-11 it is necessary to
X     keep the code segment of each module below 8K in order to  allow  the
X     segment  mapping  to  occur which is necessary to run programs larger
X     than 64K on a non-I-&-D-space machine.
X
X   - Keep strings short; many compilers have restrictive maximum lengths.
X
X   - Keep (f,s)printf arguments short.  If these exceed some compiler  de-
X     pendent  maximum  (perhaps  as  short as 128, after expansion) memory
X     will be overwritten and the program will probably core dump.
X
X   - Do not introduce system dependencies into ckprot.w or ckfns*.c.
X
XIn general, remember that this program will have to be compilable by  old  com-
Xpilers and runnable on small systems.
X
X
X
XIndex
X
X
X          C-Kermit   1
X
X          TELENET   6
X
X          Unix Kermit   1
X
X
X
X                               Table of Contents
X
X1. UNIX KERMIT                                                                1
X
X   1.1. The Unix File System                                                  1
X   1.2. Command Line Operation                                                1
X   1.3. Interactive Operation                                                 3
X   1.4. C-Kermit under Berkeley or System III/V Unix:                         7
X   1.5. C-Kermit on the DEC Pro-3xx with Venix 1.0                            7
X   1.6. C-Kermit Restrictions and Known Bugs                                  7
X   1.7. How to Build C-Kermit for a Unix System                               8
X   1.8. Adapting C-Kermit to Other Systems                                    8
X
XIndex                                                                         i
!FUNKY!STUFF!

gregg@okstate.UUCP (03/08/85)

echo x - ckuser.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckuser.c
Xchar *userv = "User Interface V4.2(038), 5 Mar 85";
X
X/*  C K U S E R --  "User Interface" for Unix Kermit (Part 1)  */
X
X/*  Frank da Cruz, Columbia University Center for Computing Activities, 1985 */
X/*
X The ckuser module contains the terminal input and output functions for Unix
X Kermit.  It includes a simple Unix-style command line parser as well as
X an interactive prompting keyword command parser.  It depends on the existence
X of Unix facilities like fopen, fgets, feof, (f)printf, argv/argc, etc.  Other
X functions that are likely to vary among Unix implementations -- like setting
X terminal modes or interrupts -- are invoked via calls to functions that are
X defined in the system-dependent modules, ck[xz]*.c.
X
X The command line parser processes any arguments found on the command line,
X as passed to main() via argv/argc.  The interactive parser uses the facilities
X of the cmd package (developed for this program, but usable by any program).
X
X Any command parser may be substituted for this one.  The only requirements
X for the Kermit command parser are these:
X
X 1. Set parameters via global variables like duplex, speed, ttname, etc.
X    See ckmain.c for the declarations and descriptions of these variables.
X
X 2. If a command can be executed without the use of Kermit protocol, then
X    execute the command directly and set the variable sstate to 0. Examples
X    include 'set' commands, local directory listings, the 'connect' command.
X
X 3. If a command requires the Kermit protocol, set the following variables:
X
X    sstate                             string data
X      'x' (enter server mode)            (none)
X      'r' (send a 'get' command)         cmarg, cmarg2
X      'v' (enter receive mode)           cmarg2
X      'g' (send a generic command)       cmarg
X      's' (send files)                   nfils, cmarg & cmarg2 OR cmlist
X      'c' (send a remote host command)   cmarg
X
X    cmlist is an array of pointers to strings.
X    cmarg, cmarg2 are pointers to strings.
X    nfils is an integer.    
X
X    cmarg can be a filename string (possibly wild), or
X       a pointer to a prefabricated generic command string, or
X       a pointer to a host command string.
X    cmarg2 is the name to send a single file under, or
X       the name under which to store an incoming file; must not be wild.
X    cmlist is a list of nonwild filenames, such as passed via argv.
X    nfils is an integer, interpreted as follows:
X      -1: argument string is in cmarg, and should be expanded internally.
X       0: stdin.
X      >0: number of files to send, from cmlist.
X
X The screen() function is used to update the screen during file transfer.
X The tlog() function maintains a transaction log.
X The debug() function maintains a debugging log.
X The intmsg() and chkint() functions provide the user i/o for interrupting
X   file transfers.
X*/
X
X/* Includes */
X
X#include "ckermi.h"
X#include "ckcmd.h"
X#include "ckuser.h"
X
X/* External Kermit Variables, see ckmain.c for description. */
X
Xextern int size, spsiz, npad, timint, speed, local, server, image, flow,
X  displa, binary, fncnv, delay, parity, deblog, escape, xargc,
X  turn, duplex, cxseen, czseen, nfils, ckxech, pktlog, seslog, tralog, stdouf,
X  turnch, chklen, bctr, bctu, fsize, dfloc, mdmtyp,
X  rptflg, ebqflg, warn, quiet, cnflg, timef, mypadn;
X
Xextern long filcnt, tlci, tlco, ffc, tfc;
X
Xextern char *versio, *protv, *ckxv, *ckzv, *fnsv, *connv, *dftty, *cmdv;
Xextern char *ckxsys, *ckzsys, *cmarg, *cmarg2, **xargv, **cmlist;
Xextern char mystch, sstate, mypadc, padch, eol, ctlq, filnam[], ttname[];
Xchar *strcpy();
X
X/* Declarations from cmd package */
X
Xextern char cmdbuf[];			/* Command buffer */
X
X/* Declarations from ckz??? module */
X
Xextern char *SPACMD, *zhome();		/* Space command, home directory. */
Xextern int backgrd;			/* Kermit executing in background */
X
X/* The background flag is set by ckxunx.c (via conint() ) to note whether */
X/* this kermit is executing in background ('&' on shell command line).    */
X
X
X/* Variables and symbols local to this module */
X
Xchar line[100], *lp;			/* Character buffer for anything */
Xchar debfil[50];			/* Debugging log file name */
Xchar pktfil[50];			/* Packet log file name */
Xchar sesfil[50];			/* Session log file name */
Xchar trafil[50];			/* Transaction log file name */
X
Xint n,					/* General purpose int */
X    cflg,				/* Command-line connect cmd given */
X    action,				/* Action selected on command line*/
X    ncmd,				/* Number of commands */
X    nprm,	    	    	    	/* Number of parameters */
X    nrmt,				/* Number of remote commands */
X    repars,				/* Reparse needed */
X    tlevel,				/* Take command level */
X    cwdf = 0;				/* CWD has been done */
X
X#define MAXTAKE 20			/* Maximum nesting of TAKE files */
XFILE *tfile[MAXTAKE];			/* File pointers for TAKE command */
X
Xchar *homdir;				/* Pointer to home directory string */
Xchar cmdstr[100];
X
X/*  C M D L I N  --  Get arguments from command line  */
X/*
X Simple Unix-style command line parser, conforming with 'A Proposed Command
X Syntax Standard for Unix Systems', Hemenway & Armitage, Unix/World, Vol.1,
X No.3, 1984.
X*/
Xcmdlin() {
X    char x;
X    cmarg = "";				/* Initialize. */
X    cmarg2 = "";
X    action = cflg = 0;
X
X    while (--xargc > 0) {		/* Go through command line words */
X	*xargv++;
X	debug(F111,"xargv",*xargv,xargc);
X    	if (**xargv == '-') {		/* Got an option (begins with dash) */
X	    x = *(*xargv+1);		/* Get the option letter */
X	    x = doarg(x);		/* Go handle the option */
X	    if (x < 0) exit(0);
X    	} else {			/* No dash where expected */
X	    usage();
X	    exit(1);
X	}
X    }
X    debug(F101,"action","",action);
X    if (!local) {
X	if ((action == 'g') || (action == 'r') ||
X	    (action == 'c') || (cflg != 0))
X	    fatal("-l and -b required");
X    }
X    if (*cmarg2 != 0) {
X	if ((action != 's') && (action != 'r') &&
X	    (action != 'v'))
X	    fatal("-a without -s, -r, or -g");
X    }
X    if ((action == 'v') && (stdouf) && (!local)) {
X    	if (isatty(1))
X	    fatal("unredirected -k can only be used in local mode");
X    }
X    if ((action == 's') || (action == 'v') ||
X    	(action == 'r') || (action == 'x')) {
X	if (local) displa = 1;
X	if (stdouf) displa = 0;
X    }
X
X    if (quiet) displa = 0;		/* No display if quiet requested */
X
X    if (cflg) {
X	conect();			/* Connect if requested */
X	if (action == 0) {
X	    if (cnflg) conect();	/* And again if requested */
X	    doexit(0);			/*  exit with status = 0 */
X	}
X    }
X    if (displa) concb(escape);		/* (for console "interrupts") */
X    return(action);			/* Then do any requested protocol */
X}
X
X/*  D O A R G  --  Do a command-line argument.  */
X
Xdoarg(x) char x; {
X    int z; char *xp;
X
X    xp = *xargv+1;			/* Pointer for bundled args */
X    while (x) {
X	switch (x) {
X
Xcase 'x':				/* server */
X    if (action) fatal("conflicting actions");
X    action = 'x';
X    break;
X
Xcase 'f':
X    if (action) fatal("conflicting actions");
X    action = setgen('F',"","","");
X    break;
X
Xcase 'r':				/* receive */
X    if (action) fatal("conflicting actions");
X    action = 'v';
X    break;
X
Xcase 'k':				/* receive to stdout */
X    if (action) fatal("conflicting actions");
X    stdouf = 1;
X    action = 'v';
X    break;
X
Xcase 's': 				/* send */
X    if (action) fatal("conflicting actions");
X    if (*(xp+1)) fatal("invalid argument bundling after -s");
X    z = nfils = 0;			/* Initialize file counter, flag */
X    cmlist = xargv+1;			/* Remember this pointer */
X    while (--xargc > 0) {		/* Traverse the list */	
X	*xargv++;
X	if (**xargv == '-') {		/* Check for sending stdin */
X	    if (strcmp(*xargv,"-") != 0) break;
X	    z++;
X        }
X	nfils++;			/* Bump file counter */
X    }
X    xargc++, *xargv--;			/* Adjust argv/argc */
X    if (nfils < 1) fatal("missing filename for -s");
X    if (z > 1) fatal("-s: too many -'s");
X    if (z == 1) {
X	if (nfils == 1) nfils = 0;
X	else fatal("invalid mixture of filenames and '-' in -s");
X    }
X    if (nfils == 0) {
X	if (isatty(0)) fatal("sending from terminal not allowed");
X    }
X    debug(F101,*xargv,"",nfils);
X    action = 's';
X    break;
X
X/* cont'd... */
X
X/* ...doarg(), cont'd */
X
Xcase 'g':				/* get */
X    if (action) fatal("conflicting actions");
X    if (*(xp+1)) fatal("invalid argument bundling after -g");
X    *xargv++, xargc--;
X    if ((xargc == 0) || (**xargv == '-'))
X    	fatal("missing filename for -g");
X    cmarg = *xargv;
X    action = 'r';
X    break;
X
Xcase 'c':				/* connect before */
X    cflg = 1;
X    break;
X
Xcase 'n':				/* connect after */
X    cnflg = 1;
X    break;
X
Xcase 'h':				/* help */
X    usage();
X    return(-1);
X
Xcase 'a':				/* "as" */
X    if (*(xp+1)) fatal("invalid argument bundling after -a");
X    *xargv++, xargc--;
X    if ((xargc < 1) || (**xargv == '-'))
X    	fatal("missing name in -a");
X    cmarg2 = *xargv;
X    break;
X
Xcase 'l':				/* set line */
X    if (*(xp+1)) fatal("invalid argument bundling after -l");
X    *xargv++, xargc--;
X    if ((xargc < 1) || (**xargv == '-'))
X    	fatal("communication line device name missing");
X    strcpy(ttname,*xargv);
X    if (strcmp(ttname,dftty) == 0) local = dfloc; else local = 1;
X    break;
X
Xcase 'b':   	    			/* set baud */
X    if (*(xp+1)) fatal("invalid argument bundling");
X    *xargv++, xargc--;
X    if ((xargc < 1) || (**xargv == '-'))
X    	fatal("missing baud");
X    z = atoi(*xargv);			/* Convert to number */
X    if (chkspd(z) > -1) speed = z;	/* Check it */
X    	else fatal("unsupported baud rate");
X    break;
X
Xcase 'i':				/* Treat files as binary */
X    binary = 1;
X    break;
X
X/* cont'd... */
X
X/* ...doarg(), cont'd */
X
X
Xcase 'w':				/* File warning */
X    warn = 1;
X    break;
X
Xcase 'q':				/* Quiet */
X    quiet = 1;
X    break;
X
Xcase 'd':				/* debug */
X    debopn("debug.log");
X    break;
X
Xcase 'p':				/* set parity */
X    if (*(xp+1)) fatal("invalid argument bundling");
X    *xargv++, xargc--;
X    if ((xargc < 1) || (**xargv == '-'))
X    	fatal("missing parity");
X    switch(x = **xargv) {
X	case 'e':
X	case 'o':
X	case 'm':
X	case 's': parity = x; break;
X	case 'n': parity = 0; break;
X	default:  fatal("invalid parity");
X        }
X    break;
X
Xcase 't':
X    turn = 1;				/* Line turnaround handshake */
X    turnch = XON;			/* XON is turnaround character */
X    duplex = 1;				/* Half duplex */
X    flow = 0;				/* No flow control */
X    break;
X
Xdefault:
X    fatal("invalid argument, type 'kermit -h' for help");
X        }
X
X    x = *++xp;				/* See if options are bundled */
X    }
X    return(0);
X}
X
X/* Misc */
X
Xfatal(msg) char *msg; {			/* Fatal error message */
X    fprintf(stderr,"\r\nFatal: %s\n",msg);
X    tlog(F110,"Fatal:",msg,0l);
X    doexit(1);				/*  exit with status = 1 */
X}
X
X
Xermsg(msg) char *msg; {			/* Print error message */
X    if (!quiet) fprintf(stderr,"\r\nError - %s\n",msg);
X    tlog(F110,"Error -",msg,0l);
X}
X
X/* Interactive command parser */ 
X
X
X/* Top-Level Keyword Table */
X
Xstruct keytab cmdtab[] = {
X    "!",	   XXSHE, 0,
X    "bye",         XXBYE, 0,
X    "c",           XXCON, CM_INV,
X    "close",	   XXCLO, 0,
X    "connect",     XXCON, 0,
X    "cwd",	   XXCWD, 0,
X    "dial",	   XXDIAL, 0,
X    "directory",   XXDIR, 0,
X    "echo",        XXECH, 0,
X    "exit",	   XXEXI, 0,
X    "finish",	   XXFIN, 0,
X    "get",	   XXGET, 0,
X    "help",	   XXHLP, 0,
X    "log",  	   XXLOG, 0,
X    "quit",	   XXQUI, 0,
X    "r",           XXREC, CM_INV,
X    "receive",	   XXREC, 0,
X    "remote",	   XXREM, 0,
X    "s",           XXSEN, CM_INV,
X    "script",	   XXLOGI, 0,
X    "send",	   XXSEN, 0,
X    "server",	   XXSER, 0,
X    "set",	   XXSET, 0,
X    "show", 	   XXSHO, 0,
X    "space",       XXSPA, 0,
X    "statistics",  XXSTA, 0,
X    "take",	   XXTAK, 0
X};
Xint ncmd = (sizeof(cmdtab) / sizeof(struct keytab));
X
X/* Parameter keyword table */
X
Xstruct keytab prmtab[] = {
X    "baud",	        XYSPEE,  CM_INV,
X    "block-check",  	XYCHKT,  0,
X    "delay",	    	XYDELA,  0,
X    "duplex",	    	XYDUPL,  0,
X    "end-of-packet",    XYEOL,   0,
X    "escape-character", XYESC,   0,
X    "file", 	  	XYFILE,  0,
X    "flow-control", 	XYFLOW,  0,
X    "handshake",    	XYHAND,  0,
X    "line",             XYLINE,  0,
X    "modem-dialer",	XYMODM,	 0,
X    "packet-length",    XYLEN,   0,
X    "pad-character",    XYPADC,  0,
X    "padding",          XYNPAD,  0,
X    "parity",	    	XYPARI,  0,
X    "prompt",	    	XYPROM,  0,
X    "speed",	        XYSPEE,  0,
X    "start-of-packet",  XYMARK,  0,
X    "timeout",	        XYTIMO,  0
X};
Xint nprm = (sizeof(prmtab) / sizeof(struct keytab)); /* How many parameters */
X
X
X/* Remote Command Table */
X
Xstruct keytab remcmd[] = {
X    "cwd",       XZCWD, 0,
X    "delete",    XZDEL, 0,
X    "directory", XZDIR, 0,
X    "help",      XZHLP, 0,
X    "host",      XZHOS, 0,
X    "space",	 XZSPA, 0,
X    "type", 	 XZTYP, 0,
X    "who",  	 XZWHO, 0
X};
Xint nrmt = (sizeof(remcmd) / sizeof(struct keytab));
X
Xstruct keytab logtab[] = {
X    "debugging",    LOGD, 0,
X    "packets",	    LOGP, 0,
X    "session",      LOGS, 0,
X    "transactions", LOGT, 0
X};
Xint nlog = (sizeof(logtab) / sizeof(struct keytab));
X
X/* Show command arguments */
X
X#define SHPAR 0				/* Parameters */
X#define SHVER 1				/* Versions */
X
Xstruct keytab shotab[] = {
X    "parameters", SHPAR, 0,
X    "versions",   SHVER, 0
X};
X
X/*  C M D I N I  --  Initialize the interactive command parser  */
X
Xcmdini() {
X
X    printf("%s,%s\nType ? for help\n",versio,ckxsys);
X    cmsetp("C-Kermit>");		/* Set default prompt. */
X
X    tlevel = -1;			/* Take file level */
X
X/* Look for init file ".kermrc" in home or current directory. */
X
X    homdir = zhome();
X    lp = line;
X    if (homdir)
X	sprintf(lp,"%s/.kermrc",homdir);
X    else
X    	sprintf(lp,".kermrc");
X    if ((tfile[0] = fopen(line,"r")) != NULL) {
X	tlevel = 0;
X	debug(F110,"init file",line,0);
X    }
X    if (homdir && (tlevel < 0)) {
X    	sprintf(lp,".kermrc");
X	if ((tfile[0] = fopen(line,"r")) != NULL) {
X	    tlevel = 0;
X	    debug(F110,"init file",line,0);
X	} else {
X	    debug(F100,"no init file","",0);
X        }
X    }
X
X    congm();				/* Get console tty modes */
X}
X
X
X/*  T R A P  --  Terminal interrupt handler */
X
Xtrap() {
X    debug(F100,"terminal interrupt...","",0);
X    doexit(0);				/* exit with status = 0 */
X}
X
X/*  P A R S E R  --  Top-level interactive command parser.  */
X
Xparser() {
X    int xx;
X    concb(escape);		/* Put console in cbreak mode. */
X    conint(trap);		/* Turn on console terminal interrupts. */
X/*
X sstate becomes nonzero when a command has been parsed that requires some
X action from the protocol module.  Any non-protocol actions, such as local
X directory listing or terminal emulation, are invoked directly from below.
X*/
X    if (local) printf("\n");		/*** Temporary kludge ***/
X    sstate = 0;				/* Start with no start state. */
X    while (sstate == 0) {		/* Parse cmds until action requested */
X	while ((tlevel > -1) && feof(tfile[tlevel])) { /* If end of take */
X		fclose(tfile[tlevel]);	/* file, close it */
X		tlevel--;		/* and forget about it. */
X		cmini(ckxech);		/* and clear the cmd buffer. */
X 	}
X	if (tlevel > -1) {
X	    if (fgets(cmdbuf,CMDBL,tfile[tlevel]) == NULL) continue;
X	    stripq(cmdbuf);		/* Strip any quotes. */
X	} else {			/* Otherwise. */
X	    prompt();			/* Issue interactive prompt. */
X	    cmini(ckxech);
X    	}
X	repars = 1;
X	displa = 0;
X	while (repars) {
X	    cmres();			/* Reset buffer pointers. */
X	    xx = cmkey(cmdtab,ncmd,"Command","");
X	    debug(F101,"top-level cmkey","",xx);
X	    switch (docmd(xx)) {
X		case -4:		/* EOF */
X		    doexit(0);		/*  exit with status 0 */
X	        case -1:		/* Reparse needed */
X		    repars = 1;
X		    continue;
X	    	case -2:		/* Invalid command given */
X		    if (backgrd) 	/* if in background, terminate */
X			fatal("Kermit command error in background execution");
X		    if (tlevel > -1) {	/* If in take file, quit */
X			ermsg("Kermit command error: take file terminated.");
X			fclose(tfile[tlevel]);
X			tlevel--;
X		    }
X		    cmini(ckxech);	/* (fall thru) */
X 	    	case -3:		/* Empty command OK at top level */
X		default:		/* Anything else (fall thru) */
X		    repars = 0;		/* No reparse, get new command. */
X		    continue;
X            }
X        }
X    }
X/* Got an action command; disable terminal interrupts and return start state */
X
X    if (!local) connoi();		/* Interrupts off only if remote */
X    return(sstate);
X}
X
X/*  D O E X I T  --  Exit from the program.  */
X
Xdoexit(exitstat) int exitstat; {
X    
X    ttclos();				/* Close external line, if any */
X    if (local) {
X	strcpy(ttname,dftty);		/* Restore default tty */
X	local = dfloc;			/* And default remote/local status */
X    }
X    if (!quiet) conres();		/* Restore console terminal. */
X    if (!quiet) connoi();		/* Turn off console interrupt traps. */
X
X    if (deblog) {			/* Close any open logs. */
X	debug(F100,"Debug Log Closed","",0);
X	*debfil = '\0';
X	deblog = 0;
X	zclose(ZDFILE);
X    }
X    if (pktlog) {
X	*pktfil = '\0';
X	pktlog = 0;
X	zclose(ZPFILE);
X    }
X    if (seslog) {
X    	*sesfil = '\0';
X	seslog = 0;
X	zclose(ZSFILE);
X    }
X    if (tralog) {
X	tlog(F100,"Transaction Log Closed","",0l);
X	*trafil = '\0';
X	tralog = 0;
X	zclose(ZTFILE);
X    }
X    exit(exitstat);				/* Exit from the program. */
X}
X
X/*  B L D L E N  --  Make length-encoded copy of string  */
X
Xchar *
Xbldlen(str,dest) char *str, *dest; {
X    int len;
X    len = strlen(str);
X    *dest = tochar(len);
X    strcpy(dest+1,str);
X    return(dest+len+1);
X}
X
X
X/*  S E T G E N  --  Construct a generic command  */
X
Xsetgen(type,arg1,arg2,arg3) char type, *arg1, *arg2, *arg3; {
X    char *upstr, *cp;
X
X    cp = cmdstr;
X    *cp++ = type;
X    *cp = NUL;
X    if (*arg1 != NUL) {
X	upstr = bldlen(arg1,cp);
X	if (*arg2 != NUL) {
X	    upstr = bldlen(arg2,upstr);
X	    if (*arg3 != NUL) bldlen(arg3,upstr);
X	}
X    }
X    cmarg = cmdstr;
X    debug(F110,"setgen",cmarg,0);
X
X    return('g');
X}
X
X/*  D O C M D  --  Do a command  */
X
X/*
X Returns:
X   -2: user typed an illegal command
X   -1: reparse needed
X    0: parse was successful (even tho command may have failed).
X*/ 
X
Xdocmd(cx) int cx; {
X    int x, y;
X    char *s;
X
X    switch (cx) {
X
Xcase -4:				/* EOF */
X    if (!quiet) printf("\r\n");
X    doexit(0);
Xcase -3:				/* Null command */
X    return(0);
Xcase -2:				/* Error */
Xcase -1:				/* Reparse needed */
X    return(cx);
X
Xcase XXBYE:				/* bye */
X    if ((x = cmcfm()) < 0) return(x);
X    if (!local) {
X	printf("You have to 'set line' first\n");
X	return(0);
X    }
X    sstate = setgen('L',"","","");
X    return(0);
X
Xcase XXCON:                     	/* connect */
X    if ((x = cmcfm()) < 0) return(x);
X    conres();				/* restore tty to normal mode */
X    x = conect();
X    concb(escape);			/* tty back in character mode */
X    return(x);
X
Xcase XXCWD:
X    if (cmtxt("Name of local directory, or carriage return",homdir,&s) < 0)
X    	return(-1);    
X    if (chdir(s)) perror(s);
X    cwdf = 1;
X    system("pwd");
X    return(0);
X
Xcase XXCLO:
X    x = cmkey(logtab,nlog,"Which log to close","");
X    if (x == -3) {
X	printf("?You must tell which log\n");
X	return(-2);
X    }
X    if (x < 0) return(x);
X    if ((y = cmcfm()) < 0) return(y);
X    switch (x) {
X
X	case LOGD:
X	    if (deblog == 0) {
X		printf("?Debugging log wasn't open\n");
X		return(0);
X	    }
X	    *debfil = '\0';
X	    deblog = 0;
X	    return(zclose(ZDFILE));
X
X	case LOGP:
X	    if (pktlog == 0) {
X		printf("?Packet log wasn't open\n");
X		return(0);
X	    }
X	    *pktfil = '\0';
X	    pktlog = 0;
X	    return(zclose(ZPFILE));
X
X	case LOGS:
X	    if (seslog == 0) {
X		printf("?Session log wasn't open\n");
X		return(0);
X	    }
X	    *sesfil = '\0';
X	    seslog = 0;
X	    return(zclose(ZSFILE));
X
X    	case LOGT:
X	    if (tralog == 0) {
X		printf("?Transaction log wasn't open\n");
X		return(0);
X	    }
X	    *trafil = '\0';
X	    tralog = 0;
X	    return(zclose(ZTFILE));
X
X	default:
X	    printf("\n?Unexpected log designator - %ld\n", x);
X	    return(0);
X    }
X
Xcase XXDIAL:				/* dial number */
X    if ((x = cmtxt("Number to be dialed","",&s)) < 0) return(x);
X    return( dial(s) );			/* return success 0=connected -2=fail*/
X
Xcase XXDIR:				/* directory */
X    if ((x = cmtxt("Directory/file specification","*",&s)) < 0) return(x);
X    lp = line;
X    sprintf(lp,"ls -l %s",s);
X    system(line);
X    return(0);
X
Xcase XXECH:				/* echo */
X    x = cmtxt("Material to be echoed","",&s);
X    if (x < 0) return(x);
X    printf("%s\n",s);
X    return(0);
X
Xcase XXQUI:				/* quit, exit */
Xcase XXEXI:
X    if ((x = cmcfm()) > -1) doexit(0);	/*  exit with status 0 */
X    else return(x);
X
Xcase XXFIN:				/* finish */
X    if ((x = cmcfm()) < 0) return(x);
X    if (!local) {
X	printf("You have to 'set line' first\n");
X	return(0);
X    }
X    sstate = setgen('F',"","","");
X    return(0);
X
Xcase XXGET:				/* Get */
X    if (!local) {
X	printf("\nYou have to 'set line' first\n");
X	return(0);
X    }
X    x = cmtxt("Name of remote file(s), or carriage return","",&cmarg);
X    if ((x == -2) || (x == -1)) return(x);
X
X/* If foreign file name omitted, get foreign and local names separately */
X
X    if (*cmarg == NUL) {
X
X	if (tlevel > -1) {		/* Input is from take file */
X
X	    if (fgets(line,100,tfile[tlevel]) == NULL)
X	    	fatal("take file ends prematurely in 'get'");
X	    stripq(line);
X	    cmarg = line;
X	    if (fgets(cmdbuf,CMDBL,tfile[tlevel]) == NULL)
X	    	fatal("take file ends prematurely in 'get'");
X		stripq(cmdbuf);
X	    if (*cmdbuf == NUL) cmarg2 = line; else cmarg2 = cmdbuf;
X
X        } else {			/* Input is from terminal */
X
X	    char psave[40];		/* Save old prompt */
X	    cmsavp(psave,40);
X	    cmsetp(" Remote file specification: "); /* Make new one */
X	    cmini(ckxech);
X	    x = -1;
X	    while (x < 0) {		/* Prompt till they answer */
X	    	prompt();
X	    	x = cmtxt("Name of remote file(s)","",&cmarg);
X	    	if (*cmarg == NUL) x = -1;
X	    }
X	    strcpy(line,cmarg);		/* Make a safe copy */
X	    cmarg = line;
X	    cmsetp(" Local name to store it under: ");	/* New prompt */
X	    cmini(ckxech);
X	    x = -1;
X	    while (x < 0) {		/* Again, prompt till answered */
X	    	prompt();
X	    	x = cmofi("Local file name",cmarg,&cmarg2);
X		if (x == -2) return(x);
X            }
X	    cmsetp(psave);		/* Restore old prompt. */
X	    if ((x == cmcfm()) < 0) return(-2);
X        }
X    }
X    sstate = 'r';			/* All ok, set start state. */
X    if (local) displa = 1;
X    return(0);
X
Xcase XXHLP:				/* Help */
X    x = cmkey(cmdtab,ncmd,"C-Kermit command","help");
X    return(dohlp(x));
X
Xcase XXLOG:				/* Log */
X    x = cmkey(logtab,nlog,"What to log","");
X    if (x == -3) {
X	printf("?You must specify what is to be logged\n");
X	return(-2);
X    }
X    if (x < 0) return(x);
X    return(dolog(x));
X
Xcase XXLOGI:				/* login to remote system */
X    if ((x = cmtxt("Text of login script","",&s)) < 0) return(x);
X    return( login(s) );			/* return success 0=completed -2=fail*/
X
Xcase XXREC:				/* Receive */
X    cmarg2 = "";
X    x = cmofi("Name under which to store the file, or CR","",&cmarg2);
X    if ((x == -1) || (x == -2)) return(x);
X    debug(F111,"cmofi cmarg2",cmarg2,x);
X    if ((x = cmcfm()) < 0) return(x);
X    sstate = 'v';
X    if (local) displa = 1;
X    return(0);
X
Xcase XXREM:				/* Remote */
X    if (!local) {
X	printf("\nYou have to 'set line' first\n");
X	return(-2);
X    }
X    x = cmkey(remcmd,nrmt,"Remote Kermit server command","");
X    if (x == -3) {
X	printf("?You must specify a command for the remote server\n");
X	return(-2);
X    }
X    return(dormt(x));
X
Xcase XXSEN:				/* Send */
X    cmarg = cmarg2 = "";
X    if ((x = cmifi("File(s) to send","",&s,&y)) < 0) {
X	if (x == -3) {
X	    printf("?A file specification is required\n");
X	    return(-2);
X	}
X	return(x);
X    }
X    nfils = -1;				/* Files come from internal list. */
X    strcpy(line,s);			/* Save copy of string just parsed. */
X    debug(F101,"Send: wild","",y);
X    *cmarg2 = '\0';			/* Initialize send-as name */
X    if (y == 0) {
X	if ((x = cmfld("Name to send it with",line,&cmarg2)) < 0) return(x);
X    }
X    if ((x = cmcfm()) < 0) return(x);
X    cmarg = line;			/* File to send */
X    debug(F110,"Sending:",cmarg,0);
X    debug(F110," as:",cmarg2,0);
X    sstate = 's';			/* Set start state */
X    if (local) displa = 1;
X    return(0);
X
Xcase XXSER:				/* Server */
X    if (x = (cmcfm()) < 0) return(x);
X    sstate = 'x';
X    if (local) displa = 1;
X    return(0);
X
Xcase XXSET:				/* Set */
X    x = cmkey(prmtab,nprm,"Parameter","");
X    if (x == -3) {
X	printf("?You must specify a parameter to set\n");
X	return(-2);
X    }
X    if (x < 0) return(x);
X    return(doprm(x));
X    
Xcase XXSHE:				/* Local shell command */
X    if (cmtxt("Unix shell command to execute","",&s) < 0) return(-1);
X    conres();				/* Make console normal  */
X    system(s);				/* Execute the command */
X    concb(escape);			/* Console back in cbreak mode */
X    return(0);
X
Xcase XXSHO:				/* Show */
X    x = cmkey(shotab,2,"","parameters");
X    if (x < 0) return(x);
X    if (y = (cmcfm()) < 0) return(y);
X    switch (x) {
X
X	case SHPAR:
X	    shopar();
X	    break;
X
X	case SHVER:
X	    printf("\nVersions:\n %s\n %s\n %s\n",versio,protv,fnsv);
X	    printf(" %s\n %s\n %s for%s\n",cmdv,userv,ckxv,ckxsys);
X            printf(" %s for%s\n %s\n\n",ckzv,ckzsys,connv);
X	    break;
X
X	default:
X	    printf("\nNothing to show...\n");
X	    break;
X    }	    
X    return(0);
X
Xcase XXSPA:				/* space */
X    if (x = (cmcfm()) < 0) return(x);
X    system(SPACMD);
X    return(0);
X
Xcase XXSTA:				/* statistics */
X    if (x = (cmcfm()) < 0) return(x);
X    printf("\nMost recent transaction --\n");
X    printf(" files: %ld\n",filcnt);
X    printf(" total file characters  : %ld\n",tfc);
X    printf(" communication line in  : %ld\n",tlci);
X    printf(" communication line out : %ld\n\n",tlco);
X    printf(" block check type used  : %d\n",bctu);
X    printf(" compression            : ");
X    if (rptflg) printf("yes\n"); else printf("no\n");
X    printf(" 8th bit prefixing      : ");
X    if (ebqflg) printf("yes\n"); else printf("no\n\n");
X    return(0);
X
Xcase XXTAK:				/* take */
X    if (tlevel > MAXTAKE-1) {
X	printf("?Take files nested too deeply\n");
X	return(-2);
X    }
X    if ((y = cmifi("C-Kermit command file","",&s,&x)) < 0) { 
X	if (y == -3) {
X	    printf("?A file specification is required\n");
X	    return(-2);
X	} else return(y);
X    }
X    if (x != 0) {
X	printf("?Wildcards not allowed in command file name\n");
X	return(-2);
X    }
X    strcpy(line,s);			/* Make a safe copy of the string */
X    if ((y = cmcfm()) < 0) return(y);
X    if ((tfile[++tlevel] = fopen(line,"r")) == NULL) {
X	perror("take");
X	printf("Can't open command file - %s\n",line);
X	debug(F110,"Failure to open",line,0);
X	tlevel--;
X	return(0);
X    }
X    return(0);
X
Xdefault:
X    printf("Not available yet - %s\n",cmdbuf);
X    return(-2);
X    }
X}
X
X/*  S H O P A R  --  Show Parameters  */
X
Xshopar() {
X
X    printf("\nLine: %s, speed: %d, mode: ",ttname,speed);
X    if (local) printf("local"); else printf("remote");
X    printf(", modem-dialer: ");
X    if (mdmtyp == 1) printf("hayes");
X	else if (mdmtyp == 2) printf("ventel");
X	else printf("direct");
X    printf("\n Parity: ");
X    switch (parity) {
X	case 'e': printf("even");  break;
X	case 'o': printf("odd");   break;
X	case 'm': printf("mark");  break;
X	case 's': printf("space"); break;
X	case 0:   printf("none");  break;
X	default:  printf("invalid - %d",parity); break;
X    }		
X    printf(", duplex: ");
X    if (duplex) printf("half, "); else printf("full, ");
X    printf("flow: ");
X    if (flow == 1) printf("xon/xoff");
X	else if (flow == 0) printf("none");
X	else printf("%d",flow);
X    printf(", handshake: ");
X    if (turn) printf("%d\n",turnch); else printf("none\n");
X    printf(" Timeout: %d, delay: %d\n",timint,delay);
X    printf(" Padding: %d, pad character: %d\n",mypadn,mypadc);
X    printf(" Packet start: %d, end: %d, length: %d",mystch,eol,spsiz);
X    printf(", block check: %d\n",bctr);
X    printf("\nFile parameters:\n");
X    printf(" Names:        ");
X    if (fncnv) printf("converted\n"); else printf("literal\n");
X    printf(" Type:         ");
X    if (binary) printf("binary\n"); else printf("text\n");
X    printf(" Warning:      ");
X    if (warn) printf("on\n"); else printf("off\n");
X    printf(" Display:      ");
X    if (quiet) printf("off\n"); else printf("on\n");
X    printf("\nLogs:");
X    printf("\n Debugging:    ");
X    if (deblog) printf("%s",debfil); else printf("none");
X    printf("\n Packets:      ");
X    if (pktlog) printf("%s",pktfil); else printf("none");
X    printf("\n Session:      ");
X    if (seslog) printf("%s",sesfil); else printf("none");
X    printf("\n Transactions: ");
X    if (tralog) printf("%s",trafil); else printf("none");
X    printf("\n\n");
X}
X
X/*  D O L O G  --  */
X
Xdolog(x) int x; {
X    int y; char *s;
X
X    switch (x) {
X
X	case LOGD:
X	    y = cmofi("Name of debugging log file","debug.log",&s);
X	    break;
X
X	case LOGP:
X	    y = cmofi("Name of packet log file","packet.log",&s);
X	    break;
X
X	case LOGS:
X	    y = cmofi("Name of session log file","session.log",&s);
X	    break;
X
X	case LOGT:
X	    y = cmofi("Name of transaction log file","transaction.log",&s);
X	    break;
X
X	default:
X	    printf("\n?Unexpected log designator - %d\n",x);
X	    return(-2);
X    }
X    if (y < 0) return(y);
X
X    strcpy(line,s);
X    s = line;
X    if ((y = cmcfm()) < 0) return(y);
X
X/* cont'd... */
X
X/* ...dolog, cont'd */
X
X
X    switch (x) {
X
X	case LOGD:
X	    return(deblog = debopn(s));
X
X	case LOGP:
X	    zclose(ZPFILE);
X	    y = zopeno(ZPFILE,s);
X	    if (y > 0) strcpy(pktfil,s); else *pktfil = '\0';
X	    return(pktlog = y);
X
X	case LOGS:
X	    zclose(ZSFILE);
X	    y = zopeno(ZSFILE,s);
X	    if (y > 0) strcpy(sesfil,s); else *sesfil = '\0';
X	    return(seslog = y);
X
X	case LOGT:
X	    zclose(ZTFILE);
X	    tralog = zopeno(ZTFILE,s);
X	    if (tralog > 0) {
X		strcpy(trafil,s);
X		tlog(F110,"Transaction Log:",versio,0l);
X		tlog(F100,ckxsys,"",0);
X		ztime(&s);
X		tlog(F100,s,"",0l);
X    	    }
X	    else *trafil = '\0';
X	    return(tralog);
X
X	default:
X	    return(-2);
X    }
X}
X
X
X/*  D E B O P N  --  Open a debugging file  */
X
Xdebopn(s) char *s; {
X    char *tp;
X    zclose(ZDFILE);
X    deblog = zopeno(ZDFILE,s);
X    if (deblog > 0) {
X	strcpy(debfil,s);
X	debug(F110,"Debug Log ",versio,0);
X	debug(F100,ckxsys,"",0);
X	ztime(&tp);
X	debug(F100,tp,"",0);
X    } else *debfil = '\0';
X    return(deblog);
X}
X
!FUNKY!STUFF!

gregg@okstate.UUCP (03/08/85)

echo x - ckcmd.h
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckcmd.h
X/*  C K C M D . H  --  Header file for cmd package  */
X
X/* Sizes of things */
X
X#define HLPLW  78			/* Width of ?-help line */
X#define HLPCW  19			/* Width of ?-help column */
X#define CMDBL  200			/* Command buffer length */
X#define HLPBL  100			/* Help string buffer length */
X#define ATMBL  100			/* Command atom buffer length*/
X
X/* Special characters */
X
X#define NUL  '\0'			/* Null */
X#define HT   '\t'			/* Horizontal Tab */
X#define NL   '\n'			/* Newline */
X#define FF   0014			/* Formfeed    (^L) */
X#define RDIS 0022			/* Redisplay   (^R) */
X#define LDEL 0025			/* Delete line (^U) */
X#define WDEL 0027			/* Delete word (^W) */
X#define ESC  0033			/* Escape */
X#define RUB  0177			/* Rubout */
X
X#ifndef BEL
X#define BEL  0007			/* Bell */
X#endif
X
X#ifndef BS
X#define BS   0010			/* Backspace */
X#endif
X
X#ifndef SP
X#define SP   0040			/* Space */
X#endif
X
X/* Keyword table flags */
X
X#define CM_INV 1			/* Invisible keyword */
X
X/* Keyword Table Template */
X
Xstruct keytab {				/* Keyword table */
X    char *kwd;				/* Pointer to keyword string */
X    int val;				/* Associated value */
X    int flgs;				/* Flags (as defined above) */
X};
!FUNKY!STUFF!
echo x - ckdebu.h
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckdebu.h
X/*  C K D E B U . H  */
X
X/*
X This file is included by all C-Kermit modules, including the modules
X that aren't specific to Kermit (like the command parser and the ck[xz]*
X modules.  It specifies format codes for debug(), tlog(), and similar
X functions, and includes any necessary typedefs.
X*/
X
X#define F000 0				/* Formats for debug() */
X
X#define F001 1
X#define F010 2
X#define F011 3
X#define F100 4
X#define F101 5
X#define F110 6
X#define F111 7
X
X/* Compiler dependencies */
X
X#ifdef PROVX1
Xtypedef char CHAR;
Xtypedef long LONG;
Xtypedef int void;
X#else
Xtypedef unsigned char CHAR;
Xtypedef unsigned long LONG;
X#endif
!FUNKY!STUFF!
echo x - ckermi.ann
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckermi.ann
X 5-Feb-85 16:10:21-EST,5392;000000000001
XMail-From: SY.FDC created at  5-Feb-85 16:09:41
XDate: Tue 5 Feb 85 16:09:41-EST
XFrom: Frank da Cruz <SY.FDC@CU20B.ARPA>
XSubject: Info-Kermit Digest V2 #1 -- New Unix Kermit
XTo: Info-Kermit-Members@CU20B.ARPA
Xcc: Info-Unix@BRL-TGR.ARPA
XReply-To: Info-Kermit@CU20B
XQueries-To: Info-Kermit-Request@CU20B
X
XInfo-Kermit Digest         Tue,  5 Feb 1985       Volume 2 : Number  1
X
X  ANNOUNCEMENTS -
X      New Unix Kermit Available for Testing
X
X----------------------------------------------------------------------
X
XMy apologies for the long delay since the last issue of the Info-Kermit
XDigest, which was Vol.1, No.46, dated 31 December 1984.  This first issue
Xof Volume 2 is to announce a test release of the new Unix Kermit.  In
Xsubsequent issues, I'll attempt to catch up on other overdue items.
X
XA new Kermit program has been written in C, initially for 4.2 Berkeley Unix.
XThe features of this program include:
X
X. Full implementation of the Kermit protocol, except for Attribute packets:
X  - Acts as server
X  - Talks to server
X  - All packet encoding and error checking options are provided
X  - File transfer interruption
X  - Filename collision avoidance
X  - Binary and text file transfer
X. Modular construction for easy portability to other systems
X. An interactive command parser as well as Unix-style command line arguments
X. Command and initialization files
X. Piped operation
X. Improved terminal connect, with optional logging
X. Logs for debugging, packets, and transactions
X. Communication with IBM mainframes
X
XSeveral items on the wish list were not done for lack of time.  They will
Xprobably be added in the future:
X
X. File attributes
X. Command macros
X. Login scripts
X. Raw file transmit
X
XThe new program is called "C-Kermit" because it is intended as a basis for
XKermit programs for any systems that have C compilers.  Its version number
Xis 4.0, to distinguish it from earlier releases of Unix Kermit, the most
Xrecent of which was 3.0.
X
XThis prerelease test version of the program runs only under Berkeley Unix 4.2.
XWe also intend to bring it to the following systems within the coming weeks:
X
X. DEC Pro-350 and Pro-380 with Venix (a Unix v7 derivative)
X. Amdahl UTS on IBM 370-series mainframes
X. Apple Macintosh (maybe)
X
XSupport for other systems will have to be added elsewhere.  The program is
Xbeing "pre-released" at this time for two reasons:
X
X1. It seems to be perfectly usable on Berkeley 4.2 systems, and is an
X   improvement over the previous version.
X
X2. The modular design may need some adjustment to accommodate certain systems.
X   Before a great deal of additional coding is done, it is highly desirable
X   to get the design and specification of the system-dependent modules stable.
X
XTherefore, please take the files, read the documentation, try running the
Xprogram on your Berkeley Unix system if you have one, and send comments or bug
Xreports to me as soon as you can.  If you have a Unix system that is not
XBerkeley Unix, or a non-Unix system with a C compiler, please take a look at
Xthe system-dependent modules to see how they could be adapted to your system;
Xagain, if you have any suggestions or criticisms of the design, please let me
Xknow.  I'm particularly interested in issues of portability.  After a round or
Xtwo of this, perhaps the design can be agreed upon, and then those who would
Xlike to contribute support for Version 6, System III, System V, Xenix, PC/IX,
Xetc etc, can do so without fear of running into other people's changes for
Xother systems.  Before attempting to adapt C-Kermit to a new system, please
Xlet me know so I can tell you whether someone else is already at work on the
Xsame thing, and perhaps put you in touch.
X
XThe files are on CU20B as KER:CK*.*, available via anonymous FTP.  The file
XCKERMI.DOC provides user-level documentation as well as a description of the
Xprogram organization and hints for adapting it to new systems.  Within several
Xdays the files should also be available on BITNET via KERMSRV (to get started
Xwith KERMSRV, type SMSG RSCS MSG CUVMA KERMSRV HELP), and to Unix systems via
XUUCP from Oklahoma State University, Stillwater, OK.
X
XHere's how to UUCP to OK State:
X
XYou need to set up "okstate" as a site in your "L.sys" UUCP dialing file
Xusing the information listed below.  You can then issue the following 
Xcommand on your system:
X
X	uucp okstate\!/u/kermit/ck\* /usr/spool/uucppublic
X
X   (this example will retrieve the new Unix version of Kermit)
X
XThe "/usr/spool/uucppublic" is chosen as the destination on your system since
Xthe destination must be WIDE OPEN (drwxrwxrwx) to everyone.  You should
Xnot remove files from your uucppublic until the entire transfer is complete
Xincluding any redials that are necessary.  If you do remove some files
Xour system may retransmit them, resulting in a higher phone bill for you. 
X
X-- UUCP Login information --
X
XSite Name    :  okstate
XPhone number :  (405) 624-6953  (one line only)
XLogin name   :  uucpker
XPassword     :  thefrog
XHours        :  10:00pm - 10:00am central time (7 day per week)
XProblem      :  okstate!uucp-support  (UUCP)
X  reports    :  uucp-support%okstate@csnet-relay  (ARPA)
X
XThe phone number is for 300/1200 baud (bell compatible).
X
X------------------------------
X
XEnd of Info-Kermit Digest
X*************************
X-------
XFrom columbia!SY.FDC@CU20B.DECNET  Wed Mar  6 22:17:55 1985
XReceived: by cucca.UUCP (4.12/4.7)
X	id AA24373; Wed, 6 Mar 85 22:17:55 est
XReceived: from CU20B.ARPA by columbia.arpa; Wed, 6 Mar 85 21:44:26 est
XDate: Wed 6 Mar 85 21:53:20-EST
XFrom: Frank da Cruz <columbia!SY.FDC@CU20B.DECNET>
XSubject: Info-Kermit Digest V2 #9, C-Kermit Release #2
XTo: Info-Kermit@CU20B.DECNET, Info-Micro@BRL-VGR.ARPA, Info-Unix@BRL.ARPA
XReply-To: Info-Kermit@CU20B
XQueries-To: Info-Kermit-Request@CU20B
X
XInfo-Kermit Digest         Wed,  6 Mar 1985       Volume 2 : Number  9
X
X               Second Pre-Release of C-Kermit for Unix
X
X----------------------------------------------------------------------
X
XDate: Wed 6 Mar 85 21:43:12-EST
XFrom: Frank da Cruz <SY.FDC@CU20B>
XSubject: Second Pre-Release of C-Kermit for Unix
XTo: Info-Kermit@CU20B
X
XThis is to announce the second "pre-release" of C-Kermit.  The first
Xpre-release (version 4.0) occurred a month ago; the program included
Xsupport only for Berkeley Unix.  This new release (4.2) includes support
Xfor:
X
X. 4.x Berkeley Unix (VAX, SUN)
X. Generic AT&T System III, System V
X. Microsoft Xenix for the PC/AT
X. Interactive on the PC/XT (PC/IX) and other systems
X. DEC Professional 3xx with Venix 1.0
X. NCR Tower
X
XAll reported bugs have been fixed (or at least fixes have been
Xattempted), and many of the restrictions lifted.  "Dial" and "script"
Xcommands have been added, along with code to support modem control and
Xdialers, uucp line locking, and the like.  The program itself has been
Xsomewhat reorganized to be more adaptable to small environments: the
Xlarger modules have been split; long character strings have been
Xshortened.
X
XMost of the new work was done by Herm Fischer of Litton Data Systems, Van
XNuys CA (HFISCHER@USC-ISIB), and there were also contributions from many
Xothers in the form of bug reports and/or fixes.  NCR Tower support came
Xfrom John Bray at Auburn University.  The new makefile (distributed as
XCKERMI.MAK) embodies procedures for building all the different versions.
X
XSince the program now runs on a variety computers, large and small, it
Xwould seem relatively safe to begin adding support for other systems
Xwithout fear that the program will have to be completely reorganized
X(again).  The only systems supported by C-Kermit so far are Unix systems;
Xrather than create a separate ckx and ckz module for each such system
X(since these systems tend to differ in small places, but still have much
Xin common), conditional compilation was used within these modules.  If
XC-Kermit is to be adapted to non-Unix systems, then a full replacement of
Xthe ckx and/or ckz modules is probably indicated.  This is what we will
Xprobably do in bringing the program up on the Macintosh.
X
XThe files are available via anonymous FTP from Internet host CU20B
X(Internet number 192.5.43.128) as KER:CK*.*.  They will appear at
Xokstate (for uucp'ing) and on KERMSRV (BITnet) shortly.  If you plan to
Xadapt this program to a new system, be sure to let me know quickly so I
Xcan prevent duplication of effort and can put people with similar
Xinterests in touch with each other.
X
X------------------------------
X
XEnd of Info-Kermit Digest
X*************************
X-------
!FUNKY!STUFF!
echo x - ckermi.h
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckermi.h
X/* ckermit.h -- Symbol and macro definitions for C-Kermit */
X
X#include <stdio.h>
X#include <ctype.h>
X#include "ckdebu.h"
X
X/* Mnemonics for ASCII characters */
X
X#define SOH	   001	    	/* ASCII Start of header */
X#define BEL        007		/* ASCII Bell (Beep) */
X#define BS         010		/* ASCII Backspace */
X#define CR         015		/* ASCII Carriage Return */
X#define XON	   021	    	/* ASCII XON */
X#define SP	   040		/* ASCII Space */
X#define DEL	   0177		/* ASCII Delete (Rubout) */
X
X/* Kermit parameters and defaults */
X
X#define MAXPACK	   94		/* Maximum packet size */
X#define RBUFL	   200 	    	/* Receive buffer length */
X#define CTLQ	   '#'		/* Control char prefix I will use */
X#define MYEBQ	   '&'		/* 8th-Bit prefix char I will use */
X#define MYRPTQ	   '~'		/* Repeat count prefix I will use */
X
X#define MAXTRY	    10	  	/* Times to retry a packet */
X#define MYPADN	    0	  	/* How many padding chars I need */
X#define MYPADC	    '\0'  	/* Which padding character I need */
X
X#define DMYTIM	    7	  	/* Default timeout interval to use. */
X#define URTIME	    10	  	/* Timeout interval to be used on me. */
X
X#define DEFTRN	    0           /* Default line turnaround handshake */
X#define DEFPAR	    0           /* Default parity */
X#define MYEOL	    CR          /* End-Of-Line character I need on packets. */
X
X#define DRPSIZ	    90	        /* Default incoming packet size. */
X#define DSPSIZ	    90	        /* Default outbound packet size. */
X
X#define DDELAY      5		/* Default delay. */
X#define DSPEED	    9600 	/* Default line speed. */
X
X/* Files */
X
X#define ZCTERM      0	    	/* Console terminal */
X#define ZSTDIO      1		/* Standard input/output */
X#define ZIFILE	    2		/* Current input file */
X#define ZOFILE      3	    	/* Current output file */
X#define ZDFILE      4	    	/* Current debugging log file */
X#define ZTFILE      5	    	/* Current transaction log file */
X#define ZPFILE      6	    	/* Current packet log file */
X#define ZSFILE      7		/* Current session log file */
X#define ZNFILS      8	    	/* How many defined file numbers */
X
X/* Macros */
X
X#define tochar(ch)  ((ch) + SP )	/* Number to character */
X#define unchar(ch)  ((ch) - SP )	/* Character to number */
X#define ctl(ch)     ((ch) ^ 64 )	/* Controllify/Uncontrollify */
X#define unpar(ch)   ((ch) & 127)	/* Clear parity bit */
!FUNKY!STUFF!
echo x - ckermi.hlp
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckermi.hlp
XThe files ck*.* comprise C-Kermit.
X
Xck*.c are the C language source files.
Xck*.h are the header files.
Xckprot.w is the "wart" source for the protocol state table.
X
Xckermi.mss is the Scribe text formatter source for the Kermit User Guide
Xchapter on C-Kermit for Unix.
X
Xckermi.doc is the plain-text documentation output from Scribe.
X
Xckermi.mak is the makefile.  It should be renamed to "makefile".  Use the
Xmakefile to build C-Kermit for your Unix system, giving the appropriate
Xcommand line argument to "make", e.g.
X
Xmake bsd	(for Berkeley Unix)
Xmake pcix	(for PC/IX)
Xmake provx1	(for Pro-350 with Venix 1.0)
X
Xetc.  The make file produces a runnable program called "wermit".  You should
Xtest it thoroughly before renaming it to "kermit".
!FUNKY!STUFF!
echo x - ckermi.mak
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckermi.mak
X# Makefile to build C-Kermit for Berkeley, Microsoft, Interactive, 
X#		and ATT Unix 
X# for Berkeley Unix 4.x, "make bsd"
X# for Microsoft xenix (/286, pc/at, etc) "make xenix"
X# for Interactive on pc/xt "make pcix"
X# for Interactive on other host "make is3"
X# for Bell generic III/V "make sys3"
X# for Pro-3xx Venix 1.0 "make provx1"
X# for NCR Tower 1632, "make tower1"
X
XLNKFLAGS=
X
Xmake: 
X	@echo 'Make what?  You must tell which system to make C-Kermit for.'
X
Xwermit: ckmain.o ckcmd.o ckuser.o ckusr2.o ckusr3.o ckprot.o ckfns.o ckfns2.o \
X		 ckconu.o ckxunx.o ckzunx.o ckdial.o cklogi.o makefile
X	cc $(LNKFLAGS) -o wermit ckmain.o ckxunx.o ckzunx.o ckfns.o ckfns2.o \
X		 ckprot.o ckcmd.o ckusr2.o ckusr3.o ckuser.o ckconu.o \
X		 ckdial.o cklogi.o
X
Xckmain.o: ckmain.c ckermi.h
X
Xckuser.o: ckuser.c ckcmd.h ckermi.h ckuser.h
X
Xckusr2.o: ckusr2.c ckcmd.h ckermi.h ckuser.h
X
Xckusr3.o: ckusr3.c ckcmd.h ckermi.h ckuser.h
X
Xckcmd.o: ckcmd.c ckcmd.h ckdebu.h
X
Xckprot.o: ckprot.w wart ckermi.h
X	wart ckprot.w ckprot.c ; cc $(CFLAGS) -c ckprot.c
X
Xckfns.o: ckfns.c ckermi.h ckdebu.h
X
Xckfns2.o: ckfns.c ckermi.h ckdebu.h
X
Xckzunx.o: ckzunx.c ckermi.h ckdebu.h
X
Xckxunx.o: ckxunx.c ckdebu.h
X
Xckconu.o: ckconu.c ckermi.h
X
Xwart: ckwart.o
X	cc $(LNKFLAGS) -o wart ckwart.o
X
Xckwart.o: ckwart.c
X
Xckdial.o: ckdial.c
X
Xcklogi.o: cklogi.c
X
X#Berkeley Unix
Xbsd:
X	make wermit "CFLAGS= -DBSD4"
X
X#Microsoft "Xenix/286" e.g., as sold by IBM for PC/AT
Xxenix:
X	make wermit "CFLAGS= -DXENIX -DUXIII -F3000 -i" "LNKFLAGS = -F3000 -i"
X
X#PC/IX, Interactive Corp System III port for IBM PC/XT as sold by IBM
Xpcix:
X	make wermit "CFLAGS= -DPCIX -DUXIII -DISIII -Dsdata=sdatax -O -i" \
X		"LNKFLAGS = -i"
X
X#interactive corp system III port --
Xis3:
X	make wermit "CFLAGS = -DISIII -DUXIII -Ddata=datax -O -i" \
X		"LNKFLAGS = -i"
X
X#plain old Bell System III or System V without strange things
Xsys3:
X	make wermit "CFLAGS = -DUXIII -i -O" "LNKFLAGS = -i"
X
X#DEC Pro-3xx with Venix 1.0
Xprovx1:
X	make wart "CFLAGS= " "LNKFLAGS= "
X	make wermit "CFLAGS = -DPROVX1 -md780" \
X		"LNKFLAGS= -u _sleep -lc -md780"
X
X#NCR Tower 1632, OS 1.02
Xtower1:
X	make wermit "CFLAGS = -DTOWER1"
X
!FUNKY!STUFF!
echo x - ckprot.w
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckprot.w
Xchar *protv = "C-Kermit Protocol Module 4.2(015), 5 Mar 85"; /* -*-C-*- */
X
X/* C K P R O T  -- C-Kermit Protocol Module, in Wart preprocessor notation. */
X
X/* Authors: Jeff Damens, Bill Catchings, Frank da Cruz (Columbia University) */
X
X#include "ckermi.h"
X/*
X Note -- This file may also be preprocessed by the Unix Lex program, but 
X you must indent the above #include statement before using Lex, and then
X restore it to the left margin in the resulting C program before compilation.
X Also, the invocation of the "wart()" function below must be replaced by an
X invocation  of the "yylex()" function.  It might also be necessary to remove
X comments in the %%...%% section.
X*/
X
X
X/* State definitions for Wart (or Lex) */
X
X%states ipkt rfile rdata ssinit ssdata sseof sseot serve generic get rgen
X
X
X/* External C-Kermit variable declarations */
X
X  extern char sstate, *versio, *srvtxt, *cmarg, *cmarg2;
X  extern char data[], filnam[], srvcmd[], ttname[], *srvptr;
X  extern int pktnum, timint, nfils, image, hcflg, xflg, speed, flow, mdmtyp;
X  extern int prvpkt, cxseen, czseen, server, local, displa, bctu, bctr, quiet;
X  extern int putsrv(), puttrm(), putfil(), errpkt();
X  extern char *DIRCMD, *DELCMD, *TYPCMD, *SPACMD, *SPACM2, *WHOCMD;
X
X
X/* Local variables */
X
X  static char vstate = 0;  		/* Saved State   */
X  static char vcmd = 0;    		/* Saved Command */
X  static int x;				/* General-purpose integer */
X
X
X/* Macros - Note, BEGIN is predefined by Wart (and Lex) */
X
X#define SERVE  tinit(); BEGIN serve
X#define RESUME if (server) { SERVE; } else return
X
X%%
X/* Protocol entry points, one for each start state (sstate) */
X
Xs { tinit();	    	    	    	/* Do Send command */
X    if (sinit()) BEGIN ssinit;
X       else RESUME; }
X
Xv { tinit(); BEGIN get; }                                       /* Receive */
Xr { tinit(); vstate = get;  vcmd = 0;   sipkt(); BEGIN ipkt; }	/* Get */
Xc { tinit(); vstate = rgen; vcmd = 'C'; sipkt(); BEGIN ipkt; }	/* Host */
Xg { tinit(); vstate = rgen; vcmd = 'G'; sipkt(); BEGIN ipkt; }	/* Generic */
X
Xx { SERVE; }	    	    	    	/* Be a Server */
X
X/* Dynamic states: <current-states>input-character { action } */
X
X<rgen,get,serve>S { rinit(data); bctu = bctr; BEGIN rfile; } /* Send-Init */
X
X<ipkt>Y  { spar(data);			/* Get ack for I-packet */
X    	   if (vcmd) { scmd(vcmd,cmarg); vcmd = 0; }
X    	   if (vstate == get) srinit();
X	   BEGIN vstate; }
X
X<ipkt>E  { if (vcmd) scmd(vcmd,cmarg);	/* Get E for I-packet (ignore) */
X    	   vcmd = 0; if (vstate == get) srinit();
X	   BEGIN vstate; }
X
X<serve>R { srvptr = srvcmd; decode(data,putsrv); /* Get Receive-Init */
X	   cmarg = srvcmd;  nfils = -1;
X    	   if (sinit()) BEGIN ssinit; else { SERVE; } }
X
X<serve>I { spar(data); rpar(data); ack1(data);	 /* Get Init Parameters */
X	   pktnum = 0; prvpkt = -1; }
X
X<serve>G { srvptr = srvcmd; decode(data,putsrv); /* Get & decode command. */
X	   putsrv('\0'); putsrv('\0');
X	   sstate = srvcmd[0]; BEGIN generic; }
X
X<serve>C { srvptr = srvcmd;		    	 /* Get command for shell */
X	   decode(data,putsrv); putsrv('\0');
X	   if (syscmd("",srvcmd)) BEGIN ssinit;
X	   else { errpkt("Can't do shell command"); SERVE; } }
X
X<serve>. { errpkt("Unimplemented server function"); SERVE; } /* Other */
X
X<generic>C { if (!cwd(srvcmd+1)) errpkt("Can't change directory"); /* CWD */
X    	     SERVE; }
X
X<generic>D { if (syscmd(DIRCMD,srvcmd+2)) BEGIN ssinit;	/* Directory */
X    	     else { errpkt("Can't list directory"); SERVE; } }
X
X<generic>E { if (syscmd(DELCMD,srvcmd+2)) BEGIN ssinit;	/* Erase */
X    	     else { errpkt("Can't remove file"); SERVE; } }
X
X<generic>F { ack(); return(0); }    	/* Finish */
X<generic>L { ack(); ttres(); return(kill(0,9)); } /* Bye, but no guarantee! */
X
X<generic>H { if (sndhlp()) BEGIN ssinit;
X    	     else { errpkt("Can't send help"); SERVE; } }
X
X<generic>T { if (syscmd(TYPCMD,srvcmd+2)) BEGIN ssinit;
X    	     else { errpkt("Can't type file"); SERVE; } }
X
X<generic>U { x = *(srvcmd+1);			/* Disk Usage query */
X    	     x = ((x == '\0') || (x == unchar(0)));
X	     x = (x ? syscmd(SPACMD,"") : syscmd(SPACM2,srvcmd+2));
X    	     if (x) BEGIN ssinit; else { errpkt("Can't check space"); SERVE; }}
X
X<generic>W { if (syscmd(WHOCMD,srvcmd+2)) BEGIN ssinit;
X    	     else { errpkt("Can't do who command"); SERVE; } }
X
X<generic>. { errpkt("Unimplemented generic server function"); SERVE; }
X
X/* Dynamic states, cont'd */
X
X
X<rgen>Y { decode(data,puttrm); RESUME; }    /* Got reply in ACK data */
X
X<rgen,rfile>F { if (rcvfil()) { ack(); BEGIN rdata; }	/* A file is coming */
X		else { errpkt("Can't open file"); RESUME; } }
X
X<rgen,rfile>X { opent(); ack(); BEGIN rdata; }	/* Screen data is coming */
X
X<rfile>B { ack(); reot(); RESUME; }	/* Got End Of Transmission */
X
X<rdata>D { if (cxseen) ack1("X");	/* Got data. */
X    	   else if (czseen) ack1("Z");
X	   else ack();
X	   decode(data,putfil); }
X
X<rdata>Z { ack(); reof(); BEGIN rfile; }    /* Got End Of File */
X
X<ssinit,ssdata,sseof,sseot>N { resend(); }  /* Got a NAK, resend. */
X
X<ssinit>Y {  int x; char *s;		/* Got ACK to Send-Init */
X    	     spar(data);
X    	     bctu = bctr;
X	     if (xflg) { x = sxpack(); s = "Can't execute command"; }
X	    	  else { x = sfile(); s = "Can't open file"; }
X	     if (x) BEGIN ssdata; else { errpkt(s); RESUME; }
X          }	    
X
X<ssdata>Y { if (canned(data) || !sdata()) { /* Got ACK to data */
X		clsif(); seof();
X		BEGIN sseof; } }
X
X<sseof>Y { if (gnfile() > 0) {		/* Got ACK to EOF, get next file */
X		if (sfile()) BEGIN ssdata;
X		else { errpkt("Can't open file") ; RESUME; }
X	   } else {			/* If no next file, EOT */
X		seot();
X		BEGIN sseot; } }
X
X<sseot>Y { RESUME; }			/* Got ACK to EOT */
X
XE { ermsg(data);			/* Error packet, issue message */
X    x = quiet; quiet = 1;		/* Close files silently */
X    clsif(); clsof();
X    quiet = x; RESUME; }
X
X. { nack(); }				/* Anything else, send NAK */
X%%
X
X/*  P R O T O  --  Protocol entry function  */
X
Xproto() {
X
X    extern int sigint();
X    int x;
X
X    conint(sigint);			/* Enable console interrupts */
X
X/* Set up the communication line for file transfer. */
X
X    if (local && (speed < 0)) {
X	screen(2,0l,"Sorry, you must 'set speed' first");
X	return;
X    }
X    if (ttopen(ttname,local,mdmtyp) < 0) {
X	screen(2,0l,"Can't open line");
X	return;
X    }
X    x = (local) ? speed : -1;
X    if (ttpkt(x,flow) < 0) {		/* Put line in packet mode, */
X	screen(2,0l,"Can't condition line"); /* setting speed, flow control */
X	return;
X    }
X    if (sstate == 'x') {		/* If entering server mode, */
X	server = 1;			/* set flag, */
X	if (!quiet) {
X	    if (!local)			/* and issue appropriate message. */
X	    	conol(srvtxt);
X	    else {
X	    	conol("Entering server mode on ");
X		conoll(ttname);
X	    }
X	}
X    } else server = 0;
X    sleep(1);
X
X/*
X The 'wart()' function is generated by the wart program.  It gets a
X character from the input() routine and then based on that character and
X the current state, selects the appropriate action, according to the state
X table above, which is transformed by the wart program into a big case
X statement.  The function is active for one transaction.
X*/
X
X    wart();				/* Enter the state table switcher. */
X    
X    if (server) {			/* Back from packet protocol. */
X	server = 0;
X    	if (!quiet)  			/* Give appropriate message */
X	    conoll("C-Kermit server done");
X    } else
X    	screen(BEL,0l,"");		/* Or beep */
X}
!FUNKY!STUFF!
echo x - ckuser.h
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckuser.h
X/*  C K U S E R . H  --  Symbol definitions for C-Kermit ckus*.c modules  */
X
X
X/* Values associated with top-level commands, must be 0 or greater. */
X
X#define XXBYE   0	/* BYE */
X#define XXCLE   1	/* CLEAR */
X#define XXCLO   2	/* CLOSE */
X#define XXCON   3	/* CONNECT */
X#define XXCPY   4	/* COPY */
X#define XXCWD   5	/* CWD (Change Working Directory) */
X#define XXDEF	6	/* DEFINE (a command macro) */
X#define XXDEL   7	/* (Local) DELETE */
X#define XXDIR   8	/* (Local) DIRECTORY */
X#define XXDIS   9	/* DISCONNECT */
X#define XXECH  10	/* ECHO */
X#define XXEXI  11	/* EXIT */
X#define XXFIN  12	/* FINISH */
X#define XXGET  13	/* GET */
X#define XXHLP  14	/* HELP */
X#define XXINP  15	/* INPUT */
X#define XXLOC  16	/* LOCAL */
X#define XXLOG  17	/* LOG */
X#define XXMAI  18	/* MAIL */
X#define XXMOU  19	/* (Local) MOUNT */
X#define XXMSG  20	/* (Local) MESSAGE */
X#define XXOUT  21	/* OUTPUT */
X#define XXPAU  22	/* PAUSE */
X#define XXPRI  23	/* (Local) PRINT */
X#define XXQUI  24	/* QUIT */
X#define XXREC  25	/* RECEIVE */
X#define XXREM  26	/* REMOTE */
X#define XXREN  27	/* (Local) RENAME */
X#define XXSEN  28	/* SEND */
X#define XXSER  29   	/* SERVER */
X#define XXSET  30	/* SET */
X#define XXSHE  31	/* Command for SHELL */
X#define XXSHO  32	/* SHOW */
X#define XXSPA  33	/* (Local) SPACE */
X#define XXSTA  34	/* STATISTICS */
X#define XXSUB  35	/* (Local) SUBMIT */
X#define XXTAK  36	/* TAKE */
X#define XXTRA  37	/* TRANSMIT */
X#define XXTYP  38	/* (Local) TYPE */
X#define XXWHO  39	/* (Local) WHO */
X#define XXDIAL 40	/* (Local) dial */
X#define XXLOGI 41	/* (Local) logon */
X
X/* SET parameters */
X
X#define XYBREA  0	/* BREAK simulation */
X#define XYCHKT  1	/* Block check type */
X#define XYDEBU  2	/* Debugging */
X#define XYDELA  3	/* Delay */
X#define XYDUPL  4	/* Duplex */
X#define XYEOL   5	/* End-Of-Line (packet terminator) */
X#define XYESC   6	/* Escape character */
X#define XYFILE  7	/* File Parameters */
X#define   XYFILN 0  	/*  Naming  */
X#define   XYFILT 1  	/*  Type    */
X#define   XYFILW 2      /*  Warning */
X#define   XYFILD 3      /*  ...     */
X/* empty space to add something */
X#define XYFLOW  9	/* Flow Control */
X#define XYHAND 10	/* Handshake */
X#define XYIFD  11	/* Incomplete File Disposition */
X#define XYIMAG 12	/* "Image Mode" */
X#define XYINPU 13	/* INPUT command parameters */
X#define XYLEN  14	/* Maximum packet length to send */
X#define XYLINE 15	/* Communication line to use */
X#define XYLOG  16	/* Log file */
X#define XYMARK 17	/* Start of Packet mark */
X#define XYNPAD 18	/* Amount of padding */
X#define XYPADC 19	/* Pad character */
X#define XYPARI 20	/* Parity */
X#define XYPAUS 21	/* Interpacket pause */
X#define XYPROM 22	/* Program prompt string */
X#define XYQBIN 23	/* 8th-bit prefix */
X#define XYQCTL 24	/* Control character prefix */
X#define XYREPT 25	/* Repeat count prefix */
X#define XYRETR 26	/* Retry limit */
X#define XYSPEE 27	/* Line speed (baud rate) */
X#define XYTACH 28	/* Character to be doubled */
X#define XYTIMO 29	/* Timeout interval */
X#define XYMODM 30	/* Modem type */
X
X/* REMOTE command symbols */
X
X#define XZCPY  0	/* Copy */
X#define XZCWD  1	/* Change Working Directory */
X#define XZDEL  2	/* Delete */
X#define XZDIR  3	/* Directory */
X#define XZHLP  4	/* Help */
X#define XZHOS  5	/* Host */
X#define XZKER  6	/* Kermit */
X#define XZLGI  7	/* Login */
X#define XZLGO  8	/* Logout */
X#define XZMAI  9	/* Mail */
X#define XZMOU 10	/* Mount */
X#define XZMSG 11	/* Message */
X#define XZPRI 12	/* Print */
X#define XZREN 13	/* Rename */
X#define XZSET 14	/* Set */
X#define XZSPA 15	/* Space */
X#define XZSUB 16	/* Submit */
X#define XZTYP 17	/* Type */
X#define XZWHO 18	/* Who */
X
X/* Symbols for logs */
X
X#define LOGD 0	    	/* Debugging */
X#define LOGP 1          /* Packets */
X#define LOGS 2          /* Session */
X#define LOGT 3          /* Transaction */
!FUNKY!STUFF!
echo x - ckwart.doc
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckwart.doc
XWART
X
XWart is a program that implements a small subset of the Unix 'lex'
Xlexical analyzer generator.  Unlike lex, wart may be distributed without
Xrequirement for a Unix license.  Wart was written at the Columbia University
XCenter of Computing Activities to facilitate development of Unix Kermit.
X
XWart is intended for production of state table switchers.  It allows a
Xset of states to be defined, along with a function for getting input.  The
Xperforms actions and switches states based on the current state and the
Xinput.
X
XThe following short program demonstrates some of the capabilities and
Xlimitations of Wart.  The program accepts from the command line a binary
Xnumber, preceded by an optional minus sign, and optionally containing a
Xfractional part.  It prints the decimal equivalent.
X
X#include <stdio.h>
X
Xint state, s = 1, m = 0, d;
Xfloat f;
Xchar *b;
X
X%states sign mantissa fraction		    /* Declare wart states */
X
X%%					    /* Begin state table */
X<sign>-      { s = -1; BEGIN mantissa; }    /* Look for sign */
X<sign>0      { m = 0;  BEGIN mantissa; }    /* Got digit, start mantissa */
X<sign>1      { m = 1;  BEGIN mantissa; }
X<sign>.      { fatal("bad input"); }	    /* Detect bad format */
X<mantissa>0  { m *= 2; }		    /* Accumulate mantissa */
X<mantissa>1  { m = 2 * m + 1; }
X<mantissa>$  { printf("%d\n", s * m); return; }
X<mantissa>.  { f = 0.0; d = 1; BEGIN fraction; }    /* Start fraction */
X<fraction>0  { d *= 2; }		    	    /* Accumulate fraction */
X<fraction>1  { d *= 2; f += 1.0 / d; }
X<fraction>$  { printf("%f\n", s * (m + f) ); return; }
X<fraction>.  { fatal("bad input"); }
X%%
X
Xinput() {				    /* Define input() function */
X    int x;
X    return(((x = *b++) == '\0') ? '$' : x );
X}
X
Xfatal(s) char *s; {			    /* Error exit */
X    fprintf(stderr,"fatal - %s\n",s);
X    exit(1);
X}
X
Xmain(argc,argv) int argc; char **argv; {    /* Main program */
X    if (argc < 1) exit(1);
X    b = *++argv;
X    state = sign;			    /* Initialize state */
X    wart();				    /* Invoke state switcher */
X    exit(0);				    /* Done */
X}
X
XThe wart program accepts as input a C program containing lines that start
Xwith "%" or sections delimited by "%%".  The directive "%states" declares
Xthe program's states.  The section enclosed by "%%" markers is the state
Xtable, with entries of the form
X
X  <state>X { action }
X
Xwhich read as "if in state <state> with input X perform { action }"
X
XThe optional <state> field tells the current state or state the program must
Xbe in to perform the indicated action.  If no state is specified, then it
Xmeans the action will be performed regardless of the current state.  If more
Xthan one state is specifed, then the action will be performed in any of the
Xlisted states.  Multiple states are separated by commas.
X
XThe required input field consists of a single literal character.  When in
Xthe indicated state, if the input is the specified character, then the
Xassociated action will be performed.  The character '.' matches any input
Xcharacter.  No pattern matching or range notation is provided.  The input
Xcharacter is obtained from the input() function, which you must define.  It
Xshould be alphanumeric, or else one of the characters ".% -$@" (quotes not
Xincluded).  Note that the program above recognize the binary point '.'
Xthrough a ruse.
X
XThe action is a series of zero or more C language statements, enclosed in
Xcurly braces.
X
XThe BEGIN macro is defined simply to be "state = ", as in lex.
X
XThe wart() function is generated by the wart program based on the state
Xdeclarations and the state transition table.  It loops through calls to
Xinput(), using the result to index into a big case statement it has created
Xfrom the state table.
X
XWart is invoked as follows:
X
X	wart
X
XInput from stdin, output to stdout
X
X	wart fn1
X
XInput from fn1, output to stdout
X
X	wart fn1 fn2
X
XInput from fn1, output to fn2.  Example:  wart a.w a.c
X
XWart programs have the conventional filetype '.w'.
!FUNKY!STUFF!
echo x - ckwho.txt
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckwho.txt
X(18 Feb 85)
X
XThe following people have tentatively volunteered to supply support in
XC-Kermit 4.0 for the following systems:
X
X
XWhat                             Who
X
XDEC Pro-350/380, Venix           SY.FDC@CU20B (Frank da Cruz)
X
XIBM 370-series, Ahmdah UTS       SY.FDC@CU20B (Frank da Cruz)
X
XApple Macintosh                  SY.WBC3@CU20B (Bill Catchings)
X
XMasscomp RTU 2.2                 sob@RICE (Stan Barber)
X
XCoherent                         vortex!lauren@RAND-UNIX (Lauren Weinstein) 
X
XCallan UniStar 300 with
X Unisoft 68000 System V Unix     EBM@MIT-XX (Eliot Moss)
X
XATT 3Bx, System V                Chris@COLUMBIA-20 (Chris Maio)
X
XIBM PC, etc, PC/IX               HFISCHER@USC-ECLB (Herm Fischer)
X
XIBM PC, etc, Xenix               HFISCHER@USC-ECLB (Herm Fischer)
X
XVAX,PDP-11 with IS3, Interactive
X Systems version of System III   HFISCHER@USC-ECLB (Herm Fischer)
X
XOs9                              BLARSON@USC-ECL (Bob Larson)
X
XVersion 7                        vasoll%okstate.csnet@CSNET-RELAY (Mark Vasoll)
X
X4.2 UUCP Line Locking            hipl!tony@NYU-CMCL2 (Tony Movshon)
X
XHP9000 Series 200 (HP9836)
X with HP-UX System III           b-davis@utah-cs (Brad Davis)
X
XCP/M (Small C or BDS C)          bdale@cmu-cs-g (Bdale Garbee)
X
XHoneywell GCOS3/8                Carlin%HIS-PHOENIX-MULTICS@MIT-MULTICS
X
X68000 Xenix                      ED@MIT-MC (Ed Schwalenberg)
X
XVAX, 2.0 BSD                     nsc!jon@DECWRL (Jon Ryshpan)
X
XCP/M-86, De Smet C               nsc!jon@DECWRL (Jon Ryshpan)
X
XLogin scripts, raw upload        poulton%hplabs.csnet@CSNET-RELAY (Ken Poulton)
X
XApple II, Aztec C                Saline@MIT-MULTICS (Steven Saline)
!FUNKY!STUFF!

gregg@okstate.UUCP (03/08/85)

echo x - ckfns.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckfns.c
Xchar *fnsv = "C-Kermit functions, 4.2(026) 5 Mar 85";
X
X/*  C K F N S  --  System-independent Kermit protocol support functions...  */
X
X/*  ...Part 1 (others moved to ckfns2 to make this module small enough) */
X
X/*
X System-dependent primitives defined in:
X
X   ckx???.c -- terminal i/o
X   cxz???.c -- file i/o, directory structure
X
X*/
X
X#include "ckermi.h"			/* Symbol definitions for Kermit */
X
X/* Externals from ckmain.c */
X
Xextern int spsiz, timint, npad, chklen, ebq, ebqflg, rpt, rptq, rptflg, capas;
X
Xextern int pktnum, prvpkt, sndtyp, fsize, bctr, bctu,
X  size, osize, maxsize, spktl, nfils, stdouf, warn, timef;
X
Xextern int parity, speed, turn, turnch, 
X delay, displa, pktlog, tralog, seslog, xflg, mypadn;
X
Xextern long filcnt, ffc, flci, flco, tlci, tlco, tfc;
X
Xextern int deblog, hcflg, binary, fncnv, local, server, cxseen, czseen;
X
Xextern char padch, mypadc, eol, ctlq, myctlq, sstate, *hlptxt;
X
Xextern char filnam[], sndpkt[], recpkt[], data[], srvcmd[], *srvptr, stchr, 
X mystch;
X
Xextern char *cmarg, *cmarg2, **cmlist;
Xchar *strcpy();
X
X/* Variables local to this module */
X
Xstatic char *memptr;			/* Pointer for memory strings */
X
Xstatic char cmdstr[100];		/* Unix system command string */
X
Xstatic int  sndsrc;			/* Flag for where to send from: */
X					/* -1: name in cmdata */
X					/*  0: stdin          */
X					/* >0: list in cmlist */
X
Xstatic int  memstr,			/* Flag for input from memory string */
X     t,					/* Current character */
X     next;				/* Next character */
X
X/*  E N C S T R  --  Encode a string from memory. */
X
X/*  Call this instead of getpkt() if source is a string, rather than a file. */
X
Xencstr(s) char* s; {
X    int m; char *p;
X
X    m = memstr; p = memptr;		/* Save these. */
X
X    memptr = s;				/* Point to the string. */
X    memstr = 1;				/* Flag memory string as source. */
X    next = -1;				/* Initialize character lookahead. */
X    getpkt(spsiz);			/* Fill a packet from the string. */
X    memstr = m;				/* Restore memory string flag */
X    memptr = p;				/* and pointer */
X    next = -1;				/* Put this back as we found it. */
X    debug(F111,"encstr",data,size);
X}
X
X/* E N C O D E - Kermit packet encoding procedure */
X
Xencode(a) int a; {			/* The current character */
X    int a7;				/* Low order 7 bits of character */
X    int b8;				/* 8th bit of character */
X
X    if (rptflg)	{			/* Repeat processing? */
X        if (a == next) {		/* Got a run... */
X	    if (++rpt < 94)		/* Below max, just count */
X                return;
X	    else if (rpt == 94) {	/* Reached max, must dump */
X                data[size++] = rptq;
X                data[size++] = tochar(rpt);
X                rpt = 0;
X	    }
X        } else if (rpt == 1) {		/* Run broken, only 2? */
X            rpt = 0;			/* Yes, reset repeat flag & count. */
X	    encode(a);			/* Do the character twice. */
X	    if (size <= maxsize) osize = size;
X	    rpt = 0;
X	    encode(a);
X	    return;
X	} else if (rpt > 1) {		/* More than two */
X            data[size++] = rptq;	/* Insert the repeat prefix */
X            data[size++] = tochar(++rpt); /* and count. */
X            rpt = 0;			/* Reset repeat counter. */
X        }
X    }
X    a7 = a & 0177;			/* Isolate ASCII part */
X    b8 = a & 0200;			/* and 8th (parity) bit. */
X
X    if (ebqflg && b8) {			/* Do 8th bit prefix if necessary. */
X        data[size++] = ebq;
X        a = a7;
X    }
X    if ((a7 < SP) || (a7==DEL))	{	/* Do control prefix if necessary */
X        data[size++] = myctlq;
X	a = ctl(a);
X    }
X    if (a7 == myctlq)			/* Prefix the control prefix */
X        data[size++] = myctlq;
X
X    if ((rptflg) && (a7 == rptq))	/* If it's the repeat prefix, */
X        data[size++] = myctlq;		/* quote it if doing repeat counts. */
X
X    if ((ebqflg) && (a7 == ebq))	/* Prefix the 8th bit prefix */
X        data[size++] = myctlq;		/* if doing 8th-bit prefixes */
X
X    data[size++] = a;			/* Finally, insert the character */
X    data[size] = '\0';			/* itself, and mark the end. */
X}
X
X/* D E C O D E  --  Kermit packet decoding procedure */
X
X/* Call with string to be decoded and an output function. */
X
Xdecode(buf,fn) char *buf; int (*fn)(); {
X    unsigned int a, a7, b8;		/* Low order 7 bits, and the 8th bit */
X
X    rpt = 0;
X
X    while ((a = *buf++) != '\0') {
X	if (rptflg) {			/* Repeat processing? */
X	    if (a == rptq) {		/* Yes, got a repeat prefix? */
X		rpt = unchar(*buf++);	/* Yes, get the repeat count, */
X		a = *buf++;		/* and get the prefixed character. */
X	    }
X	}
X	b8 = 0;				/* Check high order "8th" bit */
X	if (ebqflg) {			/* 8th-bit prefixing? */
X	    if (a == ebq) {		/* Yes, got an 8th-bit prefix? */
X		b8 = 0200;		/* Yes, remember this, */
X		a = *buf++;		/* and get the prefixed character. */
X	    }
X	}
X	if (a == ctlq) {		/* If control prefix, */
X	    a  = *buf++;		/* get its operand. */
X	    a7 = a & 0177;		/* Only look at low 7 bits. */
X	    if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') /* Uncontrollify */
X	    a = ctl(a);			/* if in control range. */
X	}
X	a |= b8;			/* OR in the 8th bit */
X	if (rpt == 0) rpt = 1;		/* If no repeats, then one */
X	for (; rpt > 0; rpt--) {	/* Output the char RPT times */
X	    if (a == CR && !binary) break; /* But skip CR if binary. */
X	    ffc++, tfc++;		/* Count the character */
X	    (*fn)(a);			/* Send it to the output function. */
X	}
X    }
X}
X
X
X/*  Output functions passed to 'decode':  */
X
Xputsrv(c) char c; { 	/*  Put character in server command buffer  */
X    *srvptr++ = c;
X    *srvptr = '\0';	/* Make sure buffer is null-terminated */
X}
X
Xputtrm(c) char c; {     /*  Output character to console.  */
X    conoc(c);
X}
X
Xputfil(c) char c; {	/*  Output char to file. */
X    zchout(ZOFILE,c);
X}
X
X/*  G E T P K T -- Fill a packet data field  */
X
X/*
X Gets characters from the current source -- file or memory string.
X Encodes the data into the packet, filling the packet optimally.
X
X Uses global variables:
X t     -- current character.
X next  -- next character.
X data  -- the packet data buffer.
X size  -- number of characters in the data buffer.
X
XReturns the size as value of the function, and also sets global size,
Xand fills (and null-terminates) the global data array.
X
XBefore calling getpkt the first time for a given source (file or string),
Xset the variable 'next' to -1.
X*/
X
Xgetpkt(maxsize) int maxsize; {		/* Fill one packet buffer */
X    int i;				/* Loop index. */
X
X    static char leftover[6] = { '\0', '\0', '\0', '\0', '\0', '\0' };
X
X    if (next < 0) t = getch();		/* Get first character of file. */
X
X    /* Do any leftovers */
X
X    for (size = 0; (data[size] = leftover[size]) != '\0'; size++)
X    	;
X    *leftover = '\0';
X
X    /* Now fill up the rest of the packet. */
X
X    while(t >= 0) {			/* Until EOF... */
X	next = getch();			/* Get next character for lookahead. */
X	osize = size;			/* Remember current position. */
X        encode(t);			/* Encode the current character. */
X        t = next;			/* Next is now current. */
X
X	if (size == maxsize) 		/* If the packet is exactly full, */
X            return(size);		/* and return. */
X
X	if (size > maxsize) {		/* If too big, save some for next. */
X	    for (i = 0; (leftover[i] = data[osize+i]) != '\0'; i++)
X	    	;
X	    size = osize;		/* Return truncated packet. */
X	    data[size] = '\0';
X	    return(size);
X	}
X    }
X    return(size);			/* Return any partial final buffer. */
X}
X
X/*  G E T C H  -- Get the next character from file (or pipe). */
X 
X/*  Convert newlines to CRLFs if newline/CRLF mapping is being done. */
X
Xgetch()	{				/* Get next character */
X    int a, x;				/* The character to return. */
X    static int b = 0;			/* A character to remember. */
X    
X    if (b > 0) {			/* Do we have a newline saved? */
X	b = 0;				/* Yes, return that. */
X	return('\n');
X    }
X
X    if (memstr)				/* Try to get the next character */
X    	x = ((a = *memptr++) == '\0');	/* from the appropriate source, */
X    else				/* memory or the current file. */
X    	x = ((a = zchin(ZIFILE)) < 0 );
X
X    if (x)
X    	return(-1);			/* No more, return -1 for EOF. */
X    else {				/* Otherwise, read the next char. */
X	ffc++, tfc++;			/* Count it. */
X	if (a == '\n' && !binary) {	/* If nl and we must do nl-CRLF */
X	    b = a;			/* mapping, save the nl, */
X	    return(CR);			/* and return a CR. */
X	} else return(a);		/* General case, return the char. */
X    }
X}
X
X
X/*  C A N N E D  --  Check if current file transfer cancelled */
X
Xcanned(buf) char *buf; {
X    if (*buf == 'X') cxseen = 1;
X    if (*buf == 'Z') czseen = 1;
X    debug(F101,"canned: cxseen","",cxseen);
X    debug(F101," czseen","",czseen);
X    return((czseen || cxseen) ? 1 : 0);
X}
X
X/*  T I N I T  --  Initialize a transaction  */
X
Xtinit() {
X    xflg = 0;				/* reset x-packet flag */
X    memstr = 0;				/* reset memory-string flag */
X    memptr = NULL;			/*  and pointer */
X    bctu = 1;				/* reset block check type to 1 */
X    filcnt = 0;				/* reset file counter */
X    tfc = tlci = tlco = 0;		/* reset character counters */
X    prvpkt = -1;			/* reset packet number */
X    pktnum = 0;
X    if (server) {			/* If acting as server, */
X	timint = 30;			/* use 30 second timeout, */
X	nack();				/* send a NAK */
X    }
X}
X
X
X/*  R I N I T  --  Respond to S packet  */
X
Xrinit(d) char *d; {
X    char *tp;
X    ztime(&tp);
X    tlog(F110,"Transaction begins",tp,0l); /* Make transaction log entry */
X    tfc = tlci = tlco = 0;
X    spar(d);
X    rpar(d);
X    ack1(d);
X}
X
X/*  S I N I T  --  Make sure file exists, then send Send-Init packet */
X
Xsinit() {
X    int x; char *tp;
X
X    sndsrc = nfils;			/* Where to look for files to send */
X    ztime(&tp);
X    tlog(F110,"Transaction begins",tp,0l); /* Make transaction log entry */
X    debug(F101,"sinit: sndsrc","",sndsrc);
X    if (sndsrc < 0) {			/* Must expand from 'send' command */
X	nfils = zxpand(cmarg);		/* Look up literal name. */
X	if (nfils < 0) {
X	    screen(2,0l,"?Too many files");
X	    return(0);
X        } else if (nfils == 0) {	/* If none found, */
X	    char xname[100];		/* convert the name. */
X	    zrtol(cmarg,xname);
X	    nfils = zxpand(xname); 	/* Look it up again. */
X	}
X	if (nfils < 1) {		/* If no match, report error. */
X	    if (server) 
X	    	errpkt("File not found");
X	    else
X		screen(2,0l,"?File not found");
X	    return(0);
X	}
X	x = gnfile();			/* Position to first file. */
X	if (x < 1) {
X	    if (!server) 
X	    	screen(2,0l,"?No readable file to send");
X            else
X	    	errpkt("No readable file to send");
X	    return(0);
X    	} 
X    } else if (sndsrc > 0) {		/* Command line arglist -- */
X	x = gnfile();			/* Get the first file from it. */
X	if (x < 1) return(0);		/* (if any) */
X    } else if (sndsrc == 0) {		/* stdin or memory always exist... */
X	cmarg2 = "";			/* No alternate name */
X	strcpy(filnam,"stdin");		/* If F packet, filnam is used. */
X	tlog(F110,"Sending from",cmdstr,0l); /* If X packet, cmdstr is used. */
X    }
X
X    debug(F101,"sinit: nfils","",nfils);
X    debug(F110," filnam",filnam,0);
X    debug(F110," cmdstr",cmdstr,0);
X    ttflui();				/* Flush input buffer. */
X    x = rpar(data);			/* Send a Send-Init packet. */
X    if (!local && !server) sleep(delay);
X    spack('S',pktnum,x,data);
X    return(1);
X}
X
Xsipkt() {
X    int x;
X    x = rpar(data);			/* Send an I-Packet. */
X    spack('I',pktnum,x,data);
X}
X
X/*  R C V F I L -- Receive a file  */
X
Xrcvfil() {
X    int x;
X    ffc = flci = flco = 0;		/* Init per-file counters */
X    srvptr = srvcmd;			/* Decode packet data. */
X    decode(data,putsrv);
X    screen(0,0l,srvcmd);			/* Update screen */
X    screen(1,0l,"=> ");
X    tlog(F110,"Receiving",srvcmd,0l);	/* Transaction log entry */
X    if (cmarg2 != NULL) {               /* Check for alternate name */
X        if (*cmarg2 != '\0') {
X            strcpy(srvcmd,cmarg2);	/* Got one, use it. */
X	    *cmarg2 = '\0';
X        }
X    }
X    x = openo(srvcmd,filnam);		/* Try to open it */
X    if (x) {
X	tlog(F110," as",filnam,0l);
X	screen(2,0l,filnam);
X	intmsg(++filcnt);
X    } else {
X        tlog(F110,"Failure to open",filnam,0l);
X	screen(2,0l,"*** error");
X    }
X    return(x);				/* Pass on return code from openo */
X}
X
X/*  R E O F  --  Receive End Of File  */
X
Xreof() {
X
X    if (cxseen == 0) cxseen = (*data == 'D');
X    clsof();
X    if (cxseen || czseen) {
X	tlog(F100," *** Discarding","",0l);
X    } else {
X	tlog(F100," end of file","",0l);
X	tlog(F101,"  file characters        ","",ffc);
X	tlog(F101,"  communication line in  ","",flci);
X	tlog(F101,"  communication line out ","",flco);
X    }
X}
X
X/*  R E O T  --  Receive End Of Transaction  */
X
Xreot() {
X    char *tp;
X    cxseen = czseen = 0; 
X    ztime(&tp);
X    tlog(F110,"End of transaction",tp,0l);
X    if (filcnt > 1) {
X	tlog(F101," files","",filcnt);
X	tlog(F101," total file characters   ","",tfc);
X	tlog(F101," communication line in   ","",tlci);
X	tlog(F101," communication line out  ","",tlco);
X    }
X}
X
X/*  S F I L E -- Send File header packet for global "filnam" */
X
Xsfile() {
X    char pktnam[100];			/* Local copy of name */
X
X    if (fncnv) {
X	if (*cmarg2 != '\0') {		/* If we have a send-as name, */
X	    zltor(cmarg2,pktnam);	/* convert it to common form, */
X	    cmarg2 = "";		/* and blank it out for next time, */
X	} else zltor(filnam,pktnam);	/* otherwise use the real file name. */
X    } else {
X	if (*cmarg2 != '\0')		/* Same as above, but without */
X	    strcpy(pktnam,cmarg2);	/* name conversion */
X        else strcpy(filnam,pktnam);
X    }
X
X    debug(F110,"sfile",filnam,0);
X    if (openi(filnam) == 0) 		/* Try to open the file */
X	return(0); 		
X
X    rpt = flci = flco = ffc = 0;	/* OK, Init counters, etc. */
X    encstr(pktnam);			/* Encode the name. */
X    nxtpkt(&pktnum);			/* Increment the packet number */
X    ttflui();				/* Clear pending input */
X    spack('F',pktnum,size,data); 	/* Send the F packet */
X    if (displa) {
X	screen(0,(long)pktnum,filnam);	/* Update screen */
X	screen(1,0l,"=> ");
X	screen(1,0l,pktnam);
X	screen(3,(long)fsize,", size");
X	intmsg(++filcnt);		/* Count file, give interrupt msg */
X    }
X    tlog(F110,"Sending",filnam,0l);	/* Transaction log entry */
X    tlog(F110," as",pktnam,0l);
X    next = -1;				/* Init file character lookahead. */
X    return(1);
X}
X
X
X/* Send an X Packet -- Like SFILE, but with Text rather than File header */
X
Xsxpack() {				/* Send an X packet */
X    debug(F110,"sxpack",cmdstr,0);
X    encstr(cmdstr);			/* Encode any data. */
X    rpt = flci = flco = ffc = 0;	/* Init counters, etc. */
X    next = -1;				/* Init file character lookahead. */
X    nxtpkt(&pktnum);			/* Increment the packet number */
X    spack('X',pktnum,size,data);	/* No incrementing pktnum */
X    screen(0,(long)pktnum,cmdstr);		/* Update screen. */
X    intmsg(++filcnt);
X    tlog(F110,"Sending from:",cmdstr,0l);
X    return(1);
X}
X
X/*  S D A T A -- Send a data packet */
X
Xsdata() {
X    int len;
X    if (cxseen || czseen) return(0);	/* If interrupted, done. */
X    if ((len = getpkt(spsiz-chklen-3)) == 0) return(0); /* If no data, done. */
X    nxtpkt(&pktnum);			/* Increment the packet number */
X    spack('D',pktnum,len,data);		/* Send the packet */
X    return(1);
X}
X
X
X/*  S E O F -- Send an End-Of-File packet */
X
Xseof() {
X    nxtpkt(&pktnum);			/* Increment the packet number */
X    if (czseen || cxseen) {
X	spack('Z',pktnum,1,"D");
X	tlog(F100," *** interrupted, sending discard request","",0l);
X    } else {
X	spack('Z',pktnum,0,"");
X	tlog(F100," end of file","",0l);
X	tlog(F101,"  file characters        ","",ffc);
X	tlog(F101,"  communication line in  ","",flci);
X	tlog(F101,"  communication line out ","",flco);
X    }
X}
X
X
X/*  S E O T -- Send an End-Of-Transaction packet */
X
Xseot() {
X    char *tp;
X    nxtpkt(&pktnum);			/* Increment the packet number */
X    spack('B',pktnum,0,"");
X    cxseen = czseen = 0; 
X    ztime(&tp);
X    tlog(F110,"End of transaction",tp,0l);
X    if (filcnt > 1) {
X	tlog(F101," files","",filcnt);
X	tlog(F101," total file characters   ","",tfc);
X	tlog(F101," communication line in   ","",tlci);
X	tlog(F101," communication line out  ","",tlco);
X    }
X}
X
X/*   R P A R -- Fill the data array with my send-init parameters  */
X
Xrpar(data) char data[]; {
X    data[0] = tochar(spsiz);		/* Biggest packet I can receive */
X    data[1] = tochar(URTIME);		/* When I want to be timed out */
X    data[2] = tochar(mypadn);		/* How much padding I need (none) */
X    data[3] = ctl(mypadc);		/* Padding character I want */
X    data[4] = tochar(MYEOL);		/* End-Of-Line character I want */
X    data[5] = CTLQ;			/* Control-Quote character I send */
X    if (ebqflg) data[6] = ebq = '&';
X    	else data[6] = 'Y';		/* 8-bit quoting */
X    data[7] = bctr + '0';		/* Block check type */
X    data[8] = MYRPTQ;			/* Do repeat counts */
X    data[9] = '\0';
X    return(9);				/* Return the length. */
X}
X
X/*   S P A R -- Get the other system's Send-Init parameters.  */
X
Xspar(data) char data[]; {
X    int len, x;
X
X    len = strlen(data);		    	/* Number of fields */
X
X    spsiz = (len-- > 0) ? unchar(data[0]) : DSPSIZ; 	/* Packet size */
X    if (spsiz < 10) spsiz = DSPSIZ;
X
X    x = (len-- > 0) ? unchar(data[1]) : DMYTIM;	/* Timeout */
X    if (!timef) {			/* Only use if not overridden */
X	timint = x;
X	if (timint < 0) timint = DMYTIM;
X    }
X
X    npad = 0; padch = '\0';		    	    	/* Padding */
X    if (len-- > 0) {
X	npad = unchar(data[2]);
X	if (len-- > 0) padch = ctl(data[3]); else padch = 0;
X    }
X
X    eol = (len-- > 0) ? unchar(data[4]) : '\r';	    	/* Terminator  */
X    if ((eol < 2) || (eol > 037)) eol = '\r';
X
X    ctlq = (len-- > 0) ? data[5] : CTLQ;    	    	/* Control prefix */
X
X    if (len-- > 0) {			    	    	/* 8th-bit prefix */
X	ebq = data[6];
X	if ((ebq > 040 && ebq < 0100) || (ebq > 0140 && ebq < 0177)) {
X	    ebqflg = 1;
X	} else if (parity && (ebq == 'Y')) {
X	    ebqflg = 1;
X	    ebq = '&';
X	} else if (ebq == 'N') {
X	    ebqflg = 0;
X	} else ebqflg = 0;
X    } else ebqflg = 0;
X
X    chklen = 1;				    	    	/* Block check */
X    if (len-- > 0) {
X	chklen = data[7] - '0';
X	if ((chklen < 1) || (chklen > 3)) chklen = 1;
X    }
X    bctr = chklen;
X
X    if (len-- > 0) {			    	    	/* Repeat prefix */
X	rptq = data[8]; 
X	rptflg = ((rptq > 040 && rptq < 0100) || (rptq > 0140 && rptq < 0177));
X    } else rptflg = 0;
X
X    if (deblog) sdebu(len);
X}
X
X/*  S D E B U  -- Record spar results in debugging log  */
X
Xsdebu(len) int len; {
X    debug(F111,"spar: data",data,len);
X    debug(F101," spsiz ","",spsiz);
X    debug(F101," timint","",timint);
X    debug(F101," npad  ","",npad);
X    debug(F101," padch ","",padch);
X    debug(F101," eol   ","",eol);
X    debug(F101," ctlq  ","",ctlq);
X    debug(F101," ebq   ","",ebq);
X    debug(F101," ebqflg","",ebqflg);
X    debug(F101," chklen","",chklen);
X    debug(F101," rptq  ","",rptq);
X    debug(F101," rptflg","",rptflg);
X}
X
X/*  G N F I L E  --  Get the next file name from a file group.  */
X
X/*  Returns 1 if there's a next file, 0 otherwise  */
X
Xgnfile() {
X    int x, y;
X
X/* If file group interruption (C-Z) occured, fail.  */
X
X    debug(F101,"gnfile: czseen","",czseen);
X
X    if (czseen) {
X	tlog(F100,"Transaction cancelled","",0l);
X	return(0);
X    }
X
X/* If input was stdin or memory string, there is no next file.  */
X
X    if (sndsrc == 0) return(0);
X
X/* If file list comes from command line args, get the next list element. */
X
X    y = -1;
X    while (y < 0) {			/* Keep trying till we get one... */
X
X	if (sndsrc > 0) {
X	    if (nfils-- > 0) {
X		strcpy(filnam,*cmlist++);
X		debug(F111,"gnfile: cmlist filnam",filnam,nfils);
X	    } else {
X		*filnam = '\0';
X		debug(F101,"gnfile cmlist: nfils","",nfils);
X		return(0);
X	    }
X	}
X
X/* Otherwise, step to next element of internal wildcard expansion list. */
X
X	if (sndsrc < 0) {
X	    x = znext(filnam);
X	    debug(F111,"gnfile znext: filnam",filnam,x);
X	    if (x == 0) return(0);
X	}
X
X/* Get here with a filename. */
X
X	y = zchki(filnam);		/* Check if file readable */
X	if (y < 0) {
X	    debug(F110,"gnfile skipping:",filnam,0);
X	    tlog(F111,filnam,"not sent, reason",(long)y);
X	    screen(0,0l,"Skipping");
X	    screen(2,0l,filnam);
X	} else fsize = y;
X    }    	
X    return(1);
X}
X
X/*  O P E N I  --  Open an existing file for input  */
X
Xopeni(name) char *name; {
X    int x, filno;
X    if (memstr) return(1);		/* Just return if file is memory. */
X
X    debug(F110,"openi",name,0);
X    debug(F101," sndsrc","",sndsrc);
X
X    filno = (sndsrc == 0) ? ZSTDIO : ZIFILE;    /* ... */
X
X    debug(F101," file number","",filno);
X
X    if (zopeni(filno,name)) {		/* Otherwise, try to open it. */
X	debug(F110," ok",name,0);
X    	return(1);
X    } else {				/* If not found, */
X	char xname[100];		/* convert the name */
X	zrtol(name,xname);		/* to local form and then */
X	debug(F110," zrtol:",xname,0);
X	x = zopeni(filno,xname);	/* try opening it again. */
X	debug(F101," zopeni","",x);
X	if (x) {
X	    debug(F110," ok",xname,0);
X	    return(1);			/* It worked. */
X        } else {
X	    screen(2,0l,"Can't open file");  /* It didn't work. */
X	    tlog(F110,xname,"could not be opened",0l);
X	    debug(F110," openi failed",xname,0);
X	    return(0);
X        }
X    }
X}
X
X/*  O P E N O  --  Open a new file for output.  */
X
X/*  Returns actual name under which the file was opened in string 'name2'. */
X
Xopeno(name,name2) char *name, *name2; {
X    char xname[100], *xp;
X
X    if (stdouf)				/* Receiving to stdout? */
X	return(zopeno(ZSTDIO,""));
X
X    debug(F110,"openo: name",name,0);
X
X    xp = xname;
X    if (fncnv)				/* If desired, */
X    	zrtol(name,xp);			/* convert name to local form */
X    else				/* otherwise, */
X    	strcpy(xname,name);		/* use it literally */
X
X    debug(F110,"openo: xname",xname,0);
X
X    if (warn) {				/* File collision avoidance? */
X	if (zchki(xname) != -1) {	/* Yes, file exists? */
X	    znewn(xname,&xp);		/* Yes, make new name. */
X	    strcpy(xname,xp);
X	    debug(F110," exists, new name ",xname,0);
X        }
X    }
X    if (zopeno(ZOFILE,xname) == 0) {	/* Try to open the file */
X	debug(F110,"openo failed",xname,0);
X	tlog(F110,"Failure to open",xname,0l);
X	return(0);
X    } else {
X	strcpy(name2,xname);
X	debug(F110,"openo ok, name2",name2,0);
X	return(1);
X    }
X}
X
X/*  O P E N T  --  Open the terminal for output, in place of a file  */
X
Xopent() {
X    ffc = tfc = 0;
X    return(zopeno(ZCTERM,""));
X}
X
X/*  C L S I F  --  Close the current input file. */
X
Xclsif() {
X    if (memstr) {			/* If input was memory string, */
X	memstr = 0;			/* indicate no more. */
X    } else if (hcflg) {
X	zclosf();			/* If host cmd close fork, */
X    } else zclose(ZIFILE);		/* else close input file. */
X
X    screen(1,0l," [OK]");
X    hcflg = cxseen = 0;			/* Reset flags. */
X}
X
X
X/*  C L S O F  --  Close an output file.  */
X
Xclsof() {
X    zclose(ZOFILE);			/* Close it. */
X    if (czseen || cxseen) {
X	zdelet(filnam);   		/* Delete it if interrupted. */
X	debug(F100,"Discarded","",0);
X	tlog(F100,"Discarded","",0l);
X	screen(1,0l," [Discarded]");
X    } else {
X	debug(F100,"Closed","",0);
X	screen(1,0l," [OK]");
X    }
X    cxseen = 0;
X}
X
X/*  S N D H L P  --  Routine to send builtin help  */
X
Xsndhlp() {
X    nfils = 0;				/* No files, no lists. */
X    xflg = 1;				/* Flag we must send X packet. */
X    strcpy(cmdstr,"help text");		/* Data for X packet. */
X    next = -1;				/* Init getch lookahead */
X    memstr = 1;				/* Just set the flag. */
X    memptr = hlptxt;			/* And the pointer. */
X    return(sinit());
X}
X
X
X/*  C W D  --  Change current working directory  */
X
X/*
X String passed has first byte as length of directory name, rest of string
X is name.  Fails if can't connect, else ACKs (with name) and succeeds. 
X*/
X
Xcwd(vdir) char *vdir; {
X    vdir[unchar(*vdir) + 1] = '\0';	/* End with a null */
X    if (zchdir(vdir+1)) {
X	encstr(vdir+1);
X	ack1(data);
X	tlog(F110,"Changed directory to",vdir+1,0l);
X	return(1); 
X    } else {
X	tlog(F110,"Failed to change directory to",vdir+1,0l);
X	return(0);
X    }
X}
X
X
X/*  S Y S C M D  --  Do a system command  */
X
X/*  Command string is formed by concatenating the two arguments.  */
X
Xsyscmd(prefix,suffix) char *prefix, *suffix; {
X    char *cp;
X
X    for (cp = cmdstr; *prefix != '\0'; *cp++ = *prefix++) ;
X    while (*cp++ = *suffix++) ;
X
X    debug(F110,"syscmd",cmdstr,0);
X    if (zxcmd(cmdstr) > 0) {
X	debug(F100,"zxcmd ok","",0);
X	nfils = sndsrc = 0;		/* Flag that input from stdin */
X	xflg = hcflg = 1;		/* And special flags for pipe */
X	return (sinit());		/* Send S packet */
X    } else {
X	debug(F100,"zxcmd failed","",0);
X	return(0);
X    }
X}
!FUNKY!STUFF!
echo x - ckfns2.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckfns2.c
X/*  C K F N S 2  --  System-independent Kermit protocol support functions... */
X
X/*  ...Part 2 (continued from ckfns.c)  */
X/*
X Note -- if you change this file, please amend the version number and date at
X the top of ckfns.c accordingly.
X*/
X
X#include "ckermi.h"
X
Xextern int spsiz, timint, npad, chklen, ebq, ebqflg, rpt, rptq, rptflg, capas;
X
Xextern int pktnum, prvpkt, sndtyp, fsize, bctr, bctu,
X size, osize, maxsize, spktl, nfils, stdouf, warn, timef;
X
Xextern int parity, speed, turn, turnch, 
X delay, displa, pktlog, tralog, seslog, xflg, mypadn;
X
Xextern long filcnt, ffc, flci, flco, tlci, tlco, tfc;
X
Xextern int deblog, hcflg, binary, fncnv, local, server, cxseen, czseen;
X
Xextern char padch, mypadc, eol, ctlq, myctlq, sstate, *hlptxt;
X
Xextern char filnam[], sndpkt[], recpkt[], data[], srvcmd[], *srvptr, stchr, 
X mystch;
X
Xextern char *cmarg, *cmarg2, **cmlist;
Xchar *strcpy();
X
X/*  I N P U T  --  Attempt to read packet number 'pktnum'.  */
X
X/*
X This is the function that feeds input to Kermit's finite state machine.
X
X If a special start state is in effect, that state is returned as if it were
X the type of an incoming packet.  Otherwise:
X
X . If the desired packet arrives within MAXTRY tries, return its type,
X   with its data stored in the global 'data' array.
X
X . If the previous packet arrives again, resend the last packet and wait for
X   another to come in.
X
X . If the desired packet does not arrive within MAXTRY tries, return indicating
X   that an error packet should be sent.
X*/
X
Xinput() {
X    int len, num, type, numtry;
X
X    if (sstate != 0) {			/* If a start state is in effect, */
X	type = sstate;			/* return it like a packet type, */
X	sstate = 0;			/* and then nullify it. */
X	*data = '\0';
X	return(type);
X    } else type = rpack(&len,&num,data); /* Else, try to read a packet. */
X
X/* If it's the same packet we just sent, it's an echo.  Read another. */
X
X    if (type == sndtyp) type = rpack(&len,&num,data);
X
X    chkint();				/* Check for console interrupts. */
X/*
X If previous packet again, a timeout pseudopacket, or a bad packet, try again.
X*/
X    for (numtry = 0; num == prvpkt || type == 'T' || type == 'Q' ; numtry++)
X    {
X	if (numtry > MAXTRY) {		/* If too many tries, give up */
X	    strcpy(data,"Timed out.");	/* and send a timeout error packet. */
X	    return('E');
X	}
X	resend();			/* Else, send last packet again, */
X	type = rpack(&len,&num,data);	/* and try to read a new one. */
X	chkint();			/* Look again for interruptions. */
X    }
X    return(type);			/* Success, return packet type. */
X}
X
X/*  S P A C K  --  Construct and send a packet  */
X
Xspack(type,num,len,dat) char type, *dat; int num, len; {
X    int i,j;
X    
X    j = dopar(padch);
X    for (i = 0; i < npad; sndpkt[i++] = j)  /* Do any requested padding */
X    	;
X    sndpkt[i++] = dopar(mystch);	/* Start packet with the start char */
X    sndpkt[i++] = dopar(tochar(len+bctu+2));	/* Put in the length */
X    sndpkt[i++] = dopar(tochar(num));		/* The packet number */
X    sndpkt[i++] = sndtyp = dopar(type);		/* Packet type */
X
X    for (j = len; j > 0; j-- ) sndpkt[i++] = dopar(*dat++); /* Data */
X
X    sndpkt[i] = '\0';			/* Mark end for block check */
X    switch(bctu) {
X	case 1: 			/* Type 1 - 6 bit checksum */
X	    sndpkt[i++] = dopar(tochar(chk1(sndpkt+1)));
X	    break;
X	case 2:				/* Type 2 - 12 bit checksum*/
X	    j = chk2(sndpkt+1);
X	    sndpkt[i++] = dopar(tochar((j & 07700) >> 6));
X	    sndpkt[i++] = dopar(tochar(j & 077));
X	    break;
X        case 3:				/* Type 3 - 16 bit CRC-CCITT */
X	    j = chk3(sndpkt+1);
X	    sndpkt[i++] = dopar(tochar(( (unsigned)(j & 0170000)) >> 12));
X	    sndpkt[i++] = dopar(tochar((j & 07700) >> 6));
X	    sndpkt[i++] = dopar(tochar(j & 077));
X	    break;
X	}
X    for (j = npad; j > 0; j-- ) sndpkt[i++] = dopar(padch); /* Padding */
X
X    sndpkt[i++] = dopar(eol);		/* EOL character */
X    sndpkt[i] = '\0';			/* End of the packet */
X    ttol(sndpkt,spktl=i);		/* Send the packet just built */
X    flco += spktl;			/* Count the characters */
X    tlco += spktl;
X    if (pktlog) zsoutl(ZPFILE,sndpkt);	/* If logging packets, log it */
X    screen(type,(long)num,sndpkt);	/* Update screen */
X}
X
X/*  D O P A R  --  Add an appropriate parity bit to a character  */
X
Xdopar (ch) char ch; {
X    int a;
X    switch (parity) {
X	case 'm':  return(ch | 128);		/* Mark */
X	case 's':  return(ch & 127);		/* Space */
X	case 'o':  ch |= 128;			/* Odd (fall thru) */
X	case 'e':				/* Even */
X	    a = (ch & 15) ^ ((ch >> 4) & 15);
X	    a = (a & 3) ^ ((a >> 2) & 3);
X	    a = (a & 1) ^ ((a >> 1) & 1);
X	    return(ch | (a << 7));
X	default:   return(ch);
X    }
X}
X
X/*  C H K 1  --  Compute a type-1 Kermit 6-bit checksum.  */
X
Xchk1(pkt) char *pkt; {
X    int chk;
X    chk = chk2(pkt);
X    return((((chk & 0300) >> 6) + chk) & 077);
X}
X
X
X/*  C H K 2  --  Compute the numeric sum of all the bytes in the packet.  */
X
Xchk2(pkt) char *pkt; {
X    unsigned int chk;
X    int p;
X    for (chk = 0; *pkt != '\0'; *pkt++) {
X    	p = (parity) ? *pkt & 0177 : *pkt;
X	chk += p;
X    }
X    return(chk);
X}
X
X
X/*  C H K 3  --  Compute a type-3 Kermit block check.  */
X/*
X Calculate the 16-bit CRC of a null-terminated string using a byte-oriented
X tableless algorithm invented by Andy Lowry (Columbia University).  The
X magic number 010201 is derived from the CRC-CCITT polynomial x^16+x^12+x^5+1.
X Note - this function could adapted for strings containing imbedded 0's
X by including a length argument.
X*/
Xchk3(s) char *s; {
X    unsigned int c, q;
X    LONG crc = 0;
X
X    while ((c = *s++) != '\0') {
X	if (parity) c &= 0177;
X	q = (crc ^ c) & 017;		/* Low-order nibble */
X	crc = (crc >> 4) ^ (q * 010201);
X	q = (crc ^ (c >> 4)) & 017;	/* High order nibble */
X	crc = (crc >> 4) ^ (q * 010201);
X    }
X    return(crc);
X}
X
X/* Functions for sending various kinds of packets */
X
Xack() {					/* Send an ordinary acknowledgment. */
X    spack('Y',pktnum,0,"");		/* No data. */
X    nxtpkt(&pktnum);			/* Increment the packet number. */
X}					/* Note, only call this once! */
X
Xack1(s) char *s; {			/* Send an ACK with data. */
X    spack('Y',pktnum,strlen(s),s);	/* Send the packet. */
X    nxtpkt(&pktnum);			/* Increment the packet number. */
X}					/* Only call this once! */
X
Xnack() {				/* Negative acknowledgment. */
X    spack('N',pktnum,0,"");		/* NAK's never have data. */
X}
X
Xresend() {				/* Send the old packet again. */
X    int w;
X
X    for (w = 0; w < timint - 2; w++) {	/* be extra sure no stuff is */
X	ttflui();			/*    still comming in */
X	sleep(1);
X	if (!ttchk() ) ttinc(1);	/* be extra sure no stuff in SIII/V */
X	if (!ttchk() ) break;
X    }
X    ttol(sndpkt,spktl);
X    screen('%',(long)pktnum,sndpkt);
X    if (pktlog) zsoutl(ZPFILE,sndpkt);
X}
X
Xerrpkt(reason) char *reason; {		/* Send an error packet. */
X    encstr(reason);
X    spack('E',pktnum,size,data);
X}
X
Xscmd(t,dat) char t, *dat; {		/* Send a packet of the given type */
X    encstr(dat);			/* Encode the command string */
X    ttflui();				/* Flush pending input. */
X    spack(t,pktnum,size,data);
X}
X
Xsrinit() {				/* Send R (GET) packet */
X    encstr(cmarg);			/* Encode the filename. */
X    ttflui();				/* Flush pending input. */
X    spack('R',pktnum,size,data);	/* Send the packet. */
X}
X
Xnxtpkt(num) int *num; {
X    prvpkt = *num;			/* Save previous */
X    *num = (*num + 1) % 64;		/* Increment packet number mod 64 */
X}
X
Xsigint() {				/* Terminal interrupt handler */
X    errpkt("User typed ^C");
X    doexit(0);				/* Exit with status = 0 */
X}
X
X/* R P A C K  --  Read a Packet */
X
Xrpack(l,n,dat) int *l, *n; char *dat; {
X    int i, j, x, done, pstart, pbl;
X    char chk[4], xchk[4], t, type;
X
X    chk[3] = xchk[3] = 0;
X    i = inlin();			/* Read a line */
X    if (i != 0) {
X	debug(F101,"rpack: inlin","",i);
X	screen('T',(long)pktnum,"");
X	return('T');
X    }
X    debug(F110,"rpack: inlin ok, recpkt",recpkt,0);
X
X/* Look for start of packet */
X
X    for (i = 0; ((t = recpkt[i]) != stchr) && (i < RBUFL) ; i++)
X    	;
X    if (++i >= RBUFL) return('Q');	/* Skip rest if not found */
X
X/* now "parse" the packet */
X
X    debug(F101,"entering rpack with i","",i);
X    done = 0;
X    while (!done) {
X	debug(F101,"rpack starting at i","",i);
X        pstart = i;			/* remember where packet started */
X
X/* length */
X
X	if ((t = recpkt[i++]) == stchr) continue; /* Resynch if SOH */
X
X   /***	if (t == 2) doexit(0); *** uncomment this to allow ^A^B cause exit ***/
X
X	if (t == MYEOL) return('Q');
X	*l = unchar(t);			/* Packet length */
X	debug(F101," pkt len","",*l);
X
X/* sequence number */
X
X	if ((t = recpkt[i++]) == stchr) continue;
X	if (t == MYEOL) return('Q');
X	*n = unchar(t);
X	debug(F101,"rpack: n","",*n);
X
X/* cont'd... */
X
X/* ...rpack(), cont'd */
X
X
X/* type */
X
X	if ((type = recpkt[i++]) == stchr) continue;
X	if (type == MYEOL) return('Q');
X	debug(F101,"rpack: type","",type);
X
X	if ((type == 'S') || (type == 'I')) pbl = 1;	/* Heuristics for  */
X	else if (type == 'N') pbl = *l - 2;    /* syncing block check type */
X	else pbl = bctu;
X
X	*l -= (pbl + 2);		/* Now compute data length */
X	debug(F101,"rpack: bctu","",bctu);
X	debug(F101," pbl","",pbl);
X	debug(F101," data length","",*l);
X
X/* data */
X
X	dat[0] = '\0';			/* Return null string if no data */
X	for (j=0; j<*l; i++,j++)
X	    if ((dat[j] = recpkt[i]) == stchr) continue;
X		else if (dat[j] == MYEOL) return('Q');
X	dat[j] = '\0';
X
X/* get the block check */
X
X    	debug(F110," packet chk",recpkt+i,0);
X    	for (j = 0; j < pbl; j++) {
X	    chk[j] = recpkt[i];
X	    debug(F101," chk[j]","",chk[j]);
X	    if (chk[j] == stchr) break;
X	    if (chk[j] == eol) return('Q');
X	    recpkt[i++] = '\0';
X	}
X	chk[j] = 0;
X	debug(F111," chk array, j",chk,j);
X	if (j != pbl) continue;		/* Block check right length? */
X	done = 1;			/* Yes, done. */
X    }
X
X/* cont'd... */
X
X/* ...rpack(), cont'd */
X
X
X/* Got packet, now check the block check */
X
X    switch (pbl) {
X	case 1:
X	    xchk[0] = tochar(chk1(&recpkt[pstart]));
X	    if (chk[0] != xchk[0]) {
X		if (deblog) {
X		    debug(F000,"rpack: chk","",chk[0]);
X		    debug(F000," should be ","",xchk[0]);
X		}
X		screen('Q',(long)n,recpkt);
X		return('Q');
X	    }
X	    break;
X	case 2:
X	    x = chk2(&recpkt[pstart]);
X	    xchk[0] = tochar((x & 07700) >> 6);
X	    xchk[1] = tochar(x & 077);
X	    if (deblog) {
X		debug(F000," xchk[0]","=",xchk[0]);
X		debug(F000," xchk[1]","=",xchk[1]);
X	    }
X	    if ((xchk[0] != chk[0]) || (xchk[1] != chk[1])) {
X		debug(F100," bct2's don't compare","",0);
X		screen('Q',(long)n,recpkt);
X		return('Q');
X            }
X	    break;
X	case 3:
X	    x = chk3(&recpkt[pstart]);
X	    xchk[0] = tochar(( (unsigned)(x & 0170000)) >> 12);
X	    xchk[1] = tochar((x & 07700) >> 6);
X	    xchk[2] = tochar(x & 077);
X	    if (deblog) {
X		debug(F000," xchk[0]","=",xchk[0]);
X		debug(F000," xchk[1]","=",xchk[1]);
X		debug(F000," xchk[2]","=",xchk[2]);
X            }
X	    if ((xchk[0] != chk[0]) || 
X	    	(xchk[1] != chk[1]) || 
X		(xchk[2] != chk[2])) {
X		    debug(F100," bct3's don't compare","",0);
X		    screen('Q',(long)n,recpkt);
X		    return('Q');
X	    }
X	    break;
X        }
X
X/* Good packet, return its type */
X
X    ttflui();				/* Done, flush any remaining. */
X    screen(type,(long)(*n),recpkt);	/* Update screen */
X    return(type);
X}
X
X/*  I N C H R  --  Input character from communication line, with timeout  */
X    	
Xinchr(timo) int timo; {
X    int c;
X    c = ttinc(timo);
X    debug(F101,"inchr ttinc","",c);
X    if (c < 0) return(c); 		/* Get a character */
X    if (parity) c = c & 0177;		/* If parity on, discard parity bit. */
X    debug(F101," after parity","",c);
X    return(c);
X}
X
X
X/*  I N L I N  -- Input a line (up to break char) from communication line  */
X
X/*  Returns 0 on success, nonzero on failure  */
X
Xinlin() {
X    int e, i, j, k;
X
X    e = (turn) ? turnch : MYEOL;
X    i = j = k = 0;
X    if (parity) {
X    	while ((j != e) && (i < RBUFL) && (k < MAXTRY)) {
X	    j = inchr(1);		/* Get char, 1 second timeout */
X	    debug(F101,"inlin inchr","",j);
X	    if (j < 0) k++;		/* Timed out. */
X	    else {
X		if (j) recpkt[i++] = j;	/* Save it */
X		k = 0;			/* Reset timeout counter. */
X	    }
X	}
X    } else {
X    	i = ttinl(recpkt,RBUFL,timint,e);	/* Get them all at once */
X	if (i < 0) k = 1;
X    }
X    debug(F111,"inlin",recpkt,i);
X    debug(F101," timeouts","",k);
X    if (i < 1) return(1);
X    if (pktlog) zsoutl(ZPFILE,recpkt);
X    if (k > MAXTRY) return(1);
X    tlci += i;				/* Count the characters. */
X    flci += i;
X    recpkt[i+1] = '\0';			/* Terminate the input string. */
X    return(0);
X}
!FUNKY!STUFF!

gregg@okstate.UUCP (03/08/85)

echo x - ckusr2.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckusr2.c
X/*  C K U S R 2  --  "User Interface" STRINGS module for Unix Kermit  */
X
X/*  This module separates long strings from the body of the ckuser module. */  
X
X#include "ckermi.h"
X#include "ckcmd.h"
X#include "ckuser.h"
X
Xextern char cmdbuf[];
Xextern int nrmt, nprm, dfloc;
Xextern char *dftty;
Xextern struct keytab prmtab[];
Xextern struct keytab remcmd[];
X
Xstatic
Xchar *hlp1[] = {
X"\n",
X"  Usage: kermit [-x arg [-x arg]...[-yyy]..]]\n",
X"   x is an option that requires an argument, y an option with no argument:\n",
X"     actions (* options also require -l and -b) --\n",
X"       -s file(s)   send (use '-s -' to send from stdin)\n",
X"       -r           receive\n",
X"       -k           receive to stdout\n",
X"     * -g file(s)   get remote file(s) from server (quote wildcards)\n",
X"       -a name      alternate name, used with -s, -r, -g\n",
X"       -x           enter server mode\n",
X"     * -f           finish remote server\n",
X"     * -c           connect before transaction\n",
X"     * -n           connect after transaction\n",
X"       -h           help - print this message\n",
X"     settings --\n",
X"       -l line      communication line device\n",
X"       -b baud      line speed, e.g. 1200\n",
X"       -i           binary file or Unix-to-Unix\n",
X"       -p x         parity, x is one of e,o,m,s,n\n",
X"       -t           line turnaround handshake = xon, half duplex\n",
X"       -w           don't write over preexisting files\n",
X"       -q    	     be quiet during file transfer\n",
X"       -d   	     log debugging info to debug.log\n",
X" If no action command is included, enter interactive dialog.\n",
X""
X};
X
X/*  U S A G E */
X
Xusage() {
X    conola(hlp1);
X}
X
X/*  Help string definitions  */
X
Xstatic char *tophlp[] = { "\n\
XType ? for a list of commands, type 'help x' for any command x.\n\
XWhile typing commands, use the following special characters:\n\n\
X DEL, RUBOUT, BACKSPACE, CTRL-H: Delete the most recent character typed.\n\
X CTRL-W: Delete the most recent word typed.\n",
X
X"\
X CTRL-U: Delete the current line.\n\
X CTRL-R: Redisplay the current line.\n\
X ?       (question mark) display help on the current command or field.\n\
X ESC     (Escape or Altmode) Attempt to complete the current field.\n",
X
X"\
X \\       (backslash) include the following character literally.\n\n\
XFrom Unix command level, type 'kermit -h' to get help about command line args.\
X\n",
X"" };
X
Xstatic char *hmxxbye = "\
XShut down and log out a remote Kermit server";
X
Xstatic char *hmxxclo = "\
XClose one of the following logs:\n\
X session, transaction, packet, debugging -- 'help log' for further info.";
X
Xstatic char *hmxxcon = "\
XConnect to a remote system via the tty device given in the\n\
Xmost recent 'set line' command";
X
Xstatic char *hmxxget = "\
XFormat: 'get filespec'.  Tell the remote Kermit server to send the named\n\
Xfiles.  If filespec is omitted, then you are prompted for the remote and\n\
Xlocal filenames separately.";
X
Xstatic char *hmxxlg[] = { "\
XRecord information in a log file:\n\n\
X debugging             Debugging information, to help track down\n\
X  (default debug.log)  bugs in the C-Kermit program.\n\n\
X packets               Kermit packets, to help track down protocol problems.\n\
X  (packet.log)\n\n",
X
X" session               Terminal session, during CONNECT command.\n\
X  (session.log)\n\n\
X transactions          Names and statistics about files transferred.\n\
X  (transaction.log)\n",
X"" } ;
X
Xstatic char *hmxxlogi[] = { "\
XSyntax: script text\n\n",
X"Login to a remote system using the text provided.  The login script\n",
X"is intended to operate similarly to uucp \"L.sys\" entries.\n",
X"A login script is a sequence of the form:\n\n",
X"	expect send [expect send] . . .\n\n",
X"where 'expect' is a prompt or message to be issued by the remote site, and\n",
X"'send' is the names, numbers, etc, to return.  The send may also be the\n",
X"keyword EOT, to send control-d, or BREAK, to send a break.  Letters in\n",
X"send may be prefixed by ~ to send special characters.  These are:\n",
X"~b backspace, ~s space, ~q '?', ~n linefeed, ~r return, ~c don\'t\n",
X"append a return, and ~o[o[o]] for octal of a character.  As with some \n",
X"uucp systems, sent strings are followed by ~r unless they end with ~c.\n\n",
X"Only the last 7 characters in each expect are matched.  A null expect,\n",
X"e.g. ~0 or two adjacent dashes, causes a short delay.  If you expect\n",
X"that a sequence might not arrive, as with uucp, conditional sequences\n",
X"may be expressed in the form:\n\n",
X"	-send-expect[-send-expect[...]]\n\n",
X"where dashed sequences are followed as long as previous expects fail.\n",
X"" };
X
Xstatic char *hmxxrc[] = { "\
XFormat: 'receive [filespec]'.  Wait for a file to arrive from the other\n\
XKermit, which must be given a 'send' command.  If the optional filespec is\n",
X
X"given, the (first) incoming file will be stored under that name, otherwise\n\
Xit will be stored under the name it arrives with.",
X"" } ;
X
Xstatic char *hmxxsen = "\
XFormat: 'send file1 [file2]'.  File1 may contain wildcard characters '*' or\n\
X'?'.  If no wildcards, then file2 may be used to specify the name file1 is\n\
Xsent under; if file2 is omitted, file1 is sent under its own name.";
X
Xstatic char *hmxxser = "\
XEnter server mode on the currently selected line.  All further commands\n\
Xwill be taken in packet form from the other Kermit program.";
X
Xstatic char *hmhset = "\
XThe 'set' command is used to establish various communication or file\n\
Xparameters.  The 'show' command can be used to display the values of\n\
X'set' parameters.  Help is available for each individual parameter;\n\
Xtype 'help set ?' to see what's available.\n";
X
Xstatic char *hmxychkt = "\
XType of packet block check to be used for error detection, 1, 2, or 3.\n\
XType 1 is standard, and catches most errors.  Types 2 and 3 specify more\n\
Xrigorous checking, at the cost of higher overhead.  Not all Kermit programs\n\
Xsupport types 2 and 3.\n";
X
Xstatic char *hmxyf[] = { "\
Xset file: names, type, warning, display.\n\n\
X'names' are normally 'converted', which means file names are converted\n\
Xto 'common form' during transmission; 'literal' means use filenames\n\
Xliterally (useful between like Unix systems).\n",
X
X"\n\
X'type' is normally 'text', in which conversion is done between Unix newlines\n\
Xand CRLF line delimiters; 'binary' means to do no conversion.  Use 'binary'\n\
Xfor executable programs or binary data.\n",
X
X"\n\
X'warning' is 'on' or 'off', normally off.  When off, incoming files will\n\
Xoverwrite existing files of the same name.  When on, new names will be\n\
Xgiven to incoming files whose names are the same as existing files.\n" ,
X
X"\n\
X'display' is normally 'on', causing file transfer progress to be displayed\n\
Xon your screen when in local mode.  'set display off' is useful for allowing\n\
Xfile transfers to proceed in the background.\n\n",
X"" } ;
X
Xstatic char *hmhrmt[] = { "\
XThe 'remote' command is used to send file management instructions to a\n\
Xremote Kermit server.  There should already be a Kermit running in server\n\
Xmode on the other end of the currently selected line.  Type 'remote ?' to\n",
X
X"\
Xsee a list of available remote commands.  Type 'help remote x' to get\n\
Xfurther information about a particular remote command 'x'.\n" ,
X""} ;
X
X/*  D O H L P  --  Give a help message  */
X
Xdohlp(xx) int xx; {
X    int x,y;
X
X    if (xx < 0) return(xx);
X    switch (xx) {
X
Xcase XXBYE:
X    return(hmsg(hmxxbye));
X
Xcase XXCLO:
X    return(hmsg(hmxxclo));
X
Xcase XXCON:
X    return(hmsg(hmxxcon));
X
Xcase XXCWD:
X    return(hmsg("Change Working Directory, equivalent to Unix 'cd' command"));
X
Xcase XXDEL:
X    return(hmsg("Delete a local file or files"));
X
Xcase XXDIAL:
X    return(hmsg("Dial a number using modem autodialer"));
X
Xcase XXDIR:
X    return(hmsg("Display a directory of local files"));
X
Xcase XXECH:
X    return(hmsg("Display the rest of the command on the terminal,\n\
Xuseful in command files."));
X
Xcase XXEXI:
Xcase XXQUI:
X    return(hmsg("Exit from the Kermit program, closing any open logs."));
X
Xcase XXFIN:
X    return(hmsg("\
XTell the remote Kermit server to shut down without logging out."));
X
Xcase XXGET:
X    return(hmsg(hmxxget));
X
Xcase XXHLP:
X    return(hmsga(tophlp));
X
Xcase XXLOG:
X    return(hmsga(hmxxlg));
X
Xcase XXLOGI:
X    return(hmsga(hmxxlogi));
X
Xcase XXREC:
X    return(hmsga(hmxxrc));
X
Xcase XXREM:
X    if ((y = cmkey(remcmd,nrmt,"Remote command","")) == -2) return(y);
X    if (y == -1) return(y);
X    if (x = (cmcfm()) < 0) return(x);
X    return(dohrmt(y));
X
Xcase XXSEN:
X    return(hmsg(hmxxsen));
X
Xcase XXSER:
X    return(hmsg(hmxxser));
X
Xcase XXSET:
X    if ((y = cmkey(prmtab,nprm,"Parameter","")) == -2) return(y);
X    if (y == -2) return(y);
X    if (x = (cmcfm()) < 0) return(x);
X    return(dohset(y));
X
Xcase XXSHE:
X    return(hmsg("\
XIssue a command to the Unix shell (space required after '!')"));
X
Xcase XXSHO:
X    return(hmsg("\
XDisplay current values of 'set' parameters; 'show version' will display\n\
Xprogram version information for each of the C-Kermit modules."));
X
Xcase XXSPA:
X    return(hmsg("Display disk usage in current device, directory"));
X
Xcase XXSTA:
X    return(hmsg("Display statistics about most recent file transfer"));
X
Xcase XXTAK:
X    return(hmsg("\
XTake Kermit commands from the named file.  Kermit command files may\n\
Xthemselves contain 'take' commands, up to a reasonable depth of nesting."));
X
Xdefault:
X    if (x = (cmcfm()) < 0) return(x);
X    printf("Not available yet - %s\n",cmdbuf);
X    break;
X    }
X    return(0);
X}
X
X/*  H M S G  --  Get confirmation, then print the given message  */
X
Xhmsg(s) char *s; {
X    int x;
X    if (x = (cmcfm()) < 0) return(x);
X    printf("%s\n",s);
X    return(0);
X}
X
Xhmsga(s) char *s[]; {			/* Same function, but for arrays */
X    int x, i;
X    if ( x = (cmcfm()) < 0) return(x);
X    for ( i = 0; *s[i] ; i++ ) fputs(s[i], stdout);
X    fputc( '\n', stdout);
X    return(0);
X}
X
X/*  D O H S E T  --  Give help for SET command  */
X
Xdohset(xx) int xx; {
X    
X    if (xx == -3) {
X        printf(hmhset);
X    	return(0);
X    }
X    if (xx < 0) return(xx);
X    switch (xx) {
X
Xcase XYCHKT:
X    printf(hmxychkt);
X    return(0);
X
Xcase XYDELA: 
X    printf("\
XNumber of seconds to wait before sending first packet after 'send' command\n");
X    return(0);
X
Xcase XYDUPL:
X    printf("\
XDuring 'connect': 'full' means remote host echoes, 'half' means this program\n\
Xdoes its own echoing.\n");
X    return(0);
X
Xcase XYEOL:
X    printf("\
XDecimal ASCII value for character to terminate outbound packets, normally\n\
X13 (CR, carriage return).  Inbound packets are assumed to end with CR.\n");
X    return(0);
X
Xcase XYESC:
X    printf("\
XDecimal ASCII value for escape character during 'connect', normally 28\n\
X(Control-\\)\n");
X    return(0);
X
Xcase XYFILE:
X    printf(hmxyf[0]);
X    printf(hmxyf[1]);
X    printf(hmxyf[2]);
X    printf(hmxyf[3]);
X    return(0);
X
Xcase XYFLOW:
X    printf("\
XType of flow control to be used.  Choices are 'xon/xoff' and 'none'.\n\
Xnormally xon/xoff.\n");
X    return(0);
X
Xcase XYHAND:
X    printf("\
XDecimal ASCII value for character to use for half duplex line turnaround\n\
Xhandshake.  Normally, handshaking is not done.\n");
X    return(0);
X
Xcase XYLEN:
X    printf("\
XPacket length to use.  90 by default.  94 maximum.\n");
X    return(0);
X
Xcase XYLINE:
X    printf("Device name of communication line to use.  Normally %s.\n",dftty);
X    if (!dfloc) {
X	printf("If you set the line to other than %s, then Kermit\n",dftty);
X	printf("\
Xwill be in 'local' mode; 'set line' will reset Kermit to remote mode.\n");
X    printf("\
XIf the line has a modem, and if the modem-dialer is set to direct, this \n\
Xcommand causes waiting for a carrier detect (e.g., on a hayes type modem). \n\
XThis can be used to wait for incomming calls.  \n");
X    printf("\
XTo use the modem to dial out, first set modem-dialer (e.g., to hayes), then \n\
Xset line, next issue the dial command, and finally connect \n");
X    }
X    return(0);
X
Xcase XYMARK:
X    printf("\
XDecimal ASCII value of character that marks the beginning of packets sent by\n\
Xthis program (normally 1 = Control-A)\n");
X    return(0);
X
Xcase XYMODM:
X    printf("\
XType of modem for dialing remote connections.  Needed to indicate modem can\n\
Xbe commanded to dial without \"carrier detect\" from modem.  Many recently\n\
Xmanufactured modems use \"hayes\" protocol.\n");
X    return(0);
X
Xcase XYNPAD:
X    printf("\
XNumber of padding characters to request for inbound packets, normally 0.\n");
X    return(0);
X
Xcase XYPADC:
X    printf("Decimal ASCII value of inbound padding character, normally 0.\n");
X    return(0);
X
Xcase XYPARI:
X    printf("\
XParity to use during terminal connection and file transfer:\n\
Xeven, odd, mark, space, or none.  Normally none.\n");
X    return(0);
X
Xcase XYPROM:
X    printf("Prompt string for this program, normally 'C-Kermit>'.\n");
X    return(0);
X
Xcase XYSPEE:
X    printf("\
XCommunication line speed for external tty line specified in most recent\n\
X'set line'.  Any of the common baud rates: 0, 110, 150, 300, 600, 1200,\n\
X1800, 2400, 4800, 9600.\n");
X    return(0);
X
Xcase XYTIMO:
X    printf("\
XTimeout interval for this program to use during file transfer, seconds.\n");
X    return(0);
X
Xdefault:
X    printf("Not available yet - %s\n",cmdbuf);
X    return(0);
X    }
X}
X
X/*  D O H R M T  --  Give help about REMOTE command  */
X
Xdohrmt(xx) int xx; {
X    int x;
X    if (xx == -3) {
X	printf(hmhrmt[0]);
X	printf(hmhrmt[1]);
X    	return(0);
X    }
X    if (xx < 0) return(xx);
X    switch (xx) {
X
Xcase XZCWD:
X    return(hmsg("\
XAsk remote Kermit server to change its working directory."));
X
Xcase XZDEL:
X    return(hmsg("\
XAsk remote Kermit server to delete the named file(s)."));
X
Xcase XZDIR:
X    return(hmsg("\
XAsk remote Kermit server to provide directory listing of the named file(s)."));
X
Xcase XZHLP:
X    return(hmsg("\
XAsk remote Kermit server to tell you what services it provides."));
X
Xcase XZHOS:
X    return(hmsg("\
XSend a command to the remote system in its own command language\n\
Xthrough the remote Kermit server."));
X
Xcase XZSPA:
X    return(hmsg("\
XAsk the remote Kermit server to tell you about its disk space."));
X
Xcase XZTYP:
X    return(hmsg("\
XAsk the remote Kermit server to type the named file(s) on your screen."));
X
Xcase XZWHO:
X    return(hmsg("\
XAsk the remote Kermit server to list who's logged in, or to give information\n\
Xabout the specified user."));
X
Xdefault:
X    if (x = (cmcfm()) < 0) return(x);
X    printf("not working yet - %s\n",cmdbuf);
X    return(-2);
X    }
X}
!FUNKY!STUFF!
echo x - ckusr3.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckusr3.c
X/*  C K U S R 3 --  "User Interface" for Unix Kermit, part 3  */
X
X/*  SET and REMOTE commands; screen, debug, interrupt, and logging functions */
X
X
X/* Includes */
X
X#include "ckermi.h"
X#include "ckcmd.h"
X#include "ckuser.h"
X
X/* Variables */
X
Xextern int size, spsiz, npad, timint, speed, local, server, image, flow,
X  displa, binary, fncnv, delay, parity, deblog, escape, xargc,
X  turn, duplex, cxseen, czseen, nfils, ckxech, pktlog, seslog, tralog, stdouf,
X  turnch, chklen, bctr, bctu, fsize, dfloc, mdmtyp,
X  rptflg, ebqflg, warn, quiet, cnflg, timef, mypadn;
X
Xextern long filcnt, tlci, tlco, ffc, tfc;
X
Xextern char *versio, *protv, *ckxv, *ckzv, *fnsv, *connv, *dftty, *cmdv;
Xextern char *cmarg, *cmarg2, **xargv, **cmlist;
Xextern char mystch, sstate, mypadc, padch, eol, ctlq, filnam[], ttname[];
Xchar *strcpy();
X
X/* Declarations from cmd package */
X
Xextern char cmdbuf[];			/* Command buffer */
X
X/* From main ckuser module... */
X
Xextern char line[100], *lp;		/* Character buffer for anything */
Xextern char debfil[50],			/* Debugging log file name */
X pktfil[50],				/* Packet log file name */
X sesfil[50],				/* Session log file name */
X trafil[50];				/* Transaction log file name */
X
Xextern int tlevel;			/* Take Command file level */
Xextern FILE *tfile[];			/* Array of take command fd's */
X
X/* Keyword tables for SET commands */
X
X
X/* Block checks */
X
Xstruct keytab blktab[] = {
X    "1", 1, 0,
X    "2", 2, 0,
X    "3", 3, 0
X};
X
X/* Duplex keyword table */
X
Xstruct keytab dpxtab[] = {
X    "full", 	 0, 0,
X    "half",      1, 0
X};
X
Xstruct keytab filtab[] = {
X    "display", XYFILD, 0,
X    "names",   XYFILN, 0,
X    "type",    XYFILT, 0,
X    "warning", XYFILW, 0
X};
Xint nfilp = (sizeof(filtab) / sizeof(struct keytab));
X
X/* Flow Control */
X
Xstruct keytab flotab[] = {
X    "none",     0, 0,
X    "xon/xoff", 1, 0
X};
Xint nflo = (sizeof(flotab) / sizeof(struct keytab));
X
X/*  Handshake characters  */
X
Xstruct keytab hshtab[] = {
X    "bell", 007, 0,
X    "cr",   015, 0,
X    "esc",  033, 0,
X    "lf",   012, 0,
X    "none", 999, 0,  /* (can't use negative numbers) */
X    "xoff", 023, 0,
X    "xon",  021, 0
X};
Xint nhsh = (sizeof(hshtab) / sizeof(struct keytab));
X
Xstruct keytab fntab[] = {   		/* File naming */
X    "converted", 1, 0,
X    "literal",   0, 0
X};
X
Xstruct keytab fttab[] = {		/* File types */
X    "binary",    1, 0,
X    "text",      0, 0
X};
X
Xextern struct keytab mdmtab[] ;		/* Modem types (in module ckdial.c) */
Xextern int nmdm;
X
X
X/* Parity keyword table */
X
Xstruct keytab partab[] = {
X    "even",    'e', 0,
X    "mark",    'm', 0,
X    "none",      0, 0,
X    "odd",     'o', 0,
X    "space",   's', 0
X};
Xint npar = (sizeof(partab) / sizeof(struct keytab));
X
X
X/* On/Off table */
X
Xstruct keytab onoff[] = {
X    "off",       0, 0,
X    "on",        1, 0
X};
X
X/*  D O P R M  --  Set a parameter.  */
X/*
X Returns:
X  -2: illegal input
X  -1: reparse needed
X   0: success
X*/
Xdoprm(xx) int xx; {
X    int x, y, z;
X    char *s;
X
Xswitch (xx) {
X
Xcase XYLINE:
X    if ((x = cmtxt("Device name",dftty,&s)) < 0) return(x);
X    ttclos();				/* close old line, if any was open */
X    strcpy(ttname,s);
X    if (strcmp(ttname,dftty) == 0) local = dfloc; else local = 1;
X    if (ttopen(ttname,local,mdmtyp) < 0 ) { /* Can we open the new line? */
X	perror("Sorry, can't open line");
X	return(-2);			/* If not, give bad return */
X	}
X    return(0);
X
Xcase XYCHKT:
X    if ((y = cmkey(blktab,3,"","1")) < 0) return(y);
X    if ((x = cmcfm()) < 0) return(x);
X    bctr = y;
X    return(0);
X
Xcase XYDEBU:
X    return(seton(&deblog));
X
Xcase XYDELA:
X    y = cmnum("Number of seconds before starting to send","5",10,&x);
X    debug(F101,"XYDELA: y","",y);
X    return(setnum(&delay,x,y));
X
Xcase XYDUPL:
X    if ((y = cmkey(dpxtab,2,"","full")) < 0) return(y);
X    if ((x = cmcfm()) < 0) return(x);
X    duplex = y;
X    return(0);
X
Xcase XYEOL:
X    y = cmnum("Decimal ASCII code for packet terminator","0",10,&x);
X    y = setcc(&z,x,y);
X    eol = z;
X    return(y);
X
Xcase XYESC:
X    y = cmnum("Decimal ASCII code for escape character","",10,&x);
X    return(setcc(&escape,x,y));
X
Xcase XYFILE:
X    if ((y = cmkey(filtab,nfilp,"File parameter","")) < 0) return(y);
X    switch (y) {
X	int z;
X	case XYFILD:			/* Display */
X	    y = seton(&z);
X	    if (y < 0) return(y);
X	    quiet = !z;
X	    return(0);
X
X	case XYFILN:			/* Names */
X	    if ((x = cmkey(fntab,2,"how to handle filenames","converted")) < 0)
X	    	return(x);
X	    if ((z = cmcfm()) < 0) return(z);
X	    fncnv = x;
X	    return(0);
X
X	case XYFILT:			/* Type */
X	    if ((x = cmkey(fttab,2,"type of file","text")) < 0)
X	    	return(x);
X	    if ((z = cmcfm()) < 0) return(z);
X	    binary = x;
X	    return(0);
X
X	case XYFILW:			/* Warning/Write-Protect */
X	    return(seton(&warn));
X    }
X
Xcase XYFLOW:				/* Flow control */
X    if ((y = cmkey(flotab,nflo,"","xon/xoff")) < 0) return(y);
X    if ((x = cmcfm()) < 0) return(x);
X    flow = y;
X    return(0);
X
Xcase XYHAND:				/* Handshake */
X    if ((y = cmkey(hshtab,nhsh,"","none")) < 0) return(y);
X    if ((x = cmcfm()) < 0) return(x);
X    turn = (y > 0127) ? 0 : 1 ;
X    turnch == y;
X    return(0);
X
Xcase XYLEN:
X    y = cmnum("Maximum number of characters in a packet","80",10,&x);
X    return(setnum(&spsiz,x,y));
X
Xcase XYMARK:
X    y = cmnum("Decimal ASCII code for packet-start character","1",10,&x);
X    y = setcc(&z,x,y);
X    mystch = z;
X    return(y);
X
Xcase XYMODM:
X    if ((x=cmkey(mdmtab,nmdm,"type of modem, direct means none","direct")) < 0)
X	return(x);
X    if ((z = cmcfm()) < 0) return(z);
X    mdmtyp = x;
X    return(0);
X	
Xcase XYNPAD:
X    y = cmnum("How many padding characters for inbound packets","0",10,&x);
X    return(setnum(&mypadn,x,y));
X
Xcase XYPADC:
X    y = cmnum("Decimal ASCII code for inbound pad character","0",10,&x);
X    y = setcc(&z,x,y);
X    mypadc = z;
X    return(y);
X
Xcase XYPARI:
X    if ((y = cmkey(partab,npar,"","none")) < 0) return(y);
X    if ((x = cmcfm()) < 0) return(x);
X    parity = y;
X    ebqflg = 1;				/* Flag we want 8th-bit prefixing */
X    return(0);
X
Xcase XYPROM:
X    if ((x = cmtxt("Program's command prompt","C-Kermit>",&s)) < 0) return(x);
X    cmsetp(s);
X    return(0);
X
Xcase XYSPEE:
X    if (!local) {
X	printf("\nSpeed setting can only be done on an external line\n");
X	printf("You must 'set line' before issuing this command\n");
X	return(0);
X    }	
X    lp = line;
X    sprintf(lp,"Baud rate for %s",ttname);
X    if ((y = cmnum(line,"",10,&x)) < 0) return(y);
X    if (y = (cmcfm()) < 0) return(y);
X    y = chkspd(x);
X    if (y < 0) 
X    	printf("?Unsupported line speed - %d\n",x);
X    else {
X    	speed = y;
X	printf("%s: %d baud\n",ttname,speed);
X    }
X    return(0);
X
Xcase XYTIMO:
X    y = cmnum("Interpacket timeout interval","5",10,&x);
X    y = setnum(&timint,x,y);
X    if (y > -1) timef = 1;
X    return(y);
X
Xdefault:
X    if (x = (cmcfm()) < 0) return(x);
X    printf("Not working yet - %s\n",cmdbuf);
X    return(0);
X    }
X}
X
X/*  C H K S P D  --  Check if argument is a valid baud rate  */
X
Xchkspd(x) int x; {
X    switch (x) {
X	case 0:
X	case 110:
X	case 150:
X	case 300:
X	case 600:
X	case 1200:
X	case 1800:
X	case 2400:
X	case 4800:
X	case 9600:
X	    return(x);
X	default: 
X	    return(-1);
X      }
X}
X
X/*  S E T O N  --  Parse on/off (default on), set parameter to result  */
X
Xseton(prm) int *prm; {
X    int x, y;
X    if ((y = cmkey(onoff,2,"","on")) < 0) return(y);
X    if ((x = cmcfm()) < 0) return(x);
X    *prm = y;
X    return(0);
X}
X
X/*  S E T N U M  --  Set parameter to result of cmnum() parse.  */
X/*
X Call with x - number from cnum parse, y - return code from cmnum
X*/
Xsetnum(prm,x,y) int x, y, *prm; {
X    debug(F101,"setnum",y);
X    if (y < 0) return(y);
X    if (x > 94) {
X	printf("\n?Sorry, 94 is the maximum\n");
X	return(-2);
X    }
X    if ((y = cmcfm()) < 0) return(y);
X    *prm = x;
X    return(0);
X}
X
X/*  S E T C C  --  Set parameter to an ASCII control character value.  */
X
Xsetcc(prm,x,y) int x, y, *prm; {
X    if (y < 0) return(y);
X    if ((x > 037) && (x != 0177)) {
X	printf("\n?Not in ASCII control range - %d\n",x);
X	return(-2);
X    }
X    if ((y = cmcfm()) < 0) return(y);
X    *prm = x;
X    return(0);
X}
X
X/*  D O R M T  --  Do a remote command  */
X
Xdormt(xx) int xx; {
X    int x;
X    char *s, sbuf[50], *s2;
X
X    if (xx < 0) return(xx);
X    switch (xx) {
X
Xcase XZCWD:				/* CWD */
X    if ((x = cmtxt("Remote directory name","",&s)) < 0) return(x);
X    debug(F111,"XZCWD: ",s,x);
X    *sbuf = NUL;
X    s2 = sbuf;
X    if (*s != NUL) {			/* If directory name given, */
X					/* get password on separate line. */
X        if (tlevel > -1) {		/* From take file... */
X
X	    *line = NUL;
X	    if (fgets(sbuf,50,tfile[tlevel]) == NULL)
X	    	ermsg("take file ends prematurely in 'remote cwd'");
X	    debug(F110," pswd from take file",s2,0);
X
X        } else {			/* From terminal... */
X
X	    printf(" Password: "); 		/* get a password */
X	    while ((x = getchar()) != '\n') {   /* without echo. */
X	    	if ((x &= 0177) == '?') {
X	    	    printf("? Password of remote directory\n Password: ");
X		    s2 = sbuf;
X		    *sbuf = NUL;
X	    	}
X	    	else if (x == ESC)	/* Mini command line editor... */
X	    	    putchar(BEL);
X		else if (x == BS || x == 0177)
X		    *s2--;
X		else if (x == 025) {
X		    s2 = sbuf;
X		    *sbuf = NUL;
X		}
X	    	else
X		    *s2++ = x;
X            }
X	    *s2 = NUL;
X	    putchar('\n');
X        }
X        s2 = sbuf;
X    } else s2 = "";
X    debug(F110," password",s2,0);
X    sstate = setgen('C',s,s2,"");
X    return(0);
X
Xcase XZDEL:				/* Delete */
X    if ((x = cmtxt("Name of remote file(s) to delete","",&s)) < 0) return(x);
X    return(sstate = rfilop(s,'E'));
X
Xcase XZDIR:				/* Directory */
X    if ((x = cmtxt("Remote directory or file specification","",&s)) < 0)
X    	return(x);
X    return(sstate = setgen('D',s,"",""));
X
Xcase XZHLP:				/* Help */
X    if (x = (cmcfm()) < 0) return(x);
X    sstate = setgen('H',"","","");
X    return(0);
X
Xcase XZHOS:				/* Host */
X    if ((x = cmtxt("Command for remote system","",&cmarg)) < 0) return(x);
X    return(sstate = 'c');
X
Xcase XZPRI:				/* Print */
X    if ((x = cmtxt("Remote file(s) to print on remote printer","",&s)) < 0)
X    	return(x);
X    return(sstate = rfilop(s,'S'));
X
Xcase XZSPA:				/* Space */
X    if ((x = cmtxt("Confirm, or remote directory name","",&s)) < 0) return(x);
X    return(sstate = setgen('U',s,"",""));
X    
Xcase XZTYP:				/* Type */
X    if ((x = cmtxt("Remote file specification","",&s)) < 0) return(x);
X    return(sstate = rfilop(s,'T'));
X
Xcase XZWHO:
X    if ((x = cmtxt("Remote user name, or carriage return","",&s)) < 0)
X    	return(x);
X    return(sstate = setgen('W',s,"",""));
X
Xdefault:
X    if (x = (cmcfm()) < 0) return(x);
X    printf("not working yet - %s\n",cmdbuf);
X    return(-2);
X    }
X}
X
X
X
X/*  R F I L O P  --  Remote File Operation  */
X
Xrfilop(s,t) char *s, t; {
X    if (*s == NUL) {
X	printf("?File specification required\n");
X	return(-2);
X    }
X    debug(F111,"rfilop",s,t);
X    return(setgen(t,s,"",""));
X}
X
X/*  S C R E E N  --  Screen display function  */
X/*  
X   c - a character or small integer
X   n - an long integer
X   s - a string.
X Fill in this routine with the appropriate display update for the system.
X This version is for a dumb tty, and uses the arguments like this:
X   c:     0 - print s on a new line, followed by a space.
X          1 - print s at current screen position.
X          2 - print s at current position, followed by newline.
X	  3 - print "s: n" at current position, followed by newline.
X      other - print c as a character.
X   n: if c is 'other', used to decide whether or how to print c.
X   s: as above.
X Horizontal screen position is kept current if screen is only updated by 
X calling this function.  Wraparound is done at column 78.
X*/
Xscreen(c,n,s) char c; long n; char *s; {
X    static int p = 0;			/* Screen position */
X    int len;  char buf[80];
X    len = strlen(s);
X    if (!displa || quiet) return;	/* No update if display flag off */
X    switch (c) {
X	case 0:				/* Print s on newline */
X	    conoll("");			/* Start new line, */
X	    conol(s);			/* Print string. */
X	    conoc(SP);			/* Leave a space. */
X	    p = len + 1;		/* Set position counter. */
X	    return;
X	case 1:
X	    if (p + len > 78) { conoll(""); p = 0; }
X	    conol(s);  if ((p += len) > 78) conoll("");
X	    return;
X	case 2:				/* Print s, then newline */
X	    if (p + len > 78) conoll("");
X	    conoll(s);  p = 0;
X	    return;
X	case 3:
X	    sprintf(buf,"%s: %ld",s,n);  conoll(buf);  p = 0;
X	    return;
X	case BS:			/* Backspace */
X	    if (p > 0) p--;
X	case BEL:			/* Beep */
X	    conoc(c);
X	    return;
X	default:			/* Packet type display */
X	    if (c == 'Y') return;	/* Don't bother with ACKs */
X	    if (c == 'D') {		/* Only show every 4th data packet */
X		c = '.';
X		if (n % 4) return;
X	    }
X	    if (p++ > 78) {		/* If near left margin, */
X		conoll("");		/* Start new line */
X		p = 0;			/* and reset counter. */
X	    }
X	    conoc(c);			/* Display the character. */
X	    return;
X    }
X}
X
X/*  I N T M S G  --  Issue message about terminal interrupts  */
X
Xintmsg(n) long n; {
X    extern char *chstr();
X    char buf[80];
X
X    if ((!displa) || (quiet)) return;
X#ifdef UXIII
X    (void) conchk();	/* clear out pending escape-signals in ckxbsd.c */
X#endif
X    if (n == 1) {
X	screen(2,0l,"");
X#ifdef UXIII
X				/* we need to signal before kb input */
X	sprintf(buf,"Type escape (%s) followed by:",chstr(escape));
X	screen(2,0l,buf);
X#endif
X#ifdef PROVX1
X	screen(2,0l,"(transfer cannot be interrupted from keyboard)");
X#else
X	screen(2,0l,"CTRL-F to cancel file,  CTRL-R to resend current packet");
X	screen(2,0l,"CTRL-B to cancel batch, CTRL-A for status report...");
X#endif
X    }
X    else screen(1,0l," ");
X}
X
X/*  C H K I N T  --  Check for console interrupts  */
X
X/*** should rework not to destroy typeahead ***/
X
Xchkint() {
X    int ch, cn;
X
X    if ((!local) || (quiet)) return(0);	/* Only do this if local & not quiet */
X    cn = conchk();			/* Any input waiting? */
X    debug(F101,"conchk","",cn);
X
X    while (cn > 0) {			/* Yes, read it. */
X	cn--;
X			/* give read 5 seconds for interrupt character */
X	if ((ch = coninc(5)) < 0) return(0);
X	switch (ch & 0177) {
X	    case 0001:			/* CTRL-A */
X		screen(2,0l,"^A  Status report:");
X		screen(1,0l,     " file type: ");
X		if (binary) screen(2,0l,"binary"); else screen(2,0l,"text");
X		screen(3,filcnt," file number");
X		screen(3,ffc,   " characters ");
X		screen(3,(long) bctu,  " block check");
X		screen(3,(long)rptflg," compression");
X		screen(3,(long)ebqflg," 8th-bit prefixing");
X		continue;
X	    case 0002:			/* CTRL-B */
X	    	screen(1,0l,"^B - Cancelling Batch ");
X	    	czseen = 1;
X		continue;
X	    case 0006:			/* CTRL-F */
X	    	screen(1,0l,"^F - Cancelling File ");
X	    	cxseen = 1;
X		continue;
X	    case 0022:	    	    	/* CTRL-R */
X	    	screen(1,0l,"^R - Resending ");
X	    	resend();
X		return(1);
X	    default:			/* Anything else, just beep */
X	    	screen(BEL,0l,"");
X		continue;
X    	}
X    }
X    return(0);
X}
X
X/*  D E B U G  --  Enter a record in the debugging log  */
X
X/*
X Call with a format, two strings, and a number:
X   f  - Format, a bit string in range 0-7.
X        If bit x is on, then argument number x is printed.
X   s1 - String, argument number 1.  If selected, printed as is.
X   s2 - String, argument number 2.  If selected, printed in brackets.
X   n  - Int, argument 3.  If selected, printed preceded by equals sign.
X
X   f=0 is special: print s1,s2, and interpret n as a char.
X*/
Xdebug(f,s1,s2,n) int f, n; char *s1, *s2; {
X    static char s[200];
X    char *sp = s;
X
X    if (!deblog) return;		/* If no debug log, don't */
X    switch (f) {
X    	case F000:			/* 0, print both strings, */
X	    sprintf(sp,"%s%s%c\n",s1,s2,n); /*  and interpret n as a char */
X	    zsout(ZDFILE,s);
X	    break;
X    	case F001:			/* 1, "=n" */
X	    sprintf(sp,"=%d\n",n);
X	    zsout(ZDFILE,s);
X	    break;
X    	case F010:			/* 2, "[s2]" */
X	    sprintf(sp,"[%s]\n",s2);
X	    zsout(ZDFILE,"");
X	    break;
X    	case F011:			/* 3, "[s2]=n" */
X	    sprintf(sp,"[%s]=%d\n",s2,n);
X	    zsout(ZDFILE,s);
X	    break;
X    	case F100:			/* 4, "s1" */
X	    zsoutl(ZDFILE,s1);
X	    break;
X    	case F101:			/* 5, "s1=n" */
X	    sprintf(sp,"%s=%d\n",s1,n);
X	    zsout(ZDFILE,s);
X	    break;
X    	case F110:			/* 6, "s1[s2]" */
X	    sprintf(sp,"%s[%s]\n",s1,s2);
X	    zsout(ZDFILE,s);
X	    break;
X    	case F111:			/* 7, "s1[s2]=n" */
X	    sprintf(sp,"%s[%s]=%d\n",s1,s2,n);
X	    zsout(ZDFILE,s);
X	    break;
X	default:
X	    sprintf(sp,"\n?Invalid format for debug() - %d\n",n);
X	    zsout(ZDFILE,s);
X    }
X}
X
X/*  T L O G  --  Log a record in the transaction file  */
X/*
X Call with a format and 3 arguments: two strings and a number:
X   f  - Format, a bit string in range 0-7, bit x is on, arg #x is printed.
X   s1 - String, argument number 1.
X   s2 - String, argument number 2.
X   n  - Int, argument 3.
X*/
Xtlog(f,s1,s2,n) int f; long n; char *s1, *s2; {
X    static char s[200];
X    char *sp = s; int x;
X    
X    if (!tralog) return;		/* If no transaction log, don't */
X    switch (f) {
X    	case F000:			/* 0 (special) "s1 n s2"  */
X	    sprintf(sp,"%s %ld %s\n",s1,n,s2);
X	    zsout(ZTFILE,s);
X	    break;
X    	case F001:			/* 1, " n" */
X	    sprintf(sp," %ld\n",n);
X	    zsout(ZTFILE,s);
X	    break;
X    	case F010:			/* 2, "[s2]" */
X	    x = strlen(s2);
X	    if (s2[x] == '\n') s2[x] = '\0';
X	    sprintf(sp,"[%s]\n",s2);
X	    zsout(ZTFILE,"");
X	    break;
X    	case F011:			/* 3, "[s2] n" */
X	    x = strlen(s2);
X	    if (s2[x] == '\n') s2[x] = '\0';
X	    sprintf(sp,"[%s] %ld\n",s2,n);
X	    zsout(ZTFILE,s);
X	    break;
X    	case F100:			/* 4, "s1" */
X	    zsoutl(ZTFILE,s1);
X	    break;
X    	case F101:			/* 5, "s1: n" */
X	    sprintf(sp,"%s: %ld\n",s1,n);
X	    zsout(ZTFILE,s);
X	    break;
X    	case F110:			/* 6, "s1 s2" */
X	    x = strlen(s2);
X	    if (s2[x] == '\n') s2[x] = '\0';
X	    sprintf(sp,"%s %s\n",s1,s2);
X	    zsout(ZTFILE,s);
X	    break;
X    	case F111:			/* 7, "s1 s2: n" */
X	    x = strlen(s2);
X	    if (s2[x] == '\n') s2[x] = '\0';
X	    sprintf(sp,"%s %s: %ld\n",s1,s2,n);
X	    zsout(ZTFILE,s);
X	    break;
X	default:
X	    sprintf(sp,"\n?Invalid format for tlog() - %ld\n",n);
X	    zsout(ZTFILE,s);
X    }
X}
!FUNKY!STUFF!
echo x - ckwart.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckwart.c
X/* W A R T
X *
X * pre-process a lex-like file into a C program.
X *
X * Jeff Damens, Columbia University Center for Computing Activites, 11/84.
X * (Reorganized by Frank da Cruz into a single source module for ease
X * of distribution).
X * Copyright (C) 1984, Trustees of Columbia University.
X * May be copied and used except for explicitly commercial purposes.
X *
X * input format is:
X *  lines to be copied | %state <state names...>
X *  %%
X * <state> | <state,state,...> CHAR  { actions }
X * ...
X *  %%
X */
X
X#include <stdio.h>
X#include <ctype.h>
X
X/* token types */
X
X#define SEP 1
X#define LBRACK 2
X#define RBRACK 3
X#define WORD 4
X#define COMMA 5
X
X/* storage sizes */
X
X#define MAXSTATES 50			/* max number of states */
X#define MAXWORD 50			/* max # of chars/word */
X#define SBYTES ((MAXSTATES+7)/8)	/* # of bytes for state bitmask */
X
X/* name of wart function in generated program */
X
X#ifndef FNAME
X#define FNAME "wart"
X#endif
X
X/* data structure for state information */
X
X#ifdef PROVX1
Xtypedef unsigned short CHAR;
X#else
Xtypedef unsigned char CHAR;
X#endif
X
Xstruct trans { CHAR states[SBYTES];	/* included states */
X    	       int anyst;		/* true if this good from any state */
X    	       CHAR inchr;		/* input character */
X	       int actno;		/* associated action */
X	       struct trans *nxt; };	/* next transition */
X
Xtypedef struct trans *Trans;
X
X/* Variables and tables */
X
Xint lines,nstates,nacts;
X
Xchar tokval[MAXWORD];
X
Xint tbl[MAXSTATES*128];
X
X
X
Xchar *txt1 = "\n\
X#define BEGIN state =\n\
X\n\
Xint state = 0;\n\
X\n";
X
Xchar *fname = FNAME;		/* function name goes here */
X
X/* rest of program... */
X
Xchar *txt2 = "()\n\
X{\n\
X  int c,actno;\n\
X  extern int tbl[];\n\
X  while (1) {\n\
X	c = input();\n\
X	if ((actno = tbl[c + state*128]) != -1)\n\
X	  switch(actno) {\n";
X
X/* this program's output goes here, followed by final text... */
X
Xchar *txt3 = "\n    }\n  }\n\}\n\n";
X
X/*
X * turn on the bit associated with the given state
X *
X */
Xsetstate(state,t)
Xint state;
XTrans t;
X{
X  int idx,msk;
X  idx = state/8;			/* byte associated with state */
X  msk = 0x80 >> (state % 8);		/* bit mask for state */
X  t->states[idx] |= msk;
X}
X
X/*
X * see if the state is involved in the transition
X *
X */
X
Xteststate(state,t)
Xint state;
XTrans t;
X{
X  int idx,msk;
X  idx = state/8;
X  msk = 0x80 >> (state % 8);
X  return(t->states[idx] & msk);
X}
X
X
X/*
X * read input from here...
X *
X */
X
XTrans
Xrdinput(infp,outfp)
XFILE *infp,*outfp;
X{
X  Trans x,rdrules();
X  lines = 1;				/* line counter */
X  nstates = 0;				/* no states */
X  nacts = 0;				/* no actions yet */
X  fprintf(outfp,"\n\
X/* WARNING -- This C source program generated by Wart preprocessor. */\n");
X  fprintf(outfp,"\
X/* Do not edit this file; edit the Wart-format source file instead, */\n");
X  fprintf(outfp,"\
X/* and then run it through Wart to produce a new C source file.     */\n\n");
X  initial(infp,outfp);			/* read state names, initial defs */
X  prolog(outfp);			/* write out our initial code */
X  x = rdrules(infp,outfp);		/* read rules */
X  epilogue(outfp);			/* write out epilogue code */
X  return(x);
X}
X
X/*
X * initial - read initial definitions and state names.  Returns
X * on EOF or %%.
X *
X */
X
Xinitial(infp,outfp)
XFILE *infp,*outfp;
X{
X  int c;
X  char wordbuf[MAXWORD];
X  while ((c = getc(infp)) != EOF) {
X	if (c == '%') {
X			rdword(infp,wordbuf);
X			if (strcmp(wordbuf,"states") == 0)
X			    rdstates(infp,outfp);
X			else if (strcmp(wordbuf,"%") == 0) return;
X			else fprintf(outfp,"%%%s",wordbuf);
X		      }
X	else putc(c,outfp);
X	if (c == '\n') lines++;
X     }
X}
X
X/*
X * boolean function to tell if the given character can be part of
X * a word.
X *
X */
Xisin(s,c) char *s; int c; {
X   for (; *s != '\0'; s++)
X      if (*s == c) return(1);
X   return(0);
X}
Xisword(c)
Xint c;
X{
X  static char special[] = ".%_-$@";	/* these are allowable */
X  return(isalnum(c) || isin(special,c));
X}
X
X/*
X * read the next word into the given buffer.
X *
X */
Xrdword(fp,buf)
XFILE *fp;
Xchar *buf;
X{
X  int len = 0,c;
X  while (isword(c = getc(fp)) && ++len < MAXWORD) *buf++ = c;
X  *buf++ = '\0';			/* tie off word */
X  ungetc(c,fp);				/* put break char back */
X}
X
X/*
X * read state names, up to a newline.
X *
X */
X
Xrdstates(fp,ofp)
XFILE *fp,*ofp;
X{
X  int c;
X  char wordbuf[MAXWORD];
X  while ((c = getc(fp)) != EOF && c != '\n')
X  {
X	if (isspace(c)) continue;	/* skip whitespace */
X	ungetc(c,fp);			/* put char back */
X	rdword(fp,wordbuf);		/* read the whole word */
X	enter(wordbuf,++nstates);	/* put into symbol tbl */
X	fprintf(ofp,"#define %s %d\n",wordbuf,nstates);
X  }
X  lines++;
X}
X		
X/*
X * allocate a new, empty transition node
X *
X */
X
XTrans
Xnewtrans()
X{
X  Trans new;
X  int i;
X  new = (Trans) malloc(sizeof (struct trans));
X  for (i=0; i<SBYTES; i++) new->states[i] = 0;
X  new->anyst = 0;
X  new->nxt = NULL;
X  return(new);
X}
X
X/*
X * read all the rules.
X *
X */
X
XTrans
Xrdrules(fp,out)
XFILE *fp,*out;
X{
X  Trans head,cur,prev;
X  int curtok,i;
X  head = cur = NULL;
X  while ((curtok = gettoken(fp)) != SEP) 
X
X	switch(curtok) {
X		case LBRACK: if (cur == NULL) cur = newtrans();
X		    	     else fatal("duplicate state list");
X			     statelist(fp,cur);/* set states */
X			     continue;	/* prepare to read char */
X
X		case WORD:   if (strlen(tokval) != 1)
X					fatal("multiple chars in state");
X			     if (cur == NULL) {
X				cur = newtrans();
X				cur->anyst = 1;
X				}
X			     cur->actno = ++nacts;
X			     cur->inchr = tokval[0];
X			     if (head == NULL) head = cur;
X			     else prev->nxt = cur;
X			     prev = cur;
X			     cur = NULL;
X			     copyact(fp,out,nacts);
X			     break; 
X		 default: fatal("bad input format");
X	     }
X	
X   return(head);
X}
X
X/*
X * read a list of (comma-separated) states, set them in the
X * given transition.
X *
X */
Xstatelist(fp,t)
XFILE *fp;
XTrans t;
X{
X  int curtok,sval;
X  curtok = COMMA;
X  while (curtok != RBRACK) {
X	if (curtok != COMMA) fatal("missing comma");
X	if ((curtok = gettoken(fp)) != WORD) fatal("missing state name");
X        if ((sval = lkup(tokval)) == -1) {
X		fprintf(stderr,"state %s undefined\n",tokval);
X		fatal("undefined state");
X	   }
X        setstate(sval,t);
X	curtok = gettoken(fp);
X   }
X}
X
X/*
X * copy an action from the input to the output file
X *
X */
Xcopyact(inp,outp,actno)
XFILE *inp,*outp;
Xint actno;
X{
X  int c,bcnt;
X  fprintf(outp,"case %d:\n",actno);
X  while ((c = getc(inp)) != '\n' && isspace(c));	/* skip whitespace */
X  if (c == '{') {
X     bcnt = 1;
X     putc(c,outp);
X     while (bcnt > 0 && (c = getc(inp)) != EOF) {
X	if (c == '{') bcnt++;
X	else if (c == '}') bcnt--;
X	else if (c == '\n') lines++;
X	putc(c,outp);
X      }
X     if (bcnt > 0) fatal("action doesn't end");
X    }
X   else {
X	  while (c != '\n' && c != EOF) {
X		putc(c,outp);
X		c = getc(inp);
X	    }
X	  lines++;
X	}
X   fprintf(outp,"\nbreak;\n");
X}
X
X/*
X * find the action associated with a given character and state.
X * returns -1 if one can't be found.
X *
X */
Xfaction(hd,state,chr)
XTrans hd;
Xint state,chr;
X{
X  while (hd != NULL) {
X    if (hd->anyst || teststate(state,hd))
X      if (hd->inchr == '.' || hd->inchr == chr) return(hd->actno);
X    hd = hd->nxt;
X    }
X  return(-1);
X}
X
X
X/*
X * empty the table...
X *
X */
Xemptytbl()
X{
X  int i;
X  for (i=0; i<nstates*128; i++) tbl[i] = -1;
X}
X
X/*
X * add the specified action to the output for the given state and chr.
X *
X */
X
Xaddaction(act,state,chr)
Xint act,state,chr;
X{
X tbl[state*128 + chr] = act;
X}
X
Xwritetbl(fp)
XFILE *fp;
X{
X  warray(fp,"tbl",tbl,128*(nstates+1));
X}
X
X/*
X * write an array to the output file, given its name and size.
X *
X */
Xwarray(fp,nam,cont,siz)
XFILE *fp;
Xchar *nam;
Xint cont[],siz;
X{
X  int i;
X  fprintf(fp,"int %s[] = {\n",nam);
X  for (i = 0; i < siz; i++) {
X	fprintf(fp,"%d, ",cont[i]);
X	if ((i % 20) == 0) putc('\n',fp);
X	}
X  fprintf(fp,"};\n");
X}
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{
X  Trans head;
X  int state,c;
X  FILE *infile,*outfile;
X
X  if (argc > 1) {
X    if ((infile = fopen(argv[1],"r")) == NULL) {
X    	fprintf(stderr,"Can't open %s\n",argv[1]);
X	fatal("unreadable input file"); } }
X  else infile = stdin;
X
X  if (argc > 2) {
X    if ((outfile = fopen(argv[2],"w")) == NULL) {
X    	fprintf(stderr,"Can't write to %s\n",argv[2]);
X	fatal("bad output file"); } }
X  else outfile = stdout;
X
X  clrhash();				/* empty hash table */
X  head = rdinput(infile,outfile);	/* read input file */
X  emptytbl();				/* empty our tables */
X  for (state = 0; state <= nstates; state++)
X    for (c = 1; c < 128; c++)
X     addaction(faction(head,state,c),state,c);	/* find actions, add to tbl */
X  writetbl(outfile);
X  copyrest(infile,outfile);
X  printf("%d states, %d actions\n",nstates,nacts);
X#ifdef undef
X  for (state = 1; state <= nstates; state ++)
X    for (c = 1; c < 128; c++)
X       if (tbl[state*128 + c] != -1) printf("state %d, chr %d, act %d\n",
X       	state,c,tbl[state*128 + c]);
X#endif
X  exit(0);
X}
X
X/*
X * fatal error handler
X *
X */
X
Xfatal(msg)
Xchar *msg;
X{
X  fprintf(stderr,"error in line %d: %s\n",lines,msg);
X  exit(1);
X}
X
Xprolog(outfp)
XFILE *outfp;
X{
X  int c;
X  while ((c = *txt1++) != '\0')  putc(c,outfp);
X  while ((c = *fname++) != '\0') putc(c,outfp);
X  while ((c = *txt2++) != '\0')  putc(c,outfp);
X}
X
Xepilogue(outfp)
XFILE *outfp;
X{
X  int c;
X  while ((c = *txt3++) != '\0') putc(c,outfp);
X}
X
Xcopyrest(in,out)
XFILE *in,*out;
X{
X  int c;
X  while ((c = getc(in)) != EOF) putc(c,out);
X}
X
X/*
X * gettoken - returns token type of next token, sets tokval
X * to the string value of the token if appropriate.
X *
X */
X
Xgettoken(fp)
XFILE *fp;
X{
X  int c;
X  while (1) {				/* loop if reading comments... */
X    do {
X	  c = getc(fp);
X	  if (c == '\n') lines++;
X       } while (isspace(c));		/* skip whitespace */
X    switch(c) {
X	  case EOF: return(SEP);
X	  case '%': if ((c = getc(fp)) == '%') return(SEP);
X		    tokval[0] = '%';
X		    tokval[1] = c;
X		    rdword(fp,tokval+2);
X		    return(WORD);
X	  case '<': return(LBRACK);
X	  case '>': return(RBRACK);
X	  case ',': return(COMMA);
X	  case '/': if ((c = getc(fp)) == '*') {
X	    	      rdcmnt(fp);	/* skip over the comment */
X		      continue; }	/* and keep looping */
X		    else {
X			ungetc(c);	/* put this back into input */
X			c = '/'; }	/* put character back, fall thru */
X
X	  default: if (isword(c)) {
X			  ungetc(c,fp);
X			  rdword(fp,tokval);
X			  return(WORD);
X		      	}
X		   else fatal("Invalid character in input");
X	     }
X  }
X}
X
X/*
X * skip over a comment
X *
X */
X
Xrdcmnt(fp)
XFILE *fp;
X{
X  int c,star,prcnt;
X  prcnt = star = 0;			/* no star seen yet */
X  while (!((c = getc(fp)) == '/' && star)) {
X    if (c == EOF || (prcnt && c == '%')) fatal("Unterminated comment");
X    prcnt = (c == '%');
X    star = (c == '*');
X    if (c == '\n') lines++; }
X}
X
X
X/*
X * symbol table management for wart
X *
X * entry points:
X *   clrhash - empty hash table.
X *   enter - enter a name into the symbol table
X *   lkup - find a name's value in the symbol table.
X *
X */
X
X#define HASHSIZE 101			/* # of entries in hash table */
X
Xstruct sym { char *name;		/* symbol name */
X	     int val;			/* value */
X	     struct sym *hnxt; }	/* next on collision chain */
X    *htab[HASHSIZE];			/* the hash table */
X
X
X/*
X * empty the hash table before using it...
X *
X */
Xclrhash()
X{
X  int i;
X  for (i=0; i<HASHSIZE; i++) htab[i] = NULL;
X}
X
X/*
X * compute the value of the hash for a symbol
X *
X */
Xhash(name)
Xchar *name;
X{
X  int sum;
X  for (sum = 0; *name != '\0'; name++) sum += (sum + *name);
X  sum %= HASHSIZE;			/* take sum mod hashsize */
X  if (sum < 0) sum += HASHSIZE;		/* disallow negative hash value */
X  return(sum);
X}
X
X/*
X * make a private copy of a string...
X *
X */
Xchar *
Xcopy(s)
Xchar *s;
X{
X  char *new;
X  new = (char *) malloc(strlen(s) + 1);
X  strcpy(new,s);
X  return(new);
X}
X
X/*
X * enter state name into the hash table
X *
X */
Xenter(name,svalue)
Xchar *name;
Xint svalue;
X{
X  int h;
X  struct sym *cur;
X  if (lkup(name) != -1) {
X	fprintf(stderr,"state %s appears twice...\n");
X	exit(1); }
X  h = hash(name);
X  cur = (struct sym *)malloc(sizeof (struct sym));
X  cur->name = copy(name);
X  cur->val = svalue;
X  cur->hnxt = htab[h];
X  htab[h] = cur;
X}
X
X/*
X * find name in the symbol table, return its value.  Returns -1
X * if not found.
X *
X */
Xlkup(name)
Xchar *name;
X{
X  struct sym *cur;
X  for (cur = htab[hash(name)]; cur != NULL; cur = cur->hnxt)
X	if (strcmp(cur->name,name) == 0) return(cur->val);
X  return(-1);
X}
!FUNKY!STUFF!
echo x - ckmain.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckmain.c
Xchar *versio = "C-Kermit 4.2(030) PRERELEASE # 2, 5 March 85";
X
X/* C K M A I N  --  C-Kermit Main program */
X
X/*
X Authors: Frank da Cruz, Bill Catchings, Jeff Damens;
X Columbia University Center for Computing Activities, 1984-85.
X Copyright (C) 1985, Trustees of Columbia University in the City of New York.
X Permission is granted to any individual or institution to copy or use this
X program except for explicitly commercial purposes, provided this copyright
X notice is retained.
X*/
X/*
X The Kermit file transfer protocol was developed at Columbia University.
X It is named after Kermit the Frog, star of the television series THE
X MUPPET SHOW; the name is used by permission of Henson Associates, Inc.
X "Kermit" is also Celtic for "free".
X*/
X/*
X Thanks to Herm Fischer, Litton Data Systems, for extensive contributions to
X version 4, and to the following people for their contributions over the years:
X
X   Bob Cattani & Chris Maio, Columbia CS Dept
X   Alan Crosswell, CUCCA
X   Carl Fongheiser, CWRU
X   Jim Guyton, Rand Corp
X   Stan Hanks, Rice U.
X   Ken Harrenstein, SRI
X   Steve Hemminger, Tektronix
X   Tony Movshon, NYU
X   Ken Poulton, HP Labs
X   Dave Tweten, NASA Ames
X   Walter Underwood, Ford Aerospace
X   Pieter Van Der Linden, Centre Mondial (Paris)
X   Lauren Weinstein, Vortex
X
X and many others.
X*/
X
X#include "ckermi.h"
X
X/* Text message definitions */
X
X#ifndef XENIX
X
Xchar *hlptxt = "C-Kermit Server\n\
X\n\
XServer Function    Customary Command to Invoke the Function\n\
X Who's Logged In?   REMOTE WHO [user]\n\
X Help               REMOTE HELP\n\
X Finish Serving     FINISH\n\
X Send File(s)       GET filespec\n\
X Receive File(s)    SEND filespec\n\
X Directory Listing  REMOTE DIRECTORY [filespec]\n\
X Change Directory   REMOTE CWD [directory]\n\
X Type File(s)       REMOTE TYPE filespec\n\
X Delete File(s)     REMOTE DELETE filespec\n\
X Disk Usage Query   REMOTE SPACE [directory]\n\
X Unix Shell Command REMOTE HOST command-string\n\
X\n\0";
X
X#else
X
Xchar *hlptxt = "C-Kermit Server Commands Supported:\n\
X\n\
XGET filespec	REMOTE CWD [directory]		REMOTE SPACE [directory]\n\
XSEND filespec	REMOTE DIRECTORY [filespec]	REMOTE HOST command-string\n\
XFINISH		REMOTE DELETE filespec		REMOTE WHO [user]\n\
XREMOTE HELP	REMOTE TYPE filespec\n\
X\n\0";
X
X#endif
X
Xchar *srvtxt = "\r\n\
XC-Kermit server starting.  Return to your local machine by typing\r\n\
Xits escape sequence for closing the connection, and issue further\r\n\
Xcommands from there.  To shut down the C-Kermit server, issue the\r\n\
XFINISH command and then reconnect.\n\
X\r\n\0";
X
X/* Declarations for Send-Init Parameters */
X
Xint spsiz =  DSPSIZ,			/* maximum packet size we can send */
X    timint = DMYTIM,			/* My timeout interval */
X    timef = 0,				/* Flag for override packet timeout */
X    npad = MYPADN,			/* How much padding to send */
X    mypadn = MYPADN,			/* How much padding to ask for */
X    chklen = 1,				/* Length of block check */
X    bctr = 1,				/* Block check type requested */
X    bctu = 1,				/* Block check type used */
X    ebq =  MYEBQ,			/* 8th bit prefix */
X    ebqflg = 0,				/* 8th-bit quoting flag */
X    rpt = 0,				/* Repeat count */
X    rptq = MYRPTQ,			/* Repeat prefix */
X    rptflg = 0,				/* Repeat processing flag */
X    capas = 0;				/* Capabilities */
X
Xchar padch = MYPADC,			/* Padding character to send */
X    mypadc = MYPADC,			/* Padding character to ask for */
X    eol = MYEOL,			/* End-Of-Line character to send */
X    ctlq = CTLQ,			/* Control prefix in incoming data */
X    myctlq = CTLQ;			/* Outbound control character prefix */
X
X
X/* Packet-related variables */
X
Xint pktnum = 0,				/* Current packet number */
X    prvpkt = -1,			/* Previous packet number */
X    sndtyp,				/* Type of packet just sent */
X    size,				/* Current size of output pkt data */
X    osize,				/* Previous output packet data size */
X    maxsize,				/* Max size for building data field */
X    spktl;				/* Length packet being sent */
X
Xchar sndpkt[MAXPACK*2], 		/* Entire packet being sent */
X    recpkt[RBUFL], 			/* Packet most recently received */
X    data[MAXPACK+4],   			/* Packet data buffer */
X    srvcmd[MAXPACK*2],			/* Where to decode server command */
X    *srvptr,				/* Pointer to above */
X    mystch = SOH,			/* Outbound packet-start character */
X    stchr = SOH;			/* Incoming packet-start character */
X
X/* File-related variables */
X
Xchar filnam[50];			/* Name of current file. */
X
Xint nfils,				/* Number of files in file group */
X    fsize;				/* Size of current file */
X
X/* Communication line variables */
X
Xchar ttname[50];			/* Name of communication line. */
X
Xint parity,				/* Parity specified, 0,'e','o',etc */
X    flow,				/* Flow control, 1 = xon/xoff */
X    speed = -1,				/* Line speed */
X    turn = 0,				/* Line turnaround handshake flag */
X    turnch = XON,			/* Line turnaround character */
X    duplex = 0,				/* Duplex, full by default */
X    escape = 034,			/* Escape character for connect */
X    delay = DDELAY,			/* Initial delay before sending */
X    mdmtyp = 0;				/* Type of modem 1=hayes 2=ventel */
X
X
X/* Statistics variables */
X
Xlong filcnt,			/* Number of files in transaction */
X    flci,			/* Characters from line, current file */
X    flco,			/* Chars to line, current file  */
X    tlci,			/* Chars from line in transaction */
X    tlco,   	    	    	/* Chars to line in transaction */
X    ffc,			/* Chars to/from current file */
X    tfc;			/* Chars to/from files in transaction */
X
X/* Flags */
X
Xint deblog = 0,				/* Flag for debug logging */
X    pktlog = 0,				/* Flag for packet logging */
X    seslog = 0,				/* Session logging */
X    tralog = 0,				/* Transaction logging */
X    displa = 0,				/* File transfer display on/off */
X    stdouf = 0,				/* Flag for output to stdout */
X    xflg   = 0,				/* Flag for X instead of F packet */
X    hcflg  = 0,				/* Doing Host command */
X    fncnv  = 1,				/* Flag for file name conversion */
X    binary = 0,				/* Flag for binary file */
X    warn   = 0,				/* Flag for file warning */
X    quiet  = 0,				/* Be quiet during file transfer */
X    local  = 0,				/* Flag for external tty vs stdout */
X    server = 0,				/* Flag for being a server */
X    cnflg  = 0,				/* Connect after transaction */
X    cxseen = 0,				/* Flag for cancelling a file */
X    czseen = 0; 	    	    	/* Flag for cancelling file group */
X
X/* Variables passed from command parser to protocol module */
X
Xchar sstate  = 0;			/* Starting state for automaton */
Xchar *cmarg  = "";			/* Pointer to command data */
Xchar *cmarg2 = "";			/* Pointer to second data field */
Xchar **cmlist;				/* Pointer to file list in argv */
X
X/* Miscellaneous */
X
Xchar **xargv;				/* Global copies of argv */
Xint  xargc;				/* and argc  */
X
Xextern char *dftty;			/* Default tty name from ckx???.c */
Xextern int dfloc;			/* Default location: remote/local */
Xextern int dfprty;			/* Default parity */
Xextern int dfflow;			/* Default flow control */
X
X
X/*  M A I N  --  C-Kermit main program  */
X
Xmain(argc,argv) int argc; char **argv; {
X
X    char *strcpy();
X
X/* Do some initialization */
X
X    xargc = argc;			/* Make global copies of argc */
X    xargv = argv;			/* ...and argv. */
X    sstate = 0;				/* No default start state. */
X    strcpy(ttname,dftty);		/* Set up default tty name. */
X    local = dfloc;			/* And whether it's local or remote. */
X    parity = dfprty;			/* Set initial parity, */
X    flow = dfflow;			/* and flow control. */
X    
X/* Look for a UNIX-style command line... */
X
X    if (argc > 1) {			/* Command line arguments? */
X	sstate = cmdlin();		/* Yes, parse. */
X	if (sstate) {
X	    proto();			/* Take any requested action, then */
X	    if (!quiet) conoll("");	/* put cursor back at left margin, */
X	    if (cnflg) conect();	/* connect if requested, */
X	    doexit(0);			/* and then exit with status 0. */
X    	}
X    }	
X    
X/* If no action requested on command line, enter interactive parser */
X
X    cmdini();				/* Initialize command parser */
X    while(sstate = parser()) {		/* Loop getting commands. */
X	if (sstate) proto();		/* Enter protocol if requested. */
X    }
X}
!FUNKY!STUFF!
echo x - cklogi.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > cklogi.c
Xchar *loginv = "Login Command for Unix, V1.0(003) 28 Feb 85";
X
X/*  C K L O G I  --  Login script for logging onto remote system */
X
X/*
X This module should work under all versions of Unix.  It calls externally
X defined system-depended functions for i/o.
X
X The module expects a login string of the expect send [expect send] ...
X format.  It is intended to operate similarly to the way the common
X uucp "L.sys" login entries work.  Conditional responses are supported
X expect[-send-expect[...]] as with uucp.  The send keyword EOT sends a
X control-d, and the keyword BREAK sends a break.  Letters prefixed
X by '~' are '~b' backspace, '~s' space, '~n' linefeed, '~r' return,
X '~t' tab, '~q' ? (not allowed on kermit command lines), '~' ~, '~'', 
X '~"', '~c' don't append return, '~o[o[o]]' octal character.  As with
X some uucp systems, sent strings are followed by ~r (not ~n) unless they
X end with ~c. Null expect strings (e.g., ~0 or --) cause a short
X delay, and are useful for sending sequences requiring slight pauses.
X
X Author: Herm Fischer, Litton Data Systems, Van Nuys CA (HFISCHER@USC-ECLB)
X*/
X
X#include "ckermi.h"
X#include <signal.h>
X#include <setjmp.h>
X
Xextern int local, speed, flow, seslog, mdmtyp;
Xextern char ttname[];
Xstatic char * chstr();
X
X#define EXP_ALRM	15		/* Time to wait for expect string */
X#define SND_ALRM	15		/* Time to allow for sending string */
X#define NULL_EXP	2		/* Time to pause on null expect strg*/ 
X
X#define SBUFL 300			/* Login Sequence buffer */
Xstatic char seq_buf[SBUFL], *s;
Xstatic int got_it, no_cr;
X
X/*  connect state parent/child communication signal handlers */
X
Xstatic jmp_buf alrmRng;		/* Envir ptr for connect errors */
X
Xstatic
XtimeInt() {				/* modem read failure handler, */
X    longjmp(alrmRng,1);		/* notifies parent process to stop */
X}
X
X/*
X Sequence interpreter -- pick up next sequence from command string,
X decode escapes and place into seq_buf
X*/
Xstatic 
Xsequenc()  {
X
X    int i;
X    char c, oct_char;
X
X    no_cr = 0;				/* output needs cr appended */
X
X    for (i=0; i<SBUFL; ) {		
X	if (*s == '\0' || *s == '-' || *s == ' ') { /* done */
X	    seq_buf[i] = '\0';
X	    return ;
X	}
X
X	if (*s == '~') {		/* escape character */
X	    switch (c = *(++s) ) {
X		case 'n':	seq_buf[i++] = '\n'; break;
X		case 'r':	seq_buf[i++] = '\r'; break;
X		case 't':	seq_buf[i++] = '\t'; break;
X		case 'b':	seq_buf[i++] = '\b'; break;
X		case 'q':	seq_buf[i++] = '?';  break;
X		case '~':	seq_buf[i++] = '~';  break;
X		case '\'':	seq_buf[i++] = '\''; break;
X		case '\"':	seq_buf[i++] = '\"'; break;
X		case 's':	seq_buf[i++] = ' ';  break;
X		case 'c':	no_cr = 1; break;
X		default:
X		    if ( isdigit(c) ) {	    	/* octal character */
X		    	oct_char = (c & 7);	/* most significant digit */
X			if (isdigit( *(s+1) ) ) {
X			    oct_char = (oct_char<<3) | ( (*(++s)) & 7 ) ;
X			    if (isdigit( *(s+1) ) ) {
X			    	oct_char = (oct_char<<3) | ( (*(++s)) & 7 ) ;
X			    }
X			}
X			seq_buf[i++] = oct_char;
X			break;
X		    }
X	    }
X	}
X	else seq_buf[i++] = *s;		/* plain old character */
X	s++;
X    }
X    seq_buf[i] = '\0';
X    return;			/* end of space, return anyway */
X}
X
X/*
X Receive sequence -- see if expected response comes return success
X (or failure) in got_it
X*/ 
Xstatic 
XrecvSeq()  {
X   
X    char *e, got[7], trace[300];
X    int i, l;
X    
X	sequenc();
X	l = strlen(e=seq_buf);		/* no more than 7 chars allowed */
X	if (l > 7) {
X	    e += l-7;
X	    l = 7;
X	}
X
X	tlog(F111,"expecting sequence",e,(long) l);
X	if (l == 0) {		/* null sequence, just delay a little */
X	    sleep (NULL_EXP);
X	    got_it = 1;
X	    tlog(F100,"got it (null sequence)","",0l);
X	    return;
X	}
X	*trace = '\0';
X	for (i=0; i<7; i++) got[i]='\0';
X
X	signal(SIGALRM,timeInt);	/* did we get it? */
X	if (!setjmp(alrmRng)) {	/* not timed out yet */
X	    alarm(EXP_ALRM);
X	    while (!got_it) {
X		for (i=0; i<(l-1); i++) got[i] = got[i+1]; /* shift over one */
X		got[l-1] = ttinc(0) & 0177;		/* next char */
X		if (strlen(trace) < sizeof(trace)-2 ) 
X			strcat(trace,chstr(got[l-1]));
X		got_it = (!strncmp(seq_buf, got, l) ) ;
X	    }
X	} else got_it = 0;		/* timed out here */
X
X	alarm(0);
X	signal(SIGALRM,SIG_IGN);
X	tlog(F110,"received sequence: ",trace,0l);
X	tlog(F101,"returning with got-it code","",(long) got_it);
X	return;
X}
X
X/*
X Output A Sequence starting at pointer s,
X return 0 if okay,
X 1 if failed to read (modem hangup or whatever)
X*/
Xstatic int
XoutSeq()  {
X    char *sb;
X    int l;
X
X    sequenc();
X    l = strlen(seq_buf);
X    tlog(F111,"sending sequence ",seq_buf,(long) l);
X    signal(SIGALRM,timeInt);
X    if (!setjmp(alrmRng)) {
X	alarm(SND_ALRM);
X	if (!strcmp(seq_buf,"EOT")) ttoc(dopar('\004'));
X	else if (!strcmp(seq_buf,"BREAK")) ttsndb();
X 	else {
X	    if (l > 0) {
X		for ( sb=seq_buf; *sb; sb++) *sb = dopar(*sb);
X		ttol(seq_buf,l);		/* with parity */
X	    }
X	    if (!no_cr) ttoc( dopar('\r') );
X	}
X	alarm(0);
X	signal(SIGALRM,SIG_IGN);
X	return(0);
X    }
X    alarm(0);    		/* else -- alarm rang */
X    signal(SIGALRM,SIG_IGN);
X    return( -1 );
X}
X
X/*  L O G I N  --  Login to remote system */
X
Xlogin(cmdstr) char *cmdstr; {
X
X	int (*saveAlm)();	/* save incomming alarm function */
X	char *e;
X
X	s = cmdstr;			/* make global to cklogi.c */
X
X	tlog(F100,loginv,"",0l);
X
X	if (!local) {
X	    printf("Sorry, you must 'set line' first\n");
X	    return(-2);
X	}
X	if (speed < 0) {
X	    printf("Sorry, you must 'set speed' first\n");
X	    return(-2);
X        }
X	if (ttopen(ttname,local,mdmtyp) < 0) {
X	    sprintf(seq_buf,"Sorry, can't open %s",ttname);
X	    perror(seq_buf);
X	    return(-2);
X    	}
X    	printf("Logging on thru %s, speed %d.\r\n",ttname,speed);
X	*seq_buf=0;
X	for (e=s; *e; e++) strcat(seq_buf, chstr(*e) );
X	printf("The logon string is: %s\r\n",seq_buf);
X	tlog(F110,"Logon command string: ",seq_buf, 0l);
X
X/* Condition console terminal and communication line */	    
X
X	if (ttvt(speed,flow) < 0) {
X	    printf("Sorry, Can't condition communication line\n");
X	    return(-2);
X    	}
X				/* save initial timer interrupt value */
X	saveAlm = signal(SIGALRM,SIG_IGN);
X
X	ttflui();		/* flush stale input */
X
X/* cont'd... */
X
X/* ...login, cont'd */
X
X/* start expect - send sequence */
X
X    while (*s) {		/* while not done with buffer */
X
X	while (*s && *s == ' ') s++;	/* skip over separating blanks */
X				/* gather up expect sequence */
X	got_it = 0;
X	recvSeq();
X
X	while (!got_it) {
X				/* no, is there a conditional send */
X	    if (*s++ != '-') goto failRet;    	/* no -- return failure */
X		
X	    		/* start of conditional send */
X	    ttflui();				/* flush out input buffer */
X	    if (outSeq()) goto failRet; 	/* if unable to send! */
X
X	    if (*s++ != '-') goto failRet; 	/* must have condit respon.*/
X	    recvSeq();
X	}	/* loop back and check got_it */
X
X	while (*s && *s++ != ' ') ; 	/* skip over conditionals and spaces */
X	ttflui();			/* Flush */
X	if (*s) if (outSeq()) goto failRet; 	/* if any */
X    }
X    signal(SIGALRM,saveAlm);
X    printf("Logged on!\r\n");
X    tlog(F100,"Logged on!","",0l);
X    return(0);
X
XfailRet:
X    signal(SIGALRM,saveAlm);
X    printf("Sorry, logon failed\r\n");
X    tlog(F100,"Logon failed","",0l);
X    return(-2);
X}
X
X
X/*  C H S T R  --  Make printable string from a character */
X
Xstatic char *
Xchstr(c) char c; {
X    static char sc[4];
X
X    if (c < SP) sprintf(sc, "^%c",ctl(c) );
X    else sprintf(sc, "%c", c);
X  
X    return(sc);
X}
!FUNKY!STUFF!

gregg@okstate.UUCP (03/08/85)

echo x - ckxunx.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckxunx.c
Xchar *ckxv = "Unix tty I/O, 4.2(016), 5 Mar 85";
X
X/* C-Kermit interrupt, terminal control & i/o functions for Unix systems */
X
X/* F. da Cruz, Columbia University Center for Computing Activities */
X
X#ifdef BSD4
Xchar *ckxsys = " 4.2 BSD";
X#endif
X
X#ifdef PROVX1
Xchar *ckxsys = " Pro-3xx Venix 1.0";
X#endif
X
X/* Tower support contributed by John Bray, Auburn University */
X#ifdef TOWER1
Xchar *ckxsys = " NCR Tower 1632, OS 1.02";
X#endif
X
X/* Sys III/V, Xenix, PC/IX support by Herm Fischer, Litton Data Systems */
X#ifdef UXIII
X#ifdef XENIX
Xchar *ckxsys = " Xenix/286";
X#else
X#ifdef PCIX
Xchar *ckxsys = " PC/IX";
X#else
X#ifdef ISIII
Xchar *ckxsys = " Interactive Systems Corp System III";
X#else
Xchar *ckxsys = " AT&T System III/System V";
X#endif
X#endif
X#endif
X#endif
X
X/*
X Note - KERLD is the Berkeley Unix Berknet line driver, modified to pass
X through all 8  bits, and to allow an arbitrary break character to be set.
X Don't define this symbol unless you have made this modification to your
X 4.2BSD kernel!
X*/
X#ifdef BSD4
X/* #define KERLD */  /* <-- note, commented out */
X#endif
X
X/*
X Variables available to outside world:
X
X   dftty  -- Pointer to default tty name string, like "/dev/tty".
X   dfloc  -- 0 if dftty is console, 1 if external line.
X   dfprty -- Default parity
X   dfflow -- Default flow control
X   ckxech -- Flag for who echoes console typein:
X     1 - The program (system echo is turned off)
X     0 - The system (or front end, or terminal).
X   functions that want to do their own echoing should check this flag
X   before doing so.
X
X   flfnam -- Name of lock file, including its path, e.g.,
X		"/usr/spool/uucp/LCK..cul0" or "/etc/locks/tty77"
X   hasLock -- Flag set if this kermit established a uucp lock.
X   inbufc -- number of tty line rawmode unread characters 
X		(system III/V unixes)
X   backgrd -- Flag indicating program executing in background ( & on 
X		end of shell command). Used to ignore INT and QUIT signals.
X
X Functions for assigned communication line (either external or console tty):
X
X   ttopen(ttname,local,mdmtyp) -- Open the named tty for exclusive access.
X   ttclos()                -- Close & reset the tty, releasing any access lock.
X   ttpkt(speed,flow)       -- Put the tty in packet mode and set the speed.
X   ttvt(speed,flow)        -- Put the tty in virtual terminal mode.
X				or in DIALING or CONNECTED modem control state.
X   ttinl(dest,max,timo)    -- Timed read line from the tty.
X   ttinc(timo)             -- Timed read character from tty.
X   myread()		   -- System 3 raw mode bulk buffer read, gives
X			   --   subsequent chars one at a time and simulates
X			   --   FIONREAD!
X   myunrd(c)		   -- Places c back in buffer to be read (one only)
X   ttchk()                 -- See how many characters in tty input buffer.
X   ttxin(n,buf)            -- Read n characters from tty (untimed).
X   ttol(string,length)     -- Write a string to the tty.
X   ttoc(c)                 -- Write a character to the tty.
X   ttflui()                -- Flush tty input buffer.
X
X   ttlock(ttname)	   -- Lock against uucp collisions (Sys III)
X   ttunlck()		   -- Unlock "       "     "
X   look4lk(ttname)	   -- Check if a lock file exists
X*/
X
X/*
XFunctions for console terminal:
X
X   congm()   -- Get console terminal modes.
X   concb(esc) -- Put the console in single-character wakeup mode with no echo.
X   conbin(esc) -- Put the console in binary (raw) mode.
X   conres()  -- Restore the console to mode obtained by congm().
X   conoc(c)  -- Unbuffered output, one character to console.
X   conol(s)  -- Unbuffered output, null-terminated string to the console.
X   conola(s) -- Unbuffered output, array of strings to the console.
X   conxo(n,s) -- Unbuffered output, n characters to the console.
X   conchk()  -- Check if characters available at console (bsd 4.2).
X		Check if escape char (^\) typed at console (System III/V).
X   coninc(timo)  -- Timed get a character from the console.
X   conint()  -- Enable terminal interrupts on the console if not background.
X   connoi()  -- Disable terminal interrupts on the console if not background.
X
XTime functions
X
X   msleep(m) -- Millisecond sleep
X   ztime(&s) -- Return pointer to date/time string
X*/
X
X/* Includes */
X
X#include <stdio.h>			/* Unix Standard i/o */
X#include <signal.h>			/* Interrupts */
X#include <setjmp.h>			/* Longjumps */
X
X#ifdef UXIII
X#include <sys/types.h>
X#endif
X
X#ifndef PROVX1
X#include <sys/file.h>			/* File information */
X#endif
X
X#ifndef DIRSIZ
X#ifdef MAXNAMLEN
X#define DIRSIZ MAXNAMLEN
X#else
X#define DIRSIZ 14
X#endif
X#endif
X
X#ifdef UXIII
X#include <termio.h>
X#include <sys/ioctl.h>
X#include <fcntl.h>			/* directory reading for locking */
X#include <sys/dir.h>
X#include <errno.h>			/* error numbers for system returns */
Xextern int errno;			/* system call error return */
X#endif
X
X#ifndef UXIII
X#include <sgtty.h>			/* Set/Get tty modes */
X#ifndef PROVX1
X#include <sys/time.h>			/* Clock info (for break generation) */
X#endif
X#endif
X
X#ifdef TOWER1
X#include <sys/timeb.h>			/* Clock info for NCR Tower */
X#endif
X
X#include "ckdebu.h"			/* Formats for debug() */
X
X/* Declarations */
X
X/* dftty is the device name of the default device for file transfer */
X/* dfloc is 0 if dftty is the user's console terminal, 1 if an external line */
X
X#ifdef PROVX1
X    char *dftty = "/dev/com1";
X    int dfloc = 1;
X#else
X    char *dftty = "/dev/tty";
X    int dfloc = 0;
X#endif
X
X    int dfprty = 0;			/* Parity (0 = none) */
X    int dfflow = 1;			/* Xon/Xoff flow control */
X    int backgrd = 0;			/* Assume in foreground (no '&' ) */
X
Xint ckxech = 0; /* 0 if system normally echoes console characters, else 1 */
X
X/* Declarations of variables global within this module */
X
Xstatic jmp_buf sjbuf;			/* Longjump buffer */
Xstatic int lkf = 0,			/* Line lock flag */
X    conif = 0,				/* Console interrupts on/off flag */
X    cgmf = 0,				/* Flag that console modes saved */
X    xlocal = 0,				/* Flag for tty local or remote */
X    ttyfd = -1;				/* TTY file descriptor */
Xstatic char escchr;			/* Escape or attn character */
X
X#ifdef KERLD
X    static int kerld = 1;		/* Special Kermit line discipline... */
X    struct tchars oldc, newc;		/* Special characters */
X    int ld = NETLDISC;			/* Special Kermit line discipline */
X    int oldld;				/* Old discipline */
X#else
X    static int kerld = 0;		/* for 4.2BSD only, */
X#endif
X
X#ifdef BSD4
X    static struct timeval tv;		/* For getting time, from sys/time.h */
X    static struct timezone tz;
X#endif
X
X#ifdef TOWER1
Xstatic long clock;			/* For getting time from sys/time.h */
Xstatic struct timeb ftp;		/* And from sys/timeb.h */
X#endif
X
X#ifdef UXIII
X  static struct termio 			/* sgtty info... */
X    ttold, ttraw, tttvt,		/* for communication line */
X    ccold, ccraw, cccbrk;		/* and for console */
X#else
X  static struct sgttyb 			/* sgtty info... */
X    ttold, ttraw, tttvt,		/* for communication line */
X    ccold, ccraw, cccbrk;		/* and for console */
X#endif
X
Xstatic char flfnam[80];			/* uucp lock file path name */
Xstatic int hasLock = 0;			/* =1 if this kermit locked uucp */
Xstatic int inbufc = 0;			/* stuff for efficient SIII raw line */
Xstatic int ungotn = -1;			/* pushback to unread character */
Xstatic int conesc = 0;			/* set to 1 if esc char (^\) typed */
X
Xstatic int ttlock();			/* definition of ttlock subprocedure */
Xstatic int ttunlck();			/*   unlock subprocedure */
X
X/*  T T O P E N  --  Open a tty for exclusive access.  */
X
X/*  Returns 0 on success, -1 on failure.  */
X
Xttopen(ttname,lcl,modem) char *ttname; int lcl, modem; {
X
X    if (ttyfd > -1) return(0);		/* If already open, ignore this call */
X    xlocal = lcl;			/* Make available to other functions */
X#ifndef UXIII
X    ttyfd = open(ttname,2);		/* Open a tty for read/write */
X#else
X    /* if modem connection, don't wait for carrier */
X    ttyfd = open(ttname,O_RDWR | (modem ? O_NDELAY : 0) );
X#endif
X    if (ttyfd < 0) return(-1);
X    lkf = 0;
X
X#ifndef PROVX1
X    if (xlocal) {
X#ifdef BSD4
X    	if ((flock(ttyfd,(LOCK_EX|LOCK_NB)) < 0) || (ttlock(ttname) < 0 )) {
X#else
X#ifdef TOWER1
X    	if (ioctl(ttyfd,TIOCEXCL, NULL) < 0) {
X#else
X    	if (ttlock(ttname) < 0) {
X#endif
X#endif
X    	fprintf(stderr,"Sorry - Exclusive access to %s was denied\n",ttname);
X	return(-1);			/* Can't open if already locked */
X    	} else lkf = 1;
X    }
X#endif
X
X#ifndef UXIII
X    gtty(ttyfd,&ttold);			/* Get sgtty info */
X    gtty(ttyfd,&ttraw);			/* And a copy of it for packets*/
X    gtty(ttyfd,&tttvt);			/* And one for virtual tty service */
X#else
X    ioctl(ttyfd,TCGETA,&ttold);		/* Same deal for Sys III, Sys V */
X    ioctl(ttyfd,TCGETA,&ttraw);
X    ioctl(ttyfd,TCGETA,&tttvt);
X#endif
X    debug(F101,"ttopen, ttyfd","",ttyfd);
X    debug(F111," lock file",flfnam,lkf);
X    return(0);
X}
X
X/*  T T C L O S  --  Close the TTY, releasing any lock.  */
X
Xttclos() {
X    if (ttyfd < 0) return(0);		/* Wasn't open. */
X#ifndef PROVX1
X#ifdef BSD4
X    if (lkf) flock(ttyfd,LOCK_UN);	/* Unlock it first. */
X#endif
X    if (xlocal) ttunlck();
X#endif
X    ttres();				/* Reset modes. */
X    close(ttyfd);			/* Close it. */
X    ttyfd = -1;				/* Mark it as closed. */
X    return(0);
X}
X
X
X/*  T T R E S  --  Restore terminal to "normal" mode.  */
X
Xttres() {				/* Restore the tty to normal. */
X    if (ttyfd < 0) return(-1);		/* Not open. */
X    sleep(1);				/* Wait for pending i/o to finish. */
X#ifdef KERLD
X    if (kerld) ioctl(ttyfd,TIOCSETD,&oldld); /* Restore old line discipline. */
X#endif
X#ifdef UXIII
X    if (ioctl(ttyfd,TCSETA,&ttold) < 0) return(-1); /* restore termio stuff */
X#else
X    debug(F101,"ttres, ttyfd","",ttyfd);
X    if (stty(ttyfd,&ttold) < 0) return(-1); /* Restore sgtty stuff */
X#endif
X#ifdef KERLD
X    if (kerld) ioctl(ttyfd,TIOCSETC,&oldc); /* Restore old special chars. */
X#endif
X
X    return(0);
X}
X
X#ifndef PROVX1
X
X/* Exclusive uucp file locking control */
X/*
X by H. Fischer, creative non-Bell coding !
X*/
Xstatic char *
Xxxlast(s,c) char *s; char c; {		/* Equivalent to strrchr() */
X    int i;
X    for (i = strlen(s); i > 0; i--)
X    	if ( s[i-1] == c ) return( s + (i - 1) );
X    return(NULL);	    
X}
Xstatic
Xlook4lk(ttname) char *ttname; {
X    extern char *strcat(), *strcpy();
X    char *device, *devname;
X    char lockfil[DIRSIZ+1];
X
X#ifdef ISIII
X    char *lockdir = "/etc/locks";
X#else
X    char *lockdir = "/usr/spool/uucp";
X#endif
X
X    device = ( (devname=xxlast(ttname,'/')) != NULL ? devname+1 : ttname);
X
X#ifdef ISIII
X    (void) strcpy( lockfil, device );
X#else
X    (void) strcat( strcpy( lockfil, "LCK.." ), device );
X#endif
X
X    if (access( lockdir, 04 ) < 0) {	/* read access denied on lock dir */
X	fprintf(stderr,"Warning, read access to lock directory denied\n");
X	return( 1 );			/* cannot check or set lock file */
X	}
X	
X    (void) strcat(strcat(strcpy(flfnam,lockdir),"/"), lockfil);
X    debug(F110,"look4lk",flfnam,0);
X
X    if ( ! access( flfnam, 00 ) ) {	/* print out lock file entry */
X	char lckcmd[40] ;
X	(void) strcat( strcpy(lckcmd, "ls -l ") , flfnam);
X	system(lckcmd);
X	return( -1 );
X	}
X    if ( access( lockdir, 02 ) < 0 ) {	/* lock file cannot be written */
X	fprintf(stderr,"Warning, write access to lock directory denied\n");
X	return( 1 );
X	}
X    return( 0 );			/* okay to go ahead and lock */
X}
X
X/*  T T L O C K  */
X
Xstatic
Xttlock(ttyfd) char *ttyfd; {		/* lock uucp if possible */
X    int lck_fil, l4l;
X	
X    hasLock = 0;			/* not locked yet */
X    if ((l4l=look4lk(ttyfd)) < 0) return(-1); /* already locked */
X    if (l4l == 1) return (0);		/* can't read/write lock directory */
X    if ((lck_fil=open (flfnam, O_CREAT | O_EXCL)) < 1 ) 
X    	return(-1);			/* failed to create */
X    close (lck_fil);
X    hasLock = 1;			/* now is locked */
X    return(0);
X}
X
X/*  T T U N L O C K  */
X
Xstatic
Xttunlck() {				/* kill uucp lock if possible */
X    if (hasLock) unlink( flfnam );
X}
X#endif
X
X
X
X/*  T T P K T  --  Condition the communication line for packets. */
X/*		or for modem dialing */
X
X#define DIALING	4		/* flags (via flow) for modem handling */
X#define CONNECT 5
X
X/*  If called with speed > -1, also set the speed.  */
X
X/*  Returns 0 on success, -1 on failure.  */
X
Xttpkt(speed,flow) int speed, flow; {
X    extern char ttname[];
X    int s;
X    if (ttyfd < 0) return(-1);		/* Not open. */
X
X#ifdef KERLD
X    if (kerld) {
X	ioctl(ttyfd,TIOCGETD,&oldld);	/* Get line discipline */
X	ioctl(ttyfd,TIOCGETC,&oldc);	/* Get special chars */
X	newc = oldc;			/* Copy special chars */
X	newc.t_brkc = '\r';		/* Set CR to be break character */
X	if(ioctl(ttyfd,TIOCSETC,&newc) < 0) return(-1);
X    }
X#endif
X
X/* cont'd... */
X
X/* ...ttpkt(), cont'd */
X
X
X/* Note, KERLD ignores the TANDEM, ECHO, and CRMOD bits */
X
X    s = ttsspd(speed);			/* Check the speed */
X
X#ifndef UXIII
X    if (flow == 1) ttraw.sg_flags |= TANDEM; /* Use XON/XOFF if selected */
X    if (flow == 0) ttraw.sg_flags &= ~TANDEM;
X    ttraw.sg_flags |= RAW;		/* Go into raw mode */
X    ttraw.sg_flags &= ~(ECHO|CRMOD);	/* Use CR for break character */
X#ifdef TOWER1
X    ttraw.sg_flags &= ~ANYP; 		/* Must tell Tower no parityr */
X#endif
X    if (s > -1) ttraw.sg_ispeed = ttraw.sg_ospeed = s; /* Do the speed */
X    if (stty(ttyfd,&ttraw) < 0) return(-1);	/* Set the new modes. */
X#endif
X
X#ifdef UXIII
X    if (flow == 1) ttraw.c_iflag |= (IXON|IXOFF);
X    if (flow == 0) ttraw.c_iflag &= ~(IXON|IXOFF);
X
X    if (flow == DIALING)  ttraw.c_cflag |= CLOCAL|HUPCL;
X    if (flow == CONNECT)  ttraw.c_cflag &= ~CLOCAL;
X
X    ttraw.c_lflag &= ~(ICANON|ECHO);
X    ttraw.c_lflag |= ISIG;		/* do check for interrupt */
X    ttraw.c_iflag |= (BRKINT|IGNPAR);
X    ttraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|IUCLC);
X    ttraw.c_oflag &= ~(ONLCR|OCRNL|ONLRET);
X    ttraw.c_cc[4] = 1;
X    ttraw.c_cc[5] = 0;
X
X    if (s > -1) ttraw.c_cflag &= ~CBAUD, ttraw.c_cflag |= s; /* set speed */
X
X    if (ioctl(ttyfd,TCSETA,&ttraw) < 0) return(-1);  /* set new modes . */
X    if (flow == DIALING) {
X	if (fcntl(ttyfd,F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NDELAY) < 0 )
X		return(-1);
X	close( open(ttname,2) );	/* magic to force file open!!! */
X	}
X#endif
X
X#ifdef KERLD
X    if (kerld) {
X	if (ioctl(ttyfd,TIOCSETD,&ld) < 0)
X	    return(-1); /* Set line discpline. */
X    }
X#endif
X
X    ttflui();				/* Flush any pending input */
X    return(0);
X}
X
X/*  T T V T -- Condition communication line for use as virtual terminal  */
X
Xttvt(speed,flow) int speed, flow; {
X    extern char ttname[];
X    int s;
X    if (ttyfd < 0) return(-1);		/* Not open. */
X
X    s = ttsspd(speed);			/* Check the speed */
X
X#ifndef UXIII
X    if (flow == 1) tttvt.sg_flags |= TANDEM; /* XON/XOFF if selected */
X    if (flow == 0) tttvt.sg_flags &= ~TANDEM;
X    tttvt.sg_flags |= RAW;		/* Raw mode */
X#ifdef TOWER1
X    tttvt.sg_flags &= ~(ECHO|ANYP);	/* No echo or system III ??? parity */
X#else
X    tttvt.sg_flags &= ~ECHO;		/* No echo */
X#endif    
X    if (s > -1) tttvt.sg_ispeed = tttvt.sg_ospeed = s; /* Do the speed */
X
X/* NOTE-- bsd code needs clocal and o_Ndelay stuff here */
X    return(stty(ttyfd,&tttvt));
X#else
X    if (flow == 1) tttvt.c_iflag |= (IXON|IXOFF);
X    if (flow == 0) tttvt.c_iflag &= ~(IXON|IXOFF);
X
X    if (flow == DIALING)  tttvt.c_cflag |= CLOCAL|HUPCL;
X    if (flow == CONNECT)  tttvt.c_cflag &= ~CLOCAL;
X
X    tttvt.c_lflag &= ~(ISIG|ICANON|ECHO);
X    tttvt.c_iflag |= (IGNBRK|BRKINT|IGNPAR);
X    tttvt.c_iflag &= ~(INLCR|IGNCR|ICRNL|IUCLC);
X    tttvt.c_oflag &= ~(ONLCR|OCRNL|ONLRET);
X    tttvt.c_cc[4] = 1;
X    tttvt.c_cc[5] = 0;
X
X    if (s > -1) tttvt.c_cflag &= ~CBAUD, tttvt.c_cflag |= s; /* set speed */
X
X    if (ioctl(ttyfd,TCSETA,&tttvt) < 0) return(-1);  /* set new modes . */
X    if (flow == DIALING) {
X	if (fcntl(ttyfd,F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NDELAY) < 0 )
X		return(-1);
X	close( open(ttname,2) );	/* magic to force file open!!! */
X	}
X    return(0);
X#endif
X}
X
X/*  T T S S P D  --  Return the internal baud rate code for 'speed'.  */
X
Xttsspd(speed) {
X    int s, spdok;
X
X    if (speed < 0) return(-1);
X	spdok = 1;			/* Assume arg ok */
X	switch (speed) {
X	    case 0:    s = B0;    break;	/* Just the common ones. */
X	    case 110:  s = B110;  break;	/* The others from ttydev.h */
X	    case 150:  s = B150;  break;	/* could also be included if */
X	    case 300:  s = B300;  break;	/* necessary... */
X	    case 600:  s = B600;  break;
X	    case 1200: s = B1200; break;
X	    case 1800: s = B1800; break;
X	    case 2400: s = B2400; break;
X	    case 4800: s = B4800; break;
X	    case 9600: s = B9600; break;
X	    default:
X	    	spdok = 0;
X		fprintf(stderr,"Unsupported line speed - %d\n",speed);
X		fprintf(stderr,"Current speed not changed\n");
X		break;
X	}	    
X	if (spdok) return(s); else return(-1);
X }
X
X
X
X/*  T T F L U I  --  Flush tty input buffer */
X
Xttflui() {
X
X#ifndef PROVX1
X    long n;
X#endif
X    if (ttyfd < 0) return(-1);		/* Not open. */
X
X#ifdef BSD4
X#ifdef TOWER1
X    n = FREAD;				/* Specify read queue */
X    if (ioctl(ttyfd,TIOCFLUSH,&n) < 0) perror("flush failed");
X#endif
X#endif
X
X#ifdef UXIII
X    inbufc = 0;
X    ungotn = -1;
X    if (ioctl(ttyfd,TCFLSH,0) < 0) perror("flush failed");
X#endif
X
X    return(0);
X}
X
X/* Interrupt Functions */
X
X
X/* Timeout handler for communication line input functions */
X
Xtimerh() {
X    longjmp(sjbuf,1);
X}
X
X 
X/* Set up terminal interrupts on console terminal */
X
X#ifdef UXIII
Xesctrp() {				/* trap console escapes (^\) */
X    conesc = 1;
X    signal(SIGQUIT,SIG_IGN);		/* ignore until trapped */
X}
X#endif
X
X
X/*  C O N I N T  --  Console Interrupt setter  */
X
Xconint(f) int (*f)(); {			/* Set an interrupt trap. */
X
X    if (backgrd) return;		/* must ignore signals in bkgrd */
X
X#ifdef UXIII
X    signal(SIGQUIT,esctrp);	/* console escape in pkt modes */
X    if (conesc) {			/* clear out pending escapes */
X	conesc = 0;
X    }
X#endif
X
X    if (conif) return;			/* Nothing to do if already on. */
X
X/* check if invoked in background -- if so signals set to be ignored */
X
X    if (signal(SIGINT,SIG_IGN) == SIG_IGN) {
X	backgrd = 1;			/*   means running in background */
X#ifdef UXIII
X	signal(SIGQUIT,SIG_IGN);	/*   must leave signals ignored */
X#endif
X	return;
X    }
X    signal(SIGINT,f);			/* Function to trap to. */
X    conif = 1;				/* Flag console interrupts on. */
X}
X
X
X/*  C O N N O I  --  Reset console terminal interrupts */
X
Xconnoi() {				/* Console-no-interrupts */
X
X    if (backgrd) return;		/* must ignore signals in bkgrd */
X
X    signal(SIGINT,SIG_DFL);
X    conif = 0;
X}
X
X/*  myread() -- System III raw read buffer to block input up */
X
Xmyread() {			/* return character or -1 if disconnected */
X
X    static int inbuf_item;
X    static CHAR inbuf[257];
X    CHAR readit;
X    
X    if (ungotn >= 0) readit = ungotn;
X    else {
X        if (inbufc > 0)
X	    readit = inbuf[++inbuf_item];
X    	        else {
X	    if ((inbufc=read(ttyfd,inbuf,256)) == 0 ) return(-1);
X	    readit = inbuf[inbuf_item=0];
X	    }
X        inbufc--;	
X        }
X    ungotn = -1;
X    return(readit );
X    }
X
Xmyunrd(ch) CHAR ch; {			/* push back up to one character */
X    ungotn = ch;
X}
X
X
X/*  T T C H K  --  Tell how many characters are waiting in tty input buffer  */
X
Xttchk() {
X#ifndef PROVX1
X    int n, x;
X#ifdef BSD4
X    x = ioctl(ttyfd, FIONREAD, &n);
X    return((x < 0) ? 0 : n);
X#else
X    return(inbufc + (ungotn >= 0) );	
X#endif
X#else
X    return(0);
X#endif
X}
X
X
X/*  T T X I N  --  Get n characters from tty input buffer  */
X
Xttxin(n,buf) int n; char *buf; {
X    int x;
X#ifndef UXIII
X    x = read(ttyfd,buf,n);
X#else
X    for( x=0; x<n; buf[x++]=myread() );
X#endif
X    if (x > 0) buf[x] = '\0';
X    return(x);
X}
X
X/*  T T O L  --  Similar to "ttinl", but for writing.  */
X
Xttol(s,n) int n; char *s; {
X    int x;
X    if (ttyfd < 0) return(-1);		/* Not open. */
X    x = write(ttyfd,s,n);
X    debug(F111,"ttol",s,n);
X    if (x < 0) debug(F101,"ttol failed","",x);
X    return(x);
X}
X
X
X/*  T T O C  --  Output a character to the communication line  */
X
Xttoc(c) char c; {
X    if (ttyfd < 0) return(-1);		/* Not open. */
X    return(write(ttyfd,&c,1));
X}
X
X/*  T T I N L  --  Read a record (up to break character) from comm line.  */
X/*
X  If no break character encountered within "max", return "max" characters,
X  with disposition of any remaining characters undefined.  Otherwise, return
X  the characters that were read, including the break character, in "dest" and
X  the number of characters read as the value of function, or 0 upon end of
X  file, or -1 if an error occurred.  Times out & returns error if not completed
X  within "timo" seconds.
X*/
X
Xttinl(dest,max,timo,eol) int max,timo; char *dest; {
X    int x, y, c;
X    if (ttyfd < 0) return(-1);		/* Not open. */
X    if (timo <= 0) {			/* Untimed. */
X#ifndef UXIII
X	x = read(ttyfd,dest,max);	/* Try to read. */
X#else
X	for (x = c = 0; (x < max) && (c != eol); x++) {
X	     c = myread(); 
X	     dest[x] = c;
X	}
X#endif
X	return(x);			/* Return the count. */
X    }
X    signal(SIGALRM,timerh);		/* Timed, set up timeout action. */
X    alarm(timo);			/* Set the timer. */
X    if (setjmp(sjbuf)) x = -1;		/* Do this if timer went off. */
X    else if (kerld) {
X	x = read(ttyfd,dest,max);	/* Try to read. */
X    } else {
X	for (x = c = y = 0; (x < max) && (c != eol); x++) {
X#ifndef UXIII
X	     while ( !(y = read(ttyfd,&c,1))) ; /* skip null reads */
X	     if (y < 0) return(y);
X#else
X	     c = myread(); 
X#endif
X	     dest[x] = c;
X	}
X	x++;
X    }
X    alarm(0);				/* Success, turn off timer, */
X    signal(SIGALRM,SIG_DFL);		/* and associated interrupt. */
X    return(x);				/* Return the count. */
X}
X
X/*  T T I N C --  Read a character from the communication line  */
X
Xttinc(timo) int timo; {
X    int n;
X    CHAR ch;
X
X    if (ttyfd < 0) return(-1);		/* Not open. */
X    if (timo <= 0) {			/* Untimed. */
X#ifndef UXIII
X	while ( !(n = read(ttyfd,&ch,1)) ) ; /* Wait for a character. */
X	return( (n > 0) ? (ch & 0377) : n );
X#else
X	/* comm line failure returns -1 thru myread, so don't &= 0377 */
X	return( myread() );
X#endif
X    }
X
X    signal(SIGALRM,timerh);		/* Timed, set up timer. */
X    alarm(timo);
X    if (setjmp(sjbuf)) n = -1;
X    else {
X#ifndef UXIII
X    n = read(ttyfd,&ch,1);		/* Read a character. */
X#else
X    ch = myread();
X    n = 1;
X#endif
X    }
X    alarm(0);				/* Turn off timer, */
X    signal(SIGALRM,SIG_DFL);		/* and interrupt. */
X    if (n > 0) return(ch & 0377); else return(n);  /* Return char or -1. */
X}
X
X/*  T T S N D B  --  Send a BREAK signal  */
X
Xttsndb() {
X    int x;
X
X    if (ttyfd < 0) return(-1);		/* Not open. */
X
X#ifdef PROVX1
X/*** insert code to set speed to 50 baud and send 2-3 nulls ***/
X    return(0);
X#else
X#ifdef UXIII
X    if (ioctl(ttyfd,TCSBRK,(char *)0) < 0) {	/* Turn on BREAK */
X    	conol("Can't send BREAK");
X	return(-1);
X    }
X    return(0);
X#else
X#ifdef BSD4
X    if (ioctl(ttyfd,TIOCSBRK,(char *)0) < 0) {	/* Turn on BREAK */
X    	conol("Can't send BREAK");
X	return(-1);
X    }
X    x = msleep(275);			/* Sleep for so many milliseconds */
X    if (ioctl(ttyfd,TIOCCBRK,(char *)0) < 0) {	/* Turn off BREAK */
X	conol("BREAK stuck!!!");
X	doexit(1);			/* Get out, closing the line. */
X					/*   with exit status = 1 */
X    }
X    return(x);
X#endif
X#endif
X#endif
X}
X
X/*  M S L E E P  --  Millisecond version of sleep().  */
X
X/*
X Intended only for small intervals.  For big ones, just use sleep().
X*/
X
Xmsleep(m) int m; {
X
X#ifdef PROVX1
X    sleep(1+m/1000.0);
X    return(0);
X#endif
X
X#ifdef BSD4
X    int t1, t3, t4;
X    if (gettimeofday(&tv, &tz) < 0) return(-1); /* Get current time. */
X    t1 = tv.tv_sec;			/* Seconds */
X
Xif (0) {				/* Old way (works) */
X    while (1) {
X	gettimeofday(&tv, &tz);
X	t3 = tv.tv_sec - t1;
X	t4 = (tv.tv_usec + 1000000 * t3) / 1000;
X	if (t4 > m) return(t4);
X    }
X} else {				/* New way (also works) */
X    tv.tv_sec = 0;
X    tv.tv_usec = m * 1000;
X    return(select( 0, (int *)0, (int *)0, (int *)0, &tv) );
X}
X#endif
X
X#ifdef TOWER1
X    if (ftime(&ftp) < 0) return(-1);		/* Get current time. */
X    t1 = ((ftp.time & 0xff) * 1000) + ftp.millitm;
X    while (1) {
X	ftime(&ftp);				/* new time */
X	t3 = (((ftp.time & 0xff) * 1000) + ftp.millitm) - t1;
X	if (t3 > m) return (t3);
X    }
X#endif
X}
X
X/*  Z T I M E  --  Return date/time string  */
X
Xztime(s) char **s; {
X
X#ifdef UXIII
X    extern long time();			/* Sys III/V way to do it */
X    char *ctime();
X    long clock_storage;
X
X    clock_storage = time( (long *) 0 );
X    *s = ctime( &clock_storage );
X#endif
X
X#ifdef PROVX1
X    int utime[2];			/* Venix way */
X    time(utime);
X    *s = ctime(utime);
X#endif
X
X#ifdef BSD4
X    char *asctime();			/* Berkeley way */
X    struct tm *localtime();
X    struct tm *tp;
X
X    gettimeofday(&tv, &tz);
X    time(&tv.tv_sec);
X    tp = localtime(&tv.tv_sec);
X    *s = asctime(tp);
X#endif
X
X#ifdef TOWER1
X    char *asctime();			/* Tower way */
X    struct tm *localtime();
X    struct tm *tp;
X
X    time(&clock);
X    tp = localtime(&clock);
X    *s = asctime(tp);
X#endif
X}
X
X/*  C O N G M  --  Get console terminal modes.  */
X
X/*
X Saves current console mode, and establishes variables for switching between 
X current (presumably normal) mode and other modes.
X*/
X
Xcongm() {
X#ifndef UXIII
X     gtty(0,&ccold);			/* Structure for restoring */
X     gtty(0,&cccbrk);			/* For setting CBREAK mode */
X     gtty(0,&ccraw);			/* For setting RAW mode */
X#else
X     ioctl(0,TCGETA,&ccold);
X     ioctl(0,TCGETA,&cccbrk);
X     ioctl(0,TCGETA,&ccraw);
X#endif
X     cgmf = 1;				/* Flag that we got them. */
X}
X
X
X/*  C O N C B --  Put console in cbreak mode.  */
X
X/*  Returns 0 if ok, -1 if not  */
X
Xconcb(esc) char esc; {
X    int x;
X    if (cgmf == 0) congm();		/* Get modes if necessary. */
X    escchr = esc;			/* Make this available to other fns */
X    ckxech = 1;				/* Program can echo characters */
X#ifndef UXIII
X    cccbrk.sg_flags |= CBREAK;		/* Set to character wakeup, */
X    cccbrk.sg_flags &= ~ECHO;		/* no echo. */
X    x = stty(0,&cccbrk);
X#else
X    cccbrk.c_lflag &= ~(ICANON|ECHO);
X    cccbrk.c_cc[0] = 003;		/* interrupt char is control-c */
X    cccbrk.c_cc[1] = escchr;		/* escape during packet modes */
X    cccbrk.c_cc[4] = 1;
X    cccbrk.c_cc[5] = 1;
X    x = ioctl(0,TCSETA,&cccbrk);  	/* set new modes . */
X#endif
X    if (x > -1) setbuf(stdout,NULL);	/* Make console unbuffered. */
X    return(x);
X}
X
X/*  C O N B I N  --  Put console in binary mode  */
X
X
X/*  Returns 0 if ok, -1 if not  */
X
Xconbin(esc) char esc; {
X    if (cgmf == 0) congm();		/* Get modes if necessary. */
X    escchr = esc;			/* Make this available to other fns */
X    ckxech = 1;				/* Program can echo characters */
X#ifndef UXIII
X    ccraw.sg_flags |= (RAW|TANDEM);   	/* Set rawmode, XON/XOFF */
X    ccraw.sg_flags &= ~(ECHO|CRMOD);  	/* Set char wakeup, no echo */
X    return(stty(0,&ccraw));
X#else
X    ccraw.c_lflag &= ~(ISIG|ICANON|ECHO);
X    ccraw.c_iflag |= (BRKINT|IGNPAR);
X    ccraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|IUCLC|IXON|IXANY|IXOFF);
X    ccraw.c_oflag &= ~(ONLCR|OCRNL|ONLRET);
X    ccraw.c_cc[4] = 1;
X    ccraw.c_cc[5] = 1;
X    return(ioctl(0,TCSETA,&ccraw) );  	/* set new modes . */
X#endif
X}
X
X
X/*  C O N R E S  --  Restore the console terminal  */
X
Xconres() {
X    if (cgmf == 0) return(0);		/* Don't do anything if modes */
X    sleep(1);				/*  not known! */
X    ckxech = 0;				/* System should echo chars */
X#ifndef UXIII
X    return(stty(0,&ccold));		/* Restore controlling tty */
X#else
X    return(ioctl(0,TCSETA,&ccold));
X#endif
X}
X
X/*  C O N O C  --  Output a character to the console terminal  */
X
Xconoc(c) char c; {
X    write(1,&c,1);
X}
X
X/*  C O N X O  --  Write x characters to the console terminal  */
X
Xconxo(x,s) char *s; int x; {
X    write(1,s,x);
X}
X
X/*  C O N O L  --  Write a line to the console terminal  */
X
Xconol(s) char *s; {
X    int len;
X    len = strlen(s);
X    write(1,s,len);
X}
X
X/*  C O N O L A  --  Write an array of lines to the console terminal */
X
Xconola(s) char *s[]; {
X    int i;
X    for (i=0 ; *s[i] ; i++) conol(s[i]);
X}
X
X/*  C O N O L L  --  Output a string followed by CRLF  */
X
Xconoll(s) char *s; {
X    conol(s);
X    write(1,"\r\n",2);
X}
X
X
X/*  C O N C H K  --  Check if characters available at console  */
X
Xconchk() {
X#ifdef PROVX1
X    return(0);
X#else
X    int n, x;
X#ifndef UXIII
X    x = ioctl(0, FIONREAD, &n);
X    return((x < 0) ? 0 : n);
X#else
X    if (conesc) {			/* Escape typed */
X	conesc = 0;
X	signal(SIGQUIT,esctrp);		/* restore escape */
X	return(1);
X    }
X    return(0);
X#endif
X#endif
X}
X
X/*  C O N I N C  --  Get a character from the console  */
X
Xconinc(timo) int timo; {
X    int n = 0; char ch;
X    if (timo <= 0 ) {			/* untimed */
X	n = read(0, &ch, 1);		/* Read a character. */
X	ch &= 0377;
X	if (n > 0) return(ch); 		/* Return the char if read */
X	else 
X#ifdef UXIII
X	    if (n < 0 && errno == EINTR) /* if read was interrupted by QUIT */
X		return(escchr);		 /* user entered escape character */
X	    else		    /* couldnt be ^c, sigint never returns */
X#endif
X		return(-1);  		/* Return the char, or -1. */
X	}
X    signal(SIGALRM,timerh);		/* timed, set up timer */
X    alarm(timo);
X    if (setjmp(sjbuf)) n = -2;
X    else {
X	n = read(0, &ch, 1);
X	alarm(0);
X	signal(SIGALRM,SIG_DFL);	/* stop timing, we got our character */
X	ch &= 0377;
X    }
X    if (n > 0) return(ch);  
X    else
X#ifdef UXIII
X        if (n == -1 && errno == EINTR)  /* If read interrupted by QUIT, */
X	    return(escchr);		/* user entered escape character, */
X        else		    		/* can't be ^c, sigint never returns */
X#endif
X	return(-1);
X}
!FUNKY!STUFF!
echo x - ckzunx.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckzunx.c
Xchar *ckzv = "Unix file support, 4.1(015) 28 Feb 85";
X
X/* C K Z B S D  --  Kermit file system support for Unix systems */
X
X/* F. da Cruz, Columbia University Center for Computing Activities */
X
X/* Berkeley Unix Version 4.x */
X#ifdef BSD4
Xchar *ckzsys = " 4.x BSD";
X#endif
X
X/* DEC Professional-300 series with Venturcom Venix 1.0 */
X#ifdef PROVX1
Xchar *ckzsys = " DEC Pro-3xx/Venix 1.0";
X#endif
X
X/* NCR Tower support contributed by Kevin O'Kane, U. of Tennessee */
X/* Tower OS is like Sys III but with BSD features -- mostly follows BSD */
X#ifdef TOWER1
Xchar *ckxsys = " NCR Tower 1632, OS 1.02";
X#endif
X
X/* Sys III/V, Xenix, PC/IX,... support by Herm Fischer, Litton Data Systems */
X#ifdef UXIII
X#ifdef XENIX
Xchar *ckzsys = " Xenix/286";
X#else
X#ifdef PCIX
Xchar *ckzsys = " PC/IX";
X#else
X#ifdef ISIII
Xchar *ckzsys = " Interactive Systems Corp, System III";
X#else
Xchar *ckzsys = " AT&T System III/System V";
X#endif
X#endif
X#endif
X#endif
X
X/* Definitions of some Unix system commands */
X
Xchar *DIRCMD = "ls -l ";		/* For directory listing */
Xchar *DELCMD = "rm -f ";		/* For file deletion */
Xchar *TYPCMD = "cat ";			/* For typing a file */
X
X#ifdef BSD4
Xchar *SPACMD = "pwd ; quota ; df .";	/* Space/quota of current directory */
X#else
Xchar *SPACMD = "df ";
X#endif
X
Xchar *SPACM2 = "df ";			/* For space in specified directory */
X
X#ifdef BSD4
Xchar *WHOCMD = "finger ";		/* For seeing who's logged in */
X#else
Xchar *WHOCMD = "who ";			/* For seeing who's logged in */
X#endif
X
X/*
X  Functions (n is one of the predefined file numbers from ckermi.h):
X
X   zopeni(n,name)   -- Opens an existing file for input.
X   zopeno(n,name)   -- Opens a new file for output.
X   zclose(n)        -- Closes a file.
X   zchin(n)         -- Gets the next character from an input file.
X   zsout(n,s)       -- Write a null-terminated string to output file, buffered.
X   zsoutl(n,s)      -- Like zsout, but appends a line terminator.
X   zsoutx(n,s,x)    -- Write x characters to output file, unbuffered.
X   zchout(n,c)      -- Add a character to an output file, unbuffered.
X   zchki(name)      -- Check if named file exists and is readable, return size.
X   zchko(name)      -- Check if named file can be created.
X   znewn(name,s)    -- Make a new unique file name based on the given name.
X   zdelet(name)     -- Delete the named file.
X   zxpand(string)   -- Expands the given wildcard string into a list of files.
X   znext(string)    -- Returns the next file from the list in "string".
X   zxcmd(cmd)       -- Execute the command in a lower fork.
X   zclosf()         -- Close input file associated with zxcmd()'s lower fork.
X   zrtol(n1,n2)     -- Convert remote filename into local form.
X   zltor(n1,n2)     -- Convert local filename into remote form.
X   zchdir(dirnam)   -- Change working directory.
X   zhome()          -- Return pointer to home directory name string.
X */
X
X/* Includes */
X
X#include "ckermi.h"			/* Kermit definitions, ctype, stdio */
X#include <sys/types.h>			/* Data types */
X#include <sys/dir.h>			/* Directory structure */
X#include <sys/stat.h>			/* File status */
X#include <pwd.h>			/* Password file for shell name */
X
X#ifndef PROVX1
X#include <sys/file.h>			/* File access */
X#endif
X
X/* Some systems define these in include files, others don't... */
X
X#ifndef R_OK
X#define R_OK 4				/* For access */
X#endif
X
X#ifndef W_OK
X#define W_OK 2
X#endif
X
X#ifdef PROVX1
X#define MAXNAMLEN DIRSIZ		/* Max file name length */
X#endif
X
X#ifdef UXIII
X#include <fcntl.h>
X#define MAXNAMLEN DIRSIZ
X#endif
X
X#ifndef O_RDONLY
X#define O_RDONLY 000
X#endif
X
X#ifndef MAXNAMLEN
X#define MAXNAMLEN 14			/* If still not defined... */
X#endif
X
X#define MAXWLD 500			/* Maximum wildcard filenames */
X
X
X/* Declarations */
X
XFILE *fp[ZNFILS] = { 			/* File pointers */
X    NULL, NULL, NULL, NULL, NULL, NULL, NULL };
X
Xstatic int pid;	    			/* pid of child fork */
Xstatic int fcount;			/* Number of files in wild group */
Xchar *getenv(), *strcpy();		/* For finding home directory */
Xextern errno;				/* System error code */
X
Xstatic char *mtchs[MAXWLD],		/* Matches found for filename */
X     **mtchptr;				/* Pointer to current match */
X
X/*  Z O P E N I  --  Open an existing file for input. */
X
Xzopeni(n,name) int n; char *name; {
X    debug(F111," zopeni",name,n);
X    debug(F101,"  fp","",(int) fp[n]);
X    if (chkfn(n) != 0) return(0);
X    if (n == ZSTDIO) {			/* Standard input? */
X	if (isatty(0)) {
X	    fprintf(stderr,"?Terminal input not allowed\n");
X	    debug(F110,"zopeni: attempts input from unredirected stdin","",0);
X	    return(0);
X	}
X	fp[ZIFILE] = stdin;
X	return(1);
X    }
X    fp[n] = fopen(name,"r");		/* Real file. */
X    debug(F111," zopeni", name, (int) fp[n]);
X    if (fp[n] == NULL) perror("zopeni");
X    return((fp[n] != NULL) ? 1 : 0);
X}
X
X/*  Z O P E N O  --  Open a new file for output.  */
X
Xzopeno(n,name) int n; char *name; {
X    debug(F111," zopeno",name,n);
X    if (chkfn(n) != 0) return(0);
X    if ((n == ZCTERM) || (n == ZSTDIO)) {   /* Terminal or standard output */
X	fp[ZOFILE] = stdout;
X	debug(F101," fp[]=stdout", "", (int) fp[n]);
X	return(1);
X    }
X    fp[n] = fopen(name,"w");		/* A real file */
X    if (fp[n] == NULL) perror("zopeno");
X    if (n == ZDFILE) setbuf(fp[n],NULL); /* Make debugging file unbuffered */
X    debug(F101, " fp[n]", "", (int) fp[n]);
X    return((fp[n] != NULL) ? 1 : 0);
X}
X
X/*  Z C L O S E  --  Close the given file.  */
X
Xzclose(n) int n; {
X    if (chkfn(n) < 1) return(0);
X    if ((fp[n] != stdout) && (fp[n] != stdin)) fclose(fp[n]);
X    fp[n] = NULL;
X    return(1);
X}
X
X/*  Z C H I N  --  Get a character from the input file.  */
X
Xzchin(n) int n; {
X    int a;
X    if (chkfn(n) < 1) return(-1);
X    a = getc(fp[n]);
X    return((a == EOF) ? -1 : a & 0377);
X}
X
X/*  Z S O U T  --  Write a string to the given file, buffered.  */
X
Xzsout(n,s) int n; char *s; {
X    if (chkfn(n) < 1) return(-1);
X    fprintf(fp[n],s);
X    return(0);
X}
X
X/*  Z S O U T L  --  Write string to file, with line terminator, buffered  */
X
Xzsoutl(n,s) int n; char *s; {
X    if (chkfn(n) < 1) return(-1);
X    fprintf(fp[n],"%s\n",s);
X    return(0);
X}
X
X/*  Z S O U T X  --  Write x characters to file, unbuffered.  */
X
Xzsoutx(n,s,x) int n, x; char *s; {
X    if (chkfn(n) < 1) return(-1);
X    return(write(fp[n]->_file,s,x));
X}
X
X
X/*  Z C H O U T  --  Add a character to the given file.  */
X
Xzchout(n,c) int n; char c; {
X    if (chkfn(n) < 1) return(-1);
X    if (n == ZSFILE)
X    	return(write(fp[n]->_file,&c,1)); /* Use unbuffered for session log */
X    else {
X    	putc(c,fp[n]);			/* Buffered for everything else */
X	return(0);
X    }
X}
X
X/*  C H K F N  --  Internal function to verify file number is ok  */
X
X/*
X Returns:
X  -1: File number n is out of range
X   0: n is in range, but file is not open
X   1: n in range and file is open
X*/
Xchkfn(n) int n; {
X    switch (n) {
X	case ZCTERM:
X	case ZSTDIO:
X	case ZIFILE:
X	case ZOFILE:
X	case ZDFILE:
X	case ZTFILE:
X	case ZPFILE:
X	case ZSFILE: break;
X	default:
X	    debug(F101,"chkfn: file number out of range","",n);
X	    fprintf(stderr,"?File number out of range - %d\n",n);
X	    return(-1);
X    }
X    return( (fp[n] == NULL) ? 0 : 1 );
X}
X
X/*  Z C H K I  --  Check if input file exists and is readable  */
X
X/*
X  Returns:
X   >= 0 if the file can be read (returns the size).
X     -1 if file doesn't exist or can't be accessed,
X     -2 if file exists but is not readable (e.g. a directory file).
X     -3 if file exists but protected against read access.
X*/
X/*
X For Berkeley Unix, a file must be of type "regular" to be readable.
X Directory files, special files, and symbolic links are not readable.
X*/
Xzchki(name) char *name; {
X    struct stat buf;
X    int x;
X
X    x = stat(name,&buf);
X    if (x < 0) {
X	debug(F111,"zchki stat fails",name,errno);
X	return(-1);
X    }
X    x = buf.st_mode & S_IFMT;		/* Isolate file format field */
X    if (x != S_IFREG) {
X	debug(F111,"zchki skipping:",name,x);
X	return(-2);
X    }
X    debug(F111,"zchki stat ok:",name,x);
X
X    if ((x = access(name,R_OK)) < 0) { 	/* Is the file accessible? */
X	debug(F111," access failed:",name,x); /* No */
X    	return(-3);			
X    } else {
X	x = buf.st_size;
X	debug(F111," access ok:",name,x); /* Yes */
X	return( (x > -1) ? x : 0 );
X    }
X}
X
X/*  Z C H K O  --  Check if output file can be created  */
X
X/*
X Returns -1 if write permission for the file would be denied, 0 otherwise.
X*/
Xzchko(name) char *name; {
X    int i, x;
X    char s[50], *sp;	
X
X    sp = s;				/* Make a copy, get length */
X    x = 0;
X    while ((*sp++ = *name++) != '\0')
X    	x++;
X    if (x == 0) return(-1);		/* If no filename, fail. */
X
X    debug(F101," length","",x);
X    for (i = x; i > 0; i--)		/* Strip filename. */
X	if (s[i-1] == '/') break;
X    
X    debug(F101," i","",i);
X    if (i == 0)				/* If no path, use current directory */
X    	strcpy(s,"./");			
X    else				/* Otherwise, use given one. */
X        s[i] = '\0';
X
X    x = access(s,W_OK);			/* Check access of path. */
X    if (x < 0) {
X	debug(F111,"zchko access failed:",s,errno);
X	return(-1);
X    } else {
X	debug(F111,"zchko access ok:",s,x);
X	return(0);
X    }
X}
X
X/*  Z D E L E T  --  Delete the named file.  */
X
Xzdelet(name) char *name; {
X    unlink(name);
X}
X
X
X/*  Z R T O L  --  Convert remote filename into local form  */
X
X/*  For UNIX, this means changing uppercase letters to lowercase.  */
X
Xzrtol(name,name2) char *name, *name2; {
X    for ( ; *name != '\0'; name++) {
X    	*name2++ = isupper(*name) ? tolower(*name) : *name;
X    }
X    *name2 = '\0';
X}
X
X
X/*  Z L T O R  --  Convert filename from local format to common form.   */
X
Xzltor(name,name2) char *name, *name2; {
X    char work[100], *cp, *pp;
X    int dc = 0;
X
X    strcpy(work,name);
X    for (cp = pp = work; *cp != '\0'; cp++) {	/* strip path name */
X    	if (*cp == '/') {
X	    pp = cp;
X	    pp++;
X	}
X	else if (islower(*cp)) *cp = toupper(*cp); /* Uppercase letters */
X	else if (*cp == '~') *cp = 'X';	/* Change tilde to 'X' */
X	else if ((*cp == '.') && (++dc > 1)) *cp = 'X'; /* & extra dots */
X    }
X    cp = name2;				/* If nothing before dot, */
X    if (*pp == '.') *cp++ = 'X';	/* insert 'X' */
X    strcpy(cp,pp);
X}    
X
X
X/*  Z C H D I R  --  Change directory  */
X
Xzchdir(dirnam) char *dirnam; {
X    char *hd;
X    if (*dirnam == '\0') hd = getenv("HOME");
X    else hd = dirnam;
X    return((chdir(hd) == 0) ? 1 : 0);
X}
X
X
X/*  Z H O M E  --  Return pointer to user's home directory  */
X
Xchar *
Xzhome() {
X    return(getenv("HOME"));
X}
X
X/*  Z X C M D -- Run a system command so its output can be read like a file */
X
Xzxcmd(comand) char *comand; {
X    int pipes[2];
X    if (pipe(pipes) != 0) return(0);	/* can't make pipe, fail */
X    if ((pid = fork()) == 0) {		/* child */
X
X/*#if BSD4*/			/* Code from Dave Tweten@AMES-NASA */
X			/* readapted to use getpwuid to find login shell */
X			/*   -- H. Fischer */
X	char *shpath, *shname, *shptr;	/* to find desired shell */
X	struct passwd *p;
X	extern struct passwd * getpwuid();
X	extern int getuid();
X	char *defShel = "/bin/sh";	/* default shell */
X/*#endif*/
X
X	close(pipes[0]);		/* close input side of pipe */
X	close(0);			/* close stdin */
X	if (open("/dev/null",0) < 0) return(0);	/* replace input by null */
X
X#ifndef UXIII
X	dup2(pipes[1],1);		/* replace stdout & stderr */
X	dup2(pipes[1],2);		/* by the pipe */
X#else
X	close(1);			/* simulate dup2 */
X	if (dup(pipes[1]) != 1 )
X	    conol("trouble duping stdout in routine zxcmd\n");
X	close(2);			/* simulate dup2 */
X	if (dup(pipes[1]) != 2 )
X	    conol("trouble duping stderr in routine zxcmd\n");
X#endif
X
X	close(pipes[1]);		/* get rid of this copy of the pipe */
X
X/**** 	shptr = shname = shpath = getenv("SHELL");  /* What shell? */
X	p = getpwuid( getuid() );	/* get login data */
X	if ( p == (struct passwd *) NULL || !*(p->pw_shell) ) shpath = defShel;
X	  else shpath = p->pw_shell;
X	shptr = shname = shpath;
X	while (*shptr != '\0') if (*shptr++ == '/') shname = shptr;
X	execl(shpath,shname,"-c",comand,0); /* Execute the command */
X
X/****	execl("/bin/sh","sh","-c",comand,0); /* Execute the command */
X
X	exit(0); }			/* just punt if it didn't work */
X
X    close(pipes[1]);			/* don't need the output side */
X    fp[ZIFILE] = fdopen(pipes[0],"r");	/* open a stream for it */
X    return(1);
X}
X
X/*  Z C L O S F  - wait for the child fork to terminate and close the pipe. */
X
Xzclosf() {
X    int wstat;
X    fclose(fp[ZIFILE]);
X    fp[ZIFILE] = NULL;
X    while ((wstat = wait(0)) != pid && wstat != -1) ;
X}
X
X/*  Z X P A N D  --  Expand a wildcard string into an array of strings  */
X/*
X  Returns the number of files that match fn1, with data structures set up
X  so that first file (if any) will be returned by the next znext() call.
X*/
Xzxpand(fn) char *fn; {
X    fcount = fgen(fn,mtchs,MAXWLD);	/* Look up the file. */
X    if (fcount > 0) {
X	mtchptr = mtchs;		/* Save pointer for next. */
X    }
X    debug(F111,"zxpand",mtchs[0],fcount);
X    return(fcount);
X}
X
X
X/*  Z N E X T  --  Get name of next file from list created by zxpand(). */
X/*
X Returns >0 if there's another file, with its name copied into the arg string,
X or 0 if no more files in list.
X*/
Xznext(fn) char *fn; {
X    if (fcount-- > 0) strcpy(fn,*mtchptr++);
X    else *fn = '\0';
X    debug(F111,"znext",fn,fcount+1);
X    return(fcount+1);
X}
X
X
X/*  Z N E W N  --  Make a new name for the given file  */
X
Xznewn(fn,s) char *fn, **s; {
X    static char buf[100];
X    char *bp, *xp;
X    int len = 0, n = 0, d = 0, t;
X
X    bp = buf;
X    while (*fn) {
X	*bp++ = *fn++;
X	len++;
X    }
X    *bp++ = '*';			/* Put a star on the end */
X    *bp-- = '\0';
X
X    n = zxpand(buf);			/* Expand the resulting wild name */
X    
X    while (n-- > 0) {			/* Find any existing name~d files */
X	xp = *mtchptr++;
X	xp += len;
X	if (*xp == '~') {
X	    t = atoi(xp+1);
X	    if (t > d) d = t;		/* Get maximum d */
X	}
X    }
X    sprintf(bp,"~%d",d+1);		/* Make and return name~(d+1) */
X    *s = buf;
X}
X
X/* Directory Functions for Unix, written by Jeff Damens, CUCCA, 1984. */
X
X/*
X * The path structure is used to represent the name to match.
X * Each slash-separated segment of the name is kept in one
X * such structure, and they are linked together, to make
X * traversing the name easier.
X */
X
Xstruct path {
X              char npart[MAXNAMLEN];	/* name part of path segment */
X              struct path *fwd;		/* forward ptr */
X            };
X
X#define SSPACE 2000			/* size of string-generating buffer */
Xstatic char sspace[SSPACE];             /* buffer to generate names in */
Xstatic char *freeptr,**resptr;         	/* copies of caller's arguments */
Xstatic int remlen;                      /* remaining length in caller's array*/
Xstatic int numfnd;                      /* number of matches found */
X
X/*
X * splitpath:
X *  takes a string and splits the slash-separated portions into
X *  a list of path structures.  Returns the head of the list.  The
X *  structures are allocated by malloc, so they must be freed.
X *  Splitpath is used internally by the filename generator.
X *
X * Input: A string.
X * Returns: A linked list of the slash-separated segments of the input.
X */
X
Xstruct path *
Xsplitpath(p)
Xchar *p;
X{
X struct path *head,*cur,*prv;
X int i;
X head = prv = NULL;
X if (*p == '/') p++;            /* skip leading slash */
X while (*p != '\0')
X {
X   cur = (struct path *) malloc(sizeof (struct path));
X   cur -> fwd = NULL;
X   if (head == NULL) head = cur;
X   else prv -> fwd = cur;       /* link into chain */
X   prv = cur;
X   for (i=0; i < MAXNAMLEN && *p != '/' && *p != '\0'; i++)
X     cur -> npart[i] = *p++;
X   cur -> npart[i] = '\0';      /* end this segment */
X   if (i >= MAXNAMLEN) while (*p != '/' && *p != '\0') p++;
X   if (*p == '/') p++;
X }
X return(head);
X}
X
X/*
X * fgen:
X *  This is the actual name generator.  It is passed a string,
X *  possibly containing wildcards, and an array of character pointers.
X *  It finds all the matching filenames and stores them into the array.
X *  The returned strings are allocated from a static buffer local to
X *  this module (so the caller doesn't have to worry about deallocating
X *  them); this means that successive calls to fgen will wipe out
X *  the results of previous calls.  This isn't a problem here
X *  because we process one wildcard string at a time.
X *
X * Input: a wildcard string, an array to write names to, the
X *        length of the array.
X * Returns: the number of matches.  The array is filled with filenames
X *          that matched the pattern.  If there wasn't enough room in the
X *	    array, -1 is returned.
X * By: Jeff Damens, CUCCA, 1984.
X */
X
Xfgen(pat,resarry,len)
Xchar *pat,*resarry[];
Xint len;
X{
X struct path *head;
X char scratch[100],*sptr;
X head = splitpath(pat);
X if (*pat == '/')
X {
X  scratch[0] = '/';
X  sptr = scratch+1;
X }
X else
X {
X  strcpy(scratch,"./");
X  sptr = scratch+2;
X }					/* init buffer correctly */
X numfnd = 0;                            /* none found yet */
X freeptr = sspace;			/* this is where matches are copied */
X resptr = resarry;			/* static copies of these so*/
X remlen = len;				/* recursive calls can alter them */
X traverse(head,scratch,sptr);		/* go walk the directory tree */
X for (; head != NULL; head = head -> fwd)
X   free(head);				/* return the path segments */
X return(numfnd);			/* and return the number of matches */
X}
X
X/* traverse:
X *  Walks the directory tree looking for matches to its arguments.
X *  The algorithm is, briefly:
X *   If the current pattern segment contains no wildcards, that
X *   segment is added to what we already have.  If the name so far
X *   exists, we call ourselves recursively with the next segment
X *   in the pattern string; otherwise, we just return.
X *
X *   If the current pattern segment contains wildcards, we open the name
X *   we've accumulated so far (assuming it is really a directory), then read 
X *   each filename in it, and, if it matches the wildcard pattern segment, add
X *   that filename to what we have so far and call ourselves recursively on the
X *   next segment.
X *
X *   Finally, when no more pattern segments remain, we add what's accumulated
X *   so far to the result array and increment the number of matches.
X *
X * Input: a pattern path list (as generated by splitpath), a string
X *	  pointer that points to what we've traversed so far (this
X *	  can be initialized to "/" to start the search at the root
X *	  directory, or to "./" to start the search at the current
X *	  directory), and a string pointer to the end of the string
X *	  in the previous argument.
X * Returns: nothing.
X */
Xtraverse(pl,sofar,endcur)
Xstruct path *pl;
Xchar *sofar,*endcur;
X{
X#ifdef BSD4
X DIR *fd;
X struct direct *dirbuf;
X#else
X int fd;
X struct direct dir_entry;
X struct direct *dirbuf = &dir_entry;
X#endif
X struct stat statbuf;
X if (pl == NULL)
X {
X  *--endcur = '\0';                    /* end string, overwrite trailing / */
X  addresult(sofar);
X  return;
X }
X if (!iswild(pl -> npart))
X {
X  strcpy(endcur,pl -> npart);
X  endcur += strlen(pl -> npart);
X  *endcur = '\0';                     	/* end current string */
X  if (stat(sofar,&statbuf) == 0)	/* if current piece exists */
X  {
X      *endcur++ = '/';                  /* add slash to end */
X      *endcur = '\0';			/* and end the string */
X      traverse(pl -> fwd,sofar,endcur);
X  }
X  return;
X }
X/* cont'd... */
X
X/*...traverse, cont'd */
X
X/* segment contains wildcards, have to search directory */
X *endcur = '\0';                        	/* end current string */
X if (stat(sofar,&statbuf) == -1) return;   	/* doesn't exist, forget it */
X if ((statbuf.st_mode & S_IFDIR) == 0) return;  /* not a directory, skip */
X#ifdef BSD4
X if ((fd = opendir(sofar)) == NULL) return;  	/* can't open, forget it */
X while (dirbuf = readdir(fd))
X#else
X if ((fd = open(sofar,O_RDONLY)) < 0) return;  	/* can't open, forget it */
X while ( read(fd,dirbuf,sizeof dir_entry) )
X#endif
X  if (dirbuf->d_ino != 0 && match(pl -> npart,dirbuf->d_name)) {
X    char *eos;
X    strcpy(endcur,dirbuf->d_name);
X    eos = endcur + strlen(dirbuf->d_name);
X    *eos = '/';                    /* end this segment */
X    traverse(pl -> fwd,sofar,eos+1);
X  }
X#ifdef BSD4
X closedir(fd);
X#else
X close(fd);
X#endif
X}
X
X/*
X * addresult:
X *  Adds a result string to the result array.  Increments the number
X *  of matches found, copies the found string into our string
X *  buffer, and puts a pointer to the buffer into the caller's result
X *  array.  Our free buffer pointer is updated.  If there is no
X *  more room in the caller's array, the number of matches is set to -1.
X * Input: a result string.
X * Returns: nothing.
X */
X
Xaddresult(str)
Xchar *str;
X{
X int l;
X if (strncmp(str,"./",2) == 0) str += 2;
X if (--remlen < 0) {
X  numfnd = -1;
X  return;
X }
X l = strlen(str) + 1;			/* size this will take up */
X if ((freeptr + l) > &sspace[SSPACE]) {
X    numfnd = -1;			/* do not record if not enough space */
X    return;
X  }
X strcpy(freeptr,str);
X *resptr++ = freeptr;
X freeptr += l;
X numfnd++;
X}
X
Xiswild(str)
Xchar *str;
X{
X char c;
X while ((c = *str++) != '\0')
X   if (c == '*' || c == '?') return(1);
X return(0);
X}
X
X/*
X * match:
X *  pattern matcher.  Takes a string and a pattern possibly containing
X *  the wildcard characters '*' and '?'.  Returns true if the pattern
X *  matches the string, false otherwise.
X * by: Jeff Damens, CUCCA
X *
X * Input: a string and a wildcard pattern.
X * Returns: 1 if match, 0 if no match.
X */
X
Xmatch(pattern,string) char *pattern,*string; {
X    char *psave,*ssave;			/* back up pointers for failure */
X    psave = ssave = NULL;
X    while (1) {
X	for (; *pattern == *string; pattern++,string++)  /* skip first */
X	    if (*string == '\0') return(1);	/* end of strings, succeed */
X	if (*string != '\0' && *pattern == '?') {
X	    pattern++;			/* '?', let it match */
X	    string++;
X	} else if (*pattern == '*') {	/* '*' ... */
X	    psave = ++pattern;		/* remember where we saw it */
X	    ssave = string;		/* let it match 0 chars */
X	} else if (ssave != NULL && *ssave != '\0') {	/* if not at end  */
X  					/* ...have seen a star */
X	    string = ++ssave;		/* skip 1 char from string */
X	    pattern = psave;		/* and back up pattern */
X	} else return(0);		/* otherwise just fail */
X    }
X}
!FUNKY!STUFF!

gregg@okstate.UUCP (03/08/85)

echo x - ckcmd.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckcmd.c
Xchar *cmdv = "Unix cmd package V1.0(015) 27 Feb 85";
X
X/*  C K C M D  --  Interactive command package for Unix  */
X/*
X Modelled after the DECSYSTEM-20 command parser (the COMND JSYS)
X
X Features:
X . parses and verifies keywords, text strings, numbers, and other data
X . displays appropriate menu or help message when user types "?"
X . does keyword and filename completion when user types ESC
X . accepts any unique abbreviation for a keyword
X . allows keywords to have attributes, like "invisible"
X . can supply defaults for fields omitted by user
X . provides command line editing (character, word, and line deletion)
X . accepts input from keyboard, command files, or redirected stdin
X . allows for full or half duplex operation, character or line input
X . settable prompt, protected from deletion
X
X Functions:
X  cmsetp - Set prompt
X  cmsavp - Save current prompt
X  prompt - Issue prompt
X  cmini  - Clear the command buffer (before parsing a new command)
X  cmres  - Reset command buffer pointers (before reparsing)
X  cmkey  - Parse a keyword
X  cmnum  - Parse a number
X  cmifi  - Parse an input file name
X  cmofi  - Parse an output file name
X  cmfld  - Parse an arbitrary field
X  cmtxt  - Parse a text string
X  cmcfm  - Parse command confirmation (end of line)
X  stripq - Strip out backslash quotes from a string.
X
X Return codes:
X  -3: no input provided when required
X  -2: input was invalid
X  -1: reparse required (user deleted into a preceding field)
X   0 or greater: success
X  See individual functions for greater detail.
X
X Before using these routines, the caller should #include ckcmd.h, and
X set the program's prompt by calling cmsetp().  If the file parsing
X functions cmifi and cmofi are to be used, this module must be linked
X with a ckz??? file system support module for the appropriate system,
X e.g. ckzbsd for Berkeley Unix.  If the caller puts the terminal in
X character wakeup ("cbreak") mode with no echo, then these functions will
X provide line editing -- character, word, and line deletion, as well as
X keyword and filename completion upon ESC and help strings, keyword, or
X file menus upon '?'.  If the caller puts the terminal into character
X wakeup/noecho mode, care should be taken to restore it before exit from
X or interruption of the program.  If the character wakeup mode is not
X set, the system's own line editor may be used.
X
X Author: Frank da Cruz (SY.FDC@CU20B),
X Columbia University Center for Computing Activities, Jan 1985.
X Copyright (C) 1985, Trustees of Columbia University in the City of New York.
X Permission is granted to any individual or institution to copy or use this
X software except for explicitly commercial purposes, provided this copyright
X notice is retained.
X*/
X
X/* Includes */
X
X#include <stdio.h>			/* Standard C I/O package */
X#include <ctype.h>			/* Character types */
X#include "ckcmd.h"			/* Command parsing definitions */
X#include "ckdebu.h"			/* Formats for debug() */
X
X/* Local variables */
X
Xint psetf = 0,				/* Flag that prompt has been set */
X    cc = 0,				/* Character count */
X    dpx = 0;				/* Duplex (0 = full) */
X
Xint hw = HLPLW,				/* Help line width */
X    hc = HLPCW,				/* Help line column width */
X    hh,					/* Current help column number */
X    hx;					/* Current help line position */
X
X#define PROML 60			/* Maximum length for prompt */
X
Xchar cmprom[PROML+1];			/* Program's prompt */
Xchar *dfprom = "Command? ";		/* Default prompt */
X
Xint cmflgs;				/* Command flags */
X
Xchar cmdbuf[CMDBL+4];			/* Command buffer */
Xchar hlpbuf[HLPBL+4];			/* Help string buffer */
Xchar atmbuf[ATMBL+4];			/* Atom buffer */
Xchar filbuf[ATMBL+4];			/* File name buffer */
X
X/* Command buffer pointers */
X
Xstatic char *bp,			/* Current command buffer position */
X    *pp,				/* Start of current field */
X    *np;				/* Start of next field */
X
X/*  C M S E T P  --  Set the program prompt.  */
X
Xcmsetp(s) char *s; {
X    char *strncpy();
X    psetf = 1;				/* Flag that prompt has been set. */
X    strncpy(cmprom,s,PROML - 1);	/* Copy the string. */
X    cmprom[PROML] = NUL;
X}
X
X
X/*  C M S A V P  --  Save a copy of the current prompt.  */
X
Xcmsavp(s,n) int n; char s[]; {
X    strncpy(s,cmprom,n-1);
X    s[n] = NUL;
X}
X
X
X/*  P R O M P T  --  Issue the program prompt.  */
X
Xprompt() {
X    if (psetf == 0) cmsetp(dfprom);	/* If no prompt set, set default. */
X    printf("\r%s",cmprom);		/* Print the prompt. */
X}
X
X
X/*  C M R E S  --  Reset pointers to beginning of command buffer.  */
X
Xcmres() {  
X    cc = 0;				/* Reset character counter. */
X    pp = np = bp = cmdbuf;		/* Point to command buffer. */
X    cmflgs = -5;			/* Parse not yet started. */
X}
X
X
X/*  C M I N I  --  Clear the command and atom buffers, reset pointers.  */
X
X/*
XThe argument specifies who is to echo the user's typein --
X  1 means the cmd package echoes
X  0 somebody else (system, front end, terminal) echoes
X*/
Xcmini(d) int d; {
X    for (bp = cmdbuf; bp < cmdbuf+CMDBL; bp++) *bp = NUL;
X    *atmbuf = NUL;
X    dpx = d;
X    cmres();
X}
X
Xstripq(s) char *s; {			/* Function to strip '\' quotes */
X    char *t;
X    while (*s) {
X	if (*s == '\\') {
X	    for (t = s; *t != '\0'; t++) *t = *(t+1);
X	}
X	s++;
X    }
X}
X
X/*  C M N U M  --  Parse a number in the indicated radix  */
X
X/*  For now, only works for positive numbers in base 10.  */
X
X/*
X Returns
X   -3 if no input present when required,
X   -2 if user typed an illegal number,
X   -1 if reparse needed,
X    0 otherwise, with n set to number that was parsed
X*/
Xcmnum(xhlp,xdef,radix,n) char *xhlp, *xdef; int radix, *n; {
X    int x; char *s;
X
X    if (radix != 10) {			/* Just do base 10 for now */
X	printf("cmnum: illegal radix - %d\n",radix);
X	return(-1);
X    }
X
X    x = cmfld(xhlp,xdef,&s);
X    debug(F101,"cmnum: cmfld","",x);
X    if (x < 0) return(x);    /* Parse a field */
X
X    if (digits(atmbuf)) {		/* Convert to number */
X	*n = atoi(atmbuf);
X	return(x);
X    } else {
X	printf("\n?not a number - %s\n",s);
X	return(-2);	
X    }
X}
X
X/*  C M O F I  --  Parse the name of an output file  */
X
X/*
X Depends on the external function zchko(); if zchko() not available, use
X cmfld() to parse output file names.
X
X Returns
X   -3 if no input present when required,
X   -2 if permission would be denied to create the file,
X   -1 if reparse needed,
X    0 or 1 otherwise, with xp pointing to name.
X*/
Xcmofi(xhlp,xdef,xp) char *xhlp, *xdef, **xp; {
X    int x; char *s;
X
X    if (*xhlp == NUL) xhlp = "Output file";
X    *xp = "";
X
X    if ((x = cmfld(xhlp,xdef,&s)) < 0) return(x);
X
X    if (chkwld(s)) {
X	printf("\n?Wildcards not allowed - %s\n",s);
X	return(-2);
X    }
X    if (zchko(s) < 0) {
X	printf("\n?Write permission denied - %s\n",s);
X	return(-2);
X    } else {
X	*xp = s;
X	return(x);
X    }
X}
X
X/*  C M I F I  --  Parse the name of an existing file  */
X
X/*
X This function depends on the external functions:
X   zchki()  - Check if input file exists and is readable.
X   zxpand() - Expand a wild file specification into a list.
X   znext()  - Return next file name from list.
X If these functions aren't available, then use cmfld() to parse filenames.
X*/
X/*
X Returns
X   -4 EOF
X   -3 if no input present when required,
X   -2 if file does not exist or is not readable,
X   -1 if reparse needed,
X    0 or 1 otherwise, with:
X	xp pointing to name,
X    	wild = 1 if name contains '*' or '?', 0 otherwise.
X*/
Xcmifi(xhlp,xdef,xp,wild) char *xhlp, *xdef, **xp; int *wild; {
X    int i, x, xc, y; char *sp;
X
X    cc = xc = 0;			/* Initialize counts & pointers */
X    *xp = "";
X    if ((x = cmflgs) != 1) {		/* Already confirmed? */
X	x = getwd();			/* No, get a word */
X    } else {
X	cc = setatm(xdef);		/* If so, use default, if any. */
X    }
X    *xp = atmbuf;			/* Point to result. */
X    *wild = chkwld(*xp);
X
X    while (1) {
X	xc += cc;			/* Count the characters. */
X	debug(F111,"cmifi: getwd",atmbuf,xc);
X    	switch (x) {
X	    case -4:			/* EOF */
X	    case -2:			/* Out of space. */
X	    case -1:			/* Reparse needed */
X	    	return(x);
X
X/* cont'd... */
X
X/* ...cmifi(), cont'd */
X
X
X	    case 0:			/* SP or NL */
X	    case 1:
X	    	if (xc == 0) *xp = xdef;    /* If no input, return default. */
X		else *xp = atmbuf;
X		if (**xp == NUL) return(-3); /* If field empty, return -3. */
X		
X		/* If filespec is wild, see if there are any matches */
X
X		*wild = chkwld(*xp);
X		debug(F101," *wild","",*wild);
X		if (*wild != 0) {
X		    y = zxpand(*xp);
X		    if (y == 0) {
X			printf("\n?No files match - %s\n",*xp);
X			return(-2);
X		    } else if (y < 0) {
X			printf("\n?Too many files match - %s\n",*xp);
X			return(-2);
X		    } else return(x);
X		}
X
X		/* If not wild, see if it exists and is readable. */
X
X		y = zchki(*xp);
X		if (y == -3) {
X		    printf("\n?Read permission denied - %s\n",*xp);
X		    return(-2);
X		} else if (y == -2) {
X		    printf("\n?File not readable - %s\n",*xp);
X		    return(-2);
X		} else if (y < 0) {
X		    printf("\n?File not found - %s\n",*xp);
X		    return(-2);
X		}
X		return(x);
X/* cont'd... */
X
X/* ...cmifi(), cont'd */
X
X
X	    case 2:			/* ESC */
X	    	if (xc == 0) {
X		    if (*xdef != '\0') {
X			printf("%s ",xdef); /* If at beginning of field, */
X			addbuf(xdef);	/* supply default. */
X			cc = setatm(xdef);
X		    } else {		/* No default */
X			putchar(BEL);
X		    }
X		    break;
X		} 
X		if (*wild = chkwld(*xp)) {  /* No completion if wild */
X		    putchar(BEL);
X		    break;
X		}
X		sp = atmbuf + cc;
X		*sp++ = '*';
X		*sp-- = '\0';
X		y = zxpand(atmbuf);	/* Add * and expand list. */
X		*sp = '\0';		/* Remove *. */
X
X		if (y == 0) {
X		    printf("\n?No files match - %s\n",atmbuf);
X		    return(-2);
X		} else if (y < 0) {
X		    printf("\n?Too many files match - %s\n",atmbuf);
X		    return(-2);
X		} else if (y > 1) {	/* Not unique, just beep. */
X		    putchar(BEL);
X		} else {		/* Unique, complete it.  */
X		    znext(filbuf);	/* Get whole name of file. */
X		    sp = filbuf + cc;	/* Point past what user typed. */
X		    printf("%s ",sp);	/* Complete the name. */
X		    addbuf(sp);		/* Add the characters to cmdbuf. */
X		    setatm(pp);		/* And to atmbuf. */
X		    *xp = atmbuf;	/* Return pointer to atmbuf. */
X		    return(cmflgs = 0);
X		}
X		break;
X
X/* cont'd... */
X
X/* ...cmifi(), cont'd */
X
X
X	    case 3:			/* Question mark */
X	    	if (*xhlp == NUL)
X	    	    printf(" Input file specification");
X		else
X		    printf(" %s",xhlp);
X		if (xc > 0) {
X		    sp = atmbuf + cc;	/* Insert * at end */
X		    *sp++ = '*';
X		    *sp-- = '\0';
X		    y = zxpand(atmbuf);
X		    *sp = '\0';
X		    if (y == 0) {		    
X			printf("\n?No files match - %s\n",atmbuf);
X			return(-2);
X		    } else if (y < 0) {
X			printf("\n?Too many file match - %s\n",atmbuf);
X			return(-2);
X		    } else {
X			printf(", one of the following:\n");
X			clrhlp();
X			for (i = 0; i < y; i++) {
X			    znext(filbuf);
X			    addhlp(filbuf);
X			}
X			dmphlp();
X		    }
X		} else printf("\n");
X		printf("%s%s",cmprom,cmdbuf);
X		break;
X	}
X    x = getwd();
X    }
X}
X
X
X
X/*  C H K W L D  --  Check for wildcard characters '*' or '?'  */
X
Xchkwld(s) char *s; {
X
X    for ( ; *s != '\0'; s++) {
X    	if ((*s == '*') || (*s == '?'))
X	    return(1);
X    }
X    return(0);
X}
X
X/*  C M F L D  --  Parse an arbitrary field  */
X/*
X Returns
X   -3 if no input present when required,
X   -2 if field too big for buffer,
X   -1 if reparse needed,
X    0 otherwise, xp pointing to string result.
X*/
Xcmfld(xhlp,xdef,xp) char *xhlp, *xdef, **xp; {
X    int x, xc;
X
X    cc = xc = 0;			/* Initialize counts & pointers */
X    *xp = "";
X    if ((x = cmflgs) != 1) {		/* Already confirmed? */
X	x = getwd();			/* No, get a word */
X    } else {
X	cc = setatm(xdef);		/* If so, use default, if any. */
X    }
X    *xp = atmbuf;			/* Point to result. */
X
X    while (1) {
X	xc += cc;			/* Count the characters. */
X	debug(F111,"cmfld: getwd",atmbuf,xc);
X	debug(F101," x","",x);
X    	switch (x) {
X	    case -4:			/* EOF */
X	    case -2:			/* Out of space. */
X	    case -1:			/* Reparse needed */
X	    	return(x);
X	    case 0:			/* SP or NL */
X	    case 1:
X	    	if (xc == 0) *xp = xdef;    /* If no input, return default. */
X		else *xp = atmbuf;
X		if (**xp == NUL) x = -3;    /* If field empty, return -3. */
X		return(x);
X	    case 2:			/* ESC *** (maybe treat as SP) */
X	    	if (xc == 0) {
X		    printf("%s ",xdef);	/* If at beginning of field, */
X		    addbuf(xdef);	/* supply default. */
X		    cc = setatm(xdef);
X		} else {
X		    putchar(BEL);	/* Beep if already into field. */
X    	    	}		    
X		break;
X	    case 3:			/* Question mark */
X	    	if (*xhlp == NUL)
X		    printf(" Please complete this field");
X		else
X	            printf(" %s",xhlp);
X		printf("\n%s%s",cmprom,cmdbuf);
X		break;
X	}
X    x = getwd();
X    }
X}
X
X/*  C M T X T  --  Get a text string, including confirmation  */
X
X/*
X  Print help message 'xhlp' if ? typed, supply default 'xdef' if null
X  string typed.  Returns
X
X   -1 if reparse needed or buffer overflows.
X    1 otherwise.
X
X  with cmflgs set to return code, and xp pointing to result string.
X*/
X
Xcmtxt(xhlp,xdef,xp) char *xhlp; char *xdef; char **xp; {
X
X    int x, xc;
X
X    cc = xc = 0;			/* Start counters off at 0 */
X    *xp = "";				/* And pointer to null string. */
X    *atmbuf = NUL;			/* And empty atom buffer. */
X    if ((x = cmflgs) != 1) {
X	x = getwd();			/* Get first word. */
X	*xp = pp;			/* Save pointer to it. */
X    }
X    while (1) {
X	xc += cc;			/* Accumulate count. */
X	debug(F111,"cmtxt: getwd",atmbuf,xc);
X	switch (x) {
X	    case -4:			/* EOF */
X	    case -2:			/* Overflow */
X	    case -1:			/* Deletion */
X	        return(x);
X	    case 0:			/* Space */
X		break;
X	    case 1:			/* CR or LF */
X	        if (xc == 0) *xp = xdef;
X		return(x);
X	    case 2:			/* ESC */
X	    	if (xc == 0) {
X		    printf("%s ",xdef);
X		    cc = addbuf(xdef);
X		} else {
X		    putchar(BEL);
X		}
X		break;
X	    case 3:			/* Question Mark */
X	    	if (*xhlp == NUL)
X		    printf(" Text string");
X		else
X		    printf(" %s",xhlp);
X		printf("\n%s%s",cmprom,cmdbuf);
X		break;
X            default:
X	    	printf("\n?Unexpected return code from getwd() - %d\n",x);
X		return(-2);
X        }
X	x = getwd();
X    }
X}
X
X/*  C M K E Y  --  Parse a keyword  */
X
X/*
X Call with:
X   table    --  keyword table, in 'struct keytab' format;
X   n        --  number of entries in table;
X   xhlp     --  pointer to help string;
X   xdef     --  pointer to default keyword;
X
X Returns:
X   -3       --  no input supplied and no default available
X   -2       --  input doesn't uniquely match a keyword in the table
X   -1       --  user deleted too much, command reparse required
X    n >= 0  --  value associated with keyword
X*/
X
Xcmkey(table,n,xhlp,xdef) struct keytab table[]; int n; char *xhlp, *xdef; {
X    int i, y, z, zz, xc;
X    char *xp;
X
X    xc = cc = 0;			/* Clear character counters. */
X
X    if ((zz = cmflgs) == 1) 		/* Command already entered? */
X	setatm(xdef);
X    else zz = getwd(); 
X
Xdebug(F101,"cmkey: table length","",n);
Xdebug(F101," cmflgs","",cmflgs);
Xdebug(F101," zz","",zz);
Xwhile (1) {
X    xc += cc;
X    debug(F111,"cmkey: getwd",atmbuf,xc);
X
X    switch(zz) {
X	case -4:			/* EOF */
X	case -2:			/* Buffer overflow */
X    	case -1:			/* Or user did some deleting. */
X	    return(zz);
X
X	case 0:				/* User terminated word with space */
X	case 1:				/* or newline */
X	    if (cc == 0) setatm(xdef);
X	    y = lookup(table,atmbuf,n,&z);
X	    switch (y) {
X		case -2:
X		    printf("\n?Ambiguous - %s\n",atmbuf);
X		    return(cmflgs = -2);
X		case -1:
X		    printf("\n?Invalid - %s\n",atmbuf);
X		    return(cmflgs = -2);
X		default:
X		    break;
X	    }
X	    return(y);
X
X/* cont'd... */
X
X/* ...cmkey(), cont'd */
X
X	case 2:				/* User terminated word with ESC */
X	    if (cc == 0) {
X	    	if (*xdef != NUL) {	/* Nothing in atmbuf */
X		    printf("%s ",xdef);	/* Supply default if any */
X		    addbuf(xdef);
X		    cc = setatm(xdef);
X		    debug(F111,"cmkey: default",atmbuf,cc);
X		} else {
X		    putchar(BEL);	/* No default, just beep */
X		    break;
X		}
X	    }
X	    y = lookup(table,atmbuf,n,&z); /* Something in atmbuf */
X	    debug(F111,"cmkey: esc",atmbuf,y);
X	    if (y == -2) {
X		putchar(BEL);
X		break;
X    	    }
X	    if (y == -1) {
X		printf("\n?Invalid - %s\n",atmbuf);
X		return(cmflgs = -2);
X	    }
X	    xp = table[z].kwd + cc;
X    	    printf("%s ",xp);
X	    addbuf(xp);
X	    debug(F110,"cmkey: addbuf",cmdbuf,0);
X	    return(y);
X
X/* cont'd... */
X
X/* ...cmkey(), cont'd */
X
X	case 3:				/* User terminated word with "?" */
X	    y = lookup(table,atmbuf,n,&z);
X	    if (y > -1) {
X		printf(" %s\n%s%s",table[z].kwd,cmprom,cmdbuf);
X		break;
X	    } else if (y == -1) {
X		printf("\n?Invalid\n");
X		return(cmflgs = -2);
X	    }
X
X	    if (*xhlp == NUL)
X	    	printf(" One of the following:\n");
X	    else
X	    	printf(" %s, one of the following:\n",xhlp);
X
X	    clrhlp();
X	    for (i = 0; i < n; i++) {	
X		if (!strncmp(table[i].kwd,atmbuf,cc)
X		    	&& !test(table[i].flgs,CM_INV))
X		    addhlp(table[i].kwd);
X	    }
X	    dmphlp();
X	    printf("%s%s", cmprom, cmdbuf);
X	    break;
X
X    	default:	    
X	    printf("\n%d - Unexpected return code from getwd\n",zz);
X	    return(cmflgs = -2);
X        }
X	zz = getwd();
X    }
X}
X
X/*  C M C F M  --  Parse command confirmation (end of line)  */
X
X/*
X Returns
X   -2: User typed anything but whitespace or newline
X   -1: Reparse needed
X    0: Confirmation was received
X*/
X
Xcmcfm() {
X    int x, xc;
X
X    debug(F101,"cmcfm: cmflgs","",cmflgs);
X
X    xc = cc = 0;
X    if (cmflgs == 1) return(0);
X
X    while (1) {
X	x = getwd();
X	xc += cc;
X	debug(F111,"cmcfm: getwd",atmbuf,xc);
X        switch (x) {
X	    case -4:			/* EOF */
X	    case -2:
X	    case -1:
X		return(x);
X
X	    case 0:			/* Space */
X	    	continue;
X	    case 1:			/* End of line */
X	    	if (xc > 0) {
X		    printf("?Not confirmed - %s\n",atmbuf);
X		    return(-2);
X    	    	} else return(0);		    
X	    case 2:
X	    	putchar(BEL);
X		continue;
X
X            case 3:
X	    	if (xc > 0) {
X		    printf("\n?Not confirmed - %s\n",atmbuf);
X		    return(-2);
X		}
X	        printf("\n Type a carriage return to confirm the command\n");
X		printf("%s%s",cmprom,cmdbuf);
X		continue;
X	}
X    }
X}
X
X/* Keyword help routines */
X
X
X/*  C L R H L P -- Initialize/Clear the help line buffer  */
X
Xclrhlp() {				/* Clear the help buffer */
X    hlpbuf[0] = NUL;
X    hh = hx = 0;
X}
X
X
X/*  A D D H L P  --  Add a string to the help line buffer  */
X
Xaddhlp(s) char *s; {			/* Add a word to the help buffer */
X    int j;
X
X    hh++;				/* Count this column */
X
X    for (j = 0; j < hc; j++) {		/* Fill the column */
X	if (*s != NUL)			/* First with chars from the string */
X	    hlpbuf[hx++] = *s++;
X	else {
X	    if (hh < (hw / hc))		/* Then with spaces */
X	    	hlpbuf[hx++] = SP;
X	    else {
X		hlpbuf[hx++] = NUL;	/* If last column, no spaces. */
X		dmphlp();		/* Print it. */
X		return;
X		}
X    	} 
X    }
X    if (*s != NUL)			/* Still some chars left in string? */
X	hlpbuf[hx-1] = '+';		/* Mark as too long for column. */
X}
X
X
X/*  D M P H L P  --  Dump the help line buffer  */
X
Xdmphlp() {				/* Print the help buffer */
X    hlpbuf[hx++] = NUL;
X    printf(" %s\n",hlpbuf);
X    clrhlp();
X}
X
X/*  L O O K U P  --  Lookup the string in the given array of strings  */
X
X/*
X Call this way:  v = lookup(table,word,n,&x);
X
X   table - a 'struct keytab' table.
X   word  - the target string to look up in the table.
X   n     - the number of elements in the table.
X   x     - address of an integer for returning the table array index.
X
X The keyword table must be arranged in ascending alphabetical order, and
X all letters must be lowercase.
X
X Returns the keyword's associated value ( zero or greater ) if found,
X with the variable x set to the array index, or:
X
X  -3 if nothing to look up (target was null),
X  -2 if ambiguous,
X  -1 if not found.
X
X A match is successful if the target matches a keyword exactly, or if
X the target is a prefix of exactly one keyword.  It is ambiguous if the
X target matches two or more keywords from the table.
X*/
X
Xlookup(table,cmd,n,x) char *cmd; struct keytab table[]; int n, *x; {
X
X    int i, v, cmdlen;
X
X/* Lowercase & get length of target, if it's null return code -3. */
X
X    if ((((cmdlen = lower(cmd))) == 0) || (n < 1)) return(-3);
X
X/* Not null, look it up */
X
X    for (i = 0; i < n-1; i++) {
X	if (!strcmp(table[i].kwd,cmd) ||
X           ((v = !strncmp(table[i].kwd,cmd,cmdlen)) &&
X             strncmp(table[i+1].kwd,cmd,cmdlen))) {
X		*x = i;
X		return(table[i].val);
X	     }
X	if (v) return(-2);
X    }	
X
X/* Last (or only) element */
X
X    if (!strncmp(table[n-1].kwd,cmd,cmdlen)) {
X	*x = n-1;
X	return(table[n-1].val);
X    } else return(-1);
X}
X
X/*  G E T W D  --  Gets a "word" from the command input stream  */
X
X/*
XUsage: retcode = getwd();
X
XReturns:
X -4 if end of file (e.g. pipe broken)
X -2 if command buffer overflows
X -1 if user did some deleting
X  0 if word terminates with SP or tab
X  1 if ... CR
X  2 if ... ESC
X  3 if ... ?
X
XWith:
X  pp pointing to beginning of word in buffer
X  bp pointing to after current position
X  atmbuf containing a copy of the word
X  cc containing the number of characters in the word copied to atmbuf
X*/
Xgetwd() {
X
X    int c;				/* Current char */
X    static int inword = 0;		/* Flag for start of word found */
X    int quote = 0;			/* Flag for quote character */
X    int echof = 0;			/* Flag for whether to echo */
X    int ignore = 0;
X
X    pp = np;				/* Start of current field */
X    debug(F101,"getwd: cmdbuf","",(int) cmdbuf);
X    debug(F101," bp","",(int) bp);
X    debug(F101," pp","",(int) pp);
X    debug(F110," cmdbuf",cmdbuf,0);
X
X    while (bp < cmdbuf+CMDBL) {		/* Loop */
X
X	ignore = echof = 0;		/* Flag for whether to echo */
X
X	if ((c = *bp) == NUL) {		/* Get next character */
X	    if (dpx) echof = 1;		/* from reparse buffer */
X	    c = getchar();		/* or from tty. */
X	    if (c == EOF) return(-4);
X	} else ignore = 1;
X
X	if (quote == 0) {
X
X	    if (!ignore && (c == '\\')) { /* Quote character */
X	       quote = 1;
X	       continue;
X    	    }
X	    if (c == FF) {		/* Formfeed. */
X	    	c = NL;			/* Replace with newline */
X	    	system("clear");	/* and clear the screen. */
X	    }
X
X	    if (c == HT) c = SP; 	/* Substitute space for tab. */
X
X/* cont'd... */
X
X/* ...getwd(), cont'd */
X
X    	    if (c == SP) {		/* If space */
X		*bp++ = c;		/* deposit it in buffer. */
X		if (echof) putchar(c);	/* echo it. */
X		if (inword == 0) {	/* If leading, gobble it. */
X		    pp++;
X		    continue;
X		} else {		/* If terminating, return. */
X		    np = bp;
X		    setatm(pp);
X		    inword = 0;
X		    return(cmflgs = 0);
X		}
X	    }
X	    if (c == NL) { 		/* CR, LF */
X		*bp = NUL;		/* End the string */
X		if (echof) putchar(c);	/* Echo the typein */
X		np = bp;		/* Where to start next field. */
X		setatm(pp);		/* Copy this field to atom buffer. */
X		inword = 0;
X		return(cmflgs = 1);
X	    }
X	    if (!ignore && (c == '?')) { /* Question mark */
X		putchar(c);
X		*bp = NUL;
X		setatm(pp);
X		return(cmflgs = 3);
X    	    }
X	    if (c == ESC) { 		/* ESC */
X		*bp = NUL;
X		setatm(pp);
X		return(cmflgs = 2);
X	    }
X	    if (c == BS || c == RUB) { 	/* Character deletion */
X		if (bp > cmdbuf) {	/* If still in buffer... */
X		    printf("\b \b");	/* erase character from screen, */
X		    bp--;		/* point behind it, */
X		    if (*bp == SP) inword = 0; /* Flag if current field gone */
X		    *bp = NUL;		/* Erase character from buffer. */
X		} else {		/* Otherwise, */
X		    putchar(BEL);	/* beep, */
X		    cmres();		/* and start parsing a new command. */
X		}
X		if (pp < bp) continue;
X		else return(cmflgs = -1);
X	    }
X	    if (c == LDEL) { 		/* ^U, line deletion */
X	    	while ((bp--) > cmdbuf) {
X	    	    printf("\b \b");
X		    *bp = NUL;
X		}
X		cmres();		/* Restart the command. */
X		inword = 0;
X		return(cmflgs = -2);
X	    }
X
X/* cont'd... */
X
X/* ...getwd(), cont'd */
X
X    	    if (c == WDEL) { 		/* ^W, word deletion */
X	    	if (bp <= cmdbuf) {	/* Beep if nothing to delete */
X		    putchar(BEL);
X		    cmres();
X		    return(cmflgs = -1);
X		}
X		bp--;
X		for ( ; (bp >= cmdbuf) && (*bp == SP) ; bp--) {
X		    printf("\b \b");
X		    *bp = NUL;
X		}
X		for ( ; (bp >= cmdbuf) && (*bp != SP) ; bp--) {
X		    printf("\b \b");
X		    *bp = NUL;
X		}
X		*bp++ == NUL;
X		inword = 0;
X		return(cmflgs = -1);
X	    }
X	    if (c == RDIS) { 		/* ^R, redisplay */
X		*bp = NUL;
X		printf("\n%s%s",cmprom,cmdbuf);
X		continue;
X	    }
X    	}
X	if (echof) putchar(c);		/* If tty input, echo. */
X	inword = 1;			/* Flag we're in a word. */
X	quote = 0;			/* Turn off quote. */
X	*bp++ = c;			/* And deposit it. */
X    }					/* end of big while */
X    putchar(BEL);			/* Get here if... */
X    printf("\n?Buffer full\n");
X    return(cmflgs = -2);
X}
X
X/* Utility functions */
X
X/* A D D B U F  -- Add the string pointed to by cp to the command buffer  */
X
Xaddbuf(cp) char *cp; {
X    int len = 0;
X    while ((*cp != NUL) && (bp < cmdbuf+CMDBL)) {
X    	*bp++ = *cp++;			/* Copy and */
X    	len++;				/* count the characters. */
X    }	
X    *bp++ = SP;				/* Put a space at the end */
X    *bp = NUL;				/* Terminate with a null */
X    np = bp;				/* Update the next-field pointer */
X    return(len);			/* Return the length */
X}
X
X/*  S E T A T M  --  Deposit a string in the atom buffer  */
X
Xsetatm(cp) char *cp; {
X    char *ap;
X    cc = 0;
X    ap = atmbuf;
X    *ap = NUL;
X    while (*cp == SP) cp++;
X    while ((*cp != SP) && (*cp != NL) && (*cp != NUL)) {
X	*ap++ = *cp++;
X	cc++;
X    }
X    *ap++ = NUL;
X    return(cc);				/* Return length */
X}
X
X/*  D I G I T S  -- Verify that all the characters in line are digits  */
X
Xdigits(s) char *s; {
X    while (*s) {
X        if (!isdigit(*s)) return(0);
X        s++;
X    }
X    return(1);
X}
X
X/*  L O W E R  --  Lowercase a string  */
X
Xlower(s) char *s; {
X    int n = 0;
X    while (*s) {
X	if (isupper(*s)) *s = tolower(*s);
X	s++, n++;
X    }
X    return(n);
X}
X
X/*  T E S T  --  Bit test  */
X
Xtest(x,m) int x, m; { /*  Returns 1 if any bits from m are on in x, else 0  */
X    return((x & m) ? 1 : 0);
X}
!FUNKY!STUFF!
echo x - ckconu.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckconu.c
Xchar *connv = "Connect Command for Unix, V4.2(006) 5 March 85";
X
X/*  C K C O N U  --  Dumb terminal connection to remote system, for Unix  */
X/*
X This module should work under all versions of Unix.  It calls externally
X defined system-dependent functions for i/o, but depends upon the existence
X of the fork() function.
X
X Enhanced by H. Fischer to detect when child process (modem reader)
X reports that the communications line has been broken and hang up.
X Also enhanced to allow escaping from connect state to command
X interpreter, to allow sending/receiving without breaking connection.
X*/
X
X#include "ckermi.h"
X#include <signal.h>
X#include <setjmp.h>
X
X#ifndef SIGUSR1
X#define SIGUSR1 16
X#endif
X
Xextern int local, speed, escape, duplex, parity, flow, seslog, mdmtyp;
Xextern char ttname[], sesfil[];
X
Xint i, active;				/* Variables global to this module */
Xchar *chstr();
X
X#define LBUFL 100			/* Line buffer */
Xchar lbuf[LBUFL];
X
X/* Connect state parent/child communication signal handlers */
X
Xstatic jmp_buf env_con;			/* Envir ptr for connect errors */
X
Xstatic
Xconn_int() {				/* Modem read failure handler, */
X    longjmp(env_con,1);			/* notifies parent process to stop */
X}
X
X/*  C O N E C T  --  Perform terminal connection  */
X
Xconect() {
X    int pid, 			/* process id of child (modem reader) */
X	parent_id,		/* process id of parent (keyboard reader) */
X	n;
X    int c;			/* c is a character, but must be signed 
X				   integer to pass thru -1, which is the
X				   modem disconnection signal, and is
X				   different from the character 0377 */
X    char errmsg[50], *erp;
X
X	if (!local) {
X	    printf("Sorry, you must 'set line' first\n");
X	    return(-2);
X	}
X	if (speed < 0) {
X	    printf("Sorry, you must 'set speed' first\n");
X	    return(-2);
X        }
X	if ((escape < 0) || (escape > 0177)) {
X	    printf("Your escape character is not ASCII - %d\n",escape);
X	    return(-2);
X	}
X	if (ttopen(ttname,local,mdmtyp) < 0) {
X	    erp = errmsg;
X	    sprintf(erp,"Sorry, can't open %s",ttname);
X	    perror(errmsg);
X	    return(-2);
X    	}
X    	printf("Connecting thru %s, speed %d.\r\n",ttname,speed);
X	printf("The escape character is %s (%d).\r\n",chstr(escape),escape);
X	printf("Type the escape character followed by C to get back,\r\n");
X	printf("or followed by ? to see other options.\r\n");
X	if (seslog) printf("(Session logged to %s.)\r\n",sesfil);
X
X/* Condition console terminal and communication line */	    
X
X    	if (conbin(escape) < 0) {
X	    printf("Sorry, can't condition console terminal\n");
X	    return(-2);
X    	}
X	if (ttvt(speed,flow) < 0) {
X	    conres();
X	    printf("Sorry, Can't condition communication line\n");
X	    return(-2);
X    	}
X
X/* cont'd... */
X
X/* ...connect, cont'd */
X
X
X	parent_id = getpid();		/* get parent id for signalling */
X	pid = fork();			/* All ok, make a fork */
X	if (pid) {			
X	  active = 1;			/* This fork reads, sends keystrokes */
X	  if (!setjmp(env_con)) {	/* comm error in child process */
X	    signal(SIGUSR1,conn_int);	/* routine for child process exit */
X	    while (active) {
X		c = coninc(0) & 0177;
X		if (c == escape) {   	/* Look for escape char */
X		    c = coninc(0) & 0177;
X		    doesc(c);
X		} else {		/* Ordinary character */
X		    ttoc(dopar(c));	/* Send it out with desired parity */
X		    if (duplex) {	/* Half duplex? */
X			conoc(c);	/* Yes, also echo it. */
X			if (seslog) zchout(ZSFILE,c);	/* And maybe log it. */
X    	    	    }			
X		}
X	      }
X    	    }				/* come here on death of child */
X	    kill(pid,9);		/* Done, kill inferior. */
X	    wait(0);			/* Wait till gone. */
X	    conres();			/* Reset the console. */
X	    printf("C-Kermit Disconnected\n");
X	    return(0);
X
X	} else {			/* Inferior reads, prints port input */
X
X	    while (1) {			/* Fresh read, wait for a character */
X		if ((c = ttinc(0)) < 0) { /* Comm line hangup detected*/
X		    printf("\r\nC-Kermit: Communications line failure\r\n");
X		    kill(parent_id,SIGUSR1);	/* notify parent. */
X		    pause();		/* Wait to be killed by parent. */
X                }
X		c &= 0177;		/* Got a char, strip parity. */
X		conoc(c);		/* Put it on the screen. */
X		if (seslog) zchout(ZSFILE,c);	/* If logging, log it. */
X		n = ttchk();		/* Any more left in buffer? */
X		if (n > 0) {
X		    if (n > LBUFL) n = LBUFL;  /* Get them all at once. */
X		    if ((n = ttxin(n,lbuf)) > 0) {
X			for (i = 0; i < n; i++) lbuf[i] &= 0177;
X			conxo(n,lbuf);
X			if (seslog) zsoutx(ZSFILE,lbuf,n);
X		    }
X	    	}
X	    }
X    	}
X}
X
X/*  H C O N N E  --  Give help message for connect.  */
X
Xhconne() {
X    int c;
X    static char *hlpmsg[] = {"\
X\r\nC to close the connection, or:",
X"\r\n  S for status",
X"\r\n  ? for help",
X"\r\n  B to send a BREAK",
X"\r\n  0 to send a null",
X"\r\n escape character twice to send the escape character.\r\n\r\n",
X"" };
X
X    conola(hlpmsg);			/* Print the help message. */
X    conol("Command>");			/* Prompt for command. */
X    c = coninc(0);
X    conoc(c);				/* Echo it. */
X    conoll("");
X    c &= 0177;				/* Strip any parity. */
X    return(c);				/* Return it. */
X}
X
X
X/*  C H S T R  --  Make a printable string out of a character  */
X
Xchar *
Xchstr(c) int c; {
X    static char s[8];
X    char *cp = s;
X
X    if (c < SP) {
X	sprintf(cp,"CTRL-%c",ctl(c));
X    } else sprintf(cp,"'%c'\n",c);
X    cp = s;
X    return(cp);
X}
X
X/*  D O E S C  --  Process an escape character argument  */
X
Xdoesc(c) char c; {
X    int d;
X  
X    c &= 0177;
X    while (1) {
X	if (c == escape) {		/* Send escape character */
X	    d = dopar(c);
X	    ttoc(d);
X	    return;
X    	} else				/* Or else look it up below. */
X	    if (isupper(c)) c = tolower(c);
X
X	switch (c) {
X
X	case 'c':			/* Close connection */
X	case '\03':
X	    active = 0;
X	    conol("\r\n");
X	    return;
X
X	case 'b':			/* Send a BREAK */
X	case '\02':
X	    ttsndb();
X	    return;
X
X	case 's':			/* Status */
X	case '\023':
X	    conol("\r\nConnected thru ");
X	    conoll(ttname);
X	    if (seslog) {
X		conol(", logging to ");
X		conol(sesfil);
X            }
X	    return;
X
X	case '?':			/* Help */
X	    c = hconne();
X	    continue;
X
X	case '0':			/* Send a null */
X	    c = '\0';
X	    d = dopar(c);
X	    ttoc(d);
X	    return;
X
X	case SP:			/* Space, ignore */
X	    return;
X
X	default:			/* Other */
X	    conoc(BEL); 		/* Invalid esc arg, beep */
X	    return;
X    	}	    
X    }
X}    
!FUNKY!STUFF!
echo x - ckdial.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckdial.c
Xchar *dialv = "Dial Command for Unix, V0.0(005) 5 Mar 85";
X
X/*  C K D I A L  --  Dialing program for connection to remote system */
X
X/*
X This module should work under all versions of Unix.  It calls externally
X defined system-depended functions for i/o, but depends upon the existence
X of various modem control functions.
X
X Author: Herm Fischer, Litton Data Systems, Van Nuys CA (HFISCHER@USC-ECLB)
X*/
X
X#include "ckermi.h"
X#include <signal.h>
X#include <setjmp.h>
X#include "ckcmd.h"
X
Xextern int local, speed, flow, mdmtyp;
Xextern char ttname[], sesfil[];
X
X#define HAYES 1			/* for mdmtyp settings */
X#define VENTEL 2
X#define HAYESNV 3		/* internal use, non-verbal V0 setting */
X
Xstruct keytab mdmtab[] = {		/* Modem types for command parsing */
X    "direct",	0, 0,
X    "hayes",	HAYES, 0,
X    "ventel",	VENTEL, 0
X};
Xint nmdm = (sizeof(mdmtab) / sizeof(struct keytab));
X
X#define DIALING 4		/* for ttvt parameter */
X#define CONNECT 5
X
X#define CONNECTED 1		/* for completion status */
X#define FAILED	  2
X
Xstatic int tries = 0;
X
X#define LBUFL 100
Xstatic char lbuf[LBUFL];
Xstatic char *lbp;
X
Xstatic jmp_buf sjbuf;
X
Xstatic
Xtimerh() {			/* timer interrupt handler */
X    longjmp(sjbuf,1);
X}
X
Xstatic
Xstripp(s) char *s; {	/* parity stripper aid */
X    for ( ; *s ; *s++ &= 0177 );
X}
X
X/*  D I A L  --  Dial up the remote system */
X
Xdial(telnbr) char *telnbr; {
X
X    char c;
X    char *i;
X    int waitct, status;
X    char errmsg[50], *erp;
X    int augMdm;		/* mdmtyp with switch settings added */
X    int mdmEcho = 0;	/* assume modem does not echo */
X    int n;
X    int (*savAlrm)();	/* save incomming alarm function */
X
X	if (!mdmtyp) {
X	    printf("Sorry, you must 'set modem' first\n");
X	    return(-2);
X	}
X	augMdm = mdmtyp;	/* internal use, to add dialer switches info*/
X
X	if (!local) {
X	    printf("Sorry, you must 'set line' first\n");
X	    return(-2);
X	}
X	if (speed < 0) {
X	    printf("Sorry, you must 'set speed' first\n");
X	    return(-2);
X        }
X	if (ttopen(ttname,local,mdmtyp) < 0) {/* Open, no wait for carrier */
X	    erp = errmsg;
X	    sprintf(erp,"Sorry, can't open %s",ttname);
X	    perror(errmsg);
X	    return(-2);
X    	}
X/* cont'd... */
X					/* interdigit waits for tone dial */
X/* ...dial, cont'd */
X
X
X	waitct = 1*strlen(telnbr) ;	/* compute time to dial worst case */
X	switch (augMdm) {
X	    case HAYES:
X	    case HAYESNV:
X		waitct += 35;		/* dial tone + carrier waits + slop */
X		for (i=telnbr; *i; i++) if (*i == ',') waitct += 2;
X		break;
X	    case VENTEL:
X		waitct += 10;		/* guess actual time for dialtones */
X		waitct += 10;	/* ventel's apparent patience for carrier */
X		for (i=telnbr; *i; i++) if (*i == '%') waitct += 5;
X		break;
X	    }
X
X       printf("Dialing thru %s, speed %d, number %s.\r\n",ttname,speed,telnbr);
X       printf("The timeout for completing the call is %d seconds.\r\n",waitct);
X
X/* Condition console terminal and communication line */	    
X				/* place line into "clocal" dialing state */
X	if ( ttpkt(speed,DIALING) < 0 )  {
X	    printf("Sorry, Can't condition communication line\n");
X	    return(-2);
X    	}
X
X/* Put modem into command state */
X
X    savAlrm = signal(SIGALRM,timerh);
X    alarm(10);			/* give modem 10 seconds to wake up */
X    if (setjmp(sjbuf)) {
X	alarm(0);
X	signal(SIGALRM,savAlrm);	/* cancel timer */
X	ttpkt(speed,CONNECT);	/* cancel dialing state ioctl */
X	printf("Sorry, unable to complete dialed connection\r\n");
X	return(-2);
X	}
X    ttflui();			/* flush input buffer if any */
X
X#define OKAY 1			/* modem attention attempt status */
X#define IGNORE 2
X#define GOT_O -2
X#define GOT_A -3
X
Xswitch (augMdm) {
X    case HAYES:
X    case HAYESNV:
X	while(tries++ < 4) {
X	    ttol("AT\r",3);	/* signal for attention, look for response */
X	    status = 0;
X	    while ( status <= 0 ) {
X		switch (ttinc(0) & 0177) {
X		    case 'A':			/* echoing, ignore */
X			status = GOT_A;
X			break;
X		    case 'T':
X			if (status == GOT_A) {
X			    mdmEcho = 1;	/* expect echoing later */
X			    status = 0;
X			    break;
X			}
X			status = IGNORE;
X			break;
X		    case '\n':
X		    case '\r':
X			status = 0;
X			break;
X		    case '0':			/* numeric result code */
X			augMdm = HAYESNV;	/* nonverbal result codes */
X			status = OKAY;
X			break;
X		    case 'O':			/* maybe English result code*/
X			status = GOT_O;
X			break;
X		    case 'K':
X			if (status == GOT_O) {
X			    augMdm = HAYES;
X			    status = OKAY;
X			    break;
X			}			/* else its default anyway */
X		    default:
X			status = IGNORE;
X			break;
X		    }
X		}
X	    if (status == OKAY) break;
X	    if (status == IGNORE) ttflui();
X	    sleep(1);		/* wait before retrying */
X	}
X        if (status != 0) break;
X        printf("Sorry, can't initialize modem\n");
X	ttpkt(speed,CONNECT);		/* cancel dialing state ioctl */
X	alarm(0);
X	signal(SIGALRM,savAlrm);	/* cancel timer */
X        return(-2);
X
X/* cont'd... */
X					/* interdigit waits for tone dial */
X/* ...dial, cont'd */
X
X    case VENTEL:
X	ttoc('\r');		/* Put Ventel into command mode */
X	sleep(1);
X	ttoc('\r');
X	sleep(1);
X	ttoc('\r');
X	while( (ttinc(0) & 0177) != '$');
X	break;
X    }
X    alarm(0);			/* turn off alarm */
X    sleep(1);			/* give things settling time */
X
X		
X/* Dial the number */
X
Xswitch (augMdm) {
X    case HAYES:
X    case HAYESNV:		  
X        sprintf(lbuf,"AT DT %s\r",telnbr);
X	break;
X    case VENTEL:
X	sprintf(lbuf,"<K%s\r>",telnbr);
X	break;
X    }
X
X    alarm(waitct);		/* time to allow for connecting */
X    ttflui();			/* clear out stuff from waking modem up */
X    ttol(lbuf,strlen(lbuf));	/* send dialing string */
X
X/* cont'd... */
X					/* interdigit waits for tone dial */
X/* ...dial, cont'd */
X
X
X/* Check for connection */
X
X    status = 0;
X    while (status == 0) {
X      switch (augMdm) {
X	case HAYES:
X	    for (n = 0; n < LBUFL; n++) lbuf[n] = '\0';
X	    n = ttinl(lbuf,LBUFL,0,'\n');
X	    if (n > 2) {
X		lbp = lbuf;
X		while ((*lbp == '\r') || (*lbp == '\n')) lbp++;
X		stripp(lbp);
X		if (strncmp(lbp,"CONNECT",7) == 0) status = CONNECTED;
X		if (strncmp(lbp,"NO CARRIER",10) == 0) status = FAILED;
X	    }
X	    break;
X
X	case HAYESNV:
X	    c = ttinc(0) & 0177;
X	    if (mdmEcho) {		/* sponge up dialing string */
X		mdmEcho = c!='\r';	/* until return is echoed */
X		break;
X		}
X	    if (c == '1') status = CONNECTED;
X	    if (c == '3') status = FAILED;
X	    if (c == '5') status = CONNECTED;
X	    break;
X
X	case VENTEL:
X	    if ( (ttinc(0) & 0177) == '!') status = CONNECTED;
X	    break;
X	}
X    }
X	
X/* Place line into modem-control (non-clocal) state */
X
X    if (status == 0) printf("Sorry, Can't get response from modem\r\n");
X    else if (status == CONNECTED) printf("Connected!\r\n");
X    else if (status == FAILED) printf("Sorry, No Carrier\r\n");
X    else printf("Failed to complete call\r\n");
X
X    ttpkt(speed,CONNECT);	/* cancel dialing state ioctl */
X    alarm(0);
X    signal(SIGALRM,savAlrm);	/* cancel timer */
X    return((status==CONNECTED) ? 0 : -2);
X}
!FUNKY!STUFF!

oz@yetti.UUCP (Ozan Yigit) (03/24/85)

Problem: C-kermit (release 4.n) "help set" or "help remote" commands
	 will core dump on Pro VENIX due to the size limitation
	 on the printf format string. (These help commands use the
	 help string as the format string, instead of printf("%s",str)).

File:	 ckusr2.c

Fix:	 Add the missing "%s" where necessary. 

Incl.:	 diff output for fixes

	 Oz
	 Dept. of Computer Science
	 York University

	 decvax{utzoo|utcs}!yetti!oz

-------- DIFF OUTPUT - C U T   H E R E ------------------------------
305c305
<         printf(hmhset);
---
>         printf("%s",hmhset);
312c312
<     printf(hmxychkt);
---
>     printf("%s",hmxychkt);
316c316
<     printf("\
---
>     printf("%s","\
321c321
<     printf("\
---
>     printf("%s","\
327c327
<     printf("\
---
>     printf("%s","\
333c333
<     printf("\
---
>     printf("%s","\
339,342c339,342
<     printf(hmxyf[0]);
<     printf(hmxyf[1]);
<     printf(hmxyf[2]);
<     printf(hmxyf[3]);
---
>     printf("%s",hmxyf[0]);
>     printf("%s",hmxyf[1]);
>     printf("%s",hmxyf[2]);
>     printf("%s",hmxyf[3]);
346c346
<     printf("\
---
>     printf("%s","\
352c352
<     printf("\
---
>     printf("%s","\
358c358
<     printf("\
---
>     printf("%s","\
366c366
< 	printf("\
---
> 	printf("%s","\
368c368
<     printf("\
---
>     printf("%s","\
372c372
<     printf("\
---
>     printf("%s","\
379c379
<     printf("\
---
>     printf("%s","\
385c385
<     printf("\
---
>     printf("%s","\
392c392
<     printf("\
---
>     printf("%s","\
397c397
<     printf("Decimal ASCII value of inbound padding character, normally 0.\n");
---
>     printf("%s","Decimal ASCII value of inbound padding character, normally 0.\n");
401c401
<     printf("\
---
>     printf("%s","\
407c407
<     printf("Prompt string for this program, normally 'C-Kermit>'.\n");
---
>     printf("%s","Prompt string for this program, normally 'C-Kermit>'.\n");
411c411
<     printf("\
---
>     printf("%s","\
418c418
<     printf("\
---
>     printf("%s","\
433,434c433,434
< 	printf(hmhrmt[0]);
< 	printf(hmhrmt[1]);
---
> 	printf("%s",hmhrmt[0]);
> 	printf("%s",hmhrmt[1]);