From 5bbbe0ad1ea84675d10762abb5d6698cd830de18 Mon Sep 17 00:00:00 2001 From: Ian Beckwith Date: Wed, 12 May 2010 01:20:52 +0100 Subject: [PATCH] applied 040_pam-password-prompting.patch --- .pc/040_pam-password-prompting.patch/.timestamp | 0 .pc/040_pam-password-prompting.patch/ckufio.c | 8310 ++++++++++++ .pc/040_pam-password-prompting.patch/ckuus7.c | 14933 ++++++++++++++++++++++ .pc/applied-patches | 1 + ckufio.c | 11 +- ckuus7.c | 27 +- 6 files changed, 23276 insertions(+), 6 deletions(-) create mode 100644 .pc/040_pam-password-prompting.patch/.timestamp create mode 100644 .pc/040_pam-password-prompting.patch/ckufio.c create mode 100644 .pc/040_pam-password-prompting.patch/ckuus7.c diff --git a/.pc/040_pam-password-prompting.patch/.timestamp b/.pc/040_pam-password-prompting.patch/.timestamp new file mode 100644 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 index 0000000..298c48e --- /dev/null +++ b/.pc/040_pam-password-prompting.patch/ckufio.c @@ -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 , + 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 +#include + +#ifdef MINIX2 +#undef MINIX +#undef CKSYSLOG +#include +#include +#define NOFILEH +#endif /* MINIX2 */ + +#ifdef MINIX +#include +#include +#include +#else +#ifdef POSIX +#include +#else +#ifdef SVR3 +#include +#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 +#else /* !XNDIR */ +#ifdef NDIR +#include +#else /* !NDIR, !XNDIR */ +#ifdef RTU +#include "/usr/lib/ndir.h" +#else /* !RTU, !NDIR, !XNDIR */ +#ifdef DIRENT +#ifdef SDIRENT +#include +#else +#include +#endif /* SDIRENT */ +#else +#include +#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 /* Password file for shell name */ +#ifdef CK_SRP +#include /* SRP Password file */ +#endif /* CK_SRP */ + +#ifdef HPUX10_TRUSTED +#include +#include +#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 /* if requested, */ +#include /* for extra fields required by */ +#else /* 88Open spec. */ +#ifdef UTIMEH /* or if requested */ +#include /* (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, . 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 +#include + +#else /* Not BSD44 */ + +#ifdef BSD4 /* BSD 4.3 and below */ +#define TIMESTAMP /* Can do file dates */ +#include /* Need this */ +#include /* 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 + +/* 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 +#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 /* File status */ +#else +#include +#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 /* */ +#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 +#endif /* NOFILEH */ + +#ifndef is68k /* Whether to include */ +#ifndef BSD41 /* All but a couple UNIXes have it. */ +#ifndef FT18 +#ifndef COHERENT +#include +#endif /* COHERENT */ +#endif /* FT18 */ +#endif /* BSD41 */ +#endif /* is68k */ + +#ifdef COHERENT +#ifdef _I386 +#include +#else +#include +#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 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 +#endif /* CK_SHADOW */ +#ifdef CK_PAM /* PAM... */ +#include +#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... , , , , ... + 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 /* For MAXPATHLEN */ +#endif /* BSD44 */ +#ifdef COHERENT +#include /* 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 */ +_PROTOTYP( FILE * fdopen, (int, char *) ); +#endif /* DCLFDOPEN */ + +#ifdef DCLPOPEN +/* popen() needs declaring because it's not declared in */ +_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 +#else /* RTAIX */ +#include +#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 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 +#include +#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 */ +#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 +#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 +#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 + . See the section for including 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 ".~~", where is argument name + (fn), and 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 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.~~" */ + 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, */ +#define OPENDIR + DIR *fd, *opendir(); + struct dirent *dirbuf; + struct dirent *readdir(); +#else /* !DIRENT */ +#ifdef LONGFN /* Old way, with opendir() */ +#define OPENDIR + DIR *fd, *opendir(); + struct direct *dirbuf; +#else /* !LONGFN */ + int fd; /* Old way, 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" */ + 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 /.txt lists every file in * and does not even open up the + subdirectories. However, "dir /rec /.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 index 0000000..4268ef1 --- /dev/null +++ b/.pc/040_pam-password-prompting.patch/ckuus7.c @@ -0,0 +1,14933 @@ +#include "ckcsym.h" + +/* C K U U S 7 -- "User Interface" for C-Kermit, part 7 */ + +/* + Authors: + Frank da Cruz , + The Kermit Project, Columbia University, New York City + Jeffrey E Altman + 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 +#endif /* TCPSOCKET */ +#endif /* VMS */ + +#ifdef OS2 +#ifndef NT +#define INCL_NOPM +#define INCL_VIO /* Needed for ckocon.h */ +#define INCL_DOSMODULEMGR +#include +#undef COMMENT +#else /* NT */ +#define APIRET ULONG +#include +#include +#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 */ + 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 */ + 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 */ + 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 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 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 */ +#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 +#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 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:""); +#endif /* OS2 */ + printf(" Anonymous Initfile: %s\r\n",anonfile?anonfile:""); + printf(" Anonymous Login: %d\r\n",ckxanon); + printf(" Anonymous Root: %s\r\n",anonroot?anonroot:""); +#endif /* CK_LOGIN */ + printf(" Bannerfile: %s\r\n",bannerfile?bannerfile:""); + printf(" CDfile: %s\r\n",cdmsgfile[0]?cdmsgfile[0]:""); + 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:""); + 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:""); + printf(" Initfile: %s\r\n",kermrc?kermrc:""); + 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:""); +#ifdef CK_LOGIN +#ifdef CKWTMP + printf(" Wtmplog: %d\r\n",ckxwtmp); + printf(" Wtmpfile: %s\r\n",wtmpfile?wtmpfile:""); +#endif /* CKWTMP */ +#endif /* CK_LOGIN */ + printf(" Xferfile: %s\r\n",xferfile?xferfile:""); + 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: */ + 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: */ + 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: */ + 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: */ + 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: */ + 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: */ + 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: */ + 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: */ + 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:{} */ + 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 */ diff --git a/.pc/applied-patches b/.pc/applied-patches index d705a33..6b6c6e7 100644 --- a/.pc/applied-patches +++ b/.pc/applied-patches @@ -1,3 +1,4 @@ 010_makefile-destdir-support.patch 020_man-hyphen-quoting.patch 030_fix-if-else.patch +040_pam-password-prompting.patch diff --git a/ckufio.c b/ckufio.c index 298c48e..4648104 100644 --- 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); diff --git a/ckuus7.c b/ckuus7.c index 4268ef1..8801478 100644 --- 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) { -- 2.11.0