applied 040_pam-password-prompting.patch
authorIan Beckwith <ianb@erislabs.net>
Wed, 12 May 2010 00:20:52 +0000 (01:20 +0100)
committerIan Beckwith <ianb@erislabs.net>
Wed, 12 May 2010 00:20:52 +0000 (01:20 +0100)
.pc/040_pam-password-prompting.patch/.timestamp [new file with mode: 0644]
.pc/040_pam-password-prompting.patch/ckufio.c [new file with mode: 0644]
.pc/040_pam-password-prompting.patch/ckuus7.c [new file with mode: 0644]
.pc/applied-patches
ckufio.c
ckuus7.c

diff --git a/.pc/040_pam-password-prompting.patch/.timestamp b/.pc/040_pam-password-prompting.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/040_pam-password-prompting.patch/ckufio.c b/.pc/040_pam-password-prompting.patch/ckufio.c
new file mode 100644 (file)
index 0000000..298c48e
--- /dev/null
@@ -0,0 +1,8310 @@
+/* C K U F I O  --  Kermit file system support for UNIX, Aegis, and Plan 9 */
+
+#define CK_NONBLOCK                     /* See zoutdump() */
+
+#ifdef aegis
+char *ckzv = "Aegis File support, 8.0.200, 4 Mar 2004";
+#else
+#ifdef Plan9
+char *ckzv = "Plan 9 File support, 8.0.200, 4 Mar 2004";
+#else
+char *ckzv = "UNIX File support, 8.0.200, 4 Mar 2004";
+#endif /* Plan9 */
+#endif /* aegis */
+/*
+  Author: Frank da Cruz <fdc@columbia.edu>,
+  Columbia University Academic Information Systems, New York City,
+  and others noted in the comments below.  Note: CUCCA = Previous name of
+  Columbia University Academic Information Systems.
+
+  Copyright (C) 1985, 2004,
+    Trustees of Columbia University in the City of New York.
+    All rights reserved.  See the C-Kermit COPYING.TXT file or the
+    copyright text in the ckcmai.c module for disclaimer and permissions.
+*/
+
+/*
+  NOTE TO CONTRIBUTORS: This file, and all the other C-Kermit files, must be
+  compatible with C preprocessors that support only #ifdef, #else, #endif,
+  #define, and #undef.  Please do not use #if, logical operators, or other
+  preprocessor features in any of the portable C-Kermit modules.  You can,
+  of course, use these constructions in platform-specific modules where you
+  know they are supported.
+*/
+/* Include Files */
+
+#ifdef MINIX2
+#define _MINIX
+#endif /* MINIX2 */
+
+#include "ckcsym.h"
+#include "ckcdeb.h"
+#include "ckcasc.h"
+
+#ifndef NOCSETS
+#include "ckcxla.h"
+#endif /* NOCSETS */
+
+#ifdef COMMENT
+/* This causes trouble in C-Kermit 8.0.  I don't remember the original */
+/* reason for this being here but it must have been needed at the time... */
+#ifdef OSF13
+#ifdef CK_ANSIC
+#ifdef _NO_PROTO
+#undef _NO_PROTO
+#endif /* _NO_PROTO */
+#endif /* CK_ANSIC */
+#endif /* OSF13 */
+#endif /* COMMENT */
+
+#include <errno.h>
+#include <signal.h>
+
+#ifdef MINIX2
+#undef MINIX
+#undef CKSYSLOG
+#include <limits.h>
+#include <time.h>
+#define NOFILEH
+#endif /* MINIX2 */
+
+#ifdef MINIX
+#include <limits.h>
+#include <sys/types.h>
+#include <time.h>
+#else
+#ifdef POSIX
+#include <limits.h>
+#else
+#ifdef SVR3
+#include <limits.h>
+#endif /* SVR3 */
+#endif /* POSIX */
+#endif /* MINIX */
+/*
+  Directory Separator macros, to allow this module to work with both UNIX and
+  OS/2: Because of ambiguity with the command line editor escape \ character,
+  the directory separator is currently left as / for OS/2 too, because the
+  OS/2 kernel also accepts / as directory separator.  But this is subject to
+  change in future versions to conform to the normal OS/2 style.
+*/
+#ifndef DIRSEP
+#define DIRSEP       '/'
+#endif /* DIRSEP */
+#ifndef ISDIRSEP
+#define ISDIRSEP(c)  ((c)=='/')
+#endif /* ISDIRSEP */
+
+#ifdef SDIRENT
+#define DIRENT
+#endif /* SDIRENT */
+
+#ifdef XNDIR
+#include <sys/ndir.h>
+#else /* !XNDIR */
+#ifdef NDIR
+#include <ndir.h>
+#else /* !NDIR, !XNDIR */
+#ifdef RTU
+#include "/usr/lib/ndir.h"
+#else /* !RTU, !NDIR, !XNDIR */
+#ifdef DIRENT
+#ifdef SDIRENT
+#include <sys/dirent.h>
+#else
+#include <dirent.h>
+#endif /* SDIRENT */
+#else
+#include <sys/dir.h>
+#endif /* DIRENT */
+#endif /* RTU */
+#endif /* NDIR */
+#endif /* XNDIR */
+
+#ifdef UNIX                             /* Pointer arg to wait() allowed */
+#define CK_CHILD                        /* Assume this is safe in all UNIX */
+#endif /* UNIX */
+
+extern int binary, recursive, stathack;
+#ifdef CK_CTRLZ
+extern int eofmethod;
+#endif /* CK_CTRLZ */
+
+#include <pwd.h>                        /* Password file for shell name */
+#ifdef CK_SRP
+#include <t_pwd.h>                      /* SRP Password file */
+#endif /* CK_SRP */
+
+#ifdef HPUX10_TRUSTED
+#include <hpsecurity.h>
+#include <prot.h>
+#endif /* HPUX10_TRUSTED */
+
+#ifdef COMMENT
+/* Moved to ckcdeb.h */
+#ifdef POSIX
+#define UTIMEH
+#else
+#ifdef HPUX9
+#define UTIMEH
+#endif /* HPUX9 */
+#endif /* POSIX */
+#endif /* COMMENT */
+
+#ifdef SYSUTIMEH                        /* <sys/utime.h> if requested,  */
+#include <sys/utime.h>                  /* for extra fields required by */
+#else                                   /* 88Open spec. */
+#ifdef UTIMEH                           /* or <utime.h> if requested */
+#include <utime.h>                      /* (SVR4, POSIX) */
+#ifndef BSD44
+#ifndef V7
+/* Not sure why this is here.  What it implies is that the code bracketed
+   by SYSUTIMEH is valid on all platforms on which we support time 
+   functionality.  But we know that is not true because the BSD44 and V7
+   platforms do not support sys/utime.h and the data structures which
+   are defined in them.  Now this worked before because prior to today's
+   changes the UTIMEH definition for BSD44 and V7 did not take place
+   until after SYSUTIMEH was defined.  It also would not have been a 
+   problem if the ordering of all the time blocks was consistent.  All but
+   one of the blocks were BSD44, V7, SYSUTIMEH, <OTHER>.  That one case
+   is where this problem was triggered.
+*/
+#define SYSUTIMEH                       /* Use this for both cases. */
+#endif /* V7 */
+#endif /* BSD44 */
+#endif /* UTIMEH */
+#endif /* SYSUTIMEH */
+
+#ifndef NOTIMESTAMP
+#ifdef POSIX
+#ifndef AS400
+#define TIMESTAMP
+#endif /* AS400 */
+#endif /* POSIX */
+
+#ifdef BSD44                            /* BSD 4.4 */
+#ifndef TIMESTAMP
+#define TIMESTAMP                       /* Can do file dates */
+#endif /* TIMESTAMP */
+#include <sys/time.h>
+#include <sys/timeb.h>
+
+#else  /* Not BSD44 */
+
+#ifdef BSD4                             /* BSD 4.3 and below */
+#define TIMESTAMP                       /* Can do file dates */
+#include <time.h>                       /* Need this */
+#include <sys/timeb.h>                  /* Need this if really BSD */
+
+#else  /* Not BSD 4.3 and below */
+
+#ifdef SVORPOSIX                        /* System V or POSIX */
+#ifndef TIMESTAMP
+#define TIMESTAMP
+#endif /* TIMESTAMP */
+#include <time.h>
+
+/* void tzset(); (the "void" type upsets some compilers) */
+#ifndef IRIX60
+#ifndef ultrix
+#ifndef CONVEX9
+/* ConvexOS 9.0, supposedly POSIX, has extern char *timezone(int,int) */
+#ifndef Plan9
+extern long timezone;
+#endif /* Plan9 */
+#endif /* CONVEX9 */
+#endif /* ultrix */
+#endif /* IRIX60 */
+#endif /* SVORPOSIX */
+#endif /* BSD4 */
+#endif /* BSD44 */
+
+#ifdef COHERENT
+#include <time.h>
+#endif /* COHERENT */
+
+/* Is `y' a leap year? */
+#define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
+
+/* Number of leap years from 1970 to `y' (not including `y' itself). */
+#define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
+
+#endif /* NOTIMESTAMP */
+
+#ifdef CIE
+#include <stat.h>                       /* File status */
+#else
+#include <sys/stat.h>
+#endif /* CIE */
+
+/* Macro to alleviate isdir() calls internal to this module */
+
+static struct stat STATBUF;
+#define xisdir(a) ((stat(a,&STATBUF)==-1)?0:(S_ISDIR(STATBUF.st_mode)?1:0))
+
+extern char uidbuf[];
+extern int xferlog;
+extern char * xferfile;
+int iklogopen = 0;
+static time_t timenow;
+
+#define IKSDMSGLEN CKMAXPATH+512
+
+static char iksdmsg[IKSDMSGLEN];
+
+extern int local;
+
+extern int server, en_mkd, en_cwd, en_del;
+
+/*
+  Functions (n is one of the predefined file numbers from ckcker.h):
+
+   zopeni(n,name)   -- Opens an existing file for input.
+   zopeno(n,name,attr,fcb) -- Opens a new file for output.
+   zclose(n)        -- Closes a file.
+   zchin(n,&c)      -- Gets the next character from an input file.
+   zsinl(n,&s,x)    -- Read a line from file n, max len x, into address s.
+   zsout(n,s)       -- Write a null-terminated string to output file, buffered.
+   zsoutl(n,s)      -- Like zsout, but appends a line terminator.
+   zsoutx(n,s,x)    -- Write x characters to output file, unbuffered.
+   zchout(n,c)      -- Add a character to an output file, unbuffered.
+   zchki(name)      -- Check if named file exists and is readable, return size.
+   zchko(name)      -- Check if named file can be created.
+   zchkspa(name,n)  -- Check if n bytes available to create new file, name.
+   znewn(name,s)    -- Make a new unique file name based on the given name.
+   zdelet(name)     -- Delete the named file.
+   zxpand(string)   -- Expands the given wildcard string into a list of files.
+   znext(string)    -- Returns the next file from the list in "string".
+   zxrewind()       -- Rewind zxpand list.
+   zxcmd(n,cmd)     -- Execute the command in a lower fork on file number n.
+   zclosf()         -- Close input file associated with zxcmd()'s lower fork.
+   zrtol(n1,n2)     -- Convert remote filename into local form.
+   zltor(n1,n2)     -- Convert local filename into remote form.
+   zchdir(dirnam)   -- Change working directory.
+   zhome()          -- Return pointer to home directory name string.
+   zkself()         -- Kill self, log out own job.
+   zsattr(struct zattr *) -- Return attributes for file which is being sent.
+   zstime(f, struct zattr *, x) - Set file creation date from attribute packet.
+   zrename(old, new) -- Rename a file.
+   zcopy(source,destination) -- Copy a file.
+   zmkdir(path)       -- Create the directory path if possible
+   zfnqfp(fname,len,fullpath) - Determine full path for file name.
+   zgetfs(name)     -- return file size regardless of accessibility
+   zchkpid(pid)     -- tell if PID is valid and active
+*/
+
+/* Kermit-specific includes */
+/*
+  Definitions here supersede those from system include files.
+  ckcdeb.h is included above.
+*/
+#include "ckcker.h"                     /* Kermit definitions */
+#include "ckucmd.h"                     /* For keyword tables */
+#include "ckuver.h"                     /* Version herald */
+
+char *ckzsys = HERALD;
+
+/*
+  File access checking ...  There are two calls to access() in this module.
+  If this program is installed setuid or setgid on a Berkeley-based UNIX
+  system that does NOT incorporate the saved-original-effective-uid/gid
+  feature, then, when we have swapped the effective and original uid/gid,
+  access() fails because it uses what it thinks are the REAL ids, but we have
+  swapped them.  This occurs on systems where ANYBSD is defined, NOSETREU
+  is NOT defined, and SAVEDUID is NOT defined.  So, in theory, we should take
+  care of this situation like so:
+
+    ifdef ANYBSD
+    ifndef NOSETREU
+    ifndef SAVEDUID
+    define SW_ACC_ID
+    endif
+    endif
+    endif
+
+  But we can't test such a general scheme everywhere, so let's only do this
+  when we know we have to...
+*/
+#ifdef NEXT                             /* NeXTSTEP 1.0-3.0 */
+#define SW_ACC_ID
+#endif /* NEXT */
+
+/* Support for tilde-expansion in file and directory names */
+
+#ifdef POSIX
+#define NAMEENV "LOGNAME"
+#else
+#ifdef BSD4
+#define NAMEENV "USER"
+#else
+#ifdef ATTSV
+#define NAMEENV "LOGNAME"
+#endif /* ATTSV */
+#endif /* BSD4 */
+#endif /* POSIX */
+
+/* Berkeley Unix Version 4.x */
+/* 4.1bsd support from Charles E Brooks, EDN-VAX */
+
+#ifdef BSD4
+#ifdef MAXNAMLEN
+#define BSD42
+#endif /* MAXNAMLEN */
+#endif /* BSD4 */
+
+/* Definitions of some system commands */
+
+char *DELCMD = "rm -f ";                /* For file deletion */
+char *CPYCMD = "cp ";                   /* For file copy */
+char *RENCMD = "mv ";                   /* For file rename */
+char *PWDCMD = "pwd ";                  /* For saying where I am */
+
+#ifdef COMMENT
+#ifdef HPUX10
+char *DIRCMD = "/usr/bin/ls -l ";       /* For directory listing */
+char *DIRCM2 = "/usr/bin/ls -l ";       /* For directory listing, no args */
+#else
+char *DIRCMD = "/bin/ls -l ";           /* For directory listing */
+char *DIRCM2 = "/bin/ls -l ";           /* For directory listing, no args */
+#endif /* HPUX10 */
+#else
+char *DIRCMD = "ls -l ";                /* For directory listing */
+char *DIRCM2 = "ls -l ";                /* For directory listing, no args */
+#endif /* COMMENT */
+
+char *TYPCMD = "cat ";                  /* For typing a file */
+
+#ifdef HPUX
+char *MAILCMD = "mailx";                /* For sending mail */
+#else
+#ifdef DGUX540
+char *MAILCMD = "mailx";
+#else
+#ifdef UNIX
+#ifdef CK_MAILCMD
+char *MAILCMD = CK_MAILCMD;            /* CFLAGS override */
+#else
+char *MAILCMD = "Mail";                        /* Default */
+#endif /* CK_MAILCMD */
+#else
+char *MAILCMD = "";
+#endif /* UNIX */
+#endif /* HPUX */
+#endif /* DGUX40 */
+
+#ifdef UNIX
+#ifdef ANYBSD                           /* BSD uses lpr to spool */
+#ifdef DGUX540                          /* And DG/UX */
+char * PRINTCMD = "lp";
+#else
+char * PRINTCMD = "lpr";
+#endif /* DGUX540 */
+#else                                   /* Sys V uses lp */
+#ifdef TRS16                            /* except for Tandy-16/6000... */
+char * PRINTCMD = "lpr";
+#else
+char * PRINTCMD = "lp";
+#endif /* TRS16 */
+#endif /* ANYBSD */
+#else  /* Not UNIX */
+#define PRINTCMD ""
+#endif /* UNIX */
+
+#ifdef FT18                             /* Fortune For:Pro 1.8 */
+#undef BSD4
+#endif /* FT18 */
+
+#ifdef BSD4
+char *SPACMD = "pwd ; df .";            /* Space in current directory */
+#else
+#ifdef FT18
+char *SPACMD = "pwd ; du ; df .";
+#else
+char *SPACMD = "df ";
+#endif /* FT18 */
+#endif /* BSD4 */
+
+char *SPACM2 = "df ";                   /* For space in specified directory */
+
+#ifdef FT18
+#define BSD4
+#endif /* FT18 */
+
+#ifdef BSD4
+char *WHOCMD = "finger ";
+#else
+char *WHOCMD = "who ";
+#endif /* BSD4 */
+
+/* More system-dependent includes, which depend on symbols defined */
+/* in the Kermit-specific includes.  Oh what a tangled web we weave... */
+
+#ifdef COHERENT                         /* <sys/file.h> */
+#define NOFILEH
+#endif /* COHERENT */
+
+#ifdef MINIX
+#define NOFILEH
+#endif /* MINIX */
+
+#ifdef aegis
+#define NOFILEH
+#endif /* aegis */
+
+#ifdef unos
+#define NOFILEH
+#endif /* unos */
+
+#ifndef NOFILEH
+#include <sys/file.h>
+#endif /* NOFILEH */
+
+#ifndef is68k                           /* Whether to include <fcntl.h> */
+#ifndef BSD41                           /* All but a couple UNIXes have it. */
+#ifndef FT18
+#ifndef COHERENT
+#include <fcntl.h>
+#endif /* COHERENT */
+#endif /* FT18  */
+#endif /* BSD41 */
+#endif /* is68k */
+
+#ifdef COHERENT
+#ifdef _I386
+#include <fcntl.h>
+#else
+#include <sys/fcntl.h>
+#endif /* _I386 */
+#endif /* COHERENT */
+
+extern int inserver;                   /* I am IKSD */
+int guest = 0;                          /* Anonymous user */
+
+#ifdef IKSD
+extern int isguest;
+extern char * anonroot;
+#endif /* IKSD */
+
+#ifdef CK_LOGIN
+#define GUESTPASS 256
+static char guestpass[GUESTPASS] = { NUL, NUL }; /* Anonymous "password" */
+static int logged_in = 0;               /* Set when user is logged in */
+static int askpasswd = 0;               /* Have OK user, must ask for passwd */
+#endif /* CK_LOGIN */
+
+#ifdef CKROOT
+static char ckroot[CKMAXPATH+1] = { NUL, NUL };
+static int ckrootset = 0;
+int ckrooterr = 0;
+#endif /* CKROOT */
+
+_PROTOTYP( VOID ignorsigs, (void) );
+_PROTOTYP( VOID restorsigs, (void) );
+
+/*
+  Change argument to "(const char *)" if this causes trouble.
+  Or... if it causes trouble, then maybe it was already declared
+  in a header file after all, so you can remove this prototype.
+*/
+#ifndef NDGPWNAM /* If not defined No Declare getpwnam... */
+#ifndef _POSIX_SOURCE
+#ifndef NEXT
+#ifndef SVR4
+/* POSIX <pwd.h> already gave prototypes for these. */
+#ifdef IRIX40
+_PROTOTYP( struct passwd * getpwnam, (const char *) );
+#else
+#ifdef IRIX51
+_PROTOTYP( struct passwd * getpwnam, (const char *) );
+#else
+#ifdef M_UNIX
+_PROTOTYP( struct passwd * getpwnam, (const char *) );
+#else
+#ifdef HPUX9
+_PROTOTYP( struct passwd * getpwnam, (const char *) );
+#else
+#ifdef HPUX10
+_PROTOTYP( struct passwd * getpwnam, (const char *) );
+#else
+#ifdef DCGPWNAM
+_PROTOTYP( struct passwd * getpwnam, (const char *) );
+#else
+_PROTOTYP( struct passwd * getpwnam, (char *) );
+#endif /* DCGPWNAM */
+#endif /* HPUX10 */
+#endif /* HPUX9 */
+#endif /* M_UNIX */
+#endif /* IRIX51 */
+#endif /* IRIX40 */
+#ifndef SUNOS4
+#ifndef HPUX9
+#ifndef HPUX10
+#ifndef _SCO_DS
+_PROTOTYP( struct passwd * getpwuid, (PWID_T) );
+#endif /* _SCO_DS */
+#endif /* HPUX10 */
+#endif /* HPUX9 */
+#endif /* SUNOS4 */
+_PROTOTYP( struct passwd * getpwent, (void) );
+#endif /* SVR4 */
+#endif /* NEXT */
+#endif /* _POSIX_SOURCE */
+#endif /* NDGPWNAM */
+
+#ifdef CK_SHADOW                        /* Shadow Passwords... */
+#include <shadow.h>
+#endif /* CK_SHADOW */
+#ifdef CK_PAM                           /* PAM... */
+#include <security/pam_appl.h>
+#ifndef PAM_SERVICE_TYPE                /* Defines which PAM service we are */
+#define PAM_SERVICE_TYPE "kermit"
+#endif /* PAM_SERVICE_TYPE */
+
+#ifdef SOLARIS
+#define PAM_CONST 
+#else /* SOLARIS */
+#define PAM_CONST CONST
+#endif 
+
+static char * pam_pw = NULL;
+
+int
+#ifdef CK_ANSIC
+pam_cb(int num_msg,
+       PAM_CONST struct pam_message **msg,
+       struct pam_response **resp,
+       void *appdata_ptr
+       )
+#else /* CK_ANSIC */
+pam_cb(num_msg, msg, resp, appdata_ptr)
+    int num_msg;
+    PAM_CONST struct pam_message **msg;
+    struct pam_response **resp;
+    void *appdata_ptr;
+#endif /* CK_ANSIC */
+{
+    int i;
+
+    debug(F111,"pam_cb","num_msg",num_msg);
+
+    for (i = 0; i < num_msg; i++) {
+        char message[PAM_MAX_MSG_SIZE];
+
+        /* Issue prompt and get response */
+        debug(F111,"pam_cb","Message",i);
+        debug(F111,"pam_cb",msg[i]->msg,msg[i]->msg_style);
+        if (msg[i]->msg_style == PAM_ERROR_MSG) {
+            debug(F111,"pam_cb","PAM ERROR",0);
+            fprintf(stdout,"%s\n", msg[i]->msg);
+            return(0);
+        } else if (msg[i]->msg_style == PAM_TEXT_INFO) {
+            debug(F111,"pam_cb","PAM TEXT INFO",0);
+            fprintf(stdout,"%s\n", msg[i]->msg);
+            return(0);
+        } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) {
+            debug(F111,"pam_cb","Reading response, no echo",0);
+            /* Ugly hack.  We check to see if a password has been pushed */
+            /* into zvpasswd().  This would be true if the password was  */
+            /* received by REMOTE LOGIN.                                 */
+            if (pam_pw) {
+                ckstrncpy(message,pam_pw,PAM_MAX_MSG_SIZE);
+            } else
+                readpass((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
+        } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON) {
+            debug(F111,"pam_cb","Reading response, with echo",0);
+            readtext((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
+        } else {
+            debug(F111,"pam_cb","unknown style",0);
+            return(0);
+        }
+
+        /* Allocate space for this message's response structure */
+        resp[i] = (struct pam_response *) malloc(sizeof (struct pam_response));
+        if (!resp[i]) {
+            int j;
+            debug(F110,"pam_cb","malloc failure",0);
+            for (j = 0; j < i; j++) {
+                free(resp[j]->resp);
+                free(resp[j]);
+            }
+            return(0);
+        }
+
+        /* Allocate a buffer for the response */
+        resp[i]->resp = (char *) malloc((int)strlen(message) + 1);
+        if (!resp[i]->resp) {
+            int j;
+            debug(F110,"pam_cb","malloc failure",0);
+            for (j = 0; j < i; j++) {
+                free(resp[j]->resp);
+                free(resp[j]);
+            }
+            free(resp[i]);
+            return(0);
+        }
+        /* Return the results back to PAM */
+        strcpy(resp[i]->resp, message);        /* safe (prechecked) */
+        resp[i]->resp_retcode = 0;
+    }
+    debug(F110,"pam_cb","Exiting",0);
+    return(0);
+}
+#endif /* CK_PAM */
+
+/* Define macros for getting file type */
+
+#ifdef OXOS
+/*
+  Olivetti X/OS 2.3 has S_ISREG and S_ISDIR defined
+  incorrectly, so we force their redefinition.
+*/
+#undef S_ISREG
+#undef S_ISDIR
+#endif /* OXOS */
+
+#ifdef UTSV                             /* Same deal for Amdahl UTSV */
+#undef S_ISREG
+#undef S_ISDIR
+#endif /* UTSV */
+
+#ifdef UNISYS52                         /* And for UNISYS UTS V 5.2 */
+#undef S_ISREG
+#undef S_ISDIR
+#endif /* UNISYS52 */
+
+#ifdef ICLSVR3                          /* And for old ICL versions */
+#undef S_ISREG
+#undef S_ISDIR
+#endif /* ICLSVR3 */
+
+#ifdef ISDIRBUG                         /* Also allow this from command line */
+#ifdef S_ISREG
+#undef S_ISREG
+#endif /* S_ISREG */
+#ifdef S_ISDIR
+#undef S_ISDIR
+#endif /*  S_ISDIR */
+#endif /* ISDIRBUG */
+
+#ifndef _IFMT
+#ifdef S_IFMT
+#define _IFMT S_IFMT
+#else
+#define _IFMT 0170000
+#endif /* S_IFMT */
+#endif /* _IFMT */
+
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif /* S_ISREG */
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif /* S_ISDIR */
+
+/* The following mainly for NeXTSTEP... */
+
+#ifndef S_IWUSR
+#define S_IWUSR 0000200
+#endif /* S_IWUSR */
+
+#ifndef S_IRGRP
+#define S_IRGRP 0000040
+#endif /* S_IRGRP */
+
+#ifndef S_IWGRP
+#define S_IWGRP 0000020
+#endif /* S_IWGRP */
+
+#ifndef S_IXGRP
+#define S_IXGRP 0000010
+#endif /* S_IXGRP */
+
+#ifndef S_IROTH
+#define S_IROTH 0000004
+#endif /* S_IROTH */
+
+#ifndef S_IWOTH
+#define S_IWOTH 0000002
+#endif /* S_IWOTH */
+
+#ifndef S_IXOTH
+#define S_IXOTH 0000001
+#endif /* S_IXOTH */
+/*
+  Define maximum length for a file name if not already defined.
+  NOTE: This applies to a path segment (directory or file name),
+  not the entire path string, which can be CKMAXPATH bytes long.
+*/
+#ifdef QNX
+#ifdef _MAX_FNAME
+#define MAXNAMLEN _MAX_FNAME
+#else
+#define MAXNAMLEN 48
+#endif /* _MAX_FNAME */
+#else
+#ifndef MAXNAMLEN
+#ifdef sun
+#define MAXNAMLEN 255
+#else
+#ifdef FILENAME_MAX
+#define MAXNAMLEN FILENAME_MAX
+#else
+#ifdef NAME_MAX
+#define MAXNAMLEN NAME_MAX
+#else
+#ifdef _POSIX_NAME_MAX
+#define MAXNAMLEN _POSIX_NAME_MAX
+#else
+#ifdef _D_NAME_MAX
+#define MAXNAMLEN _D_NAME_MAX
+#else
+#ifdef DIRSIZ
+#define MAXNAMLEN DIRSIZ
+#else
+#define MAXNAMLEN 14
+#endif /* DIRSIZ */
+#endif /* _D_NAME_MAX */
+#endif /* _POSIX_NAME_MAX */
+#endif /* NAME_MAX */
+#endif /* FILENAME_MAX */
+#endif /* sun */
+#endif /* MAXNAMLEN */
+#endif /* QNX */
+
+#ifdef COMMENT
+/* As of 2001-11-03 this is handled in ckcdeb.h */
+/* Longest pathname ... */
+/*
+  Beware: MAXPATHLEN is one of UNIX's dirty little secrets.  Where is it
+  defined?  Who knows...  <param.h>, <mod.h>, <unistd.h>, <limits.h>, ...
+  There is not necessarily even a definition for it anywhere, or it might have
+  another name.  If you get it wrong, bad things happen with getcwd() and/or
+  getwd().  If you allocate a buffer that is too short, getwd() might write
+  over memory and getcwd() will fail with ERANGE.  The definitions of these
+  functions (e.g. in SVID or POSIX.1) do not tell you how to determine the
+  maximum path length in order to allocate a buffer that is the right size.
+*/
+#ifdef BSD44
+#include <sys/param.h>                  /* For MAXPATHLEN */
+#endif /* BSD44 */
+#ifdef COHERENT
+#include <sys/param.h>  /* for MAXPATHLEN, needed for -DDIRENT */
+#endif /* COHERENT */
+#endif /* COMMENT */
+
+#ifdef MAXPATHLEN
+#ifdef MAXPATH
+#undef MAXPATH
+#endif /* MAXPATH */
+#define MAXPATH MAXPATHLEN
+#else
+#ifdef PATH_MAX
+#define MAXPATH PATH_MAX
+#else
+#ifdef _POSIX_PATH_MAX
+#define MAXPATH _POSIX_PATH_MAX
+#else
+#ifdef BSD42
+#define MAXPATH 1024
+#else
+#ifdef SVR4
+#define MAXPATH 1024
+#else
+#define MAXPATH 255
+#endif /* SVR4 */
+#endif /* BSD42 */
+#endif /* _POSIX_PATH_MAX */
+#endif /* PATH_MAX */
+#endif /* MAXPATHLEN */
+
+/* Maximum number of filenames for wildcard expansion */
+
+#ifndef MAXWLD
+/* Already defined in ckcdeb.h so the following is superfluous. */
+/* Don't expect changing them to have any effect. */
+#ifdef CK_SMALL
+#define MAXWLD 50
+#else
+#ifdef BIGBUFOK
+#define MAXWLD 102400
+#else
+#define MAXWLD 8192
+#endif /* BIGBUFOK */
+#endif /* CK_SMALL */
+#endif /* MAXWLD */
+
+static int maxnames = MAXWLD;
+
+/* Define the size of the string space for filename expansion. */
+
+#ifndef DYNAMIC
+#ifdef PROVX1
+#define SSPACE 500
+#else
+#ifdef BSD29
+#define SSPACE 500
+#else
+#ifdef pdp11
+#define SSPACE 500
+#else
+#ifdef aegis
+#define SSPACE 10000                    /* Size of string-generating buffer */
+#else                                   /* Default static buffer size */
+#ifdef BIGBUFOK
+#define SSPACE 65000                    /* Size of string-generating buffer */
+#else
+#define SSPACE 2000                     /* size of string-generating buffer */
+#endif /* BIGBUFOK */
+#endif /* aegis */
+#endif /* pdp11 */
+#endif /* BSD29 */
+#endif /* PROVX1 */
+static char sspace[SSPACE];             /* Buffer for generating filenames */
+#else /* is DYNAMIC */
+#ifdef BIGBUFOK
+#define SSPACE 500000
+#else
+#define SSPACE 10000
+#endif /* BIGBUFOK */
+char *sspace = (char *)0;
+#endif /* DYNAMIC */
+static int ssplen = SSPACE;            /* Length of string space buffer */
+
+#ifdef DCLFDOPEN
+/* fdopen() needs declaring because it's not declared in <stdio.h> */
+_PROTOTYP( FILE * fdopen, (int, char *) );
+#endif /* DCLFDOPEN */
+
+#ifdef DCLPOPEN
+/* popen() needs declaring because it's not declared in <stdio.h> */
+_PROTOTYP( FILE * popen, (char *, char *) );
+#endif /* DCLPOPEN */
+
+extern int nopush;
+
+/* More internal function prototypes */
+/*
+ * The path structure is used to represent the name to match.
+ * Each slash-separated segment of the name is kept in one
+ * such structure, and they are linked together, to make
+ * traversing the name easier.
+ */
+struct path {
+    char npart[MAXNAMLEN+4];            /* name part of path segment */
+    struct path *fwd;                   /* forward ptr */
+};
+#ifndef NOPUSH
+_PROTOTYP( int shxpand, (char *, char *[], int ) );
+#endif /* NOPUSH */
+_PROTOTYP( static int fgen, (char *, char *[], int ) );
+_PROTOTYP( static VOID traverse, (struct path *, char *, char *) );
+_PROTOTYP( static VOID addresult, (char *, int) );
+#ifdef COMMENT
+/* Replaced by ckmatch() */
+_PROTOTYP( static int match, (char *, char *) );
+#endif /* COMMENT */
+_PROTOTYP( char * whoami, (void) );
+_PROTOTYP( UID_T real_uid, (void) );
+_PROTOTYP( static struct path *splitpath, (char *p) );
+_PROTOTYP( char * zdtstr, (time_t) );
+_PROTOTYP( time_t zstrdt, (char *, int) );
+
+/* Some systems define these symbols in include files, others don't... */
+
+#ifndef R_OK
+#define R_OK 4                          /* For access */
+#endif /* R_OK */
+
+#ifndef W_OK
+#define W_OK 2
+#endif /* W_OK */
+
+#ifndef X_OK
+#define X_OK 1
+#endif /* X_OK */
+
+#ifndef O_RDONLY
+#define O_RDONLY 000
+#endif /* O_RDONLY */
+
+/* syslog and wtmp items for Internet Kermit Service */
+
+extern char * clienthost;               /* From ckcmai.c. */
+
+static char fullname[CKMAXPATH+1];
+static char tmp2[CKMAXPATH+1];
+
+extern int ckxlogging;
+
+#ifdef CKXPRINTF                        /* Our printf macro conflicts with */
+#undef printf                           /* use of "printf" in syslog.h */
+#endif /* CKXPRINTF */
+#ifdef CKSYSLOG
+#ifdef RTAIX
+#include <sys/syslog.h>
+#else  /* RTAIX */
+#include <syslog.h>
+#endif /* RTAIX */
+#endif /* CKSYSLOG */
+#ifdef CKXPRINTF
+#define printf ckxprintf
+#endif /* CKXPRINTF */
+
+int ckxanon = 1;                        /* Anonymous login ok */
+int ckxperms = 0040;                    /* Anonymous file permissions */
+int ckxpriv = 1;                       /* Priv'd login ok */
+
+#ifndef XFERFILE
+#define XFERFILE "/var/log/iksd.log"
+#endif /* XFERFILE */
+
+/* wtmp logging for IKSD... */
+
+#ifndef CKWTMP                          /* wtmp logging not selected */
+int ckxwtmp = 0;                        /* Know this at runtime */
+#else                                   /* wtmp file details */
+int ckxwtmp = 1;
+#ifdef UTMPBUG                          /* Unfortunately... */
+/*
+  Some versions of Linux have a <utmp.h> file that contains
+  "enum utlogin { local, telnet, rlogin, screen, ... };"  This clobbers
+  any program that uses any of these words as variable names, function
+  names, macro names, etc.  (Other versions of Linux have this declaration
+  within #if 0 ... #endif.)  There is nothing we can do about this other
+  than to not include the stupid file.  But we need stuff from it, so...
+*/
+#include <features.h>
+#include <sys/types.h>
+#define UT_LINESIZE     32
+#define UT_NAMESIZE     32
+#define UT_HOSTSIZE     256
+
+struct timeval {
+  time_t tv_sec;
+  time_t tv_usec;
+};
+
+struct exit_status {
+  short int e_termination;      /* Process termination status.  */
+  short int e_exit;             /* Process exit status.  */
+};
+
+struct utmp {
+  short int ut_type;                    /* Type of login */
+  pid_t ut_pid;                         /* Pid of login process */
+  char ut_line[UT_LINESIZE];            /* NUL-terminated devicename of tty */
+  char ut_id[4];                        /* Inittab id */
+  char ut_user[UT_NAMESIZE];            /* Username (not NUL terminated) */
+
+  char ut_host[UT_HOSTSIZE];            /* Hostname for remote login */
+  struct exit_status ut_exit;           /* Exit status */
+  long ut_session;                      /* Session ID, used for windowing */
+  struct timeval ut_tv;                 /* Time entry was made */
+  int32_t ut_addr_v6[4];                /* Internet address of remote host */
+  char pad[20];                         /* Reserved */
+};
+
+#define ut_time ut_tv.tv_sec    /* Why should Linux be like anything else? */
+#define ut_name ut_user         /* ... */
+
+extern void
+logwtmp __P ((__const char *__ut_line, __const char *__ut_name,
+                          __const char *__ut_host));
+
+#else  /* Not UTMPBUG */
+
+#ifndef HAVEUTMPX                       /* Who has <utmpx.h> */
+#ifdef SOLARIS
+#define HAVEUTMPX
+#else
+#ifdef IRIX60
+#define HAVEUTMPX
+#else
+#ifdef CK_SCOV5
+#define HAVEUTMPX
+#else
+#ifdef HPUX100
+#define HAVEUTMPX
+#else
+#ifdef UNIXWARE
+#define HAVEUTMPX
+#endif /* UNIXWARE */
+#endif /* HPUX100 */
+#endif /* CK_SCOV5 */
+#endif /* IRIX60 */
+#endif /* SOLARIS */
+#endif /* HAVEUTMPX */
+#ifdef HAVEUTMPX
+#include <utmpx.h>
+#else
+#ifdef OSF50
+/* Because the time_t in the utmp struct is 64 bits but time() wants 32 */
+#define __V40_OBJ_COMPAT 1
+#endif /* OSF50 */
+#include <utmp.h>
+#ifdef OSF50
+#undef __V40_OBJ_COMPAT
+#endif /* OSF50 */
+#endif /* HAVEUTMPX */
+#endif /* UTMPBUG */
+
+#ifndef WTMPFILE
+#ifdef QNX
+#define WTMPFILE "/usr/adm/wtmp.1"
+#else
+#ifdef LINUX
+#define WTMPFILE "/var/log/wtmp"
+#else
+#define WTMPFILE "/usr/adm/wtmp"
+#endif /* QNX */
+#endif /* LINUX */
+#endif /* WTMPFILE */
+char * wtmpfile = NULL;
+
+static int wtmpfd = 0;
+static char cksysline[32] = { NUL, NUL };
+
+#ifndef HAVEUTHOST                      /* Does utmp include ut_host[]? */
+#ifdef HAVEUTMPX                        /* utmpx always does */
+#define HAVEUTHOST
+#else
+#ifdef LINUX                            /* Linux does */
+#define HAVEUTHOST
+#else
+#ifdef SUNOS4                           /* SunOS does */
+#define HAVEUTHOST
+#else
+#ifdef AIX41                            /* AIX 4.1 and later do */
+#define HAVEUTHOST
+#endif /* AIX41 */
+#endif /* SUNOS4 */
+#endif /* LINUX */
+#endif /* HAVEUTMPX */
+#endif /* HAVEUTHOST */
+
+#ifdef UW200
+PID_T _vfork() {                        /* To satisfy a library foulup */
+    return(fork());                     /* in Unixware 2.0.x */
+}
+#endif /* UW200 */
+
+VOID
+#ifdef CK_ANSIC
+logwtmp(const char * line, const char * name, const char * host)
+#else
+logwtmp(line, name, host) char *line, *name, *host;
+#endif /* CK_ANSIC */
+/* logwtmp */ {
+#ifdef HAVEUTMPX
+    struct utmpx ut;                    /* Needed for ut_host[] */
+#else
+    struct utmp ut;
+#endif /* HAVEUTMPX */
+    struct stat buf;
+    /* time_t time(); */
+
+    if (!ckxwtmp)
+      return;
+
+    if (!wtmpfile)
+      makestr(&wtmpfile,WTMPFILE);
+
+    if (!line) line = "";
+    if (!name) name = "";
+    if (!host) host = "";
+
+    if (!wtmpfd && (wtmpfd = open(wtmpfile, O_WRONLY|O_APPEND, 0)) < 0) {
+        ckxwtmp = 0;
+        debug(F110,"WTMP open failed",line,0);
+        return;
+    }
+    if (!fstat(wtmpfd, &buf)) {
+        ckstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
+        ckstrncpy(ut.ut_name, name, sizeof(ut.ut_name));
+#ifdef HAVEUTHOST
+        /* Not portable */
+        ckstrncpy(ut.ut_host, host, sizeof(ut.ut_host));
+#endif /* HAVEUTHOST */
+#ifdef HAVEUTMPX
+        time(&ut.ut_tv.tv_sec);
+#else
+#ifdef LINUX
+/* In light of the following comment perhaps the previous line should */
+/* be "#ifndef COMMENT". */
+        {
+            /*
+             * On 64-bit platforms sizeof(time_t) and sizeof(ut.ut_time)
+             * are not the same and attempt to use an address of
+             * ut.ut_time as an argument to time() call may cause
+             * "unaligned access" trap.
+             */
+            time_t zz;
+            time(&zz);
+            ut.ut_time = zz;
+        }
+#else
+        time(&ut.ut_time);
+#endif /* LINUX */
+#endif /* HAVEUTMPX */
+        if (write(wtmpfd, (char *)&ut, sizeof(struct utmp)) !=
+            sizeof(struct utmp)) {
+#ifndef NOFTRUNCATE
+#ifndef COHERENT
+            ftruncate(wtmpfd, buf.st_size); /* Error, undo any partial write */
+#else
+            chsize(wtmpfd, buf.st_size); /* Error, undo any partial write */
+#endif /* COHERENT */
+#endif /* NOFTRUNCATE */
+            debug(F110,"WTMP write error",line,0);
+        } else {
+            debug(F110,"WTMP record OK",line,0);
+            return;
+        }
+    }
+}
+#endif /* CKWTMP */
+
+#ifdef CKSYSLOG
+/*
+  C K S Y S L O G  --  C-Kermit system logging function,
+
+  For use by other modules.
+  This module can, but doesn't have to, use it.
+  Call with:
+    n = SYSLG_xx values defined in ckcdeb.h
+    s1, s2, s3: strings.
+*/
+VOID
+cksyslog(n, m, s1, s2, s3) int n, m; char * s1, * s2, * s3; {
+    int level;
+
+    if (!ckxlogging)                    /* syslogging */
+      return;
+    if (!s1) s1 = "";                   /* Fix null args */
+    if (!s2) s2 = "";
+    if (!s3) s3 = "";
+    switch (n) {                        /* Translate Kermit level */
+      case SYSLG_DB:                    /* to syslog level */
+        level = LOG_DEBUG;
+        break;
+      default:
+        level = m ? LOG_INFO : LOG_ERR;
+    }
+    debug(F110,"cksyslog s1",s1,0);
+    debug(F110,"cksyslog s2",s2,0);
+    debug(F110,"cksyslog s3",s3,0);
+    errno = 0;
+    syslog(level, "%s: %s %s", s1, s2, s3); /* Write syslog record */
+    debug(F101,"cksyslog errno","",errno);
+}
+#endif /* CKSYSLOG */
+
+
+/* Declarations */
+
+int maxnam = MAXNAMLEN;                 /* Available to the outside */
+int maxpath = MAXPATH;
+int ck_znewn = -1;
+
+#ifdef UNIX
+char startupdir[MAXPATH+1];
+#endif /* UNIX */
+
+int pexitstat = -2;                     /* Process exit status */
+
+FILE *fp[ZNFILS] = {                    /* File pointers */
+   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+/* Flags for each file indicating whether it was opened with popen() */
+int ispipe[ZNFILS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+/* Buffers and pointers used in buffered file input and output. */
+#ifdef DYNAMIC
+extern char *zinbuffer, *zoutbuffer;
+#else
+extern char zinbuffer[], zoutbuffer[];
+#endif /* DYNAMIC */
+extern char *zinptr, *zoutptr;
+extern int zincnt, zoutcnt;
+extern int wildxpand;
+
+static long iflen = -1L;                /* Input file length */
+
+static PID_T pid = 0;                   /* pid of child fork */
+static int fcount = 0;                  /* Number of files in wild group */
+static int nxpand = 0;                  /* Copy of fcount */
+static char nambuf[CKMAXPATH+4];        /* Buffer for a pathname */
+
+#ifndef NOFRILLS
+#define ZMBUFLEN 200
+static char zmbuf[ZMBUFLEN];           /* For mail, remote print strings */
+#endif /* NOFRILLS */
+
+char **mtchs = NULL;                    /* Matches found for filename */
+char **mtchptr = NULL;                  /* Pointer to current match */
+
+/*  Z K S E L F  --  Kill Self: log out own job, if possible.  */
+
+/* Note, should get current pid, but if your system doesn't have */
+/* getppid(), then just kill(0,9)...  */
+
+#ifndef SVR3
+#ifndef POSIX
+#ifndef OSFPC
+/* Already declared in unistd.h for SVR3 and POSIX */
+#ifdef CK_ANSIC
+extern PID_T getppid(void);
+#else
+#ifndef PS2AIX10
+#ifndef COHERENT
+extern PID_T getppid();
+#endif /* COHERENT */
+#endif /* PS2AIX10 */
+#endif /* CK_ANSIC */
+#endif /* OSFPC */
+#endif /* POSIX */
+#endif /* SVR3 */
+
+int
+zkself() {                              /* For "bye", but no guarantee! */
+#ifdef PROVX1
+    return(kill(0,9));
+#else
+#ifdef V7
+    return(kill(0,9));
+#else
+#ifdef TOWER1
+    return(kill(0,9));
+#else
+#ifdef FT18
+    return(kill(0,9));
+#else
+#ifdef aegis
+    return(kill(0,9));
+#else
+#ifdef COHERENT
+    return(kill((PID_T)getpid(),1));
+#else
+#ifdef PID_T
+    exit(kill((PID_T)getppid(),1));
+    return(0);
+#else
+    exit(kill(getppid(),1));
+    return(0);
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+}
+
+static VOID
+getfullname(name) char * name; {
+    char *p = (char *)fullname;
+    int len = 0;
+    fullname[0] = '\0';
+    /* If necessary we could also chase down symlinks here... */
+#ifdef COMMENT
+    /* This works but is incompatible with wuftpd */
+    if (isguest && anonroot) {
+        ckstrncpy(fullname,anonroot,CKMAXPATH);
+        len = strlen(fullname);
+        if (len > 0)
+          if (fullname[len-1] == '/')
+            len--;
+    }
+    p += len;
+#endif /* COMMENT */
+    zfnqfp(name, CKMAXPATH - len, p);
+    while (*p) {
+        if (*p < '!') *p = '_';
+        p++;
+    }
+}
+
+/*  D O I K L O G  --  Open Kermit-specific ftp-like transfer log. */
+
+VOID                                    /* Called in ckcmai.c */
+doiklog() {
+    if (iklogopen)                      /* Already open? */
+      return;
+    if (xferlog) {                      /* Open iksd log if requested */
+        if (!xferfile)                  /* If no pathname given */
+          makestr(&xferfile,XFERFILE); /* use this default */
+        if (*xferfile) {
+            xferlog = open(xferfile, O_WRONLY | O_APPEND | O_CREAT, 0660);
+            debug(F101,"doiklog open","",xferlog);
+            if (xferlog < 0) {
+#ifdef CKSYSLOG
+                syslog(LOG_ERR, "xferlog open failure %s: %m", xferfile);
+#endif /* CKSYSLOG */
+                debug(F101,"doiklog open errno","",errno);
+                xferlog = 0;
+            } else
+              iklogopen = 1;
+        } else
+          xferlog = 0;
+#ifdef CKSYSLOG
+        if (xferlog && ckxlogging)
+          syslog(LOG_INFO, "xferlog: %s open ok", xferfile);
+#endif /* CKSYSLOG */
+    }
+}
+
+/*  Z O P E N I  --  Open an existing file for input. */
+
+/* Returns 1 on success, 0 on failure */
+
+int
+zopeni(n,name) int n; char *name; {
+    int x;
+
+    debug(F111,"zopeni",name,n);
+    if ((x = chkfn(n)) != 0) {
+       debug(F111,"zopeni chkfn",ckitoa(n),x);
+       return(0);
+    }
+    zincnt = 0;                         /* Reset input buffer */
+    if (n == ZSYSFN) {                  /* Input from a system function? */
+#ifdef COMMENT
+/*** Note, this function should not be called with ZSYSFN ***/
+/*** Always call zxcmd() directly, and give it the real file number ***/
+/*** you want to use.  ***/
+        return(zxcmd(n,name));          /* Try to fork the command */
+#else
+        debug(F110,"zopeni called with ZSYSFN, failing!",name,0);
+        *nambuf = '\0';                 /* No filename. */
+        return(0);                      /* fail. */
+#endif /* COMMENT */
+    }
+    if (n == ZSTDIO) {                  /* Standard input? */
+        if (is_a_tty(0)) {
+            fprintf(stderr,"Terminal input not allowed");
+            debug(F110,"zopeni: attempts input from unredirected stdin","",0);
+            return(0);
+        }
+        fp[ZIFILE] = stdin;
+        ispipe[ZIFILE] = 0;
+        return(1);
+    }
+#ifdef CKROOT
+    debug(F111,"zopeni setroot",ckroot,ckrootset);
+    if (ckrootset) if (!zinroot(name)) {
+       debug(F110,"zopeni setroot violation",name,0);
+       return(0);
+    }
+#endif /* CKROOT */
+    fp[n] = fopen(name,"r");            /* Real file, open it. */
+    debug(F111,"zopeni fopen", name, fp[n]);
+#ifdef ZDEBUG
+    printf("ZOPENI fp[%d]=%ld\n",n,fp[n]);
+#endif /* ZDEBUG */
+    ispipe[n] = 0;
+
+    if (xferlog
+#ifdef CKSYSLOG
+        || ((ckxsyslog >= SYSLG_FA) && ckxlogging)
+#endif /* CKSYSLOG */
+        ) {
+        getfullname(name);
+        debug(F110,"zopeni fullname",fullname,0);
+    }
+    if (fp[n] == NULL) {
+#ifdef CKSYSLOG
+        if (ckxsyslog >= SYSLG_FA && ckxlogging) {
+           syslog(LOG_INFO, "file[%d] %s: open failed (%m)", n, fullname);
+           perror(fullname);
+       } else
+#endif /* CKSYSLOG */
+         perror(name);
+        return(0);
+    } else {
+#ifdef CKSYSLOG
+        if (ckxsyslog >= SYSLG_FA && ckxlogging)
+          syslog(LOG_INFO, "file[%d] %s: open read ok", n, fullname);
+#endif /* CKSYSLOG */
+        clearerr(fp[n]);
+        return(1);
+    }
+}
+
+#ifdef QNX
+#define DONDELAY
+#else
+#ifdef O_NDELAY
+#define DONDELAY
+#endif /* O_NDELAY */
+#endif /* QNX */
+
+/*  Z O P E N O  --  Open a new file for output.  */
+
+/*ARGSUSED*/   /* zz not used */
+int
+zopeno(n,name,zz,fcb)
+/* zopeno */  int n; char *name; struct zattr *zz; struct filinfo *fcb; {
+
+    char p[8];
+    int append = 0;
+
+/* As of Version 5A, the attribute structure and the file information */
+/* structure are included in the arglist. */
+
+#ifdef DEBUG
+    debug(F111,"zopeno",name,n);
+    if (fcb) {
+        debug(F101,"zopeno fcb disp","",fcb->dsp);
+        debug(F101,"zopeno fcb type","",fcb->typ);
+        debug(F101,"zopeno fcb char","",fcb->cs);
+    } else {
+        debug(F100,"zopeno fcb is NULL","",0);
+    }
+#endif /* DEBUG */
+
+    if (chkfn(n) != 0)                  /* Already open? */
+      return(0);                        /* Nothing to do. */
+
+    if ((n == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */
+        fp[ZOFILE] = stdout;
+        ispipe[ZOFILE] = 0;
+#ifdef COMMENT
+       /* This seems right but it breaks client server ops */
+       fp[n] = stdout;
+        ispipe[n] = 0;
+#endif /* COMMENT */
+#ifdef DEBUG
+        if (n != ZDFILE)
+          debug(F101,"zopeno fp[n]=stdout","",fp[n]);
+#endif /* DEBUG */
+        zoutcnt = 0;
+        zoutptr = zoutbuffer;
+        return(1);
+    }
+
+/* A real file.  Open it in desired mode (create or append). */
+
+#ifdef CKROOT
+    debug(F111,"zopeno setroot",ckroot,ckrootset);
+    if (ckrootset) if (!zinroot(name)) {
+       debug(F110,"zopeno setroot violation",name,0);
+       return(0);
+    }
+#endif /* CKROOT */
+
+    ckstrncpy(p,"w",8);                        /* Assume write/create mode */
+    if (fcb) {                          /* If called with an FCB... */
+        if (fcb->dsp == XYFZ_A) {       /* Does it say Append? */
+            ckstrncpy(p,"a",8);                /* Yes. */
+            debug(F100,"zopeno append","",0);
+            append = 1;
+        }
+    }
+
+    if (xferlog
+#ifdef CKSYSLOG
+        || ((ckxsyslog >= SYSLG_FC) && ckxlogging)
+#endif /* CKSYSLOG */
+        ) {
+        getfullname(name);
+        debug(F110,"zopeno fullname",fullname,0);
+    }
+    debug(F110,"zopeno fopen arg",p,0);
+    fp[n] = fopen(name,p);              /* Try to open the file */
+    ispipe[ZIFILE] = 0;
+
+#ifdef ZDEBUG
+    printf("ZOPENO fp[%d]=%ld\n",n,fp[n]);
+#endif /* ZDEBUG */
+
+    if (fp[n] == NULL) {                /* Failed */
+        debug(F101,"zopeno failed errno","",errno);
+#ifdef CKSYSLOG
+        if (ckxsyslog >= SYSLG_FC && ckxlogging)
+          syslog(LOG_INFO, "file[%d] %s: %s failed (%m)",
+                 n,
+                 fullname,
+                 append ? "append" : "create"
+                 );
+#endif /* CKSYSLOG */
+#ifdef COMMENT                          /* Let upper levels print message. */
+        perror("Can't open output file");
+#endif /* COMMENT */
+    } else {                            /* Succeeded */
+        extern int zofbuffer, zofblock, zobufsize;
+        debug(F101, "zopeno zobufsize", "", zobufsize);
+        if (n == ZDFILE || n == ZTFILE) { /* If debug or transaction log */
+            setbuf(fp[n],NULL);           /* make it unbuffered. */
+#ifdef DONDELAY
+        } else if (n == ZOFILE && !zofblock) { /* blocking or nonblocking */
+            int flags;
+            if ((flags = fcntl(fileno(fp[n]),F_GETFL,0)) > -1)
+              fcntl(fileno(fp[n]),F_SETFL, flags |
+#ifdef QNX
+                    O_NONBLOCK
+#else
+                    O_NDELAY
+#endif /* QNX */
+                    );
+            debug(F100,"zopeno ZOFILE nonblocking","",0);
+#endif /* DONDELAY */
+        } else if (n == ZOFILE && !zofbuffer) { /* buffered or unbuffered */
+            setbuf(fp[n],NULL);
+            debug(F100,"zopeno ZOFILE unbuffered","",0);
+        }
+
+#ifdef CK_LOGIN
+        /* Enforce anonymous file-creation permission */
+        if (isguest)
+          if (n == ZWFILE || n == ZMFILE ||
+              n == ZOFILE || n == ZDFILE ||
+              n == ZTFILE || n == ZPFILE ||
+              n == ZSFILE)
+            chmod(name,ckxperms);
+#endif /* CK_LOGIN */
+#ifdef CKSYSLOG
+        if (ckxsyslog >= SYSLG_FC && ckxlogging)
+          syslog(LOG_INFO, "file[%d] %s: %s ok",
+                 n,
+                 fullname,
+                 append ? "append" : "create"
+                 );
+#endif /* CKSYSLOG */
+        debug(F100, "zopeno ok", "", 0);
+    }
+    zoutcnt = 0;                        /* (PWP) reset output buffer */
+    zoutptr = zoutbuffer;
+    return((fp[n] != NULL) ? 1 : 0);
+}
+
+/*  Z C L O S E  --  Close the given file.  */
+
+/*  Returns 0 if arg out of range, 1 if successful, -1 if close failed.  */
+
+int
+zclose(n) int n; {
+    int x = 0, x2 = 0;
+    extern long ffc;
+
+    debug(F101,"zclose file number","",n);
+    if (chkfn(n) < 1) return(0);        /* Check range of n */
+    if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */
+      x2 = zoutdump();
+
+    if (fp[ZSYSFN] || ispipe[n]) {      /* If file is really pipe */
+#ifndef NOPUSH
+        x = zclosf(n);                  /* do it specially */
+#else
+        x = EOF;
+#endif /* NOPUSH */
+        debug(F101,"zclose zclosf","",x);
+        debug(F101,"zclose zclosf fp[n]","",fp[n]);
+    } else {
+        if ((fp[n] != stdout) && (fp[n] != stdin))
+          x = fclose(fp[n]);
+        fp[n] = NULL;
+#ifdef COMMENT
+       if (n == ZCTERM || n == ZSTDIO) /* See zopeno() */
+         if (fp[ZOFILE] == stdout)
+           fp[ZOFILE] = NULL;
+#endif /* COMMENT */
+    }
+    iflen = -1L;                        /* Invalidate file length */
+    if (x == EOF) {                     /* if we got a close error */
+        debug(F101,"zclose fclose fails","",x);
+        return(-1);
+    } else if (x2 < 0) {                /* or error flushing last buffer */
+        debug(F101,"zclose error flushing last buffer","",x2);
+        return(-1);                     /* then return an error */
+    } else {
+        /* Print log record compatible with wu-ftpd */
+        if (xferlog && (n == ZIFILE || n == ZOFILE)) {
+            char * s, *p;
+            extern char ttname[];
+            if (!iklogopen) (VOID) doiklog(); /* Open log if necessary */
+            debug(F101,"zclose iklogopen","",iklogopen);
+            if (iklogopen) {
+               int len;
+               char * fnam;
+
+                timenow = time(NULL);
+#ifdef CK_LOGIN
+                if (logged_in)
+                  s = clienthost;
+                else
+#endif /* CK_LOGIN */
+                  s = (char *)ttname;
+                if (!s) s = "";
+                if (!*s) s = "*";
+#ifdef CK_LOGIN
+                if (logged_in) {
+                    p = guestpass;
+                    if (!*p) p = "*";
+                } else
+#endif /* CK_LOGIN */
+                  p = whoami();
+
+               len = 24 + 12 + (int)strlen(s) + 16
+                 + (int)strlen(fullname) + 1 + 1 + 1 + 1
+                   + (int)strlen(p) + 6 + 2 + 12;
+               fnam = fullname;
+               if (!*fnam) fnam = "(pipe)";
+
+               if (len > IKSDMSGLEN)
+                 sprintf(iksdmsg,      /* SAFE */
+                        "%.24s [BUFFER WOULD OVERFLOW]\n",ctime(&timenow));
+               else
+                 sprintf(iksdmsg,      /* SAFE */
+                        "%.24s %d %s %ld %s %c %s %c %c %s %s %d %s\n",
+                        ctime(&timenow),        /* date/time */
+                        gtimer(),               /* elapsed secs */
+                        s,                      /* peer name */
+                        ffc,                    /* byte count */
+                        fnam,                  /* full pathname of file */
+                        (binary ? 'b' : 'a'),   /* binary or ascii */
+                        "_",                    /* options = none */
+                        n == ZIFILE ? 'o' : 'i', /* in/out */
+#ifdef CK_LOGIN
+                        (isguest ? 'a' : 'r'),  /* User type */
+#else
+                        'r',
+#endif /* CK_LOGIN */
+                        p,                      /* Username or guest passwd */
+#ifdef CK_LOGIN
+                        logged_in ? "iks" : "kermit", /* Record ID */
+#else
+                        "kermit",
+#endif /* CK_LOGIN */
+                        0,              /* User ID on client system unknown */
+                        "*"             /* Ditto */
+                        );
+                debug(F110,"zclose iksdmsg",iksdmsg,0);
+                write(xferlog, iksdmsg, (int)strlen(iksdmsg));
+            }
+        }
+        debug(F101,"zclose returns","",1);
+        return(1);
+    }
+}
+
+/*  Z C H I N  --  Get a character from the input file.  */
+
+/*  Returns -1 if EOF, 0 otherwise with character returned in argument  */
+
+int
+zchin(n,c) int n; int *c; {
+    int a;
+
+#ifdef IKSD
+    if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
+        a = coninc(0);
+        if (*c < 0)
+          return(-1);
+    } else
+#endif /* IKSD */
+    /* (PWP) Just in case this gets called when it shouldn't. */
+    if (n == ZIFILE) {
+        a = zminchar();                        /* Note: this catches Ctrl-Z */
+        if (a < 0)                     /* (See zinfill()...) */
+         return(-1);
+    } else {
+       a = getc(fp[n]);
+       if (a == EOF) return(-1);
+#ifdef CK_CTRLZ
+       /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
+       if (!binary && a == 0x1A && eofmethod == XYEOF_Z)
+         return(-1);
+#endif /* CK_CTRLZ */
+    }
+    *c = (CHAR) a & 0377;
+    return(0);
+}
+
+/*  Z S I N L  --  Read a line from a file  */
+
+/*
+  Writes the line into the address provided by the caller.
+  n is the Kermit "channel number".
+  Writing terminates when newline is encountered, newline is not copied.
+  Writing also terminates upon EOF or if length x is exhausted.
+  Returns 0 on success, -1 on EOF or error.
+*/
+int
+zsinl(n,s,x) int n, x; char *s; {
+    int a, z = 0;                       /* z is return code. */
+    int count = 0;
+    int len = 0;
+    char *buf;
+    extern CHAR feol;                   /* Line terminator */
+
+    if (!s || chkfn(n) < 1)             /* Make sure file is open, etc */
+      return(-1);
+    buf = s;
+    s[0] = '\0';                        /* Don't return junk */
+
+    a = -1;                             /* Current character, none yet. */
+    while (x--) {                       /* Up to given length */
+        int old = 0;
+        if (feol)                       /* Previous character */
+          old = a;
+        if (zchin(n,&a) < 0) {          /* Read a character from the file */
+            debug(F101,"zsinl zchin fail","",count);
+            if (count == 0)
+              z = -1;                   /* EOF or other error */
+            break;
+        } else
+          count++;
+        if (feol) {                     /* Single-character line terminator */
+            if (a == feol)
+              break;
+        } else {                        /* CRLF line terminator */
+            if (a == '\015')            /* CR, get next character */
+              continue;
+            if (old == '\015') {        /* Previous character was CR */
+                if (a == '\012') {      /* This one is LF, so we have a line */
+                    break;
+                } else {                /* Not LF, deposit CR */
+                    *s++ = '\015';
+                    x--;
+                    len++;
+                }
+            }
+        }
+        *s = a;                         /* Deposit character */
+        s++;
+        len++;
+    }
+    *s = '\0';                          /* Terminate the string */
+    debug(F011,"zsinl",buf,len);
+    return(z);
+}
+
+/*  Z X I N  --  Read x bytes from a file  */
+
+/*
+  Reads x bytes (or less) from channel n and writes them
+  to the address provided by the caller.
+  Returns number of bytes read on success, 0 on EOF or error.
+*/
+int
+zxin(n,s,x) int n, x; char *s; {
+#ifdef IKSD
+    if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
+        int a, i;
+        a = ttchk();
+        if (a < 1) return(0);
+        for (i = 0; i < a && i < x; i++)
+          s[i] = coninc(0);
+        return(i);
+    }
+#endif /* IKSD */
+
+    return(fread(s, sizeof (char), x, fp[n]));
+}
+
+/*
+  Z I N F I L L  --  Buffered file input.
+
+  (re)fill the file input buffer with data.  All file input
+  should go through this routine, usually by calling the zminchar()
+  macro defined in ckcker.h.  Returns:
+
+  Value 0..255 on success, the character that was read.
+  -1 on end of file.
+  -2 on any kind of error other than end of file.
+  -3 timeout when reading from pipe (Kermit packet mode only).
+*/
+int
+zinfill() {
+    extern int kactive, srvping;
+    errno = 0;
+
+#ifdef ZDEBUG
+    printf("ZINFILL fp[%d]=%ld\n",ZIFILE,fp[ZIFILE]);
+#endif /* ZDEBUG */
+
+#ifdef IKSD
+    if (inserver && !local && fp[ZIFILE] == stdin) {
+        int a, i;
+        a = ttchk();
+        if (a < 0) return(-2);
+        for (i = 0; i < a && i < INBUFSIZE; i++) {
+            zinbuffer[i] = coninc(0);
+        }
+        zincnt = i;
+        /* set pointer to beginning, (== &zinbuffer[0]) */
+        zinptr = zinbuffer;
+        if (zincnt == 0) return(-1);
+        zincnt--;                       /* One less char in buffer */
+        return((int)(*zinptr++) & 0377); /* because we return the first */
+    }
+#endif /* IKSD */
+
+    debug(F101,"zinfill kactive","",kactive);
+
+    if (!(kactive && ispipe[ZIFILE])) {
+        if (feof(fp[ZIFILE])) {
+            debug(F100,"ZINFILL feof","",0);
+#ifdef ZDEBUG
+            printf("ZINFILL EOF\n");
+#endif /* ZDEBUG */
+            return(-1);
+        }
+    }
+    clearerr(fp[ZIFILE]);
+
+#ifdef SELECT
+    /* Here we can call select() to get a timeout... */
+    if (kactive && ispipe[ZIFILE]) {
+        int secs, z = 0;
+#ifndef NOXFER
+        if (srvping) {
+            secs = 1;
+            debug(F101,"zinfill calling ttwait","",secs);
+            z = ttwait(fileno(fp[ZIFILE]),secs);
+            debug(F101,"zinfill ttwait","",z);
+        }
+#endif /* NOXFER */
+        if (z == 0)
+          return(-3);
+    }
+#endif /* SELECT */
+
+#ifdef DEBUG
+    if (deblog) {
+        int i;
+        debug(F101,"ZINFILL INBUFSIZE","",INBUFSIZE);
+#ifdef USE_MEMCPY
+        memset(zinbuffer, 0xFF, INBUFSIZE);
+#else
+        for (i = 0; i < INBUFSIZE; i++) {
+            zinbuffer[i] = 0xFF;
+#ifdef COMMENT                         /* Too much! */
+            debug(F101,"ZINFILL zinbuffer[i]","",i);
+#endif /* COMMENT */
+        }
+#endif /* USE_MEMCPY */
+       ckstrncpy(zinbuffer,"zinbuffer is a valid buffer",INBUFSIZE);
+       debug(F111,"ZINFILL about to call fread",zinbuffer,zinbuffer);
+    }
+#endif /* DEBUG */
+
+/*
+  Note: The following read MUST be nonblocking when reading from a pipe
+  and we want timeouts to work.  See zxcmd().
+*/
+    zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]);
+    debug(F101,"ZINFILL fread","",zincnt); /* Just the size */
+#ifdef ZDEBUG
+    printf("FREAD=%d\n",zincnt);
+#endif /* ZDEBUG */
+#ifdef CK_CTRLZ
+    /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
+    if (zincnt > 0 && !binary && eofmethod == XYEOF_Z) {
+       register int i;
+       for (i = 0; i < zincnt; i++) {
+           if (zinbuffer[i] == SUB) {
+               zincnt = i;             /* Stop at first Ctrl-Z */
+               if (i == 0)
+                 return(-1);
+               break;
+           }
+        }
+    }
+#endif /* CK_CTRLZ */
+
+    if (zincnt == 0) {                  /* Got nothing? */
+        if (ferror(fp[ZIFILE])) {
+            debug(F100,"ZINFILL ferror","",0);
+            debug(F101,"ZINFILL errno","",errno);
+#ifdef ZDEBUG
+            printf("ZINFILL errno=%d\n",errno);
+#endif /* ZDEBUG */
+#ifdef EWOULDBLOCK
+            return((errno == EWOULDBLOCK) ? -3 : -2);
+#else
+            return(-2);
+#endif /* EWOULDBLOCK */
+        }
+
+    /* In case feof() didn't work just above -- sometimes it doesn't... */
+
+        if (feof(fp[ZIFILE]) ) {
+            debug(F100,"ZINFILL count 0 EOF return -1","",0);
+            return (-1);
+        } else {
+            debug(F100,"ZINFILL count 0 not EOF return -2","",0);
+            return(-2);
+        }
+    }
+    zinptr = zinbuffer;    /* set pointer to beginning, (== &zinbuffer[0]) */
+    zincnt--;                           /* One less char in buffer */
+    return((int)(*zinptr++) & 0377);    /* because we return the first */
+}
+
+/*  Z S O U T  --  Write a string out to the given file, buffered.  */
+
+int
+zsout(n,s) int n; char *s; {
+    int rc = 0;
+    rc = chkfn(n);
+    if (rc < 1) return(-1);             /* Keep this, prevents memory faults */
+    if (!s) return(0);                  /* Null pointer, do nothing, succeed */
+    if (!*s) return(0);                 /* empty string, ditto */
+
+#ifdef IKSD
+    /*
+      This happens with client-side Kermit server when a REMOTE command
+      was sent from the server to the client and the server is supposed to
+      display the text, but of course there is no place to display it
+      since it is in remote mode executing Kermit protocol.
+    */
+    if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
+#ifdef COMMENT
+        return(ttol(s,((int)strlen(s)) < 0) ? -1 : 0);
+#else
+        return(0);
+#endif /* COMMENT */
+    }
+#endif /* IKSD */
+
+    if (n == ZSFILE)
+      return(write(fileno(fp[n]),s,(int)strlen(s)));
+    rc = fputs(s,fp[n]) == EOF ? -1 : 0;
+    if (n == ZWFILE)
+      fflush(fp[n]);
+    return(rc);
+}
+
+/*  Z S O U T L  --  Write string to file, with line terminator, buffered  */
+
+int
+zsoutl(n,s) int n; char *s; {
+    if (zsout(n,s) < 0)
+        return(-1);
+
+#ifdef IKSD
+    if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
+#ifdef COMMENT
+        return(ttoc(LF));
+#else
+        return(0);                      /* See comments in zsout() */
+#endif /* COMMENT */
+    }
+#endif /* IKSD */
+
+    if (n == ZSFILE)                    /* Session log is unbuffered */
+      return(write(fileno(fp[n]),"\n",1));
+    else if (fputs("\n",fp[n]) == EOF)
+      return(-1);
+    if (n == ZDIFIL || n == ZWFILE)     /* Flush connection log records */
+      fflush(fp[n]);
+    return(0);
+}
+
+/*  Z S O U T X  --  Write x characters to file, unbuffered.  */
+
+int
+zsoutx(n,s,x) int n, x; char *s; {
+#ifdef IKSD
+    if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
+#ifdef COMMENT
+        return(ttol(s,x));              /* See comments in zsout() */
+#else
+        return(x);
+#endif /* COMMENT */
+    }
+#endif /* IKSD */
+
+#ifdef COMMENT
+    if (chkfn(n) < 1) return(-1);
+    return(write(fp[n]->_file,s,x));
+#endif /* COMMENT */
+    return(write(fileno(fp[n]),s,x) == x ? x : -1);
+}
+
+/*  Z C H O U T  --  Add a character to the given file.  */
+
+/*  Should return 0 or greater on success, -1 on failure (e.g. disk full)  */
+
+int
+#ifdef CK_ANSIC
+zchout(register int n, char c)
+#else
+zchout(n,c) register int n; char c;
+#endif /* CK_ANSIC */
+/* zchout() */ {
+    /* if (chkfn(n) < 1) return(-1); */
+
+#ifdef IKSD
+    if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
+#ifdef COMMENT
+        return(ttoc(c));
+#else
+        return(0);                      /* See comments in zsout() */
+#endif /* COMMENT */
+    }
+#endif /* IKSD */
+
+    if (n == ZSFILE)                    /* Use unbuffered for session log */
+      return(write(fileno(fp[n]),&c,1) == 1 ? 0 : -1);
+                                /* Buffered for everything else */
+    if (putc(c,fp[n]) == EOF)   /* If true, maybe there was an error */
+      return(ferror(fp[n])?-1:0);       /* Check to make sure */
+    else                                /* Otherwise... */
+      return(0);                        /* There was no error. */
+}
+
+/* (PWP) buffered character output routine to speed up file IO */
+
+int
+zoutdump() {
+    int x;
+    char * zp;
+    zoutptr = zoutbuffer;               /* Reset buffer pointer in all cases */
+#ifdef DEBUG
+    if (deblog)
+      debug(F101,"zoutdump zoutcnt","",zoutcnt);
+#endif /* DEBUG */
+    if (zoutcnt == 0) {                 /* Nothing to output */
+        return(0);
+    } else if (zoutcnt < 0) {           /* Unexpected negative argument */
+        zoutcnt = 0;                    /* Reset output buffer count */
+        return(-1);                     /* and fail. */
+    }
+
+#ifdef IKSD
+    if (inserver && !local && fp[ZOFILE] == stdout) {
+#ifdef COMMENT
+        x = ttol(zoutbuffer,zoutcnt);
+#else
+        x = 1;                          /* See comments in zsout() */
+#endif /* COMMENT */
+        zoutcnt = 0;
+        return(x > 0 ? 0 : -1);
+    }
+#endif /* IKSD */
+
+/*
+  Frank Prindle suggested that replacing this fwrite() by an fflush()
+  followed by a write() would improve the efficiency, especially when
+  writing to stdout.  Subsequent tests showed a 5-fold improvement.
+*/
+#ifdef COMMENT
+    if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) ...
+#endif /* COMMENT */
+
+#ifndef CK_NONBLOCK
+    fflush(fp[ZOFILE]);
+#endif /* CK_NONBLOCK */
+    zp = zoutbuffer;
+    while (zoutcnt > 0) {
+        if ((x = write(fileno(fp[ZOFILE]),zp,zoutcnt)) > -1) {
+#ifdef DEBUG
+            if (deblog)                 /* Save a function call... */
+              debug(F101,"zoutdump wrote","",x);
+#endif /* DEBUG */
+            zoutcnt -= x;               /* Adjust output buffer count */
+            zp += x;                    /* and pointer */
+        } else {
+#ifdef DEBUG
+            if (deblog) {
+                debug(F101,"zoutdump write error","",errno);
+                debug(F101,"zoutdump write returns","",x);
+            }
+#endif /* DEBUG */
+            zoutcnt = 0;                /* Reset output buffer count */
+            return(-1);                 /* write() failed */
+        }
+    }
+    return(0);
+}
+
+/*  C H K F N  --  Internal function to verify file number is ok  */
+
+/*
+ Returns:
+  -1: File number n is out of range
+   0: n is in range, but file is not open
+   1: n in range and file is open
+*/
+int
+chkfn(n) int n; {
+    /* if (n != ZDFILE) debug(F101,"chkfn","",n); */
+    if (n < 0 || n >= ZNFILS) {
+        if (n != ZDFILE) debug(F101,"chkfn out of range","",n);
+        return(-1);
+    } else {
+        /* if (n != ZDFILE) debug(F101,"chkfn fp[n]","",fp[n]); */
+        return((fp[n] == NULL) ? 0 : 1);
+    }
+}
+
+/*  Z G E T F S -- Return file size regardless of accessibility */
+/*
+  Used for directory listings, etc.
+  Returns:
+    The size of the file in bytes, 0 or greater, if the size can be learned.
+    -1 if the file size can not be obtained.
+  Also (and this is a hack just for UNIX):
+    If the argument is the name of a symbolic link,
+    the global variable issymlink is set to 1,
+    and the global buffer linkname[] gets the link value.
+    And it sets zgfs_dir to 1 if it's a directory, otherwise 0.
+  This lets us avoid numerous redundant calls to stat().
+*/
+int zgfs_link = 0;
+int zgfs_dir = 0;
+time_t zgfs_mtime = 0;
+unsigned int zgfs_mode = 0;
+
+#ifdef CKSYMLINK
+char linkname[CKMAXPATH+1];
+#ifndef _IFLNK
+#define _IFLNK 0120000
+#endif /* _IFLNK */
+#endif /* CKSYMLINK */
+
+long
+zgetfs(name) char *name; {
+    struct stat buf;
+    char fnam[CKMAXPATH+4];
+    long size = -1L;
+    int x;
+    int needrlink = 0;
+    char * s;
+
+    if (!name) name = "";
+    if (!*name) return(-1);
+
+#ifdef UNIX
+    x = strlen(name);
+    if (x == 9 && !strcmp(name,"/dev/null"))
+      return(0);
+#endif /* UNIX */
+
+    s = name;
+#ifdef DTILDE
+    if (*s == '~') {
+        s = tilde_expand(s);
+        if (!s) s = "";
+        if (!*s) s = name;
+    }
+#endif /* DTILDE */
+    x = ckstrncpy(fnam,s,CKMAXPATH);
+    s = fnam;
+    debug(F111,"zgetfs fnam",s,x);
+    if (x > 0 && s[x-1] == '/')
+      s[x-1] = '\0';
+
+    zgfs_dir = 0;                       /* Assume it's not a directory */
+    zgfs_link = 0;                      /* Assume it's not a symlink */
+    zgfs_mtime = 0;                    /* No time yet */
+    zgfs_mode = 0;                     /* No permission bits yet */
+
+#ifdef CKSYMLINK                        /* We're doing symlinks? */
+#ifdef USE_LSTAT                        /* OK to use lstat()? */
+    x = lstat(s,&buf);
+    debug(F101,"STAT","",1);
+    if (x < 0)                          /* stat() failed */
+      return(-1);
+    if (                                /* Now see if it's a symlink */
+#ifdef S_ISLNK
+        S_ISLNK(buf.st_mode)
+#else
+#ifdef _IFLNK
+        ((_IFMT & buf.st_mode) == _IFLNK)
+#endif /* _IFLNK */
+#endif /* S_ISLNK */
+        ) {
+        zgfs_link = 1;                  /* It's a symlink */
+        linkname[0] = '\0';             /* Get the name */
+        x = readlink(s,linkname,CKMAXPATH);
+        debug(F101,"zgetfs readlink",s,x);
+        if (x > -1 && x < CKMAXPATH) {  /* It's a link */
+            linkname[x] = '\0';
+            size = buf.st_size;         /* Remember size of link */
+            x = stat(s,&buf);           /* Now stat the linked-to file */
+           debug(F101,"STAT","",2);
+            if (x < 0)                  /* so we can see if it's a directory */
+              return(-1);
+        } else {
+            ckstrncpy(linkname,"(lookup failed)",CKMAXPATH);
+        }
+    }
+#else  /* !USE_LSTAT */
+    x = stat(s,&buf);                   /* No lstat(), use stat() instead */
+    debug(F101,"STAT","",3);
+    if (x < 0)
+      return(-1);
+#endif /* USE_LSTAT */
+
+    /* Do we need to call readlink()? */
+
+#ifdef NOLINKBITS
+/*
+  lstat() does not work in SCO operating systems.  From "man NS lstat":
+
+  lstat obtains information about the file named by path. In the case of a
+  symbolic link, lstat returns information about the link, and not the file
+  named by the link. It is only used by the NFS automount daemon and should
+  not be utilized by users.
+*/
+    needrlink = 1;
+    debug(F101,"zgetfs forced needrlink","",needrlink);
+#else
+#ifdef S_ISLNK
+    needrlink = S_ISLNK(buf.st_mode);
+    debug(F101,"zgetfs S_ISLNK needrlink","",needrlink);
+#else
+#ifdef _IFLNK
+    needrlink = (_IFMT & buf.st_mode) == _IFLNK;
+    debug(F101,"zgetfs _IFLNK needrlink","",needrlink);
+#else
+    needrlink = 1;
+    debug(F101,"zgetfs default needrlink","",needrlink);
+#endif /* _IFLNK */
+#endif /* S_ISLNK */
+#endif /* NOLINKBITS */
+
+    if (needrlink) {
+        linkname[0] = '\0';
+        errno = 0;
+        x = readlink(s,linkname,CKMAXPATH);
+#ifdef DEBUG
+        debug(F111,"zgetfs readlink",s,x);
+        if (x < 0)
+          debug(F101,"zgetfs readlink errno","",errno);
+        else
+          debug(F110,"zgetfs readlink result",linkname,0);
+#endif /* DEBUG */
+        if (x > -1 && x < CKMAXPATH) {
+            zgfs_link = 1;
+            linkname[x] = '\0';
+        }
+    }
+#else  /* !CKSYMLINK */
+    x = stat(s,&buf);                   /* Just stat the file */
+    debug(F111,"zgetfs stat",s,x);
+    if (x < 0)                          /* and get the size */
+      return(-1);
+#endif /* CKSYMLINK */
+
+    zgfs_mtime = buf.st_mtime;
+    zgfs_mode = buf.st_mode;
+    zgfs_dir = (S_ISDIR(buf.st_mode)) ? 1 : 0; /* Set "is directory" flag */
+    debug(F111,"zgetfs size",s,size);
+    debug(F111,"zgetfs st_size",s,buf.st_size);
+    return((size < 0L) ? buf.st_size : size); /* Return the size */
+}
+
+
+/*  Z C H K I  --  Check if input file exists and is readable  */
+
+/*
+  Returns:
+   >= 0 if the file can be read (returns the size).
+     -1 if file doesn't exist or can't be accessed,
+     -2 if file exists but is not readable (e.g. a directory file).
+     -3 if file exists but protected against read access.
+
+  For Berkeley Unix, a file must be of type "regular" to be readable.
+  Directory files, special files, and symbolic links are not readable.
+*/
+long
+zchki(name) char *name; {
+    struct stat buf;
+    char * s;
+    int x, itsadir = 0;
+    extern int zchkid, diractive, matchfifo;
+
+    if (!name)
+      return(-1);
+    x = strlen(name);
+    if (x < 1)
+      return(-1);
+    s = name;
+
+#ifdef UNIX
+    if (x == 9 && !strcmp(s,"/dev/null"))
+      return(0);
+    if (x == 8 && !strcmp(s,"/dev/tty"))
+      return(0);
+#endif /* UNIX */
+
+#ifdef DTILDE
+    if (*s == '~') {
+        s = tilde_expand(s);
+        if (!s) s = "";
+        if (!*s) s = name;
+    }
+#endif /* DTILDE */
+
+#ifdef CKROOT
+    debug(F111,"zchki setroot",ckroot,ckrootset);
+    if (ckrootset) if (!zinroot(name)) {
+       debug(F110,"zchki setroot violation",name,0);
+       return(-1);
+    }
+#endif /* CKROOT */
+
+    x = stat(s,&buf);
+    debug(F101,"STAT","",5);
+    if (x < 0) {
+        debug(F111,"zchki stat fails",s,errno);
+        return(-1);
+    }
+    if (S_ISDIR (buf.st_mode))
+      itsadir = 1;
+
+    if (!(itsadir && zchkid)) {         /* Unless this... */
+        if (!S_ISREG (buf.st_mode)      /* Must be regular file */
+#ifdef S_ISFIFO
+            && (!matchfifo || !S_ISFIFO (buf.st_mode))  /* or FIFO */
+#endif /* S_ISFIFO */
+            ) {
+            debug(F111,"zchki not regular file (or fifo)",s,matchfifo);
+            return(-2);
+        }
+    }
+    debug(F111,"zchki stat ok:",s,x);
+
+    if (diractive) {                   /* If listing don't check access */
+       x = 1;
+    } else {
+#ifdef SW_ACC_ID
+       debug(F100,"zchki swapping ids for access()","",0);
+       priv_on();
+#endif /* SW_ACC_ID */
+       if ((x = access(s,R_OK)) < 0)
+         x = access(s,X_OK);           /* For RUN-class commands */
+#ifdef SW_ACC_ID
+       priv_off();
+       debug(F100,"zchki swapped ids restored","",0);
+#endif /* SW_ACC_ID */
+    }
+    if (x < 0) {                       /* Is the file accessible? */
+        debug(F111,"zchki access failed:",s,x); /* No */
+        return(-3);
+    } else {
+        iflen = buf.st_size;            /* Yes, remember size */
+        ckstrncpy(nambuf,s,CKMAXPATH);  /* and name globally. */
+        debug(F111,"zchki access ok:",s,iflen);
+        return((iflen > -1L) ? iflen : 0L);
+    }
+}
+
+/*  Z C H K O  --  Check if output file can be created  */
+
+/*
+  Returns -1 if write permission for the file would be denied, 0 otherwise.
+
+  NOTE: The design is flawed.  There is no distinction among:
+   . Can I overwrite an existing file?
+   . Can I create a file (or directory) in an existing directory?
+   . Can I create a file (or directory) and its parent(s)?
+*/
+int
+zchko(name) char *name; {
+    int i, x, itsadir = 0;
+    char *s;
+    char * oname;
+    extern int zchkod;                  /* Used by IF WRITEABLE */
+
+    debug(F110,"zchko entry",name,0);
+
+    if (!name) return(-1);              /* Watch out for null pointer. */
+
+    oname = name;
+
+#ifdef CKROOT
+    debug(F111,"zchko setroot",ckroot,ckrootset);
+    if (ckrootset) if (!zinroot(name)) {
+       debug(F110,"zchko setroot violation",name,0);
+       errno = EACCES;
+       return(-1);
+    }
+#endif /* CKROOT */
+
+    x = (int)strlen(name);              /* Get length of filename */
+    debug(F111,"zchko len",name,x);
+    debug(F111,"zchko zchkod",name,zchkod);
+
+#ifdef UNIX
+/*
+  Writing to null device is OK.
+*/
+    if (x == 9 && !strcmp(name,"/dev/null"))
+      return(0);
+    if (x == 8 && !strcmp(name,"/dev/tty"))
+      return(0);
+#endif /* UNIX */
+
+    s = name;
+#ifdef DTILDE
+    if (*s == '~') {
+        s = tilde_expand(s);
+        if (!s) s = "";
+        if (!*s) s = name;
+       x = strlen(s);
+    }
+#endif /* DTILDE */
+    name = s;
+    s = NULL;
+/*
+  zchkod is a global flag meaning we're checking not to see if the directory
+  file is writeable, but if it's OK to create files IN the directory.
+*/
+    if (!zchkod && isdir(name))         /* Directories are not writeable */
+      return(-1);
+
+    s = malloc(x+3);                    /* Must copy because we can't */
+    if (!s) {                           /* write into our argument. */
+        fprintf(stderr,"zchko: Malloc error 46\n");
+        return(-1);
+    }
+    ckstrncpy(s,name,x+3);
+
+    for (i = x; i > 0; i--) {           /* Strip filename from right. */
+        if (ISDIRSEP(s[i-1])) {
+            itsadir = 1;
+            break;
+        }
+    }
+    debug(F101,"zchko i","",i);
+    debug(F101,"zchko itsadir","",itsadir);
+
+#ifdef COMMENT
+/* X/OPEN XPG3-compliant systems fail if argument ends with "/"...  */
+    if (i == 0)                         /* If no path, use current directory */
+      strcpy(s,"./");
+    else                                /* Otherwise, use given one. */
+      s[i] = '\0';
+#else
+#ifdef COMMENT
+/*
+  The following does not work for "foo/bar" where the foo directory does
+  not exist even though we could create it: access("foo/.") fails, but
+  access("foo") works OK.
+*/
+/* So now we use "path/." if path given, or "." if no path given. */
+    s[i++] = '.';                       /* Append "." to path. */
+    s[i] = '\0';
+#else
+/* So NOW we strip path segments from the right as long as they don't */
+/* exist -- we only call access() for path segments that *do* exist.. */
+/* (But this isn't quite right either since now zchko(/foo/bar/baz/xxx) */
+/* succeeds when I have write access to foo and bar but baz doesn't exit.) */
+
+    if (itsadir && i > 0) {
+        s[i-1] = '\0';
+        while (s[0] && !isdir(s)) {
+            for (i = (int)strlen(s); i > 0; i--) {
+                if (ISDIRSEP(s[i-1])) {
+                    s[i-1] = '\0';
+                    break;
+                }
+            }
+            if (i == 0)
+              s[0] = '\0';
+        }
+    } else {
+        s[i++] = '.';                   /* Append "." to path. */
+        s[i] = '\0';
+    }
+#endif /* COMMENT */
+#endif /* COMMENT */
+
+    if (!s[0])
+      ckstrncpy(s,".",x+3);
+
+#ifdef SW_ACC_ID
+    debug(F100,"zchko swapping ids for access()","",0);
+    priv_on();
+#endif /* SW_ACC_ID */
+
+    x = access(s,W_OK);                 /* Check access of path. */
+
+#ifdef SW_ACC_ID
+    priv_off();
+    debug(F100,"zchko swapped ids restored","",0);
+#endif /* SW_ACC_ID */
+
+    if (x < 0)
+      debug(F111,"zchko access failed:",s,errno);
+    else
+      debug(F111,"zchko access ok:",s,x);
+    free(s);                            /* Free temporary storage */
+
+    return((x < 0) ? -1 : 0);           /* and return. */
+}
+
+/*  Z D E L E T  --  Delete the named file.  */
+
+/* Returns: -1 on error, 0 on success */
+
+int
+zdelet(name) char *name; {
+    int x;
+#ifdef CK_LOGIN
+    if (isguest)
+      return(-1);
+#endif /* CK_LOGIN */
+
+#ifdef CKROOT
+    debug(F111,"zdelet setroot",ckroot,ckrootset);
+    if (ckrootset) if (!zinroot(name)) {
+       debug(F110,"zdelet setroot violation",name,0);
+       return(-1);
+    }
+#endif /* CKROOT */
+
+    x = unlink(name);
+    debug(F111,"zdelet",name,x);
+#ifdef CKSYSLOG
+    if (ckxsyslog >= SYSLG_FC && ckxlogging) {
+        fullname[0] = '\0';
+        zfnqfp(name,CKMAXPATH,fullname);
+        debug(F110,"zdelet fullname",fullname,0);
+        if (x < 0)
+          syslog(LOG_INFO, "file[] %s: delete failed (%m)", fullname);
+        else
+          syslog(LOG_INFO, "file[] %s: delete ok", fullname);
+    }
+#endif /* CKSYSLOG */
+    return(x);
+}
+
+/*  Z R T O L  --  Convert remote filename into local form  */
+
+VOID
+zrtol(name,name2) char *name, *name2; {
+    nzrtol(name,name2,1,0,CKMAXPATH);
+}
+
+VOID
+nzrtol(name,name2,fncnv,fnrpath,max)
+    char *name, *name2; int fncnv, fnrpath, max;
+{ /* nzrtol */
+    char *s, *p;
+    int flag = 0, n = 0;
+    char fullname[CKMAXPATH+1];
+    int devnull = 0;
+    int acase = 0;
+    if (!name2) return;
+    if (!name) name = "";
+
+    debug(F111,"nzrtol name",name,fncnv);
+
+#ifdef DTILDE
+    s = name;
+    if (*s == '~') {
+        s = tilde_expand(s);
+        if (!s) s = "";
+        if (*s) name = s;
+    }
+#endif /* DTILDE */
+
+    /* Handle the path -- we don't have to convert its format, since */
+    /* the standard path format and our (UNIX) format are the same. */
+
+    fullname[0] = NUL;
+    devnull = !strcmp(name,"/dev/null");
+
+    if (!devnull && fnrpath == PATH_OFF) { /* RECEIVE PATHNAMES OFF */
+        zstrip(name,&p);
+        strncpy(fullname,p,CKMAXPATH);
+    } else if (!devnull && fnrpath == PATH_ABS) { /* REC PATHNAMES ABSOLUTE */
+        strncpy(fullname,name,CKMAXPATH);
+    } else if (!devnull && isabsolute(name)) { /* RECEIVE PATHNAMES RELATIVE */
+       ckmakmsg(fullname,CKMAXPATH,".",name,NULL,NULL);
+    } else {                            /* Ditto */
+        ckstrncpy(fullname,name,CKMAXPATH);
+    }
+    fullname[CKMAXPATH] = NUL;
+    debug(F110,"nzrtol fullname",fullname,0);
+
+#ifndef NOTRUNCATE
+/*
+  The maximum length for any segment of a filename is MAXNAMLEN, defined
+  above.  On some platforms (at least QNX) if a segment exceeds this limit,
+  the open fails with ENAMETOOLONG, so we must prevent it by truncating each
+  overlong name segment to the maximum segment length before passing the
+  name to open().  This must be done even when file names are literal, so as
+  not to halt a file transfer unnecessarily.
+*/
+    {
+        char buf[CKMAXPATH+1];          /* New temporary buffer on stack */
+        char *p = fullname;             /* Source and  */
+        char *s = buf;                  /* destination pointers */
+        int i = 0, n = 0;
+        debug(F101,"nzrtol sizing MAXNAMLEN","",MAXNAMLEN);
+        while (*p && n < CKMAXPATH) {   /* Copy name to new buffer */
+            if (++i > MAXNAMLEN) {      /* If this segment too long */
+                while (*p && *p != '/') /* skip past the rest... */
+                  p++;
+                i = 0;                  /* and reset counter. */
+            } else if (*p == '/') {     /* End of this segment. */
+                i = 0;                  /* Reset counter. */
+            }
+            *s++ = *p++;                /* Copy this character. */
+            n++;
+        }
+        *s = NUL;
+        ckstrncpy(fullname,buf,CKMAXPATH); /* Copy back to original buffer. */
+        debug(F111,"nzrtol sizing",fullname,n);
+    }
+#endif /* NOTRUNCATE */
+
+    if (!fncnv || devnull) {            /* Not converting */
+        ckstrncpy(name2,fullname,max);  /* We're done. */
+        return;
+    }
+    name = fullname;                    /* Converting */
+
+    p = name2;
+    for (; *name != '\0' && n < maxnam; name++) {
+        if (*name > SP) flag = 1;       /* Strip leading blanks and controls */
+        if (flag == 0 && *name < '!')
+          continue;
+       if (fncnv > 0) {
+           if (*name == SP) {
+               *p++ = '_';
+               n++;
+               continue;
+           }
+           if (isupper(*name))         /* Check for mixed case */
+             acase |= 1;
+           else if (islower(*name))
+             acase |= 2;
+       }
+        *p++ = *name;
+        n++;
+    }
+    *p-- = '\0';                        /* Terminate */
+    while (*p < '!' && p > name2)       /* Strip trailing blanks & controls */
+      *p-- = '\0';
+
+    if (*name2 == '\0') {               /* Nothing left? */
+        ckstrncpy(name2,"NONAME",max); /* do this... */
+    } else if (acase == 1) {            /* All uppercase? */
+        p = name2;                      /* So convert all letters to lower */
+        while (*p) {
+            if (isupper(*p))
+              *p = tolower(*p);
+            p++;
+        }
+    }
+    debug(F110,"nzrtol new name",name2,0);
+}
+
+
+/*  Z S T R I P  --  Strip device & directory name from file specification */
+
+/*  Strip pathname from filename "name", return pointer to result in name2 */
+
+static char work[CKMAXPATH+1];
+
+VOID
+zstrip(name,name2) char *name, **name2; {
+    char *cp, *pp;
+    int n = 0;
+
+    debug(F110,"zstrip before",name,0);
+    if (!name) { *name2 = ""; return; }
+    pp = work;
+#ifdef DTILDE
+    /* Strip leading tilde */
+    if (*name == '~') name++;
+    debug(F110,"zstrip after tilde-stripping",name,0);
+#endif /* DTILDE */
+    for (cp = name; *cp; cp++) {
+        if (ISDIRSEP(*cp)) {
+            pp = work;
+            n = 0;
+        } else {
+            *pp++ = *cp;
+            if (n++ >= CKMAXPATH)
+              break;
+        }
+    }
+    *pp = '\0';                         /* Terminate the string */
+    *name2 = work;
+    debug(F110,"zstrip after",*name2,0);
+}
+
+/*  Z L T O R  --  Local TO Remote */
+
+VOID
+zltor(name,name2) char *name, *name2; {
+    nzltor(name,name2,1,0,CKMAXPATH);
+}
+
+/*  N Z L T O R  --  New Local TO Remote */
+
+/*
+  fncnv = 0 for no conversion, > 0 for regular conversion, < 0 for minimal.
+*/
+VOID
+nzltor(name,name2,fncnv,fnspath,max)
+    char *name, *name2; int fncnv, fnspath, max;
+{ /* nzltor */
+    char *cp, *pp;
+#ifdef COMMENT
+    int dc = 0;
+#endif /* COMMENT */
+    int n = 0;
+    char *dotp = NULL;
+    char *dirp = NULL;
+    char fullname[CKMAXPATH+1];
+    char *p;
+    CHAR c;
+
+#ifndef NOCSETS
+    extern int fcharset, /* tcharset, */ language;
+    int langsv;
+    _PROTOTYP ( CHAR (*sxo), (CHAR) ) = NULL; /* Translation functions */
+#ifdef CK_ANSIC
+    extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR);
+#else
+    extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])();
+#endif /* CK_ANSIC */
+    langsv = language;
+    language = L_USASCII;
+#ifdef COMMENT
+    /* Proper translation of filenames must be done elsewhere */
+    n = tcharset ? tcharset : TC_USASCII;
+    sxo = xls[n][fcharset];
+#else
+    sxo = xls[TC_USASCII][fcharset];
+#endif /* COMMENT */
+#endif /* NOCSETS */
+
+    debug(F110,"nzltor name",name,0);
+
+    /* Handle pathname */
+
+    fullname[0] = NUL;
+    if (fnspath == PATH_OFF) {          /* PATHNAMES OFF */
+        zstrip(name,&p);
+        ckstrncpy(fullname,p,CKMAXPATH);
+    } else {                            /* PATHNAMES RELATIVE or ABSOLUTE */
+        char * p = name;
+        while (1) {
+            if (!strncmp(p,"../",3))
+              p += 3;
+            else if (!strncmp(p,"./",2))
+              p += 2;
+            else
+              break;
+        }
+        if (fnspath == PATH_ABS) {      /* ABSOLUTE */
+            zfnqfp(p,CKMAXPATH,fullname);
+        } else {                        /* RELATIVE */
+            ckstrncpy(fullname,p,CKMAXPATH);
+        }
+    }
+    debug(F110,"nzltor fullname",fullname,0);
+
+    if (!fncnv) {                       /* Not converting */
+        ckstrncpy(name2,fullname,max);  /* We're done. */
+#ifndef NOCSETS
+        langsv = language;
+#endif /* NOCSETS */
+        return;
+    }
+    name = fullname;                    /* Converting */
+
+#ifdef aegis
+    char *namechars;
+    int tilde = 0, bslash = 0;
+
+    if ((namechars = getenv("NAMECHARS")) != NULL) {
+        if (ckstrchr(namechars, '~' ) != NULL) tilde  = '~';
+        if (ckstrchr(namechars, '\\') != NULL) bslash = '\\';
+    } else {
+        tilde = '~';
+        bslash = '\\';
+    }
+#endif /* aegis */
+
+    pp = work;                          /* Output buffer */
+    for (cp = name, n = 0; *cp && n < max; cp++,n++) { /* Convert name chars */
+        c = *cp;
+#ifndef NOCSETS
+        if (sxo) c = (*sxo)(c);         /* Convert to ASCII */
+#endif /* NOCSETS */
+        if (fncnv > 0 && islower(c))   /* Uppercase letters */
+          *pp++ = toupper(c);           /* Change tilde to hyphen */
+        else if (c == '~')
+          *pp++ = '-';
+        else if (fncnv > 0 && c == '#')        /* Change number sign to 'X' */
+          *pp++ = 'X';
+        else if (c == '*' || c == '?')  /* Change wildcard chars to 'X' */
+          *pp++ = 'X';
+        else if (c == ' ')              /* Change space to underscore */
+          *pp++ = '_';
+        else if (c < ' ')               /* Change controls to 'X' */
+          *pp++ = 'X';
+        else if (fncnv > 0 && c == '.') { /* Change dot to underscore */
+            dotp = pp;                  /* Remember where we last did this */
+            *pp++ = '_';
+        } else {
+            if (c == '/')
+              dirp = pp;
+            *pp++ = c;
+        }
+    }
+    *pp = NUL;                          /* Tie it off. */
+#ifdef COMMENT
+    if (dotp) *dotp = '.';              /* Restore last dot (if any) */
+#else
+    if (dotp > dirp) *dotp = '.';       /* Restore last dot in file name */
+#endif /* COMMENT */
+    cp = name2;                         /* If nothing before dot, */
+    if (*work == '.') *cp++ = 'X';      /* insert 'X' */
+    ckstrncpy(cp,work,max);
+#ifndef NOCSETS
+    language = langsv;
+#endif /* NOCSETS */
+    debug(F110,"nzltor name2",name2,0);
+}
+
+
+/*  Z C H D I R  --  Change directory  */
+/*
+  Call with:
+    dirnam = pointer to name of directory to change to,
+      which may be "" or NULL to indicate user's home directory.
+  Returns:
+    0 on failure
+    1 on success
+*/
+int
+zchdir(dirnam) char *dirnam; {
+    char *hd, *sp;
+#ifdef IKSDB
+    _PROTOTYP (int slotdir,(char *,char *));
+#endif /* IKSDB */
+#ifndef NOSPL
+    extern struct mtab *mactab;             /* Main macro table */
+    extern int nmac;                        /* Number of macros */
+#endif /* NOSPL */
+
+    debug(F110,"zchdir",dirnam,0);
+    if (!dirnam) dirnam = "";
+    if (!*dirnam)                       /* If argument is null or empty, */
+      dirnam = zhome();                 /* use user's home directory. */
+    sp = dirnam;
+    debug(F110,"zchdir 2",dirnam,0);
+
+#ifdef DTILDE
+    hd = tilde_expand(dirnam);          /* Attempt to expand tilde */
+    if (!hd) hd = "";
+    if (*hd == '\0') hd = dirnam;       /* in directory name. */
+#else
+    hd = dirnam;
+#endif /* DTILDE */
+    debug(F110,"zchdir 3",hd,0);
+
+#ifdef CKROOT
+    debug(F111,"zchdir setroot",ckroot,ckrootset);
+    if (ckrootset) if (!zinroot(hd)) {
+       debug(F110,"zchdir setroot violation",hd,0);
+       return(0);
+    }
+#endif /* CKROOT */
+
+#ifdef pdp11
+    /* Just to save some space */
+    return((chdir(hd) == 0) ? 1 : 0);
+#else
+    if (chdir(hd) == 0) {                       /* Try to cd */
+#ifdef IKSDB
+#ifdef CK_LOGIN
+        if (inserver && ikdbopen)
+          slotdir(isguest ? anonroot : "", zgtdir());
+#endif /* CK_LOGIN */
+#endif /* IKSDB */
+
+#ifndef NOSPL
+        if (nmac) {                    /* Any macros defined? */
+            int k;                     /* Yes */
+            static int on_cd = 0;
+            if (!on_cd) {
+                on_cd = 1;
+                k = mlook(mactab,"on_cd",nmac);   /* Look this up */
+                if (k >= 0) {                     /* If found, */
+                    if (dodo(k,zgtdir(),0) > -1)  /* set it up, */
+                     parser(1);                  /* and execute it */
+                }
+                on_cd = 0;
+            }
+        }
+#endif /* NOSPL */
+        return(1);
+    }
+    return(0);
+#endif /* pdp11 */
+}
+
+int
+#ifdef CK_ANSIC
+zchkpid(unsigned long xpid)
+#else
+zchkpid(xpid) unsigned long xpid;
+#endif /* CK_ANSIC */
+{
+    return((kill((PID_T)xpid,0) < 0) ? 0 : 1);
+}
+
+
+/*  Z H O M E  --  Return pointer to user's home directory  */
+
+static char * zhomdir = NULL;
+
+char *
+zhome() {
+    char * home;
+
+#ifdef CKROOT
+    if (ckrootset)
+      return((char *)ckroot);
+#endif /* CKROOT */
+
+#ifdef Plan9
+    home = getenv("home");
+#else
+    home = getenv("HOME");
+#endif /* Plan9 */
+    makestr(&zhomdir,home);
+    return(home ? zhomdir : ".");
+}
+
+/*  Z G T D I R  --  Returns a pointer to the current directory  */
+
+/*
+  The "preferred" interface for getting the current directory in modern UNIX
+  is getcwd() [POSIX 1003.1 5.2.2].  However, on certain platforms (such as
+  SunOS), it is implemented by forking a shell, feeding it the pwd command,
+  and returning the result, which is not only inefficient but also can result
+  in stray messages to the terminal.  In such cases -- as well as when
+  getcwd() is not available at all -- getwd() can be used instead by defining
+  USE_GETWD.  However, note that getwd() provides no buffer-length argument
+  and therefore no safeguard against memory leaks.
+*/
+#ifndef USE_GETWD
+#ifdef BSD42
+#define USE_GETWD
+#else
+#ifdef SUNOS4
+#define USE_GETWD
+#endif /* SUNOS4 */
+#endif /* BSD42 */
+#endif /* USE_GETWD */
+
+#ifdef pdp11
+#define CWDBL 80                        /* Save every byte we can... */
+#else
+#define CWDBL CKMAXPATH
+#endif /* pdp11 */
+static char cwdbuf[CWDBL+2];
+/*
+  NOTE: The getcwd() prototypes are commented out on purpose.  If you get
+  compile-time warnings, search through your system's header files to see
+  which one has the needed prototype, and #include it.  Usually it is
+  <unistd.h>.  See the section for including <unistd.h> in ckcdeb.h and
+  make any needed adjustments there (and report them).
+*/
+char *
+zgtdir() {
+    char * buf = cwdbuf;
+    char * s;
+
+#ifdef USE_GETWD
+    extern char *getwd();
+    s = getwd(buf);
+    debug(F110,"zgtdir BSD4 getwd()",s,0);
+    if (!s) s = "./";
+    return(s);
+#else
+#ifdef BSD44
+#ifdef DCLGETCWD
+_PROTOTYP( char * getcwd, (char *, SIZE_T) );
+#endif /* DCLGETCWD */
+    debug(F101,"zgtdir BSD44 CWDBL","",CWDBL);
+    s = getcwd(buf,CWDBL);
+    if (!s) s = "./";
+    return(s);
+#else
+#ifdef MINIX2
+#ifdef DCLGETCWD
+    _PROTOTYP( char * getcwd, (char *, SIZE_T) );
+#endif /* DCLGETCWD */
+    debug(F101,"zgtdir MINIX2 CWDBL","",CWDBL);
+    s = getcwd(buf,CWDBL);
+    if (!s) s = "./";
+    return(s);
+#else
+#ifdef SVORPOSIX
+#ifdef COMMENT
+/* This non-ANSI prototype can be fatal at runtime! (e.g. in SCO3.2v5.0.5). */
+/* Anyway it's already prototyped in some header file that we have included. */
+    extern char *getcwd();
+#else
+#ifdef DCLGETCWD
+    _PROTOTYP( char * getcwd, (char *, SIZE_T) );
+#endif /* DCLGETCWD */
+#endif /* COMMENT */
+    debug(F101,"zgtdir SVORPOSIX CWDBL","",CWDBL);
+    s = getcwd(buf,CWDBL);
+    if (!s) s = "./";
+    return(s);
+#else
+#ifdef COHERENT
+#ifdef _I386
+#ifdef DCLGETCWD
+    extern char *getcwd();
+#endif /* DCLGETCWD */
+    debug(F101,"zgtdir COHERENT _I386 CWDBL","",CWDBL);
+    s = getcwd(buf,CWDBL);
+    if (!s) s = "./";
+    return(s);
+#else
+    extern char *getwd();
+    debug(F101,"zgtdir COHERENT CWDBL","",CWDBL);
+    s = getwd(buf);
+    if (!s) s = "./";
+    return(s);
+#endif /* _I386 */
+#else
+#ifdef SUNOS4
+    debug(F101,"zgtdir SUNOS CWDBL","",CWDBL);
+    s = getcwd(buf,CWDBL);
+    if (!s) s = "./";
+    return(s);
+#else
+    return("./");
+#endif /* SUNOS4 */
+#endif /* COHERENT */
+#endif /* SYSVORPOSIX */
+#endif /* MINIX2 */
+#endif /* BSD44 */
+#endif /* USE_GETWD */
+}
+
+/*  Z X C M D -- Run a system command so its output can be read like a file */
+
+#ifndef NOPUSH
+int
+zxcmd(filnum,comand) int filnum; char *comand; {
+    int out;
+    int pipes[2];
+    extern int kactive;                 /* From ckcpro.w and ckcmai.c */
+
+    if (nopush) {
+        debug(F100,"zxcmd fails: nopush","",0);
+        return(-1);
+    }
+    debug(F111,"zxcmd",comand,filnum);
+    if (chkfn(filnum) < 0) return(-1);  /* Need a valid Kermit file number. */
+    if (filnum == ZSTDIO || filnum == ZCTERM) /* But not one of these. */
+      return(0);
+
+    out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
+    debug(F101,"zxcmd out",comand,out);
+
+/* Output to a command */
+
+    if (out) {                          /* Need popen() to do this. */
+       ckstrncpy(fullname,"(pipe)",CKMAXPATH);
+#ifdef NOPOPEN
+        return(0);                      /* no popen(), fail. */
+#else
+/* Use popen() to run the command. */
+
+#ifdef _POSIX_SOURCE
+/* Strictly speaking, popen() is not available in POSIX.1 */
+#define DCLPOPEN
+#endif /* _POSIX_SOURCE */
+
+       debug(F110,"zxcmd out",comand,0);
+
+        if (priv_chk()) {
+           debug(F100,"zxcmd priv_chk failed","",0);
+            return(0);
+       }       
+       errno = 0;
+        fp[filnum] = popen(comand,"w");
+       debug(F111,"zxcmd popen",fp[filnum] ? "OK" : "Failed", errno);
+       if (fp[filnum] == NULL)
+         return(0);
+#ifdef COMMENT
+/* I wonder what this is all about... */
+       close(pipes[0]);                /* Don't need the input side */
+       fp[filnum] = fdopen(pipes[1],"w"); /* Open output stream. */
+       fp[ZSYSFN] = fp[filnum];           /* Remember. */
+#endif /* COMMENT */
+       ispipe[filnum] = 1;
+       zoutcnt = 0;                    /* (PWP) reset input buffer */
+       zoutptr = zoutbuffer;
+       return(1);
+#endif /* NOPOPEN */
+    }
+
+/* Input from a command */
+
+#ifdef SNI541
+    /* SINIX-L 5.41 does not like fdopen() */
+    return(0);
+#else
+    if (pipe(pipes) != 0) {
+        debug(F100,"zxcmd pipe failure","",0);
+        return(0);                      /* can't make pipe, fail */
+    }
+
+/* Create a fork in which to run the named process */
+
+    if ((
+#ifdef aegis
+         pid = vfork()                  /* child */
+#else
+         pid = fork()                   /* child */
+#endif /* aegis */
+         ) == 0) {
+
+/* We're in the fork. */
+
+        char *shpath, *shname, *shptr;  /* Find user's preferred shell */
+#ifndef aegis
+        struct passwd *p;
+        char *defshell;
+#ifdef HPUX10                           /* Default shell */
+        defshell = "/usr/bin/sh";
+#else
+#ifdef Plan9
+        defshell = "/bin/rc";
+#else
+        defshell = "/bin/sh";
+#endif /* Plan9 */
+#endif /* HPUX10 */
+#endif /* aegis */
+        if (priv_can()) exit(1);        /* Turn off any privileges! */
+        debug(F101,"zxcmd pid","",pid);
+        close(pipes[0]);                /* close input side of pipe */
+        close(0);                       /* close stdin */
+        if (open("/dev/null",0) < 0) return(0); /* replace input by null */
+#ifndef OXOS
+#ifndef SVORPOSIX
+        dup2(pipes[1],1);               /* BSD: replace stdout & stderr */
+        dup2(pipes[1],2);               /* by the pipe */
+#else
+        close(1);                       /* AT&T: close stdout */
+        if (dup(pipes[1]) != 1)         /* Send stdout to the pipe */
+          return(0);
+        close(2);                       /* Send stderr to the pipe */
+        if (dup(pipes[1]) != 2)
+          return(0);
+#endif /* SVORPOSIX */
+#else /* OXOS */
+        dup2(pipes[1],1);
+        dup2(pipes[1],2);
+#endif /* OXOS */
+        close(pipes[1]);                /* Don't need this any more. */
+
+#ifdef aegis
+        if ((shpath = getenv("SERVERSHELL")) == NULL)
+          shpath = "/bin/sh";
+#else
+        shpath = getenv("SHELL");       /* What shell? */
+        if (shpath == NULL) {
+            p = getpwuid( real_uid() ); /* Get login data */
+            debug(F111,"zxcmd shpath","getpwuid()",p);
+            if (p == (struct passwd *)NULL || !*(p->pw_shell))
+              shpath = defshell;
+            else shpath = p->pw_shell;
+        }
+#endif /* aegis */
+        shptr = shname = shpath;
+        while (*shptr != '\0')
+          if (*shptr++ == '/')
+            shname = shptr;
+        debug(F110,shpath,shname,0);
+       restorsigs();                   /* Restore ignored signals */
+        execl(shpath,shname,"-c",comand,(char *)NULL); /* Execute the cmd */
+        exit(0);                        /* just punt if it failed. */
+    } else if (pid == (PID_T) -1) {
+        debug(F100,"zxcmd fork failure","",0);
+        return(0);
+    }
+    debug(F101,"zxcmd pid","",pid);
+    close(pipes[1]);                    /* Don't need the output side */
+    ispipe[filnum] = 1;                 /* Remember it's a pipe */
+    fp[filnum] = fdopen(pipes[0],"r"); /* Open a stream for input. */
+
+#ifdef DONDELAY
+#ifdef SELECT
+    if (filnum == ZIFILE && kactive) {  /* Make pipe reads nonblocking */
+        int flags, x;
+        if ((flags = fcntl(fileno(fp[filnum]),F_GETFL,0)) > -1) {
+            debug(F101,"zxcmd fcntl 1 pipe flags","",flags);
+            x = fcntl(fileno(fp[filnum]),F_SETFL, flags |
+#ifdef QNX
+                  O_NONBLOCK
+#else
+                  O_NDELAY
+#endif /* QNX */
+                  );
+            debug(F101,"zxcmd fcntl 2 result","",x);
+        }
+    }
+#endif /* SELECT */
+#endif /* DONDELAY */
+#endif /* SNI541 */
+    fp[ZSYSFN] = fp[filnum];            /* Remember. */
+    zincnt = 0;                         /* (PWP) reset input buffer */
+    zinptr = zinbuffer;
+    fullname[0] = '\0';
+    return(1);
+} /* zxcmd */
+
+/*  Z C L O S F  - wait for the child fork to terminate and close the pipe. */
+
+/*  Used internally by zclose - returns -1 on failure, 1 on success. */
+
+int
+zclosf(filnum) int filnum; {
+    int wstat, out;
+    int statusp;
+
+    debug(F101,"zclosf filnum","",filnum);
+    out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
+    debug(F101,"zclosf out","",out);
+
+#ifndef NOPOPEN
+    if (ispipe[filnum]
+        /* In UNIX we use popen() only for output files */
+        && out
+        ) {
+        int x;
+        x = pclose(fp[filnum]);
+        pexitstat = x >> 8;
+        debug(F101,"zclosf pclose","",x);
+        debug(F101,"zclosf pexitstat","",pexitstat);
+        fp[filnum] = fp[ZSYSFN] = NULL;
+        ispipe[filnum] = 0;
+        return((x != 0) ? -1 : 1);
+    }
+#endif /* NOPOPEN */
+    debug(F101,"zclosf fp[filnum]","", fp[filnum]);
+    debug(F101,"zclosf fp[ZSYSFN]","", fp[ZSYSFN]);
+
+    if (pid != (PID_T) 0) {
+        debug(F101,"zclosf killing pid","",pid);
+#ifdef Plan9
+        kill(pid, SIGKILL);
+#else
+        kill(pid,9);
+#endif /* Plan9 */
+
+#ifndef CK_CHILD
+/*
+  This is the original code (before 20 April 1997) and has proven totally
+  portable.  But it does not give us the process's return code.
+*/
+        while ((wstat = wait((WAIT_T *)0)) != pid && wstat != -1) ;
+#else
+/* Here we try to get the return code.  Let's hope this is portable too. */
+        while ((wstat = wait(&statusp)) != pid && wstat != -1) ;
+        pexitstat = (statusp & 0xff) ? statusp : statusp >> 8;
+        debug(F101,"zclosf wait statusp","",statusp);
+        debug(F101,"zclosf wait pexitstat","",pexitstat);
+#endif /* CK_CHILD */
+        pid = 0;
+    }
+    fclose(fp[filnum]);
+    fp[filnum] = fp[ZSYSFN] = NULL;
+
+    ispipe[filnum] = 0;
+    debug(F101,"zclosf fp[filnum]","",fp[filnum]);
+#ifdef CK_CHILD
+    return(pexitstat == 0 ? 1 : -1);
+#else
+    return(1);
+#endif /* CK_CHILD */
+}
+
+#else  /* NOPUSH */
+
+int
+zxcmd(filnum,comand) int filnum; char *comand; {
+    return(0);
+}
+int
+zclosf(filnum) int filnum; {
+    return(EOF);
+}
+#endif /* NOPUSH */
+
+
+/*  Z X P A N D  --  Expand a wildcard string into an array of strings  */
+/*
+  As of C-Kermit 7.0, this API is obsolete, replaced by nzxpand(), and this
+  function is only used internally.  See nzxpand() below.
+
+  Returns the number of files that match fnarg, with data structures set up
+  so that first file (if any) will be returned by the next znext() call.
+
+  Depends on external variable wildxpand: 0 means we expand wildcards
+  internally, nonzero means we call the shell to do it.
+*/
+static int xdironly = 0;
+static int xfilonly = 0;
+static int xmatchdot = 0;
+static int xrecursive = 0;
+static int xnobackup = 0;
+static int xnolinks = 0;
+
+static char *freeptr = NULL, **resptr = NULL; /* Copies of caller's args */
+static int remlen;                      /* Remaining space in caller's array */
+static int numfnd = 0;                 /* Number of matches found */
+
+#define MINSPACE 1024
+
+static int
+initspace(resarry,len) char * resarry[]; int len; {
+#ifdef DYNAMIC
+    if (len < MINSPACE) len = MINSPACE;
+    if (!sspace) {                      /* Need to allocate string space? */
+        while (len >= MINSPACE) {
+            if ((sspace = malloc(len+2))) { /* Got it. */
+                debug(F101,"fgen string space","",len);
+                break;
+            }
+            len = (len / 2) + (len / 4); /* Didn't, reduce by 3/4 */
+        }
+        if (len <= MINSPACE) {         /* Did we get it? */
+            fprintf(stderr,"fgen can't malloc string space\n");
+            return(-1);
+        }
+       ssplen = len;
+    }
+#endif /* DYNAMIC */
+
+    freeptr = sspace;                   /* This is where matches are copied. */
+    resptr = resarry;                  /* Static copies of these so */
+    remlen = len;                       /* recursive calls can alter them. */
+    debug(F101,"initspace ssplen","",ssplen);
+    return(0);
+}
+
+/*
+  Z S E T F I L  --  Query or change the size of file list buffers.
+
+  fc = 1: Change current string space to n, return new size.
+  fc = 2: Return current string space size.
+  fc = 3: Change current maxnames to n, return new maxnames.
+  fc = 4: Return current maxnames.
+  Returns < 0 on error.
+*/
+int
+zsetfil(n, fc) int n, fc; {
+#ifdef DYNAMIC
+    switch (fc) {
+      case 1:                          /* Stringspace */
+       if (sspace) {
+           free(sspace);
+           sspace = NULL;
+       }
+       if (initspace(mtchs,n) < 0)
+         return(-1);
+      case 2:                          /* Fall thru deliberately */
+       return(ssplen);
+      case 3:                          /* Listsize */
+       if (mtchs) {
+           free((char *)mtchs);
+           mtchs = NULL;
+       }
+       mtchs = (char **)malloc(n * sizeof(char *));
+       if (!mtchs)
+         return(-1);
+       maxnames = n;
+      case 4:                          /* Fall thru deliberately */
+       return(maxnames);
+    }
+#endif /* DYNAMIC */
+    return(-1);
+}
+
+
+
+#ifndef NONZXPAND
+#ifndef pdp11
+static
+#endif /* pdp11 */
+#endif /* NONZXPAND */
+int
+zxpand(fnarg) char *fnarg; {
+    extern int diractive;
+    char fnbuf[CKMAXPATH+8], * fn, * p;
+
+#ifdef DTILDE                           /* Built with tilde-expansion? */
+    char *tnam;
+#endif /* DTILDE */
+    int x;
+    int haveonedir = 0;
+
+    if (!fnarg) {                       /* If no argument provided */
+       nxpand = fcount = 0;
+       return(0);                      /* Return zero files found */
+    }
+    debug(F110,"zxpand entry",fnarg,0);
+    debug(F101,"zxpand xdironly","",xdironly);
+    debug(F101,"zxpand xfilonly","",xfilonly);
+
+    if (!*fnarg) {                     /* If no argument provided */
+       nxpand = fcount = 0;
+       return(0);                      /* Return zero files found */
+    }
+
+#ifdef CKROOT
+    debug(F111,"zxpand setroot",ckroot,ckrootset);
+    if (ckrootset) if (!zinroot(fnarg)) {
+       debug(F110,"zxpand setroot violation",fnarg,0);
+       nxpand = fcount = 0;
+       return(0);
+    }
+#endif /* CKROOT */
+
+#ifdef COMMENT
+/*
+  This would have been perfect, except it makes us return fully qualified
+  pathnames for all files.
+*/
+    zfnqfp(fnarg,CKMAXPATH,fnbuf);
+    debug(F110,"zxpand zfnqfp",fnbuf,0);
+    s = zgtdir();
+    debug(F110,"zxpand zgtdir",s,0);
+    p = fnbuf;
+    while (*p && *s)                    /* Make it relative */
+      if (*s++ != *p++)
+        break;
+    fn = (*s) ? fnbuf : p;
+    debug(F110,"zxpand fn 0",fn,0);
+    if (!*fn) {
+        fn = fnbuf;
+        fnbuf[0] = '*';
+        fnbuf[1] = '\0';
+    }
+    debug(F110,"zxpand fn 0.5",fn,0);
+#else
+#ifdef DTILDE                           /* Built with tilde-expansion? */
+    if (*fnarg == '~') {                /* Starts with tilde? */
+        tnam = tilde_expand(fnarg);     /* Try to expand it. */
+        ckstrncpy(fnbuf,tnam,CKMAXPATH);
+    } else
+#endif /* DTILDE */
+      ckstrncpy(fnbuf,fnarg,CKMAXPATH);
+    fn = fnbuf;                         /* Point to what we'll work with */
+#endif /* COMMENT */
+    debug(F110,"zxpand fn 1",fn,0);
+
+    if (!*fn)                           /* But make sure something is there */
+      return(0);
+
+    p = fn + (int)strlen(fn) - 1;
+    if (*p == '/') {                    /* If last char = / it must be a dir */
+       if (!xfilonly && !iswild(p)) haveonedir++;
+        ckstrncat(fn, "*", CKMAXPATH+8); /* so append '*' */
+    } else if (p > fn) {                /* If ends in "/." */
+        if (*(p-1) == '/' && *p == '.') /* change '.' to '*' */
+          *p = '*';
+    } else if (p == fn) {               /* If it's '.' alone */
+        if (*p == '.')                  /* change '.' to '*' */
+          *p = '*';
+    }
+    debug(F110,"zxpand fn 2",fn,0);
+    x = isdir(fn);                      /* Is it a directory? */
+    debug(F111,"zxpand isdir 1",fn,x);
+    if (x) {                            /* If so, make it into a wildcard */
+       if (!xfilonly && !iswild(p))
+         haveonedir++;
+        if ((x = strlen(fn)) > 0) {
+            if (!ISDIRSEP(fn[x-1]))
+              fn[x++] = DIRSEP;
+            fn[x++] = '*';
+            fn[x] = '\0';
+        }
+    }
+    debug(F111,"zxpand fn 3",fn,haveonedir);
+/*
+  The following allows us to parse a single directory name without opening
+  the directory and looking at its contents.  The diractive flag is a horrible
+  hack (especially since DIR /NORECURSIVE turns it off), but otherwise we'd
+  have to change the API.
+*/
+    if (!diractive && haveonedir) {
+#ifdef COMMENT
+       fcount = (mtchs == NULL &&
+                 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
+         ? 0 : 1;
+#else
+       fcount = 0;
+       if (!mtchs) {
+           mtchs = (char **)malloc(maxnames * sizeof(*mtchs));
+           if (mtchs)
+             fcount = 1;
+           if (!fcount)
+             return(nxpand = fcount);
+       }
+#endif /* COMMENT */
+       debug(F110,"zxpand haveonedir A1",fnarg,0);
+       initspace(mtchs,ssplen);
+       addresult(fnarg,1);
+       if (numfnd < 0) return(-1);
+       mtchptr = mtchs;                /* Save pointer for next. */
+       debug(F110,"zxpand haveonedir A2",*mtchptr,0);
+       return(nxpand = fcount);
+    }
+
+#ifndef NOPUSH
+    if (!nopush && wildxpand)           /* Who is expanding wildcards? */
+      fcount = (mtchs == NULL &&        /* Shell */
+                (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
+        ? 0
+          :  shxpand(fn,mtchs,maxnames);
+    else
+#endif /* NOPUSH */
+      fcount = (mtchs == NULL &&        /* Kermit */
+                (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
+        ? 0
+          : fgen(fn,mtchs,maxnames);      /* Look up the file. */
+
+    if (fcount == 0 && haveonedir) {
+       fcount = 1;
+       debug(F110,"zxpand haveonedir B",fnarg,0);
+       addresult(fnarg,1);
+       if (numfnd < 0) return(-1);
+    }
+    mtchptr = mtchs;                    /* Save pointer for next. */
+    nxpand = fcount;
+
+#ifdef DEBUG
+    if (deblog) {
+        if (fcount > 1)
+          debug(F111,"zxpand ok",mtchs[0],fcount);
+        else
+          debug(F101,"zxpand fcount","",fcount);
+    }
+#endif /* DEBUG */
+    return(fcount);
+}
+
+#ifndef NONZXPAND
+/*  N Z X P A N D  --  Expand a file list, with options.  */
+/*
+  Call with:
+   s = pointer to filename or pattern.
+   flags = option bits:
+
+     flags & ZX_FILONLY   Match regular files
+     flags & ZX_DIRONLY   Match directories
+     flags & ZX_RECURSE   Descend through directory tree
+     flags & ZX_MATCHDOT  Match "dot files"
+     flags & ZX_NOBACKUP  Don't match "backup files"
+     flags & ZX_NOLINKS   Don't follow symlinks.
+
+   Returns the number of files that match s, with data structures set up
+   so that first file (if any) will be returned by the next znext() call.
+*/
+int
+nzxpand(s,flags) char * s; int flags; {
+    char * p;
+    int x;
+
+    debug(F111,"nzxpand",s,flags);
+    x = flags & (ZX_DIRONLY|ZX_FILONLY);
+    xdironly = (x == ZX_DIRONLY);
+    xfilonly = (x == ZX_FILONLY);
+    if (xdironly && xfilonly) {
+        xdironly = 0;
+        xfilonly = 0;
+    }
+    xmatchdot  = (flags & ZX_MATCHDOT);
+    debug(F111,"nzxpand xmatchdot 1",s,xmatchdot);
+    /* If xmatchdot not set by caller but pattern implies it, set it anyway */
+    if (!xmatchdot && ((p = ckstrchr(s,'.')))) {
+       if (p == s && p[1] != '/') {
+           xmatchdot = 1;
+           debug(F111,"nzxpand xmatchdot 2",s,xmatchdot);
+       } else if (p > s) {
+           xmatchdot = (*(p-1) == ',') || (*(p-1) == '{') || (*(p-1) == '/');
+           debug(F111,"nzxpand xmatchdot 3",s,xmatchdot);
+       }
+    }
+    xrecursive = (flags & ZX_RECURSE);
+    xnobackup  = (flags & ZX_NOBACKUP);
+    xnolinks   = (flags & ZX_NOLINKS);
+
+#ifdef DEBUG
+    if (deblog) {
+       debug(F101,"nzxpand xdironly","",xdironly);
+       debug(F101,"nzxpand xfilonly","",xfilonly);
+       debug(F101,"nzxpand xmatchdot","",xmatchdot);
+       debug(F101,"nzxpand xrecursive","",xrecursive);
+       debug(F101,"nzxpand xnobackup","",xnobackup);
+       debug(F101,"nzxpand xnolinks","",xnolinks);
+    }
+#endif /* DEBUG */
+
+    x = zxpand(s);
+    if (x > 1)
+      sh_sort(mtchs,NULL,x,0,0,1);     /* Alphabetize the list */
+    xdironly = 0;
+    xfilonly = 0;
+    xmatchdot = 0;
+    xrecursive = 0;
+    xnobackup = 0;
+    xnolinks = 0;
+    return(x);
+}
+#endif /* NONZXPAND */
+
+#ifndef NOZXREWIND
+/*  Z X R E W I N D  --  Rewinds the zxpand() list */
+
+int
+zxrewind() {
+    /* if (!mtchs) return(-1); */
+    fcount = nxpand;
+    mtchptr = mtchs;
+    return(nxpand);
+}
+#endif /* NOZXREWIND */
+
+/*  Z N E X T  --  Get name of next file from list created by zxpand(). */
+/*
+  Returns >0 if there's another file, with its name copied into the arg string,
+  or 0 if no more files in list.
+*/
+int
+znext(fn) char *fn; {
+    if (fcount-- > 0) {
+        ckstrncpy(fn,*mtchptr++,CKMAXPATH);
+    } else {
+        fn[0] = '\0';
+    }
+#ifndef COMMENT
+    debug(F111,"znext",fn,fcount+1);
+    return(fcount+1);
+#else
+    debug(F111,"znext",fn,fcount);      /* Return 0 if no filename to return */
+    return(fcount);
+#endif /* COMMENT */
+}
+
+/*  Z C H K S P A  --  Check if there is enough space to store the file  */
+
+/*
+ Call with file specification f, size n in bytes.
+ Returns -1 on error, 0 if not enough space, 1 if enough space.
+*/
+/*ARGSUSED*/
+int
+#ifdef CK_ANSIC
+zchkspa(char *f, long n)
+#else
+zchkspa(f,n) char *f; long n;
+#endif /* CK_ANSIC */
+/* zchkspa() */ {
+    /* In UNIX there is no good (and portable) way. */
+    return(1);                          /* Always say OK. */
+}
+
+#ifdef COMMENT                         /* (not used) */
+
+/*  I S B A C K U P  --  Tells if given file has a backup suffix  */
+/*
+   Returns:
+   -1: Invalid argument
+    0: File does not have a backup suffix
+   >0: Backup suffix number
+*/
+int
+isbackup(fn) char * fn; {              /* Get backup suffix number */
+    int i, j, k, x, state, flag;
+
+    if (!fn)                           /* Watch out for null pointers. */
+      return(-1);
+    if (!*fn)                          /* And empty names. */
+      return(-1);
+
+    flag = state = 0;
+    for (i = (int)strlen(fn) - 1; (!flag && (i > 0)); i--) {
+       switch (state) {
+         case 0:                       /* State 0 - final char */
+           if (fn[i] == '~')           /* Is tilde */
+             state = 1;                /* Switch to next state */
+           else                        /* Otherwise */
+             flag = 1;                 /* Quit - no backup suffix. */
+           break;
+         case 1:                       /* State 1 - digits */
+           if (fn[i] == '~'  && fn[i-1] == '.') { /* Have suffix */
+               return(atoi(&fn[i+1]));
+           } else if (fn[i] >= '0' && fn[i] <= '9') { /* In number part */
+               continue;               /* Keep going */
+           } else {                    /* Something else */
+               flag = 1;               /* Not a backup suffix - quit. */
+           }
+           break;
+       }
+    }
+    return(0);
+}
+#endif /* COMMENT */
+
+
+/*  Z N E W N  --  Make a new name for the given file  */
+
+/*
+  Given the name, fn, of a file that already exists, this function builds a
+  new name of the form "<oldname>.~<n>~", where <oldname> is argument name
+  (fn), and <n> is a version number, one higher than any existing version
+  number for that file, up to 99999.  This format is consistent with that used
+  by GNU EMACS.  If the constructed name is too long for the system's maximum,
+  enough characters are truncated from the end of <fn> to allow the version
+  number to fit.  If no free version numbers exist between 1 and 99999, a
+  version number of "xxxx" is used.  Returns a pointer to the new name in
+  argument s.
+*/
+#ifdef pdp11
+#define ZNEWNBL 63                      /* Name buffer length */
+#define ZNEWNMD 3                       /* Max digits for version number */
+#else
+#define ZNEWNBL CKMAXPATH
+#define ZNEWNMD 4
+#endif /* pdp11 */
+
+#define MAXBUDIGITS 5
+
+static char znewbuf[ZNEWNBL+12];
+
+VOID
+znewn(fn,s) char *fn, **s; {
+    char * buf;                                /* Pointer to buffer for new name */
+    char * xp, * namepart = NULL;       /* Pointer to filename part */
+    struct zfnfp * fnfp;                /* znfqfp() result struct pointer */
+    int d = 0, t, fnlen, buflen;
+    int n, i, k, flag, state;
+    int max = MAXNAMLEN;                /* Maximum name length */
+    char * dname = NULL;
+
+    buf = znewbuf;
+    *s = NULL;                          /* Initialize return value */
+    if (!fn) fn = "";                   /* Check filename argument */
+    i = strlen(fn);
+
+/* If incoming file already has a backup suffix, remove it. */
+/* Then we'll tack a new on later, which will be the highest for this file. */
+
+    if (i <= max && i > 0 && fn[i-1] == '~') {
+       char * p;
+       i--;
+       debug(F111,"znewn suffix removal",fn,i);
+       if ((dname = (char *)malloc(i+1))) {
+           ckstrncpy(dname,fn,i+1);
+           p = dname;
+           for (flag = state = 0; (!flag && (i > 0)); i--) {
+               switch (state) {
+                 case 0:               /* State 0 - final char */
+                   if (p[i] == '~')    /* Is tilde */
+                     state = 1;        /* Switch to next state */
+                   else                /* Otherwise */
+                     flag = 1;         /* Quit - no backup suffix. */
+                   break;
+                 case 1:               /* State 1 - digits */
+                   if (p[i] == '~'  && p[i-1] == '.') { /* Have suffix */
+                       p[i-1] = NUL;   /* Trim it */
+                       fn = dname;
+                       debug(F111,"znewn suffix removal 2",fn,i);
+                       flag = 1;       /* done */
+                   } else if (p[i] >= '0' && p[i] <= '9') { /* Number part */
+                       continue;       /* Keep going */
+                   } else {            /* Something else */
+                       flag = 1;       /* Not a backup suffix - quit. */
+                   }
+                   break;
+               }
+           }
+       }
+    }
+    if ((fnlen = strlen(fn)) < 1) {    /* Get length */
+       if (dname) free(dname);
+       return;
+    }
+    debug(F111,"znewn",fn,fnlen);
+
+    debug(F101,"znewn max 1","",max);
+    if (max < 14) max = 14;             /* Make max reasonable for any UNIX */
+    if (max > ZNEWNBL) max = ZNEWNBL;
+    debug(F101,"znewn max 2","",max);
+
+    if ((fnfp = zfnqfp(fn, ZNEWNBL, buf))) { /* Get fully qualified name */
+        namepart = fnfp->fname;         /* Isolate the filename */
+        k = strlen(fn);                 /* Length of name part */
+        debug(F111,"znewn namepart",namepart,k);
+    } else {
+       if (dname) free(dname);
+       return;
+    }
+    buflen = fnfp->len;                 /* Length of fully qualified name */
+    debug(F111,"znewn len",buf,buflen);
+
+    if (k + MAXBUDIGITS + 3 < max) {    /* Backup name fits - no overflow */
+       /* Make pattern for backup names */
+        ckstrncpy(buf+buflen,".~*~",ZNEWNBL+12-buflen);
+        n = nzxpand(buf,ZX_FILONLY);    /* Expand the pattern */
+        debug(F111,"znewn A matches",buf,n);
+        while (n-- > 0) {               /* Find any existing name.~n~ files */
+            xp = *mtchptr++;            /* Point at matching name */
+            t = atoi(xp+buflen+2);      /* Get number */
+            if (t > d) d = t;           /* Save d = highest version number */
+        }
+        sprintf(buf+buflen,".~%d~",d+1); /* Yes, make "name.~<d+1>~" */
+        debug(F110,"znewn A newname",buf,0);
+    } else {                            /* Backup name would be too long */
+        int xlen;                       /* So we have to eat back into it */
+        int delta;
+        char buf2[ZNEWNBL+12];
+
+        delta = max - k;
+        debug(F101,"znewn B delta","",delta);
+
+        for (i = MAXBUDIGITS; i > 0; i--) { /* In this case the format of */
+            ckstrncpy(buf2,buf,ZNEWNBL+12); /* the backup name depends on */
+            xlen = buflen - i - 3 + delta;  /* how many digits are in the */
+            ckstrncpy(buf2+xlen,".~*~",ZNEWNBL+12-xlen); /* backup number */
+            n = nzxpand(buf2,ZX_FILONLY);
+            debug(F111,"znewn B matches",buf2,n);
+            if (n > 0)
+              break;
+        }
+        while (n-- > 0) {               /* Find any existing name.~n~ files */
+            xp = *mtchptr++;            /* Point at matching name */
+            t = atoi(xp+xlen+2);        /* Get number */
+            if (t > d) d = t;           /* Save d = highest version number */
+        }
+        if (d > 0)                      /* If the odometer turned over... */
+          if ((d % 10) == 9)            /* back up one space. */
+            xlen--;
+        sprintf(buf2+xlen,".~%d~",d+1); /* This just fits */
+        ckstrncpy(buf,buf2,ZNEWNBL+12);        /* (we could be more clever here...) */
+        debug(F110,"znewn B new name",buf,0);
+    }
+    *s = buf;                           /* Point to new name */
+    ck_znewn = d+1;                     /* Also make it available globally */
+    if (dname) free(dname);
+    return;
+}
+
+/*  Z R E N A M E  --  Rename a file  */
+/*
+   Call with old and new names.
+   If new name is the name of a directory, the 'old' file is moved to
+   that directory.
+   Returns 0 on success, -1 on failure.
+*/
+int
+zrename(old,new) char *old, *new; {
+    char *p, *s;
+    int x;
+
+    if (!old) old = "";
+    if (!new) new = "";
+    debug(F110,"zrename old",old,0);
+    debug(F110,"zrename new",new,0);
+    if (!*old) return(-1);
+    if (!*new) return(-1);
+
+#ifdef IKSD
+#ifdef CK_LOGIN
+    if (inserver && isguest)
+      return(-1);
+#endif /* CK_LOGIN */
+#endif /* IKSD */
+
+#ifdef CKROOT
+    debug(F111,"zrename setroot",ckroot,ckrootset);
+    if (ckrootset) {
+       if (!zinroot(old)) {
+           debug(F110,"zrename old: setroot violation",old,0);
+           return(-1);
+       }
+       if (!zinroot(new)) {
+           debug(F110,"zrename new: setroot violation",new,0);
+           return(-1);
+       }
+    }
+#endif /* CKROOT */
+
+    p = NULL;
+    s = new;
+
+    if (isdir(new)) {
+        char *q = NULL;
+        x = strlen(new);
+        if (!(p = malloc(strlen(new) + strlen(old) + 2)))
+          return(-1);
+        strcpy(p,new);                  /* (safe) Directory part */
+        if (!ISDIRSEP(*(new+x-1)))      /* Separator, if needed */
+          strcat(p,"/");               /* (safe) */
+        zstrip(old,&q);                 /* Strip path part from old name */
+        strcat(p,q);                    /* cat to new directory (safe) */
+        s = p;
+        debug(F110,"zrename dir",s,0);
+    }
+#ifdef DEBUG
+    else debug(F110,"zrename no dir",s,0);
+#endif /* DEBUG */
+
+#ifdef IKSD
+    if (inserver && (!ENABLED(en_del))) {
+       if (zchki(s) > -1)              /* Destination file exists? */
+         return(-1);
+    }
+#endif /* IKSD */
+
+    x = -1;                             /* Return code. */
+#ifdef RENAME
+/* Atomic, preferred, uses a single system call, rename(), if available. */
+    x = rename(old,s);
+    debug(F111,"zrename rename()",old,x);
+    if (x) x = -1;
+#endif /* RENAME */
+
+    /* If rename() failed or not available try link()/unlink() */
+
+    if (x < 0) {
+       if (zchko(old) > -1) {          /* Requires write access to orignal */
+           x = link(old,s);
+           debug(F111,"zrename link()",old,x);
+           if (x > -1) {               /* Make a link with the new name. */
+               x = unlink(old);
+               debug(F111,"zrename unlink()",old,x);
+           }
+           /* If link/unlink failed copy and delete */
+           if (x < 0) {
+               x = zcopy(old,s);
+               debug(F111,"zrename zcopy()",old,x);
+               if (x > -1) {
+                   x = zdelet(old);
+                   debug(F111,"zrename zdelet()",old,x);
+               }
+           }
+       }
+    }
+    fullname[0] = '\0';                        /* Clear this out for next time. */
+
+#ifdef CKSYSLOG
+    if (ckxsyslog >= SYSLG_FC && ckxlogging) {
+        zfnqfp(old,CKMAXPATH,fullname);
+        tmp2[0] = '\0';
+        zfnqfp(s,CKMAXPATH,tmp2);
+        if (x > -1)
+          syslog(LOG_INFO,"file[] %s: renamed to %s ok", fullname, tmp2);
+        else
+          syslog(LOG_INFO,"file[] %s: rename to %s failed (%m)",fullname,tmp2);
+    }
+#endif /* CKSYSLOG */
+
+    if (p) free(p);
+    return(x);
+}
+
+/*  Z C O P Y  --  Copy a single file. */
+/*
+  Call with source and destination names.
+  If destination is a directory, the source file is
+  copied to that directory with its original name.
+  Returns:
+   0 on success.
+  <0 on failure:
+  -2 = source file is not a regular file.
+  -3 = source file not found.
+  -4 = permission denied.
+  -5 = source and destination are the same file.
+  -6 = i/o error.
+  -1 = other error.
+*/
+int
+zcopy(source,destination) char *source, *destination; {
+    char *src, *dst;                   /* Local pointers to filenames */
+    int x, y, rc;                       /* Workers */
+    int in = -1, out = -1;              /* i/o file descriptors */
+    struct stat srcbuf;                 /* Source file info buffer */
+    int perms;                          /* Output file permissions */
+    char buf[1024];                     /* File copying buffer */
+
+    if (!source) source = "";
+    if (!destination) destination = "";
+
+    debug(F110,"zcopy src arg",source,0);
+    debug(F110,"zcopy dst arg",destination,0);
+
+    if (!*source) return(-1);
+    if (!*destination) return(-1);
+
+#ifdef IKSD
+#ifdef CK_LOGIN
+    if (inserver && isguest)
+      return(-4);
+#endif /* CK_LOGIN */
+#endif /* IKSD */
+
+#ifdef CKROOT
+    debug(F111,"zcopy setroot",ckroot,ckrootset);
+    if (ckrootset) {
+       if (!zinroot(source)) {
+           debug(F110,"zcopy source: setroot violation",source,0);
+           return(-1);
+       }
+       if (!zinroot(destination)) {
+           debug(F110,"zcopy destination: setroot violation",destination,0);
+           return(-1);
+       }
+    }
+#endif /* CKROOT */
+
+    src = source;
+    dst = destination;
+
+    if (stat(src,&srcbuf) == 0) {       /* Get source file info */
+        struct stat dstbuf;             /* Destination file info buffer */
+       debug(F101,"STAT","",6);
+        if (stat(dst,&dstbuf) == 0) {
+           debug(F101,"STAT","",7);
+            if (srcbuf.st_dev == dstbuf.st_dev)
+              if (srcbuf.st_ino == dstbuf.st_ino) {
+                  debug(F100,"zcopy files identical: stat()","",0);
+                  return(-5);
+              }
+        }
+    } else {                            /* stat() failed... */
+       debug(F101,"STAT","",8);
+        debug(F111,"source file not found",src,errno);
+        return(-3);
+    }
+    fullname[0] = '\0';                 /* Get full pathnames */
+    if (zfnqfp(source,CKMAXPATH,fullname))
+      src = fullname;
+    debug(F110,"zcopy src",src,0);
+    tmp2[0] = '\0';
+    if (zfnqfp(destination,CKMAXPATH,tmp2))
+      dst = tmp2;
+    debug(F110,"zcopy dst 1",dst,0);
+    if (!strcmp(src,dst)) {             /* Src and dst are same file? */
+        debug(F100,"zcopy files identical: strcmp()","",0); /* This... */
+        return(-5);                     /* should not happen. */
+    }
+    if (isdir(src)) {                   /* Source file is a directory? */
+        debug(F110,"zcopy source is directory",src,0);
+        return(-2);                     /* Fail */
+    }
+    if (isdir(dst)) {                   /* Destination is a directory? */
+        char *q = NULL;                 /* Yes, add filename to it. */
+        x = strlen(dst);
+       if (x < 1) return(-1);
+        if (!ISDIRSEP(*(dst+x-1))) {    /* Add separator if needed */
+            tmp2[x++] = '/';
+            tmp2[x] = '\0';
+        }
+       debug(F111,"zcopy dst 2",dst,x);
+        zstrip(src,&q);                 /* Strip path part from old name */
+        ckstrncpy(tmp2+x,q,CKMAXPATH-x); /* Concatenate it to new name */
+    }
+    debug(F110,"zcopy dst 3",dst,0);
+
+#ifdef IKSD
+    if (inserver && (!ENABLED(en_del))) {
+       if (zchki(dst) > -1)            /* Destination file exists? */
+         return(-4);
+    }
+#endif /* IKSD */
+
+    perms = umask(0);                   /* Get user's umask */
+    umask(perms);                      /* Put it back! */
+    perms ^= 0777;                      /* Flip the bits */
+    perms &= 0666;                      /* Zero execute bits from umask */
+    perms |= (srcbuf.st_mode & 0111);   /* OR in source file's execute bits */
+    rc = -1;                            /* Default return code */
+    errno = 0;                          /* Reset errno */
+    in = open(src, O_RDONLY, 0);        /* Open source file */
+    debug(F111,"zcopy open source",src,in);
+    if (in > -1) {                      /* If open... */
+       /* Open destination file */
+#ifdef O_TRUNC
+        out = open(dst, O_WRONLY|O_CREAT|O_TRUNC, perms);
+#else
+        out = open(dst, O_WRONLY|O_CREAT, perms);
+#endif /* O_TRUNC */
+        debug(F111,"zcopy open dest",dst,out);
+        if (out > -1) {                 /* If open... */
+            while ((x = read(in,buf,1024)) > 0) { /* Copy in 1K blocks */
+                y = write(out,buf,x);
+                if (y < 0) {            /* On write failure */
+                    x = -1;
+                    rc = -6;            /* Indicate i/o error */
+                    break;
+                }
+            }
+            debug(F101,"zcopy final read","",x);
+            debug(F101,"zcopy errno","",errno);
+            rc = (x == 0) ? 0 : -6;     /* In case of read failure */
+        }
+    }
+    if (in > -1) close(in);             /* Close files */
+    if (out > -1) close(out);
+    if (rc == -1) {                     /* Set return code */
+        switch (errno) {
+          case ENOENT: rc = -3; break;
+          case EACCES: rc = -4; break;
+          case EIO:    rc = -6;
+        }
+    }
+
+#ifdef CKSYSLOG
+    if (rc > -1 && ckxsyslog >= SYSLG_FC && ckxlogging) {
+        if (rc)
+          syslog(LOG_INFO,"file[] %s: copy to %s failed (%m)", fullname, tmp2);
+        else
+          syslog(LOG_INFO,"file[] %s: copy to %s ok", fullname, tmp2);
+    }
+#endif /* CKSYSLOG */
+
+    return(rc);
+}
+
+/*  Z S A T T R */
+/*
+ Fills in a Kermit file attribute structure for the file which is to be sent.
+ Returns 0 on success with the structure filled in, or -1 on failure.
+ If any string member is null, then it should be ignored.
+ If any numeric member is -1, then it should be ignored.
+*/
+#ifdef CK_PERMS
+
+#ifdef CK_GPERMS
+#undef CK_GPERMS
+#endif /* CK_GPERMS */
+
+#ifdef UNIX
+#ifndef S_IRUSR
+#define S_IRUSR 0400
+#endif /* S_IRUSR */
+#ifndef S_IWUSR
+#define S_IXUSR 0200
+#endif /* S_IWUSR */
+#ifndef S_IXUSR
+#define S_IXUSR 0100
+#endif /* S_IXUSR */
+#endif /* UNIX */
+
+#ifdef S_IRUSR
+#ifdef S_IWUSR
+#ifdef S_IXUSR
+#define CK_GPERMS
+#endif /* S_IXUSR */
+#endif /* S_IWUSR */
+#endif /* S_IRUSR */
+
+static char gperms[2];
+
+#endif /* CK_GPERMS */
+
+static char lperms[24];
+
+#ifdef CK_PERMS
+static char xlperms[24];
+
+/*  Z S E T P E R M  --  Set permissions of a file  */
+
+int
+zsetperm(f,code) char * f; int code; {
+    int x;
+#ifdef CK_SCO32V4
+    mode_t mask;
+#else
+    int mask;
+#endif /* CK_SCO32V4 */
+    mask = code;
+    if (inserver && guest) {
+       debug(F110,"zsetperm guest",f,0);
+       return(0);
+    }
+    x = chmod(f,mask);
+    if (x < 0) {
+       debug(F111,"zsetperm error",f,errno);
+       return(0);
+    }
+    debug(F111,"zsetperm ok",f,mask);
+    return(1);
+}
+
+/*  Z G P E R M  --  Get permissions of a file as an octal string  */
+
+char *
+zgperm(f) char *f; {
+    extern int diractive;
+    int x; char *s = (char *)xlperms;
+    struct stat buf;
+    debug(F110,"zgperm",f,0);
+    if (!f) return("----------");
+    if (!*f) return("----------");
+
+#ifdef CKROOT
+    debug(F111,"zgperm setroot",ckroot,ckrootset);
+    if (ckrootset) if (!zinroot(f)) {
+       debug(F110,"zgperm setroot violation",f,0);
+       return("----------");
+    }
+#endif /* CKROOT */
+
+#ifdef USE_LSTAT
+    if (diractive)
+      x = lstat(f,&buf);
+    else
+#endif /* USE_LSTAT */
+      x = stat(f,&buf);
+    debug(F101,"STAT","",9);
+    if (x < 0)
+      return("----------");
+    sprintf(s,"%o",buf.st_mode);
+    debug(F110,"zgperm",s,0);
+    return(s);
+}
+
+/* Like zgperm() but returns permissions in "ls -l" string format */
+
+static char xsperms[24];
+
+char *
+ziperm(f) char * f; {
+    extern int diractive;
+    int x; char *s = (char *)xsperms;
+    struct stat buf;
+    unsigned int perms = 0;
+
+    debug(F110,"ziperm",f,0);
+
+    if (!f) return(NULL);
+    if (!*f) return(NULL);
+
+    if (diractive && zgfs_mode != 0) {
+       perms = zgfs_mode;              /* zgetfs() already got them */
+    } else {
+#ifdef USE_LSTAT
+       if (diractive)
+         x = lstat(f,&buf);
+       else
+#endif /* USE_LSTAT */
+         x = stat(f,&buf);
+       debug(F101,"STAT","",10);
+       if (x < 0)
+         return("----------");
+       perms = buf.st_mode;
+    }
+    switch (perms & S_IFMT) {
+      case S_IFDIR:
+        *s++ = 'd';
+        break;
+      case S_IFCHR:                     /* Character special */
+        *s++ = 'c';
+        break;
+      case S_IFBLK:                     /* Block special */
+        *s++ = 'b';
+        break;
+      case S_IFREG:                     /* Regular */
+        *s++ = '-';
+        break;
+#ifdef S_IFLNK
+      case S_IFLNK:                     /* Symbolic link */
+        *s++ = 'l';
+        break;
+#endif /* S_IFLNK */
+#ifdef S_IFSOCK
+      case S_IFSOCK:                    /* Socket */
+        *s++ = 's';
+        break;
+#endif /* S_IFSOCK */
+#ifdef S_IFIFO
+#ifndef Plan9
+#ifndef COHERENT
+      case S_IFIFO:                     /* FIFO */
+        *s++ = 'p';
+        break;
+#endif /* COHERENT */
+#endif /* Plan9 */
+#endif /* S_IFIFO */
+#ifdef S_IFWHT
+      case S_IFWHT:                     /* Whiteout */
+        *s++ = 'w';
+        break;
+#endif /* S_IFWHT */
+      default:                          /* Unknown */
+        *s++ = '?';
+        break;
+    }
+    if (perms & S_IRUSR)          /* Owner's permissions */
+      *s++ = 'r';
+    else
+      *s++ = '-';
+    if (perms & S_IWUSR)
+      *s++ = 'w';
+    else
+      *s++ = '-';
+    switch (perms & (S_IXUSR | S_ISUID)) {
+      case 0:
+        *s++ = '-';
+        break;
+      case S_IXUSR:
+        *s++ = 'x';
+        break;
+      case S_ISUID:
+        *s++ = 'S';
+        break;
+      case S_IXUSR | S_ISUID:
+        *s++ = 's';
+        break;
+    }
+    if (perms & S_IRGRP)          /* Group permissions */
+      *s++ = 'r';
+    else
+      *s++ = '-';
+    if (perms & S_IWGRP)
+      *s++ = 'w';
+    else
+      *s++ = '-';
+    switch (perms & (S_IXGRP | S_ISGID)) {
+      case 0:
+        *s++ = '-';
+        break;
+      case S_IXGRP:
+        *s++ = 'x';
+        break;
+      case S_ISGID:
+        *s++ = 'S';
+        break;
+      case S_IXGRP | S_ISGID:
+        *s++ = 's';
+        break;
+    }
+    if (perms & S_IROTH)          /* World permissions */
+      *s++ = 'r';
+    else
+      *s++ = '-';
+    if (perms & S_IWOTH)
+      *s++ = 'w';
+    else
+      *s++ = '-';
+    switch (
+#ifdef Plan9
+            perms & (S_IXOTH)
+#else
+            perms & (S_IXOTH | S_ISVTX)
+#endif
+            ) {
+      case 0:
+        *s++ = '-';
+        break;
+      case S_IXOTH:
+        *s++ = 'x';
+        break;
+#ifndef Plan9
+      case S_ISVTX:
+        *s++ = 'T';
+        break;
+      case S_IXOTH | S_ISVTX:
+        *s++ = 't';
+        break;
+#endif /* Plan9 */
+    }
+    *s = '\0';
+    debug(F110,"ziperm",xsperms,0);
+    return((char *)xsperms);
+}
+
+#else
+
+char *
+zgperm(f) char *f; {
+    return("----------");
+}
+char *
+ziperms(f) char *f; {
+    return("----------");
+}
+#endif /* CK_PERMS */
+
+int
+zsattr(xx) struct zattr *xx; {
+    long k; int x;
+    struct stat buf;
+
+    k = iflen % 1024L;                  /* File length in K */
+    if (k != 0L) k = 1L;
+    xx->lengthk = (iflen / 1024L) + k;
+    xx->type.len = 0;                   /* File type can't be filled in here */
+    xx->type.val = "";
+    if (*nambuf) {
+        xx->date.val = zfcdat(nambuf);  /* File creation date */
+        xx->date.len = (int)strlen(xx->date.val);
+    } else {
+        xx->date.len = 0;
+        xx->date.val = "";
+    }
+    xx->creator.len = 0;                /* File creator */
+    xx->creator.val = "";
+    xx->account.len = 0;                /* File account */
+    xx->account.val = "";
+    xx->area.len = 0;                   /* File area */
+    xx->area.val = "";
+    xx->password.len = 0;               /* Area password */
+    xx->password.val = "";
+    xx->blksize = -1L;                  /* File blocksize */
+    xx->xaccess.len = 0;                /* File access */
+    xx->xaccess.val = "";
+    xx->encoding.len = 0;               /* Transfer syntax */
+    xx->encoding.val = 0;
+    xx->disp.len = 0;                   /* Disposition upon arrival */
+    xx->disp.val = "";
+    xx->lprotect.len = 0;               /* Local protection */
+    xx->lprotect.val = "";
+    xx->gprotect.len = 0;               /* Generic protection */
+    xx->gprotect.val = "";
+    x = -1;
+    if (*nambuf) x = stat(nambuf,&buf);
+    debug(F101,"STAT","",11);
+    if (x >= 0) {
+        debug(F111,"zsattr buf.st_mode & 0777",nambuf,buf.st_mode & 0777);
+        /* UNIX filemode as an octal string without filetype bits */
+        sprintf(lperms,"%o",buf.st_mode & 0777);
+        xx->lprotect.len = (int)strlen(lperms);
+        xx->lprotect.val = (char *)lperms;
+        x = 0;
+#ifdef CK_GPERMS
+        /* Generic permissions only if we have stat.h symbols defined */
+        if (buf.st_mode & S_IRUSR) x |= 1;      /* Read */
+        if (buf.st_mode & S_IWUSR) x |= (2+16); /* Write and Delete */
+        if (buf.st_mode & S_IXUSR) x |= 4;      /* Execute */
+        gperms[0] = tochar(x);
+        gperms[1] = NUL;
+        xx->gprotect.len = 1;
+        xx->gprotect.val = (char *)gperms;
+#endif /* CK_GPERMS */
+    }
+    debug(F111,"zsattr lperms",xx->lprotect.val,xx->lprotect.len);
+    debug(F111,"zsattr gperms",xx->gprotect.val,xx->gprotect.len);
+    xx->systemid.val = "U1";            /* U1 = UNIX */
+    xx->systemid.len = 2;               /* System ID */
+    xx->recfm.len = 0;                  /* Record format */
+    xx->recfm.val = "";
+    xx->sysparam.len = 0;               /* System-dependent parameters */
+    xx->sysparam.val = "";
+    xx->length = iflen;                 /* Length */
+    return(0);
+}
+
+/* Z F C D A T  --  Get file creation date */
+/*
+  Call with pointer to filename.
+  On success, returns pointer to modification date in yyyymmdd hh:mm:ss format.
+  On failure, returns pointer to null string.
+*/
+static char datbuf[40];
+
+char *
+#ifdef CK_ANSIC
+zdtstr(time_t timearg)
+#else
+zdtstr(timearg) time_t timearg;
+#endif /* CK_ANSIC */
+/* zdtstr */ {
+#ifndef TIMESTAMP
+    return("");
+#else
+    struct tm * time_stamp;
+    struct tm * localtime();
+    int yy, ss;
+
+    debug(F101,"zdtstr timearg","",timearg);
+    if (timearg < 0)
+      return("");
+    time_stamp = localtime(&(timearg));
+    if (!time_stamp) {
+        debug(F100,"localtime returns null","",0);
+        return("");
+    }
+/*
+  We assume that tm_year is ALWAYS years since 1900.
+  Any platform where this is not the case will have problems
+  starting in 2000.
+*/
+    yy = time_stamp->tm_year;           /* Year - 1900 */
+    debug(F101,"zdtstr tm_year","",time_stamp->tm_year);
+    if (yy > 1000) {
+        debug(F101,"zstrdt YEAR-2000 ALERT 1: localtime year","",yy);
+    }
+    yy += 1900;
+    debug(F101,"zdatstr year","",yy);
+
+    if (time_stamp->tm_mon  < 0 || time_stamp->tm_mon  > 11)
+      return("");
+    if (time_stamp->tm_mday < 0 || time_stamp->tm_mday > 31)
+      return("");
+    if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 23)
+      return("");
+    if (time_stamp->tm_min  < 0 || time_stamp->tm_min  > 59)
+      return("");
+    ss = time_stamp->tm_sec;            /* Seconds */
+    if (ss < 0 || ss  > 59)             /* Some systems give a BIG number */
+      ss = 0;
+    sprintf(datbuf,
+#ifdef pdp11
+/* For some reason, 2.1x BSD sprintf gets the last field wrong. */
+            "%04d%02d%02d %02d:%02d:00",
+#else
+            "%04d%02d%02d %02d:%02d:%02d",
+#endif /* pdp11 */
+            yy,
+            time_stamp->tm_mon + 1,
+            time_stamp->tm_mday,
+            time_stamp->tm_hour,
+            time_stamp->tm_min
+#ifndef pdp11
+            , ss
+#endif /* pdp11 */
+            );
+    yy = (int)strlen(datbuf);
+    debug(F111,"zdatstr",datbuf,yy);
+    if (yy > 17) datbuf[17] = '\0';
+    return(datbuf);
+#endif /* TIMESTAMP */
+}
+
+char *
+zfcdat(name) char *name; {
+#ifdef TIMESTAMP
+    struct stat buffer;
+    extern int diractive;
+    unsigned int mtime;
+    int x;
+    char * s;
+
+    if (!name)
+      return("");
+    s = name;
+    if (!*s)
+      return("");
+
+#ifdef CKROOT
+    debug(F111,"zfcdat setroot",ckroot,ckrootset);
+    if (ckrootset) if (!zinroot(name)) {
+       debug(F110,"zfcdat setroot violation",name,0);
+       return("");
+    }
+#endif /* CKROOT */
+
+#ifdef DTILDE
+    if (*s == '~') {
+        s = tilde_expand(s);
+        if (!s) s = "";
+        if (!*s) s = name;
+    }
+#endif /* DTILDE */
+
+    datbuf[0] = '\0';
+    x = 0;
+    debug(F111,"zfcdat",s,diractive);
+
+    if (diractive && zgfs_mtime) {
+       mtime = zgfs_mtime;
+    } else {
+#ifdef USE_LSTAT
+       if (diractive) {
+           x = lstat(s,&buffer);
+           debug(F101,"STAT","",12);
+           debug(F101,"zfcdat lstat","",x);
+       } else {
+#endif /* USE_LSTAT */
+           x = stat(s,&buffer);
+           debug(F101,"STAT","",13);
+           debug(F101,"zfcdat stat","",x);
+#ifdef USE_LSTAT
+       }
+#endif /* USE_LSTAT */
+       if (x != 0) {
+#ifdef USE_LSTAT
+           debug(F111,"zfcdat stat failed",s,errno);
+#else
+           debug(F111,"zfcdat lstat failed",s,errno);
+#endif /* USE_LSTAT */
+           return("");
+       }
+       debug(F101,"zfcdat buffer.st_mtime","",buffer.st_mtime);
+       mtime = buffer.st_mtime;
+    }
+    return(zdtstr(mtime));
+#else
+    return("");
+#endif /* TIMESTAMP */
+}
+
+#ifndef NOTIMESTAMP
+
+/* Z S T R D T  --  Converts local date string to internal representation */
+/*
+  In our case (UNIX) this is seconds since midnite 1 Jan 1970 UTC,
+  suitable for comparison with UNIX file dates.  As far as I know, there is
+  no library or system call -- at least nothing reasonably portable -- to
+  convert local time to UTC.
+*/
+time_t
+zstrdt(date,len) char * date; int len; {
+#ifdef M_UNIX
+/*
+  SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime().
+  ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in
+  dependence on the XPG4 supplement presence.  So always use
+  what the system header file supplies in ODT 3.0...
+*/
+#ifndef ODT30
+#ifndef _SCO_DS
+    extern void ftime();  /* extern void ftime(struct timeb *) */
+#endif /* _SCO_DS */
+#endif /* ODT30 */
+#else
+#ifndef M_XENIX
+    extern int ftime();
+#endif /* M_XENIX */
+#endif /* M_UNIX */
+    extern struct tm * localtime();
+
+    /* And this should have been declared always through a header file */
+#ifdef HPUX10
+    time_t tmx;
+    long days;
+#else
+#ifdef BSD44
+    time_t tmx;
+    long days;
+#else
+    long tmx, days;
+#endif /* BSD44 */
+#endif /* HPUX10 */
+    int i, n, isleapyear;
+                   /*       J  F  M  A   M   J   J   A   S   O   N   D   */
+                   /*      31 28 31 30  31  30  31  31  30  31  30  31   */
+    static
+    int monthdays [13] = {  0,0,31,59,90,120,151,181,212,243,273,304,334 };
+    char s[5];
+    struct tm *time_stamp;
+
+#ifdef BSD44
+    struct timeval tp[2];
+    long xtimezone = 0L;
+#else
+#ifdef V7
+    struct utimbuf {
+      time_t timep[2];          /* New access and modificaton time */
+    } tp;
+    char *tz;
+    long timezone;              /* In case timezone not defined in .h file */
+#else
+#ifdef SYSUTIMEH
+    struct utimbuf tp;
+#else
+    struct utimbuf {
+        time_t atime;
+        time_t mtime;
+    } tp;
+#endif /* SYSUTIMEH */
+#endif /* V7 */
+#endif /* BSD44 */
+
+#ifdef ANYBSD
+    long timezone = 0L;
+    static struct timeb tbp;
+#endif /* ANYBSD */
+
+#ifdef BEBOX
+    long timezone = 0L;
+#endif /* BEBOX */
+
+    debug(F111,"zstrdt",date,len);
+
+    if ((len == 0)
+        || (len != 17)
+        || (date[8] != ' ')
+        || (date[11] != ':')
+        || (date[14] != ':') ) {
+        debug(F111,"Bad creation date ",date,len);
+        return(-1);
+    }
+    debug(F111,"zstrdt date check 1",date,len);
+    for(i = 0; i < 8; i++) {
+        if (!isdigit(date[i])) {
+            debug(F111,"Bad creation date ",date,len);
+            return(-1);
+        }
+    }
+    debug(F111,"zstrdt date check 2",date,len);
+    i++;
+
+    for (; i < 16; i += 3) {
+        if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) {
+            debug(F111,"Bad creation date ",date,len);
+            return(-1);
+        }
+    }
+    debug(F111,"zstrdt date check 3",date,len);
+
+
+#ifdef COMMENT /* was BSD44 */
+/*
+   man gettimeofday on BSDI 3.1 says:
+   "The timezone field is no longer used; timezone information is stored out-
+     side the kernel.  See ctime(3) for more information."  So this chunk of
+   code is effectively a no-op, at least in BSDI 3.x.
+*/
+    {
+        int x;
+        struct timezone tzp;
+        x = gettimeofday(NULL, &tzp);
+        debug(F101,"zstrdt BSD44 gettimeofday","",x);
+        if (x > -1)
+          xtimezone = tzp.tz_minuteswest * 60L;
+        else
+          xtimezone = 0L;
+        debug(F101,"zstrdt BSD44 timezone","",xtimezone);
+    }
+#else
+#ifdef ANYBSD
+    debug(F100,"zstrdt BSD calling ftime","",0);
+    ftime(&tbp);
+    debug(F100,"zstrdt BSD back from ftime","",0);
+    timezone = tbp.timezone * 60L;
+    debug(F101,"zstrdt BSD timezone","",timezone);
+#else
+#ifdef SVORPOSIX
+    tzset();                            /* Set timezone */
+#else
+#ifdef V7
+    if ((tz = getenv("TZ")) == NULL)
+      timezone = 0;                     /* UTC/GMT */
+    else
+      timezone = atoi(&tz[3]);          /* Set 'timezone'. */
+    timezone *= 60L;
+#endif /* V7 */
+#endif /* SVORPOSIX */
+#endif /* ANYBSD */
+#endif /* COMMENT (was BSD44) */
+
+    debug(F100,"zstrdt so far so good","",0);
+
+    s[4] = '\0';
+    for (i = 0; i < 4; i++)             /* Fix the year */
+      s[i] = date[i];
+
+    n = atoi(s);
+    debug(F111,"zstrdt year",s,n);
+    if (n < 1970) {
+        debug(F100,"zstrdt fails - year","",n);
+        return(-1);
+    }
+
+/*  Previous year's leap days.  This won't work after year 2100. */
+
+    isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
+    days = (long) (n - 1970) * 365;
+    days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
+
+    s[2] = '\0';
+
+    for (i = 4; i < 16; i += 2) {
+        s[0] = date[i];
+        s[1] = date[i + 1];
+        n = atoi(s);
+        switch (i) {
+          case 4:                       /* MM: month */
+            if ((n < 1 ) || ( n > 12)) {
+                debug(F111,"zstrdt 4 bad date ",date,len);
+                return(-1);
+            }
+            days += monthdays [n];
+            if (isleapyear && n > 2)
+              ++days;
+            continue;
+
+          case 6:                       /* DD: day */
+            if ((n < 1 ) || ( n > 31)) {
+                debug(F111,"zstrdt 6 bad date ",date,len);
+                return(-1);
+            }
+            tmx = (days + n - 1) * 24L * 60L * 60L;
+            i++;                        /* Skip the space */
+            continue;
+
+          case 9:                       /* hh: hour */
+            if ((n < 0 ) || ( n > 23)) {
+                debug(F111,"zstrdt 9 bad date ",date,len);
+                return(-1);
+            }
+            tmx += n * 60L * 60L;
+            i++;                        /* Skip the colon */
+            continue;
+
+          case 12:                      /* mm: minute */
+            if ((n < 0 ) || ( n > 59)) {
+                debug(F111,"zstrdt 12 bad date ",date,len);
+                return(-1);
+            }
+#ifdef COMMENT /* (was BSD44) */        /* Correct for time zone */
+            tmx += xtimezone;
+            debug(F101,"zstrdt BSD44 tmx","",tmx);
+#else
+#ifdef ANYBSD
+            tmx += timezone;
+#else
+#ifndef CONVEX9 /* Don't yet know how to do this here */
+#ifdef ultrix
+            tmx += (long) timezone;
+#else
+#ifdef Plan9
+            {
+                extern time_t tzoffset;
+                tmx += tzoffset;
+            }
+#else
+#ifndef BSD44
+            tmx += timezone;
+#endif /* BSD44 */
+#endif /* Plan9 */
+#endif /* ultrix */
+#endif /* CONVEX9 */
+#endif /* ANYBSD */
+#endif /* COMMENT (was BSD44) */
+            tmx += n * 60L;
+            i++;                        /* Skip the colon */
+            continue;
+
+          case 15:                      /* ss: second */
+            if ((n < 0 ) || ( n > 59)) {
+                debug(F111,"zstrdt 15 bad date ",date,len);
+                return(-1);
+            }
+            tmx += n;
+        }
+        time_stamp = localtime(&tmx);
+        debug(F101,"zstrdt tmx 1","",tmx);
+        if (!time_stamp)
+          return(-1);
+#ifdef COMMENT
+        /* Why was this here? */
+        time_stamp = localtime(&tmx);
+        debug(F101,"zstrdt tmx 2","",tmx);
+#endif /* COMMENT */
+#ifdef BSD44
+        {   /* New to 7.0 - Works in at at least BSDI 3.1 and FreeBSD 2.2.7 */
+            long zz;
+            zz = time_stamp->tm_gmtoff; /* Seconds away from Zero Meridian */
+            debug(F101,"zstrdt BSD44 tm_gmtoff","",zz);
+            tmx -= zz;
+            debug(F101,"zstrdt BSD44 tmx 3 (GMT)","",tmx);
+        }
+#else
+        /*
+           Daylight Savings Time adjustment.
+           Do this everywhere BUT in BSD44 because in BSD44,
+           tm_gmtoff also includes the DST adjustment.
+        */
+        if (time_stamp->tm_isdst) {
+            tmx -= 60L * 60L;
+            debug(F101,"zstrdt tmx 3 (DST)","",tmx);
+        }
+#endif /* BSD44 */
+        n = time_stamp->tm_year;
+        if (n < 300) {
+            n += 1900;
+        }
+    }
+    return(tmx);
+}
+
+
+#ifdef ZLOCALTIME
+/* Z L O C A L T I M E  --  GMT/UTC time string to local time string */
+
+/*
+   Call with: "yyyymmdd hh:mm:ss" GMT/UTC date-time.
+   Returns:   "yyyymmdd hh:mm:ss" local date-time on success, NULL on failure.
+*/
+static char zltimbuf[64];
+
+char *
+zlocaltime(gmtstring) char * gmtstring; {
+#ifdef M_UNIX
+/*
+  SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime().
+  ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in
+  dependence on the XPG4 supplement presence.  So always use
+  what the system header file supplies in ODT 3.0...
+*/
+#ifndef ODT30
+#ifndef _SCO_DS
+    extern void ftime();  /* extern void ftime(struct timeb *) */
+#endif /* _SCO_DS */
+#endif /* ODT30 */
+#else
+#ifndef M_XENIX
+    extern int ftime();
+#endif /* M_XENIX */
+#endif /* M_UNIX */
+    extern struct tm * localtime();
+
+    /* And this should have been declared always through a header file */
+#ifdef HPUX10
+    time_t tmx;
+    long days;
+#else
+#ifdef BSD44
+    time_t tmx;
+    long days;
+#else
+    long tmx, days;
+#endif /* BSD44 */
+#endif /* HPUX10 */
+    int i, n, x, isleapyear;
+                   /*       J  F  M  A   M   J   J   A   S   O   N   D   */
+                   /*      31 28 31 30  31  30  31  31  30  31  30  31   */
+    static
+    int monthdays [13] = {  0,0,31,59,90,120,151,181,212,243,273,304,334 };
+    char s[5];
+    struct tm *time_stamp;
+
+#ifdef BSD44
+    struct timeval tp[2];
+#else
+#ifdef V7
+    struct utimbuf {
+      time_t timep[2];          /* New access and modificaton time */
+    } tp;
+#else
+#ifdef SYSUTIMEH
+    struct utimbuf tp;
+#else
+    struct utimbuf {
+        time_t atime;
+        time_t mtime;
+    } tp;
+#endif /* SYSUTIMEH */
+#endif /* V7 */
+#endif /* BSD44 */
+
+#ifdef ANYBSD
+    static struct timeb tbp;
+#endif /* ANYBSD */
+
+    char * date = gmtstring;
+    int len;
+
+    len = strlen(date);
+    debug(F111,"zlocaltime",date,len);
+
+    if ((len == 0)
+        || (len != 17)
+        || (date[8] != ' ')
+        || (date[11] != ':')
+        || (date[14] != ':') ) {
+        debug(F111,"Bad creation date ",date,len);
+        return(NULL);
+    }
+    debug(F111,"zlocaltime date check 1",date,len);
+    for(i = 0; i < 8; i++) {
+        if (!isdigit(date[i])) {
+            debug(F111,"Bad creation date ",date,len);
+            return(NULL);
+        }
+    }
+    debug(F111,"zlocaltime date check 2",date,len);
+    i++;
+
+    for (; i < 16; i += 3) {
+        if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) {
+            debug(F111,"Bad creation date ",date,len);
+           return(NULL);
+        }
+    }
+    debug(F111,"zlocaltime date check 3",date,len);
+
+    debug(F100,"zlocaltime so far so good","",0);
+
+    s[4] = '\0';
+    for (i = 0; i < 4; i++)             /* Fix the year */
+      s[i] = date[i];
+
+    n = atoi(s);
+    debug(F111,"zlocaltime year",s,n);
+    if (n < 1970) {
+        debug(F100,"zlocaltime fails - year","",n);
+        return(NULL);
+    }
+
+/*  Previous year's leap days.  This won't work after year 2100. */
+
+    isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
+    days = (long) (n - 1970) * 365;
+    days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
+
+    s[2] = '\0';
+
+    for (i = 4; i < 16; i += 2) {
+        s[0] = date[i];
+        s[1] = date[i + 1];
+        n = atoi(s);
+        switch (i) {
+          case 4:                       /* MM: month */
+            if ((n < 1 ) || ( n > 12)) {
+                debug(F111,"zlocaltime 4 bad date ",date,len);
+                return(NULL);
+            }
+            days += monthdays [n];
+            if (isleapyear && n > 2)
+              ++days;
+            continue;
+
+          case 6:                       /* DD: day */
+            if ((n < 1 ) || ( n > 31)) {
+                debug(F111,"zlocaltime 6 bad date ",date,len);
+                return(NULL);
+            }
+            tmx = (days + n - 1) * 24L * 60L * 60L;
+            i++;                        /* Skip the space */
+            continue;
+
+          case 9:                       /* hh: hour */
+            if ((n < 0 ) || ( n > 23)) {
+                debug(F111,"zlocaltime 9 bad date ",date,len);
+                return(NULL);
+            }
+            tmx += n * 60L * 60L;
+            i++;                        /* Skip the colon */
+            continue;
+
+          case 12:                      /* mm: minute */
+            if ((n < 0 ) || ( n > 59)) {
+                debug(F111,"zlocaltime 12 bad date ",date,len);
+                return(NULL);
+            }
+            tmx += n * 60L;
+            i++;                        /* Skip the colon */
+            continue;
+
+          case 15:                      /* ss: second */
+            if ((n < 0 ) || ( n > 59)) {
+                debug(F111,"zlocaltime 15 bad date ",date,len);
+                return(NULL);
+            }
+            tmx += n;
+        }
+
+/*
+  At this point tmx is the time_t representation of the argument date-time
+  string without any timezone or DST adjustments.  Therefore it should be
+  the same as the time_t representation of the GMT/UTC time.  Now we should
+  be able to feed it to localtime() and have it converted to a struct tm
+  representing the local time equivalent of the given UTC time.
+*/
+        time_stamp = localtime(&tmx);
+        if (!time_stamp)
+          return(NULL);
+    }
+
+/* Now we simply reformat the struct tm to a string */
+
+    x = time_stamp->tm_year;
+    if (time_stamp->tm_year < 70 || time_stamp->tm_year > 8099)
+      return(NULL);
+    if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11)
+      return(NULL);
+    if (time_stamp->tm_mday < 1 || time_stamp->tm_mday > 31)
+      return(NULL);
+    if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 24)
+      return(NULL);
+    if (time_stamp->tm_min < 0 || time_stamp->tm_min > 60)
+      return(NULL);
+    if (time_stamp->tm_sec < 0 || time_stamp->tm_sec > 60)
+      return(NULL);
+    sprintf(zltimbuf,"%04d%02d%02d %02d:%02d:%02d",
+           time_stamp->tm_year + 1900,
+           time_stamp->tm_mon + 1,
+           time_stamp->tm_mday,
+           time_stamp->tm_hour,
+           time_stamp->tm_min,
+           time_stamp->tm_sec
+           );
+    return((char *)zltimbuf);
+}
+#endif /* ZLOCALTIME */
+#endif /* NOTIMESTAMP */
+
+/* Z S T I M E  --  Set modification date/time+permissions for incoming file */
+/*
+ Call with:
+ f  = pointer to name of existing file.
+ yy = pointer to a Kermit file attribute structure in which yy->date.val
+      is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00.
+      yy->lprotect.val & yy->gprotect.val are permission/protection values.
+ x  = is a function code: 0 means to set the file's attributes as given.
+      1 means compare the date in struct yy with the file creation date.
+ Returns:
+ -1 on any kind of error.
+  0 if x is 0 and the attributes were set successfully.
+  0 if x is 1 and date from attribute structure <= file creation date.
+  1 if x is 1 and date from attribute structure > file creation date.
+*/
+int
+zstime(f,yy,x)
+    char *f; struct zattr *yy; int x;
+/* zstime */ {
+    int r = -1;                         /* Return code */
+#ifdef CK_PERMS
+    int setperms = 0;
+#endif /* CK_PERMS */
+    int setdate = 0;
+
+/* It is ifdef'd TIMESTAMP because it might not work on V7. bk@kullmar.se.  */
+
+#ifdef TIMESTAMP
+#ifdef BSD44
+    extern int utimes();
+#else
+    extern int utime();
+#endif /* BSD44 */
+
+    struct stat sb;
+
+/* At least, the declarations for int functions are not needed anyway */
+
+#ifdef BSD44
+    struct timeval tp[2];
+    long xtimezone;
+#else
+#ifdef V7
+    struct utimbuf {
+       time_t timep[2];                /* New access and modificaton time */
+    } tp;
+    char *tz;
+    long timezone;                      /* In case not defined in .h file */
+#else
+#ifdef SYSUTIMEH
+    struct utimbuf tp;
+#else
+    struct utimbuf {
+        time_t atime;
+        time_t mtime;
+    } tp;
+#endif /* SYSUTIMEH */
+#endif /* V7 */
+#endif /* BSD44 */
+
+    long tm = 0L;
+
+    if (!f) f = "";
+    if (!*f) return(-1);
+    if (!yy) return(-1);
+
+    debug(F110,"zstime",f,0);
+    debug(F111,"zstime date",yy->date.val,yy->date.len);
+
+#ifdef CKROOT
+    debug(F111,"zstime setroot",ckroot,ckrootset);
+    if (ckrootset) if (!zinroot(f)) {
+       debug(F110,"zstime setroot violation",f,0);
+       return(0);
+    }
+#endif /* CKROOT */
+
+    if (yy->date.len == 0) {            /* No date in struct */
+        if (yy->lprotect.len != 0) {    /* So go do permissions */
+            goto zsperms;
+        } else {
+            debug(F100,"zstime: nothing to do","",0);
+            return(0);
+        }
+    }
+    if ((tm = zstrdt(yy->date.val,yy->date.len)) < 0) {
+        debug(F101,"zstime: zstrdt fails","",0);
+        return(-1);
+    }
+    debug(F101,"zstime: tm","",tm);
+    debug(F111,"zstime: A-pkt date ok ",yy->date.val,yy->date.len);
+
+    if (stat(f,&sb)) {                  /* Get the time for the file */
+       debug(F101,"STAT","",14);
+        debug(F111,"zstime: Can't stat file:",f,errno);
+        return(-1);
+    }
+    debug(F101,"STAT","",15);
+    setdate = 1;
+
+  zsperms:
+#ifdef CK_PERMS
+    {
+        int i, x = 0, xx, flag = 0;
+        char * s;
+#ifdef DEBUG
+        char obuf[24];
+        if (deblog) {
+            debug(F111,"zstime lperms",yy->lprotect.val,yy->lprotect.len);
+            debug(F111,"zstime gperms",yy->gprotect.val,yy->gprotect.len);
+            debug(F110,"zstime system id",yy->systemid.val,0);
+            sprintf(obuf,"%o",sb.st_mode);
+            debug(F110,"zstime file perms before",obuf,0);
+        }
+#endif /* DEBUG */
+
+#ifdef CK_LOGIN
+        debug(F101,"zstime isguest","",isguest);
+        debug(F101,"zstime ckxperms","",ckxperms);
+        if (isguest) {
+#ifdef COMMENT
+            /* Clear owner permissions */
+            sb.st_mode &= (unsigned) 0177077; /* (16 bits) */
+#else
+            /* Set permissions from ckxperms variable */
+            sb.st_mode = ckxperms;
+#endif /* COMMENT */
+            debug(F101,"zstime isguest sb.st_mode","",sb.st_mode);
+#ifdef COMMENT
+            /* We already set them in zopeno() */
+            setperms = 1;
+#endif /* COMMENT */
+            flag = 0;
+        } else
+#endif /* CK_LOGIN */
+          if ((yy->lprotect.len > 0 &&  /* Have local-format permissions */
+            yy->systemid.len > 0 &&     /* from A-packet... */
+#ifdef UNIX
+            !strcmp(yy->systemid.val,"U1") /* AND you are same as me */
+#else
+            0
+#endif /* UNIX */
+             ) || (yy->lprotect.len < 0) /* OR by inheritance from old file */
+            ) {
+            flag = 1;
+            s = yy->lprotect.val;       /* UNIX filemode */
+            xx = yy->lprotect.len;
+            if (xx < 0)                 /* len < 0 means inheritance */
+              xx = 0 - xx;
+            for (i = 0; i < xx; i++) {  /* Decode octal string */
+                if (*s <= '7' && *s >= '0') {
+                    x = 8 * x + (int)(*s) - '0';
+                } else {
+                    flag = 0;
+                    break;
+                }
+                s++;
+            }
+#ifdef DEBUG
+            sprintf(obuf,"%o",x);
+            debug(F110,"zstime octal lperm",obuf,0);
+#endif /* DEBUG */
+        } else if (!flag && yy->gprotect.len > 0) {
+            int g;
+#ifdef CK_SCO32V4
+            mode_t mask;
+#else
+            int mask;
+#endif /* CK_SCO32V4 */
+            mask = umask(0);            /* Get umask */
+            debug(F101,"zstime mask 1","",mask);
+            umask(mask);                /* Put it back */
+            mask ^= 0777;               /* Flip the bits */
+            debug(F101,"zstime mask 2","",mask);
+            g = xunchar(*(yy->gprotect.val)); /* Decode generic protection */
+            debug(F101,"zstime gprotect","",g);
+#ifdef S_IRUSR
+            debug(F100,"zstime S_IRUSR","",0);
+            if (g & 1) x |= S_IRUSR;    /* Read permission */
+            flag = 1;
+#endif /* S_IRUSR */
+#ifdef S_IWUSR
+            debug(F100,"zstime S_IWUSR","",0);
+            if (g & 2) x |= S_IWUSR;    /* Write permission */
+            if (g & 16) x |= S_IWUSR;   /* Delete permission */
+            flag = 1;
+#endif /* S_IWUSR */
+#ifdef S_IXUSR
+            debug(F100,"zstime S_IXUSR","",0);
+            if (g & 4)                  /* Has execute permission bit */
+              x |= S_IXUSR;
+            else                        /* Doesn't have it */
+              mask &= 0666;             /* so also clear it out of mask */
+            flag = 1;
+#endif /* S_IXUSR */
+            debug(F101,"zstime mask x","",x);
+            x |= mask;
+            debug(F101,"zstime mask x|mask","",x);
+        }
+        debug(F101,"zstime flag","",flag);
+        if (flag) {
+#ifdef S_IFMT
+            debug(F101,"zstime S_IFMT x","",x);
+            sb.st_mode = (sb.st_mode & S_IFMT) | x;
+            setperms = 1;
+#else
+#ifdef _IFMT
+            debug(F101,"zstime _IFMT x","",x);
+            sb.st_mode = (sb.st_mode & _IFMT) | x;
+            setperms = 1;
+#endif /* _IFMT */
+#endif /* S_IFMT */
+        }
+#ifdef DEBUG
+        sprintf(obuf,"%04o",sb.st_mode);
+        debug(F111,"zstime file perms after",obuf,setperms);
+#endif /* DEBUG */
+    }
+#endif /* CK_PERMS */
+
+    debug(F101,"zstime: sb.st_atime","",sb.st_atime);
+
+#ifdef BSD44
+    tp[0].tv_sec = sb.st_atime;         /* Access time first */
+    tp[1].tv_sec = tm;                  /* Update time second */
+    debug(F100,"zstime: BSD44 modtime","",0);
+#else
+#ifdef V7
+    tp.timep[0] = tm;                   /* Set modif. time to creation date */
+    tp.timep[1] = sb.st_atime;          /* Don't change the access time */
+    debug(F100,"zstime: V7 modtime","",0);
+#else
+#ifdef SYSUTIMEH
+    tp.modtime = tm;                    /* Set modif. time to creation date */
+    tp.actime = sb.st_atime;            /* Don't change the access time */
+    debug(F100,"zstime: SYSUTIMEH modtime","",0);
+#else
+    tp.mtime = tm;                      /* Set modif. time to creation date */
+    tp.atime = sb.st_atime;             /* Don't change the access time */
+    debug(F100,"zstime: default modtime","",0);
+#endif /* SYSUTIMEH */
+#endif /* V7 */
+#endif /* BSD44 */
+
+    switch (x) {                        /* Execute desired function */
+      case 0:                           /* Set the creation date of the file */
+#ifdef CK_PERMS                         /* And permissions */
+/*
+  NOTE: If we are inheriting permissions from a previous file, and the
+  previous file was a directory, this would turn the new file into a directory
+  too, but it's not, so we try to unset the right bit.  Luckily, this code
+  will probably never be executed since the upper level modules do not allow
+  reception of a file that has the same name as a directory.
+
+  NOTE 2: We change the permissions *before* we change the modification time,
+  otherwise changing the permissions would set the mod time to the present
+  time.
+*/
+        {
+            int x;
+            debug(F101,"zstime setperms","",setperms);
+            if (S_ISDIR(sb.st_mode)) {
+                debug(F101,"zstime DIRECTORY bit on","",sb.st_mode);
+                sb.st_mode ^= 0040000;
+                debug(F101,"zstime DIRECTORY bit off","",sb.st_mode);
+            }
+            if (setperms) {
+                x = chmod(f,sb.st_mode);
+                debug(F101,"zstime chmod","",x);
+            }
+        }
+        if (x < 0) return(-1);
+#endif /* CK_PERMS */
+
+        if (!setdate)                   /* We don't have a date */
+          return(0);                    /* so skip the following... */
+
+        if (
+#ifdef BSD44
+            utimes(f,tp)
+#else
+            utime(f,&tp)
+#endif /* BSD44 */
+            ) {                         /* Fix modification time */
+            debug(F111,"zstime 0: can't set modtime for file",f,errno);
+            r = -1;
+        } else  {
+           /* Including the modtime here is not portable */
+            debug(F110,"zstime 0: modtime set for file",f,0);
+            r = 0;
+        }
+        break;
+
+      case 1:                           /* Compare the dates */
+/*
+  This was st_atime, which was wrong.  We want the file-data modification
+  time, st_mtime.
+*/
+        debug(F111,"zstime 1: compare",f,sb.st_mtime);
+        debug(F111,"zstime 1: compare","packet",tm);
+
+        r = (sb.st_mtime < tm) ? 0 : 1;
+        break;
+
+      default:                          /* Error */
+        r = -1;
+    }
+#endif /* TIMESTAMP */
+    return(r);
+}
+
+/* Find initialization file. */
+
+#ifdef NOTUSED
+int
+zkermini() {
+/*  nothing here for Unix.  This function added for benefit of VMS Kermit.  */
+    return(0);
+}
+#endif /* NOTUSED */
+
+#ifndef UNIX
+/* Historical -- not used in Unix any more (2001-11-03) */
+#ifndef NOFRILLS
+int
+zmail(p,f) char *p; char *f; {          /* Send file f as mail to address p */
+/*
+  Returns 0 on success
+   2 if mail delivered but temp file can't be deleted
+  -2 if mail can't be delivered
+  -1 on file access error
+  The UNIX version always returns 0 because it can't get a good return
+  code from zsyscmd.
+*/
+    int n;
+
+#ifdef CK_LOGIN
+    if (isguest)
+      return(-2);
+#endif /* CK_LOGIN */
+
+    if (!f) f = "";
+    if (!*f) return(-1);
+
+#ifdef CKROOT
+    debug(F111,"zmail setroot",ckroot,ckrootset);
+    if (ckrootset) if (!zinroot(f)) {
+       debug(F110,"zmail setroot violation",f,0);
+       return(-1);
+    }
+#endif /* CKROOT */
+
+#ifdef BSD4
+/* The idea is to use /usr/ucb/mail, rather than regular mail, so that   */
+/* a subject line can be included with -s.  Since we can't depend on the */
+/* user's path, we use the convention that /usr/ucb/Mail = /usr/ucb/mail */
+/* and even if Mail has been moved to somewhere else, this should still  */
+/* find it...  The search could be made more reliable by actually using  */
+/* access() to see if /usr/ucb/Mail exists. */
+
+    n = strlen(f);
+    n = n + n + 15 + (int)strlen(p);
+
+    if (n > ZMBUFLEN)
+      return(-2);
+
+#ifdef DGUX540
+    sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
+#else
+    sprintf(zmbuf,"Mail -s %c%s%c %s < %s", '"', f, '"', p, f);
+#endif /* DGUX540 */
+    zsyscmd(zmbuf);
+#else
+#ifdef SVORPOSIX
+#ifndef OXOS
+    sprintf(zmbuf,"mail %s < %s", p, f);
+#else /* OXOS */
+    sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
+#endif /* OXOS */
+    zsyscmd(zmbuf);
+#else
+    *zmbuf = '\0';
+#endif
+#endif
+    return(0);
+}
+#endif /* NOFRILLS */
+#endif /* UNIX */
+
+#ifndef NOFRILLS
+int
+zprint(p,f) char *p; char *f; {         /* Print file f with options p */
+    extern char * printername;          /* From ckuus3.c */
+    extern int printpipe;
+    int n;
+
+#ifdef CK_LOGIN
+    if (isguest)
+      return(-2);
+#endif /* CK_LOGIN */
+
+    if (!f) f = "";
+    if (!*f) return(-1);
+
+#ifdef CKROOT
+    debug(F111,"zprint setroot",ckroot,ckrootset);
+    if (ckrootset) if (!zinroot(f)) {
+       debug(F110,"zprint setroot violation",f,0);
+       return(-1);
+    }
+#endif /* CKROOT */
+
+    debug(F110,"zprint file",f,0);
+    debug(F110,"zprint flags",p,0);
+    debug(F110,"zprint printername",printername,0);
+    debug(F101,"zprint printpipe","",printpipe);
+
+#ifdef UNIX
+/*
+  Note use of standard input redirection.  In some systems, lp[r] runs
+  setuid to lp (or ...?), so if user has sent a file into a directory
+  that lp does not have read access to, it can't be printed unless it is
+  fed to lp[r] as standard input.
+*/
+    if (printpipe && printername) {
+       n = 8 + (int)strlen(f) + (int)strlen(printername);
+       if (n > ZMBUFLEN)
+         return(-2);
+        sprintf(zmbuf,"cat %s | %s", f, printername);
+    } else if (printername) {
+       n = 8 + (int)strlen(f) + (int)strlen(printername);
+       if (n > ZMBUFLEN)
+         return(-2);
+        sprintf(zmbuf,"cat %s >> %s", f, printername);
+    } else {
+       n = 4 + (int)strlen(PRINTCMD) + (int)strlen(p) + (int)strlen(f);
+       if (n > ZMBUFLEN)
+         return(-2);
+        sprintf(zmbuf,"%s %s < %s", PRINTCMD, p, f);
+    }
+    debug(F110,"zprint command",zmbuf,0);
+    zsyscmd(zmbuf);
+#else /* Not UNIX */
+    *zmbuf = '\0';
+#endif /* UNIX */
+    return(0);
+}
+#endif /* NOFRILLS */
+
+/*  Wildcard expansion functions...  */
+
+static char scratch[MAXPATH+4];         /* Used by both methods */
+
+static int oldmtchs = 0;                /* Let shell (ls) expand them. */
+#ifdef COMMENT
+static char *lscmd = "/bin/ls -d";      /* Command to use. */
+#else
+static char *lscmd = "echo";            /* Command to use. */
+#endif /* COMMENT */
+
+#ifndef NOPUSH
+int
+shxpand(pat,namlst,len) char *pat, *namlst[]; int len; {
+    char *fgbuf = NULL;                 /* Buffer for forming ls command */
+    char *p, *q;                        /* Workers */
+
+    int i, x, retcode, itsadir;
+    char c;
+
+    x = (int)strlen(pat) + (int)strlen(lscmd) + 3; /* Length of ls command */
+    for (i = 0; i < oldmtchs; i++) {    /* Free previous file list */
+        if (namlst[i] ) {               /* If memory is allocated  */
+            free(namlst[i]);            /* Free the memory         */
+            namlst[i] = NULL ;          /* Remember no memory is allocated */
+        }
+    }
+    oldmtchs = 0 ;                      /* Remember there are no matches */
+    fgbuf = malloc(x);                  /* Get buffer for command */
+    if (!fgbuf) return(-1);             /* Fail if cannot */
+    ckmakmsg(fgbuf,x,lscmd," ",pat,NULL); /* Form the command */
+    zxcmd(ZIFILE,fgbuf);                /* Start the command */
+    i = 0;                              /* File counter */
+    p = scratch;                        /* Point to scratch area */
+    retcode = -1;                       /* Assume failure */
+    while ((x = zminchar()) != -1) {    /* Read characters from command */
+        c = (char) x;
+        if (c == ' ' || c == '\n') {    /* Got newline or space? */
+            *p = '\0';                  /* Yes, terminate string */
+            p = scratch;                /* Point back to beginning */
+            if (zchki(p) == -1)         /* Does file exist? */
+              continue;                 /* No, continue */
+            itsadir = isdir(p);         /* Yes, is it a directory? */
+            if (xdironly && !itsadir)   /* Want only dirs but this isn't */
+              continue;                 /* so skip. */
+            if (xfilonly && itsadir)    /* It's a dir but want only files */
+              continue;                 /* so skip. */
+            x = (int)strlen(p);         /* Keep - get length of name */
+            q = malloc(x+1);            /* Allocate space for it */
+            if (!q) goto shxfin;        /* Fail if space can't be obtained */
+            strcpy(q,scratch);          /* (safe) Copy name to space */
+            namlst[i++] = q;            /* Copy pointer to name into array */
+            if (i >= len) goto shxfin;  /* Fail if too many */
+        } else {                        /* Regular character */
+            *p++ = c;                   /* Copy it into scratch area */
+        }
+    }
+    retcode = i;                        /* Return number of matching files */
+shxfin:                                 /* Common exit point */
+    free(fgbuf);                        /* Free command buffer */
+    fgbuf = NULL;
+    zclosf(ZIFILE);                     /* Delete the command fork. */
+    oldmtchs = i;                       /* Remember how many files */
+    return(retcode);
+}
+#endif /* NOPUSH */
+
+/*
+  Directory-reading functions for UNIX originally written for C-Kermit 4.0
+  by Jeff Damens, CUCCA, 1984.
+*/
+static char * xpat = NULL;              /* Global copy of fgen() pattern */
+static char * xpatlast = NULL;          /* Rightmost segment of pattern*/
+static int xpatslash = 0;               /* Slash count in pattern */
+static int xpatwild = 0;                /* Original pattern is wild */
+static int xleafwild = 0;               /* Last segment of pattern is wild */
+static int xpatabsolute = 0;
+
+#ifdef aegis
+static char bslash;
+#endif /* aegis */
+
+
+/*  S P L I T P A T H  */
+
+/*
+  Splits the slash-separated portions of the argument string into
+  a list of path structures.  Returns the head of the list.  The
+  structures are allocated by malloc, so they must be freed.
+  Splitpath is used internally by the filename generator.
+
+  Input:
+    A path string.
+
+  Returns:
+    A linked list of the slash-separated segments of the input.
+*/
+static struct path *
+splitpath(p) char *p; {
+    struct path *head,*cur,*prv;
+    int i;
+
+    debug(F111,"splitpath",p,xrecursive);
+    head = prv = NULL;
+
+    if (!p) return(NULL);
+    if (!*p) return(NULL);
+
+    if (!strcmp(p,"**")) {              /* Fix this */
+        p = "*";
+    }
+    if (ISDIRSEP(*p)) p++;              /* Skip leading slash if any */
+
+    /* Make linked list of path segments from pattern */
+
+    while (*p) {
+        cur = (struct path *) malloc(sizeof (struct path));
+        debug(F101,"splitpath malloc","",cur);
+        if (cur == NULL) {
+            debug(F100,"splitpath malloc failure","",0);
+            prv -> fwd = NULL;
+            return((struct path *)NULL);
+        }
+        cur -> fwd = NULL;
+        if (head == NULL)               /* First, make list head */
+          head = cur;
+        else                            /* Not first, link into chain */
+          prv -> fwd = cur;
+        prv = cur;                      /* Link from previous to this one */
+
+#ifdef aegis
+        /* treat backslash as "../" */
+        if (bslash && *p == bslash) {
+            strcpy(cur->npart, "..");  /* safe */
+            ++p;
+        } else {
+            for (i=0; i < MAXNAMLEN && *p && *p != '/' && *p != bslash; i++)
+              cur -> npart[i] = *p++;
+            cur -> npart[i] = '\0';     /* end this segment */
+            if (i >= MAXNAMLEN)
+              while (*p && *p != '/' && *p != bslash)
+                p++;
+        }
+        if (*p == '/') p++;
+#else
+        /* General case (UNIX) */
+        for (i = 0; i < MAXNAMLEN && !ISDIRSEP(*p) && *p != '\0'; i++) {
+            cur -> npart[i] = *p++;
+        }
+
+        cur -> npart[i] = '\0';         /* End this path segment */
+        if (i >= MAXNAMLEN)
+          while (!ISDIRSEP(*p) && *p != '\0') p++;
+        if (ISDIRSEP(*p))
+          p++;
+
+#endif /* aegis */
+    }
+    if (prv) {
+        makestr(&xpatlast,prv -> npart);
+        debug(F110,"splitpath xpatlast",xpatlast,0);
+    }
+#ifdef DEBUG
+    /* Show original path list */
+    if (deblog) {
+        for (i = 0, cur = head; cur; i++) {
+            debug(F111,"SPLITPATH",cur -> npart, i);
+            cur = cur -> fwd;
+        }
+    }
+#endif /* DEBUG */
+    return(head);
+}
+
+/*  F G E N  --  Generate File List  */
+
+/*
+  File name generator.  It is passed a string, possibly containing wildcards,
+  and an array of character pointers.  It finds all the matching filenames and
+  stores pointers to them in the array.  The returned strings are allocated
+  from a static buffer local to this module (so the caller doesn't have to
+  worry about deallocating them); this means that successive calls to fgen
+  will wipe out the results of previous calls.
+
+  Input:
+    A wildcard string, an array to write names to, the length of the array.
+
+  Returns:
+    The number of matches.
+    The array is filled with filenames that matched the pattern.
+    If there wasn't enough room in the array, -1 is returned.
+
+  Originally by: Jeff Damens, CUCCA, 1984.  Many changes since then.
+*/
+static int
+fgen(pat,resarry,len) char *pat,*resarry[]; int len; {
+    struct path *head;
+    char *sptr, *s;
+    int n;
+
+#ifdef aegis
+    char *namechars;
+    int tilde = 0, bquote = 0;
+
+    if ((namechars = getenv("NAMECHARS")) != NULL) {
+        if (ckstrchr(namechars, '~' ) != NULL) tilde  = '~';
+        if (ckstrchr(namechars, '\\') != NULL) bslash = '\\';
+        if (ckstrchr(namechars, '`' ) != NULL) bquote = '`';
+    } else {
+        tilde = '~'; bslash = '\\'; bquote = '`';
+    }
+    sptr = scratch;
+
+    /* copy "`node_data", etc. anchors */
+    if (bquote && *pat == bquote)
+      while (*pat && *pat != '/' && *pat != bslash)
+        *sptr++ = *pat++;
+    else if (tilde && *pat == tilde)
+      *sptr++ = *pat++;
+    while (*pat == '/')
+      *sptr++ = *pat++;
+    if (sptr == scratch) {
+        strcpy(scratch,"./");          /* safe */
+        sptr = scratch+2;
+    }
+    if (!(head = splitpath(pat))) return(-1);
+
+#else /* not aegis */
+
+    debug(F111,"fgen pat",pat,len);
+    debug(F110,"fgen current directory",zgtdir(),0);
+    debug(F101,"fgen stathack","",stathack);
+
+    scratch[0] = '\0';
+    xpatwild = 0;
+    xleafwild = 0;
+    xpatabsolute = 0;
+
+    if (!(head = splitpath(pat)))       /* Make the path segment list */
+       return(-1);
+
+    sptr = scratch;
+
+#ifdef COMMENT
+    if (strncmp(pat,"./",2) && strncmp(pat,"../",3)) {
+#endif /* COMMENT */
+       if (!ISDIRSEP(*pat))            /* If name is not absolute */
+         *sptr++ = '.';                /* put "./" in front. */
+       *sptr++ = DIRSEP;
+#ifdef COMMENT
+    }
+#endif /* COMMENT */
+    *sptr = '\0';
+#endif /* aegis */
+
+    makestr(&xpat,pat);                 /* Save copy of original pattern */
+    debug(F110,"fgen scratch",scratch,0);
+
+    for (n = 0, s = xpat; *s; s++)      /* How many slashes in the pattern */
+      if (*s == DIRSEP)                 /* since these are fences for */
+        n++;                            /* pattern matching */
+    xpatslash = n;
+    debug(F101,"fgen xpatslash","",xpatslash);
+
+    numfnd = 0;                         /* None found yet */
+
+    if (initspace(resarry,ssplen) < 0)
+      return(-1);
+
+    xpatwild = iswild(xpat);           /* Original pattern is wild? */
+    xpatabsolute = isabsolute(xpat);
+    xleafwild = iswild(xpatlast);
+
+    debug(F111,"fgen xpat",xpat,xpatwild);
+    debug(F111,"fgen xpatlast",xpatlast,xleafwild);
+    debug(F101,"fgen xpatabsolute","",xpatabsolute);
+
+    traverse(head,scratch,sptr);        /* Go walk the directory tree. */
+    while (head != NULL) {              /* Done - free path segment list. */
+        struct path *next = head -> fwd;
+        free((char *)head);
+        head = next;
+    }
+    debug(F101,"fgen","",numfnd);
+    return(numfnd);                     /* Return the number of matches */
+}
+
+/* Define LONGFN (long file names) automatically for BSD 2.9 and 4.2 */
+/* LONGFN can also be defined on the cc command line. */
+
+#ifdef BSD29
+#ifndef LONGFN
+#define LONGFN
+#endif
+#endif
+
+#ifdef BSD42
+#ifndef LONGFN
+#define LONGFN
+#endif
+#endif
+
+/*
+   T R A V E R S E  --  Traverse a directory tree.
+
+   Walks the directory tree looking for matches to its arguments.
+   The algorithm is, briefly:
+
+    If the current pattern segment contains no wildcards, that
+    segment is added to what we already have.  If the name so far
+    exists, we call ourselves recursively with the next segment
+    in the pattern string; otherwise, we just return.
+
+    If the current pattern segment contains wildcards, we open the name
+    we've accumulated so far (assuming it is really a directory), then read
+    each filename in it, and, if it matches the wildcard pattern segment, add
+    that filename to what we have so far and call ourselves recursively on
+    the next segment.
+
+    Finally, when no more pattern segments remain, we add what's accumulated
+    so far to the result array and increment the number of matches.
+
+  Inputs:
+    A pattern path list (as generated by splitpath), a string pointer that
+    points to what we've traversed so far (this can be initialized to "/"
+    to start the search at the root directory, or to "./" to start the
+    search at the current directory), and a string pointer to the end of
+    the string in the previous argument, plus the global "recursive",
+    "xmatchdot", and "xdironly" flags.
+
+  Returns: void, with:
+    mtchs[] containing the array of filename string pointers, and:
+    numfnd containing the number of filenames.
+
+  Although it might be poor practice, the mtchs[] array is revealed to the
+  outside in case it needs it; for example, to be sorted prior to use.
+  (It is poor practice because not all platforms implement file lists the
+  same way; some don't use an array at all.)
+
+  Note that addresult() acts as a second-level filter; due to selection
+  criteria outside of the pattern, it might decline to add files that
+  this routine asks it to, e.g. because we are collecting only directory
+  names but not the names of regular files.
+
+  WARNING: In the course of C-Kermit 7.0 development, this routine became
+  ridiculously complex, in order to meet approximately sixty specific
+  requirements.  DON'T EVEN THINK ABOUT MODIFYING THIS ROUTINE!  Trust me;
+  it is not possible to fix anything in it without breaking something else.
+  This routine badly needs a total redesign and rewrite.  Note: There may
+  be some good applications for realpath() and/or scandir() and/or fts_blah()
+  here, on platforms where they are available.
+*/
+static VOID
+traverse(pl,sofar,endcur) struct path *pl; char *sofar, *endcur; {
+
+/* Appropriate declarations for directory routines and structures */
+/* #define OPENDIR means to use opendir(), readdir(), closedir()  */
+/* If OPENDIR not defined, we use open(), read(), close() */
+
+#ifdef DIRENT                           /* New way, <dirent.h> */
+#define OPENDIR
+    DIR *fd, *opendir();
+    struct dirent *dirbuf;
+    struct dirent *readdir();
+#else /* !DIRENT */
+#ifdef LONGFN                           /* Old way, <dir.h> with opendir() */
+#define OPENDIR
+    DIR *fd, *opendir();
+    struct direct *dirbuf;
+#else /* !LONGFN */
+    int fd;                             /* Old way, <dir.h> with open() */
+    struct direct dir_entry;
+    struct direct *dirbuf = &dir_entry;
+#endif /* LONGFN */
+#endif /* DIRENT */
+    int mopts = 0;                     /* ckmatch() opts */
+    int depth = 0;                     /* Directory tree depth */
+
+    char nambuf[MAXNAMLEN+4];           /* Buffer for a filename */
+    int itsadir = 0, segisdir = 0, itswild = 0, mresult, n, x /* , y */ ;
+    struct stat statbuf;                /* For file info. */
+
+    debug(F101,"STAT","",16);
+    if (pl == NULL) {                   /* End of path-segment list */
+        *--endcur = '\0'; /* Terminate string, overwrite trailing slash */
+        debug(F110,"traverse add: end of path segment",sofar,0);
+        addresult(sofar,-1);
+        return;
+    }
+    if (stathack) {
+       /* This speeds up the search a lot and we still get good results */
+       /* but it breaks the tagging of directory names done in addresult */
+       if (xrecursive || xfilonly || xdironly || xpatslash) {
+           itsadir = xisdir(sofar);
+           debug(F101,"STAT","",17);
+       } else
+         itsadir = (strncmp(sofar,"./",2) == 0);
+    } else {
+       itsadir = xisdir(sofar);
+       debug(F101,"STAT","",18);
+    }
+    debug(F111,"traverse entry sofar",sofar,itsadir);
+
+#ifdef CKSYMLINK                        /* We're doing symlinks? */
+#ifdef USE_LSTAT                        /* OK to use lstat()? */
+    if (itsadir && xnolinks) {         /* If not following symlinks */
+       int x;
+       struct stat buf;
+       x = lstat(sofar,&buf);
+       debug(F111,"traverse lstat 1",sofar,x);
+       if (x > -1 &&
+#ifdef S_ISLNK
+           S_ISLNK(buf.st_mode)
+#else
+#ifdef _IFLNK
+           ((_IFMT & buf.st_mode) == _IFLNK)
+#endif /* _IFLNK */
+#endif /* S_ISLNK */
+           )
+         itsadir = 0;
+    }
+#endif /* USE_LSTAT */
+#endif /* CKSYMLINK */
+
+    if (!xmatchdot && xpatlast[0] == '.')
+      xmatchdot = 1;
+    if (!xmatchdot && xpat[0] == '.' && xpat[1] != '/' && xpat[1] != '.')
+      xmatchdot = 1;
+
+    /* ckmatch() options */
+
+    if (xmatchdot)   mopts |= 1;       /* Match dot */
+    if (!xrecursive) mopts |= 2;       /* Dirsep is fence */
+
+    debug(F111,"traverse entry xpat",xpat,xpatslash);
+    debug(F111,"traverse entry xpatlast",xpatlast,xmatchdot);
+    debug(F110,"traverse entry pl -> npart",pl -> npart,0);
+
+#ifdef RECURSIVE
+    if (xrecursive > 0 && !itsadir) {
+        char * s;         /* Recursive descent and this is a regular file */
+        *--endcur = '\0'; /* Terminate string, overwrite trailing slash */
+
+        /* Find the nth slash from the right and match from there... */
+        /* (n == the number of slashes in the original pattern - see fgen) */
+        if (*sofar == '/') {
+            debug(F110,"traverse xpatslash absolute",sofar,0);
+            s = sofar;
+        } else {
+            debug(F111,"traverse xpatslash relative",sofar,xpatslash);
+            for (s = endcur - 1, n = 0; s >= sofar; s--) {
+                if (*s == '/') {
+                    if (++n >= xpatslash) {
+                        s++;
+                        break;
+                    }
+                }
+            }
+        }
+#ifndef NOSKIPMATCH
+       /* This speeds things up a bit. */
+       /* If it causes trouble define NOSKIPMATCH and rebuild. */
+       if (xpat[0] == '*' && !xpat[1])
+         x = xmatchdot ? 1 : (s[0] != '.');
+       else
+#endif /* NOSKIPMATCH */
+         x = ckmatch(xpat, s, 1, mopts); /* Match with original pattern */
+        debug(F111,"traverse xpatslash ckmatch",s,x);
+        if (x > 0) {
+            debug(F110,"traverse add: recursive, match, && !isdir",sofar,0);
+            addresult(sofar,itsadir);
+        }
+        return;
+    }
+#endif /* RECURSIVE */
+
+    debug(F111,"traverse sofar 2",sofar,0);
+
+    segisdir = ((pl -> fwd) == NULL) ? 0 : 1;
+    itswild = iswild(pl -> npart);
+
+    debug(F111,"traverse segisdir",sofar,segisdir);
+    debug(F111,"traverse itswild ",pl -> npart,itswild);
+
+#ifdef RECURSIVE
+    if (xrecursive > 0) {               /* If recursing and... */
+        if (segisdir && itswild)        /* this is a dir and npart is wild */
+          goto blah;                    /* or... */
+        else if (!xpatabsolute && !xpatwild) /* search object is nonwild */
+          goto blah;                    /* then go recurse */
+    }
+#endif /* RECURSIVE */
+
+    if (!itswild) {                     /* This path segment not wild? */
+#ifdef COMMENT
+        strcpy(endcur,pl -> npart);     /* (safe) Append next part. */
+        endcur += (int)strlen(pl -> npart); /* Advance end pointer */
+#else
+/*
+  strcpy() does not account for quoted metacharacters.
+  We must remove the quotes before doing the stat().
+*/
+       {
+           int quote = 0;
+           char c, * s;
+           s = pl -> npart;
+           while ((c = *s++)) {
+               if (!quote) {
+                   if (c == CMDQ) {
+                       quote = 1;
+                       continue;
+                   }
+               }
+               *endcur++ = c;
+               quote = 0;
+           }
+       }
+#endif /* COMMENT */
+        *endcur = '\0';                 /* End new current string. */
+
+        if (stat(sofar,&statbuf) == 0) { /* If this piece exists... */
+            debug(F110,"traverse exists",sofar,0);
+            *endcur++ = DIRSEP;         /* add slash to end */
+            *endcur = '\0';             /* and end the string again. */
+            traverse(pl -> fwd, sofar, endcur);
+        }
+#ifdef DEBUG
+        else debug(F110,"traverse not found", sofar, 0);
+#endif /* DEBUG */
+        return;
+    }
+
+    *endcur = '\0';                     /* End current string */
+    debug(F111,"traverse sofar 3",sofar,0);
+
+    if (!itsadir)
+      return;
+
+    /* Search is recursive or ... */
+    /* path segment contains wildcards, have to open and search directory. */
+
+  blah:
+
+    debug(F110,"traverse opening directory", sofar, 0);
+
+#ifdef OPENDIR
+    debug(F110,"traverse opendir()",sofar,0);
+    if ((fd = opendir(sofar)) == NULL) {        /* Can't open, fail. */
+        debug(F101,"traverse opendir() failed","",errno);
+        return;
+    }
+    while ((dirbuf = readdir(fd)))
+#else /* !OPENDIR */
+    debug(F110,"traverse directory open()",sofar,0);
+    if ((fd = open(sofar,O_RDONLY)) < 0) {
+        debug(F101,"traverse directory open() failed","",errno);
+        return;
+    }
+    while (read(fd, (char *)dirbuf, sizeof dir_entry))
+#endif /* OPENDIR */
+      {                         /* Read each entry in this directory */
+          int exists;
+          char *eos, *s;
+          exists = 0;
+
+          /* On some platforms, the read[dir]() can return deleted files, */
+          /* e.g. HP-UX 5.00.  There is no point in grinding through this */
+          /* routine when the file doesn't exist... */
+
+          if (          /* There  actually is an inode... */
+#ifdef BSD42
+                         dirbuf->d_ino != -1
+#else
+#ifdef unos
+                         dirbuf->d_ino != -1
+#else
+#ifdef QNX
+                         dirbuf->d_stat.st_ino != 0
+#else
+#ifdef SOLARIS
+                         dirbuf->d_ino != 0
+#else
+#ifdef sun
+                         dirbuf->d_fileno != 0
+#else
+#ifdef bsdi
+                         dirbuf->d_fileno != 0
+#else
+#ifdef __386BSD__
+                         dirbuf->d_fileno != 0
+#else
+#ifdef __FreeBSD__
+                         dirbuf->d_fileno != 0
+#else
+#ifdef ultrix
+                         dirbuf->gd_ino != 0
+#else
+#ifdef Plan9
+                         1
+#else
+                         dirbuf->d_ino != 0
+#endif /* Plan9 */
+#endif /* ultrix */
+#endif /* __FreeBSD__ */
+#endif /* __386BSD__ */
+#endif /* bsdi */
+#endif /* sun */
+#endif /* SOLARIS */
+#endif /* QNX */
+#endif /* unos */
+#endif /* BSD42 */
+              )
+            exists = 1;
+          if (!exists)
+            continue;
+
+          ckstrncpy(nambuf,             /* Copy the name */
+                  dirbuf->d_name,
+                  MAXNAMLEN
+                  );
+          if (nambuf[0] == '.') {
+              if (!nambuf[1] || (nambuf[1] == '.' && !nambuf[2])) {
+                  debug(F110,"traverse skipping",nambuf,0);
+                  continue;             /* skip "." and ".." */
+              }
+          }
+          s = nambuf;                   /* Copy name to end of sofar */
+          eos = endcur;
+          while ((*eos = *s)) {
+              s++;
+              eos++;
+          }
+/*
+  Now we check the file for (a) whether it is a directory, and (b) whether
+  its name matches our pattern.  If it is a directory, and if we have been
+  told to build a recursive list, then we must descend regardless of whether
+  it matches the pattern.  If it is not a directory and it does not match
+  our pattern, we skip it.  Note: sofar is the full pathname, nambuf is
+  the name only.
+*/
+          /* Do this first to save pointless function calls */
+          if (nambuf[0] == '.' && !xmatchdot) /* Dir name starts with '.' */
+            continue;
+         if (stathack) {
+             if (xrecursive || xfilonly || xdironly || xpatslash) {
+                 itsadir = xisdir(sofar); /* See if it's a directory */
+                 debug(F101,"STAT","",19);
+             } else {
+                 itsadir = 0;
+             }
+         } else {
+             itsadir = xisdir(sofar);
+             debug(F101,"STAT","",20);
+         }
+
+#ifdef CKSYMLINK
+#ifdef USE_LSTAT
+         if (itsadir && xnolinks) {            /* If not following symlinks */
+             int x;
+             struct stat buf;
+             x = lstat(sofar,&buf);
+             debug(F111,"traverse lstat 2",sofar,x);
+             if (x > -1 &&
+#ifdef S_ISLNK
+                 S_ISLNK(buf.st_mode)
+#else
+#ifdef _IFLNK
+                 ((_IFMT & buf.st_mode) == _IFLNK)
+#endif /* _IFLNK */
+#endif /* S_ISLNK */
+                 )
+               itsadir = 0;
+         }
+#endif /* USE_LSTAT */
+#endif /* CKSYMLINK */
+
+#ifdef RECURSIVE
+          if (xrecursive > 0 && itsadir &&
+              (xpatlast[0] == '*') && !xpatlast[1]
+              ) {
+              debug(F110,
+                    "traverse add: recursive && isdir && segisdir or match",
+                    sofar,
+                    segisdir
+                    );
+             addresult(sofar,itsadir);
+             if (numfnd < 0) return;
+          }
+#endif /* RECURSIVE */
+
+          debug(F111,"traverse mresult xpat",xpat,xrecursive);
+          debug(F111,"traverse mresult pl -> npart",
+                pl -> npart,
+                ((pl -> fwd) ? 9999 : 0)
+                );
+          debug(F111,"traverse mresult sofar segisdir",sofar,segisdir);
+          debug(F111,"traverse mresult sofar itsadir",sofar,itsadir);
+          debug(F101,"traverse mresult xmatchdot","",xmatchdot);
+/*
+  Match the path so far with the pattern after stripping any leading "./"
+  from either or both.  The pattern chosen is the full original pattern if
+  the match candidate (sofar) is not a directory, or else just the name part
+  (pl->npart) if it is.
+*/
+         {
+             char * s1;                /* The pattern */
+             char * s2 = sofar;        /* The path so far */
+             char * s3;                /* Worker */
+             int opts;                 /* Match options */
+
+             s1 = itsadir ? pl->npart : xpat;
+
+#ifndef COMMENT
+             /* I can't explain this but it unbreaks "cd blah/sub<Esc>" */
+             if (itsadir && !xrecursive && xpatslash > 0 &&
+                 segisdir == 0 && itswild) {
+                 s1 = xpat;
+                 debug(F110,"traverse mresult s1 kludge",s1,0);
+             }
+#endif /* COMMENT */
+
+             if (xrecursive && xpatslash == 0)
+               s2 = nambuf;
+             while ((s1[0] == '.') && (s1[1] == '/')) /* Strip "./" */
+               s1 += 2;
+             while ((s2[0] == '.') && (s2[1] == '/')) /* Ditto */
+               s2 += 2;
+             opts = mopts;             /* Match options */
+             if (itsadir)              /* Current segment is a directory */
+               opts = mopts & 1;       /* No fences */
+             s3 = s2;                  /* Get segment depth */
+             depth = 0;
+             while (*s3) { if (*s3++ == '/') depth++; }
+#ifndef NOSKIPMATCH
+             /* This speeds things up a bit. */
+             /* If it causes trouble define NOSKIPMATCH and rebuild. */
+             if (depth == 0 && (s1[0] == '*') && !s1[1])
+               mresult = xmatchdot ? 1 : (s2[0] != '.');
+             else
+#endif /* NOSKIPMATCH */
+               mresult = ckmatch(s1,s2,1,opts); /* Match */
+         }
+#ifdef DEBUG
+         if (deblog) {
+             debug(F111,"traverse mresult depth",sofar,depth);
+             debug(F101,"traverse mresult xpatslash","",xpatslash);
+             debug(F111,"traverse mresult nambuf",nambuf,mresult);
+             debug(F111,"traverse mresult itswild",pl -> npart,itswild);
+             debug(F111,"traverse mresult segisdir",pl -> npart,segisdir);
+         }
+#endif /* DEBUG */
+          if (mresult ||               /* If match succeeded */
+             xrecursive ||             /* Or search is recursive */
+             depth < xpatslash         /* Or not deep enough to match... */
+             ) {
+              if (                      /* If it's not a directory... */
+/*
+  The problem here is that segisdir is apparently not set appropriately.
+  If I leave in the !segisdir test, then "dir /recursive blah" (where blah is
+  a directory name) misses some regular files because sometimes segisdir
+  is set and sometimes it's not.  But if I comment it out, then
+  "dir <star>/<star>.txt lists every file in * and does not even open up the
+  subdirectories.  However, "dir /rec <star>/<star>.txt" works right.
+*/
+#ifdef COMMENT
+                  mresult && (!itsadir && !segisdir)
+#else
+                  mresult &&           /* Matched */
+                  !itsadir &&          /* sofar is not a directory */
+                  ((!xrecursive && !segisdir) || xrecursive)
+#endif /* COMMENT */
+                  ) {
+                 debug(F110,
+                       "traverse add: match && !itsadir",sofar,0);
+                 addresult(sofar,itsadir);
+                 if (numfnd < 0) return;
+              } else if (itsadir && (xrecursive || mresult)) {
+                  struct path * xx = NULL;
+                  *eos++ = DIRSEP;      /* Add directory separator */
+                  *eos = '\0';          /* to end of segment */
+#ifdef RECURSIVE
+                  /* Copy previous pattern segment to this new directory */
+
+                  if (xrecursive > 0 && !(pl -> fwd)) {
+                      xx = (struct path *) malloc(sizeof (struct path));
+                      pl -> fwd = xx;
+                      if (xx) {
+                          xx -> fwd = NULL;
+                          strcpy(xx -> npart, pl -> npart); /* safe */
+                      }
+                  }
+#endif /* RECURSIVE */
+                  traverse(pl -> fwd, sofar, eos); /* Traverse new directory */
+              }
+          }
+      }
+#ifdef OPENDIR
+    closedir(fd);
+#else /* !OPENDIR */
+    close(fd);
+#endif /* OPENDIR */
+}
+
+/*
+ * addresult:
+ *  Adds a result string to the result array.  Increments the number
+ *  of matches found, copies the found string into our string
+ *  buffer, and puts a pointer to the buffer into the caller's result
+ *  array.  Our free buffer pointer is updated.  If there is no
+ *  more room in the caller's array, the number of matches is set to -1.
+ * Input: a result string.
+ * Returns: nothing.
+ */
+static VOID
+addresult(str,itsadir) char *str; int itsadir; {
+    int len;
+
+    if (!freeptr) {
+       debug(F100,"addresult string space not init'd","",0);
+       initspace(mtchs,ssplen);
+    }
+    if (!str) str = "";
+    debug(F111,"addresult",str,itsadir);
+    if (!*str)
+      return;
+
+    if (itsadir < 0) {
+       itsadir = xisdir(str);
+    }
+    if ((xdironly && !itsadir) || (xfilonly && itsadir)) {
+        debug(F111,"addresult skip",str,itsadir);
+        return;
+    }
+    while (str[0] == '.' && ISDIRSEP(str[1])) /* Strip all "./" from front */
+      str += 2;
+    if (--remlen < 0) {                 /* Elements left in array of names */
+        debug(F111,"addresult ARRAY FULL",str,numfnd);
+        numfnd = -1;
+        return;
+    }
+    len = (int)strlen(str);            /* Space this will use */
+    debug(F111,"addresult len",str,len);
+
+    if (len < 1)
+      return;
+
+    if ((freeptr + len + itsadir + 1) > (sspace + ssplen)) {
+        debug(F111,"addresult OUT OF SPACE",str,numfnd);
+#ifdef DYNAMIC
+       printf(
+"?String space %d exhausted - use SET FILE STRINGSPACE to increase\n",ssplen);
+#else
+       printf("?String space %d exhausted\n",ssplen);
+#endif /* DYNAMIC */
+        numfnd = -1;                    /* Do not record if not enough space */
+        return;
+    }
+    strcpy(freeptr,str);               /* safe */
+
+    /* Tag directory names by putting '/' at the end */
+
+    if (itsadir && (freeptr[len-1] == '/')) {
+        freeptr[len++] = DIRSEP;
+        freeptr[len] = '\0';
+    }
+    if (numfnd >= maxnames) {
+#ifdef DYNAMIC
+       printf(
+"?Too many files (%d max) - use SET FILE LISTSIZE to increase\n",maxnames);
+#else
+       printf("?Too many files - %d max\n",maxnames);
+#endif /* DYNAMIC */
+        numfnd = -1;
+        return;
+    }
+    str = freeptr;
+    *resptr++ = freeptr;
+    freeptr += (len + 1);
+    numfnd++;
+    debug(F111,"addresult ADD",str,numfnd);
+}
+
+#ifdef COMMENT
+/*
+ * match(pattern,string):
+ *  pattern matcher.  Takes a string and a pattern possibly containing
+ *  the wildcard characters '*' and '?'.  Returns true if the pattern
+ *  matches the string, false otherwise.
+ * Orignally by: Jeff Damens, CUCCA, 1984
+ * No longer used as of C-Kermit 7.0, now we use ckmatch() instead (ckclib.c).
+ *
+ * Input: a string and a wildcard pattern.
+ * Returns: 1 if match, 0 if no match.
+ */
+static int
+match(pattern, string) char *pattern, *string; {
+    char *psave = NULL, *ssave = NULL;  /* Backup pointers for failure */
+    int q = 0;                          /* Quote flag */
+
+    if (*string == '.' && *pattern != '.' && !xmatchdot) {
+        debug(F110,"match skip",string,0);
+        return(0);
+    }
+    while (1) {
+        for (; *pattern == *string; pattern++,string++) /* Skip first */
+          if (*string == '\0') return(1); /* End of strings, succeed */
+
+        if (*pattern == '\\' && q == 0) { /* Watch out for quoted */
+            q = 1;                      /* metacharacters */
+            pattern++;                  /* advance past quote */
+            if (*pattern != *string) return(0);
+            continue;
+        } else q = 0;
+
+        if (q) {
+            return(0);
+        } else {
+            if (*string != '\0' && *pattern == '?') {
+                pattern++;              /* '?', let it match */
+                string++;
+            } else if (*pattern == '*') { /* '*' ... */
+                psave = ++pattern;      /* remember where we saw it */
+                ssave = string;         /* let it match 0 chars */
+            } else if (ssave != NULL && *ssave != '\0') { /* if not at end  */
+                                        /* ...have seen a star */
+                string = ++ssave;       /* skip 1 char from string */
+                pattern = psave;        /* and back up pattern */
+            } else return(0);           /* otherwise just fail */
+        }
+    }
+}
+#endif /* COMMENT */
+
+/*
+  The following two functions are for expanding tilde in filenames
+  Contributed by Howie Kaye, CUCCA, developed for CCMD package.
+*/
+
+/*  W H O A M I  --  Get user's username.  */
+
+/*
+  1) Get real uid
+  2) See if the $USER environment variable is set ($LOGNAME on AT&T)
+  3) If $USER's uid is the same as ruid, realname is $USER
+  4) Otherwise get logged in user's name
+  5) If that name has the same uid as the real uid realname is loginname
+  6) Otherwise, get a name for ruid from /etc/passwd
+*/
+char *
+whoami() {
+#ifdef DTILDE
+#ifdef pdp11
+#define WHOLEN 100
+#else
+#define WHOLEN 257
+#endif /* pdp11 */
+    static char realname[UIDBUFLEN+1];  /* user's name */
+    static int ruid = -1;               /* user's real uid */
+    char loginname[UIDBUFLEN+1], envname[256]; /* temp storage */
+    char *c;
+    struct passwd *p;
+    _PROTOTYP(extern char * getlogin, (void) );
+
+    if (ruid != -1)
+      return(realname);
+
+    ruid = real_uid();                  /* get our uid */
+
+  /* how about $USER or $LOGNAME? */
+    if ((c = getenv(NAMEENV)) != NULL) { /* check the env variable */
+        ckstrncpy(envname, c, 255);
+        if ((p = getpwnam(envname)) != NULL) {
+            if (p->pw_uid == ruid) {    /* get passwd entry for envname */
+                ckstrncpy(realname, envname, UIDBUFLEN); /* uid's are same */
+                return(realname);
+            }
+        }
+    }
+
+  /* can we use loginname() ? */
+
+    if ((c =  getlogin()) != NULL) {    /* name from utmp file */
+        ckstrncpy (loginname, c, UIDBUFLEN);
+        if ((p = getpwnam(loginname)) != NULL) /* get passwd entry */
+          if (p->pw_uid == ruid)        /* for loginname */
+            ckstrncpy(realname, envname, UIDBUFLEN); /* if uid's are same */
+    }
+
+  /* Use first name we get for ruid */
+
+    if ((p = getpwuid(ruid)) == NULL) { /* name for uid */
+        realname[0] = '\0';             /* no user name */
+        ruid = -1;
+        return(NULL);
+    }
+    ckstrncpy(realname, p->pw_name, UIDBUFLEN);
+    return(realname);
+#else
+    return(NULL);
+#endif /* DTILDE */
+}
+
+/*  T I L D E _ E X P A N D  --  expand ~user to the user's home directory. */
+
+char *
+tilde_expand(dirname) char *dirname; {
+#ifdef DTILDE
+#ifdef pdp11
+#define BUFLEN 100
+#else
+#define BUFLEN 257
+#endif /* pdp11 */
+    struct passwd *user;
+    static char olddir[BUFLEN+1];
+    static char oldrealdir[BUFLEN+1];
+    static char temp[BUFLEN+1];
+    int i, j;
+
+    debug(F111,"tilde_expand",dirname,dirname[0]);
+
+    if (dirname[0] != '~')              /* Not a tilde...return param */
+      return(dirname);
+    if (!strcmp(olddir,dirname)) {      /* Same as last time */
+      return(oldrealdir);               /* so return old answer. */
+    } else {
+        j = (int)strlen(dirname);
+        for (i = 0; i < j; i++)         /* find username part of string */
+          if (!ISDIRSEP(dirname[i]))
+            temp[i] = dirname[i];
+          else break;
+        temp[i] = '\0';                 /* tie off with a NULL */
+        if (i == 1) {                   /* if just a "~" */
+#ifdef IKSD
+            if (inserver)
+              user = getpwnam(uidbuf);  /* Get info on current user */
+            else
+#endif /* IKSD */
+            {
+                char * p = whoami();
+                if (p)
+                 user = getpwnam(p);
+                else
+                 user = NULL;
+            }
+        } else {
+            user = getpwnam(&temp[1]);  /* otherwise on the specified user */
+        }
+    }
+    if (user != NULL) {                 /* valid user? */
+        ckstrncpy(olddir, dirname, BUFLEN); /* remember the directory */
+        ckstrncpy(oldrealdir,user->pw_dir, BUFLEN); /* and home directory */
+        ckstrncat(oldrealdir,&dirname[i], BUFLEN);
+        oldrealdir[BUFLEN] = '\0';
+        return(oldrealdir);
+    } else {                            /* invalid? */
+        ckstrncpy(olddir, dirname, BUFLEN); /* remember for next time */
+        ckstrncpy(oldrealdir, dirname, BUFLEN);
+        return(oldrealdir);
+    }
+#else
+    return(NULL);
+#endif /* DTILDE */
+}
+
+/*
+  Functions for executing system commands.
+  zsyscmd() executes the system command in the normal, default way for
+  the system.  In UNIX, it does what system() does.  Thus, its results
+  are always predictable.
+  zshcmd() executes the command using the user's preferred shell.
+*/
+int
+zsyscmd(s) char *s; {
+#ifdef aegis
+    if (nopush) return(-1);
+    if (!priv_chk()) return(system(s));
+#else
+    PID_T shpid;
+#ifdef COMMENT
+/* This doesn't work... */
+    WAIT_T status;
+#else
+    int status;
+#endif /* COMMENT */
+
+    if (nopush) return(-1);
+    if ((shpid = fork())) {
+        if (shpid < (PID_T)0) return(-1); /* Parent */
+        while (shpid != (PID_T) wait(&status))
+         ;
+        return(status);
+    }
+    if (priv_can()) {                   /* Child: cancel any priv's */
+        printf("?Privilege cancellation failure\n");
+        _exit(255);
+    }
+    restorsigs();                      /* Restore ignored signals */
+#ifdef HPUX10
+    execl("/usr/bin/sh","sh","-c",s,NULL);
+    perror("/usr/bin/sh");
+#else
+#ifdef Plan9
+    execl("/bin/rc", "rc", "-c", s, NULL);
+    perror("/bin/rc");
+#else
+    execl("/bin/sh","sh","-c",s,NULL);
+    perror("/bin/sh");
+#endif /* Plan9 */
+#endif /* HPUX10 */
+    _exit(255);
+    return(0);                          /* Shut up ANSI compilers. */
+#endif /* aegis */
+}
+
+
+/*  Z _ E X E C  --  Overlay ourselves with another program  */
+
+#ifndef NOZEXEC
+#ifdef HPUX5
+#define NOZEXEC
+#else
+#ifdef ATT7300
+#define NOZEXEC
+#endif /* ATT7300 */
+#endif /* HPUX5 */
+#endif /* NOZEXEC */
+
+VOID
+z_exec(p,s,t) char * p, ** s; int t; {  /* Overlay ourselves with "p s..." */
+#ifdef NOZEXEC
+    printf("EXEC /REDIRECT NOT IMPLEMENTED IN THIS VERSION OF C-KERMIT\n");
+    debug(F110,"z_exec NOT IMPLEMENTED",p,0);
+#else
+    int x;
+    extern int ttyfd;
+    debug(F110,"z_exec command",p,0);
+    debug(F110,"z_exec arg 0",s[0],0);
+    debug(F110,"z_exec arg 1",s[1],0);
+    debug(F101,"z_exec t","",t);
+    errno = 0;
+    if (t) {
+        if (ttyfd > 2) {
+            dup2(ttyfd, 0);
+            dup2(ttyfd, 1);
+            /* dup2(ttyfd, 2); */
+            close(ttyfd);
+        }
+    }
+    restorsigs();                      /* Restore ignored signals */
+    x = execvp(p,s);
+    if (x < 0) debug(F101,"z_exec errno","",errno);
+#endif /* NOZEXEC */
+}
+
+/*
+  Z S H C M D  --  Execute a shell command (or program thru the shell).
+
+  Original UNIX code by H. Fischer; copyright rights assigned to Columbia U.
+  Adapted to use getpwuid to find login shell because many systems do not
+  have SHELL in environment, and to use direct calling of shell rather
+  than intermediate system() call. -- H. Fischer (1985); many changes since
+  then.  Call with s pointing to command to execute.  Returns:
+   -1 on failure to start the command (can't find, can't fork, can't run).
+    1 if command ran and gave an exit status of 0.
+    0 if command ran and gave a nonzero exit status.
+  with pexitstatus containing the command's exit status.
+*/
+int
+zshcmd(s) char *s; {
+    PID_T pid;
+
+#ifdef NOPUSH
+    return(0);
+#else
+    if (nopush) return(-1);
+    debug(F110,"zshcmd command",s,0);
+
+#ifdef aegis
+    if ((pid = vfork()) == 0) {         /* Make child quickly */
+        char *shpath, *shname, *shptr;  /* For finding desired shell */
+
+        if (priv_can()) exit(1);        /* Turn off privs. */
+        if ((shpath = getenv("SHELL")) == NULL) shpath = "/com/sh";
+
+#else                                   /* All Unix systems */
+    if ((pid = fork()) == 0) {          /* Make child */
+        char *shpath, *shname, *shptr;  /* For finding desired shell */
+        struct passwd *p;
+#ifdef HPUX10                           /* Default */
+        char *defshell = "/usr/bin/sh";
+#else
+#ifdef Plan9
+        char *defshell = "/bin/rc";
+#else
+        char *defshell = "/bin/sh";
+#endif /* Plan9 */
+#endif /* HPUX10 */
+        if (priv_can()) exit(1);        /* Turn off privs. */
+#ifdef COMMENT
+/* Old way always used /etc/passwd shell */
+        p = getpwuid(real_uid());       /* Get login data */
+        if (p == (struct passwd *) NULL || !*(p->pw_shell))
+          shpath = defshell;
+        else
+          shpath = p->pw_shell;
+#else
+/* New way lets user override with SHELL variable, but does not rely on it. */
+/* This allows user to specify a different shell. */
+        shpath = getenv("SHELL");       /* What shell? */
+       debug(F110,"zshcmd SHELL",shpath,0);
+        if (shpath == NULL) {
+            p = getpwuid( real_uid() ); /* Get login data */
+            if (p == (struct passwd *)NULL || !*(p->pw_shell))
+              shpath = defshell;
+            else shpath = p->pw_shell;
+           debug(F110,"zshcmd shpath",shpath,0);
+        }
+#endif /* COMMENT */
+#endif /* aegis */
+        shptr = shname = shpath;
+        while (*shptr != '\0')
+          if (*shptr++ == DIRSEP)
+            shname = shptr;
+       restorsigs();                   /* Restore ignored signals */
+       debug(F110,"zshcmd shname",shname,0);
+        if (s == NULL || *s == '\0') {  /* Interactive shell requested? */
+            execl(shpath,shname,"-i",NULL); /* Yes, do that */
+        } else {                        /* Otherwise, */
+            execl(shpath,shname,"-c",s,NULL); /* exec the given command */
+        }                               /* If execl() failed, */
+        exit(BAD_EXIT);                 /* return bad return code. */
+
+    } else {                            /* Parent */
+
+        int wstat;                      /* ... must wait for child */
+#ifdef CK_CHILD
+        int child;                      /* Child's exit status */
+#endif /* CK_CHILD */
+        SIGTYP (*istat)(), (*qstat)();
+
+        if (pid == (PID_T) -1) return(-1); /* fork() failed? */
+
+        istat = signal(SIGINT,SIG_IGN); /* Let the fork handle keyboard */
+        qstat = signal(SIGQUIT,SIG_IGN); /* interrupts itself... */
+
+#ifdef CK_CHILD
+        while (((wstat = wait(&child)) != pid) && (wstat != -1))
+#else
+        while (((wstat = wait((WAIT_T *)0)) != pid) && (wstat != -1))
+#endif /* CK_CHILD */
+          ;                             /* Wait for fork */
+        signal(SIGINT,istat);           /* Restore interrupts */
+        signal(SIGQUIT,qstat);
+#ifdef CK_CHILD
+        pexitstat = (child & 0xff) ? child : child >> 8;
+       debug(F101,"zshcmd exit status","",pexitstat);
+        return(child == 0 ? 1 : 0);     /* Return child's status */
+#endif /* CK_CHILD */
+    }
+    return(1);
+#endif /* NOPUSH */
+}
+
+/*  I S W I L D  --  Check if filespec is "wild"  */
+
+/*
+  Returns:
+    0 if argument is empty or is the name of a single file;
+    1 if it contains wildcard characters.
+  Note: must match the algorithm used by match(), hence no [a-z], etc.
+*/
+int
+iswild(filespec) char *filespec; {
+    char c, *p, *f; int x;
+    int quo = 0;
+    if (!filespec)
+      return(0);
+    f = filespec;
+    if (wildxpand) {                   /* Shell handles wildcarding */
+        if ((x = nzxpand(filespec,0)) > 1)
+          return(1);
+        if (x == 0) return(0);          /* File does not exist */
+        p = malloc(MAXNAMLEN + 20);
+        znext(p);
+        x = (strcmp(filespec,p) != 0);
+        free(p);
+        p = NULL;
+        return(x);
+    } else {                           /* We do it ourselves */
+        while ((c = *filespec++) != '\0') {
+            if (c == '\\' && quo == 0) {
+                quo = 1;
+                continue;
+            }
+            if (!quo && (c == '*' || c == '?'
+#ifdef CKREGEX
+#ifndef VMS
+                         || c == '['
+#endif /* VMS */
+                        || c == '{'
+#endif /* CKREGEX */
+                         )) {
+               debug(F111,"iswild",f,1);
+               return(1);
+           }
+            quo = 0;
+        }
+       debug(F111,"iswild",f,0);
+        return(0);
+    }
+}
+
+/*
+  I S D I R  --  Is a Directory.
+
+  Tell if string pointer s is the name of an existing directory.  Returns 1 if
+  directory, 0 if not a directory.
+
+  The following no longer applies:
+
+  If the file is a symlink, we return 1 if
+  it is a directory OR if it is a link to a directory and the "xrecursive" flag
+  is NOT set.  This is to allow parsing a link to a directory as if it were a
+  directory (e.g. in the CD or IF DIRECTORY command) but still prevent
+  recursive traversal from visiting the same directory twice.
+*/
+
+#ifdef ISDIRCACHE
+/* This turns out to be unsafe and gives little benefit anyway. */
+/* See notes 28 Sep 2003.  Thus ISDIRCACHE is not defined. */
+
+static char prevpath[CKMAXPATH+4] = { '\0', '\0' };
+static int prevstat = -1;
+int
+clrdircache() {
+    debug(F100,"CLEAR ISDIR CACHE","",0);
+    prevstat = -1;
+    prevpath[0] = NUL;
+}
+#endif /* ISDIRCACHE */
+
+int
+isdir(s) char *s; {
+    int x, needrlink = 0, islink = 0;
+    struct stat statbuf;
+    char fnam[CKMAXPATH+4];
+
+    if (!s) return(0);
+    if (!*s) return(0);
+
+#ifdef ISDIRCACHE
+    if (prevstat > -1) {
+       if (s[0] == prevpath[0]) {
+           if (!strcmp(s,prevpath)) {
+               debug(F111,"isdir cache hit",s,prevstat);
+               return(prevstat);
+           }
+       }
+    }
+#endif /* ISDIRCACHE */
+
+#ifdef CKSYMLINK
+#ifdef COMMENT
+/*
+  The following over-clever bit has been commented out because it presumes
+  to know when a symlink might be redundant, which it can't possibly know.
+  Using plain old stat() gives Kermit the same results as ls and ls -R, which
+  is just fine: no surprises.
+*/
+#ifdef USE_LSTAT
+    if (xrecursive) {
+        x = lstat(s,&statbuf);
+        debug(F111,"isdir lstat",s,x);
+    } else {
+#endif /* USE_LSTAT */
+        x = stat(s,&statbuf);
+        debug(F111,"isdir stat",s,x);
+#ifdef USE_LSTAT
+    }
+#endif /* USE_LSTAT */
+#else
+    x = stat(s,&statbuf);
+    debug(F111,"isdir stat",s,x);
+#endif /* COMMENT */
+    if (x == -1) {
+        debug(F101,"isdir errno","",errno);
+        return(0);
+    }
+    islink = 0;
+    if (xrecursive) {
+#ifdef NOLINKBITS
+        needrlink = 1;
+#else
+#ifdef S_ISLNK
+        islink = S_ISLNK(statbuf.st_mode);
+        debug(F101,"isdir S_ISLNK islink","",islink);
+#else
+#ifdef _IFLNK
+        islink = (_IFMT & statbuf.st_mode) == _IFLNK;
+        debug(F101,"isdir _IFLNK islink","",islink);
+#endif /* _IFLNK */
+#endif /* S_ISLNK */
+#endif /* NOLINKBITS */
+        if (needrlink) {
+            if (readlink(s,fnam,CKMAXPATH) > -1)
+              islink = 1;
+        }
+    }
+#else
+    x = stat(s,&statbuf);
+    if (x == -1) {
+        debug(F101,"isdir errno","",errno);
+        return(0);
+    }
+    debug(F111,"isdir stat",s,x);
+#endif /* CKSYMLINK */
+    debug(F101,"isdir islink","",islink);
+    debug(F101,"isdir statbuf.st_mode","",statbuf.st_mode);
+    x = islink ? 0 : (S_ISDIR (statbuf.st_mode) ? 1 : 0);
+#ifdef ISDIRCACHE
+    prevstat = x;
+    ckstrncpy(prevpath,s,CKMAXPATH+1);
+#endif /* ISDIRCACHE */
+    return(x);
+}
+
+#ifdef CK_MKDIR
+/* Some systems don't have mkdir(), e.g. Tandy Xenix 3.2.. */
+
+/* Z M K D I R  --  Create directory(s) if necessary */
+/*
+   Call with:
+    A pointer to a file specification that might contain directory
+    information.  The filename is expected to be included.
+    If the file specification does not include any directory separators,
+    then it is assumed to be a plain file.
+    If one or more directories are included in the file specification,
+    this routine tries to create them if they don't already exist.
+   Returns:
+    0 or greater on success, i.e. the number of directories created.
+   -1 on failure to create the directory
+*/
+int
+zmkdir(path) char *path; {
+    char *xp, *tp, c;
+    int x, count = 0;
+
+    if (!path) path = "";
+    if (!*path) return(-1);
+
+#ifdef CKROOT
+    debug(F111,"zmkdir setroot",ckroot,ckrootset);
+    if (ckrootset) if (!zinroot(path)) {
+       debug(F110,"zmkdir setroot violation",path,0);
+       return(-1);
+    }
+#endif /* CKROOT */
+
+    x = strlen(path);
+    debug(F111,"zmkdir",path,x);
+    if (x < 1 || x > MAXPATH)           /* Check length */
+      return(-1);
+    if (!(tp = malloc(x+1)))            /* Make a temporary copy */
+      return(-1);
+    strcpy(tp,path);                   /* safe (prechecked) */
+#ifdef DTILDE
+    if (*tp == '~') {                   /* Starts with tilde? */
+        xp = tilde_expand(tp);          /* Attempt to expand tilde */
+        if (!xp) xp = "";
+        if (*xp) {
+            char *zp;
+            debug(F110,"zmkdir tilde_expand",xp,0);
+            if (!(zp = malloc(strlen(xp) + 1))) { /* Make a place for it */
+                free(tp);
+                tp = NULL;
+                return(-1);
+            }
+            free(tp);                   /* Free previous buffer */
+            tp = zp;                    /* Point to new one */
+            strcpy(tp,xp);              /* Copy expanded name to new buffer */
+        }
+    }
+#endif /* DTILDE */
+    debug(F110,"zmkdir tp after tilde_expansion",tp,0);
+    xp = tp;
+    if (ISDIRSEP(*xp))                  /* Don't create root directory! */
+      xp++;
+
+    /* Go thru filespec from left to right... */
+
+    for (; *xp; xp++) {                 /* Create parts that don't exist */
+        if (!ISDIRSEP(*xp))             /* Find next directory separator */
+          continue;
+        c = *xp;                        /* Got one. */
+        *xp = NUL;                      /* Make this the end of the string. */
+        if (!isdir(tp)) {               /* This directory exists already? */
+#ifdef CK_LOGIN
+            if (isguest)                    /* Not allowed for guests */
+             return(-1);
+#ifndef NOXFER
+            /* Nor if MKDIR and/or CD are disabled */
+            else
+#endif /* CK_LOGIN */
+             if ((server
+#ifdef IKSD
+                  || inserver
+#endif /* IKSD */
+                  ) && (!ENABLED(en_mkd) || !ENABLED(en_cwd)))
+               return(-1);
+#endif /* IKSD */
+
+            debug(F110,"zmkdir making",tp,0);
+            x =                         /* No, try to create it */
+#ifdef NOMKDIR
+               -1                       /* Systems without mkdir() */
+#else
+               mkdir(tp,0777)           /* UNIX */
+#endif /* NOMKDIR */
+                 ;
+            if (x < 0) {
+                debug(F101,"zmkdir failed, errno","",errno);
+                free(tp);               /* Free temporary buffer. */
+                tp = NULL;
+                return(-1);             /* Return failure code. */
+            } else
+              count++;
+        }
+        *xp = c;                        /* Replace the separator. */
+    }
+    free(tp);                           /* Free temporary buffer. */
+    return(count);                      /* Return success code. */
+}
+#endif /* CK_MKDIR */
+
+int
+zrmdir(path) char *path; {
+#ifdef CK_LOGIN
+    if (isguest)
+      return(-1);
+#endif /* CK_LOGIN */
+
+    if (!path) path = "";
+    if (!*path) return(-1);
+
+#ifdef CKROOT
+    debug(F111,"zrmdir setroot",ckroot,ckrootset);
+    if (ckrootset) if (!zinroot(path)) {
+       debug(F110,"zrmdir setroot violation",path,0);
+       return(-1);
+    }
+#endif /* CKROOT */
+
+#ifndef NOMKDIR
+    return(rmdir(path));
+#else
+    return(-1);
+#endif /* NOMKDIR */
+}
+
+/* Z F S E E K  --  Position input file pointer */
+/*
+   Call with:
+    Long int, 0-based, indicating desired position.
+   Returns:
+    0 on success.
+   -1 on failure.
+*/
+#ifndef NORESEND
+int
+#ifdef CK_ANSIC
+zfseek(long pos)
+#else
+zfseek(pos) long pos;
+#endif /* CK_ANSIC */
+/* zfseek */ {
+    zincnt = -1;                        /* Must empty the input buffer */
+    debug(F101,"zfseek","",pos);
+    return(fseek(fp[ZIFILE], pos, 0)?-1:0);
+}
+#endif /* NORESEND */
+
+/*  Z F N Q F P  --  Convert filename to fully qualified absolute pathname */
+
+static struct zfnfp fnfp = { 0, NULL, NULL };
+
+struct zfnfp *
+zfnqfp(fname, buflen, buf)  char * fname; int buflen; char * buf; {
+    char * s;
+    int len;
+#ifdef MAXPATHLEN
+    char zfntmp[MAXPATHLEN+4];
+#else
+    char zfntmp[CKMAXPATH+4];
+#endif /* MAXPATHLEN */
+
+    char sb[32], * tmp;
+    int i = 0, j = 0, k = 0, x = 0, y = 0;
+    int itsadir = 0;
+
+    s = fname;
+    if (!s)
+      return(NULL);
+    if (!*s)
+      return(NULL);
+    if (!buf)
+      return(NULL);
+
+    /* Initialize the data structure */
+
+    fnfp.len = ckstrncpy(buf,fname,buflen);
+    fnfp.fpath = buf;
+    fnfp.fname = NULL;
+    len = buflen;
+    debug(F111,"zfnqfp fname",fname,len);
+
+#ifdef DTILDE
+    if (*s == '~') {                    /* Starts with tilde? */
+        char * xp;
+        xp = tilde_expand(s);           /* Attempt to expand tilde */
+        debug(F110,"zfnqfp xp",xp,0);   /* (realpath() doesn't do this) */
+        if (!xp) xp = "";
+        if (*xp)
+          s = xp;
+    }
+#endif /* DTILDE */
+
+#ifdef CKREALPATH
+
+/* N.B.: The realpath() result buffer MUST be MAXPATHLEN bytes long */
+/* otherwise we write over memory. */
+
+    if (!realpath(s,zfntmp)) {
+        debug(F111,"zfnqfp realpath fails",s,errno);
+#ifdef COMMENT
+       if (errno != ENOENT)
+         return(NULL);
+#else
+       /* If realpath() fails use the do-it-yourself method */
+       /* 16 Jan 2002 */
+       goto norealpath;
+#endif /* COMMENT */
+    }
+    len = strlen(zfntmp);
+    if (len > buflen) {
+       debug(F111,"zfnqfp result too long",ckitoa(buflen),len);
+       return(NULL);
+    } else {
+       ckstrncpy(buf,zfntmp,buflen);
+    }
+    if (buf[len-1] != '/') {
+       if ((itsadir = isdir(buf)) && len < (buflen - 1)) {
+           buf[len++] = '/';
+           buf[len] = NUL;
+       }
+    }
+    fnfp.len = len;
+    fnfp.fpath = buf;
+    debug(F110,"zfnqfp realpath path",fnfp.fpath,0);
+    tmp = buf + fnfp.len - 1;
+    if (!itsadir) {
+       while (tmp >= buf) {
+           if (*tmp == '/') {
+               fnfp.fname = tmp + 1;
+               debug(F110,"zfnqfp realpath name",fnfp.fname,0);
+               break;
+           }
+           tmp--;
+       }
+    }
+    return(&fnfp);
+
+#endif /* CKREALPATH */
+
+  norealpath:
+
+    tmp = zfntmp;
+    while (*s) {                        /* Remove leading "./" (0 or more) */
+        debug(F110,"zfnqfp while *s",s,0);
+        if (*s == '.' && *(s+1) == '/') {
+            s += 2;
+            while (*s == '/') s++;
+        } else
+          break;
+    }
+    if (!*s) return(NULL);
+    if (*s == '/') {                    /* Pathname is absolute */
+        ckstrncpy(buf,s,len);
+        x = strlen(buf);
+        y = 0;
+    } else {                            /* Pathname is relative */
+        char * p;
+        if (p = zgtdir()) {             /* So get current directory */
+            debug(F110,"zfnqfp zgtdir",p,0);
+            x = ckstrncpy(buf,p,len);
+            buf[x++] = '/';
+            debug(F110,"zfnqfp buf 1",buf,0);
+            len -= x;                   /* How much room left in buffer */
+            if ((y = (int)strlen(s)) > len) /* If enough room... */
+              return(NULL);
+            ckstrncpy(buf+x,s,len);     /* ... append the filename */
+            debug(F110,"zfnqfp buf 2",buf,0);
+        } else {
+            return(NULL);
+        }
+    }
+
+    /* Buf now holds full path but maybe containing some . or .. tricks */
+
+    j = x + y;                          /* Length of what's in buf */
+    len = j;
+    debug(F101,"zfnqfp len","",len);
+
+    /* Catch dangling "/." or "/.." */
+    if ((j > 1 && buf[j-1] == '.' && buf[j-2] == '/') ||
+        (j > 2 && buf[j-1] == '.' && buf[j-2] == '.' && buf[j-3] == '/')) {
+        if (j < buflen - 2) {
+            buf[j] = '/';
+            buf[j+1] = NUL;
+        }
+    }
+    j = -1;                             /* j = position of rightmost "/" */
+    i = 0;                              /* i = destination index */
+    tmp[i] = NUL;                       /* destination is temporary buffer  */
+
+    for (x = 0; x < len; x++) {         /* x = source index */
+        if (buf[x] == '/') {
+            for (k = 0; k < 4; k++) {
+                sb[k] = buf[x+k];
+                sb[k+1] = '\0';
+                if (!sb[k]) break;
+            }
+            if (!strncmp(sb,"/./",3)) { /* Eliminate "./" in "/./" */
+                x += 1;
+                continue;
+            } else if (!strncmp(sb,"//",2)) { /* Change "//" to "/" */
+                continue;
+            } else if (!strncmp(sb,"/../",4)) { /* ".." in path */
+                for (k = i - 1; k >= 0; k--) { /* Back up one level */
+                    if (tmp[k] == '/') {
+                        i = k;
+                        tmp[i] = NUL;
+                        break;
+                    }
+                }
+                x += 2;
+                continue;
+            }
+        }
+        if (i >= (buflen - 1)) {
+            debug(F111,"zfnqfp overflow",tmp,i);
+            return(NULL);
+        }
+        tmp[i++] = buf[x];              /* Regular character, copy */
+        tmp[i] = NUL;
+        if (buf[x] == '/')              /* Remember rightmost "/" */
+          j = i;
+    }
+    ckstrncpy(buf,tmp,buflen-1);        /* Copy the result back */
+
+    buf[buflen-1] = NUL;
+    if (!buf[0]) {                      /* If empty, say root */
+        buf[0] = '/';
+        buf[2] = NUL;
+        j = 0;
+        i = 1;
+    }
+    if ((itsadir = isdir(buf))) {
+       if (buf[i-1] != '/' && i < (buflen - 1)) {
+           buf[i++] = '/';
+           buf[i] = NUL;
+       }
+    }
+    if (!itsadir && (j > -1)) {                /* Set pointer to basename */
+        fnfp.fname = (char *)(buf + j);
+        fnfp.fpath = (char *)buf;
+        fnfp.len = i;
+        debug(F111,"zfnqfp path",fnfp.fpath,i);
+        debug(F110,"zfnqfp name",fnfp.fname,0);
+        return(&fnfp);
+    }
+    return(NULL);
+}
+
+/*  Z C M P F N  --  Compare two filenames  */
+
+/*  Returns 1 if the two names refer to the same existing file, 0 otherwise. */
+
+int
+zcmpfn(s1,s2) char * s1, * s2; {
+    char buf1[CKMAXPATH+1];
+    char buf2[CKMAXPATH+1];
+
+#ifdef USE_LSTAT
+    char linkname[CKMAXPATH+1];
+    struct stat buf;
+#endif /* USE_LSTAT */
+    int x, rc = 0;
+
+    if (!s1) s1 = "";
+    if (!s2) s2 = "";
+    if (!*s1 || !*s2) return(0);
+
+#ifdef CKSYMLINK                        /* We're doing symlinks? */
+#ifdef USE_LSTAT                        /* OK to use lstat()? */
+    x = lstat(s1,&buf);
+    if (x > -1 &&                      /* Now see if it's a symlink */
+#ifdef S_ISLNK
+        S_ISLNK(buf.st_mode)
+#else
+#ifdef _IFLNK
+        ((_IFMT & buf.st_mode) == _IFLNK)
+#endif /* _IFLNK */
+#endif /* S_ISLNK */
+        ) {
+        linkname[0] = '\0';             /* Get the name */
+        x = readlink(s1,linkname,CKMAXPATH);
+        if (x > -1 && x < CKMAXPATH) {  /* It's a link */
+            linkname[x] = '\0';
+           s1 = linkname;
+        }
+    }
+#endif /* USE_LSTAT */
+#endif /* CKSYMLINK */
+
+    if (zfnqfp(s1,CKMAXPATH,buf1)) {   /* Convert to full pathname */
+
+#ifdef CKSYMLINK                       /* Same deal for second name... */
+#ifdef USE_LSTAT
+       x = lstat(s2,&buf);
+       if (x > -1 &&
+#ifdef S_ISLNK
+           S_ISLNK(buf.st_mode)
+#else
+#ifdef _IFLNK
+           ((_IFMT & buf.st_mode) == _IFLNK)
+#endif /* _IFLNK */
+#endif /* S_ISLNK */
+           ) {
+           linkname[0] = '\0';
+           x = readlink(s2,linkname,CKMAXPATH);
+           if (x > -1 && x < CKMAXPATH) {
+               linkname[x] = '\0';
+               s2 = linkname;
+           }
+       }
+#endif /* USE_LSTAT */
+#endif /* CKSYMLINK */
+       if (zfnqfp(s2,CKMAXPATH,buf2)) {
+           debug(F110,"zcmpfn s1",buf1,0);
+           debug(F110,"zcmpfn s2",buf2,0);
+           if (!strncmp(buf1,buf2,CKMAXPATH))
+             rc = 1;
+       }
+    }
+    debug(F101,"zcmpfn result","",rc);
+    return(rc);
+}
+
+#ifdef CKROOT
+
+/* User-mode chroot() implementation */
+
+int
+zsetroot(s) char * s; {                        /* Sets the root */
+    char buf[CKMAXPATH+1];
+    if (!s) return(-1);
+    if (!*s) return(-1);
+    debug(F110,"zsetroot",s,0);
+    if (!isdir(s)) return(-2);
+    if (!zfnqfp(s,CKMAXPATH,buf))      /* Get full, real path */
+      return(-3);
+    if (access(buf,R_OK) < 0) {                /* Check access */
+       debug(F110,"zsetroot access denied",buf,0);
+       return(-4);
+    }
+    s = buf;
+    if (ckrootset) {                   /* If root already set */
+       if (!zinroot(s)) {              /* make sure new root is in it */
+           debug(F110,"zsetroot new root not in root",ckroot,0);
+           return(-5);
+       }
+    }
+    if (zchdir(buf) < 1) return(-4);   /* Change directory to new root */
+    ckrootset = ckstrncpy(ckroot,buf,CKMAXPATH); /* Now set the new root */
+    if (ckroot[ckrootset-1] != '/') {
+       ckroot[ckrootset++] = '/';
+       ckroot[ckrootset] = '\0';
+    }
+    debug(F111,"zsetroot rootset",ckroot,ckrootset);
+    ckrooterr = 0;                     /* Reset error flag */
+    return(1);
+}
+
+char *
+zgetroot() {                           /* Returns the root */
+    if (!ckrootset)
+      return(NULL);
+    return((char *)ckroot);
+}
+
+int
+zinroot(s) char * s; {                 /* Checks if file s is in the root */
+    int x, n;
+    struct zfnfp * f = NULL;
+    char buf[CKMAXPATH+2];
+
+    debug(F111,"zinroot setroot",ckroot,ckrootset);
+    ckrooterr = 0;                     /* Reset global error flag */
+    if (!ckrootset)                    /* Root not set */
+      return(1);                       /* so it's ok - no need to check */
+    if (!(f = zfnqfp(s,CKMAXPATH,buf)))        /* Get full and real pathname */
+      return(0);                       /* Fail if we can't  */
+    n = f->len;                                /* Length of full pathname */
+    debug(F111,"zinroot n",buf,n);
+    if (n < (ckrootset - 1) || n > CKMAXPATH) {        /* Bad length */
+       ckrooterr = 1;                          /* Fail */
+       return(0);
+    }
+    if (isdir(buf) && buf[n-1] != '/') {  /* If it's a directory name */
+       buf[n++] = '/';                   /* make sure it ends with '/' */
+       buf[n] = '\0';
+    }
+    x = strncmp(buf,ckroot,ckrootset); /* Compare, case-sensitive */
+    debug(F111,"zinroot checked",buf,x);
+    if (x == 0)                                /* OK */
+      return(1);
+    ckrooterr = 1;                     /* Not OK */
+    return(0);
+}
+#endif /* CKROOT */
+
+#ifdef CK_LOGIN
+/*
+  The following code provides support for user login and logout
+  including anonymous accounts.  If this feature is to be supported
+  outside of UNIX, it should be spread out among the ck?fio.c modules...
+*/
+#ifndef _PATH_BSHELL
+#define _PATH_BSHELL    "/usr/bin/bash"
+#endif /* _PATH_BSHELL */
+#ifndef _PATH_FTPUSERS
+#define _PATH_FTPUSERS  "/etc/ftpusers"
+#endif /* _PATH_FTPUSERS */
+
+/*
+ * Helper function for sgetpwnam().
+ */
+char *
+sgetsave(s) char *s; {
+    char *new = malloc((unsigned) strlen(s) + 1);
+    if (new == NULL) {
+        printf("?Local resource failure: malloc\n");
+        exit(1);
+        /* NOTREACHED */
+    }
+    (void) strcpy(new, s);             /* safe */
+    return (new);
+}
+
+/*
+ * Save the result of getpwnam().  Used for USER command, since
+ * the data returned must not be clobbered by any other command
+ * (e.g., globbing).
+ */
+struct passwd *
+sgetpwnam(name) char *name; {
+    static struct passwd save;
+    register struct passwd *p;
+#ifdef CK_SHADOW
+    register struct spwd *sp;
+#endif /* CK_SHADOW */
+    char *sgetsave();
+
+#ifdef HPUX10_TRUSTED
+    struct pr_passwd *pr;
+#endif /* HPUX10_TRUSTED */
+
+#ifdef CK_SHADOW
+    sp = getspnam(name);
+    debug(F111,"sgetpwnam","getspnam()",sp);
+    if (sp == NULL)
+      return (NULL);
+#endif /* CK_SHADOW */
+
+#ifdef HPUX10_TRUSTED
+    if ((pr = getprpwnam(name)) == NULL)
+      return(NULL);
+#endif /* HPUX10_TRUSTED */
+
+    p = getpwnam(name);
+    debug(F111,"sgetpwnam","getpwnam()",p);
+    if (p == NULL)
+      return(NULL);
+    if (save.pw_name) {
+        free(save.pw_name);
+        free(save.pw_passwd);
+        free(save.pw_gecos);
+        free(save.pw_dir);
+        free(save.pw_shell);
+    }
+    save = *p;
+    save.pw_name = sgetsave(p->pw_name);
+#ifdef CK_SHADOW
+    save.pw_passwd = sgetsave(sp->sp_pwdp);
+#else /* CK_SHADOW */
+#ifdef HPUX10_TRUSTED
+    if (pr->uflg.fg_encrypt && pr->ufld.fd_encrypt && *pr->ufld.fd_encrypt)
+      save.pw_passwd = sgetsave(pr->ufld.fd_encrypt);
+    else
+      save.pw_passwd = sgetsave("");
+#else /* HPUX10_TRUSTED */
+    save.pw_passwd = sgetsave(p->pw_passwd);
+#endif /* HPUX10_TRUSTED */
+#endif /* CK_SHADOW */
+    save.pw_gecos = sgetsave(p->pw_gecos);
+    save.pw_dir = sgetsave(p->pw_dir);
+    save.pw_shell = sgetsave(p->pw_shell);
+    return(&save);
+}
+
+#define CKXLOGBSIZ 256
+
+struct passwd * pw = NULL;
+char * home = NULL;                     /* Home directory pointer for glob */
+#ifdef CMASK
+#undef CMASK
+#endif /* CMASK */
+
+#define CMASK 027
+
+int defumask = CMASK;                   /* Default umask value */
+
+/*  Z V U S E R  --  Verify user, Returns 1 if user OK, 0 otherwise.  */
+
+/* Sets global passwd pointer pw if named account exists and is acceptable;
+ * sets askpasswd if a PASS command is expected.  If logged in previously,
+ * need to reset state.  If name is "ftp" or "anonymous", the name is not in
+ * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
+ * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
+ * requesting login privileges.  Disallow anyone who does not have a standard
+ * shell as returned by getusershell().  Disallow anyone mentioned in the file
+ * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
+ */
+_PROTOTYP(static int checkuser, (char *) );
+
+char zvuname[64] = { NUL, NUL };
+char zvhome[CKMAXPATH+1] = { NUL, NUL };
+#define ZENVUSER 70
+#define ZENVHOME CKMAXPATH+12
+#define ZENVLOGNAME 74
+static char zenvuser[ZENVUSER];
+static char zenvhome[ZENVHOME];
+static char zenvlogname[ZENVLOGNAME];
+
+#ifdef CK_PAM
+static char pam_data[500];
+struct pam_conv pam_conv = {pam_cb, pam_data}; /* PAM structure */
+struct pam_handle * pamh = NULL;               /* PAM reference handle */
+#endif /* CK_PAM */
+
+int
+zvuser(name) char *name; {
+    register char *cp = NULL;
+    int x;
+    char *shell;
+#ifdef GETUSERSHELL
+    char *getusershell();
+#endif /* GETUSERSHELL */
+
+#ifdef CK_PAM
+    int pam_status;
+    const char * reply = NULL;
+#endif /* CK_PAM */
+
+    debug(F111,"user",name,logged_in);
+
+    if (!name) name = "";
+    zvuname[0] = NUL;
+
+    debug(F101,"zvuser ckxsyslog","",ckxsyslog);
+
+#ifdef CKSYSLOG
+    debug(F100,"zvuser CKSYSLOG defined","",0);
+#endif /* CKSYSLOG */
+
+    if (logged_in)                      /* Should not be called if logged in */
+      return(0);
+
+#ifdef CKSYSLOG
+    if (ckxsyslog && ckxlogging) {
+        syslog(LOG_INFO,
+                "login: user %s",name
+                );
+    }
+#endif /* CKSYSLOG */
+
+    guest = 0;                          /* Assume not guest */
+    askpasswd = 0;
+
+    if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
+        debug(F101,"zvuser anonymous ckxanon","",ckxanon);
+        if (!ckxanon) {                 /* Anonymous login not allowed */
+#ifdef CKSYSLOG
+            if (ckxsyslog && ckxlogging) {
+                syslog(LOG_INFO,
+                       "login: anonymous login not allowed: %s",
+                       clienthost ? clienthost : "(unknown host)"
+                       );
+            }
+#endif /* CKSYSLOG */
+            return(0);
+        }
+        if (checkuser("ftp") || checkuser("anonymous")) {
+            debug(F100,"zvuser anon forbidden by ftpusers file","",0);
+#ifdef CKSYSLOG
+            if (ckxsyslog && ckxlogging) {
+                syslog(LOG_INFO,
+                       "login: anonymous login forbidden by ftpusers file: %s",
+                       clienthost ? clienthost : "(unknown host)"
+                       );
+            }
+#endif /* CKSYSLOG */
+            return(0);
+       } else if ((pw = sgetpwnam("ftp")) != NULL) {
+            debug(F100,"zvuser anon sgetpwnam(ftp) OK","",0);
+            guest = 1;
+            askpasswd = 1;
+            ckstrncpy(zvuname,"anonymous",64);
+            return(1);
+        } else {
+            debug(F100,"zvuser anon sgetpwnam(ftp) FAILED","",0);
+#ifdef CKSYSLOG
+            if (ckxsyslog && ckxlogging) {
+                syslog(LOG_INFO,
+                       "login: anonymous getpwnam(ftp) failed: %s",
+                       clienthost ? clienthost : "(unknown host)"
+                       );
+            }
+#endif /* CKSYSLOG */
+            return(0);
+        }
+    }
+    pw = sgetpwnam(name);
+    if (pw) {
+/*
+  Of course some UNIX platforms (like AIX) don't have getusershell().
+  In that case we can't check if the user's account has been "turned off"
+  or somesuch, e.g. by setting their shell to "/etc/nologin" or somesuch,
+  which runs (usually just printing a message and exiting), but which is
+  not listed in /etc/shells.  For that matter, if getusershell() is not
+  available, then probably neither is /etc/shells.
+*/
+        debug(F100,"zvuser sgetpwnam ok","",0);
+        shell = pw->pw_shell;
+        if (!shell) shell = "";
+        if (!*shell)
+          shell = _PATH_BSHELL;
+        debug(F110,"zvuser shell",shell,0);
+#ifdef GETUSERSHELL
+        while ((cp = getusershell()) != NULL) {
+            debug(F110,"zvuser getusershell",cp,0);
+            if (strcmp(cp, shell) == 0)
+              break;
+        }
+        debug(F100,"zvuser endusershell 1","",0);
+        endusershell();
+        debug(F100,"zvuser endusershell 2","",0);
+#else /* GETUSERSHELL */
+        cp = "";                        /* Do not refuse if we cannot check */
+#endif /* GETUSERSHELL */
+        x = checkuser(name);
+        debug(F101,"zvuser checkuser","",x);
+        if (cp == NULL) {
+            debug(F100,"zvuser refused 1","",0);
+            pw = (struct passwd *) NULL;
+#ifdef CKSYSLOG
+            if (ckxsyslog && ckxlogging) {
+                syslog(LOG_INFO,
+                       "login: invalid shell %s for %s %s",shell, name,
+                       clienthost ? clienthost : "(unknown host)"
+                       );
+            }
+#endif /* CKSYSLOG */
+            return(0);
+        } else if (x) {
+            debug(F100,"zvuser refused 2","",0);
+            pw = (struct passwd *) NULL;
+#ifdef CKSYSLOG
+            if (ckxsyslog && ckxlogging) {
+                syslog(LOG_INFO,
+                       "login: %s login forbidden by ftpusers file: %s",
+                       name, clienthost ? clienthost : "(unknown host)"
+                       );
+            }
+#endif /* CKSYSLOG */
+            return(0);
+        } else {
+            x = 0;
+#ifdef CK_PAM
+            /* Get PAM authentication details */
+            debug(F110,"zvuser","calling pam_start",0);
+            if ((pam_status =
+                 pam_start(PAM_SERVICE_TYPE,name,&pam_conv,&pamh))
+                != PAM_SUCCESS) {
+                reply = pam_strerror(NULL, pam_status);
+                debug(F110,"zvuser PAM failure",reply,0);
+                printf("%s\n",reply);
+#ifdef CKSYSLOG
+                if (ckxsyslog && ckxlogging) {
+                    syslog(LOG_INFO,
+                           "login: %s refused by PAM \"%s\": %s",
+                           name,reply,
+                           clienthost ? clienthost : "(unknown host)"
+                           );
+                }
+#endif /* CKSYSLOG */
+                return(0);
+            }
+#endif /* CK_PAM */
+            askpasswd = 1;
+            ckstrncpy(zvuname,name,64);
+            return(1);
+        }
+    } else {
+        x = 0;
+        debug(F100,"zvuser sgetpwnam NULL","",0);
+#ifdef CKSYSLOG
+        if (ckxsyslog && ckxlogging) {
+            syslog(LOG_INFO,
+                   "login: getpwnam(%s) failed: %s",name,
+                   clienthost ? clienthost : "(unknown host)"
+                   );
+        }
+#endif /* CKSYSLOG */
+        return(0);
+    }
+
+#ifdef FTP_KERBEROS
+    if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) {
+#ifdef COMMENT
+       /* Why sprintf and then printf? */
+       /* Also, what is kerb_ok?  And is the test on it right? */
+        char buf[CKXLOGBSIZ];
+        sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s%s",
+                 kdata.pname, *kdata.pinst ? "." : "",
+                 kdata.pinst, kdata.prealm,
+                 (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not",
+                 name, kerb_ok ? "" : "; Password required.");
+        printf("%s", buf);
+#else
+        printf("Kerberos user %s%s%s@%s is%s authorized as %s%s",
+                 kdata.pname, *kdata.pinst ? "." : "",
+                 kdata.pinst, kdata.prealm,
+                 (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not",
+                 name, kerb_ok ? "" : "; Password required.");
+#endif /* COMMENT */
+        if (kerb_ok) return(1);
+    } else
+      return(0);
+#endif /* FTP_KERBEROS */
+}
+
+/* Check if the given user is in the forbidden-user file */
+
+static int
+checkuser(name) char *name; {
+    extern char * userfile;
+    FILE *fd;
+    int i;
+    char line[CKXLOGBSIZ+1];
+
+    if (!name)
+      name = "";
+    i = strlen(name);
+    debug(F111,"checkuser name",name,i);
+    if (!*name)
+      return(1);
+
+    fd = fopen(userfile ? userfile : _PATH_FTPUSERS, "r");
+    debug(F111,"checkuser userfile",userfile,fd);
+    if (fd) {
+        line[0] = '\0';
+        while (fgets(line, sizeof(line), fd)) {
+            debug(F110,"checkuser line",line,0);
+            if (line[0] <= '#')
+              continue;
+            if (strncmp(line, name, i) == 0) {
+                debug(F110,"checkuser REFUSED",name,0);
+                return(1);
+            }
+            line[0] = '\0';
+        }
+        (VOID) fclose(fd);
+    }
+    debug(F110,"checkuser OK",name,0);
+    return(0);
+}
+
+/*  Z V L O G O U T  --  Log out from Internet Kermit Service  */
+
+VOID
+zvlogout() {
+#ifdef COMMENT
+    /* This could be dangerous */
+    if (setuid((UID_T)0) < 0) {
+        debug(F100,"zvlogout setuid FAILED","",0);
+        goto bad;
+    }
+    debug(F100,"zvlogout setuid OK","",0);
+#endif /* COMMENT */
+#ifdef CKSYSLOG
+    if (ckxsyslog >= SYSLG_LI && ckxlogging) {
+        cksyslog(SYSLG_LI, 1, "logout",(char *) uidbuf, clienthost);
+    }
+#endif /* CKSYSLOG */
+#ifdef CKWTMP
+    debug(F110,"WTMP logout",cksysline,logged_in);
+    if (logged_in)
+      logwtmp(cksysline, "", "");
+#endif /* CKWTMP */
+    pw = NULL;
+    logged_in = 0;
+    guest = 0;
+    isguest = 0;
+}
+
+#ifdef FTP_KERBEROS
+kpass(name, p) char *name, *p; {
+    char instance[INST_SZ];
+    char realm[REALM_SZ];
+    char tkt_file[20];
+    KTEXT_ST ticket;
+    AUTH_DAT authdata;
+    unsigned long faddr;
+    struct hostent *hp;
+
+    if (krb_get_lrealm(realm, 1) != KSUCCESS)
+      return(0);
+
+    ckstrncpy(tkt_file, TKT_ROOT, 20);
+    ckstrncat(tkt_file, "_ftpdXXXXXX", 20);
+    krb_set_tkt_string(mktemp(tkt_file));
+
+    (VOID) ckstrncpy(instance, krb_get_phost(hostname), sizeof(instance));
+
+    if ((hp = gethostbyname(instance)) == NULL)
+      return(0);
+
+#ifdef HADDRLIST
+    hp = ck_copyhostent(hp);           /* safe copy that won't change */
+#endif /* HADDRLIST */
+    bcopy((char *)hp->h_addr, (char *) &faddr, sizeof(faddr));
+
+    if (krb_get_pw_in_tkt(name, "", realm, "krbtgt", realm, 1, p) ||
+        krb_mk_req(&ticket, "rcmd", instance, realm, 33) ||
+        krb_rd_req(&ticket, "rcmd", instance, faddr, &authdata, "") ||
+        kuserok(&authdata, name)) {
+        dest_tkt();
+        return(0);
+    }
+    dest_tkt();
+    return(1);
+}
+#endif /* FTP_KERBEROS */
+
+VOID
+zsyslog() {
+#ifdef CKSYSLOG
+    if (ckxsyslog && !ckxlogging) {
+#ifdef LOG_DAEMON
+        openlog(inserver ? "iksd" : "ckermit", LOG_PID, LOG_DAEMON);
+#else
+        openlog(inserver ? "iksd" : "ckermit", LOG_PID);
+#endif /* LOG_DAEMON */
+        ckxlogging = 1;
+        debug(F100,"zsyslog syslog opened","",0);
+    }
+#endif /* CKSYSLOG */
+}
+
+/*  Z V P A S S  --  Verify password; returns 1 if OK, 0 otherwise  */
+
+#ifndef AUTH_USER
+#define AUTH_USER 3
+#endif /* AUTH_USER */
+#ifndef AUTH_VALID
+#define AUTH_VALID 4
+#endif /* AUTH_VALID */
+
+int
+zvpass(p) char *p; {
+    char *xpasswd, *salt;
+    char * dir = NULL;
+#ifdef CK_PAM
+    int pam_status;
+    const char * reply = NULL;
+#endif /* CK_PAM */
+
+    if (logged_in || askpasswd == 0) {
+        return(0);
+    }
+    debug(F111,"zvpass",p ? (guest ? p : "xxxxxx") : "(null)",guest);
+    if (!p) p = "";
+    askpasswd = 0;
+    if (guest && !*p) {                 /* Guests must specify a password */
+#ifdef CKSYSLOG
+        if (ckxsyslog && ckxlogging) {
+            syslog(LOG_INFO,
+                   "login: anonymous guests must specify a password"
+                   );
+        }
+#endif /* CKSYSLOG */
+        return(0);
+    }
+    if (!guest
+#ifdef CK_AUTHENTICATION
+        && ck_tn_auth_valid() != AUTH_VALID
+#endif /* CK_AUTHENTICATION */
+        ) {                     /* "ftp" is only account allowed no password */
+#ifdef CK_PAM
+        debug(F110,"zvpass","calling pam_set_item(AUTHTOK)",0);
+        if ((pam_status = pam_set_item(pamh,PAM_AUTHTOK,p)) != PAM_SUCCESS) {
+            reply = pam_strerror(pamh, pam_status);
+            debug(F110,"zvpass PAM failure",reply,0);
+            /* if no password given treat as non-fatal error */
+            /* pam will prompt for password in pam_authenticate() */
+            if (!p) {
+                printf("%s\n",reply);
+                pam_end(pamh, 0);
+                debug(F100,"zvpass denied","",0);
+                pw = NULL;
+                zvuname[0] = NUL;
+                return(0);
+            }
+        }
+        debug(F110,"zvpass","calling pam_authenticate",0);
+        if (*p)
+         pam_pw = p;
+        if ((pam_status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
+            reply = pam_strerror(pamh, pam_status);
+            debug(F110,"zvpass PAM failure",reply,0);
+            printf("%s\n",reply);
+            pam_end(pamh, 0);
+            debug(F100,"zvpass denied","",0);
+            pam_pw = NULL;
+            pw = NULL;
+            zvuname[0] = NUL;
+            return(0);
+        }
+        pam_pw = NULL;
+        debug(F110,"zvpass","calling pam_acct_mgmt",0);
+        if ((pam_status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
+            reply = pam_strerror(pamh, pam_status);
+            debug(F110,"zvpass PAM failure",reply,0);
+            printf("%s\n",reply);
+            pam_end(pamh, 0);
+            debug(F100,"zvpass denied","",0);
+            pw = NULL;
+            zvuname[0] = NUL;
+            return(0);
+        }
+        debug(F110,"zvpass","PAM validates OK",0);
+        pam_end(pamh,0);
+#else /* CK_PAM */
+        if (pw == NULL)
+          salt = "xx";
+        else
+          salt = pw->pw_passwd;
+
+#ifdef HPUX10_TRUSTED
+        xpasswd = bigcrypt(p, salt);
+#else
+/*
+  On 64-bit platforms this can give "cast to pointer from integer of
+  different size" warning, but I'm not sure what the effect is at runtime,
+  or what to do about it.
+ */
+        xpasswd = (char *)crypt(p, salt);
+#endif /* HPUX10_TRUSTED */
+
+        if (
+#ifdef FTP_KERBEROS
+            /* null pw_passwd ok if Kerberos password ok */
+            pw == NULL ||
+            ((*pw->pw_passwd != '\0' ||
+              strcmp(xpasswd, pw->pw_passwd))
+             && !kpass(pw->pw_name, p))
+#else
+#ifdef CK_SRP
+            /* check with tpasswd first if there */
+            pw == NULL || *pw->pw_passwd == '\0' ||
+            t_verifypw (pw->pw_name, p) == 0 ||
+            (t_verifypw (pw->pw_name, p) < 0 &&
+            strcmp (xpasswd, pw->pw_passwd))
+#else /* CK_SRP */
+            /* The strcmp does not catch null passwords! */
+            (pw == NULL) || (*pw->pw_passwd == '\0') ||
+            strcmp(xpasswd, pw->pw_passwd)
+#endif /* CK_SRP */
+#endif /* FTP_KERBEROS */
+            ) {
+            debug(F100,"zvpass denied","",0);
+            pw = NULL;
+            zvuname[0] = NUL;
+            return(0);
+        }
+#endif /* CK_PAM */
+    }
+
+    (VOID) setgid((GID_T)pw->pw_gid);   /* Set group ID */
+
+#ifndef NOINITGROUPS
+    (VOID) initgroups(pw->pw_name, pw->pw_gid);
+#endif /* NOINITGROUPS */
+
+    logged_in = 1;
+    dir = pw->pw_dir;
+
+#ifdef CKWTMP
+    /* Open wtmp before chroot */
+    if (ckxwtmp) {
+        sprintf(cksysline,"iks_%04x", getpid()); /* safe */
+        logwtmp(cksysline, pw->pw_name,
+                 clienthost ? clienthost : "(unknown host)"
+                );
+        debug(F110,"WTMP login",cksysline,logged_in);
+    }
+#endif /* CKWTMP */
+/*
+  For anonymous users, we chroot to user ftp's home directory unless
+  started with --anonroot:xxx, in which case we chroot to xxx.  We must
+  immediately chdir() to the same directory we chroot() to or else the
+  old current directory remains accessible as "." outside the new root.
+*/
+    if (guest) {
+        if (anonroot)                   /* Non-default anonymous root */
+          dir = anonroot;
+        else
+          makestr(&anonroot,dir);
+        errno = 0;
+        debug(F110,"zvpass anon chroot",dir,0);
+        if (chroot(dir) < 0) {
+            debug(F111,"zvpass anon chroot FAILED",dir,errno);
+            goto bad;
+        }
+        errno = 0;
+        if (chdir("/") < 0) {
+            debug(F111,"zvpass anon chdir FAILED",dir,errno);
+            goto bad;
+        }
+        debug(F110,"zvpass anon chroot/chdir OK",dir,0);
+    } else if (chdir(dir) < 0) {        /* Not guest */
+#ifdef COMMENT
+        if (chdir("/") < 0) {
+            debug(F110,"Non-guest chdir FAILED",dir,0);
+            goto bad;
+        } else
+          printf("?No directory! Logging in with home=/\n");
+#else
+        debug(F110,"zvpass non-guest chdir FAILED",dir,0);
+        goto bad;                       /* Be conservative at first */
+#endif /* COMMENT */
+    }
+    debug(F110,"zvpass non-guest chdir OK",dir,0);
+    if (setuid((UID_T)pw->pw_uid) < 0) {
+        debug(F101,"zvpass setuid FAILED","",pw->pw_uid);
+        goto bad;
+    }
+    debug(F101,"zvpass setuid OK","",pw->pw_uid);
+
+    guestpass[0] = '\0';
+    if (guest) {
+        extern int fncact;
+        isguest = 1;
+        fncact = XYFX_R;                /* FILE COLLISION = RENAME */
+        debug(F110,"GUEST fncact=R",p,0);
+        lset(guestpass,"anonymous:",10,32);
+        ckstrncpy(&guestpass[10],p,GUESTPASS-10);
+        home = "/";
+        printf("Anonymous login.\r\n");
+
+#ifdef SETPROCTITLE
+       /* proctitle declared where?  Obviously this code is never compiled. */
+        sprintf(proctitle, "%s: anonymous/%.*s",
+                clienthost ? clienthost : "(unk)",
+                sizeof(proctitle) - sizeof(clienthost) -
+                sizeof(": anonymous/"), p);
+        setproctitle(proctitle);
+#endif /* SETPROCTITLE */
+
+#ifdef CKSYSLOG
+        if (ckxsyslog && ckxlogging) {
+            syslog(LOG_INFO,
+                   "login: anonymous %s %s",
+                   clienthost ? clienthost : "(unknown host)",
+                   p
+                   );
+        }
+#endif /* CKSYSLOG */
+
+    } else {                            /* Real user */
+        isguest = 0;
+        home = dir;
+        ckstrncpy(guestpass,zvuname,GUESTPASS);
+
+        printf("User %s logged in.\r\n", pw->pw_name);
+#ifdef SETPROCTITLE
+       /* not used */
+        sprintf(proctitle, "%s: %s",
+                clienthost ? clienthost : "(unk)",
+                pw->pw_name
+                );
+        setproctitle(proctitle);
+#endif /* SETPROCTITLE */
+
+#ifdef CKSYSLOG
+        if (ckxsyslog && ckxlogging)
+          syslog(LOG_INFO, "login: %s %s",
+                 pw->pw_name,
+                 clienthost ? clienthost : "(unknown host)"
+                 );
+#endif /* CKSYSLOG */
+    }
+    ckstrncpy(zvhome,home,CKMAXPATH);   /* Set environment variables */
+#ifndef NOPUTENV
+
+    ckmakmsg(zenvuser,ZENVUSER,"USER=",zvuname,NULL,NULL);
+    putenv((char *)zenvuser);
+    ckmakmsg(zenvlogname,ZENVLOGNAME,"LOGNAME=",zvuname,NULL,NULL);
+    putenv((char *)zenvlogname);
+    ckmakmsg(zenvhome,ZENVHOME,"HOME=",zvhome,NULL,NULL);
+    putenv((char *)zenvhome);
+#endif /* NOPUTENV */
+    /* homdir = (char *)zvhome; */
+    ckstrncpy((char *)uidbuf,(char *)zvuname,64);
+    (VOID) umask(defumask);
+#ifdef IKSDB
+    if (ikdbopen) {
+        char * p2;
+        int k;
+        extern char dbrec[];
+        extern unsigned long myflags;
+        extern unsigned int mydbslot;
+        extern struct iksdbfld dbfld[];
+#ifdef CK_AUTHENTICATION
+        extern unsigned long myamode, myatype;
+#endif /* CK_AUTHENTICATION */
+        myflags |= DBF_LOGGED;
+#ifdef DEBUG
+       if (deblog) {
+           debug(F101,"zvpass guest","",guest);
+           debug(F111,"zvpass zvuname",zvuname,0);
+           debug(F110,"zvpass guestpass",guestpass,0);
+           debug(F110,"zvpass dir",dir,0);
+           debug(F110,"zvpass home",home,0);
+           debug(F110,"zvpass anonroot",anonroot,0);
+       }
+#endif /* DEBUG */
+        p2 = guest ? guestpass : zvuname;
+        if (guest) {
+            p2 = (char *)guestpass;
+            myflags &= ~DBF_USER;
+        } else {
+            p2 = (char *)zvuname;
+            myflags |= DBF_USER;
+        }
+        k = strlen(p2);
+        strncpy(&dbrec[DB_ULEN],ulongtohex((unsigned long)k,4),4);
+        lset(&dbrec[dbfld[db_USER].off],p2,1024,32);
+        strncpy(&dbrec[DB_FLAGS],ulongtohex(myflags,4),4);
+#ifdef CK_AUTHENTICATION
+        myamode = ck_tn_auth_valid();
+        strncpy(&dbrec[DB_AMODE],ulongtohex(myamode,4),4);
+        myatype = ck_tn_authenticated();
+        strncpy(&dbrec[DB_ATYPE],ulongtohex(myatype,4),4);
+#endif /* CK_AUTHENTICATION */
+        if (guest) {
+            p2 = dir;
+        } else {
+            p2 = zgtdir();
+            if (!p2) p2 = "";
+            if (!*p2) p2 = home;
+        }
+        strncpy(&dbrec[DB_DLEN],
+                ulongtohex((unsigned long)strlen(p2),4),
+                4
+                );
+        lset(&dbrec[dbfld[db_DIR].off],p2,1024,32);
+        updslot(mydbslot);
+    }
+#endif /* IKSDB */
+    return(1);
+
+bad:                                    /* Common failure exit */
+    zvuname[0] = NUL;
+    zvlogout();
+    return(0);
+}
+#endif /* CK_LOGIN */
+
+/* Buggy Xenix 2.3.4 cc needs this line after the endif */
diff --git a/.pc/040_pam-password-prompting.patch/ckuus7.c b/.pc/040_pam-password-prompting.patch/ckuus7.c
new file mode 100644 (file)
index 0000000..4268ef1
--- /dev/null
@@ -0,0 +1,14933 @@
+#include "ckcsym.h"
+
+/*  C K U U S 7 --  "User Interface" for C-Kermit, part 7  */
+
+/*
+  Authors:
+    Frank da Cruz <fdc@columbia.edu>,
+      The Kermit Project, Columbia University, New York City
+    Jeffrey E Altman <jaltman@secure-endpoints.com>
+      Secure Endpoints Inc., New York City
+
+  Copyright (C) 1985, 2004,
+    Trustees of Columbia University in the City of New York.
+    All rights reserved.  See the C-Kermit COPYING.TXT file or the
+    copyright text in the ckcmai.c module for disclaimer and permissions.
+*/
+
+/*
+  This file created from parts of ckuus3.c, which became too big for
+  Mark Williams Coherent compiler to handle.
+*/
+
+/*
+  Definitions here supersede those from system include files.
+*/
+#include "ckcdeb.h"                     /* Debugging & compiler things */
+#include "ckcasc.h"                     /* ASCII character symbols */
+#include "ckcker.h"                     /* Kermit application definitions */
+#include "ckcxla.h"                     /* Character set translation */
+#include "ckcnet.h"                     /* Network symbols */
+#include "ckuusr.h"                     /* User interface symbols */
+#include "ckucmd.h"
+#include "ckclib.h"
+
+#ifdef VMS
+#ifndef TCPSOCKET
+#include <errno.h>
+#endif /* TCPSOCKET */
+#endif /* VMS */
+
+#ifdef OS2
+#ifndef NT
+#define INCL_NOPM
+#define INCL_VIO                        /* Needed for ckocon.h */
+#define INCL_DOSMODULEMGR
+#include <os2.h>
+#undef COMMENT
+#else /* NT */
+#define APIRET ULONG
+#include <windows.h>
+#include <tapi.h>
+#include "cknwin.h"
+#include "ckntap.h"
+#endif /* NT */
+#include "ckowin.h"
+#include "ckocon.h"
+#include "ckodir.h"
+#ifdef OS2MOUSE
+#include "ckokey.h"
+#endif /* OS2MOUSE */
+#ifdef KUI
+#include "ikui.h"
+#endif /* KUI */
+#ifdef putchar
+#undef putchar
+#endif /* putchar */
+#define putchar(x) conoc(x)
+extern int mskkeys;
+#endif /* OS2 */
+
+#ifdef CK_AUTHENTICATION
+#include "ckuath.h"
+#endif /* CK_AUTHENTICATION */
+#ifdef CK_SSL
+#include "ck_ssl.h"
+#endif /* CK_SSL */
+#ifdef SSHBUILTIN
+#include "ckossh.h"
+#endif /* SSHBUILTIN */
+#ifdef STRATUS                          /* Stratus Computer, Inc.  VOS */
+#ifdef putchar
+#undef putchar
+#endif /* putchar */
+#define putchar(x) conoc(x)
+#ifdef getchar
+#undef getchar
+#endif /* getchar */
+#define getchar(x) coninc(0)
+#endif /* STRATUS */
+
+char * slmsg = NULL;
+
+static int x, y = 0, z;
+static char *s;
+
+extern CHAR feol;
+extern int g_matchdot, hints, xcmdsrc, rcdactive;
+
+extern char * k_info_dir;
+
+#ifndef NOSPL
+extern int nmac;
+extern struct mtab *mactab;
+#endif /* NOSPL */
+
+#ifndef NOXFER
+#ifdef CK_SPEED
+extern short ctlp[];                    /* Control-char prefixing table */
+#endif /* CK_SPEED */
+
+#ifdef PIPESEND
+extern char * sndfilter, * g_sfilter;
+extern char * rcvfilter, * g_rfilter;
+#endif /* PIPESEND */
+
+extern char * snd_move;
+extern char * snd_rename;
+extern char * g_snd_move;
+extern char * g_snd_rename;
+extern char * rcv_move;
+extern char * rcv_rename;
+extern char * g_rcv_move;
+extern char * g_rcv_rename;
+
+#ifdef PATTERNS
+extern char *binpatterns[], *txtpatterns[];
+extern int patterns;
+#endif /* PATTERNS */
+
+extern char * remdest;
+#ifdef CK_TMPDIR
+char * dldir = NULL;
+#endif /* CK_TMPDIR */
+
+extern struct ck_p ptab[];
+
+extern int protocol, remfile, rempipe, remappd, reliable, xreliable, fmask,
+  fncnv, frecl, maxrps, wslotr, bigsbsiz, bigrbsiz, urpsiz, rpsiz, spsiz,
+  bctr, npad, timef, timint, spsizr, spsizf, maxsps, spmax, nfils, displa,
+  atcapr, pkttim, rtimo, fncact, mypadn, fdispla, f_save, pktpaus, setreliable,
+  fnrpath, fnspath, atenci, atenco, atdati, atdato, atleni, atleno, atblki,
+  atblko, attypi, attypo, atsidi, atsido, atsysi, atsyso, atdisi, atdiso;
+
+extern int stathack;
+
+extern int atfrmi, atfrmo;
+#ifdef STRATUS
+extern int atcrei, atcreo, atacti, atacto;
+#endif /* STRATUS */
+#ifdef CK_PERMS
+extern int atlpri, atlpro, atgpri, atgpro;
+#endif /* CK_PERMS */
+
+extern CHAR
+  sstate, eol, seol, stchr, mystch, mypadc, padch, ctlq, myctlq;
+
+#ifdef IKSD
+extern int inserver;
+#ifdef IKSDCONF
+extern int iksdcf;
+#endif /* IKSDCONF */
+#endif /* IKSD */
+
+extern char *cmarg, *cmarg2;
+
+#ifndef NOFRILLS
+extern char optbuf[];                   /* Buffer for MAIL or PRINT options */
+extern int rprintf;                     /* REMOTE PRINT flag */
+#endif /* NOFRILLS */
+#endif /* NOXFER */
+
+#ifdef CK_TRIGGER
+extern char * tt_trigger[];
+#endif /* CK_TRIGGER */
+
+extern int tcs_transp;
+#ifdef PCTERM
+extern int tt_pcterm;
+#endif /* PCTERM */
+#ifdef NT
+extern int tt_vtnt;
+#endif /* NT */
+
+#ifdef SSHBUILTIN
+int sl_ssh_xfw = 0;
+int sl_ssh_xfw_saved = 0;
+int sl_ssh_ver = 0;
+int sl_ssh_ver_saved = 0;
+#endif /* SSHBUILTIN */
+
+#ifdef CK_AUTHENTICATION
+extern int auth_type_user[];
+int sl_auth_type_user[AUTHTYPLSTSZ] = {AUTHTYPE_NULL, AUTHTYPE_NULL};
+int sl_auth_saved = 0;
+int sl_topt_a_su = 0;
+int sl_topt_a_s_saved = 0;
+int sl_topt_a_cm = 0;
+int sl_topt_a_c_saved = 0;
+#endif /* CK_AUTHENTICATION */
+#ifdef CK_ENCRYPTION
+extern int cx_type;
+int sl_cx_type = 0;
+int sl_cx_saved = 0;
+int sl_topt_e_su = 0;
+int sl_topt_e_sm = 0;
+int sl_topt_e_s_saved = 0;
+int sl_topt_e_cu = 0;
+int sl_topt_e_cm = 0;
+int sl_topt_e_c_saved = 0;
+#endif /* CK_ENCRYPTION */
+extern char uidbuf[];
+static int uidflag = 0;
+char sl_uidbuf[UIDBUFLEN] = { NUL, NUL };
+int  sl_uid_saved = 0;
+#ifdef TNCODE
+int  sl_tn_wait = 0;
+int  sl_tn_saved = 0;
+#endif /* TNCODE */
+
+#ifdef TNCODE
+extern int tn_wait_flg;
+#endif /* TNCODE */
+
+VOID
+slrestor() {
+#ifdef CK_AUTHENTICATION
+    int x;
+    if (sl_auth_saved) {
+        for (x = 0; x < AUTHTYPLSTSZ; x++)
+          auth_type_user[x] = sl_auth_type_user[x];
+        sl_auth_saved = 0;
+    }
+    if (sl_topt_a_s_saved) {
+        TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = sl_topt_a_su;
+        sl_topt_a_s_saved = 0;
+    }
+    if (sl_topt_a_c_saved) {
+        TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = sl_topt_a_cm;
+        sl_topt_a_c_saved = 0;
+    }
+#endif /* CK_AUTHENTICATION */
+#ifdef CK_ENCRYPTION
+    if (sl_cx_saved) {
+        cx_type = sl_cx_type;
+        sl_cx_saved = 0;
+    }
+    if (sl_topt_e_s_saved) {
+        TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION)  = sl_topt_e_su;
+        TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION) = sl_topt_e_sm;
+        sl_topt_e_s_saved = 0;
+    }
+    if (sl_topt_e_c_saved) {
+        TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION)  = sl_topt_e_cu;
+        TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) = sl_topt_e_cm;
+        sl_topt_e_c_saved = 0;
+    }
+#endif /* CK_ENCRYPTION */
+    if (sl_uid_saved) {
+        ckstrncpy(uidbuf,sl_uidbuf,UIDBUFLEN);
+        sl_uid_saved = 0;
+    }
+#ifdef TNCODE
+    if (sl_tn_saved) {
+        tn_wait_flg = sl_tn_wait;
+        sl_tn_saved = 0;
+    }
+#endif /* TNCODE */
+#ifdef SSHBUILTIN
+    if (sl_ssh_xfw_saved) {
+        ssh_xfw = sl_ssh_xfw;
+        sl_ssh_xfw_saved = 0;
+    }
+    if (sl_ssh_ver_saved) {
+        ssh_ver = sl_ssh_ver;
+        sl_ssh_ver_saved = 0;
+    }
+#endif /* SSHBUILTIN */
+}
+
+int oldplex = -1;                       /* Duplex holder around network */
+
+#ifndef NOICP
+#ifdef LOCUS
+extern int locus, autolocus;
+#endif /* LOCUS */
+#ifndef NODIAL
+extern int dialsta;
+#endif /* NODIAL */
+
+/* Note: gcc -Wall wants braces around each keyword table entry. */
+
+static struct keytab psltab[] = {       /* SET LINE/PORT command options */
+    { "/connect", SL_CNX, 0 },
+#ifdef OS2ORVMS
+    { "/noshare", SL_NSH, 0 },
+#endif /* OS2ORVMS */
+    { "/server",  SL_SRV, 0 },
+#ifdef OS2ORVMS
+    { "/share",   SL_SHR, 0 },
+#endif /* OS2ORVMS */
+    { "", 0, 0 }
+};
+static int npsltab = sizeof(psltab)/sizeof(struct keytab) - 1;
+
+#ifdef NETCONN
+static struct keytab shtab[] = {        /* SET HOST command options */
+#ifdef NETCMD
+    /* (COMMAND is also a network type) */
+    { "/command",      SL_CMD,    CM_INV },
+#endif /* NETCMD */
+    { "/connect",      SL_CNX,    0 },
+    { "/network-type", SL_NET,    CM_ARG },
+    { "/nowait",       SL_NOWAIT, 0 },
+#ifndef NOSPL
+#ifdef CK_AUTHENTICATION
+    { "/password",     SL_PSW,    CM_ARG },
+#endif /* CK_AUTHENTICATION */
+#endif /* NOSPL */
+#ifdef NETCMD
+    { "/pipe",         SL_CMD,    0 },
+#endif /* NETCMD */
+#ifdef NETPTY
+    { "/pty",          SL_PTY,    0 },
+#endif /* NETPTY */
+    { "/server",       SL_SRV,    0 },
+    { "/timeout",      SL_TMO,    CM_ARG },
+    { "/userid",       SL_UID,    CM_ARG },
+    { "/wait",         SL_WAIT,   0 },
+    { "", 0, 0 }
+};
+static int nshtab = sizeof(shtab)/sizeof(struct keytab) - 1;
+
+static struct keytab shteltab[] = {     /* TELNET command options */
+#ifdef CK_AUTHENTICATION
+    { "/auth",         SL_AUTH,   CM_ARG },
+#endif /* CK_AUTHENTICATION */
+#ifdef CK_ENCRYPTION
+    { "/encrypt",      SL_ENC,    CM_ARG },
+#endif /* CK_ENCRYPTION */
+    { "/nowait",       SL_NOWAIT, 0 },
+#ifndef NOSPL
+#ifdef CK_AUTHENTICATION
+    { "/password",     SL_PSW,    CM_ARG },
+#endif /* CK_AUTHENTICATION */
+#endif /* NOSPL */
+    { "/timeout",      SL_TMO,    CM_ARG },
+    { "/userid",       SL_UID,    CM_ARG },
+    { "/wait",         SL_WAIT,   0 },
+    { "", 0 ,0 }
+};
+static int nshteltab = sizeof(shteltab)/sizeof(struct keytab) - 1;
+
+#ifdef RLOGCODE
+static struct keytab shrlgtab[] = {     /* SET HOST RLOGIN command options */
+#ifdef CK_KERBEROS
+#ifdef CK_ENCRYPTION
+    { "/encrypt",      SL_ENC, 0 },
+#endif /* CK_ENCRYPTION */
+    { "/k4",           SL_KRB4, CM_INV },
+    { "/k5",           SL_KRB5, CM_INV },
+    { "/kerberos4",    SL_KRB4, 0 },
+    { "/kerberos5",    SL_KRB5, 0 },
+    { "/kerberos_iv",  SL_KRB4, CM_INV },
+    { "/kerberos_v",   SL_KRB5, CM_INV },
+    { "/krb4",         SL_KRB4, CM_INV },
+    { "/krb5",         SL_KRB5, CM_INV },
+#endif /* CK_KERBEROS */
+    { "", 0 ,0 }
+};
+static int nshrlgtab = sizeof(shrlgtab)/sizeof(struct keytab)-1;
+#endif /* RLOGCODE */
+
+extern struct keytab netcmd[];
+extern int nnets;
+#ifndef NODIAL
+extern int dirline;
+extern int nnetdir;                     /* Network services directory */
+extern char *netdir[];
+_PROTOTYP( VOID ndreset, (void) );
+char *nh_p[MAXDNUMS + 1];               /* Network directory entry pointers */
+char *nh_p2[MAXDNUMS + 1];              /* Network directory entry nettype */
+char *nh_px[4][MAXDNUMS + 1];           /* Network-specific stuff... */
+#endif /* NODIAL */
+int nhcount = 0;
+int ndinited = 0;
+char * n_name = NULL;                   /* Network name pointer */
+#endif /* NETCONN */
+
+_PROTOTYP(int remtxt, (char **) );
+_PROTOTYP(VOID rmsg, (void) );
+_PROTOTYP(static int remcfm, (void) );
+
+extern int nopush;
+
+int mdmsav = -1;                        /* Save modem type around network */
+extern int isguest;                     /* Global flag for anonymous login */
+
+extern xx_strp xxstring;
+
+extern int success, binary, b_save, ckwarn, msgflg, quiet, cmask, pflag, local,
+  nettype, escape, mdmtyp, duplex, dfloc, network, cdtimo, autoflow, tnlm,
+  sosi, tlevel, lf_opts, backgrd, flow, debses, parity, ttnproto, ckxech,
+  x_ifnum, cmflgs, haveline, cxtype, cxflow[], maclvl;
+
+#ifdef DCMDBUF
+extern struct cmdptr *cmdstk;           /* The command stack itself */
+#else
+extern struct cmdptr cmdstk[];          /* The command stack itself */
+#endif /* DCMDBUF */
+extern FILE * tfile[];
+extern char * macp[];
+
+extern char psave[];                    /* For saving & restoring prompt */
+extern int sprmlen, rprmlen;
+
+#ifdef OS2
+static struct keytab strmkeytab[] = {
+    { "clear",   0, 0 },
+    { "default", 1, 0 }
+};
+static int nstrmkeytab = sizeof(strmkeytab)/sizeof(struct keytab);
+
+static struct keytab strmswitab[] = {
+    { "/literal", 0, 0 }
+};
+static int nstrmswitab = sizeof(strmswitab)/sizeof(struct keytab);
+
+static struct keytab normrev[] = {
+    { "dark-display", 0, 0 },
+    { "light-display", 1, 0 },
+    { "normal",   0, 0 },
+    { "reverse",  1, 0 }
+};
+
+static struct keytab prnmtab[] = {
+    { "auto", 1, 0 },
+    { "copy", 2, 0 },
+    { "off",  0, 0 },
+    { "on",   1, CM_INV },              /* Compatibility with XPRINT version */
+    { "user", 3, 0 },
+    { "transparent", 3, CM_INV }        /* not really transparent */
+};
+static int nprnmtab = sizeof(prnmtab)/sizeof(struct keytab);
+
+extern int tt_diff_upd;
+
+#ifdef NT
+#define stricmp _stricmp
+extern int tt_attr_bug;
+#endif /* NT */
+extern int tt_rows[], tt_cols[];
+extern int tt_cols_usr;
+extern int tt_szchng[VNUM];
+int tt_modechg = TVC_ENA;
+extern int tt_url_hilite, tt_url_hilite_attr;
+extern struct _vtG G[4];
+extern int priority;
+extern bool send_c1;
+int send_c1_usr = FALSE;
+extern int sgrcolors;
+extern int marginbell, marginbellcol;
+extern int autoscroll, wy_autopage;
+extern int tt_sac;
+extern int dec_nrc, dec_lang, dec_kbd;
+#else /* OS2 */
+extern int tt_rows, tt_cols;
+#endif /*  OS2 */
+
+extern int tt_escape;
+extern long speed;
+
+extern char *dftty;
+
+extern char *tp, *lp;                   /* Temporary buffer & pointers */
+extern char ttname[];
+
+#ifdef CK_TAPI
+int tttapi = 0;                         /* is Line TAPI? */
+struct keytab * tapilinetab = NULL;
+struct keytab * _tapilinetab = NULL;
+int ntapiline = 0;
+#endif /* CK_TAPI */
+
+#ifdef NETCONN                          /* Network items */
+
+#ifdef ANYX25
+extern int revcall, closgr, cudata, nx25;
+extern char udata[];
+extern struct keytab x25tab[];
+#ifndef IBMX25
+extern int npadx3;
+extern CHAR padparms[];
+extern struct keytab padx3tab[];
+#endif /* IBMX25 */
+#endif /* ANYX25 */
+
+#ifdef OS2
+extern bool ttshare;
+#ifndef NT
+extern bool ttslip,ttppp;
+#endif /* NT */
+#endif /* OS2 */
+#ifdef NPIPE
+extern char pipename[];
+#endif /* NPIPE */
+
+#ifdef TCPSOCKET
+static struct keytab tcprawtab[] = {    /* SET HOST options */
+    { "/default",    NP_DEFAULT,    CM_INV },
+#ifdef CK_AUTHENTICATION
+#ifdef CK_KERBEROS
+#ifdef RLOGCODE
+    { "/ek4login",    NP_EK4LOGIN,    0 },
+    { "/ek5login",    NP_EK5LOGIN,    0 },
+    { "/k4login",     NP_K4LOGIN,     0 },
+    { "/k5login",     NP_K5LOGIN,     0 },
+#endif /* RLOGCODE */
+#ifdef KRB5_U2U
+    { "/k5user2user", NP_K5U2U,       0 },
+#endif /* KRB5_U2U */
+#endif /* CK_KERBEROS */
+#endif /* CK_AUTHENTICATION */
+    { "/no-telnet-init", NP_NONE,   0 },
+    { "/none",       NP_NONE,   CM_INV },
+    { "/raw-socket", NP_TCPRAW, 0 },
+#ifdef RLOGCODE
+    { "/rlogin",     NP_RLOGIN, 0 },
+#endif /* RLOGCODE */
+#ifdef CK_SSL
+    { "/ssl",        NP_SSL,    0 },
+    { "/ssl-telnet", NP_SSL_TELNET, 0 },
+#endif /* CK_SSL */
+    { "/telnet",     NP_TELNET, 0 },
+#ifdef CK_SSL
+    { "/tls",        NP_TLS,    0 },
+    { "/tls-telnet", NP_TLS_TELNET, 0 },
+#endif /* CK_SSL */
+    { "", 0, 0 }
+};
+static int ntcpraw = (sizeof(tcprawtab) / sizeof(struct keytab)) - 1;
+
+#ifdef RLOGCODE
+_PROTOTYP( int rlog_naws, (void) );
+#endif /* RLOGCODE */
+#endif /* TCPSOCKET */
+
+#ifdef SUPERLAT
+extern char slat_pwd[18];
+#endif /* SUPERLAT */
+#endif /* NETCONN */
+
+#ifdef COMMENT
+#ifndef NOSETKEY
+extern KEY *keymap;
+#ifndef OS2
+#define mapkey(x) keymap[x]
+#endif /* OS2 */
+extern MACRO *macrotab;
+#ifndef NOKVERBS
+extern struct keytab kverbs[];
+extern int nkverbs;
+#endif /* NOKVERBS */
+#endif /* NOSETKEY */
+#else
+#ifndef NOSETKEY
+extern KEY *keymap;
+extern MACRO *macrotab;
+#ifndef NOKVERBS
+extern struct keytab kverbs[];
+extern int nkverbs;
+#endif /* NOKVERBS */
+#endif /* NOSETKEY */
+#endif /* COMMENT */
+
+#ifdef OS2                              /* AUTODOWNLOAD parameters */
+extern int    adl_kmode, adl_zmode;     /* Match Packet to signal download */
+extern char * adl_kstr;                 /* KERMIT Download String */
+extern char * adl_zstr;                 /* ZMODEM Download String */
+extern int adl_kc0, adl_zc0;            /* Process ADL C0s in emulation */
+#endif /* OS2 */
+
+/* Keyword tables ... */
+
+extern struct keytab onoff[], rltab[];
+extern int nrlt;
+
+#ifndef NOCSETS
+static struct keytab fdfltab[] = {
+    { "7bit-character-set", 7, 0 },
+    { "8bit-character-set", 8, 0 }
+};
+static int nfdflt = (sizeof(fdfltab) / sizeof(struct keytab));
+#endif /* NOCSETS */
+
+/* SET FILE parameters */
+
+static struct keytab filtab[] = {
+#ifndef NOXFER
+#ifdef PATTERNS
+    { "binary-patterns",   XYFIBP,  0 },
+#endif /* PATTERNS */
+    { "bytesize",         XYFILS,   0 },
+#ifndef NOCSETS
+    { "character-set",    XYFILC,   0 },
+#endif /* NOCSETS */
+    { "collision",        XYFILX,   0 },
+    { "default",          XYF_DFLT, 0 },
+    { "destination",      XYFILY,   0 },
+    { "display",          XYFILD,   CM_INV },
+#ifdef CK_TMPDIR
+    { "download-directory", XYFILG, 0 },
+#endif /* CK_TMPDIR */
+#endif /* NOXFER */
+    { "end-of-line",      XYFILA,   0 },
+    { "eol",              XYFILA,   CM_INV },
+#ifdef CK_CTRLZ
+    { "eof",              XYFILV,   0 },
+#endif /* CK_CTRLZ */
+#ifndef NOXFER
+    { "fastlookups",      9997,     CM_INV },
+    { "incomplete",       XYFILI,   0 },
+#ifndef datageneral
+    { "inspection",       XYF_INSP, CM_INV },
+#endif /* datageneral */
+#ifdef CK_LABELED
+    { "label",            XYFILL, 0 },
+#endif /* CK_LABELED */
+
+#ifdef UNIX
+#ifdef DYNAMIC
+    { "listsize",         XYF_LSIZ, 0 },
+#endif /* DYNAMIC */
+#endif /* UNIX */
+
+    { "names",            XYFILN, 0 },
+#ifdef UNIX
+    { "output",           XYFILH, 0 },
+#endif /* UNIX */
+#ifdef PATTERNS
+    { "patterns",         XYFIPA, 0 },
+#endif /* PATTERNS */
+#ifdef COMMENT /* Not implemented (but see CHMOD) */
+    { "permissions",      XYF_PRM, CM_INV },
+    { "protection",       XYF_PRM, 0 },
+#endif /* COMMENt */
+#ifdef VMS
+    { "record-length",    XYFILR, 0 },
+#endif /* VMS */
+#ifndef datageneral
+    { "scan",             XYF_INSP, 0 },
+#endif /* datageneral */
+
+#ifdef UNIX
+#ifdef DYNAMIC
+    { "stringspace",      XYF_SSPA, 0 },
+#endif /* DYNAMIC */
+#endif /* UNIX */
+
+#ifdef PATTERNS
+    { "t",                XYFILT, CM_INV|CM_ABR },
+    { "text-patterns",    XYFITP, 0 },
+#endif /* PATTERNS */
+#endif /* NOXFER */
+    { "type",             XYFILT, 0 },
+#ifdef UNICODE
+    { "ucs",              XYFILU, 0 },
+#endif /* UNICODE */
+#ifndef NOXFER
+    { "warning",          XYFILW, CM_INV }
+#endif /* NOXFER */
+};
+static int nfilp = (sizeof(filtab) / sizeof(struct keytab));
+
+struct keytab pathtab[] = {
+    { "absolute",  PATH_ABS,  0      },
+    { "none",      PATH_OFF,  CM_INV },
+    { "off",       PATH_OFF,  0      },
+    { "on",        PATH_ABS,  CM_INV },
+    { "relative",  PATH_REL,  0      }
+};
+int npathtab = (sizeof(pathtab) / sizeof(struct keytab));
+
+struct keytab rpathtab[] = {
+    { "absolute",  PATH_ABS,  0      },
+    { "auto",      PATH_AUTO, 0      },
+    { "none",      PATH_OFF,  CM_INV },
+    { "off",       PATH_OFF,  0      },
+    { "on",        PATH_ABS,  CM_INV },
+    { "relative",  PATH_REL,  0      }
+};
+int nrpathtab = (sizeof(rpathtab) / sizeof(struct keytab));
+
+#ifdef CK_CTRLZ
+struct keytab eoftab[] = {              /* EOF detection method */
+    { "ctrl-z",          1, 0      },
+    { "length",          0, 0      },
+    { "noctrl-z",        0, CM_INV }
+};
+#endif /* CK_CTRLZ */
+
+struct keytab fttab[] = {               /* File types for SET FILE TYPE */
+    { "ascii",     XYFT_T, CM_INV },
+#ifdef VMS
+    { "b",         XYFT_B, CM_INV|CM_ABR },
+#endif /* VMS */
+    { "binary",    XYFT_B, 0 },
+#ifdef VMS
+    { "block",     XYFT_I, CM_INV },
+    { "image",     XYFT_I, 0 },
+#endif /* VMS */
+#ifdef CK_LABELED
+    { "labeled",   XYFT_L, 0 },
+#endif /* CK_LABELED */
+#ifdef MAC
+    { "macbinary", XYFT_M, 0 },
+#endif /* MAC */
+    { "text",      XYFT_T, 0 }
+};
+int nfttyp = (sizeof(fttab) / sizeof(struct keytab));
+
+static struct keytab rfttab[] = {       /* File types for REMOTE SET FILE */
+    { "ascii",     XYFT_T, CM_INV },
+    { "binary",    XYFT_B, 0 },
+#ifdef VMS
+    { "labeled",   XYFT_L, 0 },
+#else
+#ifdef OS2
+    { "labeled",   XYFT_L, 0 },
+#endif /* OS2 */
+#endif /* VMS */
+    { "text",      XYFT_T, 0 }
+};
+static int nrfttyp = (sizeof(rfttab) / sizeof(struct keytab));
+
+#ifdef OS2ORUNIX
+#define ZOF_BLK  0
+#define ZOF_NBLK 1
+#define ZOF_BUF  2
+#define ZOF_NBUF 3
+static struct keytab zoftab[] = {
+    { "blocking",    ZOF_BLK,  0 },
+    { "buffered",    ZOF_BUF,  0 },
+    { "nonblocking", ZOF_NBLK, 0 },
+    { "unbuffered",  ZOF_NBUF, 0 }
+};
+static int nzoftab = (sizeof(zoftab) / sizeof(struct keytab));
+#endif /* OS2ORUNIX */
+
+extern int query;                       /* Global flag for QUERY active */
+
+#ifndef NOSPL
+#ifndef NOXFER
+static struct keytab vartyp[] = {       /* Variable types for REMOTE QUERY */
+    { "global",   (int) 'G', CM_INV },
+    { "kermit",   (int) 'K', 0 },
+    { "system",   (int) 'S', 0 },
+    { "user",     (int) 'G', 0 }
+};
+static int nvartyp = (sizeof(vartyp) / sizeof(struct keytab));
+#endif /* NOXFER */
+#endif /* NOSPL */
+
+#ifdef CK_TIMERS
+static struct keytab timotab[] = {      /* Timer types */
+    { "dynamic", 1, 0 },
+    { "fixed",   0, 0 }
+};
+#endif /* CK_TIMERS */
+
+#ifdef DCMDBUF
+extern char *atxbuf, *atmbuf;           /* Atom buffer */
+extern char *cmdbuf;                    /* Command buffer */
+extern char *line, *tmpbuf;             /* Character buffers for anything */
+extern int *intime;                     /* INPUT TIMEOUT */
+
+#else  /* Not DCMDBUF ... */
+
+extern char atxbuf[], atmbuf[];         /* Atom buffer */
+extern char cmdbuf[];                   /* Command buffer */
+extern char line[], tmpbuf[];           /* Character buffer for anything */
+extern int intime[];
+
+#endif /* DCMDBUF */
+
+#ifndef NOCSETS
+extern struct keytab fcstab[];          /* For SET FILE CHARACTER-SET */
+extern struct csinfo fcsinfo[];         /* File character set info. */
+extern struct keytab ttcstab[];
+extern int nfilc, fcharset, tcharset, ntermc, tcsr, tcsl, dcset7, dcset8;
+#ifdef CKOUNI
+extern int tt_utf8;
+#endif /* CKOUNI */
+#ifdef OS2
+_PROTOTYP( int os2setcp, (int) );
+_PROTOTYP( int os2getcp, (void) );
+_PROTOTYP( void os2debugoff, (void) );
+#endif /* OS2 */
+#endif /* NOCSETS */
+
+extern int cmdlvl;                      /* Overall command level */
+
+#ifndef NOSPL
+#ifdef DCMDBUF
+extern int *inpcas;                     /* INPUT CASE setting on cmd stack */
+#else
+extern int inpcas[];
+#endif /* DCMDBUF */
+#endif /* NOSPL */
+
+#ifdef CK_CURSES
+#ifndef VMS
+_PROTOTYP(int tgetent,(char *, char *));
+#else
+#ifdef __DECC
+_PROTOTYP(int tgetent,(char *, char *));
+#endif /* __DECC */
+#endif /* VMS */
+#endif /* CK_CURSES */
+
+#ifndef NOXMIT
+#define XMITF 0                         /* SET TRANSMIT values */
+#define XMITL 1                         /* (Local to this module) */
+#define XMITP 2
+#define XMITE 3
+#define XMITX 4
+#define XMITS 5
+#define XMITW 6
+#define XMITT 7
+
+#define XMBUFL 50
+extern int xmitf, xmitl, xmitp, xmitx, xmits, xmitw, xmitt;
+char xmitbuf[XMBUFL+1] = { NUL };       /* TRANSMIT eof string */
+
+struct keytab xmitab[] = {              /* SET TRANSMIT */
+    { "echo",          XMITX, 0 },
+    { "eof",           XMITE, 0 },
+    { "fill",          XMITF, 0 },
+    { "linefeed",      XMITL, 0 },
+    { "locking-shift", XMITS, 0 },
+    { "pause",         XMITW, 0 },
+    { "prompt",        XMITP, 0 },
+    { "timeout",       XMITT, 0 }
+};
+int nxmit = (sizeof(xmitab) / sizeof(struct keytab));
+#endif /* NOXMIT */
+
+/* For SET FILE COLLISION */
+/* Some of the following may be possible for some C-Kermit implementations */
+/* but not others.  Those that are not possible for your implementation */
+/* should be ifdef'd out. */
+
+struct keytab colxtab[] = { /* SET FILE COLLISION options */
+#ifndef MAC
+    { "append",    XYFX_A, 0 },         /* append to old file */
+#endif /* MAC */
+#ifdef COMMENT
+    { "ask",       XYFX_Q, 0 },         /* ask what to do (not implemented) */
+#endif
+    { "backup",    XYFX_B, 0 },         /* rename old file */
+#ifndef MAC
+    /* This crashes Mac Kermit. */
+    { "discard",   XYFX_D, 0 },         /* don't accept new file */
+    { "no-supersede", XYFX_D, CM_INV }, /* ditto (MSK compatibility) */
+#endif /* MAC */
+    { "overwrite", XYFX_X, 0 },         /* overwrite the old file */
+    { "rename",    XYFX_R, 0 },         /* rename the incoming file */
+#ifndef MAC                             /* This crashes Mac Kermit. */
+    { "update",    XYFX_U, 0 },         /* replace if newer */
+#endif /* MAC */
+    { "", 0, 0 }
+};
+int ncolx = (sizeof(colxtab) / sizeof(struct keytab)) - 1;
+
+static struct keytab rfiltab[] = {      /* for REMOTE SET FILE */
+#ifndef NOCSETS
+    { "character-set", XYFILC, 0 },
+#endif /* NOCSETS */
+    { "collision",     XYFILX, 0 },
+    { "incomplete",    XYFILI, 0 },
+    { "names",         XYFILN, 0 },
+    { "record-length", XYFILR, 0 },
+    { "type",          XYFILT, 0 }
+};
+int nrfilp = (sizeof(rfiltab) / sizeof(struct keytab));
+
+struct keytab eoltab[] = {              /* File eof delimiters */
+    { "cr",        XYFA_C, 0 },
+    { "crlf",      XYFA_2, 0 },
+    { "lf",        XYFA_L, 0 }
+};
+static int neoltab = (sizeof(eoltab) / sizeof(struct keytab));
+
+struct keytab fntab[] = {               /* File naming */
+    { "converted", XYFN_C, 0      },
+    { "literal",   XYFN_L, 0      },
+    { "standard",  XYFN_C, CM_INV }
+};
+int nfntab = (sizeof(fntab) / sizeof(struct keytab));
+
+#ifndef NOLOCAL
+/* Terminal parameters table */
+static struct keytab trmtab[] = {
+#ifdef OS2
+    { "answerback",    XYTANS,    0 },
+#endif /* OS2 */
+#ifdef CK_APC
+    { "apc",           XYTAPC,    0 },
+#endif /* CK_APC */
+#ifdef OS2
+    { "arrow-keys",    XYTARR,    0 },
+#endif /* OS2 */
+#ifdef NT
+    { "at",            XYTATTR,   CM_INV|CM_ABR },
+    { "att",           XYTATTR,   CM_INV|CM_ABR },
+    { "attr",          XYTATTR,   CM_INV|CM_ABR },
+    { "attr-bug",      XYTATTBUG, CM_INV },
+#endif /* NT */
+#ifdef OS2
+    { "attribute",     XYTATTR,   0 },
+#endif /* OS2 */
+#ifdef CK_APC
+#ifdef CK_AUTODL
+   { "autodownload",   XYTAUTODL, 0, },
+#endif /* CK_AUTODL */
+#endif /* CK_APC */
+#ifdef OS2
+    { "autopage",      XYTAPAGE,  0 },
+    { "autoscroll",    XYTASCRL,  0 },
+    { "bell",          XYTBEL,    CM_INV },
+#endif /* OS2 */
+    { "bytesize",      XYTBYT,    0 },
+#ifndef NOCSETS
+    { "character-set", XYTCS,     0 },
+#endif /* NOCSETS */
+#ifdef OS2
+    { "code-page",     XYTCPG,    0 },
+    { "color",         XYTCOL,    0 },
+    { "controls",      XYTCTRL,   0 },
+#endif /* OS2 */
+    { "cr-display",    XYTCRD,    0 },
+#ifdef OS2
+    { "cursor",        XYTCUR,    0 },
+#endif /* OS2 */
+    { "debug",         XYTDEB,    0 },
+#ifdef OS2
+    { "dg-unix-mode",  XYTUNX,    0 },
+#endif /* OS2 */
+    { "echo",          XYTEC,     0 },
+    { "escape-character", XYTESC, 0 },
+#ifdef OS2
+#ifdef PCFONTS
+    { "font",          XYTFON,    0 },
+#else
+#ifdef KUI
+    { "font",          XYTFON,    0 },
+#endif /* KUI */
+#endif /* PCFONTS */
+#endif /* OS2 */
+    { "height",        XYTHIG,    0 },
+#ifdef CKTIDLE
+    { "idle-action",   XYTIACT,   0 },
+    { "idle-limit",    XYTITMO,   CM_INV },
+    { "idle-send",     XYTIDLE,   CM_INV },
+    { "idle-timeout",  XYTITMO,   0 },
+#endif /* CKTIDLE */
+#ifdef OS2
+#ifndef NOCSETS
+    { "kbd-follows-gl/gr", XYTKBDGL, 0 },
+#endif /* NOCSETS */
+    { "key",           XYTKEY,    0 },
+    { "keyboard-mode", XYTKBMOD,  0 },
+    { "keypad-mode",   XYTKPD,    0 },
+#endif /* OS2 */
+#ifndef NOCSETS
+#ifdef OS2
+#ifndef KUI
+    { "line-spacing",  XYTLSP,    CM_INV },
+    { "local-character-set", XYTLCS,  0 },
+#else
+    { "line-spacing",  XYTLSP,    0 },
+    { "local-character-set", XYTLCS,  CM_INV },
+#endif /* KUI */
+#else
+    { "local-character-set", XYTLCS,  CM_INV },
+#endif /* OS2 */
+#endif /* NOCSETS */
+    { "locking-shift", XYTSO,     0 },
+#ifdef OS2
+    { "margin-bell",   XYTMBEL,   0 },
+#endif /* OS2 */
+#ifdef OS2MOUSE
+    { "mouse",         XYTMOU,    CM_INV },
+#endif /* OS2MOUSE */
+    { "newline-mode",  XYTNL,     0 },
+#ifdef OS2
+    { "output-pacing", XYTPAC,    0 },
+#ifdef PCTERM
+    { "pcterm",        XYTPCTERM, 0 },
+#endif /* PCTERM */
+#endif /* OS2 */
+#ifdef OS2ORUNIX
+    { "print",         XYTPRN,    0 },
+#endif /* OS2ORUNIX */
+#ifndef NOCSETS
+#ifdef OS2
+    { "remote-character-set", XYTRCS,  0 },
+#else
+    { "remote-character-set", XYTRCS,  CM_INV },
+#endif /* OS2 */
+#endif /* NOCSETS */
+#ifdef OS2
+    { "roll-mode",       XYTROL, 0 },
+    { "s",               XYTUPD, CM_ABR|CM_INV },
+    { "sc",              XYTUPD, CM_ABR|CM_INV },
+    { "scr",             XYTUPD, CM_ABR|CM_INV },
+    { "scree",           XYTUPD, CM_ABR|CM_INV },
+    { "screen",          XYTUPD, CM_ABR|CM_INV },
+    { "screen-",         XYTUPD, CM_ABR|CM_INV },
+    { "screen-mode",     XYTSCNM,   0 },
+    { "screen-optimize", XYTOPTI,   0 },
+    { "screen-update",   XYTUPD,    0 },
+    { "scrollback",      XYSCRS,    0 },
+    { "send-data",         XYTSEND, 0 },
+    { "send-end-of-block", XYTSEOB, 0 },
+    { "sgr-colors",            XYTSGRC,  0 },
+    { "sni-ch.code",           XYTSNICC, 0 },
+    { "sni-firmware-versions", XYTSNIFV, 0 },
+    { "sni-language",          XYTVTLNG, 0 },
+    { "sni-pagemode",          XYTSNIPM, CM_INV },
+    { "sni-scrollmode",              XYTSNISM, CM_INV },
+    { "spacing-attribute-character", XYTSAC,   CM_INV },
+    { "statusline",                  XYTSTAT,  0 },
+    { "tra",                         XYTCTS,   CM_INV|CM_ABR },
+    { "transmit-timeout",            XYTCTS,   0 },
+#endif /* OS2 */
+
+#ifdef OS2ORUNIX
+    { "transparent-print", XYTPRN,   CM_INV },
+#endif /* OS2ORUNIX */
+
+#ifdef CK_TRIGGER
+    { "trigger",           XYTRIGGER,0 },
+#endif /* CK_TRIGGER */
+#ifdef OS2
+    { "type",              XYTTYP,   0 },
+#else
+    { "type",              XYTTYP,   CM_INV },
+#endif /* OS2 */
+
+#ifndef NOCSETS
+#ifdef UNICODE
+#ifdef CKOUNI
+    { "unicode",           XYTUNI,   CM_INV },
+#endif /* CKOUNI */
+#endif /* UNICODE */
+#endif /* NOCSETS */
+#ifdef OS2
+    { "unix-mode",         XYTUNX,   CM_INV },
+    { "url-highlight",     XYTURLHI, 0 },
+#ifdef NT
+    { "video-change",      XYTVCH,   0 },
+#endif /* NT */
+    { "vt-language",       XYTVTLNG, 0 },
+    { "vt-nrc-mode",       XYTVTNRC, 0 },
+#endif /* OS2 */
+    { "width",             XYTWID,   0 },
+#ifdef OS2
+    { "wrap",              XYTWRP,   0 },
+#endif /* OS2 */
+    { "", 0, 0 }
+};
+int ntrm = (sizeof(trmtab) / sizeof(struct keytab)) - 1;
+
+#ifdef OS2
+struct keytab termctrl[] = {    /* SET TERM CONTROLS */
+    { "7",      7, 0 },
+    { "8",      8, 0 }
+};
+int ntermctrl = (sizeof(termctrl) / sizeof(struct keytab));
+
+struct keytab curontab[] = {    /* SET TERM CURSOR */
+#ifdef KUI
+    { "noblink", 2, 0 },
+#else
+    { "noblink", 2, CM_INV },
+#endif /* KUI */
+    { "off",     0, 0 },
+    { "on",      1, 0 }
+};
+int ncuron = (sizeof(curontab) / sizeof(struct keytab));
+
+struct keytab rolltab[] = {   /* Set TERM Roll Options */
+    { "insert",    TTR_INSERT, 0      },
+    { "keystrokes",TTR_KEYS,   0      },
+    { "off",       TTR_OVER,   CM_INV },
+    { "on",        TTR_INSERT, CM_INV },
+    { "overwrite", TTR_OVER,   0      }
+};
+int nroll = (sizeof(rolltab) / sizeof(struct keytab));
+
+struct keytab rollkeytab[] = {         /* Set TERM ROLL KEYSTROKES */
+    { "ignore",            TTRK_IGN, 0 },
+    { "restore-and-send",  TTRK_RST, 0 },
+    { "send",              TTRK_SND, 0 }
+};
+int nrollkey = (sizeof(rollkeytab) / sizeof(struct keytab));
+
+#define TT_GR_ALL 4
+#define TT_GR_G0  0
+#define TT_GR_G1  1
+#define TT_GR_G2  2
+#define TT_GR_G3  3
+#define TT_GR_KBD 4
+struct keytab graphsettab[] = {  /* DEC VT Graphic Sets */
+    { "all",      TT_GR_ALL, 0 },
+    { "g0",       TT_GR_G0,  0 },
+    { "g1",       TT_GR_G1,  0 },
+    { "g2",       TT_GR_G2,  0 },
+    { "g3",       TT_GR_G3,  0 },
+    { "keyboard", TT_GR_KBD, 0 }
+};
+int ngraphset = (sizeof(graphsettab) / sizeof(struct keytab));
+#endif /* OS2 */
+
+struct keytab adltab[] = {              /* Autodownload Options */
+    { "ask",     TAD_ASK, 0 },
+    { "error",   TAD_ERR, 0 },
+#ifdef OS2
+    { "kermit",  TAD_K,   0 },
+#endif /* OS2 */
+    { "off",     TAD_OFF, 0 },
+    { "on",      TAD_ON,  0 },
+#ifdef OS2
+    { "zmodem",  TAD_Z,   0 },
+#endif /* OS2 */
+    { "", 0, 0 }
+};
+int nadltab = (sizeof(adltab) / sizeof(struct keytab)) - 1;
+
+struct keytab adlerrtab[] = {           /* Autodownload Error Options */
+    { "continue", 0, 0 },
+    { "go",       0, CM_INV },
+    { "stop",     1, 0 }
+};
+int nadlerrtab = (sizeof(adlerrtab) / sizeof(struct keytab));
+
+#ifdef OS2
+struct keytab adlxtab[] = {             /* Autodownload Options */
+    { "c0-conflicts",     TAD_X_C0,     0 },
+    { "detection-method", TAD_X_DETECT, 0 },
+    { "string",           TAD_X_STR,    0 }
+};
+int nadlxtab = (sizeof(adlxtab) / sizeof(struct keytab));
+
+struct keytab adldtab[] = {             /* Auto-dl Detection Methods */
+    { "packet",           ADL_PACK,     0 },
+    { "string",           ADL_STR,      0 }
+};
+int nadldtab = (sizeof(adldtab) / sizeof(struct keytab));
+
+struct keytab adlc0tab[] = {            /* Auto-dl Detection Methods */
+    { "ignored-by-emulator",    0,      0 },
+    { "processed-by-emulator",  1,      0 }
+};
+int nadlc0tab = (sizeof(adlc0tab) / sizeof(struct keytab));
+
+#ifndef NOCSETS
+struct keytab vtlangtab[] = {
+    { "belgian",        VTL_BELGIAN , 0 },
+    { "british",        VTL_BRITISH , 0 },
+    { "canadian",       VTL_CANADIAN, 0 },
+    { "czech",          VTL_CZECH   , 0 },
+    { "danish",         VTL_DANISH  , 0 },
+    { "dutch",          VTL_DUTCH   , 0 },
+    { "finnish",        VTL_FINNISH , 0 },
+    { "french",         VTL_FRENCH  , 0 },
+    { "french-canadian",VTL_FR_CAN  , 0 },
+    { "german",         VTL_GERMAN  , 0 },
+    { "greek",          VTL_GREEK   , 0 },
+    { "hebrew",         VTL_HEBREW  , 0 },
+    { "hungarian",      VTL_HUNGARIA, 0 },
+    { "italian",        VTL_ITALIAN , 0 },
+    { "latin-american", VTL_LATIN_AM, 0 },
+    { "north-american", VTL_NORTH_AM, 0 },
+    { "norwegian",      VTL_NORWEGIA, 0 },
+    { "polish",         VTL_POLISH  , 0 },
+    { "portugese",      VTL_PORTUGES, 0 },
+    { "romanian",       VTL_ROMANIAN, 0 },
+    { "russian",        VTL_RUSSIAN , 0 },
+    { "scs",            VTL_SCS     , CM_INV },
+    { "slovak",         VTL_SLOVAK  , 0 },
+    { "spanish",        VTL_SPANISH , 0 },
+    { "swedish",        VTL_SWEDISH , 0 },
+    { "swiss-french",   VTL_SW_FR   , 0 },
+    { "swiss-german",   VTL_SW_GR   , 0 },
+    { "turkish-f",      VTL_TURK_F  , CM_INV },
+    { "turkish-q",      VTL_TURK_Q  , CM_INV }
+};
+int nvtlangtab = (sizeof(vtlangtab) / sizeof(struct keytab));
+#endif /* NOCSETS */
+#endif /* OS2 */
+
+struct keytab crdtab[] = {              /* Carriage-return display */
+    { "crlf",        1, 0 },
+    { "normal",      0, 0 }
+};
+extern int tt_crd;                      /* Carriage-return display variable */
+
+#ifdef CK_APC
+extern int apcstatus, apcactive;
+static struct keytab apctab[] = {       /* Terminal APC parameters */
+    {  "no-input", APC_ON|APC_NOINP,0 },
+    { "off",       APC_OFF,  0 },
+    { "on",        APC_ON,   0 },
+    { "unchecked", APC_ON|APC_UNCH, 0 },
+    { "unchecked-no-input", APC_ON|APC_NOINP|APC_UNCH, 0 }
+};
+int napctab = (sizeof(apctab) / sizeof(struct keytab));
+#endif /* CK_APC */
+#endif /* NOLOCAL */
+
+extern int autodl, adl_err, adl_ask;
+
+struct keytab beltab[] = {              /* Terminal bell mode */
+#ifdef OS2
+    { "audible", XYB_AUD,  0 },
+    { "none",    XYB_NONE, 0 },
+#else
+    { "audible", XYB_AUD,  CM_INV },
+    { "none",    XYB_NONE, CM_INV },
+#endif /* OS2 */
+#ifdef OS2
+    { "off",     XYB_NONE, CM_INV },
+    { "on",      XYB_AUD,  CM_INV },
+#else
+    { "off",     XYB_NONE, 0 },
+    { "on",      XYB_AUD,  0 },
+#endif /* OS2 */
+#ifdef OS2
+    { "visible", XYB_VIS,  0 },
+#endif /* OS2 */
+    { "", 0, 0 }
+};
+int nbeltab = sizeof(beltab)/sizeof(struct keytab) - 1;
+
+int tt_unicode = 1;                     /* Use Unicode if possible */
+#ifdef CKTIDLE
+int tt_idlesnd_tmo = 0;                 /* Idle Send Timeout, disabled */
+char * tt_idlesnd_str = NULL;           /* Idle Send String, none */
+char * tt_idlestr = NULL;
+extern int tt_idleact, tt_idlelimit;
+#endif /* CKTIDLE */
+
+#ifdef OS2
+#ifndef NOLOCAL
+/*
+  OS/2 serial communication devices.
+*/
+struct keytab os2devtab[] = {
+    { "1",    1, CM_INV },                      /* Invisible synonyms, like */
+    { "2",    2, CM_INV },                      /* "set port 1" */
+    { "3",    3, CM_INV },
+    { "4",    4, CM_INV },
+    { "5",    5, CM_INV },
+    { "6",    6, CM_INV },
+    { "7",    7, CM_INV },
+    { "8",    8, CM_INV },
+    { "com1", 1, 0 },                   /* Real device names */
+    { "com2", 2, 0 },
+    { "com3", 3, 0 },
+    { "com4", 4, 0 },
+    { "com5", 5, 0 },
+    { "com6", 6, 0 },
+    { "com7", 7, 0 },
+    { "com8", 8, 0 },
+#ifdef OS2ONLY
+    { "slipcom1", 1, 0 },                       /* For use with SLIP driver */
+    { "slipcom2", 2, 0 },                       /* shared access */
+    { "slipcom3", 3, 0 },
+    { "slipcom4", 4, 0 },
+    { "slipcom5", 5, 0 },
+    { "slipcom6", 6, 0 },
+    { "slipcom7", 7, 0 },
+    { "slipcom8", 8, 0 },
+    { "pppcom1", 1, 0 },                        /* For use with PPP driver */
+    { "pppcom2", 2, 0 },                        /* shared access */
+    { "pppcom3", 3, 0 },
+    { "pppcom4", 4, 0 },
+    { "pppcom5", 5, 0 },
+    { "pppcom6", 6, 0 },
+    { "pppcom7", 7, 0 },
+    { "pppcom8", 8, 0 }
+#endif /* OS2ONLY */
+};
+int nos2dev = (sizeof(os2devtab) / sizeof(struct keytab)) - 1;
+
+#ifdef OS2ONLY
+struct keytab os2ppptab[] = {
+    { "0",    0, CM_INV },
+    { "1",    1, CM_INV },                      /* Invisible synonyms, like */
+    { "2",    2, CM_INV },                      /* "set port 1" */
+    { "3",    3, CM_INV },
+    { "4",    4, CM_INV },
+    { "5",    5, CM_INV },
+    { "6",    6, CM_INV },
+    { "7",    7, CM_INV },
+    { "8",    8, CM_INV },
+    { "9",    9, CM_INV },
+    { "ppp0", 0, 0 },
+    { "ppp1", 1, 0 },                   /* For use with PPP driver */
+    { "ppp2", 2, 0 },                   /* shared access */
+    { "ppp3", 3, 0 },
+    { "ppp4", 4, 0 },
+    { "ppp5", 5, 0 },
+    { "ppp6", 6, 0 },
+    { "ppp7", 7, 0 },
+    { "ppp8", 8, 0 },
+    { "ppp9", 9, 0 }
+};
+int nos2ppp = (sizeof(os2ppptab) / sizeof(struct keytab));
+#endif /* OS2ONLY */
+
+/*
+  Terminal parameters that can be set by SET commands.
+  Used by the ck?con.c terminal emulator code.
+  For now, only used for #ifdef OS2.  Should add these for Macintosh.
+*/
+int tt_arrow = TTK_NORM;                /* Arrow key mode: normal (cursor) */
+int tt_keypad = TTK_NORM;               /* Keypad mode: normal (numeric) */
+int tt_shift_keypad = 0;                /* Keypad Shift mode: Off */
+int tt_wrap = 1;                        /* Terminal wrap, 1 = On */
+int tt_type = TT_VT320;                 /* Terminal type, initially VT320 */
+int tt_type_mode = TT_VT320;            /* Terminal type set by host command */
+int tt_cursor = 0;                      /* Terminal cursor, 0 = Underline */
+int tt_cursor_usr = 0;                  /* Users Terminal cursor type */
+int tt_cursorena_usr = 1;               /* Users Terminal cursor enabled */
+int tt_cursor_blink = 1;                /* Terminal Cursor Blink */
+int tt_answer = 0;                      /* Terminal answerback (disabled) */
+int tt_scrsize[VNUM] = {512,512,512,1}; /* Terminal scrollback buffer size */
+int tt_roll[VNUM] = {1,1,1,1};          /* Terminal roll (on) */
+int tt_rkeys[VNUM] = {1,1,1,1};                /* Terminal roll keys (send) */
+int tt_pacing = 0;                      /* Terminal output-pacing (none) */
+int tt_ctstmo = 15;                     /* Terminal transmit-timeout */
+int tt_codepage = -1;                   /* Terminal code-page */
+int tt_update = 100;                    /* Terminal screen-update interval */
+int tt_updmode = TTU_FAST;              /* Terminal screen-update mode FAST */
+extern int updmode;
+#ifndef KUI
+int tt_status[VNUM] = {1,1,0,0};        /* Terminal status line displayed */
+int tt_status_usr[VNUM] = {1,1,0,0};
+#else  /* KUI */
+extern CKFLOAT floatval;
+CKFLOAT tt_linespacing[VNUM] = {1.0,1.0,1.0,1.0};
+#ifdef K95G
+int tt_status[VNUM] = {1,1,0,0};        /* Terminal status line displayed */
+int tt_status_usr[VNUM] = {1,1,0,0};
+#else /* K95G */
+int tt_status[VNUM] = {0,0,0,0};        /* Terminal status line displayed */
+int tt_status_usr[VNUM] = {0,0,0,0};
+#endif /* K95G */
+#endif /* KUI */
+int tt_senddata = 0;                    /* Let host read terminal data */
+extern int wy_blockend;                 /* Terminal Send Data EOB type */
+int tt_hidattr = 1;                     /* Attributes are hidden */
+
+extern unsigned char colornormal, colorselect,
+colorunderline, colorstatus, colorhelp, colorborder,
+colorgraphic, colordebug, colorreverse, coloritalic;
+
+extern int trueblink, trueunderline, truereverse, trueitalic, truedim;
+
+extern int bgi, fgi;
+extern int scrninitialized[];
+
+struct keytab audibletab[] = {          /* Terminal Bell Audible mode */
+    { "beep",          XYB_BEEP, 0 },   /* Values ORd with bell mode */
+    { "system-sounds", XYB_SYS,  0 }
+};
+int naudibletab = sizeof(audibletab)/sizeof(struct keytab);
+
+struct keytab akmtab[] = {              /* Arrow key mode */
+    { "application", TTK_APPL, 0 },
+    { "cursor",      TTK_NORM, 0 }
+};
+struct keytab kpmtab[] = {              /* Keypad mode */
+    { "application", TTK_APPL, 0 },
+    { "numeric",     TTK_NORM, 0 }
+};
+
+struct keytab ttcolmodetab[] = {
+    { "current-color", 0, 0 },
+    { "default-color", 1, 0 }
+};
+int ncolmode = sizeof(ttcolmodetab)/sizeof(struct keytab);
+
+#define TTCOLNOR  0
+#define TTCOLREV  1
+#define TTCOLUND  2
+#define TTCOLSTA  3
+#define TTCOLHLP  4
+#define TTCOLBOR  5
+#define TTCOLSEL  6
+#define TTCOLDEB  7
+#define TTCOLGRP  8
+#define TTCOLITA  9
+#define TTCOLRES  10
+#define TTCOLERA  11
+
+struct keytab ttycoltab[] = {                   /* Terminal Screen coloring */
+    { "border",             TTCOLBOR, 0 },      /* Screen border color */
+    { "debug-terminal",     TTCOLDEB, 0 },      /* Debug color */
+    { "erase",              TTCOLERA, 0 },      /* Erase mode */
+    { "graphic",            TTCOLGRP, 0 },      /* Graphic Color */
+    { "help-text",          TTCOLHLP, 0 },      /* Help screens */
+    { "italic",             TTCOLITA, 0 },      /* Italic Color */
+    { "normal",             TTCOLNOR, CM_INV }, /* Normal screen text */
+    { "reset-on-esc[0m",    TTCOLRES, 0 },      /* Reset on ESC [ 0 m */
+    { "reverse-video",      TTCOLREV, 0 },      /* Reverse video */
+    { "status-line",        TTCOLSTA, 0 },      /* Status line */
+    { "selection",          TTCOLSEL, 0 },      /* Selection color */
+    { "terminal-screen",    TTCOLNOR, 0 },      /* Better name than "normal" */
+    { "underlined-text",    TTCOLUND, 0 }       /* Underlined text */
+};
+int ncolors = (sizeof(ttycoltab) / sizeof(struct keytab));
+
+#define TTATTNOR  0
+#define TTATTBLI  1
+#define TTATTREV  2
+#define TTATTUND  3
+#define TTATTPRO  4
+#define TTATTBLD  5
+#define TTATTDIM  6
+#define TTATTINV  7
+#define TTATTITA  8
+#define TTATTDONE 9
+
+struct keytab ttyattrtab[] = {
+    { "blink",     TTATTBLI, 0 },
+    { "dim",       TTATTDIM, 0 },
+    { "italic",    TTATTITA, 0 },
+    { "protected", TTATTPRO, 0 },
+    { "reverse",   TTATTREV, 0 },
+    { "underline", TTATTUND, 0 }
+};
+int nattrib = (sizeof(ttyattrtab) / sizeof(struct keytab));
+
+struct keytab ttyprotab[] = {
+    { "blink",       TTATTBLI,  0 },
+    { "bold",        TTATTBLD,  0 },
+    { "dim",         TTATTDIM,  0 },
+    { "done",        TTATTDONE, CM_INV },
+    { "invisible",   TTATTINV,  0 },
+    { "italic",      TTATTITA,  0 },
+    { "normal",      TTATTNOR,  0 },
+    { "reverse",     TTATTREV,  0 },
+    { "underlined",  TTATTUND,  0 }
+
+};
+int nprotect = (sizeof(ttyprotab) / sizeof(struct keytab));
+
+struct keytab ttyseobtab[] = {
+    { "crlf_etx",  1, 0 },
+    { "us_cr",     0, 0 }
+};
+
+struct keytab ttyclrtab[] = {           /* Colors */
+    { "black",         0, 0      },
+    { "blue",          1, 0      },
+    { "brown",         6, 0      },
+    { "cyan",          3, 0      },
+    { "darkgray",      8, CM_INV },
+    { "dgray",         8, 0      },
+    { "green",         2, 0      },
+    { "lblue",         9, CM_INV },
+    { "lcyan",        11, CM_INV },
+    { "lgray",         7, CM_INV },
+    { "lgreen",       10, CM_INV },
+    { "lightblue",     9, 0      },
+    { "lightcyan",    11, 0      },
+    { "lightgray",     7, 0      },
+    { "lightgreen",   10, 0      },
+    { "lightmagenta", 13, 0      },
+    { "lightred",     12, 0      },
+    { "lmagenta",     13, CM_INV },
+    { "lred",         12, CM_INV },
+    { "magenta",       5, 0      },
+    { "red",           4, 0      },
+    { "white",        15, 0      },
+    { "yellow",       14, 0      }
+};
+int nclrs = (sizeof (ttyclrtab) / sizeof (struct keytab));
+
+struct keytab ttycurtab[] = {
+    { "full",        TTC_BLOCK, 0 },
+    { "half",        TTC_HALF,  0 },
+    { "underline",   TTC_ULINE, 0 }
+};
+int ncursors = 3;
+
+struct keytab ttyptab[] = {
+    { "aaa",      TT_AAA,     CM_INV },     /* AnnArbor */
+    { "adm3a",    TT_ADM3A,   0 },          /* LSI ADM-3A */
+    { "adm5",     TT_ADM5,    0 },          /* LSI ADM-5 */
+    { "aixterm",  TT_AIXTERM, 0 },          /* IBM AIXterm */
+    { "annarbor", TT_AAA,     0 },          /* AnnArbor */
+    { "ansi-bbs", TT_ANSI,    0 },          /* ANSI.SYS (BBS) */
+    { "at386",    TT_AT386,   0 },          /* Unixware ANSI */
+    { "avatar/0+",TT_ANSI,    0 },          /* AVATAR/0+ */
+    { "ba80",     TT_BA80,    0 },          /* Nixdorf BA80 */
+    { "be",       TT_BEOS,    CM_INV|CM_ABR },
+    { "beos-ansi",TT_BEOS,    CM_INV },     /* BeOS ANSI */
+    { "beterm",   TT_BEOS,    0 },          /* BeOS Terminal (as of PR2 ) */
+    { "d200",     TT_DG200,   CM_INV|CM_ABR }, /* Data General DASHER 200 */
+    { "d210",     TT_DG210,   CM_INV|CM_ABR }, /* Data General DASHER 210 */
+    { "d217",     TT_DG217,   CM_INV|CM_ABR }, /* Data General DASHER 217 */
+    { "dg200",    TT_DG200,   0 },          /* Data General DASHER 200 */
+    { "dg210",    TT_DG210,   0 },          /* Data General DASHER 210 */
+    { "dg217",    TT_DG217,   0 },          /* Data General DASHER 217 */
+    { "h1500",    TT_HZL1500, CM_INV },     /* Hazeltine 1500 */
+    { "h19",      TT_H19,     CM_INV },     /* Heath-19 */
+    { "heath19",  TT_H19,     0 },          /* Heath-19 */
+    { "hft",      TT_HFT,     0 },          /* IBM High Function Terminal */
+    { "hp2621a",  TT_HP2621,  0 },          /* HP 2621A */
+    { "hpterm",   TT_HPTERM,  0 },          /* HP TERM */
+    { "hz1500",   TT_HZL1500, 0 },          /* Hazeltine 1500 */
+    { "ibm3151",  TT_IBM31,   0 },          /* IBM 3101-xx,3161 */
+    { "linux",    TT_LINUX,   0 },          /* Linux */
+    { "qansi",    TT_QANSI,   0 },          /* QNX ANSI */
+    { "qnx",      TT_QNX,     0 },          /* QNX Console */
+    { "scoansi",  TT_SCOANSI, 0 },          /* SCO ANSI */
+    { "sni-97801",TT_97801,   0 },          /* SNI 97801 */
+    { "sun",      TT_SUN,     0 },          /* SUN Console */
+/*
+  The idea of NONE is to let the console driver handle the escape sequences,
+  which, in theory at least, would give not only ANSI emulation, but also any
+  other kind of emulation that might be provided by alternative console
+  drivers, if any existed.
+
+  For this to work, ckocon.c would need to be modified to make higher-level
+  calls, like VioWrtTTY(), DosWrite(), or (simply) write(), rather than
+  VioWrt*Cell() and similar, and it would also have to give up its rollback
+  feature, and its status line and help screens would also have to be
+  forgotten or else done in an ANSI way.
+
+  As matters stand, we already have perfectly good ANSI emulation built in,
+  and there are no alternative console drivers available, so there is no point
+  in having a terminal type of NONE, so it is commented out.  However, should
+  you uncomment it, it will work like a "glass tty" -- no escape sequence
+  interpretation at all; somewhat similar to debug mode, except without the
+  debugging (no highlighting of control chars or escape sequences); help
+  screens, status line, and rollback will still work.
+*/
+#ifdef OS2PM
+#ifdef COMMENT
+    { "tek4014", TT_TEK40,  0 },
+#endif /* COMMENT */
+#endif /* OS2PM */
+    { "tty",     TT_NONE,   0 },
+    { "tvi910+", TT_TVI910, 0 },
+    { "tvi925",  TT_TVI925, 0 },
+    { "tvi950",  TT_TVI950, 0 },
+    { "vc404",   TT_VC4404, 0 },
+    { "vc4404",  TT_VC4404, CM_INV },
+    { "vip7809", TT_VIP7809,0 },
+    { "vt100",   TT_VT100,  0 },
+    { "vt102",   TT_VT102,  0 },
+    { "vt220",   TT_VT220,  0 },
+    { "vt220pc", TT_VT220PC,0 },
+    { "vt320",   TT_VT320,  0 },
+    { "vt320pc", TT_VT320PC,0 },
+    { "vt52",    TT_VT52,   0 },
+#ifdef NT
+    { "vtnt",    TT_VTNT,   0 },
+#else /* NT */
+    { "vtnt",    TT_VTNT,  CM_INV },
+#endif /* NT */
+    { "wy160",   TT_WY160,  0 },
+    { "wy30",    TT_WY30,   0 },
+    { "wy370",   TT_WY370,  0 },
+    { "wy50",    TT_WY50,   0 },
+    { "wy60",    TT_WY60,   0 },
+    { "wyse30",  TT_WY30,   CM_INV },
+    { "wyse370", TT_WY370,  CM_INV },
+    { "wyse50",  TT_WY50,   CM_INV },
+    { "wyse60",  TT_WY60,   CM_INV }
+};
+int nttyp = (sizeof(ttyptab) / sizeof(struct keytab));
+
+struct keytab ttkeytab[] = {
+    { "aaa",       TT_AAA,        CM_INV },        /* AnnArbor */
+    { "adm3a",     TT_ADM3A,      0 },             /* LSI ADM-3A */
+    { "adm5",      TT_ADM5,       0 },             /* LSI ADM-5 */
+    { "aixterm",   TT_AIXTERM,    0 },             /* IBM AIXterm */
+    { "annarbor",  TT_AAA,        0 },             /* AnnArbor */
+    { "ansi-bbs",  TT_ANSI,       0 },             /* ANSI.SYS (BBS) */
+    { "at386",     TT_AT386,      0 },             /* Unixware ANSI */
+    { "avatar/0+", TT_ANSI,       0 },             /* AVATAR/0+ */
+    { "ba80",      TT_BA80,       0 },             /* Nixdorf BA80 */
+    { "be",        TT_BEOS,       CM_INV|CM_ABR },
+    { "beos-ansi", TT_BEOS,       CM_INV },        /* BeOS ANSI */
+    { "beterm",    TT_BEOS,       0 },             /* BeOS Terminal (DR2) */
+    { "d200",      TT_DG200,      CM_INV|CM_ABR }, /* DG DASHER 200 */
+    { "d210",      TT_DG210,      CM_INV|CM_ABR }, /* DG DASHER 210 */
+    { "d217",      TT_DG217,      CM_INV|CM_ABR }, /* DG DASHER 217 */
+    { "dg200",     TT_DG200,      0 },             /* DG DASHER 200 */
+    { "dg210",     TT_DG210,      0 },             /* DG DASHER 210 */
+    { "dg217",     TT_DG217,      0 },             /* DG DASHER 217 */
+    { "emacs",     TT_KBM_EMACS,  0 },             /* Emacs mode */
+    { "h19",       TT_H19,        CM_INV },        /* Heath-19 */
+    { "heath19",   TT_H19,        0 },             /* Heath-19 */
+    { "hebrew",    TT_KBM_HEBREW, 0 },             /* Hebrew mode */
+    { "hft",       TT_HFT,        0 },             /* IBM High Function Term */
+    { "hp2621a",   TT_HP2621,     0 },             /* HP 2621A */
+    { "hpterm",    TT_HPTERM,     0 },             /* HP TERM */
+    { "hz1500",    TT_HZL1500,    0 },             /* Hazeltine 1500 */
+    { "ibm3151",   TT_IBM31,      0 },             /* IBM 3101-xx,3161 */
+    { "linux",     TT_LINUX,      0 },             /* Linux */
+    { "qansi",     TT_QANSI,      0 },             /* QNX ANSI */
+    { "qnx",       TT_QNX,        0 },             /* QNX */
+    { "russian",   TT_KBM_RUSSIAN,0 },             /* Russian mode */
+    { "scoansi",   TT_SCOANSI,    0 },             /* SCO ANSI */
+    { "sni-97801", TT_97801,      0 },             /* SNI 97801 */
+    { "sun",       TT_SUN,        0 },             /* SUN Console */
+#ifdef OS2PM
+#ifdef COMMENT
+    { "tek4014",   TT_TEK40,      0 },
+#endif /* COMMENT */
+#endif /* OS2PM */
+    { "tty",       TT_NONE,       0 },
+    { "tvi910+",   TT_TVI910,     0 },
+    { "tvi925",    TT_TVI925,     0 },
+    { "tvi950",    TT_TVI950,     0 },
+    { "vc404",     TT_VC4404,     0 },
+    { "vc4404",    TT_VC4404,     CM_INV },
+    { "vip7809",   TT_VIP7809,    0 },
+    { "vt100",     TT_VT100,      0 },
+    { "vt102",     TT_VT102,      0 },
+    { "vt220",     TT_VT220,      0 },
+    { "vt220pc",   TT_VT220PC,    0 },
+    { "vt320",     TT_VT320,      0 },
+    { "vt320pc",   TT_VT320PC,    0 },
+    { "vt52",      TT_VT52,       0 },
+    { "vtnt",      TT_VTNT,       CM_INV },
+    { "wp",        TT_KBM_WP,     0 },             /* Word Perfect mode */
+    { "wy160",     TT_WY160,      0 },
+    { "wy30",      TT_WY30,       0 },
+    { "wy370",     TT_WY370,      0 },
+    { "wy50",      TT_WY50,       0 },
+    { "wy60",      TT_WY60,       0 },
+    { "wyse30",    TT_WY30,       CM_INV },
+    { "wyse370",   TT_WY370,      CM_INV },
+    { "wyse50",    TT_WY50,       CM_INV },
+    { "wyse60",    TT_WY60,       CM_INV }
+};
+int nttkey = (sizeof(ttkeytab) / sizeof(struct keytab));
+
+#ifndef NOSETKEY
+struct keytab kbmodtab[] = {
+    { "emacs",   KBM_EM, 0      },
+    { "english", KBM_EN, CM_INV },
+    { "hebrew",  KBM_HE, 0      },
+    { "normal",  KBM_EN, 0      },
+    { "none",    KBM_EN, CM_INV },
+    { "russian", KBM_RU, 0      },
+    { "wp",      KBM_WP, 0      }
+};
+int nkbmodtab = (sizeof(kbmodtab) / sizeof(struct keytab));
+#endif /* NOSETKEY */
+#endif /* NOLOCAL */
+
+int tt_inpacing = 0;                    /* input-pacing (none) */
+
+struct keytab prtytab[] = { /* OS/2 Priority Levels */
+    { "foreground-server", XYP_SRV, 0       },
+    { "idle",              XYP_IDLE, CM_INV },
+    { "regular",           XYP_REG, 0       },
+    { "time-critical",     XYP_RTP, 0       }
+};
+int nprty = (sizeof(prtytab) / sizeof(struct keytab));
+#endif /* OS2 */
+
+#ifdef NT
+struct keytab win95tab[] = { /* Win95 work-arounds */
+    { "8.3-filenames",         XYW8_3,    0 },
+    { "alt-gr",                XYWAGR,    0 },
+    { "horizontal-scan-line-substitutions", XYWHSL, 0 },
+    { "keyboard-translation",  XYWKEY,    0 },
+    { "lucida-substitutions",  XYWLUC,    0 },
+    { "overlapped-io",         XYWOIO,    0 },
+    { "popups",                XYWPOPUP,  0 },
+    { "select-bug",            XYWSELECT, 0 }
+};
+int nwin95 = (sizeof(win95tab) / sizeof(struct keytab));
+#endif /* NT */
+
+#ifdef OS2MOUSE
+extern int wideresult;
+int tt_mouse = 1;                       /* Terminal mouse on/off */
+
+struct keytab mousetab[] = {            /* Mouse items */
+    { "activate", XYM_ON,     0 },
+    { "button",   XYM_BUTTON, 0 },
+    { "clear",    XYM_CLEAR,  0 },
+    { "debug",    XYM_DEBUG,  0 }
+};
+int nmtab = (sizeof(mousetab)/sizeof(struct keytab));
+
+struct keytab mousebuttontab[] = {      /* event button */
+    { "1",             XYM_B1, 0 },
+    { "2",             XYM_B2, 0 },
+    { "3",             XYM_B3, 0 },
+    { "one",           XYM_B1, CM_INV },
+    { "three",         XYM_B3, CM_INV },
+    { "two",           XYM_B2, CM_INV }
+};
+int nmbtab = (sizeof(mousebuttontab) / sizeof(struct keytab));
+
+struct keytab mousemodtab[] = {         /* event button key modifier */
+    { "alt",              XYM_ALT,   0 },
+    { "alt-shift",        XYM_SHIFT|XYM_ALT, 0 },
+    { "ctrl",             XYM_CTRL,  0 },
+    { "ctrl-alt",         XYM_CTRL|XYM_ALT, 0 },
+    { "ctrl-alt-shift",   XYM_CTRL|XYM_SHIFT|XYM_ALT, 0 },
+    { "ctrl-shift",       XYM_CTRL|XYM_SHIFT, 0 },
+    { "none",             0, 0 },
+    { "shift",            XYM_SHIFT, 0 }
+};
+int nmmtab = (sizeof(mousemodtab) / sizeof(struct keytab));
+
+struct keytab mclicktab[] = {           /* event button click modifier */
+    { "click",        XYM_C1,   0 },
+    { "drag",         XYM_DRAG, 0 },
+    { "double-click", XYM_C2,   0 }
+};
+int nmctab = (sizeof(mclicktab) / sizeof(struct keytab));
+
+#ifndef NOKVERBS
+extern int nkverbs;
+extern struct keytab kverbs[];
+#endif /* NOKVERBS */
+#endif /* OS2MOUSE */
+
+/* #ifdef VMS */
+struct keytab fbtab[] = {               /* Binary record types for VMS */
+    { "fixed",     XYFT_B, 0 },         /* Fixed is normal for binary */
+    { "undefined", XYFT_U, 0 }          /* Undefined if they ask for it */
+};
+int nfbtyp = (sizeof(fbtab) / sizeof(struct keytab));
+/* #endif */
+
+#ifdef VMS
+struct keytab lbltab[] = {              /* Labeled File info */
+    { "acl",         LBL_ACL, 0 },
+    { "backup-date", LBL_BCK, 0 },
+    { "name",        LBL_NAM, 0 },
+    { "owner",       LBL_OWN, 0 },
+    { "path",        LBL_PTH, 0 }
+};
+int nlblp = (sizeof(lbltab) / sizeof(struct keytab));
+#else
+#ifdef OS2
+struct keytab lbltab[] = {              /* Labeled File info */
+    { "archive",   LBL_ARC, 0 },
+    { "extended",  LBL_EXT, 0 },
+    { "hidden",    LBL_HID, 0 },
+    { "read-only", LBL_RO,  0 },
+    { "system",    LBL_SYS, 0 }
+};
+int nlblp = (sizeof(lbltab) / sizeof(struct keytab));
+#endif /* OS2 */
+#endif /* VMS */
+
+#ifdef CK_CURSES
+#ifdef CK_PCT_BAR
+static struct keytab fdftab[] = {       /* SET FILE DISPLAY FULL options */
+    { "thermometer",    1, 0, },
+    { "no-thermometer", 0, 0  }
+};
+extern int thermometer;
+#endif /* CK_PCT_BAR */
+#endif /* CK_CURSES */
+
+static struct keytab fdtab[] = {        /* SET FILE DISPLAY options */
+#ifdef MAC                              /* Macintosh */
+    { "fullscreen", XYFD_R,      0 },   /* Full-screen but not curses */
+    { "none",       XYFD_N,      0 },
+    { "off",        XYFD_N, CM_INV },
+    { "on",         XYFD_R, CM_INV },
+    { "quiet",      XYFD_N, CM_INV },
+#else                                   /* Not Mac */
+    { "brief", XYFD_B, 0 },             /* Brief */
+    { "crt", XYFD_S, 0 },               /* CRT display */
+#ifdef CK_CURSES
+#ifdef COMMENT
+    { "curses",     XYFD_C,  CM_INV },  /* Full-screen, curses */
+#endif /* COMMENT */
+    { "fullscreen", XYFD_C,  0 },       /* Full-screen, whatever the method */
+#endif /* CK_CURSES */
+#ifdef KUI
+    { "gui",    XYFD_G, 0 },            /* GUI */
+#endif /* KUI */        
+    { "none",   XYFD_N, 0      },       /* No display */
+    { "off",    XYFD_N, CM_INV },       /* Ditto */
+    { "on",     XYFD_R, CM_INV },       /* On = Serial */
+    { "quiet",  XYFD_N, CM_INV },       /* No display */
+    { "serial", XYFD_R, 0      },       /* Serial */
+#endif /* MAC */
+    { "", 0, 0 }
+};
+int nfdtab = (sizeof(fdtab) / sizeof(struct keytab)) - 1;
+
+struct keytab rsrtab[] = {              /* For REMOTE SET RECEIVE */
+    { "packet-length", XYLEN,  0 },
+    { "timeout",       XYTIMO, 0 }
+};
+int nrsrtab = (sizeof(rsrtab) / sizeof(struct keytab));
+
+/* Send/Receive Parameters */
+
+struct keytab srtab[] = {
+    { "backup", XYBUP, 0 },
+#ifndef NOCSETS
+    { "character-set-selection", XYCSET, 0 },
+#endif /* NOCSETS */
+    { "control-prefix", XYQCTL, 0 },
+#ifdef CKXXCHAR
+    { "double-character", XYDBL, 0 },
+#endif /* CKXXCHAR */
+    { "end-of-packet", XYEOL, 0 },
+#ifdef PIPESEND
+    { "filter", XYFLTR, 0 },
+#endif /* PIPESEND */
+#ifdef CKXXCHAR
+    { "ignore-character", XYIGN, 0 },
+#endif /* CKXXCHAR */
+    { "i-packets", 993, 0 },
+    { "move-to", XYMOVE, 0 },
+    { "negotiation-string-max-length", XYINIL, CM_INV },
+    { "packet-length", XYLEN, 0 },
+    { "pad-character", XYPADC, 0 },
+    { "padding", XYNPAD, 0 },
+    { "pathnames", XYFPATH, 0 },
+    { "pause", XYPAUS, 0 },
+#ifdef CK_PERMS
+    { "permissions", 994, 0},          /* 206 */
+#endif /* CK_PERMS */
+    { "quote", XYQCTL, CM_INV },        /* = CONTROL-PREFIX */
+    { "rename-to", XYRENAME, 0 },
+    { "start-of-packet", XYMARK, 0 },
+    { "timeout", XYTIMO, 0 },
+#ifdef VMS
+    { "version-numbers", 887, 0 },      /* VMS version numbers */
+#endif /* VMS */
+    { "", 0, 0 }
+};
+int nsrtab = (sizeof(srtab) / sizeof(struct keytab)) - 1;
+
+#ifdef UNICODE
+#define UCS_BOM 1
+#define UCS_BYT 2
+static struct keytab ucstab[] = {
+    { "bom",        UCS_BOM, 0 },
+    { "byte-order", UCS_BYT, 0 },
+    { "", 0, 0 }
+};
+int nucstab = (sizeof(ucstab) / sizeof(struct keytab)) - 1;
+
+static struct keytab botab[] = {
+    { "big-endian",    0, 0 },
+    { "little-endian", 1, 0 }
+};
+static int nbotab = 2;
+#endif /* UNICODE */
+
+/* REMOTE SET */
+
+struct keytab rmstab[] = {
+    { "attributes",  XYATTR, 0      },
+    { "block-check", XYCHKT, 0      },
+    { "file",        XYFILE, 0      },
+    { "incomplete",  XYIFD,  CM_INV },  /* = REMOTE SET FILE INCOMPLETE */
+    { "match",       XYMATCH,0      },
+    { "receive",     XYRECV, 0      },
+    { "retry",       XYRETR, 0      },
+    { "server",      XYSERV, 0      },
+    { "transfer",    XYXFER, 0      },
+    { "window",      XYWIND, 0      },
+    { "xfer",        XYXFER, CM_INV }
+};
+int nrms = (sizeof(rmstab) / sizeof(struct keytab));
+
+struct keytab attrtab[] = {
+#ifdef STRATUS
+    { "account",       AT_ACCT, 0 },
+#endif /* STRATUS */
+    { "all",           AT_XALL, 0 },
+#ifdef COMMENT
+    { "blocksize",     AT_BLKS, 0 },    /* (not used) */
+#endif /* COMMENT */
+#ifndef NOCSETS
+    { "character-set", AT_ENCO, 0 },
+#endif /* NOCSETS */
+#ifdef STRATUS
+    { "creator",       AT_CREA, 0 },
+#endif /* STRATUS */
+    { "date",          AT_DATE, 0 },
+    { "disposition",   AT_DISP, 0 },
+    { "encoding",      AT_ENCO, CM_INV },
+    { "format",        AT_RECF, CM_INV },
+    { "length",        AT_LENK, 0 },
+    { "off",           AT_ALLN, 0 },
+    { "on",            AT_ALLY, 0 },
+#ifdef COMMENT
+    { "os-specific",   AT_SYSP, 0 },    /* (not used by UNIX or VMS) */
+#endif /* COMMENT */
+#ifdef CK_PERMS
+    { "protection",    AT_LPRO, 0 },
+    { "permissions",   AT_LPRO, CM_INV },
+#endif /* CK_PERMS */
+    { "record-format", AT_RECF, 0 },
+    { "system-id",     AT_SYSI, 0 },
+    { "type",          AT_FTYP, 0 }
+};
+int natr = (sizeof(attrtab) / sizeof(struct keytab)); /* how many attributes */
+
+#ifdef CKTIDLE
+struct keytab idlacts[] = {
+    { "exit",       IDLE_EXIT, 0 },
+    { "hangup",     IDLE_HANG, 0 },
+    { "output",     IDLE_OUT,  0 },
+    { "return",     IDLE_RET,  0 },
+#ifdef TNCODE
+    { "telnet-nop", IDLE_TNOP, 0 },
+    { "telnet-ayt", IDLE_TAYT, 0 },
+#endif /* TNCODE */
+    { "", 0, 0 }
+};
+int nidlacts = (sizeof(idlacts) / sizeof(struct keytab)) - 1;
+#endif /* CKTIDLE */
+
+#ifndef NOSPL
+extern int indef, inecho, insilence, inbufsize, inautodl, inintr;
+#ifdef CKFLOAT
+extern CKFLOAT inscale;
+#endif /* CKFLOAT */
+extern char * inpbuf, * inpbp;
+#ifdef OS2
+extern int interm;
+#endif /* OS2 */
+struct keytab inptab[] = {              /* SET INPUT parameters */
+#ifdef CK_AUTODL
+    { "autodownload",    IN_ADL, 0 },
+#endif /* CK_AUTODL */
+    { "buffer-length",   IN_BUF, 0 },
+    { "cancellation",    IN_CAN, 0 },
+    { "case",            IN_CAS, 0 },
+    { "default-timeout", IN_DEF, CM_INV }, /* There is no default timeout */
+    { "echo",            IN_ECH, 0 },
+#ifdef OS2
+    { "pacing",          IN_PAC, CM_INV },
+#endif /* OS2 */
+    { "scale-factor",    IN_SCA, 0 },
+    { "silence",         IN_SIL, 0 },
+#ifdef OS2
+    { "terminal",        IN_TRM, 0 },
+#endif /* OS2 */
+    { "timeout-action",  IN_TIM, 0 }
+};
+int ninp = (sizeof(inptab) / sizeof(struct keytab));
+
+struct keytab intimt[] = {              /* SET INPUT TIMEOUT parameters */
+    { "proceed", 0, 0 },                /* 0 = proceed */
+    { "quit",    1, 0 }                 /* 1 = quit */
+};
+
+struct keytab incast[] = {              /* SET INPUT CASE parameters */
+    { "ignore",  0, 0 },                /* 0 = ignore */
+    { "observe", 1, 0 }                 /* 1 = observe */
+};
+#endif /* NOSPL */
+
+struct keytab nabltab[] = {             /* For any command that needs */
+    { "disabled", 0, 0 },
+    { "enabled",  1, 0 },
+    { "off",      0, CM_INV },          /* these keywords... */
+    { "on",       1, CM_INV }
+};
+int nnabltab = sizeof(nabltab) / sizeof(struct keytab);
+
+#ifdef OS2
+struct keytab tvctab[] = {              /* SET TERM VIDEO-CHANGE */
+    { "disabled",     TVC_DIS, 0 },
+    { "enabled",      TVC_ENA, 0 },
+#ifdef NT
+    { "win95-safe",   TVC_W95, 0 },
+#endif /* NT */
+    { "", 0, 0 }
+};
+int ntvctab = (sizeof(tvctab) / sizeof(struct keytab)) - 1;
+
+struct keytab msktab[] = { /* SET MS-DOS KERMIT compatibilities */
+#ifdef COMMENT
+    { "color",    MSK_COLOR,  0 },
+#endif /* COMMENT */
+    { "keycodes", MSK_KEYS,   0 }
+};
+int nmsk = (sizeof(msktab) / sizeof(struct keytab));
+
+struct keytab scrnupd[] = {             /* SET TERMINAL SCREEN-UPDATE */
+    { "fast",   TTU_FAST,   0 },
+    { "smooth", TTU_SMOOTH, 0 }
+};
+int nscrnupd = (sizeof(scrnupd) / sizeof(struct keytab));
+
+#ifdef PCFONTS
+/* This definition of the term_font[] table is only for     */
+/* the OS/2 Full Screen Session and is not used on Windows */
+struct keytab term_font[] = {           /* SET TERMINAL FONT */
+#ifdef COMMENT
+    { "cp111", TTF_111, 0 },
+    { "cp112", TTF_112, 0 },
+    { "cp113", TTF_113, 0 },
+#endif /* COMMENT */
+    { "cp437", TTF_437, 0 },
+    { "cp850", TTF_850, 0 },
+#ifdef COMMENT
+    { "cp851", TTF_851, 0 },
+#endif /* COMMENT */
+    { "cp852", TTF_852, 0 },
+#ifdef COMMENT
+    { "cp853", TTF_853, 0 },
+    { "cp860", TTF_860, 0 },
+    { "cp861", TTF_861, 0 },
+#endif /* COMMENT */
+    { "cp862", TTF_862, 0 },
+#ifdef COMMENT
+    { "cp863", TTF_863, 0 },
+    { "cp864", TTF_864, 0 },
+    { "cp865", TTF_865, 0 },
+#endif /* COMMENT */
+    { "cp866", TTF_866, 0 },
+#ifdef COMMENT
+    { "cp880", TTF_880, 0 },
+    { "cp881", TTF_881, 0 },
+    { "cp882", TTF_882, 0 },
+    { "cp883", TTF_883, 0 },
+    { "cp884", TTF_884, 0 },
+    { "cp885", TTF_885, 0 },
+#endif /* COMMENT */
+    { "default",TTF_ROM,0 }
+};
+int ntermfont = (sizeof(term_font) / sizeof(struct keytab));
+int tt_font = TTF_ROM;                  /* Terminal screen font */
+#else /* PCFONTS */
+#ifdef NT
+#ifdef KUI
+struct keytab * term_font = NULL;
+struct keytab * _term_font = NULL;
+char * tt_facename = NULL;
+int ntermfont = 0;
+int tt_font = 0;
+int tt_font_size = 0;
+#endif /* KUI */
+#endif /* NT */
+#endif /* PCFONTS */
+
+struct keytab anbktab[] = {             /* For any command that needs */
+    { "message", 2, 0 },                /* these keywords... */
+    { "off",     0, 0 },
+    { "on",      1, 0 },
+    { "unsafe-messag0", 99, CM_INV },
+    { "unsafe-message", 3,  CM_INV }
+};
+int nansbk = (sizeof(anbktab) / sizeof(struct keytab));
+
+int win95_popup = 1;
+#ifdef NT
+#ifdef KUI
+int win95lucida = 0;
+int win95hsl = 1;
+#else /* KUI */
+int win95lucida = 1;
+int win95hsl = 1;
+#endif /* KUI */
+#else /* NT */
+int win95lucida = 0;
+int win95hsl = 1;
+#endif /* NT */
+#ifdef NT
+int win95altgr  = 0;
+extern int win95selectbug;
+extern int win95_8_3;
+
+#ifdef COMMENT
+extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR);
+extern struct keytab tcstab[];
+extern int ntcs;
+#endif /* COMMENT */
+extern int maxow, maxow_usr; owwait;    /* Overlapped I/O variables */
+#endif /* NT */
+#endif /* OS2 */
+
+
+/* The following routines broken out of doprm() to give compilers a break. */
+
+/*  S E T O N  --  Parse on/off (default on), set parameter to result  */
+
+int
+seton(prm) int *prm; {
+    int x, y;
+    if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y);
+    if ((x = cmcfm()) < 0) return(x);
+    *prm = y;
+    return(1);
+}
+
+/*  S E T O N A U T O --  Parse on/off/auto (default auto) & set result */
+
+struct keytab onoffaut[] = {
+    { "auto", SET_AUTO, 0 },            /* 2 */
+    { "off",  SET_OFF,  0 },            /* 0 */
+    { "on",   SET_ON,   0 }             /* 1 */
+};
+
+int
+setonaut(prm) int *prm; {
+    int x, y;
+    if ((y = cmkey(onoffaut,3,"","auto",xxstring)) < 0) return(y);
+    if ((x = cmcfm()) < 0) return(x);
+    *prm = y;
+    return(1);
+}
+
+/*  S E T N U M  --  Set parameter to result of cmnum() parse.  */
+/*
+ Call with pointer to integer variable to be set,
+   x = number from cnum parse, y = return code from cmnum,
+   max = maximum value to accept, -1 if no maximum.
+ Returns -9 on failure, after printing a message, or 1 on success.
+*/
+int
+setnum(prm,x,y,max) int x, y, *prm, max; {
+    debug(F101,"setnum","",y);
+    if (y == -3) {
+        printf("\n?Value required\n");
+        return(-9);
+    }
+    if (y == -2) {
+        printf("%s?Not a number: %s\n",cmflgs == 1 ? "" : "\n", atxbuf);
+        return(-9);
+    }
+    if (y < 0) return(y);
+    if (max > -1 && x > max) {
+        printf("?Sorry, %d is the maximum\n",max);
+        return(-9);
+    }
+    if ((y = cmcfm()) < 0) return(y);
+    *prm = x;
+    return(1);
+}
+
+/*  S E T C C  --  Set parameter var to an ASCII control character value.  */
+/*
+  Parses a number, or a literal control character, or a caret (^) followed
+  by an ASCII character whose value is 63-95 or 97-122, then gets confirmation,
+  then sets the parameter to the code value of the character given.  If there
+  are any parse errors, they are returned, otherwise on success 1 is returned.
+*/
+int
+setcc(dflt,var) char *dflt; int *var; {
+    int x, y;
+    unsigned int c;
+    char *hlpmsg = "Control character,\n\
+ numeric ASCII value,\n\
+ or in ^X notation,\n\
+ or preceded by a backslash and entered literally";
+
+    /* This is a hack to turn off complaints from expression evaluator. */
+    x_ifnum = 1;
+    y = cmnum(hlpmsg, dflt, 10, &x, xxstring); /* Parse a number */
+    x_ifnum = 0;                               /* Allow complaints again */
+    if (y < 0) {                        /* Parse failed */
+        if (y != -2)                    /* Reparse needed or somesuch */
+          return(y);                    /* Pass failure back up the chain */
+    }
+    /* Real control character or literal 8-bit character... */
+
+    for (c = strlen(atmbuf) - 1; c > 0; c--) /* Trim */
+      if (atmbuf[c] == SP) atmbuf[c] = NUL;
+
+    if (y < 0) {                        /* It was not a number */
+        if (((c = atmbuf[0])) && !atmbuf[1]) { /* Literal character? */
+            c &= 0xff;
+            if (((c > 31) && (c < 127)) || (c > 255)) {
+                printf("\n?%d: Out of range - must be 0-31 or 127-255\n",c);
+                return(-9);
+            } else {
+                if ((y = cmcfm()) < 0)  /* Confirm */
+                  return(y);
+                *var = c;               /* Set the variable */
+                return(1);
+            }
+        } else if (atmbuf[0] == '^' && !atmbuf[2]) { /* Or ^X notation? */
+            c = atmbuf[1];
+            if (islower((char) c))      /* Uppercase lowercase letters */
+              c = toupper(c);
+            if (c > 62 && c < 96) {     /* Check range */
+                if ((y = cmcfm()) < 0)
+                  return(y);
+                *var = ctl(c);          /* OK */
+                return(1);
+            } else {
+                printf("?Not a control character - %s\n", atmbuf);
+                return(-9);
+            }
+        } else {                        /* Something illegal was typed */
+            printf("?Invalid - %s\n", atmbuf);
+            return(-9);
+        }
+    }
+    if (((x > 31) && (x < 127)) || (x > 255)) { /* They typed a number */
+        printf("\n?%d: Out of range - must be 0-31 or 127-255\n",x);
+        return(-9);
+    }
+    if ((y = cmcfm()) < 0)              /* In range, confirm */
+      return(y);
+    *var = x;                           /* Set variable */
+    return(1);
+}
+
+#ifndef NOSPL                           /* The SORT command... */
+
+static struct keytab srtswtab[] = {     /* SORT command switches */
+    { "/case",    SRT_CAS, CM_ARG },
+    { "/key",     SRT_KEY, CM_ARG },
+    { "/numeric", SRT_NUM, 0 },
+    { "/range",   SRT_RNG, CM_ARG },
+    { "/reverse", SRT_REV, 0 }
+};
+static int nsrtswtab = sizeof(srtswtab)/sizeof(struct keytab);
+
+extern char **a_ptr[];                  /* Array pointers */
+extern int a_dim[];                     /* Array dimensions */
+
+int
+dosort() {                              /* Do the SORT command */
+    char c, *p = NULL, ** ap, ** xp = NULL;
+    struct FDB sw, fl, cm;
+    int hi, lo;
+    int xn = 0, xr = -1, xk = -1, xc = -1, xs = 0;
+    int getval = 0, range[2], confirmed = 0;
+
+    cmfdbi(&sw,                         /* First FDB - command switches */
+           _CMKEY,                      /* fcode */
+           "Array name or switch",
+           "",                          /* default */
+           "",                          /* addtl string data */
+           nsrtswtab,                   /* addtl numeric data 1: tbl size */
+           4,                           /* addtl numeric data 2: 4 = cmswi */
+           NULL,                        /* Processing function */
+           srtswtab,                    /* Keyword table */
+           &fl                          /* Pointer to next FDB */
+           );
+    cmfdbi(&fl,                         /* Anything that doesn't match */
+           _CMFLD,                      /* fcode */
+           "Array name",                /* hlpmsg */
+           "",                          /* default */
+           "",                          /* addtl string data */
+           0,                           /* addtl numeric data 1 */
+           0,                           /* addtl numeric data 2 */
+           NULL,
+           NULL,
+           &cm
+           );
+    cmfdbi(&cm,                         /* Or premature confirmation */
+           _CMCFM,                      /* fcode */
+           "",                          /* hlpmsg */
+           "",                          /* default */
+           "",                          /* addtl string data */
+           0,                           /* addtl numeric data 1 */
+           0,                           /* addtl numeric data 2 */
+           NULL,
+           NULL,
+           NULL
+           );
+
+    range[0] = -1;
+    range[1] = -1;
+
+    while (1) {                         /* Parse 0 or more switches */
+        x = cmfdb(&sw);
+        if (x < 0)
+          return(x);
+        if (cmresult.fcode != _CMKEY)   /* Break out if not a switch */
+          break;
+        c = cmgbrk();
+        getval = (c == ':' || c == '=');
+        if (getval && !(cmresult.kflags & CM_ARG)) {
+            printf("?This switch does not take arguments\n");
+            return(-9);
+        }
+        switch (cmresult.nresult) {
+          case SRT_REV:
+            xr = 1;
+            break;
+          case SRT_KEY:
+            if (getval) {
+                if ((y = cmnum("Column for comparison (1-based)",
+                               "1",10,&x,xxstring)) < 0)
+                  return(y);
+                xk = x - 1;
+            } else
+              xk = 0;
+            break;
+          case SRT_CAS:
+            if (getval) {
+                if ((y = cmkey(onoff,2,"","on",xxstring)) < 0)
+                  return(y);
+                xc = y;
+            } else
+              xc = 1;
+            break;
+          case SRT_RNG:                 /* /RANGE */
+            if (getval) {
+                char buf[32];
+                char buf2[16];
+                int i;
+                char * p, * q;
+                if ((y = cmfld("low:high element","1",&s,NULL)) < 0)
+                  return(y);
+                s = brstrip(s);
+                ckstrncpy(buf,s,32);
+                p = buf;
+                for (i = 0; *p && i < 2; i++) { /* Get low and high */
+                    q = p;              /* Start of this piece */
+                    while (*p) {        /* Find end of this piece */
+                        if (*p == ':') {
+                            *p = NUL;
+                            p++;
+                            break;
+                        }
+                        p++;
+                    }
+                    y = 15;             /* Evaluate this piece */
+                    s = buf2;
+                    zzstring(q,&s,&y);
+                    s = evalx(buf2);
+                    if (s) if (*s) ckstrncpy(buf2,s,16);
+                    if (!rdigits(buf2)) {
+                        printf("?Not numeric: %s\n",buf2);
+                        return(-9);
+                    }
+                    range[i] = atoi(buf2);
+                }
+            }
+            break;
+          case SRT_NUM:                 /* /NUMERIC */
+            xn = 1;
+            break;
+          default:
+            return(-2);
+        }
+    }
+    switch (cmresult.fcode) {
+      case _CMCFM:
+        confirmed = 1;
+        break;
+      case _CMFLD:
+        ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Safe copy of name */
+        s = line;
+        break;
+      default:
+        printf("?Unexpected function code: %d\n",cmresult.fcode);
+        return(-9);
+    }
+    if (confirmed) {
+        printf("?Array name required\n");
+        return(-9);
+    }
+    ckmakmsg(tmpbuf,TMPBUFSIZ,
+             "Second array to sort according to ",s,NULL,NULL);
+    if ((x = cmfld(tmpbuf,"",&p,NULL)) < 0)
+      if (x != -3)
+        return(x);
+    tmpbuf[0] = NUL;
+    ckstrncpy(tmpbuf,p,TMPBUFSIZ);
+    p = tmpbuf;
+    if ((x = cmcfm()) < 0)              /* Get confirmation */
+      return(x);
+
+    x = arraybounds(s,&lo,&hi);         /* Get array index & bounds */
+    if (x < 0) {                        /* Check */
+        printf("?Bad array name: %s\n",s);
+        return(-9);
+    }
+    if (lo > -1) range[0] = lo;         /* Set range */
+    if (hi > -1) range[1] = hi;
+    ap = a_ptr[x];                      /* Get pointer to array element list */
+    if (!ap) {                          /* Check */
+        printf("?Array not declared: %s\n", s);
+        return(-9);
+    }
+    if (range[0] < 0)                   /* Starting element */
+      range[0] = 1;
+    if (range[1] < 0)                   /* Final element */
+      range[1] = a_dim[x];
+    if (range[1] > a_dim[x]) {
+        printf("?range %d:%d exceeds array dimension %d\n",
+               range[0],range[1],a_dim[x]
+               );
+        return(-9);
+    }
+    ap += range[0];
+    xs = range[1] - range[0] + 1;       /* Number of elements to sort */
+    if (xs < 1) {                       /* Check */
+        printf("?Bad range: %d:%d\n",range[0],range[1]);
+        return(-9);
+    }
+    if (xk < 0) xk = 0;                 /* Key position */
+    if (xr < 0) xr = 0;                 /* Reverse flag */
+    if (xn)                             /* Numeric flag */
+      xc = 2;
+    else if (xc < 0)                    /* Not numeric */
+      xc = inpcas[cmdlvl];              /* so alpha case option */
+
+    if (*p) {                           /* Parallel array given? */
+        y = xarray(p);                  /* Yes, get its index. */
+        if (y < 0) {
+            printf("?Bad array name: %s\n", p);
+            return(-9);
+        }
+        if (y != x) {                   /* If the 2 arrays are different  */
+            xp = a_ptr[y];              /* Pointer to 2nd array element list */
+            if (!xp) {
+                printf("?Array not declared: %s\n", p);
+                return(-9);
+            }
+            if (a_dim[y] < range[1]) {
+                printf("?Array %s smaller than %s\n", p, s);
+                return(-9);
+            }
+            xp += range[0];             /* Set base to same as 1st array */
+        }
+    }
+    sh_sort(ap,xp,xs,xk,xr,xc);         /* Sort the array(s) */
+    return(success = 1);                /* Always succeeds */
+}
+#endif /* NOSPL */
+
+static struct keytab purgtab[] = {      /* PURGE command switches */
+    { "/after",        PU_AFT,  CM_ARG },
+    { "/ask",          PU_ASK,  0 },
+    { "/before",       PU_BEF,  CM_ARG },
+    { "/delete",       PU_DELE, CM_INV },
+#ifdef UNIXOROSK
+    { "/dotfiles",     PU_DOT,  0 },
+#endif /* UNIXOROSK */
+    { "/except",       PU_EXC,  CM_ARG },
+    { "/heading",      PU_HDG,  0 },
+    { "/keep",         PU_KEEP, CM_ARG },
+    { "/larger-than",  PU_LAR,  CM_ARG },
+    { "/list",         PU_LIST, 0 },
+    { "/log",          PU_LIST, CM_INV },
+    { "/noask",        PU_NASK, 0 },
+    { "/nodelete",     PU_NODE, CM_INV },
+#ifdef UNIXOROSK
+    { "/nodotfiles",   PU_NODOT,0 },
+#endif /* UNIXOROSK */
+    { "/noheading",    PU_NOH,  0 },
+    { "/nol",          PU_NOLI, CM_INV|CM_ABR },
+    { "/nolist",       PU_NOLI, 0 },
+    { "/nolog",        PU_NOLI, CM_INV },
+#ifdef CK_TTGWSIZ
+    { "/nopage",       PU_NOPA, 0 },
+#endif /* CK_TTGWSIZ */
+    { "/not-after",    PU_NAF,  CM_ARG },
+    { "/not-before",   PU_NBF,  CM_ARG },
+    { "/not-since",    PU_NAF,  CM_INV|CM_ARG },
+#ifdef CK_TTGWSIZ
+    { "/page",         PU_PAGE, 0 },
+#endif /* CK_TTGWSIZ */
+    { "/quiet",        PU_QUIE, CM_INV },
+#ifdef RECURSIVE
+    { "/recursive",    PU_RECU, 0 },
+#endif /* RECURSIVE */
+    { "/since",        PU_AFT,  CM_ARG|CM_INV },
+    { "/simulate",     PU_NODE, 0 },
+    { "/smaller-than", PU_SMA,  CM_ARG },
+    { "/verbose",      PU_VERB, CM_INV }
+};
+static int npurgtab = sizeof(purgtab)/sizeof(struct keytab);
+
+
+
+
+
+int
+bkupnum(s,i) char * s; int *i; {
+    int k = 0, pos = 0;
+    char * p = NULL, *q;
+    *i = pos;
+    if (!s) s = "";
+    if (!*s)
+      return(-1);
+    if ((k = strlen(s)) < 5)
+      return(-1);
+
+    if (s[k-1] != '~')
+      return(-1);
+    pos = k - 2;
+    q = s + pos;
+    while (q >= s && isdigit(*q)) {
+        p = q--;
+        pos--;
+    }
+    if (!p)
+      return(-1);
+    if (q < s+2)
+      return(-1);
+    if (*q != '~' || *(q-1) != '.')
+      return(-1);
+    pos--;
+    *i = pos;
+    debug(F111,"bkupnum",s+pos,pos);
+    return(atoi(p));
+}
+
+#ifdef CKPURGE
+/* Presently only for UNIX because we need direct access to the file array. */
+/* Not needed for VMS anyway, because we don't make backup files there. */
+
+#define MAXKEEP 32                      /* Biggest /KEEP: value */
+
+static int
+  pu_keep = 0, pu_list = 0, pu_dot = 0, pu_ask = 0, pu_hdg = 0;
+
+#ifdef CK_TTGWSIZ
+static int pu_page = -1;
+#else
+static int pu_page = 0;
+#endif /* CK_TTGWSIZ */
+
+#ifndef NOSHOW
+VOID
+showpurgopts() {                        /* SHOW PURGE command options */
+    int x = 0;
+    extern int optlines;
+    prtopt(&optlines,"PURGE");
+    if (pu_ask > -1) {
+        x++;
+        prtopt(&optlines, pu_ask ? "/ASK" : "/NOASK");
+    }
+#ifdef UNIXOROSK
+    if (pu_dot > -1) {
+        x++;
+        prtopt(&optlines, pu_dot ? "/DOTFILES" : "/NODOTFILES");
+    }
+#endif /* UNIXOROSK */
+    if (pu_keep > -1) {
+        x++;
+        ckmakmsg(tmpbuf,TMPBUFSIZ,"/KEEP:",ckitoa(pu_keep),NULL,NULL);
+        prtopt(&optlines,tmpbuf);
+    }
+    if (pu_list > -1) {
+        x++;
+        prtopt(&optlines, pu_list ? "/LIST" : "/NOLIST");
+    }
+    if (pu_hdg > -1) {
+        x++;
+        prtopt(&optlines, pu_hdg ? "/HEADING" : "/NOHEADING");
+    }
+#ifdef CK_TTGWSIZ
+    if (pu_page > -1) {
+        x++;
+        prtopt(&optlines, pu_page ? "/PAGE" : "/NOPAGE");
+    }
+#endif /* CK_TTGWSIZ */
+    if (!x) prtopt(&optlines,"(no options set)");
+    prtopt(&optlines,"");
+}
+#endif /* NOSHOW */
+
+int
+setpurgopts() {                         /* Set PURGE command options */
+    int c, z, getval = 0;
+    int
+      x_keep  = -1, x_list = -1, x_page = -1,
+      x_hdg   = -1, x_ask  = -1, x_dot  = -1;
+
+    while (1) {
+        if ((y = cmswi(purgtab,npurgtab,"Switch","",xxstring)) < 0) {
+            if (y == -3)
+              break;
+            else
+              return(y);
+        }
+        c = cmgbrk();
+        if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
+            printf("?This switch does not take an argument\n");
+            return(-9);
+        }
+        if (!getval && (cmgkwflgs() & CM_ARG)) {
+            printf("?This switch requires an argument\n");
+            return(-9);
+        }
+        switch (y) {
+          case PU_KEEP:
+            z = 1;
+            if (c == ':' || c == '=')
+              if ((y = cmnum("How many backup files to keep",
+                             "1",10,&z,xxstring)) < 0)
+                return(y);
+            if (z < 0 || z > MAXKEEP) {
+                printf("?Please specify a number between 0 and %d\n",
+                       MAXKEEP
+                       );
+                return(-9);
+            }
+            x_keep = z;
+            break;
+          case PU_LIST:
+          case PU_VERB:
+            x_list = 1;
+            break;
+          case PU_QUIE:
+          case PU_NOLI:
+            x_list = 0;
+            break;
+#ifdef CK_TTGWSIZ
+          case PU_PAGE:
+            x_page = 1;
+            break;
+          case PU_NOPA:
+            x_page = 0;
+            break;
+#endif /* CK_TTGWSIZ */
+          case PU_HDG:
+            x_hdg = 1;
+            break;
+          case PU_NOH:
+            x_hdg = 0;
+            break;
+          case PU_ASK:
+            x_ask = 1;
+            break;
+          case PU_NASK:
+            x_ask = 0;
+            break;
+#ifdef UNIXOROSK
+          case PU_DOT:
+            x_dot = 1;
+            break;
+          case PU_NODOT:
+            x_dot = 0;
+            break;
+#endif /* UNIXOROSK */
+          default:
+            printf("?This option can not be set\n");
+            return(-9);
+        }
+    }
+    if ((x = cmcfm()) < 0)              /* Get confirmation */
+      return(x);
+    if (x_keep > -1)                    /* Set PURGE defaults. */
+      pu_keep = x_keep;
+    if (x_list > -1)
+      pu_list = x_list;
+#ifdef CK_TTGWSIZ
+    if (x_page > -1)
+      pu_page = x_page;
+#endif /* CK_TTGWSIZ */
+    if (x_hdg > -1)
+      pu_hdg = x_hdg;
+    if (x_ask > -1)
+      pu_ask = x_ask;
+    if (x_dot > -1)
+      pu_dot = x_dot;
+    return(success = 1);
+}
+
+int
+dopurge() {                             /* Do the PURGE command */
+    extern char ** mtchs;
+    extern int xaskmore, cmd_rows, recursive;
+    int simulate = 0, asking = 0;
+    int listing = 0, paging = -1, lines = 0, deleting = 1, errors = 0;
+    struct FDB sw, sf, cm;
+    int g, i, j, k, m = 0, n, x, y, z, done = 0, count = 0, flags = 0;
+    int tokeep = 0, getval = 0, havename = 0, confirmed = 0;
+    int xx[MAXKEEP+1];                  /* Array of numbers to keep */
+    int min = -1;
+    int x_hdg = 0, fs = 0, rc = 0;
+    long minsize = -1L, maxsize = -1L;
+    char namebuf[CKMAXPATH+4];
+    char basebuf[CKMAXPATH+4];
+    char
+      * pu_aft = NULL,
+      * pu_bef = NULL,
+      * pu_naf = NULL,
+      * pu_nbf = NULL,
+      * pu_exc = NULL;
+    char * pxlist[8];                   /* Exception list */
+
+    if (pu_keep > -1)                   /* Set PURGE defaults. */
+      tokeep = pu_keep;
+    if (pu_list > -1)
+      listing = pu_list;
+#ifdef CK_TTGWSIZ
+    if (pu_page > -1)
+      paging = pu_page;
+#endif /* CK_TTGWSIZ */
+
+    for (i = 0; i <= MAXKEEP; i++)      /* Clear this number buffer */
+      xx[i] = 0;
+    for (i = 0; i < 8; i++)             /* Initialize these... */
+      pxlist[i] = NULL;
+
+    g_matchdot = matchdot;              /* Save these... */
+
+    cmfdbi(&sw,                         /* 1st FDB - PURGE switches */
+           _CMKEY,                      /* fcode */
+           "Filename or switch",        /* hlpmsg */
+           "",                          /* default */
+           "",                          /* addtl string data */
+           npurgtab,                    /* addtl numeric data 1: tbl size */
+           4,                           /* addtl numeric data 2: 4 = cmswi */
+           xxstring,                    /* Processing function */
+           purgtab,                     /* Keyword table */
+           &sf                          /* Pointer to next FDB */
+           );
+    cmfdbi(&sf,                         /* 2nd FDB - filespec to purge */
+           _CMIFI,                      /* fcode */
+           "",
+           "",                          /* default */
+           "",                          /* addtl string data */
+           0,                           /* addtl numeric data 1 */
+           0,                           /* addtl numeric data 2 */
+           xxstring,
+           NULL,
+           &cm
+           );
+    cmfdbi(&cm,                         /* Or premature confirmation */
+           _CMCFM,                      /* fcode */
+           "",                          /* hlpmsg */
+           "",                          /* default */
+           "",                          /* addtl string data */
+           0,                           /* addtl numeric data 1 */
+           0,                           /* addtl numeric data 2 */
+           NULL,
+           NULL,
+           NULL
+           );
+
+    while (!havename && !confirmed) {
+        x = cmfdb(&sw);                 /* Parse something */
+        if (x < 0) {                    /* Error */
+            rc = x;
+            goto xpurge;
+        } else if (cmresult.fcode == _CMKEY) {
+            char c;
+            c = cmgbrk();
+            if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
+                printf("?This switch does not take an argument\n");
+                rc = -9;
+                goto xpurge;
+            }
+            if (!getval && (cmgkwflgs() & CM_ARG)) {
+                printf("?This switch requires an argument\n");
+                rc = -9;
+                goto xpurge;
+            }
+            switch (k = cmresult.nresult) {
+              case PU_KEEP:
+                z = 1;
+                if (c == ':' || c == '=') {
+                    if ((y = cmnum("How many backup files to keep",
+                                   "1",10,&z,xxstring)) < 0) {
+                        rc = y;
+                        goto xpurge;
+                    }
+                }
+                if (z < 0 || z > MAXKEEP) {
+                    printf("?Please specify a number between 0 and %d\n",
+                           MAXKEEP
+                           );
+                    rc = -9;
+                    goto xpurge;
+                }
+                tokeep = z;
+                break;
+              case PU_LIST:
+                listing = 1;
+                break;
+              case PU_NOLI:
+                listing = 0;
+                break;
+#ifdef CK_TTGWSIZ
+              case PU_PAGE:
+                paging = 1;
+                break;
+              case PU_NOPA:
+                paging = 0;
+                break;
+#endif /* CK_TTGWSIZ */
+              case PU_DELE:
+                deleting = 1;
+                break;
+              case PU_NODE:
+                deleting = 0;
+                simulate = 1;
+                listing = 1;
+                break;
+              case PU_ASK:
+                asking = 1;
+                break;
+              case PU_NASK:
+                asking = 0;
+                break;
+              case PU_AFT:
+              case PU_BEF:
+              case PU_NAF:
+              case PU_NBF:
+                if ((x = cmdate("File-time","",&s,0,xxstring)) < 0) {
+                    if (x == -3) {
+                        printf("?Date-time required\n");
+                        rc = -9;
+                    } else
+                      rc = x;
+                    goto xpurge;
+                }
+                fs++;
+                switch (k) {
+                  case PU_AFT: makestr(&pu_aft,s); break;
+                  case PU_BEF: makestr(&pu_bef,s); break;
+                  case PU_NAF: makestr(&pu_naf,s); break;
+                  case PU_NBF: makestr(&pu_nbf,s); break;
+                }
+                break;
+              case PU_SMA:
+              case PU_LAR:
+                if ((x = cmnum("File size in bytes","0",10,&y,xxstring)) < 0) {
+                    rc = x;
+                    goto xpurge;
+                }
+                fs++;
+                switch (cmresult.nresult) {
+                  case PU_SMA: minsize = y; break;
+                  case PU_LAR: maxsize = y; break;
+                }
+                break;
+              case PU_DOT:
+                matchdot = 1;
+                break;
+              case PU_NODOT:
+                matchdot = 0;
+                break;
+              case PU_EXC:
+                if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
+                    if (x == -3) {
+                        printf("?Pattern required\n");
+                        rc = -9;
+                    } else
+                      rc = x;
+                    goto xpurge;
+                }
+                fs++;
+                makestr(&pu_exc,s);
+                break;
+              case PU_HDG:
+                x_hdg = 1;
+                break;
+#ifdef RECURSIVE
+              case PU_RECU:             /* /RECURSIVE */
+                recursive = 2;
+                break;
+#endif /* RECURSIVE */
+              default:
+                printf("?Not implemented yet - \"%s\"\n",atmbuf);
+                rc = -9;
+                goto xpurge;
+            }
+        } else if (cmresult.fcode == _CMIFI) {
+            havename = 1;
+        } else if (cmresult.fcode == _CMCFM) {
+            confirmed = 1;
+        } else {
+            rc = -2;
+            goto xpurge;
+        }
+    }
+    if (havename) {
+#ifdef CKREGEX
+        ckmakmsg(line,LINBUFSIZ,cmresult.sresult,".~[1-9]*~",NULL,NULL);
+#else
+        ckmakmsg(line,LINBUFSIZ,cmresult.sresult,".~*~",NULL,NULL);
+#endif /* CKREGEX */
+    } else {
+#ifdef CKREGEX
+        ckstrncpy(line,"*.~[1-9]*~",LINBUFSIZ);
+#else
+        ckstrncpy(line,"*.~*~",LINBUFSIZ);
+#endif /* CKREGEX */
+    }
+    if (!confirmed) {
+        if ((x = cmcfm()) < 0) {
+            rc = x;
+            goto xpurge;
+        }
+    }
+    /* Parse finished - now action */
+
+#ifdef CK_LOGIN
+    if (isguest) {
+        printf("?File deletion by guests not permitted.\n");
+        rc = -9;
+        goto xpurge;
+    }
+#endif /* CK_LOGIN */
+
+#ifdef CK_TTGWSIZ
+    if (paging < 0)                     /* /[NO]PAGE not given */
+      paging = xaskmore;                /* so use prevailing */
+#endif /* CK_TTGWSIZ */
+
+    lines = 0;
+    if (x_hdg > 0) {
+        printf("Purging %s, keeping %d...%s\n",
+               s,
+               tokeep,
+               simulate ? " (SIMULATION)" : "");
+        lines += 2;
+    }
+    flags = ZX_FILONLY;
+    if (recursive) flags |= ZX_RECURSE;
+    n = nzxpand(line,flags);            /* Get list of backup files */
+    if (tokeep < 1) {                   /* Deleting all of them... */
+        for (i = 0; i < n; i++) {
+            if (fs) if (fileselect(mtchs[i],
+                                   pu_aft,pu_bef,pu_naf,pu_nbf,
+                                   minsize,maxsize,0,8,pxlist) < 1) {
+                if (listing > 0) {
+                    printf(" %s (SKIPPED)\n",mtchs[i]);
+#ifdef CK_TTGWSIZ
+                    if (paging)
+                      if (++lines > cmd_rows - 3) {
+                          if (!askmore()) goto xpurge; else lines = 0;
+                      }
+#endif /* CK_TTGWSIZ */
+                }
+                continue;
+            }
+            if (asking) {
+                int x;
+                ckmakmsg(tmpbuf,TMPBUFSIZ," Delete ",mtchs[i],"?",NULL);
+                x = getyesno(tmpbuf,1);
+                switch (x) {
+                  case 0: continue;
+                  case 1: break;
+                  case 2: goto xpurge;
+                }
+            }
+            x = deleting ? zdelet(mtchs[i]) : 0;
+            if (x > -1) {
+                if (listing)
+                  printf(" %s (%s)\n", mtchs[i],deleting ? "OK" : "SELECTED");
+                count++;
+            } else {
+                errors++;
+                if (listing)
+                  printf(" %s (FAILED)\n", mtchs[i]);
+            }
+#ifdef CK_TTGWSIZ
+            if (listing && paging)
+              if (++lines > cmd_rows - 3) {
+                  if (!askmore()) goto xpurge; else lines = 0;
+              }
+#endif /* CK_TTGWSIZ */
+        }
+        goto xpurge;
+    }
+    if (n < tokeep) {                   /* Not deleting any */
+        count = 0;
+        if (listing)
+          printf(" Matches = %d: Not enough to purge.\n");
+        goto xpurge;
+    }
+
+    /* General case - delete some but not others */
+
+    sh_sort(mtchs,NULL,n,0,0,filecase); /* Alphabetize the list (ESSENTIAL) */
+
+    g = 0;                              /* Start of current group */
+    for (i = 0; i < n; i++) {           /* Go thru sorted file list */
+        x = znext(namebuf);             /* Get next file */
+        if (x < 1 || !namebuf[0] || i == n - 1) /* No more? */
+          done = 1;                     /* NOTE: 'done' must be 0 or 1 only */
+        if (fs) if (fileselect(namebuf,
+                               pu_aft,pu_bef,pu_naf,pu_nbf,
+                               minsize,maxsize,0,8,pxlist) < 1) {
+            if (listing > 0) {
+                printf(" %s (SKIPPED)\n",namebuf);
+                if (++lines > cmd_rows - 3)
+                  if (!askmore()) goto xpurge; else lines = 0;
+            }
+            continue;
+        }
+        if (x > 0)
+          if ((m = bkupnum(namebuf,&z)) < 0) /* This file's backup number. */
+            continue;
+        for (j = 0; j < tokeep; j++) {  /* Insert in list. */
+            if (m > xx[j]) {
+                for (k = tokeep - 1; k > j; k--)
+                  xx[k] = xx[k-1];
+                xx[j] = m;
+                break;
+            }
+        }
+        /* New group? */
+        if (done || (i > 0 && ckstrcmp(namebuf,basebuf,z,1))) {
+            if (i + done - g > tokeep) { /* Do we have enough to purge? */
+                min = xx[tokeep-1];     /* Yes, lowest backup number to keep */
+                debug(F111,"dopurge group",basebuf,min);
+                for (j = g; j < i + done; j++) { /* Go through this group */
+                    x = bkupnum(mtchs[j],&z);    /* Get file backup number */
+                    if (x > 0 && x < min) {      /* Below minimum? */
+                        x = deleting ? zdelet(mtchs[j]) : 0;
+                        if (x < 0) errors++;
+                        if (listing)
+                          printf(" %s (%s)\n",
+                                 mtchs[j],
+                                 ((x < 0) ? "ERROR" :
+                                  (deleting ? "DELETED" : "SELECTED"))
+                                 );
+                        count++;
+                    } else if (listing) /* Not below minimum - keep this one */
+                      printf(" %s (KEPT)\n",mtchs[j]);
+#ifdef CK_TTGWSIZ
+                    if (listing && paging)
+                      if (++lines > cmd_rows - 3) {
+                          if (!askmore()) goto xpurge; else lines = 0;
+                      }
+#endif /* CK_TTGWSIZ */
+                }
+            } else if (listing && paging) { /* Not enough to purge */
+                printf(" %s.~*~ (KEPT)\n",basebuf);
+#ifdef CK_TTGWSIZ
+                if (++lines > cmd_rows - 3) {
+                    if (!askmore()) goto xpurge; else lines = 0;
+                }
+#endif /* CK_TTGWSIZ */
+            }
+            for (j = 0; j < tokeep; j++) /* Clear the backup number list */
+              xx[j] = 0;
+            g = i;                      /* Reset the group pointer */
+        }
+        if (done)                       /* No more files, done. */
+          break;
+        strncpy(basebuf,namebuf,z);     /* Set basename of this file */
+        basebuf[z] = NUL;
+    }
+  xpurge:                               /* Common exit point */
+    if (g_matchdot > -1) {
+        matchdot = g_matchdot;          /* Restore these... */
+        g_matchdot = -1;
+    }
+    if (rc < 0) return(rc);             /* Parse error */
+    if (x_hdg)
+      printf("Files purged: %d%s\n",
+             count,
+             deleting ? "" : " (not really)"
+             );
+    return(success = count > 0 ? 1 : (errors > 0) ? 0 : 1);
+}
+#endif /* CKPURGE */
+
+#ifndef NOXFER
+#ifndef NOLOCAL
+int
+doxdis(which) int which; {             /* 1 = Kermit, 2 = FTP */
+    extern int nolocal;
+    int x, y = 0, z;
+#ifdef NEWFTP
+    extern int ftp_dis;
+#endif /* NEWFTP */
+
+#ifdef COMMENT
+    char *s;
+#endif /* COMMENT */
+
+    if ((x = cmkey(fdtab,nfdtab,"file transfer display style","",
+                   xxstring)) < 0)
+      return(x);
+#ifdef CK_PCT_BAR
+    if ((y = cmkey(fdftab,2,"","thermometer",xxstring)) < 0)
+      return(y);
+#endif /* CK_PCT_BAR */
+    if ((z = cmcfm()) < 0) return(z);
+#ifdef CK_CURSES
+    if (x == XYFD_C) {                  /* FULLSCREEN */
+#ifdef COMMENT
+#ifndef MYCURSES
+        extern char * trmbuf;           /* Real curses */
+        int z;
+#endif /* MYCURSES */
+#endif /* COMMENT */
+
+        if (nolocal)                    /* Nothing to do in this case */
+         return(success = 1);
+
+#ifdef COMMENT
+#ifndef MYCURSES
+#ifndef VMS
+        s = getenv("TERM");
+        debug(F110,"doxdis TERM",s,0);
+        if (!s) s = "";
+        fxdinit(x);
+        if (*s && trmbuf) {             /* Don't call tgetent */
+            z = tgetent(trmbuf,s);      /* if trmbuf not allocated */
+            debug(F111,"doxdis tgetent",s,z);
+        } else {
+            z = 0;
+            debug(F110,"doxdis tgetent skipped",s,0);
+        }
+        if (z < 1) {
+            printf("Sorry, terminal type unknown: \"%s\"\n",s);
+            return(success = 0);
+        }
+#endif /* VMS */
+#endif /* MYCURSES */
+#else
+        fxdinit(x);
+#endif /* COMMENT */
+
+#ifdef CK_PCT_BAR
+        thermometer = y;
+#endif /* CK_PCT_BAR */
+
+        line[0] = '\0';                 /* (What's this for?) */
+    }
+#endif /* CK_CURSES */
+    if (which == 1)                    /* It's OK. */
+      fdispla = x;
+#ifdef NEWFTP
+    else
+      ftp_dis = x;
+#endif /* NEWFTP */
+    return(success = 1);
+}
+#endif /* NOLOCAL */
+#endif /* NOXFER */
+
+int
+setfil(rmsflg) int rmsflg; {
+#ifdef COMMENT
+    extern int en_del;
+#endif /* COMMENT */
+#ifndef NOXFER
+    if (rmsflg) {
+        if ((y = cmkey(rfiltab,nrfilp,"Remote file parameter","",
+                       xxstring)) < 0) {
+            if (y == -3) {
+                printf("?Remote file parameter required\n");
+                return(-9);
+            } else return(y);
+        }
+    } else {
+#endif /* NOXFER */
+        if ((y = cmkey(filtab,nfilp,"File parameter","",xxstring)) < 0)
+          return(y);
+#ifndef NOXFER
+    }
+#endif /* NOXFER */
+    switch (y) {
+#ifdef COMMENT                          /* Not needed */
+      case XYFILB:                      /* Blocksize */
+        if ((y = cmnum("file block size",ckitoa(DBLKSIZ),10,&z,xxstring)) < 0)
+          return(y);
+        if ((x = cmcfm()) < 0) return(x);
+        if (rmsflg) {
+            sstate = setgen('S', "311", ckitoa(z), "");
+            return((int) sstate);
+        } else {
+            fblksiz = z;
+            return(success = 1);
+        }
+#endif /* COMMENT */
+
+#ifndef NOXFER
+      case XYFILS:                      /* Byte size */
+        if ((y = cmnum("file byte size (7 or 8)","8",10,&z,xxstring)) < 0)
+          return(y);
+        if (z != 7 && z != 8) {
+            printf("\n?The choices are 7 and 8\n");
+            return(0);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        if (z == 7) fmask = 0177;
+        else if (z == 8) fmask = 0377;
+        return(success = 1);
+
+#ifndef NOCSETS
+      case XYFILC: {                    /* Character set */
+          char * csetname = NULL;
+          extern int
+            r_cset, s_cset, afcset[];   /* SEND CHARACTER-SET AUTO or MANUAL */
+
+          struct FDB kw, fl;
+          cmfdbi(&kw,                   /* First FDB - command switches */
+                 _CMKEY,                /* fcode */
+                 rmsflg ? "server character-set name" : "",  /* help */
+                 "",                    /* default */
+                 "",                    /* addtl string data */
+                 nfilc,                 /* addtl numeric data 1: tbl size */
+                 0,                     /* addtl numeric data 2: 0 = keyword */
+                 xxstring,              /* Processing function */
+                 fcstab,                /* Keyword table */
+                 rmsflg ? &fl : NULL    /* Pointer to next FDB */
+           );
+          cmfdbi(&fl,                   /* Anything that doesn't match */
+                 _CMFLD,                /* fcode */
+                 "",                    /* hlpmsg */
+                 "",                    /* default */
+                 "",                    /* addtl string data */
+                 0,                     /* addtl numeric data 1 */
+                 0,                     /* addtl numeric data 2 */
+                 xxstring,
+                 NULL,
+                 NULL
+                 );
+          if ((x = cmfdb(&kw)) < 0)
+            return(x);
+          if (cmresult.fcode == _CMKEY) {
+              x = cmresult.nresult;
+              csetname = fcsinfo[x].keyword;
+          } else {
+              ckstrncpy(line,cmresult.sresult,LINBUFSIZ);
+              csetname = line;
+          }
+          if ((z = cmcfm()) < 0) return(z);
+          if (rmsflg) {
+              sstate = setgen('S', "320", csetname, "");
+              return((int) sstate);
+          }
+          fcharset = x;
+          if (s_cset == XMODE_A)        /* If SEND CHARACTER-SET is AUTO */
+            if (x > -1 && x <= MAXFCSETS)
+              if (afcset[x] > -1 && afcset[x] <= MAXTCSETS)
+                tcharset = afcset[x]; /* Pick corresponding xfer charset */
+          setxlatype(tcharset,fcharset); /* Translation type */
+          /* If I say SET FILE CHARACTER-SET blah, I want to be blah! */
+          r_cset = XMODE_M;             /* Don't switch incoming set! */
+          x = fcsinfo[fcharset].size;   /* Also set default x-bit charset */
+          if (x == 128)                 /* 7-bit... */
+            dcset7 = fcharset;
+          else if (x == 256)            /* 8-bit... */
+            dcset8 = fcharset;
+          return(success = 1);
+      }
+#endif /* NOCSETS */
+
+#ifndef NOLOCAL
+      case XYFILD:                      /* Display */
+        return(doxdis(1));             /* 1 == kermit */
+#endif /* NOLOCAL */
+#endif /* NOXFER */
+
+      case XYFILA:                      /* End-of-line */
+#ifdef NLCHAR
+        s = "";
+        if (NLCHAR == 015)
+          s = "cr";
+        else if (NLCHAR == 012)
+          s = "lf";
+        if ((x = cmkey(eoltab, neoltab,
+                       "local text-file line terminator",s,xxstring)) < 0)
+          return(x);
+#else
+        if ((x = cmkey(eoltab, neoltab,
+                       "local text-file line terminator","crlf",xxstring)) < 0)
+          return(x);
+#endif /* NLCHAR */
+        if ((z = cmcfm()) < 0) return(z);
+        feol = (CHAR) x;
+        return(success = 1);
+
+#ifndef NOXFER
+      case XYFILN:                      /* Names */
+        if ((x = cmkey(fntab,nfntab,"how to handle filenames","converted",
+                       xxstring)) < 0)
+          return(x);
+        if ((z = cmcfm()) < 0) return(z);
+        if (rmsflg) {
+            sstate = setgen('S', "301", ckitoa(1 - x), "");
+            return((int) sstate);
+        } else {
+            ptab[protocol].fncn = x;    /* Set structure */
+            fncnv = x;                  /* Set variable */
+            f_save = x;                 /* And set "permanent" variable */
+            return(success = 1);
+        }
+
+      case XYFILR:                      /* Record length */
+        if ((y = cmnum("file record length",
+                       ckitoa(DLRECL),10,&z,xxstring)) < 0)
+          return(y);
+        if ((x = cmcfm()) < 0) return(x);
+        if (rmsflg) {
+            sstate = setgen('S', "312", ckitoa(z), "");
+            return((int) sstate);
+        } else {
+            frecl = z;
+            return(success = 1);
+        }
+
+#ifdef COMMENT
+      case XYFILO:                      /* Organization */
+        if ((x = cmkey(forgtab,nforg,"file organization","sequential",
+                       xxstring)) < 0)
+          return(x);
+        if ((y = cmcfm()) < 0) return(y);
+        if (rmsflg) {
+            sstate = setgen('S', "314", ckitoa(x), "");
+            return((int) sstate);
+        } else {
+            forg = x;
+            return(success = 1);
+        }
+#endif /* COMMENT */
+
+#ifdef COMMENT                          /* Not needed */
+      case XYFILF:                      /* Format */
+        if ((x = cmkey(frectab,nfrec,"file record format","stream",
+                       xxstring)) < 0)
+          return(x);
+        if ((y = cmcfm()) < 0) return(y);
+        if (rmsflg) {
+            sstate = setgen('S', "313", ckitoa(x), "");
+            return((int) sstate);
+        } else {
+            frecfm = x;
+            return(success = 1);
+        }
+#endif /* COMMENT */
+
+#ifdef COMMENT
+      case XYFILP:                      /* Printer carriage control */
+        if ((x = cmkey(fcctab,nfcc,"file carriage control","newline",
+                       xxstring)) < 0)
+          return(x);
+        if ((y = cmcfm()) < 0) return(y);
+        if (rmsflg) {
+            sstate = setgen('S', "315", ckitoa(x), "");
+            return((int) sstate);
+        } else {
+            fcctrl = x;
+            return(success = 1);
+        }
+#endif /* COMMENT */
+#endif /* NOXFER */
+
+      case XYFILT:                      /* Type */
+        if ((x = cmkey(rmsflg ? rfttab  : fttab,
+                       rmsflg ? nrfttyp : nfttyp,
+                       "type of file transfer","text",xxstring)) < 0)
+          return(x);
+
+#ifdef VMS
+        /* Allow VMS users to choose record format for binary files */
+        if ((x == XYFT_B) && (rmsflg == 0)) {
+            if ((x = cmkey(fbtab,nfbtyp,"VMS record format","fixed",
+                           xxstring)) < 0)
+              return(x);
+        }
+#endif /* VMS */
+        if ((y = cmcfm()) < 0) return(y);
+        binary = x;
+        b_save = x;
+#ifdef MAC
+        (void) mac_setfildflg(binary);
+#endif /* MAC */
+#ifndef NOXFER
+        if (rmsflg) {
+            /* Allow for LABELED in VMS & OS/2 */
+            sstate = setgen('S', "300", ckitoa(x), "");
+            return((int) sstate);
+        } else {
+#endif /* NOXFER */
+            return(success = 1);
+#ifndef NOXFER
+        }
+#endif /* NOXFER */
+
+#ifndef NOXFER
+      case XYFILX:                      /* Collision Action */
+        if ((x = cmkey(colxtab,ncolx,"Filename collision action","backup",
+                       xxstring)) < 0)
+          return(x);
+        if ((y = cmcfm()) < 0) return(y);
+#ifdef CK_LOGIN
+        if (isguest) {
+            /* Don't let guests change existing files */
+            printf("?This command not valid for guests\n");
+            return(-9);
+        }
+#endif /* CK_LOGIN */
+#ifdef COMMENT
+        /* Not appropriate - DISABLE DELETE only refers to server */
+        if ((x == XYFX_X || x == XYFX_B || x == XYFX_U || x == XYFX_A) &&
+            (!ENABLED(en_del))) {
+            printf("?Sorry, file deletion is disabled.\n");
+            return(-9);
+        }
+#endif /* COMMENT */
+        fncact = x;
+        ptab[protocol].fnca = x;
+        if (rmsflg) {
+            sstate = setgen('S', "302", ckitoa(fncact), "");
+            return((int) sstate);
+        } else {
+            if (fncact == XYFX_R) ckwarn = 1; /* FILE WARNING implications */
+            if (fncact == XYFX_X) ckwarn = 0; /* ... */
+            return(success = 1);
+        }
+
+      case XYFILW:                      /* Warning/Write-Protect */
+        if ((x = seton(&ckwarn)) < 0) return(x);
+        if (ckwarn)
+          fncact = XYFX_R;
+        else
+          fncact = XYFX_X;
+        return(success = 1);
+
+#ifdef CK_LABELED
+      case XYFILL:                      /* LABELED FILE parameters */
+        if ((x = cmkey(lbltab,nlblp,"Labeled file feature","",
+                       xxstring)) < 0)
+          return(x);
+        if ((success = seton(&y)) < 0)
+          return(success);
+        if (y)                          /* Set or reset the selected bit */
+          lf_opts |= x;                 /* in the options bitmask. */
+        else
+          lf_opts &= ~x;
+        return(success);
+#endif /* CK_LABELED */
+
+      case XYFILI: {                    /* INCOMPLETE */
+          extern struct keytab ifdatab[];
+          extern int keep;
+          if ((y = cmkey(ifdatab,3,"","auto",xxstring)) < 0) return(y);
+          if ((x = cmcfm()) < 0) return(x);
+          if (rmsflg) {
+              sstate = setgen('S',
+                              "310",
+                              y == 0 ? "0" : (y == 1 ? "1" : "2"),
+                              ""
+                              );
+              return((int) sstate);
+          } else {
+              keep = y;
+              return(success = 1);
+          }
+      }
+
+#ifdef CK_TMPDIR
+      case XYFILG: {                    /* Download directory */
+          int x;
+          char *s;
+#ifdef ZFNQFP
+          struct zfnfp * fnp;
+#endif /* ZFNQFP */
+#ifdef MAC
+          char temp[34];
+#endif /* MAC */
+
+#ifdef GEMDOS
+          if ((x = cmdir("Name of local directory, or carriage return",
+                         "",&s,
+                         NULL)) < 0 ) {
+              if (x != -3)
+                return(x);
+          }
+#else
+#ifdef OS2
+          if ((x = cmdir("Name of PC disk and/or directory,\n\
+       or press the Enter key to use current directory",
+                         "",&s,xxstring)) < 0 ) {
+              if (x != -3)
+                return(x);
+          }
+#else
+#ifdef MAC
+          x = ckstrncpy(temp,zhome(),32);
+          if (x > 0) if (temp[x-1] != ':') { temp[x] = ':'; temp[x+1] = NUL; }
+          if ((x = cmtxt("Name of Macintosh volume and/or folder,\n\
+ or press the Return key for the desktop on the boot disk",
+                         temp,&s, xxstring)) < 0 )
+            return(x);
+#else
+          if ((x = cmdir("Name of local directory, or carriage return",
+                         "", &s, xxstring)) < 0 ) {
+              if (x != -3)
+                return(x);
+          }
+#endif /* MAC */
+#endif /* OS2 */
+#endif /* GEMDOS */
+          debug(F110,"download dir",s,0);
+
+#ifndef MAC
+          if (x == 2) {
+              printf("?Wildcards not allowed in directory name\n");
+              return(-9);
+          }
+#endif /* MAC */
+
+#ifdef ZFNQFP
+          if ((fnp = zfnqfp(s,TMPBUFSIZ - 1,tmpbuf))) {
+              if (fnp->fpath)
+                if ((int) strlen(fnp->fpath) > 0)
+                  s = fnp->fpath;
+          }
+          debug(F110,"download zfnqfp",s,0);
+#endif /* ZFNQFP */
+
+          ckstrncpy(line,s,LINBUFSIZ);  /* Make a safe copy */
+#ifndef MAC
+          if ((x = cmcfm()) < 0)        /* Get confirmation */
+            return(x);
+#endif /* MAC */
+
+#ifdef CK_LOGIN
+        if (isguest) {
+            /* Don't let guests change existing files */
+            printf("?This command not valid for guests\n");
+            return(-9);
+        }
+#endif /* CK_LOGIN */
+          x = strlen(s);
+
+          if (x) {
+#ifdef datageneral                     /* AOS/VS */
+              if (s[x-1] == ':')        /* homdir ends in colon, */
+                s[x-1] = NUL;           /* and "dir" doesn't like that... */
+#else
+#ifdef OS2ORUNIX                       /* Unix or K-95... */
+             if ((x < (LINBUFSIZ - 2)) && /* Add trailing dirsep */
+                 (s[x-1] != '/')) {    /* if none present.  */
+                 s[x] = '/';           /* Note that Windows path has */
+                 s[x+1] = NUL;         /* been canonicalized to forward */
+             }                         /* slashes at this point. */
+#endif /* OS2ORUNIX */
+#endif /* datageneral */
+              makestr(&dldir,s);
+          } else
+            makestr(&dldir,NULL);       /* dldir is NULL when not assigned */
+
+          return(success = 1);
+      }
+#endif /* CK_TMPDIR */
+      case XYFILY:
+        return(setdest());
+#endif /* NOXFER */
+
+#ifdef CK_CTRLZ
+      case XYFILV: {                    /* EOF */
+          extern int eofmethod;
+          if ((x = cmkey(eoftab,3,"end-of-file detection method","",
+                         xxstring)) < 0)
+            return(x);
+          if ((y = cmcfm()) < 0)
+            return(y);
+          eofmethod = x;
+          return(success = 1);
+      }
+#endif /* CK_CTRLZ */
+
+#ifndef NOXFER
+#ifdef UNIX
+      case XYFILH: {                    /* OUTPUT */
+          extern int zofbuffer, zobufsize, zofblock;
+#ifdef DYNAMIC
+          extern char * zoutbuffer;
+#endif /* DYNAMIC */
+
+          if ((x = cmkey(zoftab,nzoftab,"output file writing method","",
+                         xxstring)) < 0)
+            return(x);
+          if (x == ZOF_BUF || x == ZOF_NBUF) {
+              if ((y = cmnum("output buffer size","32768",10,&z,xxstring)) < 0)
+                return(y);
+              if (z < 1) {
+                  printf("?Bad size - %d\n", z);
+                  return(-9);
+              }
+          }
+          if ((y = cmcfm()) < 0) return(y);
+          switch (x) {
+            case ZOF_BUF:
+            case ZOF_NBUF:
+              zofbuffer = (x == ZOF_BUF);
+              zobufsize = z;
+              break;
+            case ZOF_BLK:
+            case ZOF_NBLK:
+              zofblock = (x == ZOF_BLK);
+              break;
+          }
+#ifdef DYNAMIC
+          if (zoutbuffer) free(zoutbuffer);
+          if (!(zoutbuffer = (char *)malloc(z))) {
+              printf("MEMORY ALLOCATION ERROR - FATAL\n");
+              doexit(BAD_EXIT,-1);
+          } else
+            zobufsize = z;
+#else
+          if (z <= OBUFSIZE) {
+              zobufsize = z;
+          } else {
+              printf("?Sorry, %d is too big - %d is the maximum\n",z,OBUFSIZE);
+              return(-9);
+          }
+#endif /* DYNAMIC */
+          return(success = 1);
+      }
+#endif /* UNIX */
+
+#ifdef PATTERNS
+      case XYFIBP:                      /* BINARY-PATTERN */
+      case XYFITP: {                    /* TEXT-PATTERN */
+          char * tmp[FTPATTERNS];
+          int i, n = 0;
+          while (n < FTPATTERNS) {
+              tmp[n] = NULL;
+              if ((x = cmfld("Pattern","",&s,xxstring)) < 0)
+                break;
+              ckstrncpy(line,s,LINBUFSIZ);
+              s = brstrip(line);
+              makestr(&(tmp[n++]),s);
+          }
+          if (x == -3) x = cmcfm();
+          for (i = 0; i <= n; i++) {
+              if (x > -1) {
+                  if (y == XYFIBP)
+                    makestr(&(binpatterns[i]),tmp[i]);
+                  else
+                    makestr(&(txtpatterns[i]),tmp[i]);
+              }
+              free(tmp[i]);
+          }
+          if (y == XYFIBP)              /* Null-terminate the list */
+            makestr(&(binpatterns[i]),NULL);
+          else
+            makestr(&(txtpatterns[i]),NULL);
+          return(x);
+      }
+
+      case XYFIPA:                      /* PATTERNS */
+        if ((x = setonaut(&patterns)) < 0)
+          return(x);
+        return(success = 1);
+#endif /* PATTERNS */
+#endif /* NOXFER */
+
+#ifdef UNICODE
+      case XYFILU: {                    /* UCS */
+          extern int ucsorder, ucsbom, byteorder;
+          if ((x = cmkey(ucstab,nucstab,"","",xxstring)) < 0)
+            return(x);
+          switch (x) {
+            case UCS_BYT:
+              if ((y = cmkey(botab,nbotab,
+                             "Byte order",
+                             byteorder ? "little-endian" : "big-endian",
+                             xxstring
+                             )
+                   ) < 0)
+                return(y);
+              if ((x = cmcfm()) < 0)
+                return(x);
+              ucsorder = y;
+              return(success = 1);
+            case UCS_BOM:
+              if ((y = cmkey(onoff,2,"","on",xxstring)) < 0)
+                return(y);
+              if ((x = cmcfm()) < 0)
+                return(x);
+              ucsbom = y;
+              return(success = 1);
+            default:
+              return(-2);
+          }
+      }
+#endif /* UNICODE */
+
+#ifndef datageneral
+      case XYF_INSP: {                  /* SCAN (INSPECTION) */
+          extern int filepeek, nscanfile;
+          if ((x = cmkey(onoff,2,"","on",xxstring)) < 0)
+            return(x);
+          if (y) {
+              if ((y = cmnum("How much to scan",ckitoa(SCANFILEBUF),
+                             10,&z,xxstring)) < 0)
+                return(y);
+          }
+          if ((y = cmcfm()) < 0)
+            return(y);
+#ifdef VMS
+          filepeek = 0;
+          nscanfile = 0;
+          return(success = 0);
+#else
+          filepeek = x;
+          nscanfile = z;
+          return(success = 1);
+#endif /* VMS */
+      }
+#endif /* datageneral */
+
+      case XYF_DFLT:
+        y = 0;
+#ifndef NOCSETS
+        if ((y = cmkey(fdfltab,nfdflt,"","",xxstring)) < 0)
+          return(y);
+        if (y == 7 || y == 8) {
+            if (y == 7)
+              s = fcsinfo[dcset7].keyword;
+            else
+              s = fcsinfo[dcset8].keyword;
+            if ((x = cmkey(fcstab,nfilc,"character-set",s,xxstring)) < 0)
+              return(x);
+        }
+        ckstrncpy(line,fcsinfo[x].keyword,LINBUFSIZ);
+        s = line;
+#endif /* NOCSETS */
+        if ((z = cmcfm()) < 0)
+          return(z);
+        switch (y) {
+#ifndef NOCSETS
+          case 7:
+            if (fcsinfo[x].size != 128) {
+                printf("%s - Not a 7-bit set\n",s);
+                return(-9);
+            }
+            dcset7 = x;
+            break;
+          case 8:
+            if (fcsinfo[x].size != 256) {
+                printf("%s - Not an 8-bit set\n",s);
+                return(-9);
+            }
+            dcset8 = x;
+            break;
+#endif /* NOCSETS */
+          default:
+            return(-2);
+        }
+        return(success = 1);
+
+#ifndef NOXFER
+      case 9997:                        /* FASTLOOKUPS */
+        return(success = seton(&stathack));
+#endif /* NOXFER */
+
+#ifdef UNIX
+#ifdef DYNAMIC
+      case XYF_LSIZ: {                  /* LISTSIZE */
+          int zz;
+          y = cmnum("Maximum number of filenames","",10,&x,xxstring);
+          if ((x = setnum(&zz,x,y,-1)) < 0)
+            return(x);
+          if (zsetfil(zz,3) < 0) {
+              printf("?Memory allocation failure\n");
+              return(-9);
+          }
+          return(success = 1);
+      }
+      case XYF_SSPA: {                  /* STRINGSPACE */
+          int zz;
+          y = cmnum("Number of characters for filename list",
+                    "",10,&x,xxstring);
+          if ((x = setnum(&zz,x,y,-1)) < 0)
+            return(x);
+          if (zsetfil(zz,1) < 0) {
+              printf("?Memory allocation failure\n");
+              return(-9);
+          }
+          return(success = 1);
+      }
+
+#endif /* DYNAMIC */
+#endif /* UNIX */
+
+      default:
+        printf("?unexpected file parameter\n");
+        return(-2);
+    }
+}
+
+#ifndef NOLOCAL
+#ifdef OS2
+/* MS-DOS KERMIT compatibility modes */
+int
+setmsk() {
+    if ((y = cmkey(msktab,nmsk,"MS-DOS Kermit compatibility mode",
+                    "keycodes",xxstring)) < 0) return(y);
+
+    switch ( y ) {
+#ifdef COMMENT
+      case MSK_COLOR:
+        return(seton(&mskcolors));
+#endif /* COMMENT */
+      case MSK_KEYS:
+        return(seton(&mskkeys));
+      default:                          /* Shouldn't get here. */
+        return(-2);
+    }
+}
+#endif /* OS2 */
+
+int
+settrmtyp() {
+#ifdef OS2
+#ifdef TNCODE
+    extern int ttnum;                    /* Last Telnet Terminal Type sent */
+    extern int ttnumend;                 /* Has end of list been found */
+#endif /* TNCODE */
+    if ((x = cmkey(ttyptab,nttyp,"","vt320",xxstring)) < 0)
+      return(x);
+    if ((y = cmcfm()) < 0)
+      return(y);
+    settermtype(x,1);
+#ifdef TNCODE
+    /* So we send the correct terminal name to the host if it asks for it */
+    ttnum = -1;                         /* Last Telnet Terminal Type sent */
+    ttnumend = 0;                       /* end of list not found */
+#endif /* TNCODE */
+    return(success = 1);
+#else  /* Not OS2 */
+    printf(
+"\n Sorry, this version of C-Kermit does not support the SET TERMINAL TYPE\n");
+    printf(
+" command.  Type \"help set terminal\" for further information.\n");
+#endif /* OS2 */
+    return(success = 0);
+}
+
+#ifdef CKTIDLE
+static char iactbuf[132];
+
+char *
+getiact() {
+    switch (tt_idleact) {
+      case IDLE_RET:  return("return");
+      case IDLE_EXIT: return("exit");
+      case IDLE_HANG: return("hangup");
+#ifdef TNCODE
+      case IDLE_TNOP: return("Telnet NOP");
+      case IDLE_TAYT: return("Telnet AYT");
+#endif /* TNCODE */
+
+      case IDLE_OUT: {
+          int c, k, n;
+          char * p, * q, * t;
+          k = ckstrncpy(iactbuf,"output ",132);
+          n = k;
+          q = &iactbuf[k];
+          p = tt_idlestr;
+          if (!p) p = "";
+          if (!*p) return("output (nothing)");
+          while ((c = *p++) && n < 131) {
+              c &= 0xff;
+              if (c == '\\') {
+                  if (n > 130) break;
+                  *q++ = '\\';
+                  *q++ = '\\';
+                  *q = NUL;
+                  n += 2;
+              } else if ((c > 31 && c < 127) || c > 159) {
+                  *q++ = c;
+                  *q = NUL;
+                  n++;
+              } else {
+                  if (n > (131 - 6))
+                    break;
+                  sprintf(q,"\\{%d}",c);
+                  k = strlen(q);
+                  q += k;
+                  n += k;
+                  *q = NUL;
+              }
+          }
+          *q = NUL;
+#ifdef OS2
+          k = tt_cols[VTERM];
+#else
+          k = tt_cols;
+#endif /* OS2 */
+          if (n > k - 52) {
+              n = k - 52;
+              iactbuf[n-2] = '.';
+              iactbuf[n-1] = '.';
+              iactbuf[n] = NUL;
+          }
+          return(iactbuf);
+      }
+      default: return("unknown");
+    }
+}
+#endif /* CKTIDLE */
+
+#ifndef NOCSETS
+VOID
+setlclcharset(x) int x; {
+    int i;
+    tcsl = y;                   /* Local character set */
+#ifdef OS2
+    for (i = 0; i < 4; i++) {
+        G[i].init = TRUE;
+        x = G[i].designation;
+        G[i].c1 = (x != tcsl) && cs_is_std(x);
+        x = G[i].def_designation;
+        G[i].def_c1 = (x != tcsl) && cs_is_std(x);
+    }
+#endif /* OS2 */
+}
+
+VOID
+setremcharset(x, z) int x, z; {
+    int i;
+
+#ifdef KUI
+    KuiSetProperty( KUI_TERM_REMCHARSET, (long) x, (long) z ) ;
+#endif /* KUI */
+#ifdef UNICODE
+    if (x == TX_TRANSP)
+#else /* UNICODE */
+    if (x == FC_TRANSP)
+#endif /* UNICODE */
+    {                           /* TRANSPARENT? */
+#ifndef OS2
+        tcsr = tcsl;            /* Make both sets the same */
+#else /* OS2 */
+#ifdef CKOUNI
+        tt_utf8 = 0;            /* Turn off UTF8 flag */
+        tcsr = tcsl = dec_kbd = TX_TRANSP; /* No translation */
+        tcs_transp = 1;
+
+        if (!cs_is_nrc(tcsl)) {
+            G[0].def_designation = G[0].designation = TX_ASCII;
+            G[0].init = TRUE;
+            G[0].def_c1 = G[0].c1 = FALSE;
+            G[0].size = cs94;
+            G[0].national = FALSE;
+        }
+        for (i = cs_is_nrc(tcsl) ? 0 : 1; i < 4; i++) {
+            G[i].def_designation = G[i].designation = tcsl;
+            G[i].init = TRUE;
+            G[i].def_c1 = G[i].c1 = FALSE;
+            switch (cs_size(G[i].designation)) { /* 94, 96, or 128 */
+            case 128:
+            case 96:
+                G[i].size = G[i].def_size = cs96;
+                break;
+            case 94:
+                G[i].size = G[i].def_size = cs94;
+                break;
+            default:
+                G[i].size = G[i].def_size = csmb;
+                break;
+            }
+            G[i].national = cs_is_nrc(x);
+        }
+#else /* CKOUNI */
+        tcsr = tcsl;            /* Make both sets the same */
+        for (i = 0; i < 4; i++) {
+            G[i].def_designation = G[i].designation = FC_TRANSP;
+            G[i].init = FALSE;
+            G[i].size = G[i].def_size = cs96;
+            G[i].c1 = G[i].def_c1 = FALSE;
+            G[i].rtoi = NULL;
+            G[i].itol = NULL;
+            G[i].ltoi = NULL;
+            G[i].itor = NULL;
+            G[i].national = FALSE;
+        }
+#endif /* CKOUNI */
+#endif /* OS2 */
+        return;
+    }
+#ifdef OS2
+#ifdef CKOUNI
+    else if (x == TX_UTF8) {
+        tcs_transp = 0;
+        tt_utf8 = 1;            /* Turn it on if we are UTF8 */
+        return;
+    }
+#endif /* CKOUNI */
+    else {
+        tcs_transp = 0;
+        tcsr = x;                       /* Remote character set */
+#ifdef CKOUNI
+        tt_utf8 = 0;                    /* Turn off UTF8 flag */
+#endif /* CKOUNI */
+
+        if (z == TT_GR_ALL) {
+            int i;
+#ifdef UNICODE
+            dec_kbd = x;
+#endif /* UNICODE */
+            for (i = 0; i < 4; i++) {
+                G[i].init = TRUE;
+                if ( i == 0 && !cs_is_nrc(x) ) {
+                    G[0].designation = G[0].def_designation = FC_USASCII;
+                    G[0].size = G[0].def_size = cs94;
+                    G[0].national = 1;
+                } else {
+                    G[i].def_designation = G[i].designation = x;
+                    switch (cs_size(x)) {       /* 94, 96, or 128 */
+                    case 128:
+                    case 96:
+                        G[i].size = G[i].def_size = cs96;
+                        break;
+                    case 94:
+                        G[i].size = G[i].def_size = cs94;
+                        break;
+                    default:
+                        G[i].size = G[i].def_size = csmb;
+                        break;
+                    }
+                    G[i].national = cs_is_nrc(x);
+                }
+                G[i].c1 = G[i].def_c1 = x != tcsl && cs_is_std(x);
+            }
+#ifdef UNICODE
+        } else if (z == TT_GR_KBD) {    /* Keyboard only */
+            dec_kbd = x;
+#endif /* UNICODE */
+        } else {                        /* Specific Gn */
+            G[z].def_designation = G[z].designation = x;
+            G[z].init = TRUE;
+            switch (cs_size(x)) {       /* 94, 96, or 128 */
+            case 128:
+            case 96:
+                G[z].size = G[z].def_size = cs96;
+                break;
+            case 94:
+                G[z].size = G[z].def_size = cs94;
+                break;
+            default:
+                G[z].size = G[z].def_size = csmb;
+                break;
+            }
+            G[z].c1 = G[z].def_c1 = x != tcsl && cs_is_std(x);
+            G[z].national = cs_is_nrc(x);
+        }
+    }
+#else /* not OS2 */
+    tcsr = x;                   /* Remote character set */
+#endif /* OS2 */
+}
+#endif /* NOCSETS */
+
+VOID
+setcmask(x) int x; {
+    if (x == 7) {
+        cmask = 0177;
+    } else if (x == 8) {
+        cmask = 0377;
+        parity = 0;
+    }
+#ifdef KUI      
+    KuiSetProperty(KUI_TERM_CMASK,x,0);
+#endif /* KUI */
+}
+
+#ifdef CK_AUTODL
+VOID
+setautodl(x,y) int x,y; {
+    autodl = x;
+    adl_ask = y;
+#ifdef KUI      
+    KuiSetProperty(KUI_TERM_AUTODOWNLOAD,x?(y?2:1):0,0);
+#endif /* KUI */
+}
+#endif /* CK_AUTODL */
+
+#ifdef OS2
+VOID
+seturlhl(int x) {
+    tt_url_hilite = x;
+#ifdef KUI      
+    KuiSetProperty(KUI_TERM_URL_HIGHLIGHT,x,0);
+#endif /* KUI */
+}
+
+VOID
+setaprint(int x) {
+    extern int aprint;
+    aprint = x;
+#ifdef KUI
+    KuiSetProperty(KUI_TERM_PRINTERCOPY,x,0);
+#endif /* KUI */
+}
+#endif /* OS2 */
+
+int
+settrm() {
+    int i = 0;
+#ifdef OS2
+    extern int colorreset, user_erasemode;
+#endif /* OS2 */
+    if ((y = cmkey(trmtab,ntrm,"", "",xxstring)) < 0) return(y);
+#ifdef MAC
+    printf("\n?Sorry, not implemented yet.  Please use the Settings menu.\n");
+    return(-9);
+#else
+#ifdef IKSD
+    if (inserver) {
+        if ((y = cmcfm()) < 0) return(y);
+        printf("?Sorry, command disabled.\r\n");
+        return(success = 0);
+    }
+#endif /* IKSD */
+
+    switch (y) {
+      case XYTBYT:                      /* SET TERMINAL BYTESIZE */
+        if ((y = cmnum("bytesize for terminal connection","8",10,&x,
+                       xxstring)) < 0)
+          return(y);
+        if (x != 7 && x != 8) {
+            printf("\n?The choices are 7 and 8\n");
+            return(success = 0);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        setcmask(x);
+#ifdef OS2
+        if (IS97801(tt_type_mode))
+          SNI_bitmode(x);
+#endif /* OS2 */
+        return(success = 1);
+
+      case XYTSO:                       /* SET TERMINAL LOCKING-SHIFT */
+        return(seton(&sosi));
+
+      case XYTNL:                       /* SET TERMINAL NEWLINE-MODE */
+        return(seton(&tnlm));
+
+#ifdef OS2
+      case XYTCOL:
+        if ((x = cmkey(ttycoltab,ncolors,"","terminal",xxstring)) < 0)
+          return(x);
+        else if (x == TTCOLRES) {
+            if ((y = cmkey(ttcolmodetab,ncolmode,
+                           "","default-color",xxstring)) < 0)
+              return(y);
+            if ((z = cmcfm()) < 0)
+              return(z);
+            colorreset = y;
+            return(success = 1);
+        } else if (x == TTCOLERA) {
+            if ((y = cmkey(ttcolmodetab,ncolmode,"",
+                           "current-color",xxstring)) < 0)
+              return(y);
+            if ((z = cmcfm()) < 0)
+              return(z);
+            user_erasemode = y;
+            return(success=1);
+        } else {                        /* No parse error */
+            int fg = 0, bg = 0;
+            fg = cmkey(ttyclrtab, nclrs,
+                       (x == TTCOLBOR ?
+                        "color for screen border" :
+                        "foreground color and then background color"),
+                       "lgray", xxstring);
+            if (fg < 0)
+              return(fg);
+            if (x != TTCOLBOR) {
+                if ((bg = cmkey(ttyclrtab,nclrs,
+                                "background color","blue",xxstring)) < 0)
+                  return(bg);
+            }
+            if ((y = cmcfm()) < 0)
+              return(y);
+            switch (x) {
+              case TTCOLNOR:
+                colornormal = fg | bg << 4;
+                fgi = fg & 0x08;
+                bgi = bg & 0x08;
+                break;
+              case TTCOLREV:
+                colorreverse = fg | bg << 4;
+                break;
+              case TTCOLITA:
+                coloritalic = fg | bg << 4;
+                break;
+              case TTCOLUND:
+                colorunderline = fg | bg << 4;
+                break;
+              case TTCOLGRP:
+                colorgraphic = fg | bg << 4;
+                break;
+              case TTCOLDEB:
+                colordebug = fg | bg << 4;
+                break;
+              case TTCOLSTA:
+                colorstatus = fg | bg << 4;
+                break;
+              case TTCOLHLP:
+                colorhelp = fg | bg << 4;
+                break;
+              case TTCOLBOR:
+                colorborder = fg;
+                break;
+              case TTCOLSEL:
+                colorselect = fg | bg << 4;
+                break;
+              default:
+                printf("%s - invalid\n",cmdbuf);
+                return(-9);
+                break;
+            }
+            scrninitialized[VTERM] = 0;
+            VscrnInit(VTERM);
+        }
+        return(success = 1);
+
+      case XYTCUR: {                    /* SET TERMINAL CURSOR */
+          extern int cursorena[];
+          extern int cursoron[] ;       /* Cursor state on/off       */
+          if ((x = cmkey(ttycurtab,ncursors,"","underline",xxstring)) < 0)
+            return(x);
+          if ((z = cmkey(curontab,ncuron,"","on",xxstring)) < 0)
+            return(z);
+          if ((y = cmcfm()) < 0) return(y);
+          tt_cursor = tt_cursor_usr = x;
+          if ( z == 2 ) {
+              cursorena[VTERM] = tt_cursorena_usr = 1;
+              tt_cursor_blink = 0;
+          } else {
+              cursorena[VTERM] = tt_cursorena_usr = z;/* turn cursor on/off */
+              tt_cursor_blink = 1;
+          }
+          cursoron[VTERM] = FALSE; /* Force newcursor to restore the cursor */
+          return(success = 1);
+      }
+#endif /* OS2 */
+
+      case XYTTYP:                      /* SET TERMINAL TYPE */
+        return(settrmtyp());
+
+#ifdef OS2
+      case XYTARR:                      /* SET TERMINAL ARROW-KEYS */
+        if ((x = cmkey(akmtab,2,"","",xxstring)) < 0) return(x);
+        if ((y = cmcfm()) < 0) return(y);
+        tt_arrow = x;                   /* TTK_NORM / TTK_APPL; see ckuusr.h */
+        return(success = 1);
+
+      case XYTKPD:                      /* SET TERMINAL KEYPAD-MODE */
+        if ((x = cmkey(kpmtab,2,"","",xxstring)) < 0) return(x);
+        if ((y = cmcfm()) < 0) return(y);
+        tt_keypad = x;                  /* TTK_NORM / TTK_APPL; see ckuusr.h */
+        return(success = 1);
+
+      case XYTUNX: {                    /* SET TERM UNIX-MODE (DG) */
+        extern int dgunix,dgunix_usr;
+        x = seton(&dgunix);
+        dgunix_usr = dgunix;
+        return(x);
+      }
+      case XYTKBMOD: {                  /* SET TERM KEYBOARD MODE */
+          extern int tt_kb_mode;
+          if ((x = cmkey(kbmodtab,
+                         nkbmodtab,
+                         "normal",
+                         "special keyboard mode for terminal emulation",
+                         xxstring)
+               ) < 0)
+            return(x);
+          if ((y = cmcfm()) < 0) return(y);
+          tt_kb_mode = x;
+          return(success = 1);
+      }
+
+      case XYTWRP:                      /* SET TERMINAL WRAP */
+        return(seton(&tt_wrap));
+
+      case XYSCRS:
+        if ((y = cmnum("CONNECT scrollback buffer size, lines","2000",10,&x,
+                       xxstring)) < 0)
+          return(y);
+        /* The max number of lines is the RAM  */
+        /* we can actually dedicate to a       */
+        /* scrollback buffer given the maximum */
+        /* process memory space of 512MB       */
+        if (x < 256 || x > 2000000L) {
+            printf("\n?The size must be between 256 and 2,000,000.\n");
+            return(success = 0);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        tt_scrsize[VTERM] = x;
+        VscrnInit(VTERM);
+        return(success = 1);
+#endif /* OS2 */
+
+#ifndef NOCSETS
+      case XYTCS: {                     /* SET TERMINAL CHARACTER-SET */
+        int eol;
+          /* set terminal character-set <remote> <local> */
+        if ((x = cmkey(
+#ifdef CKOUNI
+                       txrtab,ntxrtab,
+#else  /* CKOUNI */
+                       ttcstab,ntermc,
+#endif /* CKOUNI */
+                       "remote terminal character-set","",xxstring)) < 0)
+          return(x);
+
+#ifdef UNICODE
+        if (x == TX_TRANSP
+#ifdef CKOUNI
+            || x == TX_UTF8
+#endif /* CKOUNI */
+           ) {
+              if ((y = cmcfm()) < 0)    /* Confirm the command */
+                  return(y);
+#ifdef OS2
+            if ( isunicode() && x == TX_TRANSP ) {
+                /* If we are in unicode display mode then transparent
+                 * only affects the output direction.  We need to know
+                 * the actual remote character set in order to perform
+                 * the tcsr -> ucs2 translation for display.
+                 */
+                x = y = tcsl;
+            } else
+#endif /* OS2 */
+                y = x;
+        }
+#else /* UNICODE */
+        if (x == FC_TRANSP) {
+            if ((y = cmcfm()) < 0)      /* Confirm the command */
+                return(y);
+            y = x;
+        }
+#endif /* UNICODE */
+
+        /* Not transparent or UTF8, so get local set to translate it into */
+        s = "";
+#ifdef OS2
+        y = os2getcp();                 /* Default is current code page */
+        switch (y) {
+          case 437: s = "cp437"; break;
+          case 850: s = "cp850"; break;
+          case 852: s = "cp852"; break;
+          case 857: s = "cp857"; break;
+          case 858: s = "cp858"; break;
+          case 862: s = "cp862"; break;
+          case 866: s = "cp866"; break;
+          case 869: s = "cp869"; break;
+          case 1250: s = "cp1250"; break;
+          case 1251: s = "cp1251"; break;
+          case 1252: s = "cp1252"; break;
+          case 1253: s = "cp1253"; break;
+          case 1254: s = "cp1254"; break;
+          case 1255: s = "cp1255"; break;
+          case 1256: s = "cp1256"; break;
+          case 1257: s = "cp1257"; break;
+          case 1258: s = "cp1258"; break;
+        }
+#ifdef PCFONTS
+/*
+   If the user has loaded a font with SET TERMINAL FONT then we want
+   to change the default code page to the font that was loaded.
+*/
+        if (tt_font != TTF_ROM) {
+            for (y = 0; y < ntermfont; y++ ) {
+                if (term_font[y].kwval == tt_font) {
+                    s = term_font[y].kwd;
+                    break;
+                }
+            }
+        }
+#endif /* PCFONTS */
+#else  /* Not K95... */
+        s = fcsinfo[fcharset].keyword;
+#endif /* OS2 */
+
+        if ((y = cmkey(
+#ifdef CKOUNI
+                       txrtab,ntxrtab,
+#else /* CKOUNI */
+                       ttcstab,ntermc,
+#endif /* CKOUNI */
+                       "local character-set",s,xxstring)) < 0)
+          return(y);
+
+#ifdef UNICODE
+        if (y == TX_UTF8) {
+            printf("?UTF8 may not be used as a local character set.\r\n");
+            return(-9);
+        }
+#endif /* UNICODE */
+#ifdef OS2
+        if ((z = cmkey(graphsettab,ngraphset,
+                       "DEC VT intermediate graphic set","all",xxstring)) < 0)
+            return(z);
+#endif /* OS2 */
+        if ((eol = cmcfm()) < 0)
+            return(eol); /* Confirm the command */
+
+        /* End of command parsing - actions begin */
+        setlclcharset(y);
+        setremcharset(x,z);
+        return(success = 1);
+      }
+#endif /* NOCSETS */
+
+#ifndef NOCSETS
+      case XYTLCS:                      /* SET TERMINAL LOCAL-CHARACTER-SET */
+        /* set terminal character-set <local> */
+        s = getdcset();                 /* Get display character-set name */
+        if ((y = cmkey(
+#ifdef CKOUNI
+                       txrtab,ntxrtab,
+#else /* CKOUNI */
+                       fcstab,nfilc,
+#endif /* CKOUNI */
+                       "local character-set",s,xxstring)) < 0)
+          return(y);
+
+#ifdef UNICODE
+          if (y == TX_UTF8) {
+              printf("?UTF8 may not be used as a local character set.\r\n");
+              return(-9);
+          }
+#endif /* UNICODE */
+          if ((z = cmcfm()) < 0) return(z); /* Confirm the command */
+
+          /* End of command parsing - action begins */
+
+        setlclcharset(y);
+        return(success = 1);
+#endif /* NOCSETS */
+
+#ifndef NOCSETS
+#ifdef UNICODE
+      case XYTUNI:                      /* SET TERMINAL UNICODE */
+        return(seton(&tt_unicode));
+#endif /* UNICODE */
+
+      case XYTRCS:                      /* SET TERMINAL REMOTE-CHARACTER-SET */
+        /* set terminal character-set <remote> <Graphic-set> */
+        if ((x = cmkey(
+#ifdef CKOUNI
+                txrtab, ntxrtab,
+#else /* CKOUNI */
+                ttcstab,ntermc,
+#endif /* CKOUNI */
+                       "remote terminal character-set","",xxstring)) < 0)
+          return(x);
+
+#ifdef UNICODE
+        if (x == TX_TRANSP
+#ifdef CKOUNI
+            || x == TX_UTF8
+#endif /* CKOUNI */
+           ) {
+              if ((y = cmcfm()) < 0)    /* Confirm the command */
+                  return(y);
+#ifdef OS2
+            if ( isunicode() && x == TX_TRANSP ) {
+                /* If we are in unicode display mode then transparent
+                 * only affects the output direction.  We need to know
+                 * the actual remote character set in order to perform
+                 * the tcsr -> ucs2 translation for display.
+                 */
+                x = tcsl;
+            }
+#endif /* OS2 */
+        }
+#else /* UNICODE */
+        if (x == FC_TRANSP) {
+          if ((y = cmcfm()) < 0)        /* Confirm the command */
+            return(y);
+        }
+#endif /* UNICODE */
+        else {
+#ifdef OS2
+          if ((z = cmkey(graphsettab,ngraphset,
+                      "DEC VT intermediate graphic set","all",xxstring)) < 0)
+            return(z);
+#endif /* OS2 */
+          if ((y = cmcfm()) < 0)        /* Confirm the command */
+            return(y);
+        }
+        /* Command parsing ends here */
+
+        setremcharset(x,z);
+        return(success = 1);
+#endif /* NOCSETS */
+
+      case XYTEC:                       /* SET TERMINAL ECHO */
+        if ((x = cmkey(rltab,nrlt,"which side echos during CONNECT",
+                       "remote", xxstring)) < 0) return(x);
+        if ((y = cmcfm()) < 0) return(y);
+#ifdef NETCONN
+        oldplex = x;
+#endif /* NETCONN */
+        duplex = x;
+        return(success = 1);
+
+      case XYTESC:                      /* SET TERM ESC */
+        if ((x = cmkey(nabltab,nnabltab,"","enabled",xxstring)) < 0)
+          return(x);
+        if ((y = cmcfm()) < 0) return(y);
+        tt_escape = x;
+        return(1);
+
+      case XYTCRD:                      /* SET TERMINAL CR-DISPLAY */
+        if ((x = cmkey(crdtab,2,"", "normal", xxstring)) < 0) return(x);
+        if ((y = cmcfm()) < 0) return(y);
+        tt_crd = x;
+        return(success = 1);
+
+#ifdef OS2
+      case XYTANS: {                    /* SET TERMINAL ANSWERBACK */
+/*
+  NOTE: We let them enable and disable the answerback sequence, but we
+  do NOT let them change it, and we definitely do not let the host set it.
+  This is a security feature.
+
+  As of 1.1.8 we allow the SET TERM ANSWERBACK MESSAGE <string> to be
+  used just as MS-DOS Kermit does.  C0 and C1 controls as well as DEL
+  are not allowed to be used as characters.  They are translated to
+  underscore.  This may not be set by APC.
+*/
+          if ((x = cmkey(anbktab,nansbk,"", "off", xxstring)) < 0)
+            return(x);
+          if (x < 2) {
+              if ((y = cmcfm()) < 0)
+                return(y);
+              tt_answer = x;
+              return(success = 1);
+          } else if ( x == 2 || x == 3) {
+              int len = 0;
+              extern int safeanswerbk;
+              extern char useranswerbk[];
+              if ((y = cmtxt("Answerback extension","",&s,xxstring)) < 0)
+                return(y);
+              if (apcactive == APC_LOCAL ||
+                  (apcactive == APC_REMOTE && !(apcstatus & APC_UNCH)))
+                return(success = 0);
+              len = strlen(s);
+              if (x == 2) {
+                  /* Safe Answerback's don't have C0/C1 chars */
+                  for (z = 0; z < len; z++) {
+                      if ((s[z] & 0x7F) <= SP || (s[z] & 0x7F) == DEL)
+                        useranswerbk[z] = '_';
+                      else
+                        useranswerbk[z] = s[z];
+                  }
+                  useranswerbk[z] = '\0';
+                  safeanswerbk = 1 ;    /* TRUE */
+              } else {
+                  ckstrncpy(useranswerbk,s,60); /* (see ckocon.c) */
+                  safeanswerbk = 0;     /* FALSE */
+              }
+              updanswerbk();
+              return(success = 1);
+          } else
+            return(success = 0);
+      }
+#endif /* OS2 */
+
+#ifdef CK_APC
+      case XYTAPC:
+        if ((y = cmkey(apctab,napctab,
+                       "application program command execution","",
+                       xxstring)) < 0)
+          return(y);
+        if ((x = cmcfm()) < 0)
+          return(x);
+        if (apcactive == APC_LOCAL ||
+            (apcactive == APC_REMOTE && !(apcstatus & APC_UNCH)))
+          return(success = 0);
+        apcstatus = y;
+        return(success = 1);
+
+#ifdef CK_AUTODL
+      case XYTAUTODL:                   /* AUTODOWNLOAD */
+        if ((y = cmkey(adltab,nadltab,"Auto-download options","",
+                       xxstring)) < 0)
+          return(y);
+        switch (y) {
+          case TAD_ON:
+          case TAD_OFF:
+            if ((x = cmcfm()) < 0)
+              return(x);
+            setautodl(y,0);
+            break;
+          case TAD_ASK:
+            if ((x = cmcfm()) < 0)
+              return(x);
+            setautodl(TAD_ON,1);
+            break;
+          case TAD_ERR:
+            if ((y = cmkey(adlerrtab,nadlerrtab,"","", xxstring)) < 0)
+              return(y);
+            if ((x = cmcfm()) < 0)
+              return(x);
+            adl_err = y;
+            break;
+#ifdef OS2
+          case TAD_K:
+            if ((y = cmkey(adlxtab,nadlxtab,"","", xxstring)) < 0)
+              return(y);
+            switch (y) {
+              case TAD_X_C0:
+                if ((y = cmkey(adlc0tab,nadlc0tab,"",
+                               "processed-by-emulator",xxstring)) < 0)
+                  return(y);
+                if ((x = cmcfm()) < 0)
+                  return(x);
+                adl_kc0 = y;
+                break;
+              case TAD_X_DETECT:
+                if ((y = cmkey(adldtab,nadldtab,"","packet",xxstring)) < 0)
+                  return(y);
+                if ((x = cmcfm()) < 0)
+                  return(x);
+                adl_kmode = y;
+                break;
+              case TAD_X_STR:
+                if ((y = cmtxt("Kermit start string","KERMIT READY TO SEND...",
+                               &s,xxstring)) < 0)
+                  return(y);
+                free(adl_kstr);
+                adl_kstr = strdup(s);
+                break;
+            }
+            break;
+
+          case TAD_Z:
+            if ((y = cmkey(adlxtab,nadlxtab,"","",xxstring)) < 0)
+              return(y);
+            switch (y) {
+              case TAD_X_C0:
+                if ((y = cmkey(adlc0tab,nadlc0tab,"",
+                               "processed-by-emulator",xxstring)) < 0)
+                  return(y);
+                if ((x = cmcfm()) < 0)
+                  return(x);
+                adl_zc0 = y;
+                break;
+              case TAD_X_DETECT:
+                if ((y = cmkey(adldtab,nadldtab,"","packet",xxstring)) < 0)
+                  return(y);
+                if ((x = cmcfm()) < 0)
+                  return(x);
+                adl_zmode = y;
+                break;
+              case TAD_X_STR:
+                if ((y = cmtxt("","rz\\{13}",&s,xxstring)) < 0)
+                  return(y);
+                free(adl_zstr);
+                adl_zstr = strdup(s);
+                break;
+            }
+            break;
+#endif /* OS2 */
+        }
+        return(success = 1);
+
+#endif /* CK_AUTODL */
+#endif /* CK_APC */
+
+#ifdef OS2
+      case XYTBEL:
+        return(success = setbell());
+
+      case XYTMBEL:                     /* MARGIN-BELL */
+        if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y);
+        if (y) {                        /* ON */
+            if ((z = cmnum("Column at which to set margin bell",
+                           "72",10,&x,xxstring)) < 0)
+              return(z);
+        }
+        if ((z = cmcfm()) < 0) return(z);
+        marginbell = y;
+        marginbellcol = x;
+        return(success = 1);
+#endif /* OS2 */
+
+#ifdef CKTIDLE
+      case XYTIDLE:                     /* IDLE-SEND */
+      case XYTITMO:                     /* IDLE-TIMEOUT */
+        if ((z = cmnum("seconds of idle time to wait, or 0 to disable",
+                       "0",10,&x,xxstring)) < 0)
+          return(z);
+        if (y == XYTIDLE) {
+            if ((y = cmtxt("string to send, may contain kverbs and variables",
+                           "\\v(newline)",&s,xxstring)) < 0)
+              return(y);
+            tt_idlesnd_tmo = x;         /* (old) */
+            tt_idlelimit = x;           /* (new) */
+            makestr(&tt_idlestr,brstrip(s)); /* (new) */
+            tt_idlesnd_str = tt_idlestr; /* (old) */
+            tt_idleact = IDLE_OUT;      /* (new) */
+        } else {
+            if ((y = cmcfm()) < 0)
+              return(y);
+            tt_idlelimit = x;
+        }
+#ifdef OS2
+        puterror(VTERM);
+#endif /* OS2 */
+        return(success = 1);
+
+      case XYTIACT: {                   /* SET TERM IDLE-ACTION */
+          if ((y = cmkey(idlacts,nidlacts,"","",xxstring)) < 0)
+            return(y);
+          if (y == IDLE_OUT) {
+              if ((x = cmtxt("string to send, may contain kverbs and variables"
+                             , "\\v(newline)",&s,xxstring)) < 0)
+                return(x);
+              makestr(&tt_idlestr,brstrip(s)); /* (new) */
+              tt_idlesnd_str = tt_idlestr; /* (old) */
+          } else {
+              if ((x = cmcfm()) < 0)
+                return(x);
+          }
+          tt_idleact = y;
+          return(success = 1);
+      }
+#endif /* CKTIDLE */
+
+      case XYTDEB:                      /* TERMINAL DEBUG */
+        y = seton(&x);                  /* Go parse ON or OFF */
+        if (y > 0)                      /* Command succeeded? */
+          setdebses(x);
+        return(y);
+
+#ifdef OS2
+      case XYTASCRL:                    /* SET TERMINAL AUTOSCROLL */
+          y = seton(&autoscroll);
+          return(y);
+
+      case XYTAPAGE:                    /* SET TERMINAL AUTOPAGE */
+          y = seton(&wy_autopage);
+          return(y);
+
+      case XYTROL:                      /* SET TERMINAL ROLL */
+        if ((y = cmkey(rolltab,nroll,"scrollback mode","insert",xxstring))<0)
+         return(y);
+       if (y == TTR_KEYS) {
+           if ((x = cmkey(rollkeytab,nrollkey,"","send",xxstring))<0)
+             return(x);
+           if ((z = cmcfm()) < 0) return(z);
+           tt_rkeys[VTERM] = x;
+       } else {
+           if ((x = cmcfm()) < 0) return(x);
+           tt_roll[VTERM] = y;
+       }
+        return(success = 1);
+
+      case XYTCTS:                      /* SET TERMINAL TRANSMIT-TIMEOUT */
+        y = cmnum("Maximum seconds to allow CTS off during CONNECT",
+                  "5",10,&x,xxstring);
+        return(setnum(&tt_ctstmo,x,y,10000));
+
+      case XYTCPG: {                    /* SET TERMINAL CODE-PAGE */
+        int i;
+        int cp = -1;
+        y = cmnum("PC code page to use during terminal emulation",
+                  ckitoa(os2getcp()),10,&x,xxstring);
+        if ((x = setnum(&cp,x,y,11000)) < 0) return(x);
+        if (os2setcp(cp) != 1) {
+#ifdef NT
+            if (isWin95())
+              printf(
+                 "Sorry, Windows 95 does not support code page switching\n");
+            else
+#endif /* NT */
+              printf(
+                 "Sorry, %d is not a valid code page for this system.\n",cp);
+            return(-9);
+        }
+    /* Force the terminal character-sets conversions to be updated */
+        for ( i = 0; i < 4; i++ )
+          G[i].init = TRUE;
+        return(1);
+    }
+
+      case XYTPAC:                      /* SET TERMINAL OUTPUT-PACING */
+        y = cmnum(
+           "Pause between sending each character during CONNECT, milliseconds",
+                  "-1",10,&x,xxstring);
+        return(setnum(&tt_pacing,x,y,10000));
+
+#ifdef OS2MOUSE
+      case XYTMOU: {                    /* SET TERMINAL MOUSE */
+          int old_mou = tt_mouse;
+          if ((x = seton(&tt_mouse)) < 0)
+            return(x);
+          if (tt_mouse != old_mou)
+            if (tt_mouse)
+              os2_mouseon();
+            else
+              os2_mouseoff();
+          return(1);
+      }
+#endif /* OS2MOUSE */
+#endif /* OS2 */
+
+      case XYTWID: {
+          if ((y = cmnum(
+#ifdef OS2
+                         "number of columns in display window during CONNECT",
+#else
+                         "number of columns on your screen",
+#endif /* OS2 */
+                         "80",10,&x,xxstring)) < 0)
+            return(y);
+          if ((y = cmcfm()) < 0) return(y);
+#ifdef OS2
+          return(success = os2_settermwidth(x));
+#else  /* Not OS/2 */
+          tt_cols = x;
+          return(success = 1);
+#endif /* OS2 */
+      }
+
+      case XYTHIG:
+        if ((y = cmnum(
+#ifdef OS2
+ "number of rows in display window during CONNECT, not including status line",
+ tt_status[VTERM]?"24":"25",
+#else
+ "24","number of rows on your screen",
+#endif /* OS2 */
+                       10,&x,xxstring)) < 0)
+          return(y);
+        if ((y = cmcfm()) < 0) return(y);
+
+#ifdef OS2
+        return (success = os2_settermheight(x));
+#else  /* Not OS/2 */
+        tt_rows = x;
+        return(success = 1);
+#endif /* OS2 */
+
+#ifdef OS2
+      case XYTPRN: {                    /* Print Mode */
+          extern bool xprint, aprint, cprint, uprint;
+          if ((y = cmkey(prnmtab,nprnmtab,"","off", xxstring)) < 0) return(y);
+          if ((x = cmcfm()) < 0) return(x);
+          switch (y) {
+            case 0:
+              if (cprint || uprint || aprint || xprint)
+                printeroff();
+              cprint = xprint = uprint = 0;
+              setaprint(0);
+              break;
+            case 1:
+              if (!(cprint || uprint || aprint || xprint))
+                printeron();
+              setaprint(1);
+              cprint = xprint = uprint = 0;
+              break;
+            case 2:
+              if (!(cprint || uprint || aprint || xprint))
+                printeron();
+              cprint = 1;
+              setaprint(0);
+             xprint = uprint = 0;
+              break;
+            case 3:
+              if (!(cprint || uprint || aprint || xprint))
+                printeron();
+              uprint = 1;
+              setaprint(0);
+              xprint = cprint = 0;
+              break;
+          }
+          return(1);
+      }
+#else
+#ifdef XPRINT
+      case XYTPRN: {
+          extern int tt_print;
+          if ((x = seton(&tt_print)) < 0)
+            return(x);
+          return(success = 1);
+      }
+#endif /* XPRINT */
+#endif /* OS2 */
+
+#ifdef OS2
+      case XYTSCNM: {
+          extern int decscnm, decscnm_usr;
+          if ((y = cmkey(normrev,4,"",
+                         decscnm_usr?"reverse":"normal",
+                         xxstring)
+               ) < 0)
+            return(y);
+          if ((x = cmcfm()) < 0) return(x);
+          decscnm_usr = y;
+          if (decscnm != decscnm_usr)
+            flipscreen(VTERM);
+          return(1);
+    }
+    case XYTOPTI:
+        if ((y = cmkey(onoff,2,"",tt_diff_upd?"on":"off",
+                        xxstring)) < 0) return(y);
+        if ((x = cmcfm()) < 0) return(x);
+        tt_diff_upd = y;
+        return(1);
+    case XYTUPD: {
+        int mode, delay;
+        if ((mode = cmkey(scrnupd,nscrnupd,"","fast",xxstring)) < 0) {
+            return(mode);
+        } else {
+            y = cmnum(
+            "Pause between FAST screen updates in CONNECT mode, milliseconds",
+                      "100",10,&x,xxstring
+                      );
+            if (x < 0 || x > 1000 ) {
+                printf(
+            "\n?The update rate must be between 0 and 1000 milliseconds.\n"
+                       );
+                return(success = 0);
+            }
+            if ((y = cmcfm()) < 0) return(y);
+
+            updmode = tt_updmode = mode;
+            return(setnum(&tt_update,x,y,10000));
+        }
+    }
+    case XYTCTRL:
+          if ((x = cmkey(termctrl,ntermctrl,"","7",xxstring)) < 0) {
+              return(x);
+          } else {
+              if ((y = cmcfm()) < 0)
+                  return(y);
+              switch ( x ) {
+              case 8:
+                  send_c1 = send_c1_usr = TRUE;
+                  break;
+              case 7:
+              default:
+                  send_c1 = send_c1_usr = FALSE;
+                  break;
+              }
+          }
+          return(success = TRUE);
+          break;
+
+#ifdef PCFONTS
+      case XYTFON:
+        if ( !IsOS2FullScreen() ) {
+            printf(
+        "\n?SET TERMINAL FONT is only supported in Full Screen sessions.\n");
+            return(success = FALSE);
+        }
+
+        if ((x = cmkey(term_font,ntermfont,"","default",xxstring)) < 0) {
+            return(x);
+        } else {
+            if ((y = cmcfm()) < 0) return(y);
+            if ( !os2LoadPCFonts() ) {
+                tt_font = x;
+                return(success = TRUE);
+            } else {
+                printf(
+      "\n?PCFONTS.DLL is not available in CKERMIT executable directory.\n");
+                return(success = FALSE);
+            }
+        }
+        break;
+#else /* PCFONTS */
+#ifdef NT
+#ifdef KUI
+    case XYTFON:
+        return(setguifont());           /* ckuus3.c */
+#endif /* KUI */
+#endif /* NT */
+#endif /* PCFONTS */
+
+      case XYTVCH: {
+          extern int pheight, marginbot, cmd_rows, cmd_cols;
+          if ((x = cmkey(tvctab,ntvctab,"",isWin95()?"win95-safe":"enabled",
+                         xxstring)) < 0)
+            return(x);
+          if ((y = cmcfm()) < 0) return(y);
+#ifndef KUI
+          if (x != tt_modechg) {
+              switch (x) {
+                case TVC_DIS:
+                  /* When disabled the heights of all of the virtual screens */
+                  /* must be equal to the physical height of the console     */
+                  /* window and may not be changed.                          */
+                  /* The width of the window may not be altered.             */
+                  tt_modechg = TVC_ENA;                 /* Temporary */
+                  if (marginbot > pheight-(tt_status[VTERM]?1:0))
+                    marginbot = pheight-(tt_status[VTERM]?1:0);
+                  tt_szchng[VCMD] = 1 ;
+                  tt_rows[VCMD] = pheight;
+                  VscrnInit(VCMD);
+                  SetCols(VCMD);
+                  cmd_rows = y;
+
+                  tt_szchng[VTERM] = 2 ;
+                  tt_rows[VTERM] = pheight - (tt_status[VTERM]?1:0);
+                  VscrnInit(VTERM);
+
+                  break;
+
+                case TVC_ENA:
+                  /* When enabled the physical height of the console windows */
+                  /* should be adjusted to the height of the virtual screen  */
+                  /* The width may be set to anything.                       */
+                  /* nothing to do                                           */
+                  break;
+
+              case TVC_W95:
+                  /* Win95-safe mode allows the physical height to change    */
+                  /* but restricts it to a width of 80 and a height equal to */
+                  /* 25, 43, or 50.  Must be adjusted now.                   */
+                  /* The virtual heights must be equal to the above.         */
+                  if (pheight != 25 && pheight != 43 && pheight != 50) {
+                      if (pheight < 25)
+                        y = 25;
+                      else if (pheight < 43)
+                        y = 43;
+                      else
+                        y = 50;
+                  } else
+                    y = pheight;
+
+                  tt_modechg = TVC_ENA; /* Temporary */
+
+                  tt_szchng[VCMD] = 1;
+                  tt_rows[VCMD] = y;
+                  tt_cols[VCMD] = 80;
+                  VscrnInit(VCMD);
+                  SetCols(VCMD);
+                  cmd_rows = y;
+                  cmd_cols = 80;
+
+                  marginbot = y-(tt_status[VTERM]?1:0);
+                  tt_szchng[VTERM] = 2;
+                  tt_rows[VTERM] = y - (tt_status[VTERM]?1:0);
+                  tt_cols[VTERM] = 80;
+                  VscrnInit(VTERM);
+                  break;
+              }
+              tt_modechg = x;
+          }
+          return(success = 1);
+#else
+          return(success = 0);
+#endif /* KUI */
+      }
+      case XYTSTAT: {
+          extern int marginbot;
+          if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y);
+          if ((x = cmcfm()) < 0) return(x);
+          if (y != tt_status[VTERM] || y != tt_status_usr[VTERM]) {
+              /* Might need to fixup the margins */
+              if ( marginbot == VscrnGetHeight(VTERM)-(tt_status[VTERM]?1:0) )
+                if (y) {
+                    marginbot--;
+                } else {
+                    marginbot++;
+                }
+              tt_status_usr[VTERM] = tt_status[VTERM] = y;
+              if (y) {
+                    tt_szchng[VTERM] = 2;
+                    tt_rows[VTERM]--;
+                    VscrnInit(VTERM);  /* Height set here */
+#ifdef TNCODE
+                    if (TELOPT_ME(TELOPT_NAWS))
+                      tn_snaws();
+#endif /* TNCODE */
+#ifdef RLOGCODE
+                    if (TELOPT_ME(TELOPT_NAWS))
+                      rlog_naws();
+#endif /* RLOGCODE */
+#ifdef SSHBUILTIN
+                    if (TELOPT_ME(TELOPT_NAWS))
+                      ssh_snaws();
+#endif /* SSHBUILTIN */
+              } else {
+                  tt_szchng[VTERM] = 1;
+                  tt_rows[VTERM]++;
+                  VscrnInit(VTERM);     /* Height set here */
+#ifdef TNCODE
+                  if (TELOPT_ME(TELOPT_NAWS))
+                    tn_snaws();
+#endif /* TNCODE */
+#ifdef RLOGCODE
+                  if (TELOPT_ME(TELOPT_NAWS))
+                    rlog_naws();
+#endif /* RLOGCODE */
+#ifdef SSHBUILTIN
+                  if (TELOPT_ME(TELOPT_NAWS))
+                    ssh_snaws();
+#endif /* SSHBUILTIN */
+              }
+          }
+          return(1);
+      }
+#endif /* OS2 */
+
+#ifdef NT
+      case XYTATTBUG:
+        if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y);
+        if ((x = cmcfm()) < 0) return(x);
+        tt_attr_bug = y;
+        return(1);
+#endif /* NT */
+
+#ifdef OS2
+      case XYTSGRC:
+        if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y);
+        if ((x = cmcfm()) < 0) return(x);
+        sgrcolors = y;
+        return(1);
+
+      case XYTSEND:
+          if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y);
+          if ((x = cmcfm()) < 0) return(x);
+          tt_senddata = y;
+          return(1);
+
+      case XYTSEOB:
+          if ((y = cmkey(ttyseobtab,2,"","us_cr",xxstring)) < 0) return(y);
+          if ((x = cmcfm()) < 0) return(x);
+          wy_blockend = y;
+          return(1);
+
+      case XYTURLHI: {
+          int done = 0, attr = VT_CHAR_ATTR_NORMAL;
+
+          if ((x = cmkey(onoff,2,"","on",xxstring)) < 0)
+            return(x);
+          if (x) {
+              z = 0;
+              while (!done) {
+                  if ((y = cmkey(ttyprotab,nprotect,"",
+                                 z?"done":"reverse",xxstring)) < 0)
+                    return(y);
+                  switch (y) {
+                    case TTATTDONE:
+                      done = TRUE;
+                      break;
+                    case TTATTBLI:
+                      attr |= VT_CHAR_ATTR_BLINK;
+                      break;
+                    case TTATTREV:
+                      attr |= VT_CHAR_ATTR_REVERSE;
+                      break;
+                    case TTATTITA:
+                      attr |= VT_CHAR_ATTR_ITALIC;
+                      break;
+                    case TTATTUND:
+                      attr |= VT_CHAR_ATTR_UNDERLINE;
+                      break;
+                    case TTATTBLD:
+                      attr |= VT_CHAR_ATTR_BOLD;
+                      break;
+                    case TTATTDIM:
+                      attr |= VT_CHAR_ATTR_DIM;
+                      break;
+                    case TTATTINV:
+                      attr |= VT_CHAR_ATTR_INVISIBLE;
+                      break;
+                    case TTATTNOR:
+                      break;
+                  }
+                  z = 1;                /* One attribute has been chosen */
+              }
+          }
+          if ((z = cmcfm()) < 0) return(z);
+          seturlhl(x);
+          if (x)
+            tt_url_hilite_attr = attr;
+          return(1);
+      }
+      case XYTATTR:
+        if ((x = cmkey(ttyattrtab,nattrib,"","underline",xxstring)) < 0)
+          return(x);
+        switch (x) {
+          case TTATTBLI:
+            if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y);
+            if ((x = cmcfm()) < 0) return(x);
+            trueblink = y;
+#ifndef KUI
+            if ( !trueblink && trueunderline ) {
+                trueunderline = 0;
+                printf("Warning: Underline being simulated by color.\n");
+            }
+
+#endif /* KUI */
+            break;
+
+          case TTATTDIM:
+            if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y);
+            if ((x = cmcfm()) < 0) return(x);
+            truedim = y;
+            break;
+
+          case TTATTREV:
+            if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y);
+            if ((x = cmcfm()) < 0) return(x);
+            truereverse = y;
+            break;
+
+          case TTATTUND:
+            if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y);
+            if ((x = cmcfm()) < 0) return(x);
+            trueunderline = y;
+#ifndef KUI
+            if (!trueblink && trueunderline) {
+                trueblink = 1;
+                printf("Warning: True blink mode is active.\n");
+            }
+#endif /* KUI */
+            break;
+
+          case TTATTITA:
+              if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y);
+              if ((x = cmcfm()) < 0) return(x);
+              trueitalic = y;
+            break;
+
+          case TTATTPRO: {      /* Set default Protected Character attribute */
+              extern vtattrib WPattrib;    /* current WP Mode Attrib */
+              extern vtattrib defWPattrib; /* default WP Mode Attrib */
+              vtattrib wpa = {0,0,0,0,0,1,0,0,0,0,0};   /* Protected */
+              int done = 0;
+
+              x = 0;
+              while (!done) {
+                  if ((y = cmkey(ttyprotab,nprotect,"",
+                                 x?"done":"dim",xxstring)) < 0)
+                    return(y);
+                  switch (y) {
+                    case TTATTNOR:
+                      break;
+                    case TTATTBLI:      /* Blinking doesn't work */
+                      wpa.blinking = TRUE;
+                      break;
+                    case TTATTREV:
+                      wpa.reversed = TRUE;
+                      break;
+                    case TTATTITA:
+                      wpa.italic = TRUE;
+                      break;
+                    case TTATTUND:
+                      wpa.underlined = TRUE;
+                      break;
+                    case TTATTBLD:
+                      wpa.bold = TRUE;
+                      break;
+                    case TTATTDIM:
+                      wpa.dim = TRUE;
+                      break;
+                    case TTATTINV:
+                      wpa.invisible = TRUE ;
+                      break;
+                    case TTATTDONE:
+                      done = TRUE;
+                      break;
+                  }
+                  x = 1;                /* One attribute has been chosen */
+              }
+              if ((x = cmcfm()) < 0) return(x);
+              WPattrib = defWPattrib = wpa;
+              break;
+          }
+        }
+        return(1);
+
+      case XYTKEY: {                    /* SET TERMINAL KEY */
+          int t, x, y;
+          int clear = 0, deflt = 0;
+          int confirmed = 0;
+          int flag = 0;
+          int kc = -1;                  /* Key code */
+          int litstr = 0;               /* Literal String? */
+          char *s = NULL;               /* Key binding */
+#ifndef NOKVERBS
+          char *p = NULL;               /* Worker */
+#endif /* NOKVERBS */
+          con_event defevt;
+          extern int os2gks;
+          extern int mskkeys;
+          extern int initvik;
+          struct FDB kw,sw,nu,cm;
+
+          defevt.type = error;
+
+          if ((t = cmkey(ttkeytab,nttkey,"","",xxstring)) < 0)
+            return(t);
+          cmfdbi(&nu,                   /* First FDB - command switches */
+                 _CMNUM,                /* fcode */
+                 "/literal, keycode, or action",
+                 "",                    /* default */
+                 "",                    /* addtl string data */
+                 10,                    /* addtl numeric data 1: radix */
+                 0,                     /* addtl numeric data 2: 0 */
+                 xxstring,              /* Processing function */
+                 NULL,                  /* Keyword table */
+                 &sw                    /* Pointer to next FDB */
+                 );                     /*  */
+          cmfdbi(&sw,                   /* Second FDB - switches */
+                 _CMKEY,                /* fcode */
+                 "",
+                 "",                    /* default */
+                 "",                    /* addtl string data */
+                 nstrmswitab,           /* addtl numeric data 1: tbl size */
+                 4,                     /* addtl numeric data 2: 4 = cmswi */
+                 xxstring,              /* Processing function */
+                 strmswitab,            /* Keyword table */
+                 &kw                    /* Pointer to next FDB */
+                 );
+          cmfdbi(&kw,                   /* Third FDB - command switches */
+                 _CMKEY,                /* fcode */
+                 "/literal, keycode, or action",
+                 "",                    /* default */
+                 "",                    /* addtl string data */
+                 nstrmkeytab,           /* addtl numeric data 1: tbl size */
+                 0,                     /* addtl numeric data 2 */
+                 xxstring,              /* Processing function */
+                 strmkeytab,            /* Keyword table */
+                 &cm                    /* Pointer to next FDB */
+                 );
+          cmfdbi(&cm,                   /* Final FDB - Confirmation */
+                 _CMCFM,                /* fcode */
+                 "",
+                 "",                    /* default */
+                 "",                    /* addtl string data */
+                 0,                     /* addtl numeric data 1: tbl size */
+                 0,                     /* addtl numeric data 2: 4 = cmswi */
+                 xxstring,              /* Processing function */
+                 NULL,                  /* Keyword table */
+                 NULL                   /* Pointer to next FDB */
+                 );
+          while (kc < 0) {
+              x = cmfdb(&nu);           /* Parse something */
+              if (x < 0)
+                return(x);
+
+              switch (cmresult.fcode) {
+                case _CMCFM:
+                  printf(" Press key to be defined: ");
+                  conbin((char)escape); /* Put terminal in binary mode */
+                  os2gks = 0;           /* Turn off Kverb preprocessing */
+                  kc = congks(0);       /* Get character or scan code */
+                  os2gks = 1;           /* Turn on Kverb preprocessing */
+                  concb((char)escape);  /* Restore terminal to cbreak mode */
+                  if (kc < 0) {         /* Check for error */
+                      printf("?Error reading key\n");
+                      return(0);
+                  }
+                  shokeycode(kc,t);     /* Show current definition */
+                  flag = 1;             /* Remember it's a multiline command */
+                  break;
+                case _CMNUM:
+                  kc = cmresult.nresult;
+                  break;
+                case _CMKEY:
+                  if (cmresult.fdbaddr == &sw) { /* Switch */
+                      if (cmresult.nresult == 0)
+                        litstr = 1;
+                  } else if (cmresult.fdbaddr == &kw) { /* Keyword */
+                      if (cmresult.nresult == 0)
+                        clear = 1;
+                      else
+                        deflt = 1;
+                      if ((x = cmcfm()) < 0)
+                        return(x);
+                      if (clear)
+                        clearkeymap(t);
+                      else if (deflt)
+                        defaultkeymap(t);
+                      initvik = 1;
+                      return(1);
+                  }
+              }
+          }
+
+    /* Normal SET TERMINAL KEY <terminal> <scancode> <value> command... */
+
+          if (mskkeys)
+            kc = msktock(kc);
+
+          if (kc < 0 || kc >= KMSIZE) {
+              printf("?key code must be between 0 and %d\n", KMSIZE - 1);
+              return(-9);
+          }
+          if (kc == escape) {
+              printf("Sorry, %d is the CONNECT-mode escape character\n",kc);
+              return(-9);
+          }
+          wideresult = -1;
+          if (flag) {
+              cmsavp(psave,PROMPTL);
+              cmsetp(" Enter new definition: ");
+              cmini(ckxech);
+          }
+        def_again:
+          if (flag) prompt(NULL);
+          if ((y = cmtxt("key definition,\n\
+ or Ctrl-C to cancel this command,\n\
+ or Enter to restore default definition",
+                         "",&s,NULL)) < 0) {
+              if (flag)                 /* Handle parse errors */
+                goto def_again;
+              else
+                return(y);
+          }
+          s = brstrip(s);
+#ifndef NOKVERBS
+          p = s;                        /* Save this place */
+#endif /* NOKVERBS */
+/*
+  If the definition included any \Kverbs, quote the backslash so the \Kverb
+  will still be in the definition when the key is pressed.  We don't do this
+  in zzstring(), because \Kverbs are valid only in this context and nowhere
+  else.
+
+  We use this code active for all versions that support SET KEY, even if they
+  don't support \Kverbs, because otherwise \K would behave differently for
+  different versions.
+*/
+          for (x = 0, y = 0; s[x]; x++, y++) { /* Convert \K to \\K */
+              if ((x > 0) &&
+                  (s[x] == 'K' || s[x] == 'k')
+                  ) {                   /* Have K */
+
+                  if ((x == 1 && s[x-1] == CMDQ) ||
+                      (x > 1 && s[x-1] == CMDQ && s[x-2] != CMDQ)) {
+                      line[y++] = CMDQ; /* Make it \\K */
+                  }
+                  if (x > 1 && s[x-1] == '{' && s[x-2] == CMDQ) {
+                      line[y-1] = CMDQ; /* Have \{K */
+                      line[y++] = '{';  /* Make it \\{K */
+                  }
+              }
+              line[y] = s[x];
+          }
+          line[y++] = NUL;              /* Terminate */
+          s = line + y + 1;             /* Point to after it */
+          x = LINBUFSIZ - (int) strlen(line) - 1; /* Get remaining space */
+          if ((x < (LINBUFSIZ / 2)) ||
+              (zzstring(line, &s, &x) < 0)) { /* Expand variables, etc. */
+              printf("?Key definition too long\n");
+              if (flag) cmsetp(psave);
+              return(-9);
+          }
+          s = line + y + 1;             /* Point to result. */
+
+#ifndef NOKVERBS
+/*
+  Special case: see if the definition starts with a \Kverb.
+  If it does, point to it with p, otherwise set p to NULL.
+*/
+          p = s;
+          if (*p++ == CMDQ) {
+              if (*p == '{') p++;
+              p = (*p == 'k' || *p == 'K') ? p + 1 : NULL;
+          }
+#endif /* NOKVERBS */
+
+          switch (strlen(s)) {          /* Action depends on length */
+            case 0:                     /* Clear individual key def */
+              deletekeymap(t,kc);
+              break;
+            case 1:
+              if (!litstr) {
+                  defevt.type = key;    /* Single character */
+                  defevt.key.scancode = *s;
+                  break;
+              }
+            default:                    /* Character string */
+#ifndef NOKVERBS
+              if (p) {
+                  y = xlookup(kverbs,p,nkverbs,&x); /* Look it up */
+                  /* Need exact match */
+                  debug(F101,"set key kverb lookup",0,y);
+                  if (y > -1) {
+                      defevt.type = kverb;
+                      defevt.kverb.id = y;
+                      break;
+                  }
+              }
+#endif /* NOKVERBS */
+              if (litstr) {
+                  defevt.type = literal;
+                  defevt.literal.string = (char *) malloc(strlen(s)+1);
+                  if (defevt.literal.string)
+                    strcpy(defevt.literal.string, s); /* safe */
+              } else {
+                  defevt.type = macro;
+                  defevt.macro.string = (char *) malloc(strlen(s)+1);
+                  if (defevt.macro.string)
+                    strcpy(defevt.macro.string, s); /* safe */
+              }
+              break;
+          }
+          insertkeymap(t, kc, defevt);
+          if (flag)
+            cmsetp(psave);
+          initvik = 1;                  /* Update VIK table */
+          return(1);
+      }
+
+#ifdef PCTERM
+      case XYTPCTERM:                   /* PCTERM Keyboard Mode */
+        if ((x = seton(&tt_pcterm)) < 0) return(x);
+        return(success = 1);
+#endif /* PCTERM */
+#endif /* OS2 */
+
+#ifdef CK_TRIGGER
+      case XYTRIGGER:
+        if ((y = cmtxt("String to trigger automatic return to command mode",
+                       "",&s,xxstring)) < 0)
+          return(y);
+        makelist(s,tt_trigger,TRIGGERS);
+        return(1);
+#endif /* CK_TRIGGER */
+
+#ifdef OS2
+      case XYTSAC:
+        if ((y = cmnum("ASCII value to use for spacing attributes",
+                       "32",10,&x,xxstring)) < 0)
+          return(y);
+        if ((y = cmcfm()) < 0) return(y);
+        tt_sac = x;
+        return(success = 1);
+
+      case XYTKBDGL: {      /* SET TERM KBD-FOLLOWS-GL/GR */
+          extern int tt_kb_glgr;        /* from ckoco3.c */
+          if ((x = seton(&tt_kb_glgr)) < 0)
+              return(x);
+          return(success = 1);
+      }
+#ifndef NOCSETS
+      case XYTVTLNG:        /* SET TERM DEC-LANGUAGE */
+        if ((y = cmkey(vtlangtab,nvtlangtab,"VT language",
+                       IS97801(tt_type_mode)?"german":"north-american",
+                       xxstring)) < 0)
+          return(y);
+        if ((x = cmcfm()) < 0) return(x);
+
+        /* A real VT terminal would use the language to set the   */
+        /* default keyboard language for both 8-bit multinational */
+        /* and 7-bit national modes.  For 8-bit mode it would     */
+        /* set the terminal character-set to the ISO set if it    */
+        /* is not already set.                                    */
+        /* Latin-1 can be replaced by DEC Multinational           */
+        switch (y) {
+          case VTL_NORTH_AM:  /* North American */
+            /* Multinational: Latin-1   */
+            /* National:      US_ASCII  */
+            dec_lang = y;
+            dec_nrc = TX_ASCII;
+            dec_kbd = TX_8859_1;
+            break;
+          case VTL_BRITISH :
+            /* Multinational: Latin-1   */
+            /* National:      UK_ASCII  */
+            dec_lang = y;
+            dec_nrc = TX_BRITISH;
+            dec_kbd = TX_8859_1;
+            break;
+          case VTL_FRENCH  :
+          case VTL_BELGIAN :
+          case VTL_CANADIAN:
+            /* Multinational: Latin-1   */
+            /* National:      FR_ASCII  */
+            dec_lang = y;
+            dec_nrc = TX_FRENCH;
+            dec_kbd = TX_8859_1;
+            break;
+          case VTL_FR_CAN  :
+            /* Multinational: Latin-1   */
+            /* National:      FC_ASCII  */
+            dec_lang = y;
+            dec_nrc = TX_CN_FRENCH;
+            dec_kbd = TX_8859_1;
+            break;
+          case VTL_DANISH  :
+          case VTL_NORWEGIA:
+            /* Multinational: Latin-1   */
+            /* National:      NO_ASCII  */
+            dec_lang = y;
+            dec_nrc = TX_NORWEGIAN;
+            dec_kbd = TX_8859_1;
+            break;
+          case VTL_FINNISH :
+            /* Multinational: Latin-1   */
+            /* National:      FI_ASCII  */
+            dec_lang = y;
+            dec_nrc = TX_FINNISH;
+            dec_kbd = TX_8859_1;
+            break;
+          case VTL_GERMAN  :
+            /* Multinational: Latin-1   */
+            /* National:      GR_ASCII  */
+            dec_lang = y;
+            dec_nrc = TX_GERMAN;
+            dec_kbd = TX_8859_1;
+            break;
+          case VTL_DUTCH   :
+            /* Multinational: Latin-1   */
+            /* National:      DU_ASCII  */
+            dec_lang = y;
+            dec_nrc = TX_DUTCH;
+            dec_kbd = TX_8859_1;
+            break;
+          case VTL_ITALIAN :
+            /* Multinational: Latin-1   */
+            /* National:      IT_ASCII  */
+            dec_lang = y;
+            dec_nrc = TX_ITALIAN;
+            dec_kbd = TX_8859_1;
+            break;
+          case VTL_SW_FR   :
+          case VTL_SW_GR   :
+            /* Multinational: Latin-1   */
+            /* National:      CH_ASCII  */
+            dec_lang = y;
+            dec_nrc = TX_SWISS;
+            dec_kbd = TX_8859_1;
+            break;
+          case VTL_SWEDISH :
+            /* Multinational: Latin-1   */
+            /* National:      SW_ASCII  */
+            dec_lang = y;
+            dec_nrc = TX_SWEDISH;
+            dec_kbd = TX_8859_1;
+            break;
+          case VTL_SPANISH :
+            /* Multinational: Latin-1   */
+            /* National:      SP_ASCII  */
+            dec_lang = y;
+            dec_nrc = TX_SPANISH;
+            dec_kbd = TX_8859_1;
+            break;
+          case VTL_PORTUGES:
+            /* Multinational: Latin-1   */
+            /* National:      Portugese ASCII  */
+            dec_lang = y;
+            dec_nrc = TX_PORTUGUESE;
+            dec_kbd = TX_8859_1;
+            break;
+          case VTL_HEBREW  :
+            /* Multinational: Latin-Hebrew / DEC-Hebrew  */
+            /* National:      DEC 7-bit Hebrew  */
+            dec_lang = y;
+            dec_nrc = TX_HE7;
+            dec_kbd = TX_8859_8;
+            break;
+          case VTL_GREEK   :
+            /* Multinational: Latin-Greek / DEC-Greek   */
+            /* National:      DEC Greek NRC             */
+            /* is ELOT927 equivalent to DEC Greek????   */
+            dec_lang = y;
+            dec_nrc = TX_ELOT927;
+            dec_kbd = TX_8859_7;
+            break;
+#ifdef COMMENT
+          case VTL_TURK_Q  :
+          case VTL_TURK_F  :
+            /* Multinational: Latin-Turkish / DEC-Turkish   */
+            /* National:      DEC 7-bit Turkish             */
+            break;
+#endif /* COMMENT */
+          case VTL_HUNGARIA:
+            /* Multinational: Latin-2   */
+            /* National:      no national mode  */
+            dec_lang = y;
+            dec_nrc = TX_HUNGARIAN;
+            dec_kbd = TX_8859_2;
+            break;
+          case VTL_SLOVAK  :
+          case VTL_CZECH   :
+          case VTL_POLISH  :
+          case VTL_ROMANIAN:
+            /* Multinational: Latin-2   */
+            /* National:      no national mode  */
+            dec_lang = y;
+            dec_nrc = TX_ASCII;
+            dec_kbd = TX_8859_2;
+            break;
+          case VTL_RUSSIAN :
+            /* Multinational: Latin-Cyrillic / KOI-8   */
+            /* National:      DEC Russian NRC  */
+            dec_lang = y;
+            dec_nrc = TX_KOI7;
+            dec_kbd = TX_8859_5;
+            break;
+          case VTL_LATIN_AM:
+            /* Multinational: not listed in table   */
+            /* National:      not listed in table  */
+            dec_lang = y;
+            dec_nrc = TX_ASCII;
+            dec_kbd = TX_8859_1;
+            break;
+#ifdef COMMENT
+          case VTL_SCS     :
+            /* Multinational: Latin-2   */
+            /* National:      SCS NRC   */
+            break;
+#endif /* COMMENT */
+          default:
+            return(success = 0);
+        }
+        if (IS97801(tt_type_mode)) {
+            SNI_bitmode(cmask == 0377 ? 8 : 7);
+        }
+        return(success = 1);
+#endif /* NOCSETS */
+
+      case XYTVTNRC: {                  /* SET TERM DEC-NRC-MODE */
+          extern int decnrcm_usr, decnrcm;        /* from ckoco3.c */
+          if ((x = seton(&decnrcm_usr)) < 0)
+            return(x);
+          decnrcm = decnrcm_usr;
+          return(success = 1);
+      }
+      case XYTSNIPM: {                  /* SET TERM SNI-PAGEMODE */
+          extern int sni_pagemode, sni_pagemode_usr;
+          if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y);
+          if ((x = cmcfm()) < 0) return(x);
+          sni_pagemode_usr = sni_pagemode = y;
+          return(success = 1);
+      }
+      case XYTSNISM: {                  /* SET TERM SNI-SCROLLMODE */
+          extern int sni_scroll_mode, sni_scroll_mode_usr;
+          if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y);
+          if ((x = cmcfm()) < 0) return(x);
+          sni_scroll_mode_usr = sni_scroll_mode = y;
+          return(success = 1);
+      }
+      case XYTSNICC: {  /* SET TERM SNI-CH.CODE */
+          extern int sni_chcode_usr;
+          if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y);
+          if ((x = cmcfm()) < 0) return(x);
+          sni_chcode_usr = y;
+          SNI_chcode(y);
+          return(success = 1);
+      }
+      case XYTSNIFV: {  /* SET TERM SNI-FIRMWARE-VERSIONS */
+          extern CHAR sni_kbd_firmware[], sni_term_firmware[];
+          CHAR kbd[7],term[7];
+
+          if ((x = cmfld("Keyboard Firmware Version",sni_kbd_firmware,
+                         &s, xxstring)) < 0)
+            return(x);
+          if ((int)strlen(s) != 6) {
+              printf("?Sorry - the firmware version must be 6 digits long\n");
+              return(-9);
+          }
+          for (i = 0; i < 6; i++) {
+              if (!isdigit(s[i])) {
+   printf("?Sorry - the firmware version can only contain digits [0-9]\n");
+                  return(-9);
+              }
+          }
+          ckstrncpy(kbd,s,7);
+
+          if ((x = cmfld("Terminal Firmware Version",sni_term_firmware,
+                         &s, xxstring)) < 0)
+            return(x);
+          if ((int)strlen(s) != 6) {
+              printf("?Sorry - the firmware version must be 6 digits long\n");
+              return(-9);
+          }
+          for (i = 0; i < 6; i++) {
+              if (!isdigit(s[i])) {
+   printf("?Sorry - the firmware version can only contain digits [0-9]\n");
+                   return(-9);
+              }
+          }
+          ckstrncpy(term,s,7);
+          if ((x = cmcfm()) < 0) return(x);
+
+          ckstrncpy(sni_kbd_firmware,kbd,7);
+          ckstrncpy(sni_term_firmware,term,7);
+          return(success = 1);
+    }
+
+    case XYTLSP: {              /* SET TERM LINE-SPACING */
+        if ((x = cmfld("Line Spacing","1",&s, xxstring)) < 0)
+          return(x);
+        if (isfloat(s,0) < 1) {                /* (sets floatval) */
+            printf("?Integer or floating-point number required\n");
+            return(-9);
+        }
+        if (floatval < 1.0 || floatval > 3.0) {
+            printf("?Value must within the range 1.0 and 3.0 (inclusive)\n");
+            return(-9);
+        }
+        if ((x = cmcfm()) < 0) return(x);
+#ifdef KUI
+        tt_linespacing[VCMD] = tt_linespacing[VTERM] = floatval;
+        return(success = 1);
+#else /* KUI */
+        printf("?Sorry, Line-spacing is only supported in K95G.EXE.\n");
+        return(success = 0);
+#endif /* KUI */
+    }
+#endif /* OS2 */
+
+      default:                          /* Shouldn't get here. */
+        return(-2);
+    }
+#endif /* MAC */
+#ifdef COMMENT
+    /*
+      This was supposed to shut up picky compilers but instead it makes
+      most compilers complain about "statement not reached".
+    */
+    return(-2);
+#endif /* COMMENT */
+#ifdef OS2
+return(-2);
+#endif /* OS2 */
+}
+
+#ifdef OS2
+int
+settitle(void) {
+    extern char usertitle[];
+    if ((y = cmtxt("title text","",&s,xxstring)) < 0)
+      return(y);
+#ifdef IKSD
+    if (inserver) {
+        printf("?Sorry, command disabled.\r\n");
+        return(success = 0);
+    }
+#endif /* IKSD */
+    s = brstrip(s);
+    ckstrncpy(usertitle,s,64);
+    os2settitle("",1);
+    return(1);
+}
+
+static struct keytab dialertab[] = {    /* K95 Dialer types */
+    "backspace",        0, 0,
+    "enter",            1, 0
+};
+static int ndialer = 2;
+
+int
+setdialer(void) {
+    int t, x, y;
+    int clear = 0, deflt = 0;
+    int kc;                             /* Key code */
+    char *s = NULL;                     /* Key binding */
+#ifndef NOKVERBS
+    char *p = NULL;                     /* Worker */
+#endif /* NOKVERBS */
+    con_event defevt;
+    extern int os2gks;
+    extern int mskkeys;
+    extern int initvik;
+
+    defevt.type = error;
+
+    if (( x = cmkey(dialertab, ndialer,
+                    "Kermit-95 dialer work-arounds",
+                    "", xxstring)) < 0 )
+      return(x);
+    switch (x) {
+      case 0:                           /* Backspace */
+        kc = 264;
+        break;
+      case 1:                           /* Enter */
+        kc = 269;
+        break;
+      default:
+        printf("Illegal value in setdialer()\n");
+        return(-9);
+    }
+    if ((y = cmtxt("Key definition","",&s,xxstring)) < 0)
+      return(y);
+
+#ifdef IKSD
+    if (inserver) {
+        printf("?Sorry, command disabled.\r\n");
+        return(success = 0);
+    }
+#endif /* IKSD */
+    s = brstrip(s);
+#ifndef NOKVERBS
+    p = s;                              /* Save this place */
+#endif /* NOKVERBS */
+/*
+  If the definition included any \Kverbs, quote the backslash so the \Kverb
+  will still be in the definition when the key is pressed.  We don't do this
+  in zzstring(), because \Kverbs are valid only in this context and nowhere
+  else.
+
+  We use this code active for all versions that support SET KEY, even if they
+  don't support \Kverbs, because otherwise \K would behave differently for
+  different versions.
+*/
+    for (x = 0, y = 0; s[x]; x++, y++) { /* Convert \K to \\K */
+        if ((x > 0) &&
+            (s[x] == 'K' || s[x] == 'k')
+            ) {                         /* Have K */
+
+            if ((x == 1 && s[x-1] == CMDQ) ||
+                (x > 1 && s[x-1] == CMDQ && s[x-2] != CMDQ)) {
+                line[y++] = CMDQ;       /* Make it \\K */
+            }
+            if (x > 1 && s[x-1] == '{' && s[x-2] == CMDQ) {
+                line[y-1] = CMDQ;       /* Have \{K */
+                line[y++] = '{';        /* Make it \\{K */
+            }
+        }
+        line[y] = s[x];
+    }
+    line[y++] = NUL;                    /* Terminate */
+    s = line + y + 1;                   /* Point to after it */
+    x = LINBUFSIZ - (int) strlen(line) - 1; /* Calculate remaining space */
+    if ((x < (LINBUFSIZ / 2)) ||
+        (zzstring(line, &s, &x) < 0)) { /* Expand variables, etc. */
+        printf("?Key definition too long\n");
+        return(-9);
+    }
+    s = line + y + 1;                   /* Point to result. */
+
+#ifndef NOKVERBS
+/*
+  Special case: see if the definition starts with a \Kverb.
+  If it does, point to it with p, otherwise set p to NULL.
+*/
+    p = s;
+    if (*p++ == CMDQ) {
+        if (*p == '{') p++;
+        p = (*p == 'k' || *p == 'K') ? p + 1 : NULL;
+    }
+#endif /* NOKVERBS */
+
+    /* Clear the definition for SET KEY */
+    if (macrotab[kc]) {                 /* Possibly free old macro from key. */
+        free((char *)macrotab[kc]);
+        macrotab[kc] = NULL;
+    }
+    keymap[kc] = (KEY) kc;
+
+    /* Now reprogram the default value for all terminal types */
+    /* remember to treat Wyse and Televideo terminals special */
+    /* because of their use of Kverbs for Backspace and Enter */
+    for (t = 0; t <= TT_MAX; t++) {
+        if ( ISDG200(t) && kc == 264) {
+            extern char * udkfkeys[] ;
+            if (kc == 264) {            /* \Kdgbs */
+                if (udkfkeys[83])
+                  free(udkfkeys[83]);
+                udkfkeys[83] = strdup(s);
+            }
+        } else if (ISWYSE(t) || ISTVI(t)) {
+            extern char * udkfkeys[] ;
+            if (kc == 264) {            /* \Kwybs or \Ktvibs */
+                if (udkfkeys[32])
+                  free(udkfkeys[32]);
+                udkfkeys[32] = strdup(s);
+            }
+            if (kc == 269) {            /* \Kwyenter and \Kwyreturn */
+                if (udkfkeys[39])       /* \Ktvienter and \Ktvireturn */
+                  free(udkfkeys[39]);
+                udkfkeys[39] = strdup(s);
+                if (udkfkeys[49])
+                  free(udkfkeys[49]);
+                udkfkeys[49] = strdup(s);
+            }
+        } else {
+            switch (strlen(s)) {        /* Action depends on length */
+              case 0:                   /* Clear individual key def */
+                deletekeymap(t,kc);
+                break;
+              case 1:
+                defevt.type = key;      /* Single character */
+                defevt.key.scancode = *s;
+                break;
+              default:                  /* Character string */
+#ifndef NOKVERBS
+                if (p) {
+                    y = xlookup(kverbs,p,nkverbs,&x); /* Look it up */
+                    /* Exact match req'd */
+                    debug(F101,"set key kverb lookup",0,y);
+                    if (y > -1) {
+                        defevt.type = kverb;
+                        defevt.kverb.id = y;
+                        break;
+                    }
+                }
+#endif /* NOKVERBS */
+                defevt.type = macro;
+                defevt.macro.string = (char *) malloc(strlen(s)+1);
+                if (defevt.macro.string)
+                  strcpy(defevt.macro.string, s); /* safe */
+                break;
+            }
+            insertkeymap( t, kc, defevt ) ;
+            initvik = 1;                /* Update VIK table */
+        }
+    }
+    return(1);
+}
+#endif /* OS2 */
+
+#ifdef NT
+int
+setwin95( void ) {
+    int x, y, z;
+
+    if (( y = cmkey(win95tab, nwin95,
+                    "Windows 95 specific work-arounds",
+                    "keyboard-translation",
+                    xxstring)) < 0 )
+        return (y);
+    switch (y) {
+      case XYWPOPUP:
+        if ((y = cmkey(onoff,2,"popups are used to prompt the user for data",
+                       "on",xxstring)) < 0)
+          return(y);
+        if ((x = cmcfm()) < 0) return(x);
+        win95_popup = y;
+        return(1);
+
+      case XYW8_3:
+        if ((y = cmkey(onoff,2,"8.3 FAT file names","off",xxstring)) < 0)
+          return(y);
+        if ((x = cmcfm()) < 0) return(x);
+        win95_8_3 = y;
+        return(1);
+
+      case XYWSELECT:
+        if ((y = cmkey(onoff,2,"\"select()\" fails on write","off",
+             xxstring)) < 0)
+          return(y);
+        if ((x = cmcfm()) < 0) return(x);
+        win95selectbug = y;
+        return(1);
+
+      case XYWAGR:
+        if ((y = cmkey(onoff,2,"Right-Alt is Alt-Gr","off",xxstring)) < 0)
+          return(y);
+        if ((x = cmcfm()) < 0) return(x);
+        win95altgr = y;
+        return(1);
+
+      case XYWOIO:
+        if ((y = cmkey(onoff,2,"Use Overlapped I/O","on",xxstring)) < 0)
+          return(y);
+        if (y) {
+            if ((x = cmnum("Maximum number of outstanding I/O requests",
+                           "10",10,&z,xxstring)) < 0)
+              return(x);
+            if (z < 1 || z > 7) {
+                printf(
+"?Maximum outstanding I/O requests must be between 1 and 7.\n");
+                return(-9);
+            }
+        } else
+          z = 1;
+        if ((x = cmcfm()) < 0) return(x);
+        owwait = !y;
+        maxow = maxow_usr = z;
+        return(1);
+
+      case XYWKEY:
+#ifndef COMMENT
+        printf("\n?\"Keyboard-Translation\" is no longer required.\n");
+        return(-9);
+#else /* COMMENT */
+        if (( z = cmkey(tcstab, ntcs,
+                        "Keyboard Character Set",
+                        "latin1-iso",
+                        xxstring)) < 0)
+          return (z);
+        if ((x = cmcfm()) < 0)
+          return(x);
+
+        win95kcsi = z;
+        win95kl2 = (win95kcsi == TC_2LATIN);
+
+        if (win95kcsi == TC_TRANSP) {
+            win95kcs = NULL;
+        } else {
+#ifdef UNICODE
+            win95kcs = xlr[win95kcsi][tx2fc(tcsl)];
+#else /* UNICODE */
+            win95kcs = xlr[win95kcsi][tcsl];
+#endif /* UNICODE */
+        }
+        return(1);
+#endif /* COMMENT */
+
+      case XYWLUC:
+        if ((y = cmkey(onoff,2,"Unicode-to-Lucida-Console substitutions",
+                       "on",xxstring)) < 0)
+          return(y);
+        if ((x = cmcfm()) < 0) return(x);
+        win95lucida = y;
+        return(1);
+
+      case XYWHSL:
+       if ((y = cmkey(onoff,2,"Horizontal Scan Line substitutions",
+                      "on",xxstring)) < 0)
+         return(y);
+       if ((x = cmcfm()) < 0) return(x);
+       win95hsl = y;
+       return(1);
+
+      default:
+        printf("Illegal value in setwin95()\n");
+        return(-9);
+    }
+}
+#endif /* NT */
+
+#ifdef OS2
+int
+setprty (
+#ifdef CK_ANSIC
+    void
+#endif /* CK_ANSIC */
+/* setprty */ ) {
+    int x, y, z;
+
+    if (( y = cmkey(prtytab, nprty,
+                    "priority level of terminal and communication threads",
+                    "foreground-server",
+                    xxstring)) < 0 )
+      return (y);
+
+    if ((x = cmcfm()) < 0)
+      return (x);
+#ifdef IKSD
+    if (inserver &&
+#ifdef IKSDCONF
+         iksdcf
+#else
+         1
+#endif /* IKSDCONF */
+    ) {
+        if ((y = cmcfm()) < 0) return(y);
+        printf("?Sorry, command disabled.\r\n");
+        return(success = 0);
+    }
+#endif /* IKSD */
+    priority = y;
+    return(TRUE);
+}
+#endif /* OS2 */
+
+int
+setbell() {
+    int y, x;
+#ifdef OS2
+    int z;
+#endif /* OS2 */
+
+    if ((y = cmkey(beltab,nbeltab,
+#ifdef OS2
+        "how console and terminal bells should\nbe generated", "audible",
+#else
+        "Whether Kermit should ring the terminal bell (beep)", "on",
+#endif /* OS2 */
+                   xxstring)) < 0)
+          return(y);
+
+#ifdef IKSD
+    if (inserver) {
+        if ((y = cmcfm()) < 0) return(y);
+        printf("?Sorry, command disabled.\r\n");
+        return(success = 0);
+    }
+#endif /* IKSD */
+
+    switch (y) {                        /* SET BELL */
+      case XYB_NONE:
+#ifdef OS2
+      case XYB_VIS:
+#endif /* OS2 */
+        if ((x = cmcfm()) < 0)
+          return(x);
+#ifdef OS2
+        tt_bell = y;
+#else
+        tt_bell = 0;
+#endif /* OS2 */
+        break;
+
+      case XYB_AUD:
+#ifdef OS2
+        if ((x = cmkey(audibletab, naudibletab,
+               "how audible console and terminal\nbells should be generated",
+                       "beep",xxstring))<0)
+          return(x);
+        if ((z = cmcfm()) < 0)
+          return(z);
+        tt_bell = y | x;
+#else
+        /* This lets C-Kermit accept but ignore trailing K95 keywords */
+        if ((x = cmtxt("Confirm with carriage return","",&s,xxstring)) < 0)
+          return(x);
+        tt_bell = 1;
+#endif /* OS2 */
+        break;
+    }
+    return(1);
+}
+
+#ifdef OS2MOUSE
+int
+setmou(
+#ifdef CK_ANSIC
+       void
+#endif /* CK_ANSIC */
+ /* setmou */ ) {
+    extern int initvik;
+    int button = 0, event = 0;
+    char * p;
+
+    if ((y = cmkey(mousetab,nmtab,"","",xxstring)) < 0)
+      return(y);
+
+#ifdef IKSD
+    if (inserver) {
+        if ((y = cmcfm()) < 0) return(y);
+        printf("?Sorry, command disabled.\r\n");
+        return(success = 0);
+    }
+#endif /* IKSD */
+
+    if (y == XYM_ON) {                  /* MOUSE ACTIVATION */
+        int old_mou = tt_mouse;
+        if ((x = seton(&tt_mouse)) < 0)
+            return(x);
+        if (tt_mouse != old_mou)
+          if (tt_mouse)
+            os2_mouseon();
+          else
+            os2_mouseoff();
+        return(1);
+    }
+
+    if (y == XYM_DEBUG) {               /* MOUSE DEBUG */
+        extern int MouseDebug;
+        if ((x = seton(&MouseDebug)) < 0)
+            return(x);
+        return(1);
+    }
+
+    if (y == XYM_CLEAR) {               /* Reset Mouse Defaults */
+        if ((x = cmcfm()) < 0) return(x);
+        mousemapinit(-1,-1);
+        initvik = 1;                    /* Update VIK Table */
+        return 1;
+    }
+    if (y != XYM_BUTTON) {              /* Shouldn't happen. */
+        printf("Internal parsing error\n");
+        return(-9);
+    }
+
+    /* MOUSE EVENT ... */
+
+    if ((button = cmkey(mousebuttontab,nmbtab,
+                        "Button number","1",
+                        xxstring)) < 0)
+      return(button);
+
+    if ((y =  cmkey(mousemodtab,nmmtab,
+                    "Keyboard modifier","none",
+                    xxstring)) < 0)
+      return(y);
+
+    event |= y;                         /* OR in the bits */
+
+    if ((y =  cmkey(mclicktab,nmctab,"","click",xxstring)) < 0)
+      return(y);
+
+    /* Two bits are assigned, if neither are set then it is button one */
+
+    event |= y;                 /* OR in the bit */
+
+    wideresult = -1;
+
+    if ((y = cmtxt("definition,\n\
+or Ctrl-C to cancel this command,\n\
+or Enter to restore default definition",
+                   "",&s,NULL)) < 0) {
+        return(y);
+    }
+    s = brstrip(s);
+    p = s;                              /* Save this place */
+/*
+  If the definition included any \Kverbs, quote the backslash so the \Kverb
+  will still be in the definition when the key is pressed.  We don't do this
+  in zzstring(), because \Kverbs are valid only in this context and nowhere
+  else.  This code copied from SET KEY, q.v. for addt'l commentary.
+*/
+    for (x = 0, y = 0; s[x]; x++, y++) { /* Convert \K to \\K */
+        if ((x > 0) &&
+            (s[x] == 'K' || s[x] == 'k')
+            ) {                         /* Have K */
+
+            if ((x == 1 && s[x-1] == CMDQ) ||
+                (x > 1 && s[x-1] == CMDQ && s[x-2] != CMDQ)) {
+                line[y++] = CMDQ;       /* Make it \\K */
+            }
+            if (x > 1 && s[x-1] == '{' && s[x-2] == CMDQ) {
+                line[y-1] = CMDQ;       /* Have \{K */
+                line[y++] = '{';        /* Make it \\{K */
+            }
+        }
+        line[y] = s[x];
+    }
+    line[y++] = NUL;                    /* Terminate */
+    s = line + y + 1;                   /* Point to after it */
+    x = LINBUFSIZ - (int) strlen(line) - 1; /* Calculate remaining space */
+    if ((x < (LINBUFSIZ / 2)) ||
+        (zzstring(line, &s, &x) < 0)) { /* Expand variables, etc. */
+        printf("?Key definition too long\n");
+        return(-9);
+    }
+    s = line + y + 1;                   /* Point to result. */
+
+#ifndef NOKVERBS
+/*
+  Special case: see if the definition starts with a \Kverb.
+  If it does, point to it with p, otherwise set p to NULL.
+*/
+    p = s;
+    if (*p++ == CMDQ) {
+        if (*p == '{') p++;
+        p = (*p == 'k' || *p == 'K') ? p + 1 : NULL;
+    }
+#else
+    p = NULL;
+#endif /* NOKVERBS */
+
+    /* free the old definition if necessary */
+    if (mousemap[button][event].type == macro) {
+        free( mousemap[button][event].macro.string);
+        mousemap[button][event].macro.string = NULL;
+    }
+    switch (strlen(s)) {                /* Action depends on length */
+      case 0:                           /* Reset to default binding */
+        mousemapinit( button, event );
+        break;
+      case 1:                           /* Single character */
+            mousemap[button][event].type = key;
+        mousemap[button][event].key.scancode = *s;
+        break;
+      default:                          /* Character string */
+#ifndef NOKVERBS
+        if (p) {
+            y = xlookup(kverbs,p,nkverbs,&x); /* Look it up */
+            debug(F101,"set mouse kverb lookup",0,y); /* need exact match */
+            if (y > -1) {
+            /* Assign the kverb to the event */
+            mousemap[button][event].type = kverb;
+            mousemap[button][event].kverb.id = F_KVERB | y;
+            break;
+            }
+        }
+#endif /* NOKVERBS */
+
+       /* Otherwise, it's a macro, so assign the macro to the event */
+       mousemap[button][event].type = macro;
+       mousemap[button][event].macro.string = (MACRO) malloc(strlen(s)+1);
+       if (mousemap[button][event].macro.string)
+         strcpy((char *) mousemap[button][event].macro.string, s); /* safe */
+        break;
+    }
+    initvik = 1;                        /* Update VIK Table */
+    if ( (button == XYM_B3) && (mousebuttoncount() < 3) && !quiet )
+    {
+        printf("?Warning: this machine does not have a three button mouse.\n");
+        return(0);
+    }
+    return(1);
+}
+#endif /* OS2MOUSE */
+#endif /* NOLOCAL */
+
+#ifndef NOXFER
+int                                     /* SET SEND/RECEIVE */
+setsr(xx, rmsflg) int xx; int rmsflg; {
+    if (xx == XYRECV)
+      ckstrncpy(line,"Parameter for inbound packets",LINBUFSIZ);
+    else
+      ckstrncpy(line,"Parameter for outbound packets",LINBUFSIZ);
+
+    if (rmsflg) {
+        if ((y = cmkey(rsrtab,nrsrtab,line,"",xxstring)) < 0) {
+            if (y == -3) {
+                printf("?Remote receive parameter required\n");
+                return(-9);
+            } else return(y);
+        }
+    } else {
+        if ((y = cmkey(srtab,nsrtab,line,"",xxstring)) < 0) return(y);
+    }
+    switch (y) {
+      case XYQCTL:                      /* CONTROL-PREFIX */
+        if ((x = cmnum("ASCII value of control prefix","",10,&y,xxstring)) < 0)
+          return(x);
+        if ((x = cmcfm()) < 0) return(x);
+        if ((y > 32 && y < 63) || (y > 95 && y < 127)) {
+            if (xx == XYRECV)
+              ctlq = (CHAR) y;          /* RECEIVE prefix, use with caution! */
+            else
+              myctlq = (CHAR) y;        /* SEND prefix, OK to change */
+            return(success = 1);
+        } else {
+            printf("?Illegal value for prefix character\n");
+            return(-9);
+        }
+
+      case XYEOL:
+        if ((y = setcc("13",&z)) < 0)
+            return(y);
+        if (z > 31) {
+            printf("Sorry, the legal values are 0-31\n");
+            return(-9);
+        }
+        if (xx == XYRECV)
+          eol = (CHAR) z;
+        else
+          seol = (CHAR) z;
+        return(success = y);
+
+      case XYLEN:
+        y = cmnum("Maximum number of characters in a packet","90",10,&x,
+                  xxstring);
+        if (xx == XYRECV) {             /* Receive... */
+            if ((y = setnum(&z,x,y,maxrps)) < 0)
+              return(y);
+            if (protocol != PROTO_K) {
+                printf("?Sorry, this command does not apply to %s protocol.\n",
+                       ptab[protocol].p_name
+                       );
+                printf("Use SET SEND PACKET-LENGTH for XYZMODEM\n");
+                return(-9);
+            }
+            if (z < 10) {
+                printf("Sorry, 10 is the minimum\n");
+                return(-9);
+            }
+            if (rmsflg) {
+                sstate = setgen('S', "401", ckitoa(z), "");
+                return((int) sstate);
+            } else {
+                if (protocol == PROTO_K) {
+                    if (z > MAXRP) z = MAXRP;
+                    y = adjpkl(z,wslotr,bigrbsiz);
+                    if (y != z) {
+                        urpsiz = y;
+                        if (!xcmdsrc)
+                          if (msgflg) printf(
+" Adjusting receive packet-length to %d for %d window slots\n",
+                                             y, wslotr);
+                    }
+                    urpsiz = y;
+                    ptab[protocol].rpktlen = urpsiz;
+                    rpsiz =  (y > 94) ? 94 : y;
+                } else {
+#ifdef CK_XYZ
+                    if ((protocol == PROTO_X || protocol == PROTO_XC) &&
+                         z != 128 && z != 1024) {
+                        printf("Sorry, bad packet length for XMODEM.\n");
+                        printf("Please use 128 or 1024.\n");
+                        return(-9);
+                    }
+#endif /* CK_XYZ */
+                    urpsiz = rpsiz = z;
+                }
+            }
+        } else {                        /* Send... */
+            if ((y = setnum(&z,x,y,maxsps)) < 0)
+              return(y);
+            if (z < 10) {
+                printf("Sorry, 10 is the minimum\n");
+                return(-9);
+            }
+            if (protocol == PROTO_K) {
+                if (z > MAXSP) z = MAXSP;
+                spsiz = z;              /* Set it */
+                y = adjpkl(spsiz,wslotr,bigsbsiz);
+                if (y != spsiz && !xcmdsrc)
+                  if (msgflg)
+                    printf("Adjusting packet size to %d for %d window slots\n",
+                           y,wslotr);
+            } else
+              y = z;
+#ifdef CK_XYZ
+            if ((protocol == PROTO_X || protocol == PROTO_XC) &&
+                 z != 128 && z != 1024) {
+                printf("Sorry, bad packet length for XMODEM.\n");
+                printf("Please use 128 or 1024.\n");
+                return(-9);
+            }
+#endif /* CK_XYZ */
+            spsiz = spmax = spsizr = y; /* Set it and flag that it was set */
+            spsizf = 1;                 /* to allow overriding Send-Init. */
+            ptab[protocol].spktflg = spsizf;
+            ptab[protocol].spktlen = spsiz;
+        }
+        if (pflag && protocol == PROTO_K && !xcmdsrc) {
+            if (z > 94 && !reliable && msgflg) {
+                /* printf("Extended-length packets requested.\n"); */
+                if (bctr < 2 && z > 200) printf("\
+Remember to SET BLOCK 2 or 3 for long packets.\n");
+            }
+            if (speed <= 0L) speed = ttgspd();
+#ifdef COMMENT
+/*
+  Kermit does this now itself.
+*/
+            if (speed <= 0L && z > 200 && msgflg) {
+                printf("\
+Make sure your timeout interval is long enough for %d-byte packets.\n",z);
+            }
+#endif /* COMMENT */
+        }
+        return(success = y);
+
+      case XYMARK:
+#ifdef DOOMSDAY
+/*
+  Printable start-of-packet works for UNIX and VMS only!
+*/
+        x_ifnum = 1;
+        y = cmnum("Code for packet-start character","1",10,&x,xxstring);
+        x_ifnum = 0;
+        if ((y = setnum(&z,x,y,126)) < 0) return(y);
+#else
+        if ((y = setcc("1",&z)) < 0)
+            return(y);
+#endif /* DOOMSDAY */
+        if (xx == XYRECV)
+          stchr = (CHAR) z;
+        else {
+            mystch = (CHAR) z;
+#ifdef IKS_OPTION
+            /* If IKS negotiation in use   */
+            if (TELOPT_U(TELOPT_KERMIT) || TELOPT_ME(TELOPT_KERMIT))
+              tn_siks(KERMIT_SOP);      /* Report change to other side */
+#endif /* IKS_OPTION */
+        }
+        return(success = y);
+
+      case XYNPAD:                      /* PADDING */
+        y = cmnum("How many padding characters for inbound packets","0",10,&x,
+                  xxstring);
+        if ((y = setnum(&z,x,y,94)) < 0) return(y);
+        if (xx == XYRECV)
+          mypadn = (CHAR) z;
+        else
+          npad = (CHAR) z;
+        return(success = y);
+
+      case XYPADC:                      /* PAD-CHARACTER */
+        if ((y = setcc("0",&z)) < 0) return(y);
+        if (xx == XYRECV) mypadc = z; else padch = z;
+        return(success = y);
+
+      case XYTIMO:                      /* TIMEOUT */
+        if (xx == XYRECV) {
+            y = cmnum("Packet timeout interval",ckitoa(URTIME),10,&x,xxstring);
+            if ((y = setnum(&z,x,y,94)) < 0) return(y);
+
+            if (rmsflg) {               /* REMOTE SET RECEIVE TIMEOUT */
+                sstate = setgen('S', "402", ckitoa(z), "");
+                return((int) sstate);
+            } else {                    /* SET RECEIVE TIMEOUT */
+                pkttim = z;             /*   Value to put in my negotiation */
+            }                           /*   packet for other Kermit to use */
+
+        } else {                        /* SET SEND TIMEOUT */
+#ifdef CK_TIMERS
+            extern int rttflg, mintime, maxtime;
+            int tmin = 0, tmax = 0;
+#endif /* CK_TIMERS */
+            y = cmnum("Packet timeout interval",ckitoa(DMYTIM),10,&x,xxstring);
+            if (y == -3) {              /* They cancelled a previous */
+                x = DMYTIM;             /* SET SEND command, so restore */
+                timef = 0;              /* and turn off the override flag */
+                y = cmcfm();
+            }
+#ifdef CK_TIMERS
+            if (y < 0) return(y);
+            if (x < 0) {
+                printf("?Out of range - %d\n",x);
+                return(-9);
+            }
+            if ((z = cmkey(timotab,2,"","dynamic",xxstring)) < 0) return(z);
+            if (z) {
+                if ((y = cmnum("Minimum timeout to allow",
+                               "1",10,&tmin,xxstring)) < 0)
+                  return(y);
+                if (tmin < 1) {
+                    printf("?Out of range - %d\n",tmin);
+                    return(-9);
+                }
+                if ((y = cmnum("Maximum timeout to allow",
+                               "0",10,&tmax,xxstring)) < 0)
+                  return(y);
+                /* 0 means let Kermit choose, < 0 means no maximum */
+            }
+            if ((y = cmcfm()) < 0)
+              return(y);
+            rttflg = z;                 /* Round-trip timer flag */
+            z = x;
+#else
+            if ((y = setnum(&z,x,y,94)) < 0)
+              return(y);
+#endif /* CK_TIMERS */
+            timef = 1;                  /* Turn on the override flag */
+            timint = rtimo = z;         /* Override value for me to use */
+#ifdef CK_TIMERS
+            if (rttflg) {               /* Lower and upper bounds */
+                mintime = tmin;
+                maxtime = tmax;
+            }
+#endif /* CK_TIMERS */
+        }
+        return(success = 1);
+
+      case XYFPATH:                     /* PATHNAMES */
+        if (xx == XYRECV) {
+            y = cmkey(rpathtab,nrpathtab,"","auto",xxstring);
+        } else {
+            y = cmkey(pathtab,npathtab,"","off",xxstring);
+        }
+        if (y < 0) return(y);
+
+        if ((x = cmcfm()) < 0) return(x);
+        if (xx == XYRECV) {             /* SET RECEIVE PATHNAMES */
+            fnrpath = y;
+            ptab[protocol].fnrp = fnrpath;
+        } else {                        /* SET SEND PATHNAMES */
+            fnspath = y;
+            ptab[protocol].fnsp = fnspath;
+        }
+        return(success = 1);            /* Note: 0 = ON, 1 = OFF */
+        /* In other words, ON = leave pathnames ON, OFF = take them off. */
+
+      case XYPAUS:                      /* SET SEND/RECEIVE PAUSE */
+        y = cmnum("Milliseconds to pause between packets","0",10,&x,xxstring);
+        if ((y = setnum(&z,x,y,15000)) < 0)
+          return(y);
+        pktpaus = z;
+        return(success = 1);
+
+#ifdef CKXXCHAR                         /* SET SEND/RECEIVE IGNORE/DOUBLE */
+      case XYIGN:
+      case XYDBL: {
+          int i, zz;
+          short *p;
+          extern short dblt[];
+          extern int dblflag, ignflag;
+
+          /* Make space for a temporary copy of the ignore/double table */
+
+          zz = y;
+#ifdef COMMENT
+          if (zz == XYIGN && xx == XYSEND) {
+              blah blah who cares
+          }
+          if (zz == XYDBL && xx == XYRECV) {
+              blah blah
+          }
+#endif /* COMMENT */
+          p = (short *)malloc(256 * sizeof(short));
+          if (!p) {
+              printf("?Internal error - malloc failure\n");
+              return(-9);
+          }
+          for (i = 0; i < 256; i++) p[i] = dblt[i]; /* Copy current table */
+
+          while (1) {                   /* Collect a list of numbers */
+#ifndef NOSPL
+              x_ifnum = 1;              /* Turn off complaints from eval() */
+#endif /* NOSPL */
+              if ((x = cmnum(zz == XYDBL ?
+                             "Character to double" :
+                             "Character to ignore",
+                             "",10,&y,xxstring
+                             )) < 0) {
+#ifndef NOSPL
+                  x_ifnum = 0;
+#endif /* NOSPL */
+                  if (x == -3)          /* Done */
+                    break;
+                  if (x == -2) {
+                      if (p) { free(p); p = NULL; }
+                      debug(F110,"SET S/R DOUBLE/IGNORE atmbuf",atmbuf,0);
+                      if (!ckstrcmp(atmbuf,"none",4,0) ||
+                          !ckstrcmp(atmbuf,"non",3,0) ||
+                          !ckstrcmp(atmbuf,"no",2,0) ||
+                          !ckstrcmp(atmbuf,"n",1,0)) {
+                          if ((x = cmcfm()) < 0) /* Get confirmation */
+                            return(x);
+                          for (y = 0; y < 256; y++)
+                            dblt[y] &= (zz == XYDBL) ? 1 : 2;
+                          if (zz == XYDBL) dblflag = 0;
+                          if (zz == XYIGN) ignflag = 0;
+                          return(success = 1);
+                      } else {
+                          printf(
+                            "?Please specify a number or the word NONE\n");
+                          return(-9);
+                      }
+                  } else {
+                      free(p);
+                      p = NULL;
+                      return(x);
+                  }
+              }
+#ifndef NOSPL
+              x_ifnum = 0;
+#endif /* NOSPL */
+              if (y < 0 || y > 255) {
+                  printf("?Please enter a character code in range 0-255\n");
+                  free(p);
+                  p = NULL;
+                  return(-9);
+              }
+              p[y] |= (zz == XYDBL) ? 2 : 1;
+              if (zz == XYDBL) dblflag = 1;
+              if (zz == XYIGN) ignflag = 1;
+          } /* End of while loop */
+
+          if ((x = cmcfm()) < 0) return(x);
+/*
+  Get here only if they have made no mistakes.  Copy temporary table back to
+  permanent one, then free temporary table and return successfully.
+*/
+          if (p) {
+              for (i = 0; i < 256; i++) dblt[i] = p[i];
+              free(p);
+              p = NULL;
+          }
+          return(success = 1);
+      }
+#endif /* CKXXCHAR */
+
+#ifdef PIPESEND
+      case XYFLTR: {                    /* SET { SEND, RECEIVE } FILTER */
+          if ((y = cmtxt((xx == XYSEND) ?
+                "Filter program for sending files -\n\
+ use \\v(filename) to substitute filename" :
+                "Filter program for receiving files -\n\
+ use \\v(filename) to substitute filename",
+                         "",&s,NULL)) < 0)
+            return(y);
+          if (!*s) {                    /* Removing a filter... */
+              if (xx == XYSEND && sndfilter) {
+                  makestr(&g_sfilter,NULL);
+                  makestr(&sndfilter,NULL);
+              } else if (rcvfilter) {
+                  makestr(&g_rfilter,NULL);
+                  makestr(&rcvfilter,NULL);
+              }
+              return(success = 1);
+          }                             /* Adding a filter... */
+          s = brstrip(s);               /* Strip any braces */
+          y = strlen(s);
+          if (xx == XYSEND) {           /* For SEND filter... */
+              for (x = 0; x < y; x++) { /* make sure they included "\v(...)" */
+                  if (s[x] != '\\') continue;
+                  if (s[x+1] == 'v') break;
+              }
+              if (x == y) {
+                  printf(
+              "?Filter must contain a replacement variable for filename.\n"
+                         );
+                  return(-9);
+              }
+          }
+          if (xx == XYSEND) {
+              makestr(&sndfilter,s);
+              makestr(&g_sfilter,s);
+          } else {
+              makestr(&rcvfilter,s);
+              makestr(&g_rfilter,s);
+          }
+          return(success = 1);
+      }
+#endif /* PIPESEND */
+
+      case XYINIL:
+        y = cmnum("Max length for protocol init string","-1",10,&x,xxstring);
+        if ((y = setnum(&z,x,y,-1)) < 0)
+          return(y);
+        if (xx == XYSEND)
+          sprmlen = z;
+        else
+          rprmlen = z;
+        return(success = 1);
+
+      case 993: {
+          extern int sendipkts;
+          if (xx == XYSEND) {
+              if ((x = seton(&sendipkts)) < 0)
+                return(x);
+          }
+          return(1);
+      }
+#ifdef CK_PERMS
+      case 994:
+       switch(xx) {
+         case XYSEND:
+           if ((x = seton(&atlpro)) < 0) return(x);
+           atgpro = atlpro;
+           return(1);
+         case XYRECV:
+           if ((x = seton(&atlpri)) < 0) return(x);
+           atgpri = atlpri;
+           return(1);
+         default:
+           return(-2);
+       }
+#endif /* CK_PERMS */
+
+#ifndef NOCSETS
+      case XYCSET: {                    /* CHARACTER-SET-SELECTION */
+          extern struct keytab xfrmtab[];
+          extern int r_cset, s_cset;
+          if ((y = cmkey(xfrmtab,2,"","automatic",xxstring)) < 0)
+            return(y);
+          if ((x = cmcfm()) < 0)
+            return(x);
+          if (xx == XYSEND)
+            s_cset = y;
+          else
+            r_cset = y;
+          return(success = 1);
+      }
+#endif /* NOCSETS */
+
+      case XYBUP:
+        if ((y = cmkey(onoff,2,"","on",xxstring)) < 0)
+          return(y);
+        if ((x = cmcfm()) < 0) return(x);
+        if (xx == XYSEND) {
+            extern int skipbup;
+            skipbup = (y == 0) ? 1 : 0;
+            return(success = 1);
+        } else {
+            printf(
+"?Please use SET FILE COLLISION to choose the desired action\n");
+            return(-9);
+        }
+
+      case XYMOVE:
+#ifdef COMMENT
+        y = cmdir("Directory to move file(s) to after successful transfer",
+                  "",&s,xxstring);
+#else
+        y = cmtxt("Directory to move file(s) to after successful transfer",
+                 "",&s,xxstring);
+#endif /* COMMENT */
+
+        if (y < 0 && y != -3)
+          return(y);
+        ckstrncpy(line,s,LINBUFSIZ);
+        s = brstrip(line);
+
+#ifdef COMMENT
+       /* Only needed for cmdir() */
+        if ((x = cmcfm()) < 0)
+          return(x);
+#endif /* COMMENT */
+       
+       /* Check directory existence if absolute */
+       /* THIS MEANS IT CAN'T INCLUDE ANY DEFERRED VARIABLES! */
+       if (s) if (*s) {
+           if (isabsolute(s) && !isdir(s)) {
+               printf("?Directory does not exist - %s\n",s);
+               return(-9);
+           }
+       }
+        if (xx == XYSEND) {
+            if (*s) {
+#ifdef COMMENT
+               /* Allow it to be relative */
+                zfnqfp(s,LINBUFSIZ,line);
+#endif /* COMMENT */
+                makestr(&snd_move,line);
+                makestr(&g_snd_move,line);
+            } else {
+                makestr(&snd_move,NULL);
+                makestr(&g_snd_move,NULL);
+            }
+        } else {
+            if (*s) {
+#ifdef COMMENT
+               /* Allow it to be relative */
+                zfnqfp(s,LINBUFSIZ,line);
+#endif /* COMMENT */
+                makestr(&rcv_move,line);
+                makestr(&g_rcv_move,line);
+            } else {
+                makestr(&rcv_move,NULL);
+                makestr(&g_rcv_move,NULL);
+            }
+        }
+        return(success = 1);
+
+      case XYRENAME:
+        y = cmtxt("Template to rename file(s) to after successful transfer",
+                  "",&s,NULL);         /* NOTE: no xxstring */
+        if (y < 0 && y != -3)          /* Evaluation is deferred */
+          return(y);
+        ckstrncpy(line,s,LINBUFSIZ);
+        s = brstrip(line);
+        if ((x = cmcfm()) < 0)
+          return(x);
+        if (xx == XYSEND) {
+            if (*s) {
+                makestr(&snd_rename,s);
+                makestr(&g_snd_rename,s);
+            } else {
+                makestr(&snd_rename,NULL);
+                makestr(&g_snd_rename,NULL);
+            }
+        } else {
+            if (*s) {
+                makestr(&rcv_rename,s);
+                makestr(&g_rcv_rename,s);
+            } else {
+                makestr(&rcv_rename,NULL);
+                makestr(&g_rcv_rename,NULL);
+            }
+        }
+        return(success = 1);
+
+#ifdef VMS
+      case 887:                                /* VERSION-NUMBERS */
+        if (xx == XYSEND) {
+            extern int vmssversions;
+            return(seton(&vmssversions));
+        } else {
+            extern int vmsrversions;
+            return(seton(&vmsrversions));
+        }
+#endif /* VMS */
+
+      default:
+        return(-2);
+    }                                   /* End of SET SEND/RECEIVE... */
+}
+#endif /* NOXFER */
+
+#ifndef NOXMIT
+int
+setxmit() {
+    if ((y = cmkey(xmitab,nxmit,"","",xxstring)) < 0) return(y);
+    switch (y) {
+      case XMITE:                       /* EOF */
+        y = cmtxt("Characters to send at end of file,\n\
+ Use backslash codes for control characters","",&s,xxstring);
+        if (y < 0) return(y);
+        if ((int)strlen(s) > XMBUFL) {
+            printf("?Too many characters, %d maximum\n",XMBUFL);
+            return(-2);
+        }
+        ckstrncpy(xmitbuf,s,XMBUFL);
+        return(success = 1);
+
+      case XMITF:                       /* Fill */
+        y = cmnum("Numeric code for blank-line fill character","0",10,&x,
+                  xxstring);
+        if ((y = setnum(&z,x,y,127)) < 0) return(y);
+        xmitf = z;
+        return(success = 1);
+      case XMITL:                       /* Linefeed */
+        return(seton(&xmitl));
+      case XMITS:                       /* Locking-Shift */
+        return(seton(&xmits));
+      case XMITP:                       /* Prompt */
+        y = cmnum("Numeric code for host's prompt character, 0 for none",
+                  "10",10,&x,xxstring);
+        if ((y = setnum(&z,x,y,127)) < 0) return(y);
+        xmitp = z;
+        return(success = 1);
+      case XMITX:                       /* Echo */
+        return(seton(&xmitx));
+      case XMITW:                       /* Pause */
+        y = cmnum("Number of milliseconds to pause between binary characters\n\
+or text lines during transmission","0",10,&x,xxstring);
+        if ((y = setnum(&z,x,y,1000)) < 0) return(y);
+        xmitw = z;
+        return(success = 1);
+      case XMITT:                       /* Timeout */
+        y = cmnum("Seconds to wait for each character to echo",
+                  "1",10,&x,xxstring);
+        if ((y = setnum(&z,x,y,1000)) < 0) return(y);
+        xmitt = z;
+        return(success = 1);
+      default:
+        return(-2);
+    }
+}
+#endif /* NOXMIT */
+
+#ifndef NOXFER
+/*  D O R M T  --  Do a remote command  */
+
+VOID
+rmsg() {
+    if (pflag && !quiet && fdispla != XYFD_N)
+      printf(
+#ifdef CK_NEED_SIG
+       " Type your escape character, %s, followed by X or E to cancel.\n",
+       dbchr(escape)
+#else
+       " Press the X or E key to cancel.\n"
+#endif /* CK_NEED_SIG */
+      );
+}
+
+static int xzcmd = 0;                   /* Global copy of REMOTE cmd index */
+
+/*  R E M C F M  --  Confirm a REMOTE command  */
+/*
+  Like cmcfm(), but allows for a redirection indicator on the end,
+  like "> filename" or "| command".  Returns what cmcfm() would have
+  returned: -1 if reparse needed, etc etc blah blah.  On success,
+  returns 1 with:
+
+    char * remdest containing the name of the file or command.
+    int remfile set to 1 if there is to be any redirection.
+    int remappd set to 1 if output file is to be appended to.
+    int rempipe set to 1 if remdest is a command, 0 if it is a file.
+*/
+static int
+remcfm() {
+    int x;
+    char *s;
+    char c;
+
+    remfile = 0;
+    rempipe = 0;
+    remappd = 0;
+
+    if ((x = cmtxt(
+             "> filename, | command,\n\
+or type carriage return to confirm the command",
+                   "",&s,xxstring)) < 0)
+      return(x);
+    if (remdest) {
+        free(remdest);
+        remdest = NULL;
+    }
+    debug(F101,"remcfm local","",local);
+    debug(F110,"remcfm s",s,0);
+    debug(F101,"remcfm cmd","",xzcmd);
+
+    if (!*s) {                          /* No redirection indicator */
+        if (!local &&
+            (xzcmd == XZDIR || xzcmd == XZTYP ||
+             xzcmd == XZXIT || xzcmd == XZSPA ||
+             xzcmd == XZHLP || xzcmd == XZPWD ||
+             xzcmd == XZLGI || xzcmd == XZLGO ||
+             xzcmd == XZWHO || xzcmd == XZHOS)) {
+            printf("?\"%s\" has no effect in remote mode\n",cmdbuf);
+            return(-9);
+        } else
+          return(1);
+    }
+    c = *s;                             /* We have something */
+    if (c != '>' && c != '|') {         /* Is it > or | ? */
+        printf("?Not confirmed\n");     /* No */
+        return(-9);
+    }
+    s++;                                /* See what follows */
+    if (c == '>' && *s == '>') {        /* Allow for ">>" too */
+        s++;
+        remappd = 1;                    /* Append to output file */
+    }
+    while (*s == SP || *s == HT) s++;   /* Strip intervening whitespace */
+    if (!*s) {
+        printf("?%s missing\n", c == '>' ? "Filename" : "Command");
+        return(-9);
+    }
+    if (c == '>' && zchko(s) < 0) {     /* Check accessibility */
+        printf("?Access denied - %s\n", s);
+        return(-9);
+    }
+    remfile = 1;                        /* Set global results */
+    rempipe = (c == '|');
+    if (rempipe
+#ifndef NOPUSH
+        && nopush
+#endif /* NOPUSH */
+        ) {
+        printf("?Sorry, access to external commands is disabled.\n");
+        return(-9);
+    }
+    makestr(&remdest,s);
+#ifndef NODEBUG
+    if (deblog) {
+        debug(F101,"remcfm remfile","",remfile);
+        debug(F101,"remcfm remappd","",remappd);
+        debug(F101,"remcfm rempipe","",rempipe);
+        debug(F110,"remcfm remdest",remdest, 0);
+    }
+#endif /* NODEBUG */
+    return(1);
+}
+
+/*  R E M T X T  --  Like remcfm()...  */
+/*
+   ... but for REMOTE commands that end with cmtxt().
+   Here we must decipher braces to discover whether the trailing
+   redirection indicator is intended for local use, or to be sent out
+   to the server, as in:
+
+     remote host blah blah > file                 This end
+     remote host { blah blah } > file             This end
+     remote host { blah blah > file }             That end
+     remote host { blah blah > file } > file      Both ends
+
+   Pipes too:
+
+     remote host blah blah | cmd                  This end
+     remote host { blah blah } | cmd              This end
+     remote host { blah blah | cmd }              That end
+     remote host { blah blah | cmd } | cmd        Both ends
+
+   Or both:
+
+     remote host blah blah | cmd > file           This end, etc etc...
+
+   Note: this really only makes sense for REMOTE HOST, but why be picky?
+   Call after calling cmtxt(), with pointer to string that cmtxt() parsed,
+   as in "remtxt(&s);".
+
+   Returns:
+    1 on success with braces & redirection things removed & pointer updated,
+   -9 on failure (bad indirection), after printing error message.
+*/
+int
+remtxt(p) char ** p; {
+    int i, x, bpos, ppos;
+    char c, *s, *q;
+
+    remfile = 0;                        /* Initialize global results */
+    rempipe = 0;
+    remappd = 0;
+    if (remdest) {
+        free(remdest);
+        remdest = NULL;
+    }
+    s = *p;
+    if (!s)                             /* No redirection indicator */
+      s = "";
+    if (!*s) {                          /* Ditto */
+        if (!local &&
+            (xzcmd == XZDIR || xzcmd == XZTYP ||
+             xzcmd == XZXIT || xzcmd == XZSPA ||
+             xzcmd == XZHLP || xzcmd == XZPWD ||
+             xzcmd == XZLGI || xzcmd == XZLGO ||
+             xzcmd == XZWHO || xzcmd == XZHOS)) {
+            printf("?\"%s\" has no effect in remote mode\n",cmdbuf);
+            if (hints) {
+                printf("Hint: Try again with an output redirector.\n");
+            }
+            return(-9);
+        } else
+          return(1);
+    }
+    bpos = -1;                          /* Position of > (bracket) */
+    ppos = -1;                          /* Position of | (pipe) */
+    x = strlen(s);                      /* Length of cmtxt() string */
+
+    for (i = x-1; i >= 0; i--) {        /* Search right to left. */
+        c = s[i];
+        if (c == '}')                   /* Break on first right brace */
+          break;                        /* Don't look at contents of braces */
+        else if (c == '>')              /* Record position of > */
+          bpos = i;
+        else if (c == '|')              /* and of | */
+          ppos = i;
+    }
+    if (bpos < 0 && ppos < 0) {         /* No redirectors. */
+        if (!local &&
+            (xzcmd == XZDIR || xzcmd == XZTYP ||
+             xzcmd == XZXIT || xzcmd == XZSPA ||
+             xzcmd == XZHLP || xzcmd == XZPWD ||
+             xzcmd == XZLGI || xzcmd == XZLGO ||
+             xzcmd == XZWHO || xzcmd == XZHOS)) {
+            printf("?\"%s\" has no effect in remote mode\n",cmdbuf);
+            if (hints) {
+                printf("Hint: Try again with an output redirector.\n");
+            }
+            return(-9);
+        }
+        s = brstrip(s);                 /* Remove outer braces if any. */
+        *p = s;                         /* Point to result */
+        return(1);                      /* and return. */
+    }
+    remfile = 1;                        /* It's | or > */
+    i = -1;                             /* Get leftmost symbol */
+    if (bpos > -1)                      /* Bracket */
+      i = bpos;
+    if (ppos > -1 && (ppos < bpos || bpos < 0)) { /* or pipe */
+        i = ppos;
+        rempipe = 1;
+    }
+    if (rempipe
+#ifndef NOPUSH
+        && nopush
+#endif /* NOPUSH */
+        ) {
+        printf("?Sorry, access to external commands is disabled.\n");
+        return(-9);
+    }
+    c = s[i];                           /* Copy of symbol */
+
+    if (c == '>' && s[i+1] == '>')      /* ">>" for append? */
+      remappd = 1;                     /* It's not just a flag it's a number */
+
+    q = s + i + 1 + remappd;            /* Point past symbol in string */
+    while (*q == SP || *q == HT) q++;   /* and any intervening whitespace */
+    if (!*q) {
+        printf("?%s missing\n", c == '>' ? "Filename" : "Command");
+        return(-9);
+    }
+    if (c == '>' && zchko(q) < 0) {     /* (Doesn't work for | cmd > file) */
+        printf("?Access denied - %s\n", q);
+        return(-9);
+    }
+    makestr(&remdest,q);                /* Create the destination string */
+    q = s + i - 1;                      /* Point before symbol */
+    while (q > s && (*q == SP || *q == HT)) /* Strip trailing whitespace */
+      q--;
+    *(q+1) = NUL;                       /* Terminate the string. */
+    s = brstrip(s);                     /* Remove any braces */
+    *p = s;                             /* Set return value */
+
+#ifndef NODEBUG
+    if (deblog) {
+        debug(F101,"remtxt remfile","",remfile);
+        debug(F101,"remtxt remappd","",remappd);
+        debug(F101,"remtxt rempipe","",rempipe);
+        debug(F110,"remtxt remdest",remdest, 0);
+        debug(F110,"remtxt command",s,0);
+    }
+#endif /* NODEBUG */
+
+    return(1);
+}
+
+int
+plogin(xx) int xx; {
+    char *p1 = NULL, *p2 = NULL, *p3 = NULL;
+    int psaved = 0, rc = 0;
+#ifdef CK_RECALL
+    extern int on_recall;               /* around Password prompting */
+#endif /* CK_RECALL */
+    debug(F101,"plogin local","",local);
+
+    if (!local || (network && ttchk() < 0)) {
+        printf("?No connection\n");
+        return(-9);
+    }
+    if ((x = cmfld("User ID","",&s,xxstring)) < 0) { /* Get User ID */
+        if (x != -3) return(x);
+    }
+    y = strlen(s);
+    if (y > 0) {
+        if ((p1 = malloc(y + 1)) == NULL) {
+            printf("?Internal error: malloc\n");
+            rc = -9;
+            goto XZXLGI;
+        } else
+          strcpy(p1,s);                 /* safe */
+        if ((rc = cmfld("Password","",&s,xxstring)) < 0)
+          if (rc != -3) goto XZXLGI;
+        y = strlen(s);
+        if (y > 0) {
+            if ((p2 = malloc(y + 1)) == NULL) {
+                printf("?Internal error: malloc\n");
+                rc = -9;
+                goto XZXLGI;
+            } else
+              strcpy(p2,s);             /* safe */
+            if ((rc = cmfld("Account","",&s,xxstring)) < 0)
+              if (rc != -3) goto XZXLGI;
+            y = strlen(s);
+            if (y > 0) {
+                if ((p3 = malloc(y + 1)) == NULL) {
+                    printf("?Internal error: malloc\n");
+                    rc = -9;
+                    goto XZXLGI;
+                } else
+                  strcpy(p3,s);         /* safe */
+            }
+        }
+    }
+    if ((rc = remtxt(&s)) < 0)          /* Confirm & handle redirectors */
+      goto XZXLGI;
+
+    if (!p1) {                          /* No Userid specified... */
+        debok = 0;                      /* Don't log this */
+        /* Prompt for username, password, and account */
+#ifdef CK_RECALL
+        on_recall = 0;
+#endif /* CK_RECALL */
+        cmsavp(psave,PROMPTL);          /* Save old prompt */
+        psaved = 1;
+        debug(F110,"REMOTE LOGIN saved",psave,0);
+
+        cmsetp("Username: ");           /* Make new prompt */
+        concb((char)escape);            /* Put console in cbreak mode */
+        cmini(1);
+        prompt(xxstring);
+        rc = -9;
+        for (x = -1; x < 0; ) {         /* Prompt till they answer */
+            cmres();                    /* Reset the parser */
+            x = cmtxt("","",&s,NULL);   /* Get a literal line of text */
+        }
+        y = strlen(s);
+        if (y < 1) {
+            printf("?Canceled\n");
+            goto XZXLGI;
+        }
+        if ((p1 = malloc(y + 1)) == NULL) {
+            printf("?Internal error: malloc\n");
+            goto XZXLGI;
+        } else
+          strcpy(p1,s);                 /* safe */
+
+        cmsetp("Password: ");           /* Make new prompt */
+        concb((char)escape);            /* Put console in cbreak mode */
+        cmini(0);                       /* No echo */
+        prompt(xxstring);
+        debok = 0;
+        for (x = -1; x < 0 && x != -3; ) { /* Get answer */
+            cmres();                    /* Reset the parser */
+            x = cmtxt("","",&s,NULL);   /* Get literal line of text */
+        }
+        if ((p2 = malloc((int)strlen(s) + 1)) == NULL) {
+            printf("?Internal error: malloc\n");
+            goto XZXLGI;
+        } else
+          strcpy(p2,s);                 /* safe */
+        printf("\r\n");
+        if ((rc = cmcfm()) < 0)
+          goto XZXLGI;
+    }
+    sstate = setgen('I',p1,p2,p3);      /* Get here with at least user ID */
+    rc = 0;
+
+  XZXLGI:                               /* Common exit point */
+    if (psaved)
+      cmsetp(psave);                    /* Restore original prompt */
+    if (p3) { free(p3); p3 = NULL; }    /* Free malloc'd storage */
+    if (p2) { free(p2); p2 = NULL; }
+    if (p1) { free(p1); p1 = NULL; }
+    if (rc > -1) {
+        if (local && rc > -1)           /* If local, flush tty input buffer */
+          ttflui();
+    }
+    return(rc);
+}
+
+#ifdef OS2
+#ifndef NOLOCAL
+int
+dormt(xx) int xx; {
+    int rc = 0;
+    extern int term_io;
+    int term_io_sav = term_io;
+#ifdef NEWFTP
+    extern int ftpget, ftpisopen();
+    if ((ftpget == 1) || ((ftpget == 2) && ftpisopen()))
+      return(doftprmt(xx,0));
+#endif /* NEWFTP */
+    term_io = 0;
+    rc = xxdormt(xx);
+    term_io = term_io_sav;
+    return rc;
+}
+
+
+int
+xxdormt(xx) int xx;
+#else /* NOLOCAL */
+int
+dormt(xx) int xx;
+#endif /* NOLOCAL */
+#else /* OS2 */
+int
+dormt(xx) int xx;
+#endif /* OS2 */
+{                                       /* REMOTE commands */
+    int x, y, retcode;
+    char *s, sbuf[50], *s2;
+
+#ifdef NEWFTP
+    extern int ftpget, ftpisopen();
+    if ((ftpget == 1) || ((ftpget == 2) && ftpisopen()))
+      return(doftprmt(xx,0));
+#endif /* NEWFTP */
+
+    remfile = 0;                        /* Clear these */
+    rempipe = 0;
+    remappd = 0;
+
+    if (xx < 0) return(xx);             /* REMOTE what? */
+
+    xzcmd = xx;                         /* Make global copy of arg */
+
+    if (xx == XZSET) {                  /* REMOTE SET */
+        if ((y = cmkey(rmstab,nrms,"","",xxstring)) < 0) {
+            if (y == -3) {
+                printf("?Parameter name required\n");
+                return(-9);
+            } else return(y);
+        }
+        return(doprm(y,1));
+    }
+
+    switch (xx) {                       /* Others... */
+
+      case XZCDU:
+        if ((x = cmcfm()) < 0) return(x);
+        printf("?Sorry, REMOTE CDUP not supported yet\n");
+        return(-9);
+
+      case XZCWD:                       /* CWD (CD) */
+        if ((x = cmtxt("Remote directory name","",&s,xxstring)) < 0)
+          return(x);
+        if ((x = remtxt(&s)) < 0)
+          return(x);
+        debug(F111,"XZCWD: ",s,x);
+        *sbuf = NUL;
+        s2 = sbuf;
+/*
+  The following is commented out because since the disappearance of the
+  DECSYSTEM-20 from the planet, no known computer requires a password for
+  changing directory.
+*/
+#ifdef DIRPWDPR
+        if (*s != NUL) {                /* If directory name given, */
+                                        /* get password on separate line. */
+            if (tlevel > -1) {          /* From take file... */
+
+                if (fgets(sbuf,50,tfile[tlevel]) == NULL)
+                  fatal("take file ends prematurely in 'remote cwd'");
+                debug(F110," pswd from take file",s2,0);
+                for (x = (int)strlen(sbuf);
+                     x > 0 && (sbuf[x-1] == NL || sbuf[x-1] == CR);
+                     x--)
+                  sbuf[x-1] = '\0';
+
+            } else {                    /* From terminal... */
+
+                printf(" Password: ");  /* get a password */
+#ifdef IKSD
+                if (!local && inserver) {
+                    x = coninc(0);
+                } else
+#endif /* IKSD */
+#ifdef OS2
+                  x = is_a_tty(0) ? coninc(0) : /* with no echo ... */
+                    getchar();
+#else /* OS2 */
+                x = getchar();
+#endif /* OS2 */
+                while ((x != NL) && (x != CR)) {
+                    if ((x &= 0177) == '?') {
+                        printf("? Password of remote directory\n Password: ");
+                        s2 = sbuf;
+                        *sbuf = NUL;
+                    } else if (x == ESC) /* Mini command line editor... */
+                      bleep(BP_WARN);
+                    else if (x == BS || x == 0177)
+                      s2--;
+                    else if (x == 025) {        /* Ctrl-U */
+                        s2 = sbuf;
+                        *sbuf = NUL;
+                    } else
+                      *s2++ = x;
+
+                    /* Get the next character */
+#ifdef IKSD
+                    if (!local && inserver) {
+                        x = coninc(0);
+                    } else
+#endif /* IKSD */
+#ifdef OS2
+                    x = is_a_tty(0) ? coninc(0) : /* with no echo ... */
+                      getchar();
+#else /* OS2 */
+                    x = getchar();
+#endif /* OS2 */
+                }
+                *s2 = NUL;
+                putchar('\n');
+            }
+            s2 = sbuf;
+        } else s2 = "";
+#endif /* DIRPWDPR */
+
+        debug(F110," password",s2,0);
+       rcdactive = 1;
+        sstate = setgen('C',s,s2,"");
+        retcode = 0;
+        break;
+
+      case XZDEL:                               /* Delete */
+        if ((x = cmtxt("Name of remote file(s) to delete",
+                       "",&s,xxstring)) < 0) {
+            if (x == -3) {
+                printf("?Name of remote file(s) required\n");
+                return(-9);
+            } else return(x);
+        }
+        if ((x = remtxt(&s)) < 0)
+          return(x);
+        if (local) ttflui();            /* If local, flush tty input buffer */
+        retcode = sstate = rfilop(s,'E');
+        break;
+
+      case XZDIR:                       /* Directory */
+        if ((x = cmtxt("Remote directory or file specification","",&s,
+                       xxstring)) < 0)
+          return(x);
+        if ((x = remtxt(&s)) < 0)
+          return(x);
+        if (local) ttflui();            /* If local, flush tty input buffer */
+        rmsg();
+        retcode = sstate = setgen('D',s,"","");
+        break;
+
+      case XZHLP:                       /* Help */
+        if ((x = remcfm()) < 0) return(x);
+        sstate = setgen('H',"","","");
+        retcode = 0;
+        break;
+
+      case XZHOS:                       /* Host */
+        if ((x = cmtxt("Command for remote system","",&s,xxstring)) < 0)
+          return(x);
+        if ((x = remtxt(&s)) < 0)
+          return(x);
+        if ((y = (int)strlen(s)) < 1)
+          return(x);
+        ckstrncpy(line,s,LINBUFSIZ);
+        cmarg = line;
+        rmsg();
+        retcode = sstate = 'c';
+        break;
+
+#ifndef NOFRILLS
+      case XZKER:
+        if ((x = cmtxt("Command for remote Kermit","",&s,xxstring)) < 0)
+          return(x);
+        if ((x = remtxt(&s)) < 0)
+          return(x);
+        if ((int)strlen(s) < 1)  {
+            if (x == -3) {
+                printf("?Remote Kermit command required\n");
+                return(-9);
+            } else return(x);
+        }
+        ckstrncpy(line,s,LINBUFSIZ);
+        cmarg = line;
+        retcode = sstate = 'k';
+        rmsg();
+        break;
+
+      case XZLGI:                       /* Login */
+       rcdactive = 1;                  /* Suppress "Logged in" msg if quiet */
+        return(plogin(XXREM));
+
+      case XZLGO: {                     /* Logout */
+          extern int bye_active;
+          if ((x = remcfm()) < 0) return(x);
+          sstate = setgen('I',"","","");
+          retcode = 0;
+          bye_active = 1;               /* Close connection when done */
+          break;
+      }
+
+      case XZPRI:                       /* Print */
+        if (!atdiso || !atcapr) {       /* Disposition attribute off? */
+            printf("?Disposition Attribute is Off\n");
+            return(-2);
+        }
+        cmarg = "";
+        cmarg2 = "";
+        if ((x = cmifi("Local file(s) to print on remote printer","",&s,&y,
+                       xxstring)) < 0) {
+            if (x == -3) {
+                printf("?Name of local file(s) required\n");
+                return(-9);
+            }
+            return(x);
+        }
+        ckstrncpy(line,s,LINBUFSIZ);    /* Make a safe copy of filename */
+        *optbuf = NUL;                  /* Wipe out any old options */
+        if ((x = cmtxt("Options for remote print command","",&s,xxstring)) < 0)
+          return(x);
+        if ((x = remtxt(&s)) < 0)
+          return(x);
+        if ((int)strlen(optbuf) > 94) { /* Make sure this is legal */
+            printf("?Option string too long\n");
+            return(-9);
+        }
+        ckstrncpy(optbuf,s,OPTBUFLEN);  /* Make a safe copy of options */
+        nfils = -1;                     /* Expand file list internally */
+        cmarg = line;                   /* Point to file list. */
+        rprintf = 1;                    /* REMOTE PRINT modifier for SEND */
+        sstate = 's';                   /* Set start state to SEND */
+        if (local) displa = 1;
+        retcode = 0;
+        break;
+#endif /* NOFRILLS */
+
+      case XZSPA:                       /* Space */
+        if ((x = cmtxt("Confirm, or remote directory name",
+                       "",&s,xxstring)) < 0)
+          return(x);
+        if ((x = remtxt(&s)) < 0)
+          return(x);
+        retcode = sstate = setgen('U',s,"","");
+        break;
+
+#ifndef NOFRILLS
+      case XZTYP:                       /* Type */
+        if ((x = cmtxt("Remote file specification","",&s,xxstring)) < 0)
+          return(x);
+        if ((int)strlen(s) < 1) {
+            printf("?Remote filename required\n");
+            return(-9);
+        }
+        if ((x = remtxt(&s)) < 0)
+          return(x);
+        rmsg();
+        retcode = sstate = rfilop(s,'T');
+        break;
+#endif /* NOFRILLS */
+
+#ifndef NOFRILLS
+      case XZWHO:
+        if ((x = cmtxt("Remote user name, or carriage return",
+                       "",&s,xxstring)) < 0)
+          return(x);
+        if ((x = remtxt(&s)) < 0)
+          return(x);
+        retcode = sstate = setgen('W',s,"","");
+        break;
+#endif /* NOFRILLS */
+
+      case XZPWD:                       /* PWD */
+        if ((x = remcfm()) < 0) return(x);
+        sstate = setgen('A',"","","");
+        retcode = 0;
+        break;
+
+#ifndef NOSPL
+      case XZQUE: {                     /* Query */
+          char buf[2];
+          extern char querybuf[], * qbufp;
+          extern int qbufn;
+          if ((y = cmkey(vartyp,nvartyp,"","",xxstring)) < 0)
+            return(y);
+          if ((x = cmtxt(y == 'F' ? "Remote function invocation" :
+                         ('K' ? "Remote variable name or function":
+                         "Remote variable name"),
+                         "",
+                         &s,
+                         (y == 'K') ? xxstring : NULL
+                         )) < 0)        /* Don't evaluate */
+            return(x);
+          if ((x = remtxt(&s)) < 0)
+            return(x);
+          query = 1;                    /* QUERY is active */
+          qbufp = querybuf;             /* Initialize query response buffer */
+          qbufn = 0;
+          querybuf[0] = NUL;
+          buf[0] = (char) (y & 127);
+          buf[1] = NUL;
+          retcode = sstate = setgen('V',"Q",(char *)buf,s);
+          break;
+      }
+
+      case XZASG: {                     /* Assign */
+          char buf[VNAML];
+          if ((y = cmfld("Remote variable name","",&s,NULL)) < 0) /* No eval */
+            return(y);
+          if ((int)strlen(s) >= VNAML) {
+              printf("?Too long\n");
+              return(-9);
+          }
+          ckstrncpy(buf,s,VNAML);
+          if ((x = cmtxt("Assignment for remote variable",
+                   "",&s,xxstring)) < 0) /* Evaluate this one */
+            return(x);
+          if ((x = remtxt(&s)) < 0)
+            return(x);
+#ifdef COMMENT
+/*
+  Server commands can't be long packets.  In principle there's no reason
+  why they shouldn't be, except that we don't know at this point if the
+  server is capable of accepting long packets because we haven't started
+  the protocol yet.  In practice, allowing a long packet here breaks a lot
+  of assumptions, causes buffer overruns and crashes, etc.  To be fixed
+  later.  (But since this is commented out, evidently I fixed it later...)
+*/
+          if ((int)strlen(s) > 85) {    /* Allow for encoding expansion */
+              printf("?Sorry, value is too long - 85 characters max\n");
+              return(-9);
+          }
+#endif /* COMMENT */
+          retcode = sstate = setgen('V',"S",(char *)buf,s);
+          break;
+      }
+#endif /* NOSPL */
+
+      case XZCPY: {                     /* COPY */
+          char buf[TMPBUFSIZ];
+          buf[TMPBUFSIZ-1] = '\0';
+          if ((x = cmfld("Name of remote file to copy","",&s,xxstring)) < 0) {
+              if (x == -3) {
+                  printf("?Name of remote file required\n");
+                  return(-9);
+              }
+              else
+                return(x);
+          }
+          ckstrncpy(buf,s,TMPBUFSIZ);
+          if ((x = cmfld("Name of remote destination file or directory",
+                         "",&s, xxstring)) < 0) {
+              if (x == -3) {
+                  printf("?Name of remote file or directory required\n");
+                  return(-9);
+              } else return(x);
+          }
+          ckstrncpy(tmpbuf,s,TMPBUFSIZ);
+          if ((x = remcfm()) < 0)
+            return(x);
+          if (local) ttflui();          /* If local, flush tty input buffer */
+          retcode = sstate = setgen('K',buf,tmpbuf,"");
+          break;
+      }
+      case XZREN: {                     /* Rename */
+          char buf[TMPBUFSIZ];
+          buf[TMPBUFSIZ-1] = '\0';
+          if ((x = cmfld("Name of remote file to rename",
+                         "",&s,xxstring)) < 0) {
+              if (x == -3) {
+                  printf("?Name of remote file required\n");
+                  return(-9);
+              } else return(x);
+          }
+          ckstrncpy(buf,s,TMPBUFSIZ);
+          if ((x = cmfld("New name of remote file","",&s, xxstring)) < 0) {
+              if (x == -3) {
+                  printf("?Name of remote file required\n");
+                  return(-9);
+              } else return(x);
+          }
+          ckstrncpy(tmpbuf,s,TMPBUFSIZ);
+          if ((x = remcfm()) < 0)
+            return(x);
+          if (local) ttflui();          /* If local, flush device buffer */
+          retcode = sstate = setgen('R',buf,tmpbuf,"");
+          break;
+      }
+      case XZMKD:                       /* mkdir */
+      case XZRMD:                       /* rmdir */
+        if ((x = cmtxt((xx == XZMKD) ?
+                       "Name of remote directory to create" :
+                       "Name of remote directory to delete",
+                       "",
+                       &s,
+                       xxstring
+                       )) < 0) {
+            if (x == -3) {
+                printf("?Name required\n");
+                return(-9);
+            } else return(x);
+        }
+        if ((x = remtxt(&s)) < 0)
+          return(x);
+        if (local) ttflui();            /* If local, flush tty input buffer */
+        retcode = sstate = rfilop(s, (char)(xx == XZMKD ? 'm' : 'd'));
+        break;
+
+      case XZXIT:                       /* Exit */
+        if ((x = remcfm()) < 0) return(x);
+        sstate = setgen('X',"","","");
+        retcode = 0;
+        break;
+
+      default:
+        if ((x = remcfm()) < 0) return(x);
+        printf("?Not implemented - %s\n",cmdbuf);
+        return(-2);
+    }
+    if (local && retcode > -1)          /* If local, flush tty input buffer */
+      ttflui();
+    return(retcode);
+}
+
+
+/*  R F I L O P  --  Remote File Operation  */
+
+CHAR
+#ifdef CK_ANSIC
+rfilop(char * s, char t)
+#else
+rfilop(s,t) char *s, t;
+#endif /* CK_ANSIC */
+/* rfilop */ {
+    if (*s == NUL) {
+        printf("?File specification required\n");
+        return((CHAR) 0);
+    }
+    debug(F111,"rfilop",s,t);
+    return(setgen(t,s,"",""));
+}
+#endif /* NOXFER */
+
+#ifdef ANYX25
+int
+setx25() {
+    if ((y = cmkey(x25tab,nx25,"X.25 call options","",xxstring)) < 0)
+      return(y);
+    switch (y) {
+      case XYUDAT:
+        if ((z = cmkey(onoff,2,"X.25 call user data","",xxstring))
+            < 0) return(z);
+        if (z == 0) {
+            if ((z = cmcfm()) < 0) return(z);
+            cudata = 0;             /* disable call user data */
+            return (success = 1);
+        }
+        if ((x = cmtxt("X.25 call user data string","",&s,xxstring)) < 0)
+          return(x);
+        if ((int)strlen(s) == 0) {
+            return (-3);
+        } else if ((int)strlen(s) > MAXCUDATA) {
+            printf("?The length must be > 0 and <= %d\n",MAXCUDATA);
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        ckstrncpy(udata,s,MAXCUDATA);
+        cudata = 1;                     /* X.25 call user data specified */
+        return (success = 1);
+      case XYCLOS:
+        if ((z = cmkey(onoff,2,"X.25 closed user group call","",xxstring))
+            < 0) return(z);
+        if (z == 0) {
+            if ((z = cmcfm()) < 0) return(z);
+            closgr = -1;                /* disable closed user group */
+            return (success = 1);
+        }
+        if ((y = cmnum("0 <= cug index >= 99","",10,&x,xxstring)) < 0)
+          return(y);
+        if (x < 0 || x > 99) {
+            printf("?The choices are 0 <= cug index >= 99\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        closgr = x;                     /* closed user group selected */
+        return (success = 1);
+
+      case XYREVC:
+        if((z = cmkey(onoff,2,"X.25 reverse charge call","",xxstring)) < 0)
+          return(z);
+        if ((x = cmcfm()) < 0) return(x);
+        revcall = z;
+        return (success = 1);
+    }
+}
+
+#ifndef IBMX25
+int
+setpadp() {
+    if ((y = cmkey(padx3tab,npadx3,"PAD X.3 parameter name","",xxstring)) < 0)
+      return(y);
+    x = y;
+    switch (x) {
+      case PAD_BREAK_CHARACTER:
+        if ((y = cmnum("PAD break character value","",10,&z,xxstring)) < 0)
+          return(y);
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+      case PAD_ESCAPE:
+        if ((y = cmnum("PAD escape","",10,&z,xxstring)) < 0) return(y);
+        if (z != 0 && z != 1) {
+            printf("?The choices are 0 or 1\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+      case PAD_ECHO:
+        if ((y = cmnum("PAD echo","",10,&z,xxstring)) < 0) return(y);
+        if (z != 0 && z != 1) {
+            printf("?The choices are 0 or 1\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+      case PAD_DATA_FORWARD_CHAR:
+        if ((y = cmnum("PAD data forward char","",10,&z,xxstring)) < 0)
+          return(y);
+        if (z != 0 && z != 2) {
+            printf("?The choices are 0 or 2\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+      case PAD_DATA_FORWARD_TIMEOUT:
+        if ((y = cmnum("PAD data forward timeout","",10,&z,xxstring)) < 0)
+            return(y);
+        if (z < 0 || z > 255) {
+            printf("?The choices are 0 or 1 <= timeout <= 255\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+      case PAD_FLOW_CONTROL_BY_PAD:
+        if ((y = cmnum("PAD pad flow control","",10,&z,xxstring)) < 0)
+          return(y);
+        if (z != 0 && z != 1) {
+            printf("?The choices are 0 or 1\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+      case PAD_SUPPRESSION_OF_SIGNALS:
+        if ((y = cmnum("PAD service","",10,&z,xxstring)) < 0) return(y);
+        if (z != 0 && z != 1) {
+            printf("?The choices are 0 or 1\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+
+      case PAD_BREAK_ACTION:
+        if ((y = cmnum("PAD break action","",10,&z,xxstring)) < 0) return(y);
+        if (z != 0 && z != 1 && z != 2 && z != 5 && z != 8 && z != 21) {
+            printf("?The choices are 0, 1, 2, 5, 8 or 21\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+
+      case PAD_SUPPRESSION_OF_DATA:
+        if ((y = cmnum("PAD data delivery","",10,&z,xxstring)) < 0) return(y);
+        if (z != 0 && z != 1) {
+            printf("?The choices are 0 or 1\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+
+      case PAD_PADDING_AFTER_CR:
+        if ((y = cmnum("PAD crpad","",10,&z,xxstring)) < 0) return(y);
+        if (z < 0 || z > 7) {
+            printf("?The choices are 0 or 1 <= crpad <= 7\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+
+      case PAD_LINE_FOLDING:
+        if ((y = cmnum("PAD linefold","",10,&z,xxstring)) < 0) return(y);
+        if (z < 0 || z > 255) {
+            printf("?The choices are 0 or 1 <= linefold <= 255\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+
+      case PAD_LINE_SPEED:
+        if ((y = cmnum("PAD baudrate","",10,&z,xxstring)) < 0) return(y);
+        if (z < 0 || z > 18) {
+            printf("?The choices are 0 <= baudrate <= 18\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+
+      case PAD_FLOW_CONTROL_BY_USER:
+        if ((y = cmnum("PAD terminal flow control","",10,&z,xxstring)) < 0)
+            return(y);
+        if (z != 0 && z != 1) {
+            printf("?The choices are 0 or 1\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+
+      case PAD_LF_AFTER_CR:
+        if ((y = cmnum("PAD crpad","",10,&z,xxstring)) < 0) return(y);
+        if (z < 0 || z == 3 || z > 7) {
+            printf("?The choices are 0, 1, 2, 4, 5, 6 or 7\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+
+      case PAD_PADDING_AFTER_LF:
+        if ((y = cmnum("PAD lfpad","",10,&z,xxstring)) < 0) return(y);
+        if (z < 0 || z > 7) {
+            printf("?The choices are 0 or 1 <= lfpad <= 7\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+
+      case PAD_EDITING:
+        if ((y = cmnum("PAD edit control","",10,&z,xxstring)) < 0) return(y);
+        if (z != 0 && z != 1) {
+            printf("?The choices are 0 or 1\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+
+      case PAD_CHAR_DELETE_CHAR:
+        if ((y = cmnum("PAD char delete char","",10,&z,xxstring)) < 0)
+            return(y);
+        if (z < 0 || z > 127) {
+            printf("?The choices are 0 or 1 <= chardelete <= 127\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+
+      case PAD_BUFFER_DELETE_CHAR:
+        if ((y = cmnum("PAD buffer delete char","",10,&z,xxstring)) < 0)
+            return(y);
+        if (z < 0 || z > 127) {
+            printf("?The choices are 0 or 1 <= bufferdelete <= 127\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+
+      case PAD_BUFFER_DISPLAY_CHAR:
+        if ((y = cmnum("PAD display line char","",10,&z,xxstring)) < 0)
+            return(y);
+        if (z < 0 || z > 127) {
+            printf("?The choices are 0 or 1 <= displayline <= 127\n");
+            return(-2);
+        }
+        if ((y = cmcfm()) < 0) return(y);
+        break;
+    }
+    padparms[x] = z;
+    return(success = 1);
+}
+#endif /* IBMX25 */
+#endif /* ANYX25 */
+
+#ifndef NOXFER
+int
+setat(rmsflg) int rmsflg; {
+    int xx;
+    if ((y = cmkey(attrtab,natr,"File Attribute packets","",xxstring)) < 0)
+      return(y);
+    if (y == AT_XALL) {                 /* ATTRIBUTES ALL ON or ALL OFF */
+        if ((z = seton(&xx)) < 0) return(z);
+        if (rmsflg) {
+            printf("Sorry, command not available\n");
+            return(-9);
+        } else {
+            atenci = xx;                /* Encoding in */
+            atenco = xx;                /* Encoding out */
+            atdati = xx;                /* Date in */
+            atdato = xx;                /* Date out */
+            atdisi = xx;                /* Disposition in/out */
+            atdiso = xx;
+            atleni = xx;                /* Length in/out (both kinds) */
+            atleno = xx;
+            atblki = xx;                /* Blocksize in/out */
+            atblko = xx;
+            attypi = xx;                /* File type in/out */
+            attypo = xx;
+            atsidi = xx;                /* System ID in/out */
+            atsido = xx;
+            atsysi = xx;                /* System-dependent params in/out */
+            atsyso = xx;
+#ifdef CK_PERMS                         /* Protection */
+            atlpri = xx;                /* Local in */
+            atlpro = xx;                /* Local out */
+            atgpri = xx;                /* Generic in */
+            atgpro = xx;                /* Generic out */
+#endif /* CK_PERMS */
+#ifdef STRATUS
+            atfrmi = xx;                /* Format in/out */
+            atfrmo = xx;
+            atcrei = xx;                /* Creator id in/out */
+            atcreo = xx;
+            atacti = xx;                /* Account in/out */
+            atacto = xx;
+#endif /* STRATUS */
+        }
+        return(z);
+    } else if (y == AT_ALLY || y == AT_ALLN) { /* ATTRIBUTES ON or OFF */
+        if ((x = cmcfm()) < 0) return(x);
+        atcapr = (y == AT_ALLY) ? 1 : 0;
+        if (rmsflg) {
+            sstate = setgen('S', "132", atcapr ? "1" : "0", "");
+            return((int) sstate);
+        } else return(success = 1);
+    }
+    /* Otherwise, it's an individual attribute that wants turning off/on */
+
+    if ((z = cmkey(onoff,2,"","",xxstring)) < 0) return(z);
+    if ((x = cmcfm()) < 0) return(x);
+
+/* There are better ways to do this... */
+/* The real problem is that we're not separating the in and out cases */
+/* and so we have to arbitrarily pick the "in" case, i.e tell the remote */
+/* server to ignore incoming attributes of the specified type, rather */
+/* than telling it not to send them.  The protocol does not (yet) define */
+/* codes for "in-and-out-at-the-same-time". */
+
+    switch (y) {
+#ifdef CK_PERMS
+/* We're lumping local and generic protection together for now... */
+      case AT_LPRO:
+      case AT_GPRO:
+        if (rmsflg) {
+            sstate = setgen('S', "143", z ? "1" : "0", "");
+            return((int) sstate);
+        }
+        atlpri = atlpro = atgpri = atgpro = z; break;
+#endif /* CK_PERMS */
+      case AT_DISP:
+        if (rmsflg) {
+            sstate = setgen('S', "142", z ? "1" : "0", "");
+            return((int) sstate);
+        }
+        atdisi = atdiso = z; break;
+      case AT_ENCO:
+        if (rmsflg) {
+            sstate = setgen('S', "141", z ? "1" : "0", "");
+            return((int) sstate);
+        }
+        atenci = atenco = z; break;
+      case AT_DATE:
+        if (rmsflg) {
+            sstate = setgen('S', "135", z ? "1" : "0", "");
+            return((int) sstate);
+        }
+        atdati = atdato = z; break;
+      case AT_LENB:
+      case AT_LENK:
+        if (rmsflg) {
+            sstate = setgen('S', "133", z ? "1" : "0", "");
+            return((int) sstate);
+        }
+        atleni = atleno = z; break;
+      case AT_BLKS:
+        if (rmsflg) {
+            sstate = setgen('S', "139", z ? "1" : "0", "");
+            return((int) sstate);
+        }
+        atblki = atblko = z; break;
+      case AT_FTYP:
+        if (rmsflg) {
+            sstate = setgen('S', "134", z ? "1" : "0", "");
+            return((int) sstate);
+        }
+        attypi = attypo = z; break;
+#ifdef STRATUS
+      case AT_CREA:
+        if (rmsflg) {
+            sstate = setgen('S', "136", z ? "1" : "0", "");
+            return((int) sstate);
+        }
+        atcrei = atcreo = z; break;
+      case AT_ACCT:
+        if (rmsflg) {
+            sstate = setgen('S', "137", z ? "1" : "0", "");
+            return((int) sstate);
+        }
+        atacti = atacto = z; break;
+#endif /* STRATUS */
+      case AT_SYSI:
+        if (rmsflg) {
+            sstate = setgen('S', "145", z ? "1" : "0", "");
+            return((int) sstate);
+        }
+        atsidi = atsido = z; break;
+      case AT_RECF:
+        if (rmsflg) {
+            sstate = setgen('S', "146", z ? "1" : "0", "");
+            return((int) sstate);
+        }
+        atfrmi = atfrmo = z; break;
+      case AT_SYSP:
+        if (rmsflg) {
+            sstate = setgen('S', "147", z ? "1" : "0", "");
+            return((int) sstate);
+        }
+        atsysi = atsyso = z; break;
+      default:
+        printf("?Not available\n");
+        return(-2);
+    }
+    return(1);
+}
+#endif /* NOXFER */
+
+#ifndef NOSPL
+int
+setinp() {
+    if ((y = cmkey(inptab,ninp,"","",xxstring)) < 0) return(y);
+    switch (y) {
+#ifdef OS2
+      case IN_PAC:                      /* SET INPUT PACING */
+        z = cmnum("milliseconds","0",10,&x,xxstring);
+        return(setnum(&tt_inpacing,x,z,1000));
+      case IN_TRM:                      /* SET INPUT TERMINAL */
+        return(seton(&interm));
+#endif /* OS2 */
+      case IN_DEF:                      /* SET INPUT DEFAULT-TIMEOUT */
+        z = cmnum("Positive number","",10,&x,xxstring);
+        return(setnum(&indef,x,z,94));
+#ifdef CKFLOAT
+      case IN_SCA:                      /* SET INPUT SCALE-FACTOR */
+       if ((x = cmfld("Number such as 2 or 0.5","1.0",&s, xxstring)) < 0)
+         return(x);
+        if (isfloat(s,0)) {            /* A floating-point number? */
+            extern char * inpscale;
+           inscale = floatval;         /* Yes, get its value */
+           makestr(&inpscale,s);       /* Save it as \v(inscale) */
+           return(success = 1);
+       } else {
+           return(-2);
+       }
+#endif /* CKFLOAT */
+      case IN_TIM:                      /* SET INPUT TIMEOUT-ACTION */
+        if ((z = cmkey(intimt,2,"","",xxstring)) < 0) return(z);
+        if ((x = cmcfm()) < 0) return(x);
+        intime[cmdlvl] = z;
+        return(success = 1);
+      case IN_CAS:                      /* SET INPUT CASE */
+        if ((z = cmkey(incast,2,"","",xxstring)) < 0) return(z);
+        if ((x = cmcfm()) < 0) return(x);
+        inpcas[cmdlvl] = z;
+        return(success = 1);
+      case IN_ECH:                      /* SET INPUT ECHO */
+        return(seton(&inecho));
+      case IN_SIL:                      /* SET INPUT SILENCE */
+        z = cmnum("Seconds of inactivity before INPUT fails","",10,&x,
+                  xxstring);
+        return(setnum(&insilence,x,z,-1));
+
+      case IN_BUF:                      /* SET INPUT BUFFER-SIZE */
+        if ((z = cmnum("Number of bytes in INPUT buffer",
+                       ckitoa(INPBUFSIZ),10,&x, xxstring)) < 0)
+          return(z);
+        if ((y = cmcfm()) < 0) return(y);
+        inbufsize = 0;
+        if (inpbuf) {
+            free(inpbuf);
+            inpbuf = NULL;
+            inpbp = NULL;
+        }
+        if (!(s = (char *)malloc(x + 1)))
+          return(0);
+        inpbuf = s;
+        inpbp = s;
+        inbufsize = x;
+        for (x = 0; x <= inbufsize; x++)
+          inpbuf[x] = NUL;
+        return(success = 1);
+
+#ifdef CK_AUTODL
+      case IN_ADL:                      /* AUTODOWNLOAD */
+        return(seton(&inautodl));
+#endif /* CK_AUTODL */
+
+      case IN_CAN:                      /* SET INPUT INTERRUPTS */
+        return(seton(&inintr));
+    }
+    return(0);
+}
+#endif /* NOSPL */
+
+#ifdef NETCONN
+VOID
+ndreset() {
+#ifndef NODIAL                          /* This depends on DIAL... */
+    int i=0, j=0;
+    if (!ndinited)                      /* Don't free garbage... */
+      return;
+    for (i = 0; i < nhcount; i++) {     /* Clean out previous list */
+        if (nh_p[i])
+          free(nh_p[i]);
+        nh_p[i] = NULL;
+        if (nh_p2[i])
+          free(nh_p2[i]);
+        nh_p2[i] = NULL;
+        for (j = 0; j < 4; j++) {
+            if (nh_px[j][i])
+              free(nh_px[j][i]);
+            nh_px[j][i] = NULL;
+        }
+    }
+#endif /* NODIAL */
+}
+
+VOID
+ndinit() {                              /* Net directory pointers */
+#ifndef NODIAL                          /* This depends on DIAL... */
+    int i, j;
+    if (ndinited++)                     /* Don't do this more than once. */
+      return;
+    for (i = 0; i < MAXDDIR; i++) {     /* Init all pointers to NULL */
+        netdir[i] = NULL;
+    }
+    for (i = 0; i < MAXDNUMS; i++) {
+        nh_p[i] = NULL;
+        nh_p2[i] = NULL;
+        for (j = 0; j < 4; j++)
+          nh_px[j][i] = NULL;
+    }
+#endif /* NODIAL */
+}
+
+#ifndef NODIAL
+#ifdef NETCONN
+VOID                                    /* Get net defaults from environment */
+getnetenv() {
+    char *p = NULL;
+
+    makestr(&p,getenv("K_NET_DIRECTORY")); /* Dialing directories */
+    if (p) {
+        int i;
+        xwords(p,MAXDDIR,netdir,0);
+        for (i = 0; i < MAXDDIR; i++) { /* Fill in any gaps... */
+            if (!netdir[i+1])
+              break;
+            else
+              netdir[i] = netdir[i+1];
+            debug(F111,"netdir[i]",netdir[i],i);
+        }
+        nnetdir = i;
+    }
+}
+#endif /* NETCONN */
+#endif /* NODIAL */
+
+int
+#ifdef CK_ANSIC
+lunet(char *s)                          /* s = name to look up   */
+#else
+lunet(s) char *s;
+#endif /* CK_ANSIC */
+/* lunet */ {
+#ifndef NODIAL                          /* This depends on DIAL... */
+    int n, n1, t, dd = 0;
+    int ambiguous = 0;
+    FILE * f;
+    char *line = NULL;
+    extern int dialdpy;
+    int netdpy = dialdpy;
+    char *info[8];
+
+    nhcount = 0;                        /* Set this before returning */
+
+    if (!s || nnetdir < 1)              /* Validate arguments */
+      return(-1);
+
+    if (isdigit(*s) || *s == '*' || *s == '.')
+      return(0);
+
+    if ((n1 = (int) strlen(s)) < 1)     /* Length of string to look up */
+      return(-1);
+
+    if (!(line = malloc(1024)))         /* Allocate input buffer */
+      return(-1);
+
+  lu_again:
+    f = NULL;                           /* Network directory file descriptor */
+    t = nhcount = 0;                    /* Match count */
+    dd = 0;                             /* Directory counter */
+
+    dirline = 0;
+    while (1) {                         /* We make one pass */
+        if (!f) {                       /* Directory not open */
+            if (dd >= nnetdir)          /* No directories left? */
+              break;                    /* Done. */
+            if ((f = fopen(netdir[dd],"r")) == NULL) { /* Open it */
+                perror(netdir[dd]);     /* Can't, print message saying why */
+                dd++;
+                continue;               /* But go on to next one. */
+            }
+            if (netdpy)
+              printf("Opening %s...\n",netdir[dd]);
+            dd++;
+        }
+        line[0] = NUL;
+        if (getnct(line,1023,f,1) < 0) { /* Read a line */
+            if (f) {                    /* f can be clobbered! */
+                fclose(f);              /* Close the file */
+                f = NULL;               /* Indicate next one needs opening */
+            }
+            continue;
+        }
+        if (!line[0])                   /* Empty line */
+          continue;
+
+        xwords(line,7,info,0);          /* Parse it */
+
+        if (!info[1] || !info[2] || !info[3]) /* Required fields */
+          continue;
+        if (*info[1] == ';')            /* Full-line comment */
+          continue;
+        if ((n = (int) strlen(info[1])) < 1) /* Length of name-tag */
+          continue;
+        if (n < n1)                     /* Search name is longer */
+          continue;                     /* Can't possibly match */
+        if (ambiguous && n != n1)
+          continue;
+        if (ckstrcmp(s,info[1],n1,0))   /* Compare using length of */
+          continue;                     /* search string s. */
+
+        /* Have a match */
+
+        makestr(&(nh_p[nhcount]), info[3]);    /* address */
+        makestr(&(nh_p2[nhcount]),info[2]);    /* net type */
+        makestr(&(nh_px[0][nhcount]),info[4]); /* net-specific stuff... */
+        makestr(&(nh_px[1][nhcount]),info[5]);
+        makestr(&(nh_px[2][nhcount]),info[6]);
+        makestr(&(nh_px[3][nhcount]),info[7]);
+
+        nhcount++;                      /* Count this match */
+        if (nhcount > MAXDNUMS) {       /* Watch out for too many */
+            printf("Warning: %d matches found, %d max\n",
+                   nhcount,
+                   MAXDNUMS
+                   );
+            nhcount = MAXDNUMS;
+            break;
+        }
+        if (nhcount == 1) {             /* First one - save entry name */
+            if (n_name) {               /* Free the one from before if any */
+                free(n_name);
+                n_name = NULL;
+            }
+            if (!(n_name = (char *)malloc(n + 1))) { /* Allocate new storage */
+                printf("?memory allocation error - lunet:3\n");
+                if (line) {
+                    free(line);
+                    line = NULL;
+                }
+                nhcount = 0;
+                return(-1);
+            }
+            t = n;                      /* Remember its length */
+            strcpy(n_name,info[1]);     /* safe */
+        } else {                        /* Second or subsequent one */
+            if ((int) strlen(info[1]) == t) /* Lengths compare */
+              if (!ckstrcmp(n_name,info[1],t,0)) /* Caseless compare OK */
+                continue;
+
+            /* Name given by user matches entries with different names */
+
+            if (ambiguous)              /* Been here before */
+              break;
+
+            ambiguous = 1;              /* Now an exact match is required */
+            ndreset();                  /* Clear out previous list */
+            goto lu_again;              /* Do it all over again. */
+        }
+    }
+    if (line) {
+        free(line);
+        line = NULL;
+    }
+    if (nhcount == 0 && ambiguous)
+      printf("?\"%s\" - ambiguous in network directory\n",s);
+#else
+    nhcount = 0;
+#endif /* NODIAL */
+    return(nhcount);
+}
+#endif /* NETCONN */
+
+#ifndef NOLOCAL
+/*  C L S C O N N X  --  Close connection  */
+
+int
+clsconnx(ask) int ask; {
+    int x, rc = 0;
+#ifdef NEWFTP
+    extern int ftpget, ftpisopen(), ftpbye();
+    if ((ftpget == 1) || ((ftpget == 2) && !local && ftpisopen()))
+      return(success = ftpbye());
+#endif /* NEWFTP */
+    debug(F101,"clsconnx local","",local);
+    if (local) {
+        x = ask ? hupok(1) : 1;         /* Make sure it's OK to close */
+        if (!x) {
+            rc = -1;
+            debug(F101,"clsconnx hupok says no","",rc);
+            return(rc);
+        }
+        ttflui();                       /* Clear away buffered up junk */
+#ifndef NODIAL
+#ifdef OS2ONLY
+/* Don't hangup a line that is shared with the SLIP or PPP driver */
+        if (!ttslip && !ttppp)
+#endif /* OS2ONLY */
+          mdmhup();
+#endif /* NODIAL */
+        if (network && msgflg)
+          printf(" Closing connection\n");
+        ttclos(0);                      /* Close old connection, if any */
+        rc = 1;
+        {
+            extern int wasclosed, whyclosed;
+            if (wasclosed) {
+                whyclosed = WC_CLOS;
+#ifndef NOSPL
+                if (nmac) {             /* Any macros defined? */
+                    int k;              /* Yes */
+                    /* printf("ON_CLOSE CLSCONNX\n"); */
+                    wasclosed = 0;
+                    k = mlook(mactab,"on_close",nmac);  /* Look this up */
+                    if (k >= 0) {                       /* If found, */
+                        if (dodo(k,ckitoa(whyclosed),0) > -1) /* set it up, */
+                          parser(1);                    /* and execute it */
+                    }
+                }
+#endif /* NOSPL */
+                whyclosed = WC_REMO;
+                wasclosed = 0;
+            }
+        }
+    }
+#ifdef VMS                              /* Or maybe #ifndef UNIX? */
+    else {                              /* Need to do this in VMS to */
+        ttclos(0);                      /* free the tty channel number */
+        rc = 1;                         /* obtained in ttopen() or else */
+    }                                   /* subsequent ttopen's won't work */
+#endif /* VMS */
+    dologend();
+    haveline = 0;
+    if (mdmtyp < 0) {                   /* Switching from net to async? */
+        if (mdmsav > -1)                /* Restore modem type from last */
+          mdmtyp = mdmsav;              /* SET MODEM command, if any. */
+        else
+          mdmtyp = 0;
+        mdmsav = -1;
+    }
+    if (network)
+      network = 0;
+#ifdef NETCONN
+    if (oldplex > -1) {                 /* Restore previous duplex setting. */
+        duplex = oldplex;
+        oldplex = -1;
+    }
+#endif /* NETCONN */
+#ifndef MAC
+    ckstrncpy(ttname,dftty,TTNAMLEN);   /* Restore default communication */
+#endif /* MAC */
+    local = dfloc;                      /* device and local/remote status */
+    if (local) {
+        cxtype = CXT_DIRECT;            /* Something reasonable */
+        speed = ttgspd();               /* Get the current speed */
+    } else {
+        cxtype = CXT_REMOTE;
+        speed = -1L;
+    }
+#ifndef NOXFER
+    if (xreliable > -1 && !setreliable) {
+        reliable = xreliable;
+        debug(F101,"clsconnx reliable A","",reliable);
+    } else if (!setreliable) {
+        reliable = SET_AUTO;
+        debug(F101,"clsconnx reliable B","",reliable);
+    }
+#endif /* NOXFER */
+    setflow();                          /* Revert flow control */
+    return(rc);
+}
+
+int
+clskconnx(x) int x; {                   /* Close Kermit connection only */
+    int t, rc;                          /* (not FTP) */
+#ifdef NEWFTP
+    extern int ftpget;
+    t = ftpget;
+    ftpget = 0;
+#endif /* NEWFTP */
+    rc = clsconnx(x);
+#ifdef NEWFTP
+    ftpget = t;
+#endif /* NEWFTP */
+    return(rc);
+}
+
+/* May 2002: setlin() decomposition starts here ... */
+
+#ifdef OS2
+#define SRVBUFSIZ PIPENAML
+#else /* OS2 */
+#define SRVBUFSIZ 63
+#endif /* OS2 */
+#define HOSTNAMLEN 15*65
+
+int netsave = -1;
+static char * tmpstring = NULL;
+static char * tmpusrid = NULL;
+
+#ifdef SSHCMD
+char * sshcmd = NULL;
+char * defsshcmd = "ssh -e none";
+#else
+#ifdef SSHBUILTIN
+char * sshrcmd = NULL;
+char * sshtmpcmd = NULL;
+#endif /* SSHBUILTIN */
+#endif /* SSHCMD */
+
+/* c x _ f a i l  --  Common error exit routine for cx_net, cx_line */
+
+int
+cx_fail(msg, text) int msg; char * text; {
+    makestr(&slmsg,text);              /* For the record (or GUI) */
+    if (msg)                           /* Not GUI, not quiet, etc */
+      printf("?%s\n",text);            /* Print error message */
+    slrestor();                                /* Restore LINE/HOST to known state */
+    return(msg ? -9 : (success = 0));  /* Return appropriate code */
+}
+
+#ifdef NETCONN
+/* c x _ n e t  --  Make a network connection */
+
+/*
+  Call with:
+    net      = network type
+    protocol = protocol type
+    host     = string pointer to host name.
+    svc      = string pointer to service or port on host.
+    username = username for connection
+    password = password for connection
+    command  = command to execute
+    param1   = Telnet: Authentication type
+               SSH:    Version
+    param2   = Telnet: Encryption type
+               SSH:    Command as Subsystem
+    param3   = Telnet: 1 to wait for negotiations, 0 otherwise
+               SSH:    X11 Forwarding
+    cx       = 1 to automatically enter Connect mode, 0 otherwise.
+    sx       = 1 to automatically enter Server mode, 0 otherwise.
+    flag     = if no host name given, 1 = close current connection, 0 = resume
+    gui      = 1 if called from GUI dialog, 0 otherwise.
+  Returns:
+    1 on success
+    0 on failure and no message printed, slmsg set to failure message.
+   -9 on failure and message printed, ditto.
+*/
+int
+#ifdef CK_ANSIC
+cx_net( int net, int protocol, char * xhost, char * svc, 
+        char * username, char * password, char * command,
+        int param1, int param2, int param3, int cx, int sx, int flag, int gui)
+#else /* CK_ANSIC */
+cx_net(net, protocol, xhost, svc,
+       username, password, command,
+       param1, param2, param3, cx, sx, flag, gui)
+    char * xhost, * svc, * username, *password, *command; 
+    int net, protocol, cx, sx, flag, param1, param2, param3, gui; 
+#endif /* CK_ANSIC */
+/* cx_net */ {
+
+    int i, n, x, msg;
+    int _local = -1;
+
+    extern char pwbuf[], * g_pswd;
+    extern int pwflg, pwcrypt, g_pflg, g_pcpt, nolocal;
+
+    char srvbuf[SRVBUFSIZ+1];          /* Service */
+    char hostbuf[HOSTNAMLEN];          /* Host buffer to manipulate */
+    char hostname[HOSTNAMLEN];         /* Copy of host parameter */
+    char * host = hostbuf;             /* Pointer to copy of host param */
+
+    if (!xhost) xhost = "";            /* Watch out for null pointers */
+    if (!svc) svc = "";
+    ckstrncpy(host,xhost,HOSTNAMLEN);  /* Avoid buffer confusion */
+
+    debug(F110,"cx_net host",host,0);
+    debug(F111,"cx_net service",svc,SRVBUFSIZ);
+    debug(F101,"cx_net network type","",net);
+
+    msg = (gui == 0) && msgflg;                /* Whether to print messages */
+
+#ifndef NODIAL
+    debug(F101,"cx_net nnetdir","",nnetdir);
+    x = 0;                             /* Look up in network directory */
+    if (*host == '=') {                        /* If number starts with = sign */
+       host++;                         /* strip it */
+       while (*host == SP) host++;     /* and any leading spaces */
+       debug(F110,"cx_net host 2",host,0);
+       nhcount = 0;
+    } else if (*host) {                        /* We want to look it up. */
+       if (nnetdir > 0)                /* If there is a directory... */
+         x = lunet(host);              /* (sets nhcount) */
+       else                            /* otherwise */
+         nhcount = 0;                  /* we didn't find any there */
+       if (x < 0)                      /* Internal error? */
+         return(cx_fail(msg,"Network directory lookup error"));
+       debug(F111,"cx_net lunet nhcount",host,nhcount);
+    }
+#endif /* NODIAL */
+
+    /* New connection wanted.  Make a copy of the host name/address... */
+
+    if (clskconnx(1) < 0)              /* Close current Kermit connection */
+      return(cx_fail(msg,"Error closing previous connection"));
+
+    if (*host) {                       /* They gave a hostname */
+       _local = 1;                     /* Network connection always local */
+       if (mdmsav < 0)
+         mdmsav = mdmtyp;              /* Remember old modem type */
+       mdmtyp = -net;                  /* Special code for network */
+    } else {                           /* They just said "set host" */
+       host = dftty;                   /* So go back to normal */
+       _local = dfloc;                 /* default tty, location, */
+       if (flag) {                     /* Close current connection */
+           setflow();                  /* Maybe change flow control */
+           haveline = 1;               /* (* is this right? *) */
+           dologend();
+#ifndef NODIAL
+           dialsta = DIA_UNK;
+#endif /* NODIAL */
+#ifdef LOCUS
+           if (autolocus) {
+               setlocus(1,1);
+           }
+#endif /* LOCUS */
+            /* XXX - Is this right? */
+           /* Should we be returning without doing anything ? */
+           /* Yes it's right -- we closed the old connection just above. */
+           return(success = 1);        
+       }
+    }
+    success = 0;
+    if (host != line)                   /* line[] is a global */
+      ckstrncpy(line,host,LINBUFSIZ);
+    ckstrncpy(hostname,host,HOSTNAMLEN);
+    ckstrncpy(srvbuf,svc,SRVBUFSIZ+1);
+
+#ifndef NODIAL
+    if ((nhcount > 1) && msg) {
+       int k;
+       printf("%d entr%s found for \"%s\"%s\n",
+              nhcount,
+              (nhcount == 1) ? "y" : "ies",
+              s,
+              (nhcount > 0) ? ":" : "."
+              );
+       for (i = 0; i < nhcount; i++) {
+               printf("%3d. %-12s => %-9s %s",
+                      i+1,n_name,nh_p2[i],nh_p[i]);
+           for (k = 0; k < 4; k++) { /* Also list net-specific items */
+               if (nh_px[k][i])      /* free format... */
+                 printf(" %s",nh_px[k][i]);
+               else
+                 break;
+           }
+           printf("\n");
+       }
+    }
+    if (nhcount == 0)
+      n = 1;
+    else
+      n = nhcount;
+#else
+    n = 1;
+    nhcount = 0;
+#endif /* NODIAL */
+
+    for (i = 0; i < n; i++) {          /* Loop for each entry found */
+       debug(F101,"cx_net loop i","",i);
+#ifndef NODIAL
+       if (nhcount > 0) {              /* If we found at least one entry... */
+           ckstrncpy(line,nh_p[i],LINBUFSIZ); /* Copy current entry */
+           if (lookup(netcmd,nh_p2[i],nnets,&x) > -1) { /* Net type */
+               int xx;
+               xx = netcmd[x].kwval;
+               /* User specified SSH so don't let net directory override */
+               if (net != NET_SSH || xx != NET_TCPB) {
+                   net = xx;
+                   mdmtyp  = 0 - net;
+               }
+           } else {
+               makestr(&slmsg,"Network type not supported");
+               if (msg)
+                 printf("Error - network type \"%s\" not supported\n",
+                        nh_p2[i]
+                        );
+               continue;
+           }
+           switch (net) {              /* Net-specific directory things */
+#ifdef SSHBUILTIN
+             case NET_SSH:             /* SSH */
+                /* Any SSH specific network directory stuff? */
+                break;                  /* NET_SSH */
+#endif /* SSHBUILTIN */
+
+             case NET_TCPB: {          /* TCP/IP TELNET,RLOGIN,... */
+#ifdef TCPSOCKET
+                 char *q;
+                 int flag = 0;
+
+                 /* Extract ":service", if any, from host string */
+                 debug(F110,"cx_net service 1",line,0);
+                 for (q = line; (*q != '\0') && (*q != ':'); q++)
+                   ;
+                 if (*q == ':') { *q++ = NUL; flag = 1; }
+                 debug(F111,"cx_net service 2",line,flag);
+
+                 /* Get service, if any, from directory entry */
+
+                 if (!*srvbuf) {
+                     if (nh_px[0][i]) {
+                         ckstrncpy(srvbuf,nh_px[0][i],SRVBUFSIZ);
+                         debug(F110,"cx_net service 3",srvbuf,0);
+                     }
+                     if (flag) {
+                         ckstrncpy(srvbuf,q,SRVBUFSIZ);
+                         debug(F110,"cx_net service 4",srvbuf,0);
+                     }
+                 }
+                 ckstrncpy(hostname,line,HOSTNAMLEN);
+
+                 /* If we have a service, append to host name/address */
+                 if (*srvbuf) {
+                     ckstrncat(line, ":", LINBUFSIZ);
+                     ckstrncat(line, srvbuf, LINBUFSIZ);
+                     debug(F110,"cx_net service 5",line,0);
+                 }
+#ifdef RLOGCODE
+                 /* If no service given but command was RLOGIN */
+                 else if (ttnproto == NP_RLOGIN) { /* add this... */
+                     ckstrncat(line, ":login",LINBUFSIZ);
+                     debug(F110,"cx_net service 6",line,0);
+                 }
+#ifdef CK_AUTHENTICATION
+#ifdef CK_KERBEROS
+                 else if (ttnproto == NP_K4LOGIN ||
+                          ttnproto == NP_K5LOGIN) { /* add this... */
+                     ckstrncat(line, ":klogin",LINBUFSIZ);
+                     debug(F110,"cx_net service 7",line,0);
+                 }
+                 else if (ttnproto == NP_EK4LOGIN ||
+                          ttnproto == NP_EK5LOGIN) { /* add this... */
+                     ckstrncat(line, ":eklogin",LINBUFSIZ);
+                     debug(F110,"cx_net service 8",line,0);
+                 }
+#endif /* CK_KERBEROS */
+#endif /* CK_AUTHENTICATION */
+#endif /* RLOGCODE */
+                 else {                /* Otherwise, add ":telnet". */
+                     ckstrncat(line, ":telnet", LINBUFSIZ);
+                     debug(F110,"cx_net service 9",line,0);
+                 }
+                 if (username) {       /* This is a parameter... */
+                     ckstrncpy(uidbuf,username,UIDBUFLEN);
+                     uidflag = 1;
+                 }
+                 /* Fifth field, if any, is user ID (for rlogin) */
+
+                 if (nh_px[1][i] && !uidflag)
+                   ckstrncpy(uidbuf,username,UIDBUFLEN);
+#ifdef RLOGCODE
+                 if (IS_RLOGIN() && !uidbuf[0])
+                   return(cx_fail(msg,"Username required"));
+#endif /* RLOGCODE */
+#endif /* TCPSOCKET */
+                 break;
+             }
+             case NET_PIPE:            /* Pipe */
+#ifdef NPIPE
+               if (!pipename[0]) { /* User didn't give a pipename */
+                   if (nh_px[0][i]) { /* But directory entry has one */
+                       if (strcmp(pipename,"\\pipe\\")) {
+                           ckstrncpy(pipename,"\\pipe\\",LINBUFSIZ);
+                           ckstrncat(srvbuf,nh_px[0][i],PIPENAML-6);
+                       } else {
+                           ckstrncpy(pipename,nh_px[0][i],PIPENAML);
+                       }
+                       debug(F110,"cx_net pipeneme",pipename,0);
+                   }
+               }
+#endif /* NPIPE */
+               break;
+
+             case NET_SLAT:            /* LAT / CTERM */
+#ifdef SUPERLAT
+               if (!slat_pwd[0]) { /* User didn't give a password */
+                   if (nh_px[0][i]) { /* But directory entry has one */
+                       ckstrncpy(slat_pwd,nh_px[0][i],18);
+                       debug(F110,"cx_net SuperLAT password",slat_pwd,0);
+                   }
+               }
+#endif /* SUPERLAT */
+               break;
+
+             case NET_SX25:        /* X.25 keyword parameters */
+             case NET_IX25:
+             case NET_VX25: {
+#ifdef ANYX25
+                 int k;            /* Cycle through the four fields */
+                 for (k = 0; k < 4; k++) {
+                     if (!nh_px[k][i]) /* Bail out if none left */
+                       break;
+                     if (!ckstrcmp(nh_px[k][i],"cug=",4,0)) {
+                         closgr = atoi(nh_px[k][i]+4);
+                         debug(F101,"X25 CUG","",closgr);
+                     } else if (!ckstrcmp(nh_px[k][i],"cud=",4,0)) {
+                         cudata = 1;
+                         ckstrncpy(udata,nh_px[k][i]+4,MAXCUDATA);
+                         debug(F110,"X25 CUD",cudata,0);
+                     } else if (!ckstrcmp(nh_px[k][i],"rev=",4,0)) {
+                         revcall = !ckstrcmp(nh_px[k][i]+4,"=on",3,0);
+                         debug(F101,"X25 REV","",revcall);
+#ifndef IBMX25
+                     } else if (!ckstrcmp(nh_px[k][i],"pad=",4,0)) {
+                         int x3par, x3val;
+                         char *s1, *s2;
+                         s1 = s2 = nh_px[k][i]+4; /* PAD parameters */
+                         while (*s2) {            /* Pick them apart */
+                             if (*s2 == ':') {
+                                 *s2 = NUL;
+                                 x3par = atoi(s1);
+                                 s1 = ++s2;
+                                 continue;
+                             } else if (*s2 == ',') {
+                                 *s2 = NUL;
+                                 x3val = atoi(s1);
+                                 s1 = ++s2;
+                                 debug(F111,"X25 PAD",x3par,x3val);
+                                 if (x3par > -1 &&
+                                     x3par <= MAXPADPARMS)
+                                   padparms[x3par] = x3val;
+                                 continue;
+                             } else
+                               s2++;
+                         }
+#endif /* IBMX25 */
+                     }
+                 }
+#endif /* ANYX25 */
+                 break;
+             }
+             default:                  /* Nothing special for other nets */
+               break;
+           }
+       } else
+#endif /* NODIAL */
+       {                               /* No directory entries found. */
+           ckstrncpy(line,hostname,LINBUFSIZ); /* Put this back... */
+           /* If the user gave a TCP service */
+           if (net == NET_TCPB || net == NET_SSH)
+             if (*srvbuf) {            /* Append it to host name/address */
+                 ckstrncat(line, ":", LINBUFSIZ);
+                 ckstrncat(line, srvbuf,LINBUFSIZ);
+             }
+       }
+       /*
+          Get here with host name/address and all net-specific
+          parameters set, ready to open the connection.
+       */
+       mdmtyp = -net;                  /* This should have been done */
+                                       /* already but just in case ... */
+
+       debug(F110,"cx_net net line[] before ttopen",line,0);
+       debug(F101,"cx_net net mdmtyp before ttopen","",mdmtyp);
+       debug(F101,"cx_net net ttnproto","",ttnproto);
+
+#ifdef SSHBUILTIN
+        if (net == NET_SSH) {
+            makestr(&ssh_hst,hostname);        /* Stash everything */
+            if (username) {
+                if (!sl_uid_saved) {
+                    ckstrncpy(sl_uidbuf,uidbuf,UIDBUFLEN);
+                    sl_uid_saved = 1;
+                }
+                ckstrncpy(uidbuf,username,UIDBUFLEN);
+            }
+            if (srvbuf[0]) {
+                makestr(&ssh_prt,srvbuf);
+            } else
+                makestr(&ssh_prt,NULL);
+
+            if (command) {
+                makestr(&ssh_cmd,brstrip(command));
+                ssh_cas = param2;
+            } else
+                makestr(&ssh_cmd,NULL);
+
+            if (param1 > -1) {
+#ifndef SSHTEST
+                if (!sl_ssh_ver_saved) {
+                    sl_ssh_ver = ssh_ver;
+                    sl_ssh_ver_saved = 1;
+                }
+#endif /* SSHTEST */
+                ssh_ver = param1;
+            }
+            if (param3 > -1) {
+#ifndef SSHTEST
+                if (!sl_ssh_xfw_saved) {
+                    sl_ssh_xfw = ssh_xfw;
+                    sl_ssh_xfw_saved = 1;
+                }
+#endif /* SSHTEST */
+                ssh_xfw = param3;
+            }
+        } else                          /* NET_SSH */
+#endif /* SSHBUILTIN */
+#ifdef TCPSOCKET
+         if (net == NET_TCPB) {
+            switch (protocol) {
+#ifdef CK_SSL
+             case NP_SSL:
+                ttnproto = protocol;
+                ssl_only_flag = 1;
+                tls_only_flag = 0;
+                break;
+
+             case NP_TLS:
+                ttnproto = protocol;
+                ssl_only_flag = 0;
+                tls_only_flag = 1;
+                break;
+
+             case NP_SSL_TELNET:
+                ttnproto = NP_TELNET;
+                ssl_only_flag = 1;
+                tls_only_flag = 0;
+                break;
+
+             case NP_TLS_TELNET:
+                ttnproto = NP_TELNET;
+                ssl_only_flag = 0;
+                tls_only_flag = 1;
+                break;
+#endif /* CK_SSL */
+             case NP_NONE:
+             case NP_TCPRAW:
+             case NP_RLOGIN:
+             case NP_K4LOGIN:
+             case NP_K5LOGIN:
+             case NP_EK4LOGIN:
+             case NP_EK5LOGIN:
+             case NP_TELNET:
+             case NP_KERMIT:
+             default:
+                ttnproto = protocol;
+#ifdef CK_SSL
+                ssl_only_flag = 0;
+                tls_only_flag = 0;
+#endif /* CK_SSL */
+                break;
+            }
+#ifdef CK_AUTHENTICATION
+            if ((ttnproto == NP_TELNET || ttnproto == NP_KERMIT) &&
+               param1 > -1) {
+           if (!sl_auth_saved) {
+               int x;
+               for (x = 0; x < AUTHTYPLSTSZ; x++)
+                 sl_auth_type_user[x] = auth_type_user[x];
+               sl_auth_saved = 1;
+           }
+           if (!sl_topt_a_s_saved) {
+               sl_topt_a_su = TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION);
+               sl_topt_a_s_saved = 1;
+           }
+           if (!sl_topt_a_c_saved) {
+               sl_topt_a_cm = TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION);
+               sl_topt_a_c_saved = 1;
+           }
+           switch (param1) {
+             case AUTHTYPE_AUTO:
+               auth_type_user[0] = AUTHTYPE_AUTO;
+               TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_RQ;
+               TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_RQ;
+               break;
+             case AUTHTYPE_NULL:
+               auth_type_user[0] = AUTHTYPE_NULL;
+               TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_RF;
+               TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_RF;
+               break;
+#ifdef CK_SRP
+             case AUTHTYPE_SRP:
+               auth_type_user[0] = AUTHTYPE_SRP;
+               auth_type_user[1] = AUTHTYPE_NULL;
+               TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU;
+               TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU;
+               break;
+#endif /* CK_SRP */
+#ifdef CK_SSL
+             case AUTHTYPE_SSL:
+               auth_type_user[0] = AUTHTYPE_SSL;
+               auth_type_user[1] = AUTHTYPE_NULL;
+               TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU;
+               TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU;
+               break;
+#endif /* CK_SSL */
+#ifdef NT
+             case AUTHTYPE_NTLM:
+               auth_type_user[0] = AUTHTYPE_NTLM;
+               auth_type_user[1] = AUTHTYPE_NULL;
+               TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU;
+               TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU;
+               break;
+#endif /* NT */
+#ifdef CK_KERBEROS
+             case AUTHTYPE_KERBEROS_V4:
+               auth_type_user[0] = AUTHTYPE_KERBEROS_V4;
+               auth_type_user[1] = AUTHTYPE_NULL;
+               TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU;
+               TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU;
+               break;
+
+             case AUTHTYPE_KERBEROS_V5:
+               auth_type_user[0] = AUTHTYPE_KERBEROS_V5;
+               auth_type_user[1] = AUTHTYPE_NULL;
+               TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU;
+               TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU;
+               break;
+#endif /* CK_KERBEROS */
+           }
+       }
+       /*
+          If the user requires a particular type of Kerberos connection,
+          make sure we have a valid TGT.
+       */
+       makestr(&slmsg,"Authentication failure");
+       if ((ttnproto == NP_TELNET || ttnproto == NP_KERMIT) &&
+           (line[0] == '*' &&
+            TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) == TN_NG_MU ||
+            line[0] != '*' &&
+            TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) == TN_NG_MU)
+           ) {
+#ifdef CK_KERBEROS
+           if ( auth_type_user[0] == AUTHTYPE_KERBEROS_V4 ) {
+               extern int krb4_autoget;
+               if (!ck_krb4_is_installed())
+                 return(cx_fail(msg,
+             "Required authentication method (Kerberos 4) is not installed"));
+#ifdef COMMENT
+               /* This code results in false failures when using */
+               /* kerberos to machines in realms other than the  */
+               /* default since we don't know the realm of the   */
+               /* other machine until perform the reverse DNS    */
+               /* lookup.                                        */
+               else if (line[0] != '*' && !ck_krb4_is_tgt_valid() &&
+                          (!krb4_autoget ||
+                           krb4_autoget && !ck_krb4_autoget_TGT(NULL))) {
+                   return(cx_fail(msg,
+                          "Kerberos 4: Ticket Getting Ticket not valid"));
+               }
+#endif /* COMMENT */
+           } else if (auth_type_user[0] == AUTHTYPE_KERBEROS_V5) {
+               extern int krb5_autoget;
+               if (!ck_krb5_is_installed()) {
+                   return(cx_fail(msg,
+          "Required authentication method (Kerberos 5) is not installed"));
+               }
+#ifdef COMMENT
+               /* This code results in false failures when using */
+               /* kerberos to machines in realms other than the  */
+               /* default since we don't know the realm of the   */
+               /* other machine until perform the reverse DNS    */
+               /* lookup.                                        */
+               else if (line[0] != '*' && !ck_krb5_is_tgt_valid() &&
+                          (!krb5_autoget ||
+                           krb5_autoget && !ck_krb5_autoget_TGT(NULL))) {
+                   return(cx_fail(msg,
+                        "Kerberos 5: Ticket Getting Ticket not valid."));
+               }
+#endif /* COMMENT */
+           }
+#endif /* CK_KERBEROS */
+#ifdef NT
+           if (auth_type_user[0] == AUTHTYPE_NTLM) {
+               if (!ck_ntlm_is_installed()) {
+                   return(cx_fail(msg,
+                  "Required authentication method (NTLM) is not installed"));
+               } else if (line[0] != '*' && !ck_ntlm_is_valid(0)) {
+                   return(cx_fail(msg,"NTLM: Credentials are unavailable."));
+               }
+           }
+#endif /* NT */
+#ifdef CK_SSL
+           if (auth_type_user[0] == AUTHTYPE_SSL) {
+               if (!ck_ssleay_is_installed()) {
+                   return(cx_fail(msg,
+                    "Required authentication method (SSL) is not installed"));
+               }
+           }
+#endif /* CK_SSL */
+#ifdef CK_SRP
+           if (auth_type_user[0] == AUTHTYPE_SRP) {
+               if (!ck_srp_is_installed()) {
+                   return(cx_fail(msg,
+                    "Required authentication method (SRP) is not installed"));
+               }
+           }
+#endif /* CK_SRP */
+       }
+#endif /* CK_AUTHENTICATION */
+#ifdef CK_ENCRYPTION
+       if ((ttnproto == NP_TELNET || ttnproto == NP_KERMIT) &&
+            param2 > -1) {
+           if (!sl_cx_saved) {
+               sl_cx_type = cx_type;
+               sl_cx_saved = 1;
+           }
+           if (!sl_topt_e_s_saved) {
+               sl_topt_e_su = TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION);
+               sl_topt_e_sm = TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION);
+               sl_topt_e_s_saved = 1;
+           }
+           if (!sl_topt_e_c_saved) {
+               sl_topt_e_cu = TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION);
+               sl_topt_e_cm = TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION);
+               sl_topt_e_c_saved = 1;
+           }
+           cx_type = param2;
+           if (cx_type == CX_AUTO) {
+               TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ;
+               TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ;
+               TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ;
+               TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ;
+           } else if (cx_type == CX_NONE) {
+               TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF;
+               TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF;
+               TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF;
+               TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF;
+           } else {
+               TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION) = TN_NG_MU;
+               TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_MU;
+               TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) = TN_NG_MU;
+               TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_MU;
+           }
+       }
+       if (ttnproto == NP_EK4LOGIN || ttnproto == NP_EK5LOGIN ||
+           (ttnproto == NP_TELNET || ttnproto == NP_KERMIT) &&
+           ((line[0] == '*' &&
+             TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION) == TN_NG_MU &&
+             TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION) == TN_NG_MU) ||
+            (line[0] != '*' &&
+             TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) == TN_NG_MU &&
+             TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) == TN_NG_MU))
+           ) {
+           if (!ck_crypt_is_installed()) {
+               return(cx_fail(msg,
+                 "Required Encryption methods are not installed"));
+           }
+       }
+#endif /* CK_ENCRYPTION */
+#ifdef RLOGCODE
+#ifdef CK_KERBEROS
+#ifdef KRB4
+       if (ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN) {
+           extern int krb4_autoget;
+           char tgt[256];
+           char * realm;
+
+           /* We don't have the full hostname at yet so  */
+           /* we do a DNS lookup before calling ttopen() */ 
+
+           realm = ck_krb4_realmofhost(ckgetfqhostname(hostname));
+           ckmakmsg(tgt,256,"krbtgt.",realm,"@",realm);
+           if (!ck_krb4_is_installed()) {
+               return(cx_fail(msg,
+                "Required authentication method (Kerberos 4) is not installed"
+                              ));
+           } else {
+               if ((ck_krb4_tkt_isvalid(tgt) <= 0) &&
+                   (!krb4_autoget ||
+                    krb4_autoget && !ck_krb4_autoget_TGT(realm))) {
+                   return(cx_fail(msg,
+                          "Kerberos 4: Ticket Getting Ticket not valid"));
+               }
+           }
+       }
+#endif /* KRB4 */
+#ifdef KRB5
+       if (ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN ||
+           ttnproto == NP_K5U2U)
+       {
+           extern int krb5_autoget;
+           char tgt[256];
+           char * realm;
+
+           /* Must get full hostname before calling ttopen() */
+
+           realm = ck_krb5_realmofhost(ckgetfqhostname(hostname));
+           ckmakmsg(tgt,256,"krbtgt/",realm,"@",realm);
+
+           if (!ck_krb5_is_installed()) {
+               return(cx_fail(msg,
+                 "Required authentication method (Kerberos 5) not installed"));
+           } else if (!((ck_krb5_tkt_isvalid(NULL,tgt) > 0) ||
+                         ck_krb5_is_tgt_valid()) &&
+                      (!krb5_autoget ||
+                       krb5_autoget && !ck_krb5_autoget_TGT(realm))) {
+               return(cx_fail(msg,
+                      "Kerberos 5: Ticket Getting Ticket not valid."));
+           }
+       }
+#endif /* KRB5 */
+#endif /* CK_KERBEROS */
+#endif /* RLOGCODE */
+
+#ifndef NOSPL
+#ifdef RLOGCODE
+       if (username) {
+           if (!sl_uid_saved) {
+               ckstrncpy(sl_uidbuf,uidbuf,UIDBUFLEN);
+               sl_uid_saved = 1;
+           }
+           ckstrncpy(uidbuf,username,UIDBUFLEN);
+           uidflag = 1;
+       }
+#endif /* RLOGCODE */
+#ifdef TNCODE
+       if (!sl_tn_saved) {
+           sl_tn_wait = tn_wait_flg;
+           sl_tn_saved = 1;
+       }
+       tn_wait_flg = param3;
+#endif /* TNCODE */
+#endif /* NOSPL */
+       } /* if (net == NET_TCPB) */
+#endif /* TCPSOCKET */
+
+#ifndef NOSPL
+#ifdef CK_SECURITY
+       if (password) {
+           if (password[0]) {
+               ckstrncpy(pwbuf,password,PWBUFL+1);
+               pwflg = 1;
+               pwcrypt = 0;
+           } else
+               pwflg = 0;
+       }
+#endif /* CK_SECURITY */
+#endif /* NOSPL */
+
+       /* Try to open - network */
+       ckstrncpy(ttname,line,TTNAMLEN);
+       y = ttopen(line, &_local, mdmtyp, 0 );
+
+#ifndef NOHTTP
+       /*  If the connection failed and we are using an HTTP Proxy
+        *  and the reason for the failure was an authentication
+        *  error, then we need to give the user to ability to
+        *  enter a username and password, just like a browser.
+        *
+        *  I tried to do all of this within the netopen() call
+        *  but it is much too much work.
+        */
+       while (y < 0 && tcp_http_proxy != NULL ) {
+
+           if (tcp_http_proxy_errno == 401 ||
+               tcp_http_proxy_errno == 407 ) {
+               char uid[UIDBUFLEN];
+               char pwd[256];
+                struct txtbox tb[2];
+                int ok;
+
+                tb[0].t_buf = uid;
+                tb[0].t_len = UIDBUFLEN;
+                tb[0].t_lbl = "Proxy Userid: ";
+                tb[0].t_dflt = NULL;
+                tb[0].t_echo = 1;
+                tb[1].t_buf = pwd;
+                tb[1].t_len = 256;
+                tb[1].t_lbl = "Proxy Passphrase: ";
+                tb[1].t_dflt = NULL;
+                tb[1].t_echo = 2;
+
+                ok = uq_mtxt("Proxy Server Authentication Required\n",
+                              NULL, 2, tb);
+
+               if (ok && uid[0]) {
+                   char * proxy_user, * proxy_pwd;
+
+                   proxy_user = tcp_http_proxy_user;
+                   proxy_pwd  = tcp_http_proxy_pwd;
+
+                   tcp_http_proxy_user = uid;
+                   tcp_http_proxy_pwd = pwd;
+
+                   ckstrncpy(ttname,line,TTNAMLEN);
+                   y = ttopen(line, &_local, mdmtyp, 0);
+                   memset(pwd,0,sizeof(pwd));
+                   tcp_http_proxy_user = proxy_user;
+                   tcp_http_proxy_pwd = proxy_pwd;
+               } else
+                 break;
+           } else
+             break;
+       }
+#endif /* NOHTTP */
+       if (y < 0) {
+           slrestor();
+           makestr(&slmsg,"Network connection failure");
+#ifdef VMS
+           if (msg && hints && !xcmdsrc && IS_RLOGIN()) {
+               makestr(&slmsg,"RLOGIN failure");
+               if  (socket_errno == EACCES) {
+                   printf("*************************\n");
+                   printf(
+          "Hint: RLOGIN requires privileges to open an outbound port.\n");
+                   printf(
+                   "(Use SET HINTS OFF to suppress future hints.)\n");
+                   printf("*************************\n");
+               }
+           }
+#else  /* Not VMS... */
+           if (errno) {
+               int x;
+               debug(F111,"set host line, errno","",errno);
+               makestr(&slmsg,ck_errstr());
+               if (msg) {
+#ifdef OS2
+                   printf("Can't connect to %s\n",line);
+#else /* OS2 */
+#ifdef UNIX
+                   if (hints && !xcmdsrc && IS_RLOGIN()) {
+                       makestr(&slmsg,"RLOGIN failure");
+                       printf("*************************\n");
+                       printf(
+        "Hint: RLOGIN requires privileges to open an outbound port.\n");
+                       printf(
+        "(Use SET HINTS OFF to suppress future hints.)\n");
+                       printf("*************************\n");
+                   }
+#endif /* UNIX */
+#endif /* OS2 */
+               } else printf("Can't connect to %s\n",line);
+           } else
+#endif /* VMS */
+             if (msg) printf("Can't open connection to %s\n",line);
+           continue;
+       } else {
+           success = 1;
+#ifndef NODIAL
+           dialsta = DIA_UNK;
+#endif /* NODIAL */
+           switch (net) {
+             case NET_TCPA:
+             case NET_TCPB:
+               cxtype = CXT_TCPIP;
+#ifdef COMMENT
+/* This works but it messes up interactive anonymous login */
+#ifndef NOXFER
+#ifdef IKS_OPTION
+               /* If we have connected to an Internet Kermit service */
+               /* and a /USER: switch was given, then log in. */
+
+               if (TELOPT_U(TELOPT_KERMIT) || TELOPT_ME(TELOPT_KERMIT)) {
+                   debug(F111,"cx_net IKSD /USER:",uidbuf,haveuser);
+                   if (haveuser /* && cx == 0 */ ) { /* /USER: given */
+                       char * psw = pwbuf; /* Do we have a password? */
+                       if (!*psw) {        /* No... */
+                           if (!strcmp(uidbuf,"anonymous") ||
+                               !strcmp(uidbuf,"ftp")) {
+                               extern char myhost[];
+                               char * u = (char *)sl_uidbuf;
+                               char * h = (char *)myhost;
+                               if (!*u) u = "nobody";
+                               if (!*h) h = "nowhere";
+                               ckmakmsg(tmpbuf,TMPBUFSIZ,u,"@",h,NULL);
+                               psw = tmpbuf;
+                               debug(F110,"cx_net IKSD anon",psw,0);
+                           } else {
+                               readpass(" Password: ",pwbuf,PWBUFL);
+                           }
+                       }
+                       sstate = setgen('I',uidbuf,psw,"");
+                   }
+               }
+#endif /* IKS_OPTION */
+#endif /* NOXFER */
+#endif /* COMMENT */
+               break;
+             case NET_SSH:
+               cxtype = CXT_SSH;
+               duplex = 0;         /* Remote echo */
+               break;
+             case NET_SLAT:
+               cxtype = CXT_LAT;
+               break;
+             case NET_SX25:
+             case NET_IX25:
+             case NET_HX25:
+             case NET_VX25:
+               cxtype = CXT_X25;
+               break;
+             case NET_BIOS:
+               cxtype = CXT_NETBIOS;
+               break;
+             case NET_FILE:
+             case NET_PIPE:
+             case NET_CMD:
+             case NET_DLL:
+             case NET_PTY:
+               cxtype = CXT_PIPE;
+               break;
+             default:
+               cxtype = CXT_PIPE;
+               break;
+           }
+           break;
+       }
+    } /* for-loop */
+    s = line;
+
+    debug(F101,"cx_net post ttopen success","",success);
+    if (!success) {
+        local = dfloc;                  /* Go back to normal */
+#ifndef MAC
+        ckstrncpy(ttname,dftty,TTNAMLEN); /* Restore default tty name */
+#endif /* MAC */
+        speed = ttgspd();
+        network = 0;                    /* No network connection active */
+        haveline = 0;
+        if (mdmtyp < 0) {               /* Switching from net to async? */
+            if (mdmsav > -1)            /* Restore modem type from last */
+              mdmtyp = mdmsav;          /* SET MODEM command, if any. */
+            else
+              mdmtyp = 0;
+            mdmsav = -1;
+        }
+        return(0);                      /* Return failure */
+    }
+    if (_local > -1) local = _local;    /* Opened ok, set local/remote. */
+    makestr(&slmsg,NULL);
+    network = (mdmtyp < 0);             /* Remember connection type. */
+    ckstrncpy(ttname,s,TTNAMLEN);       /* Copy name into real place. */
+    debug(F110,"cx_net ok",ttname,0);
+    debug(F101,"cx_net network","",network);
+#ifndef NOXFER
+    if ((reliable != SET_OFF || !setreliable)) /* Assume not reliable. */
+      reliable = SET_OFF;
+#endif /* NOXFER */
+    if (!network || istncomport())     
+      speed = ttgspd();                 /* Get the current speed. */
+    debug(F101,"cx_net local","",local);
+    if (network) {
+        debug(F101,"cx_net net","",net);
+#ifndef NOXFER
+        /* Force prefixing of 255 on TCP/IP connections... */
+        if (net == NET_TCPB
+#ifdef SSHBUILTIN
+             || net == NET_SSH
+#endif /* SSHBUILTIN */
+             ) {
+            debug(F101,"cx_net reliable A","",reliable);
+#ifdef CK_SPEED
+            ctlp[(unsigned)255] = 1;
+#endif /* CK_SPEED */
+            if ((reliable != SET_OFF || !setreliable)) {
+#ifdef TN_COMPORT
+                if (istncomport()) {    /* Telnet communication port */
+                    reliable = SET_OFF; /* Transport is not reliable */
+                    debug(F101,"cx_net reliable istncomport()","",1);
+                } else {
+                    reliable = SET_ON;  /* Transport is reliable end to end */
+                    debug(F101,"cx_net reliable istncomport()","",0);
+                }
+#else
+                reliable = SET_ON;      /* Transport is reliable end to end */
+#endif /* ifdef TN_COMPORT */
+            }
+            debug(F101,"cx_net reliable B","",reliable);
+        } else if (net == NET_SX25 ||
+                   net == NET_VX25 ||
+                   net == NET_IX25 ||
+                   net == NET_HX25) {
+            duplex = 1;                 /* Local echo for X.25 */
+            if (reliable != SET_OFF || !setreliable)
+              reliable = SET_ON;        /* Transport is reliable end to end */
+        }
+#endif /* NOXFER */
+    }
+#ifndef NOXFER
+    debug(F101,"cx_net reliable","",reliable);
+#endif /* NOXFER */
+#ifdef OS2
+    if (mdmtyp <= 0)                    /* Network or Direct Connection */
+      DialerSend(OPT_KERMIT_CONNECT, 0);
+#endif /* OS2 */
+
+  xcx_net:
+
+    setflow();                          /* Set appropriate flow control */
+
+    haveline = 1;
+#ifdef CKLOGDIAL
+    dolognet();
+#endif /* CKLOGDIAL */
+
+#ifndef NOSPL
+    if (local) {
+        if (nmac) {                     /* Any macros defined? */
+            int k;                      /* Yes */
+            k = mlook(mactab,"on_open",nmac);   /* Look this up */
+            if (k >= 0) {                       /* If found, */
+                if (dodo(k,ttname,0) > -1)      /* set it up, */
+                  parser(1);                    /* and execute it */
+            }
+        }
+    }
+#endif /* NOSPL */
+
+    if (local && (cx || sx)) {          /* /CONNECT or /SERVER switch given */
+        if (cx) {                       /* /CONNECT */
+           if (!gui) {
+               /* Command was confirmed so we can pre-pop command level.  */
+               /* This is so CONNECT module won't think we're executing a */
+               /* script if CONNECT was the final command in the script.  */
+               if (cmdlvl > 0)
+                 prepop();
+           }
+#ifndef NODIAL
+            dialsta = DIA_UNK;
+#endif /* NODIAL */
+#ifdef LOCUS
+            if (autolocus) {
+               setlocus(1,1);
+            }
+#endif /* LOCUS */
+            success = doconect(0, cmdlvl == 0 ? 1 : 0);
+            if (ttchk() < 0)
+              dologend();
+           debug(F101,"cx_net post doconect success","",success);
+            return(success);
+#ifndef NOXFER
+        } else if (sx) {                /* /SERVER */
+            sstate = 'x';
+#ifdef MAC
+            what = W_RECV;
+            scrcreate();
+#endif /* MAC */
+            if (local) displa = 1;
+#ifdef AMIGA
+            reqoff();                   /* No DOS requestors while server */
+#endif /* AMIGA */
+#endif /* NOXFER */
+        }
+    }
+#ifndef NODIAL
+    dialsta = DIA_UNK;
+#endif /* NODIAL */
+#ifdef LOCUS
+    if (autolocus) {
+        setlocus(1,1);
+    }
+#endif /* LOCUS */
+    return(success = 1);
+}
+#endif /* NETCONN */
+
+/* c x _ s e r i a l  --  Make a serial connection */
+
+/*
+  Call with:
+    device  = string pointer to device name.
+    cx      = 1 to automatically enter Connect mode, 0 otherwise.
+    sx      = 1 to automatically enter Server mode, 0 otherwise.
+    shr     = 1 if device should be opened in shareable mode, 0 otherwise.
+    flag    = if no dev name given: 1 = close current connection, 0 = resume.
+    gui     = 1 if called from GUI dialog, 0 otherwise.
+  Returns:
+    1 on success
+    0 on failure and no message printed, slmsg set to failure message.
+   -9 on failure and message printed, ditto.
+*/
+
+/* these are bit flags */
+#define CX_TAPI 1
+#define CX_PPP  2
+#define CX_SLIP 4
+
+int
+#ifdef CK_ANSIC
+cx_serial(char *device, 
+          int cx, int sx, int shr, int flag, int gui, int special)
+#else /* CK_ANSIC */
+cx_serial(device, cx, sx, shr, flag, gui, special)
+    char * device; int cx, sx, shr, flag, gui, special; 
+#endif /* CK_ANSIC */
+/* cx_serial */ {
+    int i, n, x, y, msg;
+    int _local = -1;
+    char *s;
+
+    debug(F110,"cx_serial device",device,0);
+    s = device;
+    msg = (gui == 0) && msgflg;                /* Whether to print messages */
+    success = 0;
+
+#ifndef NODIAL
+    dialsta = DIA_UNK;
+#endif /* NODIAL */
+    debug(F101,"cx_serial mdmtyp","",mdmtyp);
+    if (clskconnx(1) < 0)              /* Close the Kermit connection */
+      return(success = 0);
+    if (*s) {                          /* They gave a device name */
+       _local = -1;                    /* Let ttopen decide about it */
+    } else {                           /* They just said "set line" */
+       s = dftty;                      /* so go back to normal tty */
+       _local = dfloc;                 /* and mode. */
+    }
+#ifdef VMS
+    {
+       extern int ok_to_share;
+       ok_to_share = shr;
+    }
+#endif /* VMS */
+
+#ifdef OS2                              /* Must wait until after ttclos() */
+#ifdef NT                               /* to change these settings       */
+#ifdef CK_TAPI
+    tttapi = special & CX_TAPI;
+#endif /* CK_TAPI */
+#else
+    ttslip = special & CX_SLIP;
+    ttppp  = special & CX_PPP;
+#endif /* NT */
+    ttshare = shr;                     /* Shareable device ? */
+    debug(F110,"OS2 SET PORT final s",s,"");
+#endif /* OS2 */
+
+    /* Open the new line */        
+
+    ckstrncpy(ttname,s,TTNAMLEN);
+    if ((y = ttopen(s,&_local,mdmtyp,cdtimo)) > -1) {
+       cxtype = (mdmtyp > 0) ? CXT_MODEM : CXT_DIRECT;
+#ifndef NODIAL
+       dialsta = DIA_UNK;
+#ifdef CK_TAPI
+       /* if the line is a tapi device, then we need to auto-execute */
+       /* SET MODEM TYPE TAPI - which we do the equivalent of here.  */
+       if (tttapi) {
+           extern int usermdm;
+           usermdm = 0;
+           initmdm(38);                /* From ckudia.c n_TAPI == 38 */
+       }
+#endif /* CK_TAPI */
+#endif /* NODIAL */
+       success = 1;
+    } else {                           /* Failed */
+#ifdef OS2ONLY
+       if (!strcmp(s,dftty))   /* Do not generate an error with dftty */
+         ;
+       else if (y == -6 && ttslip) {
+           makestr(&slmsg,"Can't access SLIP driver");
+           if (msg) printf("?%s\n",slmsg);
+       } else if (y == -6 && ttppp) {
+           makestr(&slmsg,"Can't access PPP driver");
+           if (msg) printf("?%s\n",slmsg);
+       } else
+#endif /* OS2ONLY */
+         if (y == -2) {
+             makestr(&slmsg,"Timed out - no carrier");
+             if (msg) {
+                 printf("?%s\n",slmsg);
+                 if (hints) {
+                     printf("\n*************************\n");
+                     printf(
+                      "HINT (Use SET HINTS OFF to suppress future hints):\n");
+                     printf(
+                         "Try SET CARRIER OFF and SET LINE again, or else\n");
+                     printf("SET MODEM, SET LINE, and then DIAL.\n");
+                     printf("*************************\n\n");
+                 }
+             }
+         } else if (y == -3) {
+             makestr(&slmsg,"Access to lock denied");
+             if (msg) {
+#ifdef UNIX
+                 printf(
+                  "Sorry, write access to UUCP lockfile directory denied.\n");
+#ifndef NOHINTS
+                 if (hints) {
+                     printf("\n*************************\n");
+                     printf(
+                      "HINT (Use SET HINTS OFF to suppress future hints):\n");
+                     printf(
+         "Please read the installation instructions file, %sckuins.txt,\n",
+                            k_info_dir ? k_info_dir : ""
+                             );
+                     printf(
+         "or the UNIX appendix of the manual, \"Using C-Kermit\"\n"
+                             );
+                     printf(
+          "or visit http://www.columbia.edu/kermit/ckuins.html \n"
+                             );
+                     printf("*************************\n\n");
+                 }
+#endif /* NOHINTS */
+#else
+                 printf("Sorry, access to lock denied: %s\n",s);
+#endif /* UNIX */
+             }
+         } else if (y == -4) {
+             makestr(&slmsg,"Access to device denied");
+             if (msg) {
+                 printf("Sorry, access to device denied: %s\n",s);
+#ifdef UNIX
+#ifndef NOHINTS
+                 if (hints) {
+                     printf("\n*************************\n");
+                     printf(
+                     "HINT (Use SET HINTS OFF to suppress future hints):\n");
+                     printf(
+           "Please read the installation instructions file, %sckuins.txt,\n",
+                             k_info_dir ? k_info_dir : ""
+                             );
+                     printf(
+           "or the UNIX appendix of the manual, \"Using C-Kermit\".\n"
+                             );
+                     printf("*************************\n\n");
+                 }
+#endif /* NOHINTS */
+#endif /* UNIX */
+             }
+         } else if (y == -5) {
+             makestr(&slmsg,"Device is in use or unavailable");
+             if (msg)
+#ifdef VMS
+               printf(
+                 "Sorry, device is in use or otherwise unavailable: %s\n",s);
+#else
+             printf("Sorry, device is in use: %s\n",s);
+#endif /* VMS */
+         } else {                      /* Other error. */
+             makestr(&slmsg,"Device open failed");
+             if (
+#ifdef VMS
+                 1
+#else
+                 errno
+#endif /* VMS */
+                 ) {
+                 int x;                /* Find a safe, long buffer */
+                 makestr(&slmsg,ck_errstr());
+#ifndef VMS
+                 debug(F111,"cx_serial serial errno",slmsg,errno);
+#endif /* VMS */
+                 if (msg)
+                   printf("Connection to %s failed: %s\n",s,slmsg);
+             } else if (msg)
+               printf("Sorry, can't open connection: %s\n",s);
+         }
+    }
+    network = 0;                       /* No network connection active */
+    speed = ttgspd();
+    if (!success) {
+        local = dfloc;                  /* Go back to normal */
+#ifndef MAC
+        ckstrncpy(ttname,dftty,TTNAMLEN); /* Restore default tty name */
+#endif /* MAC */
+        haveline = 0;
+        if (mdmtyp < 0) {               /* Switching from net to async? */
+            if (mdmsav > -1)            /* Restore modem type from last */
+              mdmtyp = mdmsav;          /* SET MODEM command, if any. */
+            else
+              mdmtyp = 0;
+            mdmsav = -1;
+        }
+        return(msg ? -9 : 0);          /* Return failure */
+    }
+    if (_local > -1)
+      local = _local;                  /* Opened ok, set local/remote. */
+    makestr(&slmsg,NULL);              /* Erase SET LINE message */
+    ckstrncpy(ttname,s,TTNAMLEN);       /* Copy name into real place. */
+    debug(F110,"cx_serial ok",ttname,0);
+#ifndef NOXFER
+    if ((reliable != SET_OFF || !setreliable)) /* Assume not reliable. */
+      reliable = SET_OFF;
+#endif /* NOXFER */
+
+  xcx_serial:
+    setflow();                          /* Set appropriate flow control */
+    haveline = 1;
+#ifdef CKLOGDIAL
+      dologline();
+#endif /* CKLOGDIAL */
+
+#ifndef NOSPL
+    if (local) {
+        if (nmac) {                     /* Any macros defined? */
+            int k;                      /* Yes */
+            k = mlook(mactab,"on_open",nmac);   /* Look this up */
+            if (k >= 0) {                       /* If found, */
+                if (dodo(k,ttname,0) > -1)      /* set it up, */
+                  parser(1);                    /* and execute it */
+            }
+        }
+    }
+#endif /* NOSPL */
+
+    if (local && (cx || sx)) {          /* /CONNECT or /SERVER switch given */
+        extern int carrier;
+        if (carrier != CAR_OFF) {      /* Looking for carrier? */
+            /* Open() turns on DTR -- wait up to a second for CD to come up */
+            int i, x;
+            for (i = 0; i < 10; i++) {  /* WAIT 1 CD... */
+                x = ttgmdm();
+                if (x < 0 || x & BM_DCD)
+                  break;
+                msleep(100);
+            }
+        }
+        if (cx) {                       /* /CONNECT */
+            /* Command was confirmed so we can pre-pop command level. */
+            /* This is so CONNECT module won't think we're executing a */
+            /* script if CONNECT was the final command in the script. */
+
+            if (cmdlvl > 0)
+              prepop();
+#ifndef NODIAL
+            dialsta = DIA_UNK;
+#endif /* NODIAL */
+#ifdef LOCUS
+            if (autolocus) {
+                setlocus(1,1);
+            }
+#endif /* LOCUS */
+            success = doconect(0, cmdlvl == 0 ? 1 : 0);
+            if (ttchk() < 0)
+              dologend();
+            return(success);
+#ifndef NOXFER
+        } else if (sx) {                /* /SERVER */
+            sstate = 'x';
+#ifdef MAC
+            what = W_RECV;
+            scrcreate();
+#endif /* MAC */
+            if (local) displa = 1;
+#ifdef AMIGA
+            reqoff();                   /* No DOS requestors while server */
+#endif /* AMIGA */
+#endif /* NOXFER */
+        }
+    }
+#ifndef NODIAL
+    dialsta = DIA_UNK;
+#endif /* NODIAL */
+#ifdef LOCUS
+    if (autolocus) {
+        setlocus(1,1);
+    }
+#endif /* LOCUS */
+    return(success = 1);
+}
+
+
+/* S E T L I N -- parse name of and then open communication device. */
+/*
+  Call with:
+    xx == XYLINE for a serial (tty) line, XYHOST for a network host,
+    zz == 0 means if user doesn't give a device name, continue current
+            active connection (if any);
+    zz != 0 means if user doesn't give a device name, then close the
+            current connection and restore the default communication device.
+    fc == 0 to just make the connection, 1 to also CONNECT (e.g. "telnet").
+*/
+int
+setlin(xx, zz, fc) 
+    int xx, zz, fc; 
+{
+    extern char pwbuf[], * g_pswd;
+    extern int pwflg, pwcrypt, g_pflg, g_pcpt, nolocal;
+    int wait;
+    /* int tn_wait_sv; */
+    int mynet;
+    int _local = -1;
+    int c, i, haveswitch = 0;
+    int haveuser = 0;
+    int getval = 0;
+    int wild = 0;                       /* Filespec has wildcards */
+    int cx = 0;                         /* Connect after */
+    int sx = 0;                         /* Become server after */
+    int a_type = -1;                    /* Authentication type */
+    int e_type = -1;                    /* Telnet /ENCRYPT type */
+#ifdef CK_ENCRYPTION
+    int encrypt = 0;                    /* Encrypted? */
+#endif /* CK_ENCRYPTION */
+    int shr = 0;                        /* Share serial device */
+    int confirmed = 0;                  /* Command has been entered */
+    struct FDB sw, tx, nx;
+#ifdef OS2
+    struct FDB fl;
+#endif /* OS2 */
+
+    char * ss;
+#ifdef TCPSOCKET
+    int rawflg = 0;
+#endif /* TCPSOCKET */
+
+    char srvbuf[SRVBUFSIZ+1];
+
+#ifdef OS2
+#ifdef NT
+    int xxtapi = 0;
+#else
+    int xxslip = 0, xxppp = 0;
+#endif /* NT */
+#endif /* OS2 */
+
+    int dossh = 0;
+
+    debug(F101,"setlin fc","",fc);
+    debug(F101,"setlin zz","",zz);
+    debug(F101,"setlin xx","",xx);
+
+#ifdef SSHCMD
+    if (xx == XXSSH) {                  /* SSH becomes PTY SSH ... */
+        dossh = 1;
+        xx = XYHOST;
+    }
+#endif /* SSHCMD */
+
+#ifdef TNCODE
+    /* tn_wait_sv = tn_wait_flg; */
+    wait = tn_wait_flg;
+#else
+    /* tn_wait_sv = 0; */
+    wait = 0;
+#endif /* TNCODE */
+
+    mynet = nettype;
+
+    if (nolocal) {
+        makestr(&slmsg,"Making connections is disabled");
+        printf("?Sorry, making connections is disabled\n");
+        return(-9);
+    }
+    if (netsave > -1)
+      nettype = netsave;
+
+    if (fc != 0 || zz == 0)             /* Preset /CONNECT switch */
+      cx = 1;
+
+    debug(F101,"setlin cx","",cx);
+
+    *srvbuf = NUL;
+
+    line[0] = NUL;
+    s = line;
+
+#ifdef NETCONN
+#ifdef CK_SECURITY
+    if (tmpstring)
+        makestr(&tmpstring,NULL);
+#endif /* CK_SECURITY */
+    if (tmpusrid)
+        makestr(&tmpusrid,NULL);
+#endif /* NETCONN */
+
+    autoflow = 1;                       /* Enable automatic flow setting */
+
+    if (xx == XYHOST) {                 /* SET HOST <hostname> */
+#ifndef NETCONN
+        makestr(&slmsg,"Network connections not supported");
+        printf("?%s\n",slmsg);
+        return(-9);
+#else /* NETCONN */
+#ifndef NOPUSH
+        if ((mynet == NET_CMD || mynet == NET_PTY || dossh) && nopush) {
+            makestr(&slmsg,"Access to external commands is disabled");
+            printf("?Sorry, access to external commands is disabled\n");
+            return(-9);
+        }
+#endif /* NOPUSH */
+
+#ifdef SSHCMD
+        if (dossh) {                    /* SSH connection via pty */
+            int k;
+            k = ckstrncpy(line, sshcmd ? sshcmd : defsshcmd, LINBUFSIZ);
+            debug(F111,"setlin sshcmd 1",line,k);
+            if ((x = cmtxt("Optional switches and hostname","",&s,xxstring))<0)
+              return(x);
+            if (!*s) {
+                printf("?SSH to where?\n");
+                return(-9);
+            }
+            if (k < LINBUFSIZ) {
+                line[k++] = SP;
+                line[k] = NUL;
+                debug(F111,"setlin sshcmd 2",line,k);
+            } if (k < LINBUFSIZ) {
+                ckstrncpy(&line[k],s,LINBUFSIZ-k);
+                debug(F111,"setlin sshcmd 3",line,k);
+            } else {
+                printf("?Too long\n");
+                return(-9);
+            }
+           x = cx_net( NET_PTY,                /* network type */
+                        0,                      /* protocol (not used) */
+                        line,                   /* host */
+                        NULL,                   /* service (not used) */
+                        NULL,                   /* username (not used) */
+                        NULL,                   /* password (not used) */
+                        NULL,                   /* command (not used) */
+                        -1,-1,-1,               /* params 1-3 (not used) */
+                        1,                      /* connect immediately */
+                        sx,                     /* server? */
+                        zz,                     /* close current? */
+                        0);                     /* not gui */
+           debug(F111,"setlin cx_net",line,x);
+           return(x);
+        }
+#endif /* SSHCMD */
+
+/*
+  Here we parse optional switches and then the hostname or whatever,
+  which depends on the network type.  The tricky part is, the network type
+  can be set by a switch.
+*/
+#ifndef NOSPL
+        makestr(&g_pswd,pwbuf);         /* Save global pwbuf */
+        g_pflg = pwflg;                 /* and flag */
+        g_pcpt = pwcrypt;
+#endif /* NOSPL */
+
+        confirmed = 0;
+        haveswitch = 0;
+#ifdef NETFILE
+        if (mynet != NET_FILE) {
+#endif /* NETFILE */
+            ss = (mynet == NET_CMD || mynet == NET_PTY) ?
+              "Command, or switch" :
+                (mynet == NET_TCPA || mynet == NET_TCPB
+                  || mynet == NET_SSH) ?
+                  "Hostname, ip-address, or switch" :
+                    "Host or switch";
+            if (fc) {
+                if (mynet == NET_TCPB &&
+                    (ttnproto == NP_TELNET || ttnproto == NP_KERMIT)) {
+                    if (nshteltab) {
+                        haveswitch++;
+                        cmfdbi(&sw,_CMKEY,ss,"","",nshteltab,4,xxstring,
+                             shteltab,&nx);
+                    }
+                }
+#ifdef RLOGCODE
+                else if (mynet == NET_TCPB && ttnproto == NP_RLOGIN) {
+                    if (nshrlgtab) {
+                        haveswitch++;
+                        cmfdbi(&sw,_CMKEY,ss,"","",nshrlgtab,4,xxstring,
+                               shrlgtab,&nx);
+                    }
+                }
+#endif /* RLOGCODE */
+            } else {
+                haveswitch++;
+                cmfdbi(&sw,_CMKEY,ss,"","",nshtab,4,xxstring,shtab,&nx);
+            }
+#ifdef NETFILE
+        }
+#endif /* NETFILE */
+        if (mynet == NET_TCPB || mynet == NET_SLAT ||
+           mynet == NET_SSH  || mynet == NET_DEC) {
+            cmfdbi(&nx,_CMFLD,"Host","","",0,0,xxstring,NULL,NULL);
+#ifdef NETFILE
+        } else if (mynet == NET_FILE) {
+            cmfdbi(&nx,_CMIFI,"Filename","","",0,0,xxstring,NULL,NULL);
+#endif /* NETFILE */
+#ifdef PTYORPIPE
+        } else if (mynet == NET_CMD || mynet == NET_PTY) {
+            cmfdbi(&nx,_CMTXT,"Command","","",0,0,xxstring,NULL,NULL);
+#endif /* PTYORPIPE */
+        } else {
+            cmfdbi(&nx,_CMTXT,"Host","","",0,0,xxstring,NULL,NULL);
+        }
+        while (1) {
+            x = cmfdb(haveswitch ? &sw : &nx);
+            debug(F101,"setlin cmfdb","",x);
+            if (x < 0)
+              if (x != -3)
+                return(x);
+            if (x == -3) {
+                if ((x = cmcfm()) < 0) {
+                    return(x);
+                } else {
+                    confirmed = 1;
+                    break;
+                }
+            }
+            if (cmresult.fcode != _CMKEY) {    /* Not a switch */
+                ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Save the data */
+                s = line;                      /* that was parsed... */
+                if (cmresult.fcode == _CMIFI) {
+                    wild = cmresult.nresult;
+                } else if (cmresult.fcode == _CMTXT) {
+                    confirmed = 1;
+                }
+                break;                  /* and break out of this loop */
+            }
+            c = cmgbrk();               /* Have switch - get break character */
+            getval = (c == ':' || c == '='); /* Must parse an agument? */
+            if (getval && !(cmresult.kflags & CM_ARG)) {
+                printf("?This switch does not take arguments\n");
+                return(-9);
+            }
+            if (!getval && (cmgkwflgs() & CM_ARG)) {
+                printf("?This switch requires an argument\n");
+                return(-9);
+            }
+            switch (cmresult.nresult) { /* It's a switch.. */
+              case SL_CNX:              /* /CONNECT */
+                cx = 1;
+                sx = 0;
+                break;
+              case SL_SRV:              /* /SERVER */
+                cx = 0;
+                sx = 1;
+                break;
+#ifdef NETCMD
+              case SL_CMD:              /* /COMMAND */
+                netsave = mynet;
+                mynet = NET_CMD;
+                break;
+#endif /* NETCMD */
+#ifdef NETPTY
+              case SL_PTY:              /* /PTY */
+                netsave = mynet;
+                mynet = NET_PTY;
+                break;
+#endif /* NETPTY */
+              case SL_NET:              /* /NETWORK-TYPE */
+                if ((x = cmkey(netcmd,nnets,"","",xxstring)) < 0)
+                  return(x);
+                mynet = x;
+                break;
+
+#ifdef CK_SECURITY
+              case SL_PSW:              /* /PASSWORD: */
+                if (!getval)
+                  break;
+                debok = 0;
+                if ((x = cmfld("Password","",&s,xxstring)) < 0) {
+                    if (x == -3) {
+                        makestr(&tmpstring,"");
+                    } else {
+                        return(x);
+                    }
+                } else {
+                    s = brstrip(s);
+                    if ((x = (int)strlen(s)) > PWBUFL) {
+                        makestr(&slmsg,"Internal error");
+                        printf("?Sorry, too long - max = %d\n",PWBUFL);
+                        return(-9);
+                    }
+                    makestr(&tmpstring,s);
+                }
+                break;
+#endif /* CK_SECURITY */
+
+              case SL_UID:              /* /USERID: */
+                if (!getval)
+                  break;
+                if ((x = cmfld("Userid","",&s,xxstring)) < 0) {
+                    if (x == -3) {
+                        makestr(&tmpusrid,"");
+                    } else {
+                        return(x);
+                    }
+                } else {
+                    s = brstrip(s);
+                    if ((x = (int)strlen(s)) > 63) {
+                        makestr(&slmsg,"Internal error");
+                        printf("?Sorry, too long - max = %d\n",63);
+                        return(-9);
+                    }
+                    makestr(&tmpusrid,s);
+                    haveuser = 1;
+                }
+                break;
+
+#ifdef CK_AUTHENTICATION
+#ifdef CK_SRP
+              case SL_SRP:
+                a_type = AUTHTYPE_SRP;
+                break;
+#endif /* CK_SRP */
+#ifdef CK_SSL
+              case SL_SSL:
+                a_type = AUTHTYPE_SSL;
+                break;
+#endif /* CK_SSL */
+#ifdef NT
+              case SL_NTLM:
+                a_type = AUTHTYPE_NTLM;
+                break;
+#endif /* NT */
+#ifdef CK_KERBEROS
+              case SL_KRB4:
+                a_type = AUTHTYPE_KERBEROS_V4;
+                if (ttnproto == NP_RLOGIN)
+                  ttnproto =
+#ifdef CK_ENCRYPTION
+                    encrypt ? NP_EK4LOGIN :
+#endif /* CK_ENCRYPTION */
+                      NP_K4LOGIN;
+                else if (ttnproto == NP_K5LOGIN)
+                  ttnproto = NP_K4LOGIN;
+#ifdef CK_ENCRYPTION
+                else if (ttnproto == NP_EK5LOGIN)
+                  ttnproto = NP_EK4LOGIN;
+#endif /* CK_ENCRYPTION */
+                break;
+              case SL_KRB5:
+                a_type = AUTHTYPE_KERBEROS_V5;
+                if (ttnproto == NP_RLOGIN)
+                  ttnproto =
+#ifdef CK_ENCRYPTION
+                    encrypt ? NP_EK5LOGIN :
+#endif /* CK_ENCRYPTION */
+                      NP_K5LOGIN;
+                else if (ttnproto == NP_K4LOGIN)
+                  ttnproto = NP_K5LOGIN;
+#ifdef CK_ENCRYPTION
+                else if (ttnproto == NP_EK4LOGIN)
+                  ttnproto = NP_EK5LOGIN;
+#endif /* CK_ENCRYPTION */
+                break;
+#endif /* CK_KERBEROS */
+              case SL_AUTH: {
+                  extern struct keytab autyptab[];
+                  extern int nautyp;
+                  if ((x = cmkey(autyptab,nautyp,"type of authentication",
+                                 "automatic",xxstring)) < 0)
+                    return(x);
+                  a_type = x;
+                  break;
+              }
+#endif /* CK_AUTHENTICATION */
+#ifdef CK_ENCRYPTION
+              case SL_ENC:
+                switch (ttnproto) {
+                  case NP_K4LOGIN:
+                    ttnproto = NP_EK4LOGIN;
+                    encrypt = 1;
+                    break;
+                  case NP_K5LOGIN:
+                    ttnproto = NP_EK5LOGIN;
+                    encrypt = 1;
+                    break;
+                  case NP_KERMIT:
+                  case NP_TELNET: {
+                      static struct keytab * tnetbl = NULL;
+                      static int ntnetbl = 0;
+                      x = ck_get_crypt_table(&tnetbl,&ntnetbl);
+                      debug(F101,"ck_get_crypt_table x","",x);
+                      debug(F101,"ck_get_crypt_table n","",ntnetbl);
+                      if (x < 1 || !tnetbl || ntnetbl < 1) /* Didn't get it */
+                        x = 0;
+                      if (!x) {
+                          makestr(&slmsg,"Internal error");
+                          printf("?Oops, types not loaded\n");
+                          return(-9);
+                      }
+                      if ((x = cmkey(tnetbl,ntnetbl,"type of encryption",
+                                     "automatic",xxstring)) < 0)
+                        return(x);
+                      e_type = x;
+                      break;
+                  }
+                }
+                break;
+#endif /* CK_ENCRYPTION */
+              case SL_WAIT:
+                wait = 1;
+                break;
+              case SL_NOWAIT:
+                wait = 0;
+                break;
+            }
+        }
+
+#ifdef NETFILE
+        if (mynet == NET_FILE) {        /* Parsed by cmifi() */
+           if ((x = cmcfm()) < 0)      /* Needs confirmation */
+             return(x);
+           x = cx_net(mynet,           /* nettype */
+                      0,               /* protocol (not used) */
+                      line,            /* host */
+                      "",              /* port */
+                      NULL,            /* alternate username */
+                      NULL,            /* password */
+                      NULL,            /* command to execute */
+                      0,               /* param1 */
+                      0,               /* param2 */
+                      0,               /* param3 */
+                      cx,              /* enter CONNECT mode */
+                      sx,              /* enter SERVER mode */
+                      zz,              /* close connection if open */
+                      0                /* gui */
+                      );
+        }
+#endif /* NETFILE */
+
+#ifdef NETCMD
+        if (mynet == NET_CMD || mynet == NET_PTY) {
+            char *p = NULL;
+            if (!confirmed) {
+                if ((x = cmtxt("Rest of command","",&s,xxstring)) < 0)
+                  return(x);
+                if (*s) {
+                    strncat(line," ",LINBUFSIZ);
+                    strncat(line,s,LINBUFSIZ);
+                }
+                s = line;
+            }
+            /* s == line - so we must protect the line buffer */
+            s = brstrip(s);
+            makestr(&p,s);
+            ckstrncpy(line,p,LINBUFSIZ);
+            makestr(&p,NULL);
+
+            x = cx_net( mynet,                  /* nettype */
+                        0,                      /* protocol (not used) */
+                        line,                   /* host */
+                        "",                     /* port */
+                        NULL,                   /* alternate username */
+                        NULL,                   /* password */
+                        NULL,                   /* command to execute */
+                        0,                      /* param1 */
+                        0,                      /* param2 */
+                        0,                      /* param3 */
+                        cx,                     /* enter CONNECT mode */
+                        sx,                     /* enter SERVER mode */
+                        zz,                     /* close connection if open */
+                        0                       /* gui */
+                        );
+        }
+#endif /* NETCMD */
+
+#ifdef NPIPE                            /* Named pipe */
+        if (mynet == NET_PIPE) {        /* Needs backslash twiddling */
+            if (line[0]) {
+                if (strcmp(line,"*")) {    /* If remote, begin with */
+                    char * p = NULL;
+                    makestr(&p,line);      
+                    ckstrncpy(line,"\\\\",LINBUFSIZ); /* server name */
+                    ckstrncat(line,p,LINBUFSIZ);
+                    makestr(&p,NULL);      
+                } else {
+                    line[0]='\0';
+                }
+                ckstrncat(line,"\\pipe\\", LINBUFSIZ); /* Make pipe name */
+                ckstrncat(line,pipename, LINBUFSIZ); /* Add name of pipe */
+
+                x = cx_net(mynet,      /* nettype */
+                          0,           /* protocol (not used) */
+                          line,        /* host */
+                          "",          /* port */
+                          NULL,        /* alternate username */
+                          NULL,        /* password */
+                          NULL,        /* command to execute */
+                          0,           /* param1 */
+                          0,           /* param2 */
+                          0,           /* param3 */
+                          cx,          /* enter CONNECT mode */
+                          sx,          /* enter SERVER mode */
+                          zz,          /* close connection if open */
+                          0            /* gui */
+                          );
+            }
+        }
+#endif /* NPIPE */
+
+#ifdef SUPERLAT
+        if (mynet == NET_SLAT) {        /* Needs password, etc. */
+            slat_pwd[0] = NUL;          /* Erase any previous password */
+            debok = 0;
+            if (*line) {               /* If they gave a host name... */
+                if ((x = cmfld(
+                     "password,\n or carriage return if no password required",
+                               "",
+                               &s,
+                               xxstring
+                               )) < 0 && x != -3)
+                  return(x);
+                ckstrncpy(slat_pwd,s,18); /* Set the password, if any */
+            }
+            if ((x = cmcfm()) < 0) return(x); /* Confirm the command */
+
+            x = cx_net(mynet,          /* nettype */
+                       0,              /* protocol (not used) */
+                       line,           /* host */
+                       "",             /* port */
+                       NULL,           /* alternate username */
+                       NULL,           /* password */
+                       NULL,           /* command to execute */
+                       0,              /* param1 */
+                       0,              /* param2 */
+                       0,              /* param3 */
+                       cx,             /* enter CONNECT mode */
+                       sx,             /* enter SERVER mode */
+                       zz,             /* close connection if open */
+                       0               /* gui */
+                       );
+        }
+#endif /* SUPERLAT */
+
+#ifdef DECNET
+        if (mynet == NET_DEC) {  
+            if (!line[0]) {                   /* If they gave a host name... */
+                printf("?hostname required\n");
+                return(-3);
+            }
+            if ((x = cmcfm()) < 0) return(x); /* Confirm the command */
+
+            x = cx_net(mynet,          /* nettype */
+                       0,              /* protocol (not used) */
+                       line,           /* host */
+                       "",             /* port */
+                       NULL,           /* alternate username */
+                       NULL,           /* password */
+                       NULL,           /* command to execute */
+                       0,              /* param1 */
+                       0,              /* param2 */
+                       0,              /* param3 */
+                       cx,             /* enter CONNECT mode */
+                       sx,             /* enter SERVER mode */
+                       zz,             /* close connection if open */
+                       0               /* gui */
+                       );
+        }
+#endif /* DECNET */
+
+#ifdef SSHBUILTIN
+        if (mynet == NET_SSH) {                /* SSH connection */
+            int k, havehost = 0, trips = 0;
+            int    tmpver = -1, tmpxfw = -1, tmpssh_cas;
+#ifndef SSHTEST
+            extern int sl_ssh_xfw, sl_ssh_xfw_saved;
+            extern int sl_ssh_ver, sl_ssh_ver_saved;
+#endif /* SSHTEST */
+            extern struct keytab sshopnsw[];
+            extern int nsshopnsw;
+            extern char *ssh_tmpcmd, *ssh_tmpport;
+            struct FDB sw, kw, fl;
+
+            debug(F110,"setlin SSH service 0",srvbuf,0);
+            debug(F110,"setlin SSH host s 2",s,0);
+            if (*s) {           /* If they gave a host name... */
+                debug(F110,"setlin SSH host s 1",s,0);
+                if (*s == '*') {
+                    makestr(&slmsg,"Incoming connections not supported");
+                    printf(
+     "?Sorry, incoming connections not supported for SSH.\n"
+                           );
+                    return(-9);
+                }
+                ckstrncpy(line,s,LINBUFSIZ);
+            } else {
+                printf("?hostname required\n");
+                return(-3);
+            }
+
+            /* Parse [ port ] [ switches ] */
+            cmfdbi(&kw,                 /* Switches */
+                    _CMKEY,
+                    "Port number or service name,\nor switch",
+                    "",
+                    "",
+                    nsshopnsw,
+                    4,
+                    xxstring,
+                    sshopnsw,
+                    &fl
+                    );
+            cmfdbi(&fl,                 /* Port number or service name */
+                    _CMFLD,
+                    "",
+                    "",
+                    "",
+                    0,
+                    0,
+                    xxstring,
+                    NULL,
+                    NULL
+                    );
+            trips = 0;                  /* Explained below */
+            while (1) {                 /* Parse port and switches */
+                y = cmfdb(&kw);         /* Get a field */
+                if (y == -3)            /* User typed CR so quit from loop */
+                    break;
+                if (y < 0)              /* Other parse error, pass it back */
+                    return(y);
+                switch (cmresult.fcode) { /* Field or Keyword? */
+                case _CMFLD:              /* Field */
+                    ckstrncpy(srvbuf,cmresult.sresult,SRVBUFSIZ);
+                    break;
+                case _CMKEY:            /* Keyword */
+                    switch (cmresult.nresult) { /* Which one? */
+                    case SSHSW_PWD:
+                        if (!cmgbrk()) {
+                            printf("?This switch requires an argument\n");
+                            return(-9);
+                        }
+                        debok = 0;
+                        if ((y = cmfld("Password","",&s,xxstring)) < 0) {
+                            if (y == -3) {
+                                makestr(&tmpstring,"");
+                            } else {
+                                return(y);
+                            }
+                        } else {
+                            s = brstrip(s);
+                            if ((y = (int)strlen(s)) > PWBUFL) {
+                                makestr(&slmsg,"Internal error");
+                                printf("?Sorry, too long - max = %d\n",PWBUFL);
+                                return(-9);
+                            }
+                            makestr(&tmpstring,s);
+                        }
+                        break;
+                    case SSHSW_USR:             /* /USER: */
+                        if (!cmgbrk()) {
+                            printf("?This switch requires an argument\n");
+                            return(-9);
+                        }
+                        if ((y = cmfld("Username","",&s,xxstring)) < 0)
+                            return(y);
+                        s = brstrip(s);
+                        makestr(&tmpusrid,s);
+                        break;
+                    case SSHSW_VER:
+                        if ((y = cmnum("Number","",10,&z,xxstring)) < 0)
+                            return(y);
+                        if (z < 1 || z > 2) {
+                            printf("?Out of range: %d\n",z);
+                            return(-9);
+                        }
+                        tmpver = z;
+                        break;
+                    case SSHSW_CMD:
+                    case SSHSW_SUB:
+                        if ((y = cmfld("Text","",&s,xxstring)) < 0)
+                          return(y);
+                        makestr(&ssh_tmpcmd,s);
+                        tmpssh_cas = (cmresult.nresult == SSHSW_SUB);
+                        break;
+                    case SSHSW_X11:
+                        if ((y = cmkey(onoff,2,"","on",xxstring)) < 0)
+                            return(y);
+                        tmpxfw = y;
+                        break;
+                    default:
+                        return(-2);
+                    }
+                }
+                if (trips++ == 0) {     /* After first time through */
+                    cmfdbi(&kw,         /* only parse switches, not port. */
+                            _CMKEY,
+                            "Switch",
+                            "",
+                            "",
+                            nsshopnsw,
+                            4,
+                            xxstring,
+                            sshopnsw,
+                            NULL
+                            );
+                }
+            }
+            if ((y = cmcfm()) < 0)      /* Get confirmation */
+                return(y);
+
+            debug(F110,"setlin pre-cx_net line",line,0);
+            debug(F110,"setlin pre-cx_net srvbuf",srvbuf,0);
+            x = cx_net( mynet,                  /* nettype */
+                        0,                      /* protocol (not used) */
+                        line,                   /* host */
+                        srvbuf,                 /* port */
+                        tmpusrid,               /* alternate username */
+                        tmpstring,              /* password */
+                        ssh_tmpcmd,             /* command to execute */
+                        tmpver,                 /* param1 - ssh version */
+                        tmpssh_cas,             /* param2 - ssh cas  */
+                        tmpxfw,                 /* param3 - ssh x11fwd */
+                        cx,                     /* enter CONNECT mode */
+                        sx,                     /* enter SERVER mode */
+                        zz,                     /* close connection if open */
+                        0                       /* gui */
+                        );
+            if (tmpusrid)
+                makestr(&tmpusrid,NULL);
+            if (ssh_tmpcmd)
+                makestr(&ssh_tmpcmd,NULL);
+        }
+#endif /* SSHBUILTIN */
+
+#ifdef TCPSOCKET
+        if (mynet == NET_TCPB) {        /* TCP/IP connection */
+            debug(F110,"setlin service 0",srvbuf,0);
+            debug(F110,"setlin host s 2",s,0);
+            if (*s) {                  /* If they gave a host name... */
+                debug(F110,"setlin host s 1",s,0);
+#ifdef NOLISTEN
+                if (*s == '*') {
+                    makestr(&slmsg,"Incoming connections not supported");
+                    printf(
+     "?Sorry, incoming connections not supported in this version of Kermit.\n"
+                           );
+                    return(-9);
+                }
+#endif /* NOLISTEN */
+#ifdef RLOGCODE
+                /* Allow a username if rlogin is requested */
+                if (mynet == NET_TCPB &&
+                    (ttnproto == NP_RLOGIN || ttnproto == NP_K5LOGIN ||
+                     ttnproto == NP_EK5LOGIN || ttnproto == NP_K4LOGIN ||
+                     ttnproto == NP_EK4LOGIN
+                    )) {
+                    int y;
+                    uidflag = 0;
+                    /* Check for "host:service" */
+                    for ( ; (*s != '\0') && (*s != ':'); s++) ;
+                    if (*s) {   /* Service, save it */
+                        *s = NUL;
+                        ckstrncpy(srvbuf,++s,SRVBUFSIZ);
+                    } else {            /* No :service, then use default. */
+#ifdef VMS
+                        switch (ttnproto) {
+                          case NP_RLOGIN:
+                            ckstrncpy(srvbuf,"513",SRVBUFSIZ); /* "login" */
+                            break;
+                          case NP_K4LOGIN:
+                          case NP_K5LOGIN:
+                            ckstrncpy(srvbuf,"543",SRVBUFSIZ); /* "klogin" */
+                            break;
+                          case NP_EK4LOGIN:
+                          case NP_EK5LOGIN:
+                            ckstrncpy(srvbuf,"2105",SRVBUFSIZ); /* "eklogin" */
+                            break;
+                        }
+#else /* VMS */
+                        switch (ttnproto) {
+                          case NP_RLOGIN:
+                            ckstrncpy(srvbuf,"login",SRVBUFSIZ);
+                            break;
+                          case NP_K4LOGIN:
+                          case NP_K5LOGIN:
+                            ckstrncpy(srvbuf,"klogin",SRVBUFSIZ);
+                            break;
+                          case NP_EK4LOGIN:
+                          case NP_EK5LOGIN:
+                            ckstrncpy(srvbuf,"eklogin",SRVBUFSIZ);
+                            break;
+                        }
+#endif /* VMS */
+                    }
+                    if (!confirmed) {
+                        y = cmfld("Userid on remote system",
+                                  uidbuf,&s,xxstring);
+                        if (y < 0 && y != -3)
+                          return(y);
+                        if ((int)strlen(s) > 63) {
+                            makestr(&slmsg,"Internal error");
+                            printf("Sorry, too long\n");
+                            return(-9);
+                        }
+                        makestr(&tmpusrid,s);
+                    }
+                } else {        /* TELNET or SET HOST */
+#endif /* RLOGCODE */
+                    /* Check for "host:service" */
+                    for ( ; (*s != '\0') && (*s != ':'); s++) ;
+                    if (*s) {   /* Service, save it */
+                        *s = NUL;
+                        ckstrncpy(srvbuf,++s,SRVBUFSIZ);
+                    } else if (!confirmed) {
+                        /* No :service, let them type one. */
+                        if (*line != '*') { /* Not incoming */
+                            if (mynet == NET_TCPB && ttnproto == NP_KERMIT) {
+                                if ((x = cmfld(
+                                               "TCP service name or number",
+                                               "kermit",&s,xxstring)
+                                     ) < 0 && x != -3)
+                                  return(x);
+#ifdef RLOGCODE
+                            } else if (mynet == NET_TCPB &&
+                                       ttnproto == NP_RLOGIN) {
+                                if ((x = cmfld(
+  "TCP service name or number,\n or carriage return for rlogin (513)",
+                                               "login",&s,xxstring)
+                                     ) < 0 && x != -3)
+                                  return(x);
+#ifdef CK_AUTHENTICATION
+#ifdef CK_KERBEROS
+                            } else if (mynet == NET_TCPB &&
+                                       (ttnproto == NP_K4LOGIN ||
+                                       ttnproto == NP_K5LOGIN)) {
+                                if ((x = cmfld(
+  "TCP service name or number,\n or carriage return for klogin (543)",
+                                               "klogin",&s,xxstring)
+                                     ) < 0 && x != -3)
+                                  return(x);
+                            } else if (mynet == NET_TCPB &&
+                                       (ttnproto == NP_EK4LOGIN ||
+                                        ttnproto == NP_EK5LOGIN)) {
+                                if ((x = cmfld(
+  "TCP service name or number,\n or carriage return for eklogin (2105)",
+                                               "eklogin",&s,xxstring)
+                                     ) < 0 && x != -3)
+                                  return(x);
+#endif /* CK_KERBEROS */
+#endif /* CK_AUTHENTICATION */
+#endif /* RLOGCODE */
+                            } else {
+                                /* Do not set a default value in this call */
+                                /* If you do then it will prevent entries  */
+                                /* in the network directory from accessing */
+                                /* alternate ports.                        */
+
+                                if ((x = cmfld(
+                                               "TCP service name or number",
+                                               "",&s,xxstring)
+                                     ) < 0 && x != -3)
+                                  return(x);
+                            }
+                        } else { /* Incoming connection */
+                            if ((x = cmfld("TCP service name or number",
+                                           "",&s,xxstring)
+                                 ) < 0 && x != -3)
+                              return(x);
+                        }
+                        if (*s)         /* If they gave a service, */
+                          ckstrncpy(srvbuf,s,SRVBUFSIZ); /* copy it */
+                        debug(F110,"setlin service 0.5",srvbuf,0);
+                    }
+#ifdef RLOGCODE
+                }
+#endif /* RLOGCODE */
+                if (!confirmed) {
+                    char * defproto;
+                    switch (ttnproto) {
+                      case NP_RLOGIN:
+                        defproto = "/rlogin";
+                        break;
+                      case NP_K4LOGIN:
+                        defproto = "/k4login";
+                        break;
+                      case NP_K5LOGIN:
+                        defproto = "/k5login";
+                        break;
+                      case NP_EK4LOGIN:
+                        defproto = "/ek4login";
+                        break;
+                      case NP_EK5LOGIN:
+                        defproto = "/ek5login";
+                        break;
+                      case NP_KERMIT:
+                      case NP_TELNET:
+                        defproto = "/telnet";
+                        break;
+                      default:
+                        defproto = "/default";
+                    }
+                    if ((x = cmkey(tcprawtab,ntcpraw,"Switch",defproto,
+                                   xxstring)) < 0) {
+                        if (x != -3)
+                          return(x);
+                        else if ((x = cmcfm()) < 0)
+                          return(x);
+                    } else {
+                        rawflg = x;
+                        if ((x = cmcfm()) < 0)
+                          return(x);
+                    }
+                }
+            }
+            debug(F110,"setlin pre-cx_net line",line,0);
+            debug(F110,"setlin pre-cx_net srvbuf",srvbuf,0);
+            x = cx_net( mynet,                  /* nettype */
+                        rawflg                  /* protocol */,
+                        line,                   /* host */
+                        srvbuf,                 /* port */
+                        tmpusrid,               /* alternate username */
+                        tmpstring,              /* password */
+                        NULL,                   /* command to execute */
+                        a_type,                 /* param1 - telnet authtype */
+                        e_type,                 /* param2 - telnet enctype  */
+                        wait,                   /* param3 - telnet wait */
+                        cx,                     /* enter CONNECT mode */
+                        sx,                     /* enter SERVER mode */
+                        zz,                     /* close connection if open */
+                        0                       /* gui */
+                        );
+        }
+#endif /* TCPSOCKET */
+
+#ifdef CK_SECURITY
+        if (tmpstring)
+            makestr(&tmpstring,NULL);
+#endif /* CK_SECURITY */
+        if (tmpusrid)
+            makestr(&tmpusrid,NULL);
+       debug(F111,"setlin cx_net",line,x);
+       return(x);
+#endif /* NETCONN */
+    }
+
+/* Serial tty device, possibly modem, connection... */
+
+#ifdef OS2
+/*
+  User can type:
+    COM1..COM8 = Regular COM port
+    1..8       = Synonym for COM1..COM8, is translated to COM1..COM8
+    _n         = (n is a number) = open file handle
+    string     = any text string = name of some other kind of device,
+                 taken literally, as given.
+*/
+    s = "Communication device name";
+
+#ifdef CK_TAPI
+    if (TAPIAvail)
+      cktapiBuildLineTable(&tapilinetab, &_tapilinetab, &ntapiline);
+    if (!(tapilinetab && _tapilinetab && ntapiline > 0) &&
+       xx == XYTAPI_LIN ) {
+       makestr(&slmsg,"TAPI device not configured");
+       printf("\nNo TAPI Line Devices are configured for this system\n");
+       return(-9);
+    }
+    if (xx == XYTAPI_LIN) {            /* Default (first) TAPI line */
+       s = "tapi";                     /* (whatever it is) */
+    } else {                           /* Query the user */
+#endif /* CK_TAPI */
+
+/* Now parse optional switches and then device name */
+
+       confirmed = 0;
+       cmfdbi(&sw,_CMKEY,"Device name, or switch",
+              "","",npsltab,4,xxstring,psltab,&fl);
+       cmfdbi(&fl,_CMFLD,"",dftty,"",0,0,xxstring,NULL,NULL);
+       while (1) {
+           x = cmfdb(&sw);
+           debug(F101,"setlin cmfdb","",x);
+           if (x < 0)
+             if (x != -3)
+               return(x);
+           if (x == -3) {
+               if ((x = cmcfm()) < 0) {
+                   return(x);
+               } else {
+                   confirmed = 1;
+                   break;
+               }
+           }
+           if (cmresult.fcode == _CMFLD) {
+               s = cmresult.sresult;
+               break;
+           } else if (cmresult.fcode == _CMKEY) {
+               switch (cmresult.nresult) {
+                 case SL_CNX:          /* /CONNECT */
+                   cx = 1;
+                   sx = 0;
+                   break;
+                 case SL_SRV:          /* /SERVER */
+                   cx = 0;
+                   sx = 1;
+                   break;
+                 case SL_SHR:          /* /SHARE */
+                   shr = 1;
+                   break;
+                 case SL_NSH:          /* /NOSHARE */
+                   shr = 0;
+                   break;
+               }
+           }
+       }
+#ifdef CK_TAPI
+    }
+#endif /* CK_TAPI */
+
+    debug(F110,"OS2 SET PORT s",s,0);
+    y = lookup(os2devtab,s,nos2dev,&x); /* Look up in keyword table */
+    debug(F101,"OS2 SET PORT x","",x);
+    debug(F101,"OS2 SET PORT y","",y);
+    if ((y > -1) && (x >= 0 && x < 8)) { /* User typed a digit 1..8 */
+       s = os2devtab[x+8].kwd;         /* Substitite its real name */
+#ifdef NT
+       xxtapi = 0;
+#else /* NT */
+       xxslip = xxppp = 0;
+#endif /* NT */
+       debug(F110,"OS2 SET PORT subst s",s,"");
+#ifndef NT
+    } else if ((y >-1) && (x >= 16 && x < 24)) { /* SLIP access */
+       s = os2devtab[x-8].kwd;         /* Substitite its real name */
+       debug(F110,"OS2 SET PORT SLIP subst s",s,"");
+       xxslip = 1;
+       xxppp  = 0;
+    } else if ((y >-1) && (x >= 24 && x < 32)) { /* PPP access */
+       s = os2devtab[x-16].kwd;        /* Substitite its real name */
+       debug(F110,"OS2 SET PORT PPP subst s",s,"");
+       xxppp = 1;
+       xxslip = 0;
+       if ((y = cmkey(os2ppptab,
+                      nos2ppp,
+                      "PPP driver interface",
+                      "ppp0",
+                      xxstring)
+            ) < 0)
+         return(y);
+       debug(F101,"OS2 SET PORT PPP INTERFACE y","",y);
+       xxppp = (y % 10) + 1;
+#endif /* NT */
+    } else if (*s == '_') {            /* User used "_" prefix */
+       s++;                            /* Remove it */
+       /* Rest must be numeric */
+       debug(F110,"OS2 SET PORT HANDLE _subst s",s,0);
+       if (!rdigits(s)) {
+           makestr(&slmsg,"Invalid file handle");
+           printf("?Invalid format for file handle\n");
+           return(-9);
+       }
+#ifdef NT
+       xxtapi = 0;
+#else /* NT */
+       xxslip = xxppp = 0;
+#endif /* NT */
+    } else {                           /* A normal COMx port or a string */
+       s = brstrip(s);                 /* Strip braces if any */
+#ifdef NT
+#ifdef CK_TAPI
+       /* Windows TAPI support - Look up in keyword table */
+       if (tapilinetab && _tapilinetab && ntapiline > 0) {
+           if (!ckstrcmp(s,"tapi",4,0)) {
+
+               /* Find out what the lowest numbered TAPI device is */
+               /* and use it as the default.                       */
+               int j = 9999, k = -1;
+               for (i = 0; i < ntapiline; i++) {
+                   if (tapilinetab[i].kwval < j) {
+                       j = tapilinetab[i].kwval;
+                       k = i;
+                   }
+               }
+               if (k >= 0)
+                 s = _tapilinetab[k].kwd;
+               else
+                 s = "";
+
+               if ((y = cmkey(_tapilinetab,ntapiline,
+                              "TAPI device name",s,xxstring)) < 0)
+                 return(y);
+
+               xxtapi = 1;
+
+               /* Get the non Underscored string */
+               for (i = 0; i < ntapiline; i++ ) {
+                   if (tapilinetab[i].kwval == y) {
+                       s = tapilinetab[i].kwd;
+                       break;
+                   }
+               }
+           } else
+             xxtapi = 0;
+       }
+#endif /* CK_TAPI */
+#else /* NT */
+       /* not OS/2 SLIP or PPP */
+       xxslip = xxppp = 0;
+#endif /* NT */
+    }
+    ckstrncpy(tmpbuf,s,TMPBUFSIZ);     /* Copy to a safe place */
+    s = tmpbuf;
+    if ((x = cmcfm()) < 0)
+      return(x);
+
+#else /* !OS2 */
+
+    cmfdbi(&sw,_CMKEY,"Device name, or switch",
+          "","",npsltab,4,xxstring,psltab,&tx);
+    cmfdbi(&tx,_CMTXT,"",dftty,"",0,0,xxstring,NULL,NULL);
+    while (!confirmed) {
+       x = cmfdb(&sw);
+       debug(F101,"setlin cmfdb","",x);
+       if (x < 0)
+         if (x != -3)
+           return(x);
+       if (x == -3) {
+           if ((x = cmcfm()) < 0) {
+               return(x);
+           } else {
+               confirmed = 1;
+               break;
+           }
+       }
+       switch (cmresult.fcode) {
+         case _CMTXT:
+           ckstrncpy(tmpbuf,cmresult.sresult,TMPBUFSIZ);
+           s = tmpbuf;
+           debug(F110,"setlin CMTXT",tmpbuf,0);
+           confirmed = 1;
+           break;
+         case _CMKEY:                  /* Switch */
+           debug(F101,"setlin CMKEY",tmpbuf,cmresult.nresult);
+           switch (cmresult.nresult) {
+             case SL_CNX:              /* /CONNECT */
+               cx = 1;
+               sx = 0;
+               break;
+             case SL_SRV:              /* /SERVER */
+               cx = 0;
+               sx = 1;
+               break;
+#ifdef VMS
+             case SL_SHR:              /* /SHARE */
+               shr = 1;
+               break;
+             case SL_NSH:              /* /NOSHARE */
+               shr = 0;
+               break;
+#endif /* VMS */
+           }
+           continue;
+         default:
+           debug(F101,"setlin bad cmfdb result","",cmresult.fcode);
+           makestr(&slmsg,"Internal error");
+           printf("?Internal parsing error\n");
+           return(-9);
+       }
+    }
+#endif /* OS2 */
+    if (!confirmed)
+      if ((x = cmcfm()) < 0)
+       return(x);
+
+    debug(F110,"setlin pre-cx_serial s",s,0);
+    debug(F110,"setlin pre-cx_serial line",line,0);
+    x = cx_serial(s,cx,sx,shr,zz,0,
+#ifdef OS2
+#ifdef NT
+                   (xxtapi ? CX_TAPI : 0)
+#else
+                   (xxslip ? CX_SLIP : 0) | (xxppp ? CX_PPP : 0)
+#endif /* NT */
+#else /* OS2 */
+                   0
+#endif /* OS2 */
+                   );
+    debug(F111,"setlin cx_serial",line,x);
+    return(x);
+}
+#endif /* NOLOCAL */
+
+#ifdef CKCHANNELIO
+/*
+  C-Library based file-i/o package for scripts.  This should be portable to
+  all C-Kermit versions since it uses the same APIs we have always used for
+  processing command files.  The entire channel i/o package is contained
+  herein, apart from some keyword table entries in the main keyword table
+  and the help text in the HELP command module.
+
+  On platforms like VMS and VOS, this package handles only UNIX-style
+  stream files.  If desired, it can be replaced for those platforms by
+  <#>ifdef'ing out this code and adding the equivalent replacement routines
+  to the ck?fio.c module, e.g. for RMS-based file i/o in ckvfio.c.
+*/
+
+/* Define NOSTAT if the <#>include causes trouble. */
+
+#ifndef NOSTAT
+#ifdef VMS
+#ifdef VAXC                             /* As it does in VAX C */
+#define NOSTAT
+#endif /* VAXC */
+#endif /* VMS */
+#endif /* NOSTAT */
+
+#ifndef NOSTAT
+#include <sys/stat.h>
+#endif /* NOSTAT */
+
+#ifdef NLCHAR
+static int z_lt = 1;                    /* Length of line terminator */
+#else
+static int z_lt = 2;
+#endif /* NLCHAR */
+
+struct ckz_file {                       /* C-Kermit file struct */
+    FILE * z_fp;                        /* Includes the C-Lib file struct */
+    unsigned int z_flags;               /* Plus C-Kermit mode flags, */
+    long z_nline;                       /* current line number if known, */
+    char z_name[CKMAXPATH+2];           /* and the file's name. */
+};
+static struct ckz_file * z_file = NULL; /* Array of C-Kermit file structs */
+static int z_inited = 0;                /* Flag for array initialized */
+int z_maxchan = Z_MAXCHAN;              /* Max number of C-Kermit channels */
+int z_openmax = CKMAXOPEN;              /* Max number of open files overall */
+int z_nopen = 0;                        /* How many channels presently open */
+int z_error = 0;                        /* Most recent error */
+int z_filcount = -1;                    /* Most recent FILE COUNT result */
+
+#define RD_LINE 0                       /* FILE READ options */
+#define RD_CHAR 1
+#define RD_SIZE 2
+#define RD_TRIM 8                      /* Like Snobol &TRIM = 1 */
+#define RD_UNTA 9                      /* Untabify */
+
+#define WR_LINE RD_LINE                 /* FILE WRITE options */
+#define WR_CHAR RD_CHAR
+#define WR_SIZE RD_SIZE
+#define WR_STRI 3
+#define WR_LPAD 4
+#define WR_RPAD 5
+
+#ifdef UNIX
+extern int ckmaxfiles;                  /* Filled in by sysinit(). */
+#endif /* UNIX */
+
+/* See ckcker.h for error numbers */
+/* See ckcdeb.h for Z_MAXCHAN and CKMAXOPEN definitions */
+/* NOTE: For VMS we might be able to fill in ckmaxfiles */
+/* from FILLM and CHANNELCNT -- find out about these... */
+
+static char * fopnargs[] = {            /* Mode combinations for fopen() */
+#ifdef COMMENT
+    /* All combinations of rwa */
+    "",  "r",  "w",  "rw",  "a",  "ra",  "wa",  "rwa", /* Text mode */
+    "b", "rb", "wb", "rwb", "ab", "rab", "wab", "rwab" /* Binary mode */
+#else
+    /* Combinations and syntax permitted by C libraries... */
+    "",  "r",  "w",  "r+",  "a",  "",   "a",  "", /* Text mode */
+#ifdef OS2
+    "",  "rb", "wb", "r+b", "ab", "",   "ab", "" /* Binary modes for K95 */
+#else
+#ifdef VMS
+    "",  "rb", "wb", "r+b", "ab", "",   "ab", "" /* Binary modes for VMS */
+#else
+    "",  "r",   "w", "r+",  "a",  "",   "a",  "" /* Binary modes for UNIX */
+#endif /* VMS */
+#endif /* OS2 */
+#endif /* COMMENT */
+};
+static int nfopnargs = sizeof(fopnargs) / sizeof(char *);
+
+char *                                  /* Error messages */
+ckferror(n) int n; {
+    switch (n) {
+      case FX_NER: return("No error");
+      case FX_SYS: return(ck_errstr());
+      case FX_EOF: return("End of file");
+      case FX_NOP: return("File not open");
+      case FX_CHN: return("Channel out of range");
+      case FX_RNG: return("Parameter out of range");
+      case FX_NMF: return("Too many files open");
+      case FX_FOP: return("Operation conflicts with OPEN mode");
+      case FX_NYI: return("OPEN mode not supported");
+      case FX_BOM: return("Illegal combination of OPEN modes");
+      case FX_ACC: return("Access denied");
+      case FX_FNF: return("File not found");
+      case FX_OFL: return("Buffer overflow");
+      case FX_LNU: return("Current line number unknown");
+      case FX_ROO: return("Off limits");
+      case FX_UNK: return("Operation fails - reason unknown");
+      default: return("Error number out of range");
+    }
+}
+
+/*
+  Z _ O P E N --  Open a file for the requested type of access.
+
+  Call with:
+    name:  Name of file to be opened.
+    flags: Any combination of FM_xxx values except FM_EOF (ckcker.h).
+  Returns:
+    >= 0 on success: The assigned channel number
+    <  0 on failure: A negative FX_xxx error code (ckcker.h).
+*/
+int
+z_open(name, flags) char * name; int flags; {
+    int i, n;
+    FILE * t;
+    char * mode;
+    debug(F111,"z_open",name,flags);
+    if (!name) name = "";               /* Check name argument */
+    if (!name[0])
+      return(z_error = FX_BFN);
+    if (flags & FM_CMD)                 /* Opening pipes not implemented yet */
+      return(z_error = FX_NYI);         /* (and not portable either) */
+    debug(F101,"z_open nfopnargs","",nfopnargs);
+    if (flags < 0 || flags >= nfopnargs) /* Range check flags */
+      return(z_error = FX_RNG);
+    mode = fopnargs[flags];             /* Get fopen() arg */
+    debug(F111,"z_open fopen args",mode,flags);
+    if (!mode[0])                       /* Check for illegal combinations */
+      return(z_error = FX_BOM);
+    if (!z_inited) {                    /* If file structs not inited */
+        debug(F101,"z_open z_maxchan 1","",z_maxchan);
+#ifdef UNIX
+        debug(F101,"z_open ckmaxfiles","",ckmaxfiles);
+        if (ckmaxfiles > 0) {           /* Set in ck?tio.c: sysinit() */
+            int x;
+            x = ckmaxfiles - ZNFILS - 5;
+            if (x > z_maxchan)          /* sysconf() value greater than */
+              z_maxchan = x;            /* value from header files. */
+            debug(F101,"z_open z_maxchan 2","",z_maxchan);
+        }
+#endif /* UNIX */
+        if (z_maxchan < Z_MINCHAN)      /* Allocate at least this many. */
+          z_maxchan = Z_MINCHAN;
+        debug(F101,"z_open z_maxchan 3","",z_maxchan);
+        /* Note: This could be a pretty big chunk of memory */
+        /* if z_maxchan is a big number.  If this becomes a problem */
+        /* we'll need to malloc and free each element at open/close time */
+        if (!(z_file = (struct ckz_file *)
+              malloc(sizeof(struct ckz_file) * (z_maxchan + 1))))
+          return(z_error = FX_NMF);
+        for (i = 0; i < z_maxchan; i++) {
+            z_file[i].z_fp = NULL;
+            z_file[i].z_flags = 0;
+            z_file[i].z_nline = 0;
+            *(z_file[i].z_name) = '\0';
+        }
+        z_inited = 1;                   /* Remember we did */
+    }
+    for (n = -1, i = 0; i < z_maxchan; i++) {
+        if (!z_file[i].z_fp) {
+            n = i;
+            break;
+        }
+    }
+    if (n < 0 || n >= z_maxchan)        /* Any free channels? */
+      return(z_error = FX_NMF);         /* No, fail. */
+    errno = 0;
+
+    z_file[n].z_flags = 0;              /* In case of failure... */
+
+    t = fopen(name, mode);              /* Try to open the file. */
+    if (!t) {                           /* Failed... */
+        debug(F111,"z_open error",name,errno);
+#ifdef EMFILE
+        if (errno == EMFILE)
+          return(z_error = FX_NMF);
+#endif /* EMFILE */
+        return(z_error = (errno ?  FX_SYS : FX_UNK)); /* Return error code */
+    }
+#ifdef NT
+#ifdef O_SEQUENTIAL
+    if (t)                              /* Caching hint for NT */
+      _setmode(_fileno(t),O_SEQUENTIAL);
+#endif /* O_SEQUENTIAL */
+#endif /* NT */
+    z_nopen++;                          /* Open, count it. */
+    z_file[n].z_fp = t;                 /* Stash the file pointer */
+    z_file[n].z_flags = flags;          /* and the flags */
+    z_error = 0;
+    zfnqfp(name,CKMAXPATH,z_file[n].z_name); /* and the file's full name */
+    return(n);                          /* Return the channel number */
+}
+
+int
+z_close(channel) int channel; {         /* Close file on given channel */
+    int x;
+    FILE * t;
+    if (!z_inited)                      /* Called before any files are open? */
+      return(z_error = FX_NOP);
+    if (channel >= z_maxchan)           /* Channel out of range? */
+      return(z_error = FX_CHN);
+    if (!(t = z_file[channel].z_fp))    /* Channel wasn't open? */
+      return(z_error = FX_NOP);
+    errno = 0;                          /* Set errno 0 to get a good reading */
+    x = fclose(t);                      /* Try to close */
+    if (x == EOF)                       /* On failure */
+      return(z_error = FX_SYS);         /* indicate system error. */
+    z_nopen--;                          /* Closed OK, decrement open count */
+    z_file[channel].z_fp = NULL;        /* Set file pointer to NULL */
+    z_file[channel].z_nline = 0;        /* Current line number is 0 */
+    z_file[channel].z_flags = 0;        /* Set flags to 0 */
+    *(z_file[channel].z_name) = '\0';   /* Clear name */
+    return(z_error = 0);
+}
+
+/*
+  Z _ O U T  --  Output string to channel.
+
+  Call with:
+    channel:     Channel number to write to.
+    s:           String to write.
+    length > -1: How many characters of s to write.
+    length < 0:  Write entire NUL-terminated string.
+    flags == 0:  Supply line termination.
+    flags >  0:  Don't supply line termination.
+    flags <  0:  Write 'length' NUL characters.
+  Special case:
+    If flags > -1 and s is empty or NULL and length == 1, write 1 NUL.
+  Returns:
+    Number of characters written to channel on success, or
+    negative FX_xxx error code on failure.
+*/
+int
+z_out(channel,s,length,flags) int channel, flags, length; char * s; {
+    FILE * t;
+    int x, n;
+    char c = '\0';
+
+    if (!s) s = "";                     /* Guard against null pointer */
+#ifdef DEBUG
+    if (deblog) {
+        debug(F111,"z_out",s,channel);
+        debug(F101,"z_out length","",length);
+        debug(F101,"z_out flags","",flags);
+    }
+#endif /* DEBUG */
+    if (!z_inited)                      /* File i/o inited? */
+      return(z_error = FX_NOP);
+    if (channel >= z_maxchan)           /* Channel in range? */
+      return(z_error = FX_CHN);
+    if (!(t = z_file[channel].z_fp))    /* File open? */
+      return(z_error = FX_NOP);
+    if (!((z_file[channel].z_flags) & (FM_WRI|FM_APP))) /* In write mode? */
+      return(z_error = FX_FOP);
+    n = length;                         /* Length of string to write */
+    if (n < 0) {                        /* Negative means get it ourselves */
+        if (flags < 0)                  /* Except when told to write NULs in */
+          return(z_error = FX_RNG);     /* which case args are inconsistent */
+        n = strlen(s);                  /* Get length of string arg */
+    }
+    errno = 0;                          /* Reset errno */
+    debug(F101,"z_out n","",n);
+    if (flags < 0) {                    /* Writing NULs... */
+        int i;
+        for (i = 0; i < n; i++) {
+            x = fwrite(&c,1,1,t);
+            if (x < 1)
+              return(z_error = (errno ? FX_SYS : FX_UNK));
+        }
+        z_file[channel].z_nline = -1;   /* Current line no longer known */
+        z_error = 0;
+        return(i);
+    } else {                            /* Writing string arg */
+        if (n == 1 && !s[0])            /* Writing one char but it's NUL */
+          x = fwrite(&c,1,1,t);
+        else                            /* Writing non-NUL char or string */
+          x = fwrite(s,1,n,t);
+        debug(F101,"z_out fwrite",ckitoa(x),errno);
+        if (x < n)                      /* Failure to write requested amount */
+          return(z_error = (errno ? FX_SYS : FX_UNK)); /* Return error */
+        if (flags == 0) {               /* If supplying line termination */
+            if (fwrite("\n",1,1,t))     /* do that  */
+              x += z_lt;                /* count the terminator */
+            if (z_file[channel].z_nline > -1) /* count this line */
+              z_file[channel].z_nline++;
+        } else {
+            z_file[channel].z_nline = -1; /* Current line no longer known */
+        }
+    }
+    z_error = 0;
+    return(x);
+}
+
+#define Z_INBUFLEN 64
+
+/*
+  Z _ I N  --  Multichannel i/o file input function.
+
+  Call with:
+    channel number to read from.
+    s = address of destination buffer.
+    buflen = destination buffer length.
+    length = Number of bytes to read, must be < buflen.
+    flags: 0 = read a line; nonzero = read the given number of bytes.
+  Returns:
+    Number of bytes read into buffer or a negative error code.
+    A terminating NUL is deposited after the last byte that was read.
+*/
+int
+z_in(channel,s,buflen,length,flags)
+ int channel, buflen, length, flags; char * s;
+/* z_in */ {
+    int i, j, x;
+    FILE * t;
+    char * p;
+
+    if (!z_inited)                      /* Check everything... */
+      return(z_error = FX_NOP);
+    if (channel >= z_maxchan)
+      return(z_error = FX_CHN);
+    if (!(t = z_file[channel].z_fp))
+      return(z_error = FX_NOP);
+    if (!((z_file[channel].z_flags) & FM_REA))
+      return(z_error = FX_FOP);
+    if (!s)                             /* Check destination */
+     return(z_error = FX_RNG);
+    s[0] = NUL;
+    if (length == 0)                    /* Read 0 bytes - easy. */
+      return(z_error = 0);
+    debug(F101,"z_in channel","",channel);
+    debug(F101,"z_in buflen","",buflen);
+    debug(F101,"z_in length","",length);
+    debug(F101,"z_in flags","",flags);
+    if (length < 0 || buflen < 0)       /* Check length args */
+      return(z_error = FX_RNG);
+    if (buflen <= length)
+      return(z_error = FX_RNG);
+    errno = 0;                          /* Reset errno */
+    if (flags) {                        /* Read block or byte */
+        i = fread(s,1,length,t);
+#ifdef DEBUG
+        if (deblog) {
+            debug(F111,"z_in block",s,i);
+            debug(F101,"z_in block errno","",errno);
+            debug(F101,"z_in block ferror","",ferror(t));
+            debug(F101,"z_in block feof","",feof(t));
+        }
+#endif /* DEBUG */
+        z_file[channel].z_nline = -1;   /* Current line no longer known */
+    } else {                            /* Read line */
+#ifndef COMMENT
+        /* This method is used because it's simpler than the others */
+        /* and also marginally faster. */
+        debug(F101,"z_in getc loop","",ftell(t));
+        for (i = 0; i < length; i++) {
+            if ((x = getc(t)) == EOF) {
+                debug(F101,"z_in getc error","",ftell(t));
+                s[i] = '\0';
+                break;
+            }
+            s[i] = x;
+            if (s[i] == '\n') {
+                s[i] = '\0';
+                break;
+            }
+        }
+        debug(F111,"z_in line byte loop",ckitoa(errno),i);
+        debug(F111,"z_in line got",s,z_file[channel].z_nline);
+        if (z_file[channel].z_nline > -1)
+          z_file[channel].z_nline++;
+#else
+#ifdef COMMENT2
+        /* Straightforward but strlen() slows it down. */
+        s[0] = '\0';
+        i = 0;
+        if (fgets(s,length,t)) {
+            i = strlen(s);
+            if (i > 0 && s[i-1] == '\n') i--;
+        }
+        debug(F111,"z_in line fgets",ckitoa(errno),i);
+        if (z_file[channel].z_nline > -1)
+          z_file[channel].z_nline++;
+#else
+        /* This is a do-it-yourself fgets() with its own readahead and */
+        /* putback.  It's a bit faster than real fgets() but not enough */
+        /* to justify the added complexity or the risk of the ftell() and */
+        /* fseek() calls failing. */
+        int k, flag = 0;
+        long pos;
+        for (i = 0; !flag && i <= (length - Z_INBUFLEN); i += Z_INBUFLEN) {
+            k = ((length - i) < Z_INBUFLEN) ? length - i : Z_INBUFLEN;
+            if ((x = fread(s+i,1,k,t)) < 1)
+              break;
+            s[i+x] = '\0';
+            for (j = 0; j < x; j++) {
+                if (s[i+j] == '\n') {
+                    s[i+j] = '\0';
+                    flag ++;
+                    pos = ftell(t);
+                    if (pos > -1) {
+                        pos -= (x - j - 1);
+                        x = fseek(t, pos, 0);
+                        i += j;
+                        break;
+                    } else
+                      return(z_error = FX_SYS);
+                }
+            }
+        }
+        if (z_file[channel].z_nline > -1)
+          z_file[channel].z_nline++;
+        debug(F111,"z_in line chunk loop",ckitoa(errno),i);
+#endif /* COMMENT2 */
+#endif /* COMMENT */
+    }
+    debug(F111,"z_in i",ckitoa(errno),i);
+    if (i < 0) i = 0;                   /* NUL-terminate result */
+    s[i] = '\0';
+    if (i > 0) {
+        z_error = 0;
+        return(i);
+    }
+    if (i == 0 && feof(t))              /* EOF on reading? */
+      return(z_error = FX_EOF);         /* Return EOF code */
+    return(errno ? (z_error = -1) : i); /* Return length or system error */
+}
+
+int
+z_flush(channel) int channel; {         /* Flush output channel */
+    FILE * t;
+    int x;
+    if (!z_inited)                      /* Regular checks */
+      return(z_error = FX_NOP);
+    if (channel >= z_maxchan)
+      return(z_error = FX_CHN);
+    if (!(t = z_file[channel].z_fp))
+      return(z_error = FX_NOP);
+    if (!((z_file[channel].z_flags) & (FM_WRI|FM_APP))) /* Write access? */
+      return(z_error = FX_FOP);
+    errno = 0;                          /* Reset errno */
+    x = fflush(t);                      /* Try to flush */
+    return(x ? (z_error = FX_SYS) : 0); /* Return system error or 0 if OK */
+}
+
+int
+#ifdef CK_ANSIC
+z_seek(int channel, long pos)           /* Move file pointer to byte */
+#else
+z_seek(channel,pos) int channel; long pos; /* (seek to given position) */
+#endif /* CK_ANSIC */
+{
+    int i, x = 0, rc;
+    FILE * t;
+    if (!z_inited)                      /* Check... */
+      return(z_error = FX_NOP);
+    if (channel >= z_maxchan)
+      return(z_error = FX_CHN);
+    if (!(t = z_file[channel].z_fp))
+      return(z_error = FX_NOP);
+    if (pos < 0L) {
+        x = 2;
+        pos = (pos == -2) ? -1L : 0L;
+    }
+    errno = 0;
+    rc = fseek(t,pos,x);                /* Try to seek */
+    debug(F111,"z_seek",ckitoa(errno),rc);
+    if (rc < 0)                         /* OK? */
+      return(z_error = FX_SYS); /* No. */
+    z_file[channel].z_nline = ((pos || x) ? -1 : 0);
+    return(z_error = 0);
+}
+
+int
+#ifdef CK_ANSIC
+z_line(int channel, long pos)           /* Move file pointer to line */
+#else
+z_line(channel,pos) int channel; long pos; /* (seek to given position) */
+#endif /* CK_ANSIC */
+{
+    int i, len, x = 0;
+    long current = 0L, prev = -1L, old = -1L;
+    FILE * t;
+    char tmpbuf[256];
+    if (!z_inited)                      /* Check... */
+      return(z_error = FX_NOP);
+    if (channel >= z_maxchan)
+      return(z_error = FX_CHN);
+    if (!(t = z_file[channel].z_fp))
+      return(z_error = FX_NOP);
+    debug(F101,"z_line pos","",pos);
+    if (pos < 0L) {                     /* EOF wanted */
+        long n;
+        n = z_file[channel].z_nline;
+        debug(F101,"z_line n","",n);
+        if (n < 0 || pos < 0) {
+            rewind(t);
+            n = 0;
+        }
+        while (1) {                     /* This could take a while... */
+            if ((x = getc(t)) == EOF)
+              break;
+            if (x == '\n') {
+                n++;
+                if (pos == -2) {
+                    old = prev;
+                    prev = ftell(t);
+                }
+            }
+        }
+        debug(F101,"z_line old","",old);
+        debug(F101,"z_line prev","",prev);
+        if (pos == -2) {
+            if ((x = z_seek(channel,old)) < 0)
+              return(z_error = x);
+            else
+              n--;
+        }
+        z_file[channel].z_nline = n;
+        return(z_error = 0);
+    }
+    if (pos == 0L) {                    /* Rewind wanted */
+        z_file[channel].z_nline = 0L;
+        rewind(t);
+        debug(F100,"z_line rewind","",0);
+        return(0L);
+    }
+    tmpbuf[255] = NUL;                  /* Make sure buf is NUL terminated */
+    current = z_file[channel].z_nline;  /* Current line */
+    /*
+      If necessary the following could be optimized, e.g. for positioning
+      to a previous line in a large file without starting over.
+    */
+    if (current < 0 || pos < current) { /* Not known or behind us... */
+        debug(F101,"z_line rewinding","",pos);
+        if ((x = z_seek(channel, 0L)) < 0) /* Rewind */
+          return(z_error = x);
+        if (pos == 0)                   /* If 0th line wanted we're done */
+          return(z_error = 0);
+        current = 0;
+    }
+    while (current < pos) {             /* Search for specified line */
+        if (fgets(tmpbuf,255,t)) {
+            len = strlen(tmpbuf);
+            if (len > 0 && tmpbuf[len-1] == '\n') {
+                current++;
+                debug(F111,"z_line read",ckitoa(len),current);
+            } else if (len == 0) {
+                return(z_error = FX_UNK);
+            }
+        } else {
+            z_file[channel].z_nline = -1L;
+            debug(F101,"z_line premature EOF","",current);
+            return(z_error = FX_EOF);
+        }
+    }
+    z_file[channel].z_nline = current;
+    debug(F101,"z_line result","",current);
+    z_error = 0;
+    return(current);
+}
+
+char *
+z_getname(channel) int channel; {       /* Return name of file on channel */
+    FILE * t;
+    if (!z_inited) {
+        z_error = FX_NOP;
+        return(NULL);
+    }
+    if (channel >= z_maxchan) {
+        z_error = FX_CHN;
+        return(NULL);
+    }
+    if (!(t = z_file[channel].z_fp)) {
+        z_error = FX_NOP;
+        return(NULL);
+    }
+    return((char *)(z_file[channel].z_name));
+}
+
+int
+z_getmode(channel) int channel; {       /* Return OPEN modes of channel */
+    FILE * t;                           /* 0 if file not open */
+#ifndef NOSTAT
+#ifdef NT
+    struct _stat statbuf;
+#else /* NT */
+    struct stat statbuf;
+#endif /* NT */
+#endif /* NOSTAT */
+    int x;
+    if (!z_inited)
+      return(0);
+    if (channel >= z_maxchan)
+      return(z_error = FX_CHN);
+    if (!(t = z_file[channel].z_fp))
+      return(0);
+    x = z_file[channel].z_flags;
+    if (feof(t)) {                      /* This might not work for */
+        x |= FM_EOF;                    /* output files */
+#ifndef NOSTAT
+    /* But this does if we can use it. */
+    } else if (stat(z_file[channel].z_name,&statbuf) > -1) {
+        if (ftell(t) == statbuf.st_size)
+          x |= FM_EOF;
+#endif /* NOSTAT */
+    }
+    return(x);
+}
+
+long
+z_getpos(channel) int channel; {        /* Get file pointer position */
+    FILE * t;                           /* on this channel */
+    long x;
+    if (!z_inited)
+      return(z_error = FX_NOP);
+    if (channel >= z_maxchan)
+      return(z_error = FX_CHN);
+    if (!(t = z_file[channel].z_fp))
+      return(z_error = FX_NOP);
+    x = ftell(t);
+    return((x < 0L) ? (z_error = FX_SYS) : x);
+}
+
+long
+z_getline(channel) int channel; {       /* Get current line number */
+    FILE * t;                           /* in file on this channel */
+    long rc;
+    if (!z_inited)
+      return(z_error = FX_NOP);
+    if (channel >= z_maxchan)
+      return(z_error = FX_CHN);
+    if (!(t = z_file[channel].z_fp))
+      return(z_error = FX_NOP);
+    debug(F101,"z_getline","",z_file[channel].z_nline);
+    rc = z_file[channel].z_nline;
+    return((rc < 0) ? (z_error = FX_LNU) : rc);
+}
+
+int
+z_getfnum(channel) int channel; {       /* Get file number / handle */
+    FILE * t;                           /* for file on this channel */
+    if (!z_inited)
+      return(z_error = FX_NOP);
+    if (channel >= z_maxchan)
+      return(z_error = FX_CHN);
+    if (!(t = z_file[channel].z_fp))
+      return(z_error = FX_NOP);
+    z_error = 0;
+    return(fileno(t));
+}
+
+/*
+  Line-oriented counts and seeks are as dumb as they can be at the moment.
+  Later we can speed them up by building little indexes.
+*/
+long
+z_count(channel, what) int channel, what; { /* Count bytes or lines in file */
+    FILE * t;
+    int i, x;
+    long pos, count = 0L;
+    if (!z_inited)                      /* Check stuff... */
+      return(z_error = FX_NOP);
+    if (channel >= z_maxchan)
+      return(z_error = FX_CHN);
+    if (!(t = z_file[channel].z_fp))
+      return(z_error = FX_NOP);
+    pos = ftell(t);                     /* Save current file pointer */
+    errno = 0;
+    z_error = 0;
+    if (what == RD_CHAR) {              /* Size in bytes requested */
+        if (!fseek(t,0L,2)) {           /* Seek to end */
+            count = ftell(t);           /* Get file pointer */
+            fseek(t,pos,0);             /* Restore file file pointer */
+            return(count);
+        } else                          /* Fallback in case seek fails */
+          return(zgetfs(z_file[channel].z_name));
+    }
+    rewind(t);                          /* Line count requested - rewind. */
+    while (1) {                         /* Count lines. */
+        if ((x = getc(t)) == EOF)       /* Stupid byte loop */
+          break;                        /* but it works as well as anything */
+        if (x == '\n')                  /* else... */
+          count++;
+    }
+    x = fseek(t,pos,0);                 /* Restore file pointer */
+    return(count);
+}
+
+/* User interface for generalized channel-oriented file i/o */
+
+struct keytab fctab[] = {               /* FILE subcommands */
+    { "close",      FIL_CLS, 0 },
+    { "count",      FIL_COU, 0 },
+    { "flush",      FIL_FLU, 0 },
+    { "list",       FIL_LIS, 0 },
+    { "open",       FIL_OPN, 0 },
+    { "read",       FIL_REA, 0 },
+    { "rewind",     FIL_REW, 0 },
+    { "seek",       FIL_SEE, 0 },
+    { "status",     FIL_STA, 0 },
+    { "write",      FIL_WRI, 0 }
+};
+int nfctab = (sizeof (fctab) / sizeof (struct keytab));
+
+static struct keytab fcswtab[] = {      /* OPEN modes */
+    { "/append",    FM_APP,  0 },
+    { "/binary",    FM_BIN,  0 },
+#ifdef COMMENT
+    { "/command",   FM_CMD,  0 },       /* Not implemented */
+#endif /* COMMENT */
+    { "/read",      FM_REA,  0 },
+    { "/write",     FM_WRI,  0 }
+};
+static int nfcswtab = (sizeof (fcswtab) / sizeof (struct keytab));
+
+static struct keytab fclkwtab[] = {     /* CLOSE options */
+    { "all",        1,       0 }
+};
+
+static struct keytab fsekwtab[] = {     /* SEEK symbols */
+    { "eof",        1,       0 },
+    { "last",       2,       0 }
+};
+static int nfsekwtab = (sizeof (fsekwtab) / sizeof (struct keytab));
+
+#define SEE_LINE  RD_LINE               /* SEEK options */
+#define SEE_CHAR  RD_CHAR
+#define SEE_REL   3
+#define SEE_ABS   4
+
+static struct keytab fskswtab[] = {
+    { "/absolute",  SEE_ABS,  0 },
+    { "/byte",      SEE_CHAR, 0 },
+    { "/character", SEE_CHAR, CM_INV },
+    { "/line",      SEE_LINE, 0 },
+    { "/relative",  SEE_REL,  0 }
+};
+static int nfskswtab = (sizeof (fskswtab) / sizeof (struct keytab));
+
+#define COU_LINE  RD_LINE               /* COUNT options */
+#define COU_CHAR  RD_CHAR
+#define COU_LIS   3
+#define COU_NOL   4
+
+static struct keytab fcoswtab[] = {
+    { "/bytes",     COU_CHAR, 0      },
+    { "/characters",COU_CHAR, CM_INV },
+    { "/lines",     COU_LINE, 0      },
+    { "/list",      COU_LIS,  0      },
+    { "/nolist",    COU_NOL,  0      },
+    { "/quiet",     COU_NOL,  CM_INV }
+};
+static int nfcoswtab = (sizeof (fcoswtab) / sizeof (struct keytab));
+
+static struct keytab frdtab[] = {       /* READ types */
+    { "/block",     RD_SIZE, CM_INV|CM_ARG },
+    { "/byte",      RD_CHAR, CM_INV },
+    { "/character", RD_CHAR, 0      },
+    { "/line",      RD_LINE, 0      },
+    { "/size",      RD_SIZE, CM_ARG },
+    { "/trim",      RD_TRIM, 0      },
+    { "/untabify",  RD_UNTA, 0      }
+};
+static int nfrdtab = (sizeof (frdtab) / sizeof (struct keytab));
+
+static struct keytab fwrtab[] = {       /* WRITE types */
+    { "/block",     WR_SIZE, CM_INV|CM_ARG },
+    { "/byte",      WR_CHAR, CM_INV },
+    { "/character", WR_CHAR, 0      },
+    { "/line",      WR_LINE, 0      },
+    { "/lpad",      WR_LPAD, CM_ARG },
+    { "/rpad",      WR_RPAD, CM_ARG },
+    { "/size",      WR_SIZE, CM_ARG },
+    { "/string",    WR_STRI, 0      }
+};
+static int nfwrtab = (sizeof (fwrtab) / sizeof (struct keytab));
+
+static char blanks[] = "\040\040\040\040"; /* Some blanks for formatting */
+
+int
+dofile(op) int op; {                    /* Do the FILE command */
+    char vnambuf[VNAML];                /* Buffer for variable names */
+    char *vnp = NULL;                   /* Pointer to same */
+    char zfilnam[CKMAXPATH+2];
+    char * p;
+    struct FDB fl, sw, nu;
+    long z;
+    int rsize, filmode = 0, relative = -1, eofflg = 0;
+    int rc, x, y, cx, n, getval, dummy, confirmed, listing = -1;
+    int charflag = 0, sizeflag = 0;
+    int pad = 32, wr_lpad = 0, wr_rpad = 0, rd_trim = 0, rd_untab = 0;
+
+    if (op == XXFILE) {                 /* FILE command was given */
+        /* Get subcommand */
+        if ((cx = cmkey(fctab,nfctab,"Operation","",xxstring)) < 0) {
+            if (cx == -3) {
+                printf("?File operation required\n");
+                x = -9;
+            }
+            return(cx);
+        }
+    } else {                            /* Shorthand command was given */
+        switch (op) {
+          case XXF_CL: cx = FIL_CLS; break; /* FCLOSE */
+          case XXF_FL: cx = FIL_FLU; break; /* FFLUSH */
+          case XXF_LI: cx = FIL_LIS; break; /* FLIST */
+          case XXF_OP: cx = FIL_OPN; break; /* etc... */
+          case XXF_RE: cx = FIL_REA; break;
+          case XXF_RW: cx = FIL_REW; break;
+          case XXF_SE: cx = FIL_SEE; break;
+          case XXF_ST: cx = FIL_STA; break;
+          case XXF_WR: cx = FIL_WRI; break;
+          case XXF_CO: cx = FIL_COU; break;
+          default: return(-2);
+        }
+    }
+    switch (cx) {                       /* Do requested subcommand */
+      case FIL_OPN:                     /* OPEN */
+        cmfdbi(&sw,                     /* Switches */
+               _CMKEY,                  /* fcode */
+               "Variable or switch",    /* hlpmsg */
+               "",                      /* default */
+               "",                      /* addtl string data */
+               nfcswtab,                /* addtl numeric data 1: tbl size */
+               4,                       /* addtl numeric data 2: 4 = cmswi */
+               xxstring,                /* Processing function */
+               fcswtab,                 /* Keyword table */
+               &fl                      /* Pointer to next FDB */
+               );
+        cmfdbi(&fl,                     /* Anything that doesn't match */
+               _CMFLD,                  /* fcode */
+               "Variable",              /* hlpmsg */
+               "",
+               "",
+               0,
+               0,
+               NULL,
+               NULL,
+               NULL
+               );
+        while (1) {
+            x = cmfdb(&sw);             /* Parse something */
+            if (x < 0) {
+                if (x == -3) {
+                    printf("?Variable name and file name required\n");
+                    x = -9;
+                }
+                return(x);
+            }
+            if (cmresult.fcode == _CMFLD)
+              break;
+            else if (cmresult.fcode == _CMKEY) {
+                char c;
+                c = cmgbrk();
+                if ((getval =
+                     (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
+                    printf("?This switch does not take an argument\n");
+                    return(-9);
+                }
+#ifdef COMMENT
+                /* Uncomment if we add any switches ere that take args */
+                if (!getval && (cmgkwflgs() & CM_ARG)) {
+                    printf("?This switch requires an argument\n");
+                    return(-9);         /* (none do...) */
+                }
+#endif /* COMMENT */
+                filmode |= cmresult.nresult; /* OR in the file mode */
+            } else
+              return(-2);
+        }
+        /* Not a switch - get the string */
+        ckstrncpy(vnambuf,cmresult.sresult,VNAML);
+        if (!vnambuf[0] || chknum(vnambuf)) { /* (if there is one...) */
+            printf("?Variable name required\n");
+            return(-9);
+        }
+        vnp = vnambuf;                  /* Check variable-name syntax */
+        if (vnambuf[0] == CMDQ &&
+            (vnambuf[1] == '%' || vnambuf[1] == '&'))
+          vnp++;
+        y = 0;
+        if (*vnp == '%' || *vnp == '&') {
+            if ((y = parsevar(vnp,&x,&dummy)) < 0) {
+                printf("?Syntax error in variable name\n");
+                return(-9);
+            }
+        }
+        if (!(filmode & FM_RWA))        /* If no access mode specified */
+          filmode |= FM_REA;            /* default to /READ. */
+
+        y = 0;                          /* Now parse the filename */
+        if ((filmode & FM_RWA) == FM_WRI)
+          x = cmofi("Name of new file","",&s,xxstring);
+        else if ((filmode & FM_RWA) == FM_REA)
+          x = cmifi("Name of existing file","",&s,&y,xxstring);
+        else {
+            x = cmiofi("Filename","",&s,&y,xxstring);
+            debug(F101,"fopen /append x","",x);
+        }
+        if (x == -9) {
+            if (zchko(s) < 0) {
+                printf("Can't create \"%s\"\n",s);
+                return(x);
+            }
+        } else if (x < 0) {
+            if (x == -3) {
+                printf("?Filename required\n");
+                x = -9;
+            }
+            return(x);
+        }
+        if (y) {                        /* No wildcards */
+            printf("?Wildcards not allowed here\n");
+            return(-9);
+        }
+        if (filmode & (FM_APP|FM_WRI)) { /* Check output access */
+#ifndef VMS
+            if (zchko(s) < 0) {          /* and set error code if denied */
+                z_error = FX_ACC;
+                printf("?Write access denied - \"%s\"\n",s);
+                return(-9);
+            }
+#endif /* VMS */
+        }
+        ckstrncpy(zfilnam,s,CKMAXPATH); /* Is OK - make safe copy */
+        if ((x = cmcfm()) < 0)          /* Get confirmation of command */
+          return(x);
+        if ((n = z_open(zfilnam,filmode)) < 0) {
+            printf("?OPEN failed - %s: %s\n",zfilnam,ckferror(n));
+            return(-9);
+        }
+        addmac(vnambuf,ckitoa(n));      /* Assign channel number to variable */
+        return(success = 1);
+
+      case FIL_REW:                     /* REWIND */
+        if ((x = cmnum("Channel number","",10,&n, xxstring)) < 0) {
+            if (x == -3) {
+                printf("?Channel number required\n");
+                x = -9;
+            }
+            return(x);
+        }
+        if ((x = cmcfm()) < 0)
+          return(x);
+        if ((rc = z_seek(n,0L)) < 0) {
+            printf("?REWIND failed - Channel %d: %s\n",n,ckferror(rc));
+            return(-9);
+        }
+        return(success = 1);
+
+      case FIL_CLS:                     /* CLOSE */
+          cmfdbi(&sw,                   /* Second FDB - switches */
+                 _CMKEY,                /* fcode */
+                 "Channel number; or keyword",
+                 "",
+                 "",                    /* addtl string data */
+                 1,                     /* addtl numeric data 1: tbl size */
+                 0,                     /* addtl numeric data 2: 4 = cmswi */
+                 xxstring,              /* Processing function */
+                 fclkwtab,              /* Keyword table */
+                 &nu                    /* Pointer to next FDB */
+                 );
+          cmfdbi(&nu,                   /* First FDB - command switches */
+                 _CMNUM,                /* fcode */
+                 "",
+                 "",                    /* default */
+                 "",                    /* addtl string data */
+                 10,                    /* addtl numeric data 1: radix */
+                 0,                     /* addtl numeric data 2: 0 */
+                 xxstring,              /* Processing function */
+                 NULL,                  /* Keyword table */
+                 NULL                   /* Pointer to next FDB */
+                 );                     /*  */
+        x = cmfdb(&sw);                 /* Parse something */
+        if (x < 0) {
+            if (x == -3) {
+                printf("?Channel number or ALL required\n");
+                x = -9;
+            }
+            return(x);
+        }
+        if (cmresult.fcode == _CMNUM)
+          n = cmresult.nresult;
+        else if (cmresult.fcode == _CMKEY)
+          n = -1;
+        if ((x = cmcfm()) < 0)
+          return(x);
+        rc = 1;
+        if (n < 0) {
+            int count = 0;
+            int i;
+            for (i = 0; i < z_maxchan; i++) {
+                x = z_close(i);
+                if (x == FX_SYS) {
+                    printf("?CLOSE failed - Channel %d: %s\n",n,ckferror(x));
+                    rc = 0;
+                } else if (x > -1)
+                  count++;
+            }
+            debug(F101,"FILE CLOSE ALL","",count);
+        } else if ((x = z_close(n)) < 0) {
+            printf("?CLOSE failed - Channel %d: %s\n",n,ckferror(x));
+            return(-9);
+        }
+        return(success = rc);
+
+      case FIL_REA:                     /* READ */
+      case FIL_WRI:                     /* WRITE */
+        rsize = 0;
+        cmfdbi(&sw,                     /* Switches */
+               _CMKEY,                  /* fcode */
+               "Channel or switch",     /* hlpmsg */
+               "",                      /* default */
+               "",                      /* addtl string data */
+               (cx == FIL_REA) ? nfrdtab : nfwrtab,
+               4,                       /* addtl numeric data 2: 4 = cmswi */
+               xxstring,                /* Processing function */
+               (cx == FIL_REA) ? frdtab : fwrtab, /* Keyword table */
+               &nu                      /* Pointer to next FDB */
+               );
+        cmfdbi(&nu,                     /* Channel number */
+               _CMNUM,                  /* fcode */
+               "Channel",
+               "",                      /* default */
+               "",                      /* addtl string data */
+               10,                      /* addtl numeric data 1: radix */
+               0,                       /* addtl numeric data 2: 0 */
+               xxstring,                /* Processing function */
+               NULL,                    /* Keyword table */
+               NULL                     /* Pointer to next FDB */
+               );
+        do {
+            x = cmfdb(&sw);             /* Parse something */
+            if (x < 0) {
+                if (x == -3) {
+                    printf("?Channel number required\n");
+                    x = -9;
+                }
+                return(x);
+            }
+            if (cmresult.fcode == _CMNUM) /* Channel number */
+              break;
+            else if (cmresult.fcode == _CMKEY) { /* Switch */
+                char c;
+                c = cmgbrk();
+                if ((getval =
+                     (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
+                    printf("?This switch does not take an argument\n");
+                    return(-9);
+                }
+                if (!getval && (cmgkwflgs() & CM_ARG)) {
+                    printf("?This switch requires an argument\n");
+                    return(-9);
+                }
+                switch (cmresult.nresult) {
+                  case WR_LINE:
+                    charflag = 0;
+                    sizeflag = 0;
+                    rsize = 0;
+                    break;
+                  case WR_CHAR:
+                    rsize = 1;
+                    charflag = 1;
+                    sizeflag = 1;
+                    break;
+                  case WR_SIZE:
+                    if ((x = cmnum("Bytes","",10,&rsize, xxstring)) < 0) {
+                        if (x == -3) {
+                            printf("?Number required\n");
+                            x = -9;
+                        }
+                        return(x);
+                    }
+                    charflag = 0;
+                    sizeflag = 1;
+                    break;
+                  case WR_STRI:
+                    rsize = 1;
+                    charflag = 0;
+                    sizeflag = 0;
+                    break;
+                  case WR_LPAD:
+                  case WR_RPAD:
+                    if ((x = cmnum("Numeric ASCII character value",
+                                   "32",10,&pad, xxstring)) < 0)
+                      return(x);
+                    if (cmresult.nresult == WR_LPAD)
+                      wr_lpad = 1;
+                    else
+                      wr_rpad = 1;
+                    break;
+                 case RD_TRIM:
+                   rd_trim = 1;
+                   break;
+                 case RD_UNTA:
+                   rd_untab = 1;
+                   break;
+                }
+                debug(F101,"FILE READ rsize 2","",rsize);
+            } else
+              return(-2);
+        } while
+          (cmresult.fcode == _CMKEY);
+
+        n = cmresult.nresult;           /* Channel */
+        debug(F101,"FILE READ/WRITE channel","",n);
+
+        if (cx == FIL_WRI) {            /* WRITE */
+            int len = 0;
+            if ((x = cmtxt("Text","",&s,xxstring)) < 0)
+              return(x);
+            ckstrncpy(line,s,LINBUFSIZ); /* Make a safe copy */
+            s = line;
+            s = brstrip(s);             /* Strip braces */
+            if (charflag) {             /* Write one char */
+                len = 1;                /* So length = 1 */
+                rsize = 1;              /* Don't supply terminator */
+            } else if (!sizeflag) {     /* Write a string */
+                len = -1;               /* So length is unspecified */
+            } else {                    /* Write a block of given size */
+                int i, k, xx;
+                if (rsize > TMPBUFSIZ) {
+                    z_error = FX_OFL;
+                    printf("?Buffer overflow\n");
+                    return(-9);
+                }
+                len = rsize;            /* rsize is really length */
+                rsize = 1;              /* Don't supply a terminator */
+                xx = strlen(s);         /* Size of given string */
+                if (xx >= len) {        /* Bigger or equal */
+                    s[len] = NUL;
+                } else if (wr_lpad) {   /* Smaller, left-padding requested */
+                    for (i = 0; i < len - xx; i++) /* Must make a copy */
+                      tmpbuf[i] = pad;
+                    ckstrncpy(tmpbuf+i,s,TMPBUFSIZ-i);
+                    tmpbuf[len] = NUL;
+                    s = tmpbuf;         /* Redirect write source */
+                } else if (wr_rpad) {   /* Smaller with right-padding */
+                    for (i = xx; i < len; i++)
+                      s[i] = pad;
+                    s[len] = NUL;
+                }
+            }
+            if ((rc = z_out(n,s,len,rsize)) < 0) { /* Try to write */
+                printf("?Channel %d WRITE error: %s\n",n,ckferror(rc));
+                return(-9);
+            }
+        } else {                        /* FIL_REA READ */
+            confirmed = 0;
+            vnambuf[0] = NUL;
+            x = cmfld("Variable name","",&s,NULL);
+            debug(F111,"FILE READ cmfld",s,x);
+            if (x < 0) {
+                if (x == -3 || !*s) {
+                    if ((x = cmcfm()) < 0)
+                      return(x);
+                    else
+                      confirmed++;
+                } else
+                  return(x);
+            }
+            ckstrncpy(vnambuf,s,VNAML);
+            debug(F111,"FILE READ vnambuf",vnambuf,confirmed);
+            if (vnambuf[0]) {           /* Variable name given, check it */
+                if (!confirmed) {
+                    x = cmcfm();
+                    if (x < 0)
+                      return(x);
+                    else
+                      confirmed++;
+                }
+                vnp = vnambuf;
+                if (vnambuf[0] == CMDQ &&
+                    (vnambuf[1] == '%' || vnambuf[1] == '&'))
+                  vnp++;
+                y = 0;
+                if (*vnp == '%' || *vnp == '&') {
+                    if ((y = parsevar(vnp,&x,&dummy)) < 0) {
+                        printf("?Syntax error in variable name\n");
+                        return(-9);
+                    }
+                }
+            }
+            debug(F111,"FILE READ variable",vnambuf,confirmed);
+
+            if (!confirmed)
+              if ((x = cmcfm()) < 0)
+                return(x);
+
+            line[0] = NUL;              /* Clear destination buffer */
+            if (rsize >= LINBUFSIZ)     /* Don't overrun it */
+              rsize = LINBUFSIZ - 1;
+
+            if (rsize == 0) {          /* Read a line */
+               rc = z_in(n,line,LINBUFSIZ,LINBUFSIZ-1,0);
+            } else {
+               rc = z_in(n,line,LINBUFSIZ,rsize,1); /* Read a block */
+           }
+            if (rc < 0) {               /* Error... */
+                debug(F101,"FILE READ error","",rc);
+                debug(F101,"FILE READ errno","",errno);
+                if (rc == FX_EOF) {     /* EOF - fail but no error message */
+                    return(success = 0);
+                } else {                /* Other error - fail and print msg */
+                    printf("?READ error: %s\n",ckferror(rc));
+                    return(-9);
+                }
+            }
+           if (rsize == 0) {           /* FREAD /LINE postprocessing */
+               if (rd_trim) {          /* Trim */
+                   int i, k;
+                   k = strlen(line);
+                   if (k > 0) {
+                       for (i = k-1; i > 0; i--) {
+                           if (line[i] == SP || line[i] == '\t')
+                             line[i] = NUL;
+                           else
+                             break;
+                       }
+                   }
+               }
+               if (rd_untab) {         /* Untabify */
+                   if (untabify(line,tmpbuf,TMPBUFSIZ) > -1)
+                     ckstrncpy(line,tmpbuf,LINBUFSIZ);
+               }
+           }
+            debug(F110,"FILE READ data",line,0);
+            if (vnambuf[0])             /* Read OK - If variable name given */
+              addmac(vnambuf,line);     /* Assign result to variable */
+            else                        /* otherwise */
+              printf("%s\n",line);      /* just print it */
+        }
+        return(success = 1);
+
+      case FIL_SEE:                     /* SEEK */
+      case FIL_COU:                     /* COUNT */
+        rsize = RD_CHAR;                /* Defaults to /BYTE */
+        cmfdbi(&sw,                     /* Switches */
+               _CMKEY,                  /* fcode */
+               "Channel or switch",     /* hlpmsg */
+               "",                      /* default */
+               "",                      /* addtl string data */
+               ((cx == FIL_SEE) ? nfskswtab : nfcoswtab),
+               4,                       /* addtl numeric data 2: 4 = cmswi */
+               xxstring,                /* Processing function */
+               ((cx == FIL_SEE) ? fskswtab : fcoswtab),
+               &nu                      /* Pointer to next FDB */
+               );
+        cmfdbi(&nu,                     /* Channel number */
+               _CMNUM,                  /* fcode */
+               "Channel",
+               "",                      /* default */
+               "",                      /* addtl string data */
+               10,                      /* addtl numeric data 1: radix */
+               0,                       /* addtl numeric data 2: 0 */
+               xxstring,                /* Processing function */
+               NULL,                    /* Keyword table */
+               NULL                     /* Pointer to next FDB */
+               );
+        do {
+            x = cmfdb(&sw);             /* Parse something */
+            if (x < 0) {
+                if (x == -3) {
+                    printf("?Channel number required\n");
+                    x = -9;
+                }
+                return(x);
+            }
+            if (cmresult.fcode == _CMNUM) /* Channel number */
+              break;
+            else if (cmresult.fcode == _CMKEY) { /* Switch */
+                char c;
+                c = cmgbrk();
+                if ((getval =
+                     (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
+                    printf("?This switch does not take an argument\n");
+                    return(-9);
+                }
+                if (cx == FIL_SEE) {
+                    switch (cmresult.nresult) {
+                      case SEE_REL: relative = 1; break;
+                      case SEE_ABS: relative = 0; break;
+                      default: rsize = cmresult.nresult;
+                    }
+                } else if (cx == FIL_COU) {
+                    switch (cmresult.nresult) {
+                      case COU_LIS: listing = 1; break;
+                      case COU_NOL: listing = 0; break;
+                      default: rsize = cmresult.nresult;
+                    }
+                }
+            }
+        } while
+          (cmresult.fcode == _CMKEY);
+
+        n = cmresult.nresult;           /* Channel */
+        debug(F101,"FILE SEEK/COUNT channel","",n);
+        if (cx == FIL_COU) {
+            if ((x = cmcfm()) < 0)
+              return(x);
+            z_filcount = z_count(n,rsize);
+            if (z_filcount < 0) {
+                rc = z_filcount;
+                printf("?COUNT error: %s\n",ckferror(rc));
+                return(-9);
+            }
+            if (listing < 0)
+              listing = !xcmdsrc;
+            if (listing)
+              printf(" %ld %s%s\n",
+                     z_filcount,
+                     ((rsize == RD_CHAR) ? "byte" : "line"),
+                     ((z_filcount == 1L) ? "" : "s")
+                     );
+            return(success = (z_filcount > -1) ? 1 : 0);
+        }
+        cmfdbi(&sw,                     /* SEEK symbolic targets (EOF) */
+               _CMKEY,                  /* fcode */
+               "Channel number;\n or keyword",
+               "",
+               "",                      /* addtl string data */
+               nfsekwtab,               /* addtl numeric data 1: table size */
+               0,                       /* addtl numeric data 2: 4 = cmswi */
+               xxstring,                /* Processing function */
+               fsekwtab,                /* Keyword table */
+               &nu                      /* Pointer to next FDB */
+               );
+        cmfdbi(&nu,                     /* Channel number */
+               _CMNUM,                  /* fcode */
+               "",
+               "",                      /* default */
+               "",                      /* addtl string data */
+               10,                      /* addtl numeric data 1: radix */
+               0,                       /* addtl numeric data 2: 0 */
+               xxstring,                /* Processing function */
+               NULL,                    /* Keyword table */
+               NULL                     /* Pointer to next FDB */
+               );
+        x = cmfdb(&sw);                 /* Parse something */
+        if (x < 0) {
+            if (x == -3) {
+                printf("?Channel number or EOF required\n");
+                x = -9;
+            }
+            return(x);
+        }
+        if (cmresult.fcode == _CMNUM) {
+            y = cmresult.nresult;
+            debug(F110,"FILE SEEK atmbuf",atmbuf,0);
+            if (relative < 0) {
+                if (cx == FIL_SEE && (atmbuf[0] == '+' || atmbuf[0] == '-'))
+                  relative = 1;
+                else
+                  relative = 0;
+            }
+        } else if (cmresult.fcode == _CMKEY) {
+            eofflg = cmresult.nresult;
+            relative = 0;
+            y = 0 - eofflg;
+        }
+        if ((x = cmcfm()) < 0)
+          return(x);
+        z = y;                          /* Convert to long */
+        y = 1;                          /* Recycle this */
+        z_flush(n);
+        debug(F101,"FILE SEEK relative","",relative);
+        debug(F101,"FILE SEEK rsize","",rsize);
+
+        if (rsize == RD_CHAR) {         /* Seek to byte position */
+            if (relative > 0) {
+                long pos;
+                pos = z_getpos(n);
+                if (pos < 0L) {
+                    rc = pos;
+                    printf("?Relative SEEK failed: %s\n",ckferror(rc));
+                    return(-9);
+                }
+                z += pos;
+            } else {
+                if (z < 0 && !eofflg) { /* Negative arg but not relative */
+                    y = 0;              /* Remember this was bad */
+                    z = 0;              /* but substitute 0 */
+                }
+            }
+            debug(F101,"FILE SEEK /CHAR z","",z);
+            if (z < 0 && !eofflg) {
+                z_error = FX_RNG;
+                return(success = 0);
+            }
+            if ((rc = z_seek(n,z)) < 0) {
+                if (rc == FX_EOF) return(success = 0);
+                printf("?SEEK /BYTE failed - Channel %d: %s\n",n,ckferror(rc));
+                return(-9);
+            }
+        } else {                        /* Seek to line */
+            if (relative > 0) {
+                long pos;
+                pos = z_getline(n);
+                debug(F101,"FILE SEEK /LINE pos","",pos);
+                if (pos < 0L) {
+                    rc = pos;
+                    printf("?Relative SEEK failed: %s\n",ckferror(rc));
+                    return(-9);
+                }
+                z += pos;
+            }
+            debug(F101,"FILE SEEK /LINE z","",z);
+            debug(F101,"FILE SEEK /LINE eofflg","",eofflg);
+            if (z < 0 && !eofflg) {
+                z_error = FX_RNG;
+                return(success = 0);
+            }
+            if ((rc = z_line(n,z)) < 0) {
+                if (rc == FX_EOF) return(success = 0);
+                printf("?SEEK /LINE failed - Channel %d: %s\n",n,ckferror(rc));
+                return(-9);
+            }
+        }
+        return(success = y);
+
+      case FIL_LIS: {                   /* LIST open files */
+#ifdef CK_TTGWSIZ
+          extern int cmd_rows, cmd_cols;
+#endif /* CK_TTGWSIZ */
+          extern int xaskmore;
+          int i, x, n = 0, paging = 0;
+          char * s;
+
+          if ((x = cmcfm()) < 0)
+            return(x);
+
+#ifdef CK_TTGWSIZ
+          if (cmd_rows > 0 && cmd_cols > 0)
+#endif /* CK_TTGWSIZ */
+            paging = xaskmore;
+
+          printf("System open file limit: %4d\n", z_openmax);
+          printf("Maximum for FILE OPEN:  %4d\n", z_maxchan);
+          printf("Files currently open:   %4d\n\n", z_nopen);
+          n = 4;
+          for (i = 0; i < z_maxchan; i++) {
+              s = z_getname(i);         /* Got one? */
+              if (s) {                  /* Yes */
+                  char m[8];
+                  m[0] = NUL;
+                  printf("%2d. %s",i,s); /* Print name */
+                  n++;                   /* Count it */
+                  x = z_getmode(i);      /* Get modes & print them */
+                  if (x > -1) {
+                      if (x & FM_REA) ckstrncat(m,"R",8);
+                      if (x & FM_WRI) ckstrncat(m,"W",8);
+                      if (x & FM_APP) ckstrncat(m,"A",8);
+                      if (x & FM_BIN) ckstrncat(m,"B",8);
+                      if (m[0])
+                        printf(" (%s)",m);
+                      if (x & FM_EOF)
+                        printf(" [EOF]");
+                      else
+                        printf(" %ld",z_getpos(i)); /* And file position too */
+                  }
+                  printf("\n");
+#ifdef CK_TTGWSIZ
+                  if (paging > 0) {     /* Pause at end of screen */
+                      if (n > cmd_rows - 3) {
+                          if (!askmore())
+                            break;
+                          else
+                            n = 0;
+                      }
+                  }
+#endif /* CK_TTGWSIZ */
+              }
+          }
+          return(success = 1);
+      }
+
+      case FIL_FLU:                     /* FLUSH */
+        if ((x = cmnum("Channel number","",10,&n, xxstring)) < 0) {
+            if (x == -3) {
+                printf("?Channel number required\n");
+                x = -9;
+            }
+            return(x);
+        }
+        if ((x = cmcfm()) < 0)
+          return(x);
+        if ((rc = z_flush(n)) < 0) {
+            printf("?FLUSH failed - Channel %d: %s\n",n,ckferror(rc));
+            return(-9);
+        }
+        return(success = 1);
+
+      case FIL_STA:                     /* STATUS */
+        if ((x = cmnum("Channel number","",10,&n, xxstring)) < 0) {
+            if (x == -3) {
+                printf("?Channel number required\n");
+                x = -9;
+            }
+            return(x);
+        }
+        if ((x = cmcfm()) < 0)
+          return(x);
+        p = blanks + 3;                 /* Tricky formatting... */
+        if (n < 1000) p--;
+        if (n < 100) p--;
+        if (n < 10) p--;
+        if ((rc = z_getmode(n)) < 0) {
+            printf("Channel %d:%s%s\n",n,p,ckferror(rc));
+            return(success = 0);
+        } else if (!rc) {
+            printf("Channel %d:%sNot open\n",n,p);
+            return(success = 0);
+        } else {
+            long xx;
+            s = z_getname(n);
+            if (!s) s = "(name unknown)";
+            printf("Channel %d:%sOpen\n",n,p);
+            printf(" File:        %s\n Modes:      ",s);
+            if (rc & FM_REA) printf(" /READ");
+            if (rc & FM_WRI) printf(" /WRITE");
+            if (rc & FM_APP) printf(" /APPEND");
+            if (rc & FM_BIN) printf(" /BINARY");
+            if (rc & FM_CMD) printf(" /COMMAND");
+            if (rc & FM_EOF) printf(" [EOF]");
+            printf("\n Size:        %ld\n",z_count(n,RD_CHAR));
+            printf(" At byte:     %ld\n",z_getpos(n));
+            xx = z_getline(n);
+            if (xx > -1)
+              printf(" At line:     %ld\n",xx);
+            return(success = 1);
+        }
+      default:
+        return(-2);
+    }
+}
+#endif /* CKCHANNELIO */
+
+#ifndef NOSETKEY
+/* Save Key maps and in OS/2 Mouse maps */
+int
+savkeys(name,disp) char * name; int disp; {
+    char *tp;
+    static struct filinfo xx;
+    int savfil, i, j, k;
+    char buf[1024];
+
+    zclose(ZMFILE);
+
+    if (disp) {
+        xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
+        xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
+        xx.lblopts = 0;
+        savfil = zopeno(ZMFILE,name,NULL,&xx);
+    } else savfil = zopeno(ZMFILE,name,NULL,NULL);
+
+    if (savfil) {
+#ifdef OS2
+        ztime(&tp);
+        zsout(ZMFILE, "; Kermit 95 SAVE KEYMAP file: ");
+        zsoutl(ZMFILE,tp);
+        if (mskkeys) {
+            zsoutl(ZMFILE,
+         "if eq \"\\v(program)\" \"C-Kermit\" set mskermit keycodes on");
+        } else {
+            zsoutl(ZMFILE,
+         "if NOT eq \"\\v(program)\" \"C-Kermit\" stop 1 C-Kermit required.");
+            zsoutl(ZMFILE,"set mskermit keycodes off");
+        }
+        zsoutl(ZMFILE,"");
+#else /* OS2 */
+        ztime(&tp);
+        zsout(ZMFILE, "; C-Kermit SAVE KEYMAP file: ");
+        zsoutl(ZMFILE,tp);
+#endif /* OS2 */
+
+        zsoutl(ZMFILE,"; Clear previous keyboard mappings ");
+        zsoutl(ZMFILE,"set key clear");
+#ifdef OS2
+        for (k = 0; k < nttkey; k++) {
+            if (!ttkeytab[k].flgs) {
+                ckmakmsg(buf,1024,
+                         "set terminal key ",
+                         ttkeytab[k].kwd,
+                         " clear",
+                         NULL
+                         );
+                zsoutl(ZMFILE,buf);
+            }
+        }
+#endif /* OS2 */
+        zsoutl(ZMFILE,"");
+
+        for (i = 0; i < KMSIZE; i++) {
+            if (macrotab[i]) {
+                int len = strlen((char *)macrotab[i]);
+#ifdef OS2
+                ckmakmsg(buf,
+                         1024,
+                         "set key \\",
+                         ckitoa(mskkeys ? cktomsk(i) : i),
+                         " ",
+                         NULL
+                         );
+#else /* OS2 */
+                ckmakmsg(buf,
+                         1024,
+                         "set key \\",
+                         ckitoa(i),
+                         NULL,NULL
+                         );
+#endif /* OS2 */
+                zsout(ZMFILE,buf);
+
+                for (j = 0; j < len; j++) {
+                    char ch = macrotab[i][j];
+                    if (ch <= SP || ch >= DEL ||
+                         ch == '-' || ch == ',' ||
+                         ch == '{' || ch == '}' ||
+                         ch == ';' || ch == '?' ||
+                         ch == '.' || ch == '\'' ||
+                         ch == '\\' || ch == '/' ||
+                         ch == '#') {
+                        ckmakmsg(buf,1024,"\\{",ckitoa((int)ch),"}",NULL);
+                        zsout(ZMFILE,buf);
+                    } else {
+                        ckmakmsg(buf,1024,ckctoa((char)ch),NULL,NULL,NULL);
+                        zsout(ZMFILE,buf);
+                    }
+                }
+#ifdef OS2
+                ckmakmsg(buf,1024,"\t; ",keyname(i),NULL,NULL);
+                zsoutl(ZMFILE,buf);
+#else
+                zsoutl(ZMFILE,"");
+#endif /* OS2 */
+            } else if ( keymap[i] != i ) {
+#ifndef NOKVERBS
+                if (IS_KVERB(keymap[i])) {
+                    for (j = 0; j < nkverbs; j++)
+                      if (kverbs[j].kwval == (keymap[i] & ~F_KVERB))
+                        break;
+                    if (j != nkverbs) {
+#ifdef OS2
+#ifdef COMMENT
+                        sprintf(buf, "set key \\%d \\K%s\t; %s",
+                                mskkeys ? cktomsk(i) : i,
+                                kverbs[j].kwd, keyname(i)
+                                );
+#else
+                        ckmakxmsg(buf,  /* 12 string args */
+                                  1024,
+                                  "set key \\",
+                                  ckitoa(mskkeys ? cktomsk(i) : i),
+                                  " \\K",
+                                  kverbs[j].kwd,
+                                  "\t; ",
+                                  keyname(i),
+                                  NULL, NULL, NULL, NULL, NULL, NULL);
+#endif /* COMMENT */
+                        zsoutl(ZMFILE,buf);
+#else
+#ifdef COMMENT
+                        sprintf(buf, "set key \\%d \\K%s", i, kverbs[j].kwd);
+#else
+                        ckmakmsg(buf,1024,
+                                 "set key \\",
+                                 ckitoa(i),
+                                 " \\K",
+                                 kverbs[j].kwd
+                                 );
+#endif /* COMMENT */
+                        zsoutl(ZMFILE,buf);
+#endif
+                    }
+                } else
+#endif /* NOKVERBS */
+                  {
+#ifdef OS2
+#ifdef COMMENT
+                      sprintf(buf, "set key \\%d \\{%d}\t; %s",
+                              mskkeys ? cktomsk(i) : i,
+                              keymap[i],
+                              keyname(i)
+                              );
+#else
+                      ckmakxmsg(buf,    /* 8 string args */
+                                1024,
+                                "set key \\",
+                                ckitoa(mskkeys ? cktomsk(i) : i),
+                                " \\{",
+                                ckitoa(keymap[i]),
+                                "}\t; ",
+                                keyname(i),
+                                NULL,NULL,NULL,NULL,NULL,NULL);
+#endif /* COMMENT */
+                      zsoutl(ZMFILE,buf);
+#else
+#ifdef COMMENT
+                      sprintf(buf, "set key \\%d \\{%d}", i, keymap[i]);
+#else
+                      ckmakxmsg(buf,1024,
+                               "set key \\",
+                               ckitoa(i),
+                               " \\{",
+                               ckitoa(keymap[i]),
+                               "}",
+                               NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+#endif /* COMMENT */
+                      zsoutl(ZMFILE,buf);
+#endif /* OS2 */
+                  }
+            }
+        }
+#ifdef OS2
+        /* OS/2 also has the SET TERMINAL KEY <termtype> defines */
+        for (k = 0; k < nttkey; k++) {
+            extern struct keynode * ttkeymap[];
+            struct keynode * pnode = NULL;
+
+            if (ttkeytab[k].flgs)       /* Don't process CM_INV or CM_ABR */
+              continue;
+
+            zsoutl(ZMFILE,"");
+            ckmakmsg(buf,1024,"; SET TERMINAL KEY ",ttkeytab[k].kwd,NULL,NULL);
+            zsoutl(ZMFILE,buf);
+
+            for (pnode = ttkeymap[ttkeytab[k].kwval];
+                 pnode;
+                 pnode = pnode->next
+                 ) {
+                switch (pnode->def.type) {
+                  case key:
+#ifdef COMMENT
+                    sprintf(buf, "set terminal key %s \\%d \\{%d}\t; %s",
+                            ttkeytab[k].kwd,
+                            mskkeys ? cktomsk(pnode->key) : pnode->key,
+                            pnode->def.key.scancode,
+                            keyname(pnode->key)
+                            );
+#else
+                    ckmakxmsg(buf,
+                              1024,
+                              "set terminal key ",
+                              ttkeytab[k].kwd,
+                              " \\",
+                              ckitoa(mskkeys ?
+                                     cktomsk(pnode->key) :
+                                     pnode->key),
+                              " \\{",
+                              ckitoa(pnode->def.key.scancode),
+                              "}\t; ",
+                              keyname(pnode->key),
+                              NULL,NULL,NULL,NULL
+                              );
+#endif /* COMMENT */
+                    zsoutl(ZMFILE,buf);
+                    break;
+                  case kverb:
+                    for (j = 0; j < nkverbs; j++)
+                      if (kverbs[j].kwval == (pnode->def.kverb.id & ~F_KVERB))
+                        break;
+                    if (j != nkverbs) {
+#ifdef COMMENT
+                        sprintf(buf, "set terminal key %s \\%d \\K%s\t; %s",
+                                ttkeytab[k].kwd,
+                                mskkeys ? cktomsk(pnode->key) : pnode->key,
+                                kverbs[j].kwd, keyname(pnode->key)
+                                );
+#else
+                        ckmakxmsg(buf,
+                                  1024,
+                                  "set terminal key ",
+                                  ttkeytab[k].kwd,
+                                  " \\",
+                                  ckitoa(mskkeys ?
+                                         cktomsk(pnode->key) :
+                                         pnode->key),
+                                  " \\K",
+                                  kverbs[j].kwd,
+                                  "\t; ",
+                                  keyname(pnode->key),
+                                  NULL,NULL,NULL,NULL
+                                  );
+#endif /* COMMENT */
+                        zsoutl(ZMFILE,buf);
+                    }
+                    break;
+                  case macro: {
+                      int len = strlen((char *)pnode->def.macro.string);
+#ifdef COMMENT
+                      sprintf(buf,"set terminal key %s \\%d ",
+                              ttkeytab[k].kwd,
+                              mskkeys ? cktomsk(pnode->key) : pnode->key);
+#else
+                      ckmakxmsg(buf,
+                               1024,
+                               "set terminal key ",
+                               ttkeytab[k].kwd,
+                               " \\",
+                               ckitoa(mskkeys ?
+                                      cktomsk(pnode->key) :
+                                      pnode->key),
+                               " ",
+                               NULL,NULL,NULL,NULL,NULL,NULL,NULL
+                              );
+#endif /* COMMENT */
+                      zsout(ZMFILE,buf);
+
+                      for (j = 0; j < len; j++) {
+                          char ch = pnode->def.macro.string[j];
+                          if (ch <= SP || ch >= DEL ||
+                               ch == '-' || ch == ',' ||
+                               ch == '{' || ch == '}' ||
+                               ch == ';' || ch == '?' ||
+                               ch == '.' || ch == '\'' ||
+                               ch == '\\' || ch == '/' ||
+                               ch == '#') {
+                              ckmakmsg(buf,1024,
+                                       "\\{",ckitoa((int)ch),"}",NULL);
+                              zsout(ZMFILE,buf);
+                          } else {
+                              ckmakmsg(buf,1024,
+                                       ckctoa((char)ch),NULL,NULL,NULL);
+                              zsout(ZMFILE,buf);
+                          }
+                      }
+                      ckmakmsg(buf,1024,"\t; ",keyname(pnode->key),NULL,NULL);
+                      zsoutl(ZMFILE,buf);
+                      break;
+                  }
+                  case literal: {
+                      int len = strlen((char *)pnode->def.literal.string);
+#ifdef COMMENT
+                      sprintf(buf,"set terminal key %s /literal \\%d ",
+                              ttkeytab[k].kwd,
+                              mskkeys ? cktomsk(pnode->key) : pnode->key);
+#else
+                      ckmakxmsg(buf,
+                               1024,
+                               "set terminal key ",
+                               ttkeytab[k].kwd,
+                               " /literal \\",
+                               ckitoa(mskkeys ?
+                                      cktomsk(pnode->key) :
+                                      pnode->key),
+                               " ",
+                               NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+#endif /* COMMENT */
+                      zsout(ZMFILE,buf);
+
+                      for (j = 0; j < len; j++) {
+                          char ch = pnode->def.literal.string[j];
+                          if (ch <= SP || ch >= DEL ||
+                               ch == '-' || ch == ',' ||
+                               ch == '{' || ch == '}' ||
+                               ch == ';' || ch == '?' ||
+                               ch == '.' || ch == '\'' ||
+                               ch == '\\' || ch == '/' ||
+                               ch == '#') {
+                              ckmakmsg(buf,1024,
+                                       "\\{",ckitoa((int)ch),"}",NULL);
+                              zsout(ZMFILE,buf);
+                          } else {
+                              ckmakmsg(buf,1024,
+                                       ckctoa((char)ch),NULL,NULL,NULL);
+                              zsout(ZMFILE,buf);
+                          }
+                      }
+                      ckmakmsg(buf,1024,"\t; ",keyname(pnode->key),NULL,NULL);
+                      zsoutl(ZMFILE,buf);
+                      break;
+                  }
+                  case esc:
+#ifdef COMMENT
+                    sprintf(buf,
+                       "set terminal key %s /literal \\%d \\{%d}\\{%d}\t; %s",
+                            ttkeytab[k].kwd,
+                            mskkeys ? cktomsk(pnode->key) : pnode->key,
+                            ISDG200(ttkeytab[k].kwval) ? 30 : 27,
+                            pnode->def.esc.key & ~F_ESC,
+                            keyname(pnode->key)
+                            );
+#else
+                    ckmakxmsg(buf,
+                              1024,
+                              "set terminal key ",
+                              ttkeytab[k].kwd,
+                              " /literal \\",
+                              ckitoa(mskkeys ?
+                                     cktomsk(pnode->key) :
+                                     pnode->key),
+                              " \\{",
+                              ckitoa(ISDG200(ttkeytab[k].kwval) ? 30 : 27),
+                              "}\\{",
+                              ckitoa(pnode->def.esc.key & ~F_ESC),
+                              "}\t; ",
+                              keyname(pnode->key),
+                              NULL,NULL
+                              );
+#endif /* COMMENT */
+                    zsoutl(ZMFILE,buf);
+                    break;
+                  case csi:
+#ifdef COMMENT
+                    sprintf(buf,
+                       "set terminal key %s /literal \\%d \\{27}[\\{%d}\t; %s",
+                            ttkeytab[k].kwd,
+                            mskkeys ? cktomsk(pnode->key) : pnode->key,
+                            pnode->def.csi.key & ~F_CSI,
+                            keyname(pnode->key)
+                            );
+#else
+                    ckmakxmsg(buf,
+                              1024,
+                              "set terminal key ",
+                              ttkeytab[k].kwd,
+                              " /literal \\",
+                              ckitoa(mskkeys ?
+                                     cktomsk(pnode->key) :
+                                     pnode->key),
+                              " \\{27}[\\{",
+                              ckitoa(pnode->def.csi.key & ~F_CSI),
+                              "}\t; ",
+                              keyname(pnode->key),
+                              NULL,NULL,NULL,NULL
+                              );
+#endif /* COMMENT */
+                    zsoutl(ZMFILE,buf);
+                    break;
+                  default:
+                    continue;
+                }
+            }
+        }
+#endif /* OS2 */
+
+        zsoutl(ZMFILE,"");
+        zsoutl(ZMFILE,"; End");
+        zclose(ZMFILE);
+        return(success = 1);
+    } else {
+        return(success = 0);
+    }
+}
+#endif /* NOSETKEY */
+
+#define SV_SCRL 0
+#define SV_HIST 1
+
+#ifdef OS2
+#ifndef NOLOCAL
+static struct keytab trmtrmopt[] = {
+    { "scrollback", SV_SCRL, 0 }
+};
+#endif /* NOLOCAL */
+#endif /* OS2 */
+
+static struct keytab cmdtrmopt[] = {
+#ifdef CK_RECALL
+    { "history",    SV_HIST, 0 },
+#endif /* CK_RECALL */
+#ifdef OS2
+#ifndef NOLOCAL
+    { "scrollback", SV_SCRL, 0 },
+#endif /* NOLOCAL */
+#endif /* OS2 */
+    { "", 0, 0 }
+};
+static int ncmdtrmopt = (sizeof (cmdtrmopt) / sizeof (struct keytab)) - 1;
+
+#ifdef OS2
+#ifndef NOLOCAL
+_PROTOTYP(int savscrbk, (int, char *, int));
+#endif /* NOLOCAL */
+#endif /* OS2 */
+
+#ifdef CK_RECALL
+_PROTOTYP(int savhistory, (char *, int));
+#endif /* CK_RECALL */
+
+int
+dosave(xx) int xx; {
+    int x, y = 0, disp;
+    char * s = NULL;
+    extern struct keytab disptb[];
+#ifdef ZFNQFP
+    struct zfnfp * fnp;
+#endif /* ZFNQFP */
+
+#ifndef NOSETKEY
+    if (xx == XSKEY) {                  /* SAVE KEYMAP.. */
+        z = cmofi("Name of Kermit command file","keymap.ksc",&s,xxstring);
+    } else {
+#endif /* NOSETKEY */
+        switch (xx) {
+          case XSCMD:                   /* SAVE COMMAND.. */
+            if ((y = cmkey(cmdtrmopt, ncmdtrmopt, "What to save",
+#ifdef OS2
+                           "scrollback",
+#else
+                           "history",
+#endif /* OS2 */
+                           xxstring)) < 0)
+              return(y);
+            break;
+#ifdef OS2
+#ifndef NOLOCAL
+          case XSTERM:                  /* SAVE TERMINAL.. */
+            if ((y = cmkey(trmtrmopt,1,
+                           "What to save","scrollback",xxstring)) < 0)
+              return(y);
+            break;
+#endif /* NOLOCAL */
+#endif /* OS2 */
+        }
+        z = cmofi("Filename",
+                  ((y == SV_SCRL) ? "scrollbk.txt" : "history.txt"),
+                  &s,
+                  xxstring
+                  );
+#ifndef NOSETKEY
+    }
+#endif /* NOSETKEY */
+    if (z < 0)                          /* Check output-file parse results */
+      return(z);
+    if (z == 2) {
+        printf("?Sorry, %s is a directory name\n",s);
+        return(-9);
+    }
+#ifdef ZFNQFP
+    if ((fnp = zfnqfp(s,TMPBUFSIZ - 1,tmpbuf))) {/* Convert to full pathname */
+        if (fnp->fpath)
+          if ((int) strlen(fnp->fpath) > 0)
+            s = fnp->fpath;
+    }
+#endif /* ZFNQFP */
+
+    ckstrncpy(line,s,LINBUFSIZ);        /* Make safe copy of pathname */
+    s = line;
+#ifdef MAC
+    z = 0;
+#else
+    /* Get NEW/APPEND disposition */
+    if ((z = cmkey(disptb,2,"Disposition","new",xxstring)) < 0)
+      return(z);
+#endif /* MAC */
+    disp = z;
+    if ((x = cmcfm()) < 0)              /* Get confirmation */
+      return(x);
+
+    switch (xx) {                       /* Do action.. */
+#ifndef NOSETKEY
+      case XSKEY:                       /* SAVE KEYMAP */
+        return (savkeys(s,disp));
+#endif /* NOSETKEY */
+
+      case XSCMD:                       /* SAVE COMMAND.. */
+#ifdef OS2
+#ifndef NOLOCAL
+        if (y == SV_SCRL)               /* .. SCROLLBACK */
+          return(success = savscrbk(VCMD,s,disp));
+#endif /* NOLOCAL */
+#endif /* OS2 */
+#ifndef NORECALL
+        if (y == SV_HIST)               /* .. HISTORY */
+          return(success = savhistory(s,disp));
+        break;
+#endif /* NORECALL */
+
+#ifdef OS2
+#ifndef NOLOCAL
+      case XSTERM:                      /* SAVE TERMINAL SCROLLBACK */
+        return(success = savscrbk(VTERM,s,disp));
+#endif /* NOLOCAL */
+#endif /* OS2 */
+    }
+    success = 0;
+    return(-2);
+}
+
+/*
+  R E A D T E X T
+
+  Read text with a custom prompt into given buffer using command parser but
+  with no echoing or entry into recall buffer.
+*/
+int
+readtext(prmpt, buffer, bufsiz) char * prmpt; char * buffer; int bufsiz; {
+#ifdef CK_RECALL
+    extern int on_recall;               /* Around Password prompting */
+#endif /* CK_RECALL */
+    int rc;
+#ifndef NOLOCAL
+#ifdef OS2
+    extern int vmode;
+    extern int startflags;
+    int vmode_sav = vmode;
+
+    if (!prmpt) prmpt = "";
+
+    if (win95_popup && !(startflags & 96)
+#ifdef IKSD
+         && !inserver
+#endif /* IKSD */
+         )
+      return(popup_readtext(vmode,NULL,prmpt,buffer,bufsiz,0));
+
+    if (vmode == VTERM) {
+        vmode = VCMD;
+        VscrnIsDirty(VTERM);
+        VscrnIsDirty(VCMD);
+    }
+#endif /* OS2 */
+#endif /* NOLOCAL */
+
+#ifdef CK_RECALL
+    on_recall = 0;
+#endif /* CK_RECALL */
+    cmsavp(psave,PROMPTL);              /* Save old prompt */
+    cmsetp(prmpt);                      /* Make new prompt */
+    concb((char)escape);                /* Put console in cbreak mode */
+    cmini(1);                           /* and echo mode */
+    if (pflag) prompt(xxstring);        /* Issue prompt if at top level */
+    cmres();                            /* Reset the parser */
+    for (rc = -1; rc < 0; ) {           /* Prompt till they answer */
+        rc = cmtxt("","",&s,NULL);      /* Get a literal line of text */
+        cmres();                        /* Reset the parser again */
+    }
+    ckstrncpy(buffer,s,bufsiz);
+    cmsetp(psave);                      /* Restore original prompt */
+
+#ifndef NOLOCAL
+#ifdef OS2
+    if (vmode != vmode_sav) {
+        vmode = VTERM;
+        VscrnIsDirty(VCMD);
+        VscrnIsDirty(VTERM);
+    }
+#endif /* OS2 */
+#endif /* NOLOCAL */
+    return(0);
+}
+#endif /* NOICP */
+
+/* A general function to allow a Password or other information  */
+/* to be read from the command prompt without it going into     */
+/* the recall buffer or being echo'd.                           */
+
+int
+readpass(prmpt, buffer, bufsiz) char * prmpt; char * buffer; int bufsiz; {
+    int x;
+#ifdef NOICP
+    if (!prmpt) prmpt = "";
+    printf("%s", prmpt);
+#ifdef COMMENT
+    /* Some linkers won't allow this because it's unsafe */
+    gets(buffer);
+#else  /* COMMENT */
+    {
+        int c, i; char * p;
+        p = buffer;
+        for (i = 0; i < bufsiz-1; i++) {
+            if ((c = getchar()) == EOF)
+              break;
+            if (c < SP)
+              break;
+            buffer[i] = c;
+        }
+        buffer[i] = NUL;
+    }
+#endif /* COMMENT */
+    return(1);
+#else  /* NOICP */
+#ifdef CK_RECALL
+    extern int on_recall;               /* around Password prompting */
+#endif /* CK_RECALL */
+    int rc;
+#ifndef NOLOCAL
+#ifdef OS2
+    extern int vmode;
+    extern int startflags;
+    int vmode_sav = vmode;
+#endif /* OS2 */
+#endif /* NOLOCAL */
+#ifdef CKSYSLOG
+    int savlog;
+#endif /* CKSYSLOG */
+    if (!prmpt) prmpt = "";
+#ifndef NOLOCAL
+    debok = 0;                          /* Don't log */
+#ifdef OS2
+    if (win95_popup && !(startflags & 96)
+#ifdef IKSD
+         && !inserver
+#endif /* IKSD */
+         ) {
+        x = popup_readpass(vmode,NULL,prmpt,buffer,bufsiz,0);
+        debok = 1;
+        return(x);
+    }
+#endif /* OS2 */
+#endif /* NOLOCAL */
+
+#ifdef CKSYSLOG
+    savlog = ckxsyslog;                 /* Save and turn off syslogging */
+    ckxsyslog = 0;
+#endif /* CKSYSLOG */
+#ifndef NOLOCAL
+#ifdef OS2
+    if (vmode == VTERM) {
+        vmode = VCMD;
+        VscrnIsDirty(VTERM);
+        VscrnIsDirty(VCMD);
+    }
+#endif /* OS2 */
+#endif /* NOLOCAL */
+#ifdef CK_RECALL
+    on_recall = 0;
+#endif /* CK_RECALL */
+    cmsavp(psave,PROMPTL);              /* Save old prompt */
+    cmsetp(prmpt);                      /* Make new prompt */
+    concb((char)escape);                /* Put console in cbreak mode */
+    cmini(0);                           /* and no-echo mode */
+    if (pflag) prompt(xxstring);        /* Issue prompt if at top level */
+    cmres();                            /* Reset the parser */
+    for (rc = -1; rc < 0; ) {           /* Prompt till they answer */
+        rc = cmtxt("","",&s,NULL);      /* Get a literal line of text */
+        cmres();                        /* Reset the parser again */
+    }
+    ckstrncpy(buffer,s,bufsiz);
+    printf("\r\n");                     /* Echo a CRLF */
+    cmsetp(psave);                      /* Restore original prompt */
+    cmini(1);                           /* Restore echo mode */
+#ifndef NOLOCAL
+#ifdef OS2
+    if (vmode != vmode_sav) {
+        vmode = VTERM;
+        VscrnIsDirty(VCMD);
+        VscrnIsDirty(VTERM);
+    }
+#endif /* OS2 */
+#endif /* NOLOCAL */
+#ifdef CKSYSLOG
+    ckxsyslog = savlog;                 /* Restore syslogging */
+#endif /* CKSYSLOG */
+    debok = 1;
+    return(0);
+#endif /* NOICP */
+}
+
+#ifndef NOICP
+struct keytab authtab[] = {             /* Available authentication types */
+#ifdef CK_KERBEROS
+    { "k4",        AUTH_KRB4, CM_INV },
+    { "k5",        AUTH_KRB5, CM_INV },
+    { "kerberos4", AUTH_KRB4, 0      },
+    { "kerberos5", AUTH_KRB5, 0      },
+    { "krb4",      AUTH_KRB4, CM_INV },
+    { "krb5",      AUTH_KRB5, CM_INV },
+#endif /* CK_KERBEROS */
+#ifdef NT
+    { "ntlm",      AUTH_NTLM, 0 },
+#endif /* NT */
+#ifdef CK_SRP
+    { "srp",       AUTH_SRP,  0 },
+#endif /* CK_SRP */
+#ifdef CK_SSL
+    { "ssl",       AUTH_SSL,  0 },
+#endif /* CK_SSL */
+    { "",          0,         0 }
+};
+int authtabn = sizeof(authtab)/sizeof(struct keytab)-1;
+
+#ifdef CK_KERBEROS
+struct keytab kerbtab[] = {             /* Kerberos authentication types */
+    { "k4",        AUTH_KRB4, CM_INV },
+    { "k5",        AUTH_KRB5, CM_INV },
+    { "kerberos4", AUTH_KRB4, 0      },
+    { "kerberos5", AUTH_KRB5, 0      },
+    { "krb4",      AUTH_KRB4, CM_INV },
+    { "krb5",      AUTH_KRB5, CM_INV }
+};
+int kerbtabn = sizeof(kerbtab)/sizeof(struct keytab);
+
+static struct keytab krb_s_tbl[] = {    /* AUTHENTICATE command switches: */
+    { "/cache",   KRB_S_CA, CM_ARG }
+};
+static int krb_s_n = sizeof(krb_s_tbl)/sizeof(struct keytab);
+
+static struct keytab krb_v_tbl[] = {    /* KERBEROS version values: */
+    { "4",    4, 0 },
+    { "5",    5, 0 },                   /* (add others as needed...) */
+    { "auto", 0, 0 }                    /* Note: 0 = auto */
+};
+static int krb_v_n = sizeof(krb_v_tbl)/sizeof(struct keytab);
+
+static struct keytab krb_a_tbl[] = {    /* KERBEROS actions: */
+    { "destroy",           KRB_A_DE, 0 },
+    { "initialize",        KRB_A_IN, 0 },
+    { "list-credentials",  KRB_A_LC, 0 }
+};
+static int krb_a_n = sizeof(krb_a_tbl)/sizeof(struct keytab);
+
+static struct keytab krb4_i_tbl[] = {   /* KERBEROS 4 INITIALIZE switches: */
+    { "/brief",            KRB_I_BR, 0 },      /* /BRIEF       */
+    { "/instance",         KRB_I_IN, CM_ARG }, /* /INSTANCE:   */
+    { "/lifetime",         KRB_I_LF, CM_ARG }, /* /LIFETIME:   */
+    { "/not-preauth",      KRB_I_NPA, 0 },     /* /NOT-PREAUTH */
+    { "/password",         KRB_I_PW, CM_ARG }, /* /PASSWORD:   */
+#ifdef OS2
+    { "/popup",            KRB_I_POP, 0 },     /* /POPUP       */
+#endif /* OS2 */
+    { "/preauth",          KRB_I_PA, 0 },      /* /PREAUTH     */
+    { "/realm",            KRB_I_RL, CM_ARG }, /* /REALM:      */
+    { "/verbose",          KRB_I_VB, 0 },      /* /VERBOSE     */
+    { "", 0, 0 }
+};
+static int krb4_i_n = sizeof(krb4_i_tbl)/sizeof(struct keytab) - 1;
+
+static struct keytab krb5_i_tbl[] = {   /* KERBEROS 5 INITIALIZE switches: */
+    { "/addresses",        KRB_I_ADR, CM_ARG },
+    { "/forwardable",      KRB_I_FW,  0 },         /* /FORWARDABLE */
+    { "/instance",         KRB_I_IN, CM_ARG }, /* /INSTANCE:   */
+    { "/k4",               KRB_I_K4,  CM_INV }, /* /KERBEROS4   */
+    { "/kerberos4",        KRB_I_K4,  0 },      /* /KERBEROS4   */
+    { "/krb4",             KRB_I_K4,  CM_INV }, /* /KERBEROS4   */
+    { "/lifetime",         KRB_I_LF,  CM_ARG }, /* /LIFETIME:   */
+    { "/no-addresses",     KRB_I_NAD, 0 },      /* /NO-ADDRESSES */
+    { "/no-k4",            KRB_I_NK4, CM_INV },/* /NO-KERBEROS4 */
+    { "/no-kerberos4",     KRB_I_NK4, 0 },     /* /NO-KERBEROS4 */
+    { "/no-krb4",          KRB_I_NK4, CM_INV },/* /NO-KERBEROS4 */
+    { "/not-forwardable",  KRB_I_NFW, 0 },         /* /NOT-FORWARDABLE */
+    { "/not-proxiable",    KRB_I_NPR, 0 },     /* /NOT-PROXIABLE   */
+    { "/password",         KRB_I_PW,  CM_ARG }, /* /PASSWORD:   */
+#ifdef OS2
+    { "/popup",            KRB_I_POP, 0 },     /* /POPUP       */
+#endif /* OS2 */
+    { "/postdate",         KRB_I_PD, CM_ARG }, /* /POSTDATE:   */
+    { "/pr",               KRB_I_PR, CM_INV|CM_ABR }, /* to allow for */
+    { "/pro",              KRB_I_PR, CM_INV|CM_ABR }, /* different spellings */
+    { "/prox",             KRB_I_PR, CM_INV|CM_ABR },
+    { "/proxiable",        KRB_I_PR, 0 },      /* /PROXIABLE   */
+    { "/proxyable",        KRB_I_PR, CM_INV }, /* /PROXYABLE   */
+    { "/realm",            KRB_I_RL, CM_ARG }, /* /REALM:      */
+    { "/renew",            KRB_I_RN, 0 },          /* /RENEW       */
+    { "/renewable",        KRB_I_RB, CM_ARG }, /* /RENEWABLE:  */
+    { "/service",          KRB_I_SR, CM_ARG }, /* /SERVICE:    */
+    { "/validate",         KRB_I_VA, 0 },          /* /VALIDATE    */
+    { "", 0, 0 }
+};
+static int krb5_i_n = sizeof(krb5_i_tbl)/sizeof(struct keytab) - 1;
+
+static struct keytab klctab[] = {       /* List Credentials switches*/
+    { "/addresses",  XYKLCAD, 0 },
+    { "/encryption", XYKLCEN, 0 },
+    { "/flags",      XYKLCFL, 0 }
+};
+static int nklctab = sizeof(klctab)/sizeof(struct keytab);
+
+extern int krb_action;
+extern struct krb_op_data krb_op;
+
+extern struct krb5_list_cred_data krb5_lc;
+extern struct krb5_init_data krb5_init;
+extern char * krb5_d_principal;         /* Default principal */
+extern char * krb5_d_instance;
+extern char * krb5_d_realm;             /* Default realm */
+extern char * krb5_d_cc;                /* Default credentials cache */
+extern char * krb5_d_srv;               /* Default service name */
+extern int    krb5_d_lifetime;          /* Default lifetime */
+extern int    krb5_d_forwardable;
+extern int    krb5_d_proxiable;
+extern int    krb5_d_renewable;
+extern int    krb5_autoget;
+extern int    krb5_autodel;
+extern int    krb5_d_getk4;
+extern int    krb5_d_no_addresses;
+extern int    krb5_checkaddrs;
+extern char * krb5_d_addrs[];
+extern char * k5_keytab;                /* Keytab file */
+
+extern struct krb4_init_data krb4_init;
+extern char * krb4_d_principal;         /* Default principal */
+extern char * krb4_d_realm;             /* Default realm */
+extern char * krb4_d_srv;               /* Default service name */
+extern int    krb4_d_lifetime;          /* Default lifetime */
+extern int    krb4_d_preauth;
+extern char * krb4_d_instance;
+extern int    krb4_autoget;
+extern int    krb4_autodel;
+extern int    krb4_checkaddrs;
+extern char * k4_keytab;                /* Keytab file */
+#endif /* CK_KERBEROS */
+
+#ifndef NOSHOW
+int
+sho_iks() {
+#ifdef IKSDCONF
+#ifdef CK_LOGIN
+    extern int ckxsyslog, ckxwtmp, ckxanon;
+#ifdef UNIX
+    extern int ckxpriv;
+#endif /* UNIX */
+#ifdef CK_PERMS
+    extern int ckxperms;
+#endif /* CK_PERMS */
+    extern char * anonfile, * userfile, * anonroot;
+#ifdef OS2
+    extern char * anonacct;
+#endif /* OS2 */
+#ifdef NT
+    extern char * iks_domain;
+#endif /* NT */
+#endif /* CK_LOGIN */
+#ifdef CKWTMP
+    extern char * wtmpfile;
+#endif /* CKWTMP */
+#ifdef IKSDB
+    extern char * dbfile;
+    extern int dbenabled;
+#endif /* IKSDB */
+#ifdef CK_LOGIN
+    extern int logintimo;
+#endif /* CK_LOGIN */
+    extern int srvcdmsg, success, iksdcf, noinit, arg_x;
+    extern char * cdmsgfile[], * cdmsgstr, *kermrc;
+    char * bannerfile = NULL;
+    char * helpfile = NULL;
+    extern int xferlog;
+    extern char * xferfile;
+    int i;
+
+    if (isguest) {
+        printf("?Command disabled\r\n");
+        return(success = 0);
+    }
+
+    printf("IKS Settings\r\n");
+#ifdef CK_LOGIN
+#ifdef OS2
+    printf("  Anonymous Account:   %s\r\n",anonacct?anonacct:"<none>");
+#endif /* OS2 */
+    printf("  Anonymous Initfile:  %s\r\n",anonfile?anonfile:"<none>");
+    printf("  Anonymous Login:     %d\r\n",ckxanon);
+    printf("  Anonymous Root:      %s\r\n",anonroot?anonroot:"<none>");
+#endif /* CK_LOGIN */
+    printf("  Bannerfile:          %s\r\n",bannerfile?bannerfile:"<none>");
+    printf("  CDfile:              %s\r\n",cdmsgfile[0]?cdmsgfile[0]:"<none>");
+    for ( i=1;i<16 && cdmsgfile[i];i++ )
+        printf("  CDfile:              %s\r\n",cdmsgfile[i]);
+    printf("  CDMessage:           %d\r\n",srvcdmsg);
+#ifdef IKSDB
+    printf("  DBfile:              %s\r\n",dbfile?dbfile:"<none>");
+    printf("  DBenabled:           %d\r\n",dbenabled);
+#endif /* IKSDB */
+#ifdef CK_LOGIN
+#ifdef NT
+    printf("  Default-domain:      %s\r\n",iks_domain?iks_domain:".");
+#endif /* NT */
+#endif /* CK_LOGIN */
+    printf("  Helpfile:            %s\r\n",helpfile?helpfile:"<none>");
+    printf("  Initfile:            %s\r\n",kermrc?kermrc:"<none>");
+    printf("  No-Initfile:         %d\r\n",noinit);
+#ifdef CK_LOGIN
+#ifdef CK_PERM
+    printf("  Permission code:     %0d\r\n",ckxperms);
+#endif /* CK_PERM */
+#ifdef UNIX
+    printf("  Privileged Login:    %d\r\n",ckxpriv);
+#endif /* UNIX */
+#endif /* CK_LOGIN */
+    printf("  Server-only:         %d\r\n",arg_x);
+    printf("  Syslog:              %d\r\n",ckxsyslog);
+    printf("  Timeout (seconds):   %d\r\n",logintimo);
+    printf("  Userfile:            %s\r\n",userfile?userfile:"<none>");
+#ifdef CK_LOGIN
+#ifdef CKWTMP
+    printf("  Wtmplog:             %d\r\n",ckxwtmp);
+    printf("  Wtmpfile:            %s\r\n",wtmpfile?wtmpfile:"<none>");
+#endif /* CKWTMP */
+#endif /* CK_LOGIN */
+    printf("  Xferfile:            %s\r\n",xferfile?xferfile:"<none>");
+    printf("  Xferlog:             %d\r\n",xferlog);
+#else /* IKSDCONF */
+    printf("?Nothing to show.\r\n");
+#endif /* IKSDCONF */
+    return(success = 1);
+}
+
+#ifdef CK_AUTHENTICATION
+int
+sho_auth(cx) int cx; {
+    extern int auth_type_user[], cmd_rows;
+    int i;
+    char * p;
+    int kv = 0, all = 0, n = 0;
+
+#ifdef IKSD
+    if (inserver && isguest) {
+        printf("?Sorry, command disabled.\r\n");
+        return(success = 0);
+    }
+#endif /* IKSD */
+    if (cx) {
+        kv = cx;
+    } else if (auth_type_user[0] != AUTHTYPE_AUTO) {
+        kv = auth_type_user[0];
+    } else {
+        all = 1;
+        kv = AUTHTYPE_KERBEROS_V4;
+    }
+    while (kv) {
+        switch (kv) {
+          case AUTHTYPE_KERBEROS_V4:
+            kv = all ? AUTHTYPE_KERBEROS_V5 : 0;
+            if (ck_krb4_is_installed()) {
+                printf(" Authentication:      Kerberos 4\n");
+                if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            } else {
+                printf(" Authentication:      Kerberos 4 (not installed)\n");
+                if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+                continue;
+            }
+#ifdef CK_KERBEROS
+            printf(" Keytab file:         %s\n",
+                      k4_keytab ? k4_keytab : "(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            if (krb_action < 0) {
+                p = "(none)";
+            } else {
+                for (p = "", i = 0; i < krb_a_n; i++) {
+                    if (krb_action == krb_a_tbl[i].kwval) {
+                        p = krb_a_tbl[i].kwd;
+                        break;
+                    }
+                }
+            }
+            printf(" Action:              %s\n", p);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Default lifetime     %d\n",krb4_d_lifetime);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Lifetime:            %d (minutes)\n",krb4_init.lifetime);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Default preauth:     %d\n",krb4_d_preauth);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Preauth:             %d\n",krb4_init.preauth);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Default principal:   \"%s\"\n",
+                    krb4_d_principal ? krb4_d_principal : "");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Principal:           \"%s\"\n",
+                    krb4_init.principal ? krb4_init.principal : "");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Default realm:       \"%s\"\n",
+                    krb4_d_realm ? krb4_d_realm : "");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Realm:               \"%s\"\n",
+                    krb4_init.realm ? krb4_init.realm : "");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Default instance:    \"%s\"\n",
+                    krb4_d_instance ? krb4_d_instance : "");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Instance:            \"%s\"\n",
+                    krb4_init.instance ? krb4_init.instance : "");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Auto-Get TGTs:       %d\n",krb4_autoget);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Auto-Destroy TGTs:   %s\n",
+                    krb4_autodel==KRB_DEL_NO?"never":
+                    krb4_autodel==KRB_DEL_CL?"on-close":"on-exit");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Check IP Addresses:  %d\n",krb4_checkaddrs);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+#ifdef COMMENT
+            printf(" Password:    \"%s\"\n",
+                    krb4_init.password  ? krb4_init.password  : "");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+#endif /* COMMENT */
+#endif /* CK_KERBEROS */
+            printf("\n");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            break;
+        case AUTHTYPE_KERBEROS_V5:
+            kv = all ? AUTHTYPE_SSL : 0;
+            if (ck_krb5_is_installed()) {
+                if (ck_gssapi_is_installed())
+                    printf(" Authentication:      Kerberos 5 plus GSSAPI\n");
+                else
+                    printf(" Authentication:      Kerberos 5\n");
+                if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            } else {
+                printf(" Authentication:      Kerberos 5 (not installed)\n");
+                if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+                continue;
+            }
+
+#ifdef CK_KERBEROS
+            printf(" Cache file:          %s\n",
+                    krb_op.cache ? krb_op.cache : "(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Default cache:       %s\n",
+                    krb5_d_cc ? krb5_d_cc : "(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Keytab file:         %s\n",
+                      k5_keytab ? k5_keytab : "(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            if (krb_action < 0) {
+                p = "(none)";
+            } else  {
+                for (p = "", i = 0; i < krb_a_n; i++) {
+                    if (krb_action == krb_a_tbl[i].kwval) {
+                        p = krb_a_tbl[i].kwd;
+                        break;
+                    }
+                }
+            }
+            printf(" Action:              %s\n", p);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+
+            printf(" Default forwardable  %d\n",krb5_d_forwardable);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Forwardable:         %d\n",krb5_init.forwardable);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Default lifetime     %d\n",krb5_d_lifetime);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Lifetime:            %d (minutes)\n",krb5_init.lifetime);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Postdate:            \"%s\"\n",
+                    krb5_init.postdate ? krb5_init.postdate: "");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Default proxiable:   %d\n",krb5_d_proxiable);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Proxiable:           %d\n",krb5_init.proxiable);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Renew:               %d\n",krb5_init.renew);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Default renewable:   %d (minutes)\n",krb5_d_renewable);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Renewable:           %d (minutes)\n",krb5_init.renewable);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Service:             \"%s\"\n",
+                    krb5_init.service ? krb5_init.service : "");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Validate:            %d\n",krb5_init.validate);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Default principal:   \"%s\"\n",
+                    krb5_d_principal ? krb5_d_principal : "");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Principal:           \"%s\"\n",
+                    krb5_init.principal ? krb5_init.principal : "");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Default instance:    \"%s\"\n",
+                    krb5_d_instance ? krb5_d_instance : "");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Default realm:       \"%s\"\n",
+                    krb5_d_realm ? krb5_d_realm : "");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Realm:               \"%s\"\n",
+                    krb5_init.realm ? krb5_init.realm : "");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Auto-Get TGTs:       %d\n",krb5_autoget);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Auto-Destroy TGTs:   %s\n",
+                    krb5_autodel==KRB_DEL_NO?"never":
+                    krb5_autodel==KRB_DEL_CL?"on-close":"on-exit");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Default get K4 TGTs: %d\n",krb5_d_getk4);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Get K4 TGTs: %d\n",krb5_init.getk4);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Check IP Addresses:  %d\n",krb5_checkaddrs);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" No IP Addresses:  %d\n",krb5_d_no_addresses);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" IP-Addresses:        ");
+            if (krb5_init.addrs && krb5_init.addrs[0]) {
+                for (i = 0; krb5_init.addrs[i]; i++) {
+                    if (i)
+                      printf(",");
+                    printf("%s",krb5_init.addrs[i]);
+                }
+            } else if (krb5_d_addrs[0]) {
+                for (i = 0;i < KRB5_NUM_OF_ADDRS && krb5_d_addrs[i];i++) {
+                    if (i)
+                      printf(",");
+                    printf("%s",krb5_d_addrs[i]);
+                }
+            } else {
+                printf("(use default)");
+            }
+            printf("\n");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+#ifdef COMMENT
+            printf(" Password:            \"%s\"\n",
+                    krb5_init.password  ? krb5_init.password  : "");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+#endif /* COMMENT */
+#endif /* CK_KERBEROS */
+            printf("\n");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            break;
+          case AUTHTYPE_SSL:
+            kv = all ? AUTHTYPE_SRP : 0;
+            if (ck_ssleay_is_installed()) {
+                printf(" Authentication:      SSL/TLS (%s)\n",
+                        SSLeay_version(SSLEAY_VERSION));
+                if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            } else {
+                printf(" Authentication:      SSL/TLS (not installed)\n");
+                if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+                continue;
+            }
+
+#ifdef CK_SSL
+            printf(" RSA Certs file: %s\n",ssl_rsa_cert_file?
+                  ssl_rsa_cert_file:"(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" RSA Certs Chain file: %s\n",ssl_rsa_cert_chain_file?
+                  ssl_rsa_cert_chain_file:"(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" RSA Key file: %s\n",ssl_rsa_key_file?
+                  ssl_rsa_key_file:"(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" DSA Certs file: %s\n",ssl_dsa_cert_file?
+                  ssl_dsa_cert_file:"(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" DSA Certs Chain file: %s\n",ssl_dsa_cert_chain_file?
+                  ssl_dsa_cert_chain_file:"(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" DH Key file: %s\n",ssl_dh_key_file?
+                  ssl_dh_key_file:"(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" DH Param file: %s\n",ssl_dh_param_file?
+                  ssl_dh_param_file:"(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" CRL file: %s\n",ssl_crl_file?
+                  ssl_crl_file:"(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" CRL dir: %s\n",ssl_crl_dir?
+                    ssl_crl_dir:"(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Random file: %s\n",ssl_rnd_file?
+                  ssl_rnd_file:"(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Verify file: %s\n",ssl_verify_file?
+                  ssl_verify_file:"(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Verify dir: %s\n",ssl_verify_dir?
+                  ssl_verify_dir:"(none)");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Cipher list: %s\n",ssl_cipher_list ? ssl_cipher_list : 
+                   DEFAULT_CIPHER_LIST);
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            if (ssl_con == NULL) {
+                SSL_library_init();
+                ssl_ctx = (SSL_CTX *)
+                  SSL_CTX_new((SSL_METHOD *)TLSv1_method());
+                if (ssl_ctx != NULL)
+                  ssl_con= (SSL *) SSL_new(ssl_ctx);
+            }
+            if (ssl_con != NULL) {
+                CHAR * p = NULL;
+                int i;
+
+                for (i = 0; ; i++) {
+                    p = (CHAR *) SSL_get_cipher_list(ssl_con,i);
+                    if (p == NULL)
+                      break;
+                    printf("    %s\n",p);
+                    if (++n > cmd_rows - 3)
+                        if (!askmore()) return(0); else n = 0;
+                }
+            }
+            printf(" Certs OK? %s\n",ssl_certsok_flag? "yes" : "no");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Debug mode: %s\n", ssl_debug_flag ? "on" : "off");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Verbose mode: %s\n", ssl_verbose_flag ? "on" : "off");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" Verify mode: %s\n",
+                    ssl_verify_flag == SSL_VERIFY_NONE ? "none" :
+                    ssl_verify_flag == SSL_VERIFY_PEER ? "peer-cert" :
+                    "fail-if-no-peer-cert");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" SSL only? %s\n", ssl_only_flag ? "yes" : "no");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            printf(" TLS only? %s\n", tls_only_flag ? "yes" : "no");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+#endif /* CK_SSL */
+            break;
+          case AUTHTYPE_NTLM:
+            kv = 0;
+            if (ck_ntlm_is_installed()) {
+                printf(" Authentication:      NTLM\n");
+                if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+                printf(" No options\n");
+                if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            } else {
+                printf(" Authentication:      NTLM (not installed)\n");
+                if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+                continue;
+            }
+            printf("\n");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            break;
+          case AUTHTYPE_SRP:
+            kv = all ? AUTHTYPE_NTLM : 0;
+            if (ck_srp_is_installed()) {
+                if (ck_krypto_is_installed())
+                    printf(" Authentication:      SRP plus Krypto API\n");
+                else
+                    printf(" Authentication:      SRP\n");
+                if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+                printf(" No options\n");
+                if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            } else {
+                printf(" Authentication:      SRP (not installed)\n");
+                if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+                continue;
+            }
+            printf("\n");
+            if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
+            break;
+        }
+    }
+    return(success = 1);
+}
+#endif /* CK_AUTHENTICATION */
+#endif /* NOSHOW */
+
+#ifdef CK_KERBEROS
+
+/*  C P _ A U T H  --  AUTHENTICATE command parsing  */
+
+int
+cp_auth() {                             /* Command_Parse AUTHENTICATE */
+    int c, i, n;                        /* Workers */
+    int rc = 0;                         /* Return code */
+    int getval;                         /* Parsing helpers */
+    int tmpauth = 0;                    /* Temporary authentication type */
+    int kv = 0;                         /* Temporary Kerberos version */
+    int tmp_action = -1;                /* Temporary Kerberos action */
+    int tmp_klc = 0;                    /* Temporary list-credentials */
+    char tmphlp[256];                   /* For building help message */
+    char * p;
+    char * tmppswd  = NULL;             /* Password */
+    char * tmpprinz = NULL;             /* Principal */
+    char * tmprealm = NULL;             /* Realm */
+    char * tmpcache = NULL;             /* Cache file */
+    char * tmpinst  = NULL;             /* K4 Instance */
+    char * tmpaddrs[KRB5_NUM_OF_ADDRS];
+#ifdef CK_RECALL
+    extern int on_recall;               /* around Password prompting */
+#endif /* CK_RECALL */
+    struct stringint {                  /* Temporary array for switch values */
+        char * sval;                    /* String value */
+        int ival;                       /* Integer value */
+    } pv[KRB_I_MAX+1];                  /* This many */
+    struct FDB kw, sw, fl;              /* FDBs for each parse function */
+
+    krb_action = -1;                    /* Initialize Kerberos action. */
+    tmp_action = -1;                    /* And our local copy. */
+    for (i = 0; i < KRB5_NUM_OF_ADDRS; i++)
+      tmpaddrs[i] = NULL;
+
+    if ((y = cmkey(kerbtab,kerbtabn,"authentication type","",xxstring)) < 0)
+      {
+          if (y == -3)
+            printf("?Authentication type not specified - nothing happens\n");
+          return(y);
+      }
+    tmpauth = y;
+    debug(F101,"kerberos authentication","",tmpauth);
+    switch (tmpauth) {
+      case AUTH_KRB4: kv = 4; break;    /* Don't assume values are the same */
+      case AUTH_KRB5: kv = 5; break;
+      default:
+        printf("?Authentication type not supported: \"%s\"\n",atmbuf);
+        return(-9);
+    }
+
+    /* From here down is Kerberos */
+    ini_kerb();                         /* Reset Init data to defaults */
+
+    if (kv == 4) {                      /* Set K4 defaults */
+        if (krb4_d_realm)
+          makestr(&tmprealm,krb4_d_realm);
+        if (krb4_d_principal)
+          makestr(&tmpprinz,krb4_d_principal);
+        if (krb4_d_instance)
+          makestr(&tmpinst,krb4_d_instance);
+    } else if (kv == 5) {               /* Set K5 defaults */
+        if (krb5_d_cc)
+          makestr(&tmpcache,krb5_d_cc);
+        if (krb5_d_realm)
+          makestr(&tmprealm,krb5_d_realm);
+        if (krb5_d_principal)
+          makestr(&tmpprinz,krb5_d_principal);
+        if (krb5_d_instance)
+          makestr(&tmpinst,krb5_d_instance);
+    }
+
+    for (i = 0; i <= KRB_I_MAX; i++) {  /* Initialize switch values */
+        pv[i].sval = NULL;              /* to null pointers */
+        pv[i].ival = 0;                 /* and 0 int values */
+    }
+
+    if (kv == 4) {                      /* Kerberos 4 */
+        pv[KRB_I_LF].ival = krb4_d_lifetime;
+        pv[KRB_I_PA].ival = krb4_d_preauth;
+
+        if ((n = cmkey(krb_a_tbl,krb_a_n,"Kerberos 4 action","",xxstring)) < 0)
+          {
+              if (n == -3)
+                printf("?Action not specified - nothing happens.\n");
+              return(n);
+          }
+    } else if (kv == 5) {               /* Kerberos 5 */
+        pv[KRB_I_FW].ival = krb5_d_forwardable;
+        pv[KRB_I_PR].ival = krb5_d_proxiable;
+        pv[KRB_I_LF].ival = krb5_d_lifetime;
+        pv[KRB_I_RB].ival = krb5_d_renewable;
+        pv[KRB_I_K4].ival = krb5_d_getk4;
+        pv[KRB_I_NAD].ival = krb5_d_no_addresses;
+
+        /* Make help message that shows switches and action keywords */
+        ckstrncpy(tmphlp,"Kerberos 5 action, one of the following:\n ",256);
+        for (i = 0; i < krb_a_n; i++) {
+            ckstrncat(tmphlp,krb_a_tbl[i].kwd,sizeof(tmphlp));
+            if (i == krb_a_n - 1)
+              ckstrncat(tmphlp,"\nor switch",sizeof(tmphlp));
+            else
+              ckstrncat(tmphlp,"   ",sizeof(tmphlp));
+        }
+        /* Set up first set of chained FDB's */
+
+        cmfdbi(&sw,                     /* First FDB - command switches */
+               _CMKEY,                  /* fcode */
+               tmphlp,                  /* hlpmsg */
+               "",                      /* default (none) */
+               "",                      /* addtl string data */
+               krb_s_n,                 /* Switch table size */
+               4,                       /* addtl numeric data 2: 4 = cmswi */
+               xxstring,                /* Processing function */
+               krb_s_tbl,               /* Switch table */
+               &kw                      /* Pointer to next FDB */
+               );
+        cmfdbi(&kw,                     /* Second FDB - action keywords */
+               _CMKEY,                  /* fcode */
+               "Kerberos action",       /* hlpmsg */
+               "",                      /* default (none) */
+               "",                      /* addtl string data */
+               krb_a_n,                 /* Switch table size */
+               0,                       /* addtl num data (0 = NOT switch) */
+               xxstring,                /* Processing function */
+               krb_a_tbl,               /* Keyword table */
+               NULL                     /* Pointer to next FDB (none) */
+               );
+
+        /* Parse */
+
+        while (1) {                     /* Parse 0 or more switches */
+            rc = cmfdb(&sw);            /* Parse something */
+            debug(F101,"kerberos cmfdb 1 rc","",rc);
+            if (rc < 0) {                       /* Error */
+                if (rc == -3)
+                  printf("?Action not specified - nothing happens.\n");
+                return(rc);             /* or reparse needed */
+            }
+            if (cmresult.fdbaddr != &sw) /* Break out if not a switch */
+              break;
+            c = cmgbrk();               /* Have switch - get break character */
+            getval = (c == ':' || c == '='); /* Must parse an agument? */
+            if (getval && !(cmresult.kflags & CM_ARG)) {
+                printf("?This switch does not take arguments\n");
+                return(-9);             /* OK because nothing malloc'd yet */
+            }
+            if (!getval && (cmgkwflgs() & CM_ARG)) {
+                printf("?This switch requires an argument\n");
+                return(-9);
+            }
+            n = cmresult.nresult;       /* Numeric result = switch value */
+            debug(F101,"kerberos command switch","",n);
+
+            switch (n) {                /* Handle the switch */
+              case KRB_S_CA:            /* /CACHE:<filename> */
+                p = krb5_d_cc ? krb5_d_cc : "";
+                if ((y = cmofi("Name of cache file",p,&s,xxstring)) < 0) {
+                    if (y == -3)
+                      s = NULL;
+                    else
+                      return(y);
+                }
+                makestr(&tmpcache,s);
+                break;
+              default:
+                printf("?Unexpected switch value - internal error\n");
+                return(-9);             /* (if) nothing malloc'd yet. */
+            }
+        }
+        if (cmresult.fdbaddr != &kw) {  /* Checking... */
+            printf("?Unexpected result - internal error\n");
+            return(-9);                 /* Nothing malloc'd yet. */
+        }
+        n = cmresult.nresult;           /* Get keyword value */
+    } else {
+        printf("?Unexpected Kerberos version - Internal error\n");
+        return(-9);
+    }
+    debug(F101,"kerberos action","",n);
+    switch (n) {
+      case KRB_A_IN:                    /* INITIALIZE */
+      case KRB_A_DE:                    /* DESTROY */
+      case KRB_A_LC:                    /* LIST-CREDENTIALS */
+        tmp_action = n;                 /* OK, set */
+        break;
+      default:                          /* Not OK, punt. */
+        printf("?Unexpected action - internal error\n");
+        return(-9);
+    }
+    if (tmp_action == KRB_A_IN) {       /* Action is INITIALIZE */
+        int x;
+        cmfdbi(&sw,                     /* INITIALIZE switches */
+               _CMKEY,                  /* fcode */
+               "Principal,\n or optional INITIALIZE switch(es)", /* hlpmsg */
+               "",                      /* default (none) */
+               "",                      /* addtl string data */
+               kv == 4 ?  krb4_i_n : krb5_i_n, /* Switch table size */
+               4,                       /* addtl numeric data 2: 4 = cmswi */
+               xxstring,                /* Processing function */
+               kv == 4 ?  krb4_i_tbl : krb5_i_tbl,      /* Switch table */
+               &fl                      /* Pointer to next FDB */
+               );
+        cmfdbi(&fl,                     /* 3rd FDB - command to send from */
+               _CMFLD,                  /* fcode */
+               "Principal",             /* hlpmsg */
+               kv == 4 ? krb4_d_principal : krb5_d_principal, /* principal */
+               "",                      /* addtl string data */
+               0,                       /* addtl numeric data 1 */
+               0,                       /* addtl numeric data 2 */
+               xxstring,
+               NULL,
+               NULL
+               );
+        while (1) {                     /* Parse INIT switches or principal */
+            rc = cmfdb(&sw);
+            debug(F101,"kerberos cmfdb 2 rc","",rc);
+            if (rc < 0) {
+                if (rc == -3)
+                  printf("?Principal name required\n");
+                goto kerbx;
+            }
+            debug(F101,"kerberos cmfdb 2 fcode","",cmresult.fcode);
+            if (cmresult.fcode != _CMKEY) /* Not a switch, quit switch loop */
+              break;
+            c = cmgbrk();               /* Switch - get break character */
+            debug(F101,"kerberos cmfdb 2 cmgbrk","",c);
+            getval = (c == ':' || c == '=');
+            if (getval && !(cmresult.kflags & CM_ARG)) {
+                printf("?This switch does not take arguments\n");
+                return(-9);             /* OK because nothing malloc'd yet */
+            }
+            if (!getval && (cmgkwflgs() & CM_ARG)) {
+                printf("?This switch requires an argument\n");
+                return(-9);
+            }
+            n = cmresult.nresult;       /* Numeric result = switch value */
+            switch (n) {
+              /* These don't take args... */
+              case KRB_I_PA:            /* /PREAUTH */
+              case KRB_I_FW:            /* /FORWARDABLE */
+              case KRB_I_PR:            /* /PROXIABLE */
+              case KRB_I_RN:            /* /RENEW */
+              case KRB_I_VA:            /* /VALIDATE */
+              case KRB_I_NPA:           /* /NOT-PREAUTH */
+              case KRB_I_NFW:           /* /NOT-FORWARDABLE */
+              case KRB_I_NPR:           /* /NOT-PROXIABLE */
+              case KRB_I_VB:            /* /VERBOSE */
+              case KRB_I_BR:            /* /BRIEF */
+              case KRB_I_K4:            /* /KERBEROS4 */
+              case KRB_I_NK4:           /* /NO-KERBEROS4 */
+              case KRB_I_POP:           /* /POPUP */
+              case KRB_I_NAD:           /* /NO-ADDRESSES */
+                if (getval) {
+                    printf("?This switch does not take a value\n");
+                    rc = -9;
+                    goto kerbx;
+                }
+                switch (n) {
+                  case KRB_I_NPA:
+                    pv[KRB_I_PA].ival = 0;
+                    break;
+                  case KRB_I_NFW:
+                    pv[KRB_I_FW].ival = 0;
+                    break;
+                  case KRB_I_NPR:
+                    pv[KRB_I_PR].ival = 0;
+                    break;
+                  case KRB_I_VB:
+                    pv[KRB_I_BR].ival = 0;
+                    break;
+                  case KRB_I_NK4:
+                    pv[KRB_I_K4].ival = 0;
+                    break;
+                  default:
+                    pv[n].ival = 1;
+                }
+                break;
+
+                /* These do take arguments */
+
+              case KRB_I_RB:            /* /RENEWABLE:<minutes> */
+                pv[n].ival = 0;
+                if (!getval) break;
+                if ((rc = cmnum("Minutes",ckitoa(krb5_init.renewable),
+                                10,&y, xxstring)) < 0)
+                  goto kerbx;
+                pv[n].ival = y;
+                break;
+
+              case KRB_I_LF:            /* /LIFETIME:<minutes> */
+                pv[n].ival = 0;
+                /* Default is previous value */
+                sprintf(tmpbuf,"%d",    /* SAFE */
+                        kv == 4 ?
+                        krb4_init.lifetime :
+                        krb5_init.lifetime
+                        );
+                if (!getval) break;
+                if ((rc = cmnum("Minutes",tmpbuf,10,&y, xxstring)) < 0)
+                  goto kerbx;
+                pv[n].ival = y;
+                break;
+
+              case KRB_I_PD:            /* /POSTDATE:<timestamp> */
+                if (pv[n].sval) {
+                    free(pv[n].sval);
+                    pv[n].sval = NULL;
+                }
+                if (!getval) break;
+                if ((rc = cmdate("date-time","",&s,0,xxstring)) < 0)
+                  goto kerbx;
+                makestr(&(pv[n].sval),s);
+                break;
+
+              case KRB_I_SR:            /* /SERVICE:<name> */
+                if (pv[n].sval) {
+                    free(pv[n].sval);
+                    pv[n].sval = NULL;
+                }
+                if (!getval) break;
+                if ((rc = cmfld("Service-name","",&s,xxstring)) < 0)
+                  goto kerbx;
+                makestr(&(pv[n].sval),s);
+                break;
+
+              case KRB_I_RL:            /* /REALM:<name> */
+                if (pv[n].sval) {
+                    free(pv[n].sval);
+                    pv[n].sval = NULL;
+                }
+                if (!getval) break;
+                if (kv == 4)
+                  p = krb4_d_realm ? krb4_d_realm : "";
+                else
+                  p = krb5_d_realm ? krb5_d_realm : "";
+                if ((rc = cmfld("Realm",p,&s,xxstring)) < 0)
+                  goto kerbx;
+                makestr(&(pv[n].sval),s);
+                break;
+
+              case KRB_I_IN:            /* /INSTANCE:<name> */
+                if (pv[n].sval) {
+                    free(pv[n].sval);
+                    pv[n].sval = NULL;
+                }
+                if (!getval) break;
+                if (kv == 4)
+                    p = krb4_d_instance ? krb4_d_instance : "";
+                else
+                    p = krb5_d_instance ? krb5_d_instance : "";
+                if ((rc = cmfld("Instance",p,&s,xxstring)) < 0)
+                  goto kerbx;
+                makestr(&(pv[n].sval),s);
+                break;
+
+              case KRB_I_PW:            /* /PASSWORD:<password> */
+                debok = 0;
+                if (pv[n].sval) {
+                    free(pv[n].sval);
+                    pv[n].sval = NULL;
+                }
+                if (!getval) break;
+                if ((rc = cmfld("Password","",&s,xxstring)) < 0)
+                  if (rc != -3)
+                    goto kerbx;
+                makestr(&(pv[n].sval),s);
+                break;
+
+              case KRB_I_ADR:           /* /ADDRESSES:{<address-list>} */
+                if (pv[n].sval) {
+                    free(pv[n].sval);
+                    pv[n].sval = NULL;
+                }
+                if (!getval) break;
+                if ((rc = cmfld("List of IP addresses","",&s,xxstring)) < 0)
+                  goto kerbx;
+                makelist(s,tmpaddrs,KRB5_NUM_OF_ADDRS);
+                for (i = 0; i < KRB5_NUM_OF_ADDRS && tmpaddrs[i]; i++) {
+                    if (inet_addr(tmpaddrs[i]) == 0xffffffff) {
+                        printf("invalid ip address: %s\n",tmpaddrs[i]);
+                        rc = -9;
+                        goto kerbx;
+                    }
+                }
+                pv[KRB_I_NAD].ival = 0;
+                break;
+
+              default:
+                printf("?Unexpected switch value - internal error\n");
+                rc = -9;
+                goto kerbx;
+            }
+        }
+        if (cmresult.fcode != _CMFLD) {
+            printf("?Unexected result - internal error\n");
+            rc = -9;
+            goto kerbx;
+        }
+        /* cmresult.sresult may be of the form PRINCIPAL@REALM */
+        i = ckindex("@",cmresult.sresult,0,0,0);
+        if (i != 0) {
+            makestr(&tmprealm,&cmresult.sresult[i]);
+            cmresult.sresult[i-1] = '\0';
+        }
+        makestr(&tmpprinz,cmresult.sresult); /* Principal (user) */
+
+        if ((rc = cmcfm()) < 0) {       /* Now get confirmation */
+            if (rc == -3) {
+                printf("?Principal name required\n");
+            }
+            goto kerbx;
+        }
+        if (!tmpprinz || !tmpprinz[0]) {
+            printf("?Principal name required\n");
+            goto kerbx;
+        }
+        if (!pv[KRB_I_RN].ival && !pv[KRB_I_VA].ival) {
+            /* Don't use a password if Validating or Renewing */
+            if (pv[KRB_I_PW].sval) {    /* If they gave a /PASSWORD switch */
+                makestr(&tmppswd,pv[KRB_I_PW].sval); /* use this value */
+            }
+#ifdef COMMENT
+            /* Password prompting has been moved to ck_krb[45]_initTGT() */
+            else {                      /* Otherwise must prompt for it */
+                char prmpt[80];
+                if (pv[KRB_I_RL].sval)
+                  sprintf(prmpt,"%s@%s's Password: ",
+                          tmpprinz,pv[KRB_I_RL].sval);
+                else if (tmprealm)
+                  sprintf(prmpt,"%s@%s's Password: ",
+                          tmpprinz,tmprealm);
+                else
+                  sprintf(prmpt,"%s's Password: ",tmpprinz);
+#ifdef OS2
+                if (pv[KRB_I_POP].ival) {
+                    char passwd[80]="";
+                    readpass(prmpt,passwd,80);
+                    makestr(&tmppswd,passwd);
+                    memset(passwd,0,80);
+                } else
+#endif /* OS2 */
+                {
+#ifdef CK_RECALL
+                    on_recall = 0;
+#endif /* CK_RECALL */
+                    cmsavp(psave,PROMPTL); /* Save old prompt */
+                    cmsetp(prmpt);      /* Make new prompt */
+                    concb((char)escape); /* Put console in cbreak mode */
+                    cmini(0);           /* and no-echo mode */
+                    /* Issue prompt if at top level */
+                    if (pflag) prompt(xxstring);
+                    cmres();            /* Reset the parser */
+                    for (rc = -1; rc < 0; ) { /* Prompt till they answer */
+                        /* Get a literal line of text */
+                        rc = cmtxt("","",&s,NULL);
+                        cmres();        /* Reset the parser again */
+                    }
+                    makestr(&tmppswd,s);
+                    printf("\n");       /* Echo a CRLF */
+                    cmsetp(psave);      /* Restore original prompt */
+                }
+            }
+            x = 0;                      /* Check for password */
+            if (tmppswd)
+              if (*tmppswd)
+                x = 1;
+            if (!x) {
+                printf("?Password required\n");
+                goto kerbx;
+            }
+#endif /* COMMENT */
+        }
+    } else if (kv == 5 && tmp_action == KRB_A_LC) { /* LIST-CREDENTIALS */
+        tmp_klc = 0;
+        while (1) {
+            if ((x = cmkey(klctab,nklctab,"Switch","",xxstring)) < 0) {
+                if (x == -3) {
+                    if ((rc = cmcfm()) < 0)
+                      goto kerbx;
+                    else
+                      break;
+                } else {
+                    rc = x;
+                    goto kerbx;
+                }
+            }
+            tmp_klc |= x;
+        }
+    } else if ((rc = cmcfm()) < 0)      /* DESTROY, just confirm */
+        goto kerbx;
+
+/* Done - Move confirmed data to final locations */
+
+    krb_action = tmp_action;            /* Action requested */
+    krb_op.version = kv;                /* Kerberos version */
+    krb_op.cache = tmpcache;            /* Cache file */
+    tmpcache = NULL;                    /* So we don't free it */
+
+    switch (krb_action) {
+      case KRB_A_IN:                    /* INITIALIZE */
+        if (kv == 5) {
+            krb5_init.forwardable = pv[KRB_I_FW].ival;
+            krb5_init.proxiable   = pv[KRB_I_PR].ival;
+            krb5_init.lifetime    = pv[KRB_I_LF].ival;
+            krb5_init.renew       = pv[KRB_I_RN].ival;
+            krb5_init.renewable   = pv[KRB_I_RB].ival;
+            krb5_init.validate    = pv[KRB_I_VA].ival;
+
+            /* Here we just reassign the pointers and then set them to NULL */
+            /* so they won't be freed below. */
+
+            krb5_init.postdate = pv[KRB_I_PD].sval; pv[KRB_I_PD].sval = NULL;
+            krb5_init.service  = pv[KRB_I_SR].sval; pv[KRB_I_SR].sval = NULL;
+            if (pv[KRB_I_RL].sval) {
+                krb5_init.realm  = pv[KRB_I_RL].sval; pv[KRB_I_RL].sval = NULL;
+            } else if (tmprealm) {
+                krb5_init.realm = tmprealm; tmprealm = NULL;
+            }
+            if (pv[KRB_I_IN].sval) {
+                krb5_init.instance = pv[KRB_I_IN].sval;
+                pv[KRB_I_IN].sval = NULL;
+            } else if ( tmpinst ) {
+                krb5_init.instance = tmpinst;
+                tmpinst = NULL;
+            }
+            if (tmpprinz) {
+                krb5_init.principal = tmpprinz;
+                tmpprinz = NULL;
+            }
+            krb5_init.password = tmppswd;
+            tmppswd = NULL;
+
+            krb5_init.getk4 = pv[KRB_I_K4].ival;
+            if (krb5_init.getk4) {
+                krb4_init.lifetime = pv[KRB_I_LF].ival;
+                if (krb5_init.realm)
+                    makestr(&krb4_init.realm,krb5_init.realm);
+                krb4_init.preauth  = krb4_d_preauth;
+                krb4_init.verbose  = pv[KRB_I_BR].ival ? 0 : 1;
+                if (krb5_init.principal)
+                    makestr(&krb4_init.principal,krb5_init.principal);
+                if (krb5_init.principal)
+                    makestr(&krb4_init.password,krb5_init.password);
+            }
+            krb5_init.no_addresses = pv[KRB_I_NAD].ival;
+            if (tmpaddrs[0]) {
+                for (i = 0; i < KRB5_NUM_OF_ADDRS; i++) {
+                    if (krb5_init.addrs[i]) {
+                        free(krb5_init.addrs[i]);
+                        krb5_init.addrs[i] = NULL;
+                    }
+                    krb5_init.addrs[i] = tmpaddrs[i];
+                    tmpaddrs[i] = NULL;
+                }
+            }
+        } else if (kv == 4) {           /* Same deal for Kerberos 4 */
+            krb4_init.lifetime = pv[KRB_I_LF].ival;
+            if (pv[KRB_I_RL].sval) {
+                krb4_init.realm  = pv[KRB_I_RL].sval;
+                pv[KRB_I_RL].sval = NULL;
+            } else if ( tmprealm ) {
+                krb4_init.realm  = tmprealm;
+                tmprealm = NULL;
+            }
+            if (pv[KRB_I_IN].sval) {
+                krb4_init.instance = pv[KRB_I_IN].sval;
+                pv[KRB_I_IN].sval = NULL;
+            } else if ( tmpinst ) {
+                krb4_init.instance = tmpinst;
+                tmpinst = NULL;
+            }
+            krb4_init.preauth  = pv[KRB_I_PA].ival;
+            krb4_init.verbose  = pv[KRB_I_BR].ival ? 0 : 1;
+
+            if (tmpprinz) {
+                krb4_init.principal = tmpprinz;
+                tmpprinz = NULL;
+            }
+            krb4_init.password = tmppswd;
+            tmppswd = NULL;
+        }
+        break;
+      case KRB_A_LC:                    /* List Credentials */
+        krb5_lc.encryption = tmp_klc & XYKLCEN;
+        krb5_lc.flags = tmp_klc & XYKLCFL;
+        krb5_lc.addr  = tmp_klc & XYKLCAD;
+        break;
+    }
+
+/* Common exit - Free temporary storage */
+
+  kerbx:
+    for (i = 0; i <= KRB_I_MAX; i++) {  /* Free malloc'd switch data */
+        if (pv[i].sval)
+          free(pv[i].sval);
+    }
+    for (i = 0; i < KRB5_NUM_OF_ADDRS; i++) {
+        if (tmpaddrs[i])
+          free(tmpaddrs[i]);
+    }
+    if (tmpprinz) free(tmpprinz);       /* And these too. */
+    if (tmppswd)  free(tmppswd);
+    if (tmpcache) free(tmpcache);
+    if (tmprealm) free(tmprealm);
+    if (tmpinst)  free(tmpinst);
+
+    return(rc);                         /* Return the return code */
+}
+#endif /* CK_KERBEROS */
+
+#ifdef CK_LOGIN
+int
+#ifdef CK_ANSIC
+ckxlogin(CHAR * userid, CHAR * passwd, CHAR * acct, int promptok)
+#else /* CK_ANSIC */
+ckxlogin(userid, passwd, acct, promptok)
+  CHAR * userid; CHAR * passwd; CHAR * acct; int promptok;
+#endif /* CK_ANSIC */
+/* ckxlogin */ {
+#ifdef CK_RECALL
+    extern int on_recall;               /* around Password prompting */
+#endif /* CK_RECALL */
+#ifdef CK_PAM
+    extern int guest;
+#endif /* CK_PAM */
+    int rprompt = 0;                    /* Restore prompt */
+#ifdef CKSYSLOG
+    int savlog;
+#endif /* CKSYSLOG */
+
+    extern int what, srvcdmsg;
+
+    int x = 0, ok = 0, rc = 0;
+    CHAR * _u = NULL, * _p = NULL, * _a = NULL;
+
+    debug(F111,"ckxlogin userid",userid,promptok);
+    debug(F110,"ckxlogin passwd",passwd,0);
+
+    isguest = 0;                        /* Global "anonymous" flag */
+
+    if (!userid) userid = (CHAR *)"";
+    if (!passwd) passwd = (CHAR *)"";
+
+    debug(F111,"ckxlogin userid",userid,what);
+
+#ifdef CK_RECALL
+    on_recall = 0;
+#endif /* CK_RECALL */
+
+#ifdef CKSYSLOG
+    savlog = ckxsyslog;                 /* Save and turn off syslogging */
+#endif /* CKSYSLOG */
+
+    if ((!*userid || !*passwd) &&       /* Need to prompt for missing info */
+        promptok) {
+        cmsavp(psave,PROMPTL);          /* Save old prompt */
+        debug(F110,"ckxlogin saved",psave,0);
+        rprompt = 1;
+    }
+    if (!*userid) {
+        if (!promptok)
+          return(0);
+        cmsetp("Username: ");           /* Make new prompt */
+        concb((char)escape);            /* Put console in cbreak mode */
+        cmini(1);
+
+/* Flush typeahead */
+
+#ifdef IKS_OPTION
+        debug(F101,
+              "ckxlogin TELOPT_SB(TELOPT_KERMIT).kermit.me_start",
+              "",
+              TELOPT_SB(TELOPT_KERMIT).kermit.me_start
+              );
+#endif /* IKS_OPTION */
+
+        while (ttchk() > 0) {
+            x = ttinc(0);
+            debug(F101,"ckxlogin flush user x","",x);
+            if (x < 0)
+              doexit(GOOD_EXIT,0);      /* Connection lost */
+#ifdef TNCODE
+            if (sstelnet) {
+                if (x == IAC) {
+                    x = tn_doop((CHAR)(x & 0xff),ckxech,ttinc);
+                    debug(F101,"ckxlogin user tn_doop","",x);
+#ifdef IKS_OPTION
+                    debug(F101,
+                      "ckxlogin user TELOPT_SB(TELOPT_KERMIT).kermit.me_start",
+                      "",
+                      TELOPT_SB(TELOPT_KERMIT).kermit.me_start
+                      );
+#endif /* IKS_OPTION */
+
+                    if (x < 0)
+                      goto XCKXLOG;
+                    switch (x) {
+                      case 1: ckxech = 1; break; /* Turn on echoing */
+                      case 2: ckxech = 0; break; /* Turn off echoing */
+#ifdef IKS_OPTION
+                      case 4:                    /* IKS event */
+                        if (!TELOPT_SB(TELOPT_KERMIT).kermit.me_start)
+                          break;                 /* else fall thru... */
+#endif /* IKS_OPTION */
+                      case 6:                    /* Logout */
+                        goto XCKXLOG;
+                    }
+                }
+            }
+#endif /* TNCODE */
+        }
+        if (pflag) prompt(xxstring);    /* Issue prompt if at top level */
+        cmres();                        /* Reset the parser */
+        for (x = -1; x < 0;) {          /* Prompt till they answer */
+            /* Get a literal line of text */
+            x=cmtxt("Your username, or \"ftp\", or \"anonymous\"","",&s,NULL);
+            if (x == -4 || x == -10) {
+                printf("\r\n%sLogin cancelled\n",
+                      x == -10 ? "Timed out: " : "");
+#ifdef CKSYSLOG
+                ckxsyslog = savlog;
+#endif /* CKSYSLOG */
+                doexit(GOOD_EXIT,0);
+            }
+            if (sstate)                 /* Did a packet come instead? */
+              goto XCKXLOG;
+            cmres();                    /* Reset the parser again */
+        }
+        if ((_u = (CHAR *)malloc((int)strlen(s) + 1)) == NULL) {
+            printf("?Internal error: malloc\n");
+            goto XCKXLOG;
+        } else {
+            strcpy((char *)_u,s);       /* safe */
+            userid = _u;
+        }
+    }
+    ok = zvuser((char *)userid);        /* Verify username */
+    debug(F111,"ckxlogin zvuser",userid,ok);
+
+    if (!*passwd && promptok
+#ifdef CK_PAM
+        && guest
+#endif /* CK_PAM */
+        ) {
+        char prmpt[80];
+
+#ifdef CKSYSLOG
+        savlog = ckxsyslog;             /* Save and turn off syslogging */
+        ckxsyslog = 0;
+#endif /* CKSYSLOG */
+
+/* Flush typeahead again */
+
+        while (ttchk() > 0) {
+            x = ttinc(0);
+            debug(F101,"ckxlogin flush user x","",x);
+#ifdef TNCODE
+            if (sstelnet) {
+                if (x == IAC) {
+                    x = tn_doop((CHAR)(x & 0xff),ckxech,ttinc);
+                    debug(F101,"ckxlogin pass tn_doop","",x);
+#ifdef IKS_OPTION
+                    debug(F101,
+                      "ckxlogin pass TELOPT_SB(TELOPT_KERMIT).kermit.me_start",
+                      "",
+                      TELOPT_SB(TELOPT_KERMIT).kermit.me_start
+                      );
+#endif /* IKS_OPTION */
+                    if (x < 0)
+                      goto XCKXLOG;
+                    switch (x) {
+                      case 1: ckxech = 1; break; /* Turn on echoing */
+                      case 2: ckxech = 0; break; /* Turn off echoing */
+                      case 4:                    /* IKS event */
+                        if (!TELOPT_SB(TELOPT_KERMIT).kermit.me_start)
+                          break;                 /* else fall thru... */
+                      case 6:                    /* Logout */
+                        goto XCKXLOG;
+                    }
+                }
+            }
+#endif /* TNCODE */
+        }
+        if (!strcmp((char *)userid,"anonymous") ||
+            !strcmp((char *)userid,"ftp")) {
+            if (!ok)
+              goto XCKXLOG;
+            ckstrncpy(prmpt,"Enter e-mail address as Password: ",80);
+        } else if (*userid && strlen((char *)userid) < 60) {
+#ifdef NT
+            extern CHAR * pReferenceDomainName;
+            if (pReferenceDomainName)
+              ckmakxmsg(prmpt,
+                       80,
+                       "Enter ",
+                       pReferenceDomainName,
+                       "\\\\",
+                       userid,
+                       "'s Password: ",
+                       NULL,NULL,NULL,NULL,NULL,NULL,NULL
+                       );
+            else
+#endif /* NT */
+              ckmakmsg(prmpt,80,"Enter ",(char *)userid,"'s Password: ",NULL);
+        } else
+          ckstrncpy(prmpt,"Enter Password: ",80);
+        cmsetp(prmpt);                  /* Make new prompt */
+        concb((char)escape);            /* Put console in cbreak mode */
+        if (strcmp((char *)userid,"anonymous") &&
+            strcmp((char *)userid,"ftp")) { /* and if not anonymous */
+            debok = 0;
+            cmini(0);                   /* and no-echo mode */
+        } else {
+            cmini(1);
+        }
+        if (pflag) prompt(xxstring);    /* Issue prompt if at top level */
+        cmres();                        /* Reset the parser */
+        for (x = -1; x < 0;) {          /* Prompt till they answer */
+            x = cmtxt("","",&s,NULL);   /* Get a literal line of text */
+            if (x == -4 || x == -10) {
+                printf("\r\n%sLogin cancelled\n",
+                      x == -10 ? "Timed out: " : "");
+#ifdef CKSYSLOG
+                ckxsyslog = savlog;
+#endif /* CKSYSLOG */
+                doexit(GOOD_EXIT,0);
+            }
+            if (sstate)                 /* In case of a Kermit packet */
+              goto XCKXLOG;
+            cmres();                    /* Reset the parser again */
+        }
+        printf("\r\n");                 /* Echo a CRLF */
+        if ((_p = (CHAR *)malloc((int)strlen(s) + 1)) == NULL) {
+            printf("?Internal error: malloc\n");
+            goto XCKXLOG;
+        } else {
+            strcpy((char *)_p,s);       /* safe */
+            passwd = _p;
+        }
+    }
+#ifdef CK_PAM
+    else {
+        cmres();                        /* Reset the parser */
+
+        /* We restore the prompt now because the PAM engine will call  */
+        /* readpass() which will overwrite psave. */
+        if (rprompt) {
+            cmsetp(psave);              /* Restore original prompt */
+            debug(F110,"ckxlogin restored",psave,0);
+            rprompt = 0;
+        }
+    }
+#endif /* CK_PAM */
+
+#ifdef CKSYSLOG
+    ckxsyslog = savlog;
+#endif /* CKSYSLOG */
+
+    if (ok) {
+        ok = zvpass((char *)passwd);    /* Check password */
+        debug(F101,"ckxlogin zvpass","",ok);
+    }
+
+    if (ok > 0 && isguest) {
+#ifndef NOPUSH
+        nopush = 1;
+#endif /* NOPUSH */
+        srvcdmsg = 1;
+    }
+    rc = ok;                            /* Set the return code */
+    if ((char *)uidbuf != (char *)userid)
+      ckstrncpy(uidbuf,(char *)userid,UIDBUFLEN); /* Remember username */
+
+  XCKXLOG:                              /* Common exit */
+#ifdef CKSYSLOG
+    ckxsyslog = savlog;                 /* In case of GOTO above */
+#endif /* CKSYSLOG */
+    if (rprompt) {
+        cmsetp(psave);                  /* Restore original prompt */
+        debug(F110,"ckxlogin restored",psave,0);
+    }
+    if (_u || _p || _a) {
+        if (_u) free(_u);
+        if (_p) free(_p);
+        if (_a) free(_a);
+    }
+    return(rc);
+}
+
+int
+ckxlogout() {
+    doexit(GOOD_EXIT,0);                /* doexit calls zvlogout */
+    return(0);                          /* not reached */
+}
+#endif /* CK_LOGIN */
+
+#endif /* NOICP */
index d705a33..6b6c6e7 100644 (file)
@@ -1,3 +1,4 @@
 010_makefile-destdir-support.patch
 020_man-hyphen-quoting.patch
 030_fix-if-else.patch
+040_pam-password-prompting.patch
index 298c48e..4648104 100644 (file)
--- a/ckufio.c
+++ b/ckufio.c
@@ -490,6 +490,9 @@ extern char * anonroot;
 static char guestpass[GUESTPASS] = { NUL, NUL }; /* Anonymous "password" */
 static int logged_in = 0;               /* Set when user is logged in */
 static int askpasswd = 0;               /* Have OK user, must ask for passwd */
+#ifdef CK_PAM
+extern int gotemptypasswd;
+#endif /* CK_PAM */
 #endif /* CK_LOGIN */
 
 #ifdef CKROOT
@@ -8043,8 +8046,12 @@ zvpass(p) char *p; {
             }
         }
         debug(F110,"zvpass","calling pam_authenticate",0);
-        if (*p)
-         pam_pw = p;
+        if (*p
+#ifdef CK_LOGIN
+           || gotemptypasswd
+#endif /* CK_LOGIN */
+           )
+           pam_pw = p;
         if ((pam_status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
             reply = pam_strerror(pamh, pam_status);
             debug(F110,"zvpass PAM failure",reply,0);
index 4268ef1..8801478 100644 (file)
--- a/ckuus7.c
+++ b/ckuus7.c
@@ -98,6 +98,12 @@ extern int g_matchdot, hints, xcmdsrc, rcdactive;
 
 extern char * k_info_dir;
 
+#ifdef CK_LOGIN
+#ifdef CK_PAM
+int gotemptypasswd = 0;   /* distinguish empty passwd from none given */
+#endif /* CK_PAM */
+#endif /* CK_LOGIN */
+
 #ifndef NOSPL
 extern int nmac;
 extern struct mtab *mactab;
@@ -14656,9 +14662,9 @@ ckxlogin(userid, passwd, acct, promptok)
 #ifdef CK_RECALL
     extern int on_recall;               /* around Password prompting */
 #endif /* CK_RECALL */
-#ifdef CK_PAM
+#ifdef COMMENT
     extern int guest;
-#endif /* CK_PAM */
+#endif /* COMMENT */
     int rprompt = 0;                    /* Restore prompt */
 #ifdef CKSYSLOG
     int savlog;
@@ -14774,9 +14780,9 @@ ckxlogin(userid, passwd, acct, promptok)
     debug(F111,"ckxlogin zvuser",userid,ok);
 
     if (!*passwd && promptok
-#ifdef CK_PAM
+#ifdef COMMENT
         && guest
-#endif /* CK_PAM */
+#endif /* COMMENT */
         ) {
         char prmpt[80];
 
@@ -14852,6 +14858,9 @@ ckxlogin(userid, passwd, acct, promptok)
         if (pflag) prompt(xxstring);    /* Issue prompt if at top level */
         cmres();                        /* Reset the parser */
         for (x = -1; x < 0;) {          /* Prompt till they answer */
+#ifdef CK_PAM
+           gotemptypasswd=0;
+#endif /* CK_PAM */
             x = cmtxt("","",&s,NULL);   /* Get a literal line of text */
             if (x == -4 || x == -10) {
                 printf("\r\n%sLogin cancelled\n",
@@ -14861,6 +14870,10 @@ ckxlogin(userid, passwd, acct, promptok)
 #endif /* CKSYSLOG */
                 doexit(GOOD_EXIT,0);
             }
+#ifdef CK_PAM
+           if(!*s)
+               gotemptypasswd=1;
+#endif /* CK_PAM */
             if (sstate)                 /* In case of a Kermit packet */
               goto XCKXLOG;
             cmres();                    /* Reset the parser again */
@@ -14895,6 +14908,12 @@ ckxlogin(userid, passwd, acct, promptok)
     if (ok) {
         ok = zvpass((char *)passwd);    /* Check password */
         debug(F101,"ckxlogin zvpass","",ok);
+#ifdef CK_PAM
+    } else {
+       /* Fake pam password failure for nonexistent users */
+       sleep(1);
+       printf("Authentication failure\n");
+#endif
     }
 
     if (ok > 0 && isguest) {