| /* C K U F I O  --  Kermit file system support for UNIX, Aegis, and Plan 9 */ | 
 |  | 
 | #define CK_NONBLOCK                     /* See zoutdump() */ | 
 |  | 
 | #ifdef aegis | 
 | char *ckzv = "Aegis File support, 8.0.200, 4 Mar 2004"; | 
 | #else | 
 | #ifdef Plan9 | 
 | char *ckzv = "Plan 9 File support, 8.0.200, 4 Mar 2004"; | 
 | #else | 
 | char *ckzv = "UNIX File support, 8.0.200, 4 Mar 2004"; | 
 | #endif /* Plan9 */ | 
 | #endif /* aegis */ | 
 | /* | 
 |   Author: Frank da Cruz <fdc@columbia.edu>, | 
 |   Columbia University Academic Information Systems, New York City, | 
 |   and others noted in the comments below.  Note: CUCCA = Previous name of | 
 |   Columbia University Academic Information Systems. | 
 |  | 
 |   Copyright (C) 1985, 2004, | 
 |     Trustees of Columbia University in the City of New York. | 
 |     All rights reserved.  See the C-Kermit COPYING.TXT file or the | 
 |     copyright text in the ckcmai.c module for disclaimer and permissions. | 
 | */ | 
 |  | 
 | /* | 
 |   NOTE TO CONTRIBUTORS: This file, and all the other C-Kermit files, must be | 
 |   compatible with C preprocessors that support only #ifdef, #else, #endif, | 
 |   #define, and #undef.  Please do not use #if, logical operators, or other | 
 |   preprocessor features in any of the portable C-Kermit modules.  You can, | 
 |   of course, use these constructions in platform-specific modules where you | 
 |   know they are supported. | 
 | */ | 
 | /* Include Files */ | 
 |  | 
 | #ifdef MINIX2 | 
 | #define _MINIX | 
 | #endif /* MINIX2 */ | 
 |  | 
 | #include "ckcsym.h" | 
 | #include "ckcdeb.h" | 
 | #include "ckcasc.h" | 
 |  | 
 | #ifndef NOCSETS | 
 | #include "ckcxla.h" | 
 | #endif /* NOCSETS */ | 
 |  | 
 | #ifdef COMMENT | 
 | /* This causes trouble in C-Kermit 8.0.  I don't remember the original */ | 
 | /* reason for this being here but it must have been needed at the time... */ | 
 | #ifdef OSF13 | 
 | #ifdef CK_ANSIC | 
 | #ifdef _NO_PROTO | 
 | #undef _NO_PROTO | 
 | #endif /* _NO_PROTO */ | 
 | #endif /* CK_ANSIC */ | 
 | #endif /* OSF13 */ | 
 | #endif /* COMMENT */ | 
 |  | 
 | #include <errno.h> | 
 | #include <signal.h> | 
 |  | 
 | #ifdef MINIX2 | 
 | #undef MINIX | 
 | #undef CKSYSLOG | 
 | #include <limits.h> | 
 | #include <time.h> | 
 | #define NOFILEH | 
 | #endif /* MINIX2 */ | 
 |  | 
 | #ifdef MINIX | 
 | #include <limits.h> | 
 | #include <sys/types.h> | 
 | #include <time.h> | 
 | #else | 
 | #ifdef POSIX | 
 | #include <limits.h> | 
 | #else | 
 | #ifdef SVR3 | 
 | #include <limits.h> | 
 | #endif /* SVR3 */ | 
 | #endif /* POSIX */ | 
 | #endif /* MINIX */ | 
 | /* | 
 |   Directory Separator macros, to allow this module to work with both UNIX and | 
 |   OS/2: Because of ambiguity with the command line editor escape \ character, | 
 |   the directory separator is currently left as / for OS/2 too, because the | 
 |   OS/2 kernel also accepts / as directory separator.  But this is subject to | 
 |   change in future versions to conform to the normal OS/2 style. | 
 | */ | 
 | #ifndef DIRSEP | 
 | #define DIRSEP       '/' | 
 | #endif /* DIRSEP */ | 
 | #ifndef ISDIRSEP | 
 | #define ISDIRSEP(c)  ((c)=='/') | 
 | #endif /* ISDIRSEP */ | 
 |  | 
 | #ifdef SDIRENT | 
 | #define DIRENT | 
 | #endif /* SDIRENT */ | 
 |  | 
 | #ifdef XNDIR | 
 | #include <sys/ndir.h> | 
 | #else /* !XNDIR */ | 
 | #ifdef NDIR | 
 | #include <ndir.h> | 
 | #else /* !NDIR, !XNDIR */ | 
 | #ifdef RTU | 
 | #include "/usr/lib/ndir.h" | 
 | #else /* !RTU, !NDIR, !XNDIR */ | 
 | #ifdef DIRENT | 
 | #ifdef SDIRENT | 
 | #include <sys/dirent.h> | 
 | #else | 
 | #include <dirent.h> | 
 | #endif /* SDIRENT */ | 
 | #else | 
 | #include <sys/dir.h> | 
 | #endif /* DIRENT */ | 
 | #endif /* RTU */ | 
 | #endif /* NDIR */ | 
 | #endif /* XNDIR */ | 
 |  | 
 | #ifdef UNIX                             /* Pointer arg to wait() allowed */ | 
 | #define CK_CHILD                        /* Assume this is safe in all UNIX */ | 
 | #endif /* UNIX */ | 
 |  | 
 | extern int binary, recursive, stathack; | 
 | #ifdef CK_CTRLZ | 
 | extern int eofmethod; | 
 | #endif /* CK_CTRLZ */ | 
 |  | 
 | #include <pwd.h>                        /* Password file for shell name */ | 
 | #ifdef CK_SRP | 
 | #include <t_pwd.h>                      /* SRP Password file */ | 
 | #endif /* CK_SRP */ | 
 |  | 
 | #ifdef HPUX10_TRUSTED | 
 | #include <hpsecurity.h> | 
 | #include <prot.h> | 
 | #endif /* HPUX10_TRUSTED */ | 
 |  | 
 | #ifdef COMMENT | 
 | /* Moved to ckcdeb.h */ | 
 | #ifdef POSIX | 
 | #define UTIMEH | 
 | #else | 
 | #ifdef HPUX9 | 
 | #define UTIMEH | 
 | #endif /* HPUX9 */ | 
 | #endif /* POSIX */ | 
 | #endif /* COMMENT */ | 
 |  | 
 | #ifdef SYSUTIMEH                        /* <sys/utime.h> if requested,  */ | 
 | #include <sys/utime.h>                  /* for extra fields required by */ | 
 | #else                                   /* 88Open spec. */ | 
 | #ifdef UTIMEH                           /* or <utime.h> if requested */ | 
 | #include <utime.h>                      /* (SVR4, POSIX) */ | 
 | #ifndef BSD44 | 
 | #ifndef V7 | 
 | /* Not sure why this is here.  What it implies is that the code bracketed | 
 |    by SYSUTIMEH is valid on all platforms on which we support time  | 
 |    functionality.  But we know that is not true because the BSD44 and V7 | 
 |    platforms do not support sys/utime.h and the data structures which | 
 |    are defined in them.  Now this worked before because prior to today's | 
 |    changes the UTIMEH definition for BSD44 and V7 did not take place | 
 |    until after SYSUTIMEH was defined.  It also would not have been a  | 
 |    problem if the ordering of all the time blocks was consistent.  All but | 
 |    one of the blocks were BSD44, V7, SYSUTIMEH, <OTHER>.  That one case | 
 |    is where this problem was triggered. | 
 | */ | 
 | #define SYSUTIMEH                       /* Use this for both cases. */ | 
 | #endif /* V7 */ | 
 | #endif /* BSD44 */ | 
 | #endif /* UTIMEH */ | 
 | #endif /* SYSUTIMEH */ | 
 |  | 
 | #ifndef NOTIMESTAMP | 
 | #ifdef POSIX | 
 | #ifndef AS400 | 
 | #define TIMESTAMP | 
 | #endif /* AS400 */ | 
 | #endif /* POSIX */ | 
 |  | 
 | #ifdef BSD44                            /* BSD 4.4 */ | 
 | #ifndef TIMESTAMP | 
 | #define TIMESTAMP                       /* Can do file dates */ | 
 | #endif /* TIMESTAMP */ | 
 | #include <sys/time.h> | 
 | #include <sys/timeb.h> | 
 |  | 
 | #else  /* Not BSD44 */ | 
 |  | 
 | #ifdef BSD4                             /* BSD 4.3 and below */ | 
 | #define TIMESTAMP                       /* Can do file dates */ | 
 | #include <time.h>                       /* Need this */ | 
 | #include <sys/timeb.h>                  /* Need this if really BSD */ | 
 |  | 
 | #else  /* Not BSD 4.3 and below */ | 
 |  | 
 | #ifdef SVORPOSIX                        /* System V or POSIX */ | 
 | #ifndef TIMESTAMP | 
 | #define TIMESTAMP | 
 | #endif /* TIMESTAMP */ | 
 | #include <time.h> | 
 |  | 
 | /* void tzset(); (the "void" type upsets some compilers) */ | 
 | #ifndef IRIX60 | 
 | #ifndef ultrix | 
 | #ifndef CONVEX9 | 
 | /* ConvexOS 9.0, supposedly POSIX, has extern char *timezone(int,int) */ | 
 | #ifndef Plan9 | 
 | extern long timezone; | 
 | #endif /* Plan9 */ | 
 | #endif /* CONVEX9 */ | 
 | #endif /* ultrix */ | 
 | #endif /* IRIX60 */ | 
 | #endif /* SVORPOSIX */ | 
 | #endif /* BSD4 */ | 
 | #endif /* BSD44 */ | 
 |  | 
 | #ifdef COHERENT | 
 | #include <time.h> | 
 | #endif /* COHERENT */ | 
 |  | 
 | /* Is `y' a leap year? */ | 
 | #define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0) | 
 |  | 
 | /* Number of leap years from 1970 to `y' (not including `y' itself). */ | 
 | #define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400) | 
 |  | 
 | #endif /* NOTIMESTAMP */ | 
 |  | 
 | #ifdef CIE | 
 | #include <stat.h>                       /* File status */ | 
 | #else | 
 | #include <sys/stat.h> | 
 | #endif /* CIE */ | 
 |  | 
 | /* Macro to alleviate isdir() calls internal to this module */ | 
 |  | 
 | static struct stat STATBUF; | 
 | #define xisdir(a) ((stat(a,&STATBUF)==-1)?0:(S_ISDIR(STATBUF.st_mode)?1:0)) | 
 |  | 
 | extern char uidbuf[]; | 
 | extern int xferlog; | 
 | extern char * xferfile; | 
 | int iklogopen = 0; | 
 | static time_t timenow; | 
 |  | 
 | #define IKSDMSGLEN CKMAXPATH+512 | 
 |  | 
 | static char iksdmsg[IKSDMSGLEN]; | 
 |  | 
 | extern int local; | 
 |  | 
 | extern int server, en_mkd, en_cwd, en_del; | 
 |  | 
 | /* | 
 |   Functions (n is one of the predefined file numbers from ckcker.h): | 
 |  | 
 |    zopeni(n,name)   -- Opens an existing file for input. | 
 |    zopeno(n,name,attr,fcb) -- Opens a new file for output. | 
 |    zclose(n)        -- Closes a file. | 
 |    zchin(n,&c)      -- Gets the next character from an input file. | 
 |    zsinl(n,&s,x)    -- Read a line from file n, max len x, into address s. | 
 |    zsout(n,s)       -- Write a null-terminated string to output file, buffered. | 
 |    zsoutl(n,s)      -- Like zsout, but appends a line terminator. | 
 |    zsoutx(n,s,x)    -- Write x characters to output file, unbuffered. | 
 |    zchout(n,c)      -- Add a character to an output file, unbuffered. | 
 |    zchki(name)      -- Check if named file exists and is readable, return size. | 
 |    zchko(name)      -- Check if named file can be created. | 
 |    zchkspa(name,n)  -- Check if n bytes available to create new file, name. | 
 |    znewn(name,s)    -- Make a new unique file name based on the given name. | 
 |    zdelet(name)     -- Delete the named file. | 
 |    zxpand(string)   -- Expands the given wildcard string into a list of files. | 
 |    znext(string)    -- Returns the next file from the list in "string". | 
 |    zxrewind()       -- Rewind zxpand list. | 
 |    zxcmd(n,cmd)     -- Execute the command in a lower fork on file number n. | 
 |    zclosf()         -- Close input file associated with zxcmd()'s lower fork. | 
 |    zrtol(n1,n2)     -- Convert remote filename into local form. | 
 |    zltor(n1,n2)     -- Convert local filename into remote form. | 
 |    zchdir(dirnam)   -- Change working directory. | 
 |    zhome()          -- Return pointer to home directory name string. | 
 |    zkself()         -- Kill self, log out own job. | 
 |    zsattr(struct zattr *) -- Return attributes for file which is being sent. | 
 |    zstime(f, struct zattr *, x) - Set file creation date from attribute packet. | 
 |    zrename(old, new) -- Rename a file. | 
 |    zcopy(source,destination) -- Copy a file. | 
 |    zmkdir(path)       -- Create the directory path if possible | 
 |    zfnqfp(fname,len,fullpath) - Determine full path for file name. | 
 |    zgetfs(name)     -- return file size regardless of accessibility | 
 |    zchkpid(pid)     -- tell if PID is valid and active | 
 | */ | 
 |  | 
 | /* Kermit-specific includes */ | 
 | /* | 
 |   Definitions here supersede those from system include files. | 
 |   ckcdeb.h is included above. | 
 | */ | 
 | #include "ckcker.h"                     /* Kermit definitions */ | 
 | #include "ckucmd.h"                     /* For keyword tables */ | 
 | #include "ckuver.h"                     /* Version herald */ | 
 |  | 
 | char *ckzsys = HERALD; | 
 |  | 
 | /* | 
 |   File access checking ...  There are two calls to access() in this module. | 
 |   If this program is installed setuid or setgid on a Berkeley-based UNIX | 
 |   system that does NOT incorporate the saved-original-effective-uid/gid | 
 |   feature, then, when we have swapped the effective and original uid/gid, | 
 |   access() fails because it uses what it thinks are the REAL ids, but we have | 
 |   swapped them.  This occurs on systems where ANYBSD is defined, NOSETREU | 
 |   is NOT defined, and SAVEDUID is NOT defined.  So, in theory, we should take | 
 |   care of this situation like so: | 
 |  | 
 |     ifdef ANYBSD | 
 |     ifndef NOSETREU | 
 |     ifndef SAVEDUID | 
 |     define SW_ACC_ID | 
 |     endif | 
 |     endif | 
 |     endif | 
 |  | 
 |   But we can't test such a general scheme everywhere, so let's only do this | 
 |   when we know we have to... | 
 | */ | 
 | #ifdef NEXT                             /* NeXTSTEP 1.0-3.0 */ | 
 | #define SW_ACC_ID | 
 | #endif /* NEXT */ | 
 |  | 
 | /* Support for tilde-expansion in file and directory names */ | 
 |  | 
 | #ifdef POSIX | 
 | #define NAMEENV "LOGNAME" | 
 | #else | 
 | #ifdef BSD4 | 
 | #define NAMEENV "USER" | 
 | #else | 
 | #ifdef ATTSV | 
 | #define NAMEENV "LOGNAME" | 
 | #endif /* ATTSV */ | 
 | #endif /* BSD4 */ | 
 | #endif /* POSIX */ | 
 |  | 
 | /* Berkeley Unix Version 4.x */ | 
 | /* 4.1bsd support from Charles E Brooks, EDN-VAX */ | 
 |  | 
 | #ifdef BSD4 | 
 | #ifdef MAXNAMLEN | 
 | #define BSD42 | 
 | #endif /* MAXNAMLEN */ | 
 | #endif /* BSD4 */ | 
 |  | 
 | /* Definitions of some system commands */ | 
 |  | 
 | char *DELCMD = "rm -f ";                /* For file deletion */ | 
 | char *CPYCMD = "cp ";                   /* For file copy */ | 
 | char *RENCMD = "mv ";                   /* For file rename */ | 
 | char *PWDCMD = "pwd ";                  /* For saying where I am */ | 
 |  | 
 | #ifdef COMMENT | 
 | #ifdef HPUX10 | 
 | char *DIRCMD = "/usr/bin/ls -l ";       /* For directory listing */ | 
 | char *DIRCM2 = "/usr/bin/ls -l ";       /* For directory listing, no args */ | 
 | #else | 
 | char *DIRCMD = "/bin/ls -l ";           /* For directory listing */ | 
 | char *DIRCM2 = "/bin/ls -l ";           /* For directory listing, no args */ | 
 | #endif /* HPUX10 */ | 
 | #else | 
 | char *DIRCMD = "ls -l ";                /* For directory listing */ | 
 | char *DIRCM2 = "ls -l ";                /* For directory listing, no args */ | 
 | #endif /* COMMENT */ | 
 |  | 
 | char *TYPCMD = "cat ";                  /* For typing a file */ | 
 |  | 
 | #ifdef HPUX | 
 | char *MAILCMD = "mailx";                /* For sending mail */ | 
 | #else | 
 | #ifdef DGUX540 | 
 | char *MAILCMD = "mailx"; | 
 | #else | 
 | #ifdef UNIX | 
 | #ifdef CK_MAILCMD | 
 | char *MAILCMD = CK_MAILCMD;		/* CFLAGS override */ | 
 | #else | 
 | char *MAILCMD = "Mail";			/* Default */ | 
 | #endif /* CK_MAILCMD */ | 
 | #else | 
 | char *MAILCMD = ""; | 
 | #endif /* UNIX */ | 
 | #endif /* HPUX */ | 
 | #endif /* DGUX40 */ | 
 |  | 
 | #ifdef UNIX | 
 | #ifdef ANYBSD                           /* BSD uses lpr to spool */ | 
 | #ifdef DGUX540                          /* And DG/UX */ | 
 | char * PRINTCMD = "lp"; | 
 | #else | 
 | char * PRINTCMD = "lpr"; | 
 | #endif /* DGUX540 */ | 
 | #else                                   /* Sys V uses lp */ | 
 | #ifdef TRS16                            /* except for Tandy-16/6000... */ | 
 | char * PRINTCMD = "lpr"; | 
 | #else | 
 | char * PRINTCMD = "lp"; | 
 | #endif /* TRS16 */ | 
 | #endif /* ANYBSD */ | 
 | #else  /* Not UNIX */ | 
 | #define PRINTCMD "" | 
 | #endif /* UNIX */ | 
 |  | 
 | #ifdef FT18                             /* Fortune For:Pro 1.8 */ | 
 | #undef BSD4 | 
 | #endif /* FT18 */ | 
 |  | 
 | #ifdef BSD4 | 
 | char *SPACMD = "pwd ; df .";            /* Space in current directory */ | 
 | #else | 
 | #ifdef FT18 | 
 | char *SPACMD = "pwd ; du ; df ."; | 
 | #else | 
 | char *SPACMD = "df "; | 
 | #endif /* FT18 */ | 
 | #endif /* BSD4 */ | 
 |  | 
 | char *SPACM2 = "df ";                   /* For space in specified directory */ | 
 |  | 
 | #ifdef FT18 | 
 | #define BSD4 | 
 | #endif /* FT18 */ | 
 |  | 
 | #ifdef BSD4 | 
 | char *WHOCMD = "finger "; | 
 | #else | 
 | char *WHOCMD = "who "; | 
 | #endif /* BSD4 */ | 
 |  | 
 | /* More system-dependent includes, which depend on symbols defined */ | 
 | /* in the Kermit-specific includes.  Oh what a tangled web we weave... */ | 
 |  | 
 | #ifdef COHERENT                         /* <sys/file.h> */ | 
 | #define NOFILEH | 
 | #endif /* COHERENT */ | 
 |  | 
 | #ifdef MINIX | 
 | #define NOFILEH | 
 | #endif /* MINIX */ | 
 |  | 
 | #ifdef aegis | 
 | #define NOFILEH | 
 | #endif /* aegis */ | 
 |  | 
 | #ifdef unos | 
 | #define NOFILEH | 
 | #endif /* unos */ | 
 |  | 
 | #ifndef NOFILEH | 
 | #include <sys/file.h> | 
 | #endif /* NOFILEH */ | 
 |  | 
 | #ifndef is68k                           /* Whether to include <fcntl.h> */ | 
 | #ifndef BSD41                           /* All but a couple UNIXes have it. */ | 
 | #ifndef FT18 | 
 | #ifndef COHERENT | 
 | #include <fcntl.h> | 
 | #endif /* COHERENT */ | 
 | #endif /* FT18  */ | 
 | #endif /* BSD41 */ | 
 | #endif /* is68k */ | 
 |  | 
 | #ifdef COHERENT | 
 | #ifdef _I386 | 
 | #include <fcntl.h> | 
 | #else | 
 | #include <sys/fcntl.h> | 
 | #endif /* _I386 */ | 
 | #endif /* COHERENT */ | 
 |  | 
 | extern int inserver;			/* I am IKSD */ | 
 | int guest = 0;                          /* Anonymous user */ | 
 |  | 
 | #ifdef IKSD | 
 | extern int isguest; | 
 | extern char * anonroot; | 
 | #endif /* IKSD */ | 
 |  | 
 | #ifdef CK_LOGIN | 
 | #define GUESTPASS 256 | 
 | static char guestpass[GUESTPASS] = { NUL, NUL }; /* Anonymous "password" */ | 
 | static int logged_in = 0;               /* Set when user is logged in */ | 
 | static int askpasswd = 0;               /* Have OK user, must ask for passwd */ | 
 | #endif /* CK_LOGIN */ | 
 |  | 
 | #ifdef CKROOT | 
 | static char ckroot[CKMAXPATH+1] = { NUL, NUL }; | 
 | static int ckrootset = 0; | 
 | int ckrooterr = 0; | 
 | #endif /* CKROOT */ | 
 |  | 
 | _PROTOTYP( VOID ignorsigs, (void) ); | 
 | _PROTOTYP( VOID restorsigs, (void) ); | 
 |  | 
 | /* | 
 |   Change argument to "(const char *)" if this causes trouble. | 
 |   Or... if it causes trouble, then maybe it was already declared | 
 |   in a header file after all, so you can remove this prototype. | 
 | */ | 
 | #ifndef NDGPWNAM /* If not defined No Declare getpwnam... */ | 
 | #ifndef _POSIX_SOURCE | 
 | #ifndef NEXT | 
 | #ifndef SVR4 | 
 | /* POSIX <pwd.h> already gave prototypes for these. */ | 
 | #ifdef IRIX40 | 
 | _PROTOTYP( struct passwd * getpwnam, (const char *) ); | 
 | #else | 
 | #ifdef IRIX51 | 
 | _PROTOTYP( struct passwd * getpwnam, (const char *) ); | 
 | #else | 
 | #ifdef M_UNIX | 
 | _PROTOTYP( struct passwd * getpwnam, (const char *) ); | 
 | #else | 
 | #ifdef HPUX9 | 
 | _PROTOTYP( struct passwd * getpwnam, (const char *) ); | 
 | #else | 
 | #ifdef HPUX10 | 
 | _PROTOTYP( struct passwd * getpwnam, (const char *) ); | 
 | #else | 
 | #ifdef DCGPWNAM | 
 | _PROTOTYP( struct passwd * getpwnam, (const char *) ); | 
 | #else | 
 | _PROTOTYP( struct passwd * getpwnam, (char *) ); | 
 | #endif /* DCGPWNAM */ | 
 | #endif /* HPUX10 */ | 
 | #endif /* HPUX9 */ | 
 | #endif /* M_UNIX */ | 
 | #endif /* IRIX51 */ | 
 | #endif /* IRIX40 */ | 
 | #ifndef SUNOS4 | 
 | #ifndef HPUX9 | 
 | #ifndef HPUX10 | 
 | #ifndef _SCO_DS | 
 | _PROTOTYP( struct passwd * getpwuid, (PWID_T) ); | 
 | #endif /* _SCO_DS */ | 
 | #endif /* HPUX10 */ | 
 | #endif /* HPUX9 */ | 
 | #endif /* SUNOS4 */ | 
 | _PROTOTYP( struct passwd * getpwent, (void) ); | 
 | #endif /* SVR4 */ | 
 | #endif /* NEXT */ | 
 | #endif /* _POSIX_SOURCE */ | 
 | #endif /* NDGPWNAM */ | 
 |  | 
 | #ifdef CK_SHADOW                        /* Shadow Passwords... */ | 
 | #include <shadow.h> | 
 | #endif /* CK_SHADOW */ | 
 | #ifdef CK_PAM                           /* PAM... */ | 
 | #include <security/pam_appl.h> | 
 | #ifndef PAM_SERVICE_TYPE                /* Defines which PAM service we are */ | 
 | #define PAM_SERVICE_TYPE "kermit" | 
 | #endif /* PAM_SERVICE_TYPE */ | 
 |  | 
 | #ifdef SOLARIS | 
 | #define PAM_CONST  | 
 | #else /* SOLARIS */ | 
 | #define PAM_CONST CONST | 
 | #endif  | 
 |  | 
 | static char * pam_pw = NULL; | 
 |  | 
 | int | 
 | #ifdef CK_ANSIC | 
 | pam_cb(int num_msg, | 
 |        PAM_CONST struct pam_message **msg, | 
 |        struct pam_response **resp, | 
 |        void *appdata_ptr | 
 |        ) | 
 | #else /* CK_ANSIC */ | 
 | pam_cb(num_msg, msg, resp, appdata_ptr) | 
 |     int num_msg; | 
 |     PAM_CONST struct pam_message **msg; | 
 |     struct pam_response **resp; | 
 |     void *appdata_ptr; | 
 | #endif /* CK_ANSIC */ | 
 | { | 
 |     int i; | 
 |  | 
 |     debug(F111,"pam_cb","num_msg",num_msg); | 
 |  | 
 |     for (i = 0; i < num_msg; i++) { | 
 |         char message[PAM_MAX_MSG_SIZE]; | 
 |  | 
 |         /* Issue prompt and get response */ | 
 |         debug(F111,"pam_cb","Message",i); | 
 |         debug(F111,"pam_cb",msg[i]->msg,msg[i]->msg_style); | 
 |         if (msg[i]->msg_style == PAM_ERROR_MSG) { | 
 |             debug(F111,"pam_cb","PAM ERROR",0); | 
 |             fprintf(stdout,"%s\n", msg[i]->msg); | 
 |             return(0); | 
 |         } else if (msg[i]->msg_style == PAM_TEXT_INFO) { | 
 |             debug(F111,"pam_cb","PAM TEXT INFO",0); | 
 |             fprintf(stdout,"%s\n", msg[i]->msg); | 
 |             return(0); | 
 |         } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) { | 
 |             debug(F111,"pam_cb","Reading response, no echo",0); | 
 |             /* Ugly hack.  We check to see if a password has been pushed */ | 
 |             /* into zvpasswd().  This would be true if the password was  */ | 
 |             /* received by REMOTE LOGIN.                                 */ | 
 |             if (pam_pw) { | 
 |                 ckstrncpy(message,pam_pw,PAM_MAX_MSG_SIZE); | 
 |             } else | 
 |                 readpass((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE); | 
 |         } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON) { | 
 |             debug(F111,"pam_cb","Reading response, with echo",0); | 
 |             readtext((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE); | 
 |         } else { | 
 |             debug(F111,"pam_cb","unknown style",0); | 
 |             return(0); | 
 |         } | 
 |  | 
 |         /* Allocate space for this message's response structure */ | 
 |         resp[i] = (struct pam_response *) malloc(sizeof (struct pam_response)); | 
 |         if (!resp[i]) { | 
 |             int j; | 
 |             debug(F110,"pam_cb","malloc failure",0); | 
 |             for (j = 0; j < i; j++) { | 
 |                 free(resp[j]->resp); | 
 |                 free(resp[j]); | 
 |             } | 
 |             return(0); | 
 |         } | 
 |  | 
 |         /* Allocate a buffer for the response */ | 
 |         resp[i]->resp = (char *) malloc((int)strlen(message) + 1); | 
 |         if (!resp[i]->resp) { | 
 |             int j; | 
 |             debug(F110,"pam_cb","malloc failure",0); | 
 |             for (j = 0; j < i; j++) { | 
 |                 free(resp[j]->resp); | 
 |                 free(resp[j]); | 
 |             } | 
 |             free(resp[i]); | 
 |             return(0); | 
 |         } | 
 |         /* Return the results back to PAM */ | 
 |         strcpy(resp[i]->resp, message);	/* safe (prechecked) */ | 
 |         resp[i]->resp_retcode = 0; | 
 |     } | 
 |     debug(F110,"pam_cb","Exiting",0); | 
 |     return(0); | 
 | } | 
 | #endif /* CK_PAM */ | 
 |  | 
 | /* Define macros for getting file type */ | 
 |  | 
 | #ifdef OXOS | 
 | /* | 
 |   Olivetti X/OS 2.3 has S_ISREG and S_ISDIR defined | 
 |   incorrectly, so we force their redefinition. | 
 | */ | 
 | #undef S_ISREG | 
 | #undef S_ISDIR | 
 | #endif /* OXOS */ | 
 |  | 
 | #ifdef UTSV                             /* Same deal for Amdahl UTSV */ | 
 | #undef S_ISREG | 
 | #undef S_ISDIR | 
 | #endif /* UTSV */ | 
 |  | 
 | #ifdef UNISYS52                         /* And for UNISYS UTS V 5.2 */ | 
 | #undef S_ISREG | 
 | #undef S_ISDIR | 
 | #endif /* UNISYS52 */ | 
 |  | 
 | #ifdef ICLSVR3                          /* And for old ICL versions */ | 
 | #undef S_ISREG | 
 | #undef S_ISDIR | 
 | #endif /* ICLSVR3 */ | 
 |  | 
 | #ifdef ISDIRBUG                         /* Also allow this from command line */ | 
 | #ifdef S_ISREG | 
 | #undef S_ISREG | 
 | #endif /* S_ISREG */ | 
 | #ifdef S_ISDIR | 
 | #undef S_ISDIR | 
 | #endif /*  S_ISDIR */ | 
 | #endif /* ISDIRBUG */ | 
 |  | 
 | #ifndef _IFMT | 
 | #ifdef S_IFMT | 
 | #define _IFMT S_IFMT | 
 | #else | 
 | #define _IFMT 0170000 | 
 | #endif /* S_IFMT */ | 
 | #endif /* _IFMT */ | 
 |  | 
 | #ifndef S_ISREG | 
 | #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) | 
 | #endif /* S_ISREG */ | 
 | #ifndef S_ISDIR | 
 | #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) | 
 | #endif /* S_ISDIR */ | 
 |  | 
 | /* The following mainly for NeXTSTEP... */ | 
 |  | 
 | #ifndef S_IWUSR | 
 | #define S_IWUSR 0000200 | 
 | #endif /* S_IWUSR */ | 
 |  | 
 | #ifndef S_IRGRP | 
 | #define S_IRGRP 0000040 | 
 | #endif /* S_IRGRP */ | 
 |  | 
 | #ifndef S_IWGRP | 
 | #define S_IWGRP 0000020 | 
 | #endif /* S_IWGRP */ | 
 |  | 
 | #ifndef S_IXGRP | 
 | #define S_IXGRP 0000010 | 
 | #endif /* S_IXGRP */ | 
 |  | 
 | #ifndef S_IROTH | 
 | #define S_IROTH 0000004 | 
 | #endif /* S_IROTH */ | 
 |  | 
 | #ifndef S_IWOTH | 
 | #define S_IWOTH 0000002 | 
 | #endif /* S_IWOTH */ | 
 |  | 
 | #ifndef S_IXOTH | 
 | #define S_IXOTH 0000001 | 
 | #endif /* S_IXOTH */ | 
 | /* | 
 |   Define maximum length for a file name if not already defined. | 
 |   NOTE: This applies to a path segment (directory or file name), | 
 |   not the entire path string, which can be CKMAXPATH bytes long. | 
 | */ | 
 | #ifdef QNX | 
 | #ifdef _MAX_FNAME | 
 | #define MAXNAMLEN _MAX_FNAME | 
 | #else | 
 | #define MAXNAMLEN 48 | 
 | #endif /* _MAX_FNAME */ | 
 | #else | 
 | #ifndef MAXNAMLEN | 
 | #ifdef sun | 
 | #define MAXNAMLEN 255 | 
 | #else | 
 | #ifdef FILENAME_MAX | 
 | #define MAXNAMLEN FILENAME_MAX | 
 | #else | 
 | #ifdef NAME_MAX | 
 | #define MAXNAMLEN NAME_MAX | 
 | #else | 
 | #ifdef _POSIX_NAME_MAX | 
 | #define MAXNAMLEN _POSIX_NAME_MAX | 
 | #else | 
 | #ifdef _D_NAME_MAX | 
 | #define MAXNAMLEN _D_NAME_MAX | 
 | #else | 
 | #ifdef DIRSIZ | 
 | #define MAXNAMLEN DIRSIZ | 
 | #else | 
 | #define MAXNAMLEN 14 | 
 | #endif /* DIRSIZ */ | 
 | #endif /* _D_NAME_MAX */ | 
 | #endif /* _POSIX_NAME_MAX */ | 
 | #endif /* NAME_MAX */ | 
 | #endif /* FILENAME_MAX */ | 
 | #endif /* sun */ | 
 | #endif /* MAXNAMLEN */ | 
 | #endif /* QNX */ | 
 |  | 
 | #ifdef COMMENT | 
 | /* As of 2001-11-03 this is handled in ckcdeb.h */ | 
 | /* Longest pathname ... */ | 
 | /* | 
 |   Beware: MAXPATHLEN is one of UNIX's dirty little secrets.  Where is it | 
 |   defined?  Who knows...  <param.h>, <mod.h>, <unistd.h>, <limits.h>, ... | 
 |   There is not necessarily even a definition for it anywhere, or it might have | 
 |   another name.  If you get it wrong, bad things happen with getcwd() and/or | 
 |   getwd().  If you allocate a buffer that is too short, getwd() might write | 
 |   over memory and getcwd() will fail with ERANGE.  The definitions of these | 
 |   functions (e.g. in SVID or POSIX.1) do not tell you how to determine the | 
 |   maximum path length in order to allocate a buffer that is the right size. | 
 | */ | 
 | #ifdef BSD44 | 
 | #include <sys/param.h>                  /* For MAXPATHLEN */ | 
 | #endif /* BSD44 */ | 
 | #ifdef COHERENT | 
 | #include <sys/param.h>  /* for MAXPATHLEN, needed for -DDIRENT */ | 
 | #endif /* COHERENT */ | 
 | #endif /* COMMENT */ | 
 |  | 
 | #ifdef MAXPATHLEN | 
 | #ifdef MAXPATH | 
 | #undef MAXPATH | 
 | #endif /* MAXPATH */ | 
 | #define MAXPATH MAXPATHLEN | 
 | #else | 
 | #ifdef PATH_MAX | 
 | #define MAXPATH PATH_MAX | 
 | #else | 
 | #ifdef _POSIX_PATH_MAX | 
 | #define MAXPATH _POSIX_PATH_MAX | 
 | #else | 
 | #ifdef BSD42 | 
 | #define MAXPATH 1024 | 
 | #else | 
 | #ifdef SVR4 | 
 | #define MAXPATH 1024 | 
 | #else | 
 | #define MAXPATH 255 | 
 | #endif /* SVR4 */ | 
 | #endif /* BSD42 */ | 
 | #endif /* _POSIX_PATH_MAX */ | 
 | #endif /* PATH_MAX */ | 
 | #endif /* MAXPATHLEN */ | 
 |  | 
 | /* Maximum number of filenames for wildcard expansion */ | 
 |  | 
 | #ifndef MAXWLD | 
 | /* Already defined in ckcdeb.h so the following is superfluous. */ | 
 | /* Don't expect changing them to have any effect. */ | 
 | #ifdef CK_SMALL | 
 | #define MAXWLD 50 | 
 | #else | 
 | #ifdef BIGBUFOK | 
 | #define MAXWLD 102400 | 
 | #else | 
 | #define MAXWLD 8192 | 
 | #endif /* BIGBUFOK */ | 
 | #endif /* CK_SMALL */ | 
 | #endif /* MAXWLD */ | 
 |  | 
 | static int maxnames = MAXWLD; | 
 |  | 
 | /* Define the size of the string space for filename expansion. */ | 
 |  | 
 | #ifndef DYNAMIC | 
 | #ifdef PROVX1 | 
 | #define SSPACE 500 | 
 | #else | 
 | #ifdef BSD29 | 
 | #define SSPACE 500 | 
 | #else | 
 | #ifdef pdp11 | 
 | #define SSPACE 500 | 
 | #else | 
 | #ifdef aegis | 
 | #define SSPACE 10000                    /* Size of string-generating buffer */ | 
 | #else                                   /* Default static buffer size */ | 
 | #ifdef BIGBUFOK | 
 | #define SSPACE 65000                    /* Size of string-generating buffer */ | 
 | #else | 
 | #define SSPACE 2000                     /* size of string-generating buffer */ | 
 | #endif /* BIGBUFOK */ | 
 | #endif /* aegis */ | 
 | #endif /* pdp11 */ | 
 | #endif /* BSD29 */ | 
 | #endif /* PROVX1 */ | 
 | static char sspace[SSPACE];             /* Buffer for generating filenames */ | 
 | #else /* is DYNAMIC */ | 
 | #ifdef BIGBUFOK | 
 | #define SSPACE 500000 | 
 | #else | 
 | #define SSPACE 10000 | 
 | #endif /* BIGBUFOK */ | 
 | char *sspace = (char *)0; | 
 | #endif /* DYNAMIC */ | 
 | static int ssplen = SSPACE;		/* Length of string space buffer */ | 
 |  | 
 | #ifdef DCLFDOPEN | 
 | /* fdopen() needs declaring because it's not declared in <stdio.h> */ | 
 | _PROTOTYP( FILE * fdopen, (int, char *) ); | 
 | #endif /* DCLFDOPEN */ | 
 |  | 
 | #ifdef DCLPOPEN | 
 | /* popen() needs declaring because it's not declared in <stdio.h> */ | 
 | _PROTOTYP( FILE * popen, (char *, char *) ); | 
 | #endif /* DCLPOPEN */ | 
 |  | 
 | extern int nopush; | 
 |  | 
 | /* More internal function prototypes */ | 
 | /* | 
 |  * The path structure is used to represent the name to match. | 
 |  * Each slash-separated segment of the name is kept in one | 
 |  * such structure, and they are linked together, to make | 
 |  * traversing the name easier. | 
 |  */ | 
 | struct path { | 
 |     char npart[MAXNAMLEN+4];            /* name part of path segment */ | 
 |     struct path *fwd;                   /* forward ptr */ | 
 | }; | 
 | #ifndef NOPUSH | 
 | _PROTOTYP( int shxpand, (char *, char *[], int ) ); | 
 | #endif /* NOPUSH */ | 
 | _PROTOTYP( static int fgen, (char *, char *[], int ) ); | 
 | _PROTOTYP( static VOID traverse, (struct path *, char *, char *) ); | 
 | _PROTOTYP( static VOID addresult, (char *, int) ); | 
 | #ifdef COMMENT | 
 | /* Replaced by ckmatch() */ | 
 | _PROTOTYP( static int match, (char *, char *) ); | 
 | #endif /* COMMENT */ | 
 | _PROTOTYP( char * whoami, (void) ); | 
 | _PROTOTYP( UID_T real_uid, (void) ); | 
 | _PROTOTYP( static struct path *splitpath, (char *p) ); | 
 | _PROTOTYP( char * zdtstr, (time_t) ); | 
 | _PROTOTYP( time_t zstrdt, (char *, int) ); | 
 |  | 
 | /* Some systems define these symbols in include files, others don't... */ | 
 |  | 
 | #ifndef R_OK | 
 | #define R_OK 4                          /* For access */ | 
 | #endif /* R_OK */ | 
 |  | 
 | #ifndef W_OK | 
 | #define W_OK 2 | 
 | #endif /* W_OK */ | 
 |  | 
 | #ifndef X_OK | 
 | #define X_OK 1 | 
 | #endif /* X_OK */ | 
 |  | 
 | #ifndef O_RDONLY | 
 | #define O_RDONLY 000 | 
 | #endif /* O_RDONLY */ | 
 |  | 
 | /* syslog and wtmp items for Internet Kermit Service */ | 
 |  | 
 | extern char * clienthost;               /* From ckcmai.c. */ | 
 |  | 
 | static char fullname[CKMAXPATH+1]; | 
 | static char tmp2[CKMAXPATH+1]; | 
 |  | 
 | extern int ckxlogging; | 
 |  | 
 | #ifdef CKXPRINTF                        /* Our printf macro conflicts with */ | 
 | #undef printf                           /* use of "printf" in syslog.h */ | 
 | #endif /* CKXPRINTF */ | 
 | #ifdef CKSYSLOG | 
 | #ifdef RTAIX | 
 | #include <sys/syslog.h> | 
 | #else  /* RTAIX */ | 
 | #include <syslog.h> | 
 | #endif /* RTAIX */ | 
 | #endif /* CKSYSLOG */ | 
 | #ifdef CKXPRINTF | 
 | #define printf ckxprintf | 
 | #endif /* CKXPRINTF */ | 
 |  | 
 | int ckxanon = 1;                        /* Anonymous login ok */ | 
 | int ckxperms = 0040;                    /* Anonymous file permissions */ | 
 | int ckxpriv = 1;			/* Priv'd login ok */ | 
 |  | 
 | #ifndef XFERFILE | 
 | #define XFERFILE "/var/log/iksd.log" | 
 | #endif /* XFERFILE */ | 
 |  | 
 | /* wtmp logging for IKSD... */ | 
 |  | 
 | #ifndef CKWTMP                          /* wtmp logging not selected */ | 
 | int ckxwtmp = 0;                        /* Know this at runtime */ | 
 | #else                                   /* wtmp file details */ | 
 | int ckxwtmp = 1; | 
 | #ifdef UTMPBUG                          /* Unfortunately... */ | 
 | /* | 
 |   Some versions of Linux have a <utmp.h> file that contains | 
 |   "enum utlogin { local, telnet, rlogin, screen, ... };"  This clobbers | 
 |   any program that uses any of these words as variable names, function | 
 |   names, macro names, etc.  (Other versions of Linux have this declaration | 
 |   within #if 0 ... #endif.)  There is nothing we can do about this other | 
 |   than to not include the stupid file.  But we need stuff from it, so... | 
 | */ | 
 | #include <features.h> | 
 | #include <sys/types.h> | 
 | #define UT_LINESIZE     32 | 
 | #define UT_NAMESIZE     32 | 
 | #define UT_HOSTSIZE     256 | 
 |  | 
 | struct timeval { | 
 |   time_t tv_sec; | 
 |   time_t tv_usec; | 
 | }; | 
 |  | 
 | struct exit_status { | 
 |   short int e_termination;      /* Process termination status.  */ | 
 |   short int e_exit;             /* Process exit status.  */ | 
 | }; | 
 |  | 
 | struct utmp { | 
 |   short int ut_type;                    /* Type of login */ | 
 |   pid_t ut_pid;                         /* Pid of login process */ | 
 |   char ut_line[UT_LINESIZE];            /* NUL-terminated devicename of tty */ | 
 |   char ut_id[4];                        /* Inittab id */ | 
 |   char ut_user[UT_NAMESIZE];            /* Username (not NUL terminated) */ | 
 |  | 
 |   char ut_host[UT_HOSTSIZE];            /* Hostname for remote login */ | 
 |   struct exit_status ut_exit;           /* Exit status */ | 
 |   long ut_session;                      /* Session ID, used for windowing */ | 
 |   struct timeval ut_tv;                 /* Time entry was made */ | 
 |   int32_t ut_addr_v6[4];                /* Internet address of remote host */ | 
 |   char pad[20];                         /* Reserved */ | 
 | }; | 
 |  | 
 | #define ut_time ut_tv.tv_sec    /* Why should Linux be like anything else? */ | 
 | #define ut_name ut_user         /* ... */ | 
 |  | 
 | extern void | 
 | logwtmp __P ((__const char *__ut_line, __const char *__ut_name, | 
 |                           __const char *__ut_host)); | 
 |  | 
 | #else  /* Not UTMPBUG */ | 
 |  | 
 | #ifndef HAVEUTMPX                       /* Who has <utmpx.h> */ | 
 | #ifdef SOLARIS | 
 | #define HAVEUTMPX | 
 | #else | 
 | #ifdef IRIX60 | 
 | #define HAVEUTMPX | 
 | #else | 
 | #ifdef CK_SCOV5 | 
 | #define HAVEUTMPX | 
 | #else | 
 | #ifdef HPUX100 | 
 | #define HAVEUTMPX | 
 | #else | 
 | #ifdef UNIXWARE | 
 | #define HAVEUTMPX | 
 | #endif /* UNIXWARE */ | 
 | #endif /* HPUX100 */ | 
 | #endif /* CK_SCOV5 */ | 
 | #endif /* IRIX60 */ | 
 | #endif /* SOLARIS */ | 
 | #endif /* HAVEUTMPX */ | 
 | #ifdef HAVEUTMPX | 
 | #include <utmpx.h> | 
 | #else | 
 | #ifdef OSF50 | 
 | /* Because the time_t in the utmp struct is 64 bits but time() wants 32 */ | 
 | #define __V40_OBJ_COMPAT 1 | 
 | #endif /* OSF50 */ | 
 | #include <utmp.h> | 
 | #ifdef OSF50 | 
 | #undef __V40_OBJ_COMPAT | 
 | #endif /* OSF50 */ | 
 | #endif /* HAVEUTMPX */ | 
 | #endif /* UTMPBUG */ | 
 |  | 
 | #ifndef WTMPFILE | 
 | #ifdef QNX | 
 | #define WTMPFILE "/usr/adm/wtmp.1" | 
 | #else | 
 | #ifdef LINUX | 
 | #define WTMPFILE "/var/log/wtmp" | 
 | #else | 
 | #define WTMPFILE "/usr/adm/wtmp" | 
 | #endif /* QNX */ | 
 | #endif /* LINUX */ | 
 | #endif /* WTMPFILE */ | 
 | char * wtmpfile = NULL; | 
 |  | 
 | static int wtmpfd = 0; | 
 | static char cksysline[32] = { NUL, NUL }; | 
 |  | 
 | #ifndef HAVEUTHOST                      /* Does utmp include ut_host[]? */ | 
 | #ifdef HAVEUTMPX                        /* utmpx always does */ | 
 | #define HAVEUTHOST | 
 | #else | 
 | #ifdef LINUX                            /* Linux does */ | 
 | #define HAVEUTHOST | 
 | #else | 
 | #ifdef SUNOS4                           /* SunOS does */ | 
 | #define HAVEUTHOST | 
 | #else | 
 | #ifdef AIX41                            /* AIX 4.1 and later do */ | 
 | #define HAVEUTHOST | 
 | #endif /* AIX41 */ | 
 | #endif /* SUNOS4 */ | 
 | #endif /* LINUX */ | 
 | #endif /* HAVEUTMPX */ | 
 | #endif /* HAVEUTHOST */ | 
 |  | 
 | #ifdef UW200 | 
 | PID_T _vfork() {                        /* To satisfy a library foulup */ | 
 |     return(fork());                     /* in Unixware 2.0.x */ | 
 | } | 
 | #endif /* UW200 */ | 
 |  | 
 | VOID | 
 | #ifdef CK_ANSIC | 
 | logwtmp(const char * line, const char * name, const char * host) | 
 | #else | 
 | logwtmp(line, name, host) char *line, *name, *host; | 
 | #endif /* CK_ANSIC */ | 
 | /* logwtmp */ { | 
 | #ifdef HAVEUTMPX | 
 |     struct utmpx ut;                    /* Needed for ut_host[] */ | 
 | #else | 
 |     struct utmp ut; | 
 | #endif /* HAVEUTMPX */ | 
 |     struct stat buf; | 
 |     /* time_t time(); */ | 
 |  | 
 |     if (!ckxwtmp) | 
 |       return; | 
 |  | 
 |     if (!wtmpfile) | 
 |       makestr(&wtmpfile,WTMPFILE); | 
 |  | 
 |     if (!line) line = ""; | 
 |     if (!name) name = ""; | 
 |     if (!host) host = ""; | 
 |  | 
 |     if (!wtmpfd && (wtmpfd = open(wtmpfile, O_WRONLY|O_APPEND, 0)) < 0) { | 
 |         ckxwtmp = 0; | 
 |         debug(F110,"WTMP open failed",line,0); | 
 |         return; | 
 |     } | 
 |     if (!fstat(wtmpfd, &buf)) { | 
 |         ckstrncpy(ut.ut_line, line, sizeof(ut.ut_line)); | 
 |         ckstrncpy(ut.ut_name, name, sizeof(ut.ut_name)); | 
 | #ifdef HAVEUTHOST | 
 |         /* Not portable */ | 
 |         ckstrncpy(ut.ut_host, host, sizeof(ut.ut_host)); | 
 | #endif /* HAVEUTHOST */ | 
 | #ifdef HAVEUTMPX | 
 |         time(&ut.ut_tv.tv_sec); | 
 | #else | 
 | #ifdef LINUX | 
 | /* In light of the following comment perhaps the previous line should */ | 
 | /* be "#ifndef COMMENT". */ | 
 |         { | 
 |             /* | 
 |              * On 64-bit platforms sizeof(time_t) and sizeof(ut.ut_time) | 
 |              * are not the same and attempt to use an address of | 
 |              * ut.ut_time as an argument to time() call may cause | 
 |              * "unaligned access" trap. | 
 |              */ | 
 |             time_t zz; | 
 |             time(&zz); | 
 |             ut.ut_time = zz; | 
 |         } | 
 | #else | 
 |         time(&ut.ut_time); | 
 | #endif /* LINUX */ | 
 | #endif /* HAVEUTMPX */ | 
 |         if (write(wtmpfd, (char *)&ut, sizeof(struct utmp)) != | 
 |             sizeof(struct utmp)) { | 
 | #ifndef NOFTRUNCATE | 
 | #ifndef COHERENT | 
 |             ftruncate(wtmpfd, buf.st_size); /* Error, undo any partial write */ | 
 | #else | 
 |             chsize(wtmpfd, buf.st_size); /* Error, undo any partial write */ | 
 | #endif /* COHERENT */ | 
 | #endif /* NOFTRUNCATE */ | 
 |             debug(F110,"WTMP write error",line,0); | 
 |         } else { | 
 |             debug(F110,"WTMP record OK",line,0); | 
 |             return; | 
 |         } | 
 |     } | 
 | } | 
 | #endif /* CKWTMP */ | 
 |  | 
 | #ifdef CKSYSLOG | 
 | /* | 
 |   C K S Y S L O G  --  C-Kermit system logging function, | 
 |  | 
 |   For use by other modules. | 
 |   This module can, but doesn't have to, use it. | 
 |   Call with: | 
 |     n = SYSLG_xx values defined in ckcdeb.h | 
 |     s1, s2, s3: strings. | 
 | */ | 
 | VOID | 
 | cksyslog(n, m, s1, s2, s3) int n, m; char * s1, * s2, * s3; { | 
 |     int level; | 
 |  | 
 |     if (!ckxlogging)                    /* syslogging */ | 
 |       return; | 
 |     if (!s1) s1 = "";                   /* Fix null args */ | 
 |     if (!s2) s2 = ""; | 
 |     if (!s3) s3 = ""; | 
 |     switch (n) {                        /* Translate Kermit level */ | 
 |       case SYSLG_DB:                    /* to syslog level */ | 
 |         level = LOG_DEBUG; | 
 |         break; | 
 |       default: | 
 |         level = m ? LOG_INFO : LOG_ERR; | 
 |     } | 
 |     debug(F110,"cksyslog s1",s1,0); | 
 |     debug(F110,"cksyslog s2",s2,0); | 
 |     debug(F110,"cksyslog s3",s3,0); | 
 |     errno = 0; | 
 |     syslog(level, "%s: %s %s", s1, s2, s3); /* Write syslog record */ | 
 |     debug(F101,"cksyslog errno","",errno); | 
 | } | 
 | #endif /* CKSYSLOG */ | 
 |  | 
 |  | 
 | /* Declarations */ | 
 |  | 
 | int maxnam = MAXNAMLEN;                 /* Available to the outside */ | 
 | int maxpath = MAXPATH; | 
 | int ck_znewn = -1; | 
 |  | 
 | #ifdef UNIX | 
 | char startupdir[MAXPATH+1]; | 
 | #endif /* UNIX */ | 
 |  | 
 | int pexitstat = -2;                     /* Process exit status */ | 
 |  | 
 | FILE *fp[ZNFILS] = {                    /* File pointers */ | 
 |    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL | 
 | }; | 
 |  | 
 | /* Flags for each file indicating whether it was opened with popen() */ | 
 | int ispipe[ZNFILS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; | 
 |  | 
 | /* Buffers and pointers used in buffered file input and output. */ | 
 | #ifdef DYNAMIC | 
 | extern char *zinbuffer, *zoutbuffer; | 
 | #else | 
 | extern char zinbuffer[], zoutbuffer[]; | 
 | #endif /* DYNAMIC */ | 
 | extern char *zinptr, *zoutptr; | 
 | extern int zincnt, zoutcnt; | 
 | extern int wildxpand; | 
 |  | 
 | static long iflen = -1L;                /* Input file length */ | 
 |  | 
 | static PID_T pid = 0;                   /* pid of child fork */ | 
 | static int fcount = 0;                  /* Number of files in wild group */ | 
 | static int nxpand = 0;                  /* Copy of fcount */ | 
 | static char nambuf[CKMAXPATH+4];        /* Buffer for a pathname */ | 
 |  | 
 | #ifndef NOFRILLS | 
 | #define ZMBUFLEN 200 | 
 | static char zmbuf[ZMBUFLEN];		/* For mail, remote print strings */ | 
 | #endif /* NOFRILLS */ | 
 |  | 
 | char **mtchs = NULL;                    /* Matches found for filename */ | 
 | char **mtchptr = NULL;                  /* Pointer to current match */ | 
 |  | 
 | /*  Z K S E L F  --  Kill Self: log out own job, if possible.  */ | 
 |  | 
 | /* Note, should get current pid, but if your system doesn't have */ | 
 | /* getppid(), then just kill(0,9)...  */ | 
 |  | 
 | #ifndef SVR3 | 
 | #ifndef POSIX | 
 | #ifndef OSFPC | 
 | /* Already declared in unistd.h for SVR3 and POSIX */ | 
 | #ifdef CK_ANSIC | 
 | extern PID_T getppid(void); | 
 | #else | 
 | #ifndef PS2AIX10 | 
 | #ifndef COHERENT | 
 | extern PID_T getppid(); | 
 | #endif /* COHERENT */ | 
 | #endif /* PS2AIX10 */ | 
 | #endif /* CK_ANSIC */ | 
 | #endif /* OSFPC */ | 
 | #endif /* POSIX */ | 
 | #endif /* SVR3 */ | 
 |  | 
 | int | 
 | zkself() {                              /* For "bye", but no guarantee! */ | 
 | #ifdef PROVX1 | 
 |     return(kill(0,9)); | 
 | #else | 
 | #ifdef V7 | 
 |     return(kill(0,9)); | 
 | #else | 
 | #ifdef TOWER1 | 
 |     return(kill(0,9)); | 
 | #else | 
 | #ifdef FT18 | 
 |     return(kill(0,9)); | 
 | #else | 
 | #ifdef aegis | 
 |     return(kill(0,9)); | 
 | #else | 
 | #ifdef COHERENT | 
 |     return(kill((PID_T)getpid(),1)); | 
 | #else | 
 | #ifdef PID_T | 
 |     exit(kill((PID_T)getppid(),1)); | 
 |     return(0); | 
 | #else | 
 |     exit(kill(getppid(),1)); | 
 |     return(0); | 
 | #endif | 
 | #endif | 
 | #endif | 
 | #endif | 
 | #endif | 
 | #endif | 
 | #endif | 
 | } | 
 |  | 
 | static VOID | 
 | getfullname(name) char * name; { | 
 |     char *p = (char *)fullname; | 
 |     int len = 0; | 
 |     fullname[0] = '\0'; | 
 |     /* If necessary we could also chase down symlinks here... */ | 
 | #ifdef COMMENT | 
 |     /* This works but is incompatible with wuftpd */ | 
 |     if (isguest && anonroot) { | 
 |         ckstrncpy(fullname,anonroot,CKMAXPATH); | 
 |         len = strlen(fullname); | 
 |         if (len > 0) | 
 |           if (fullname[len-1] == '/') | 
 |             len--; | 
 |     } | 
 |     p += len; | 
 | #endif /* COMMENT */ | 
 |     zfnqfp(name, CKMAXPATH - len, p); | 
 |     while (*p) { | 
 |         if (*p < '!') *p = '_'; | 
 |         p++; | 
 |     } | 
 | } | 
 |  | 
 | /*  D O I K L O G  --  Open Kermit-specific ftp-like transfer log. */ | 
 |  | 
 | VOID                                    /* Called in ckcmai.c */ | 
 | doiklog() { | 
 |     if (iklogopen)                      /* Already open? */ | 
 |       return; | 
 |     if (xferlog) {                      /* Open iksd log if requested */ | 
 |         if (!xferfile)                  /* If no pathname given */ | 
 |           makestr(&xferfile,XFERFILE);	/* use this default */ | 
 |         if (*xferfile) { | 
 |             xferlog = open(xferfile, O_WRONLY | O_APPEND | O_CREAT, 0660); | 
 |             debug(F101,"doiklog open","",xferlog); | 
 |             if (xferlog < 0) { | 
 | #ifdef CKSYSLOG | 
 |                 syslog(LOG_ERR, "xferlog open failure %s: %m", xferfile); | 
 | #endif /* CKSYSLOG */ | 
 |                 debug(F101,"doiklog open errno","",errno); | 
 |                 xferlog = 0; | 
 |             } else | 
 |               iklogopen = 1; | 
 |         } else | 
 |           xferlog = 0; | 
 | #ifdef CKSYSLOG | 
 |         if (xferlog && ckxlogging) | 
 |           syslog(LOG_INFO, "xferlog: %s open ok", xferfile); | 
 | #endif /* CKSYSLOG */ | 
 |     } | 
 | } | 
 |  | 
 | /*  Z O P E N I  --  Open an existing file for input. */ | 
 |  | 
 | /* Returns 1 on success, 0 on failure */ | 
 |  | 
 | int | 
 | zopeni(n,name) int n; char *name; { | 
 |     int x; | 
 |  | 
 |     debug(F111,"zopeni",name,n); | 
 |     if ((x = chkfn(n)) != 0) { | 
 | 	debug(F111,"zopeni chkfn",ckitoa(n),x); | 
 | 	return(0); | 
 |     } | 
 |     zincnt = 0;                         /* Reset input buffer */ | 
 |     if (n == ZSYSFN) {                  /* Input from a system function? */ | 
 | #ifdef COMMENT | 
 | /*** Note, this function should not be called with ZSYSFN ***/ | 
 | /*** Always call zxcmd() directly, and give it the real file number ***/ | 
 | /*** you want to use.  ***/ | 
 |         return(zxcmd(n,name));          /* Try to fork the command */ | 
 | #else | 
 |         debug(F110,"zopeni called with ZSYSFN, failing!",name,0); | 
 |         *nambuf = '\0';                 /* No filename. */ | 
 |         return(0);                      /* fail. */ | 
 | #endif /* COMMENT */ | 
 |     } | 
 |     if (n == ZSTDIO) {                  /* Standard input? */ | 
 |         if (is_a_tty(0)) { | 
 |             fprintf(stderr,"Terminal input not allowed"); | 
 |             debug(F110,"zopeni: attempts input from unredirected stdin","",0); | 
 |             return(0); | 
 |         } | 
 |         fp[ZIFILE] = stdin; | 
 |         ispipe[ZIFILE] = 0; | 
 |         return(1); | 
 |     } | 
 | #ifdef CKROOT | 
 |     debug(F111,"zopeni setroot",ckroot,ckrootset); | 
 |     if (ckrootset) if (!zinroot(name)) { | 
 | 	debug(F110,"zopeni setroot violation",name,0); | 
 | 	return(0); | 
 |     } | 
 | #endif /* CKROOT */ | 
 |     fp[n] = fopen(name,"r");            /* Real file, open it. */ | 
 |     debug(F111,"zopeni fopen", name, fp[n]); | 
 | #ifdef ZDEBUG | 
 |     printf("ZOPENI fp[%d]=%ld\n",n,fp[n]); | 
 | #endif /* ZDEBUG */ | 
 |     ispipe[n] = 0; | 
 |  | 
 |     if (xferlog | 
 | #ifdef CKSYSLOG | 
 |         || ((ckxsyslog >= SYSLG_FA) && ckxlogging) | 
 | #endif /* CKSYSLOG */ | 
 |         ) { | 
 |         getfullname(name); | 
 |         debug(F110,"zopeni fullname",fullname,0); | 
 |     } | 
 |     if (fp[n] == NULL) { | 
 | #ifdef CKSYSLOG | 
 |         if (ckxsyslog >= SYSLG_FA && ckxlogging) { | 
 | 	    syslog(LOG_INFO, "file[%d] %s: open failed (%m)", n, fullname); | 
 | 	    perror(fullname); | 
 | 	} else | 
 | #endif /* CKSYSLOG */ | 
 | 	  perror(name); | 
 |         return(0); | 
 |     } else { | 
 | #ifdef CKSYSLOG | 
 |         if (ckxsyslog >= SYSLG_FA && ckxlogging) | 
 |           syslog(LOG_INFO, "file[%d] %s: open read ok", n, fullname); | 
 | #endif /* CKSYSLOG */ | 
 |         clearerr(fp[n]); | 
 |         return(1); | 
 |     } | 
 | } | 
 |  | 
 | #ifdef QNX | 
 | #define DONDELAY | 
 | #else | 
 | #ifdef O_NDELAY | 
 | #define DONDELAY | 
 | #endif /* O_NDELAY */ | 
 | #endif /* QNX */ | 
 |  | 
 | /*  Z O P E N O  --  Open a new file for output.  */ | 
 |  | 
 | /*ARGSUSED*/	/* zz not used */ | 
 | int | 
 | zopeno(n,name,zz,fcb) | 
 | /* zopeno */  int n; char *name; struct zattr *zz; struct filinfo *fcb; { | 
 |  | 
 |     char p[8]; | 
 |     int append = 0; | 
 |  | 
 | /* As of Version 5A, the attribute structure and the file information */ | 
 | /* structure are included in the arglist. */ | 
 |  | 
 | #ifdef DEBUG | 
 |     debug(F111,"zopeno",name,n); | 
 |     if (fcb) { | 
 |         debug(F101,"zopeno fcb disp","",fcb->dsp); | 
 |         debug(F101,"zopeno fcb type","",fcb->typ); | 
 |         debug(F101,"zopeno fcb char","",fcb->cs); | 
 |     } else { | 
 |         debug(F100,"zopeno fcb is NULL","",0); | 
 |     } | 
 | #endif /* DEBUG */ | 
 |  | 
 |     if (chkfn(n) != 0)                  /* Already open? */ | 
 |       return(0);                        /* Nothing to do. */ | 
 |  | 
 |     if ((n == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */ | 
 |         fp[ZOFILE] = stdout; | 
 |         ispipe[ZOFILE] = 0; | 
 | #ifdef COMMENT | 
 | 	/* This seems right but it breaks client server ops */ | 
 | 	fp[n] = stdout; | 
 |         ispipe[n] = 0; | 
 | #endif /* COMMENT */ | 
 | #ifdef DEBUG | 
 |         if (n != ZDFILE) | 
 |           debug(F101,"zopeno fp[n]=stdout","",fp[n]); | 
 | #endif /* DEBUG */ | 
 |         zoutcnt = 0; | 
 |         zoutptr = zoutbuffer; | 
 |         return(1); | 
 |     } | 
 |  | 
 | /* A real file.  Open it in desired mode (create or append). */ | 
 |  | 
 | #ifdef CKROOT | 
 |     debug(F111,"zopeno setroot",ckroot,ckrootset); | 
 |     if (ckrootset) if (!zinroot(name)) { | 
 | 	debug(F110,"zopeno setroot violation",name,0); | 
 | 	return(0); | 
 |     } | 
 | #endif /* CKROOT */ | 
 |  | 
 |     ckstrncpy(p,"w",8);			/* Assume write/create mode */ | 
 |     if (fcb) {                          /* If called with an FCB... */ | 
 |         if (fcb->dsp == XYFZ_A) {       /* Does it say Append? */ | 
 |             ckstrncpy(p,"a",8);		/* Yes. */ | 
 |             debug(F100,"zopeno append","",0); | 
 |             append = 1; | 
 |         } | 
 |     } | 
 |  | 
 |     if (xferlog | 
 | #ifdef CKSYSLOG | 
 |         || ((ckxsyslog >= SYSLG_FC) && ckxlogging) | 
 | #endif /* CKSYSLOG */ | 
 |         ) { | 
 |         getfullname(name); | 
 |         debug(F110,"zopeno fullname",fullname,0); | 
 |     } | 
 |     debug(F110,"zopeno fopen arg",p,0); | 
 |     fp[n] = fopen(name,p);              /* Try to open the file */ | 
 |     ispipe[ZIFILE] = 0; | 
 |  | 
 | #ifdef ZDEBUG | 
 |     printf("ZOPENO fp[%d]=%ld\n",n,fp[n]); | 
 | #endif /* ZDEBUG */ | 
 |  | 
 |     if (fp[n] == NULL) {                /* Failed */ | 
 |         debug(F101,"zopeno failed errno","",errno); | 
 | #ifdef CKSYSLOG | 
 |         if (ckxsyslog >= SYSLG_FC && ckxlogging) | 
 |           syslog(LOG_INFO, "file[%d] %s: %s failed (%m)", | 
 |                  n, | 
 |                  fullname, | 
 |                  append ? "append" : "create" | 
 |                  ); | 
 | #endif /* CKSYSLOG */ | 
 | #ifdef COMMENT                          /* Let upper levels print message. */ | 
 |         perror("Can't open output file"); | 
 | #endif /* COMMENT */ | 
 |     } else {                            /* Succeeded */ | 
 |         extern int zofbuffer, zofblock, zobufsize; | 
 |         debug(F101, "zopeno zobufsize", "", zobufsize); | 
 |         if (n == ZDFILE || n == ZTFILE) { /* If debug or transaction log */ | 
 |             setbuf(fp[n],NULL);           /* make it unbuffered. */ | 
 | #ifdef DONDELAY | 
 |         } else if (n == ZOFILE && !zofblock) { /* blocking or nonblocking */ | 
 |             int flags; | 
 |             if ((flags = fcntl(fileno(fp[n]),F_GETFL,0)) > -1) | 
 |               fcntl(fileno(fp[n]),F_SETFL, flags | | 
 | #ifdef QNX | 
 |                     O_NONBLOCK | 
 | #else | 
 |                     O_NDELAY | 
 | #endif /* QNX */ | 
 |                     ); | 
 |             debug(F100,"zopeno ZOFILE nonblocking","",0); | 
 | #endif /* DONDELAY */ | 
 |         } else if (n == ZOFILE && !zofbuffer) { /* buffered or unbuffered */ | 
 |             setbuf(fp[n],NULL); | 
 |             debug(F100,"zopeno ZOFILE unbuffered","",0); | 
 |         } | 
 |  | 
 | #ifdef CK_LOGIN | 
 |         /* Enforce anonymous file-creation permission */ | 
 |         if (isguest) | 
 |           if (n == ZWFILE || n == ZMFILE || | 
 |               n == ZOFILE || n == ZDFILE || | 
 |               n == ZTFILE || n == ZPFILE || | 
 |               n == ZSFILE) | 
 |             chmod(name,ckxperms); | 
 | #endif /* CK_LOGIN */ | 
 | #ifdef CKSYSLOG | 
 |         if (ckxsyslog >= SYSLG_FC && ckxlogging) | 
 |           syslog(LOG_INFO, "file[%d] %s: %s ok", | 
 |                  n, | 
 |                  fullname, | 
 |                  append ? "append" : "create" | 
 |                  ); | 
 | #endif /* CKSYSLOG */ | 
 |         debug(F100, "zopeno ok", "", 0); | 
 |     } | 
 |     zoutcnt = 0;                        /* (PWP) reset output buffer */ | 
 |     zoutptr = zoutbuffer; | 
 |     return((fp[n] != NULL) ? 1 : 0); | 
 | } | 
 |  | 
 | /*  Z C L O S E  --  Close the given file.  */ | 
 |  | 
 | /*  Returns 0 if arg out of range, 1 if successful, -1 if close failed.  */ | 
 |  | 
 | int | 
 | zclose(n) int n; { | 
 |     int x = 0, x2 = 0; | 
 |     extern long ffc; | 
 |  | 
 |     debug(F101,"zclose file number","",n); | 
 |     if (chkfn(n) < 1) return(0);        /* Check range of n */ | 
 |     if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */ | 
 |       x2 = zoutdump(); | 
 |  | 
 |     if (fp[ZSYSFN] || ispipe[n]) {      /* If file is really pipe */ | 
 | #ifndef NOPUSH | 
 |         x = zclosf(n);                  /* do it specially */ | 
 | #else | 
 |         x = EOF; | 
 | #endif /* NOPUSH */ | 
 |         debug(F101,"zclose zclosf","",x); | 
 |         debug(F101,"zclose zclosf fp[n]","",fp[n]); | 
 |     } else { | 
 |         if ((fp[n] != stdout) && (fp[n] != stdin)) | 
 |           x = fclose(fp[n]); | 
 |         fp[n] = NULL; | 
 | #ifdef COMMENT | 
 | 	if (n == ZCTERM || n == ZSTDIO)	/* See zopeno() */ | 
 | 	  if (fp[ZOFILE] == stdout) | 
 | 	    fp[ZOFILE] = NULL; | 
 | #endif /* COMMENT */ | 
 |     } | 
 |     iflen = -1L;                        /* Invalidate file length */ | 
 |     if (x == EOF) {                     /* if we got a close error */ | 
 |         debug(F101,"zclose fclose fails","",x); | 
 |         return(-1); | 
 |     } else if (x2 < 0) {                /* or error flushing last buffer */ | 
 |         debug(F101,"zclose error flushing last buffer","",x2); | 
 |         return(-1);                     /* then return an error */ | 
 |     } else { | 
 |         /* Print log record compatible with wu-ftpd */ | 
 |         if (xferlog && (n == ZIFILE || n == ZOFILE)) { | 
 |             char * s, *p; | 
 |             extern char ttname[]; | 
 |             if (!iklogopen) (VOID) doiklog(); /* Open log if necessary */ | 
 |             debug(F101,"zclose iklogopen","",iklogopen); | 
 |             if (iklogopen) { | 
 | 		int len; | 
 | 		char * fnam; | 
 |  | 
 |                 timenow = time(NULL); | 
 | #ifdef CK_LOGIN | 
 |                 if (logged_in) | 
 |                   s = clienthost; | 
 |                 else | 
 | #endif /* CK_LOGIN */ | 
 |                   s = (char *)ttname; | 
 |                 if (!s) s = ""; | 
 |                 if (!*s) s = "*"; | 
 | #ifdef CK_LOGIN | 
 |                 if (logged_in) { | 
 |                     p = guestpass; | 
 |                     if (!*p) p = "*"; | 
 |                 } else | 
 | #endif /* CK_LOGIN */ | 
 |                   p = whoami(); | 
 |  | 
 | 		len = 24 + 12 + (int)strlen(s) + 16 | 
 | 		  + (int)strlen(fullname) + 1 + 1 + 1 + 1 | 
 | 		    + (int)strlen(p) + 6 + 2 + 12; | 
 | 		fnam = fullname; | 
 | 		if (!*fnam) fnam = "(pipe)"; | 
 |  | 
 | 		if (len > IKSDMSGLEN) | 
 | 		  sprintf(iksdmsg,	/* SAFE */ | 
 |                         "%.24s [BUFFER WOULD OVERFLOW]\n",ctime(&timenow)); | 
 | 		else | 
 | 		  sprintf(iksdmsg,	/* SAFE */ | 
 |                         "%.24s %d %s %ld %s %c %s %c %c %s %s %d %s\n", | 
 |                         ctime(&timenow),        /* date/time */ | 
 |                         gtimer(),               /* elapsed secs */ | 
 |                         s,                      /* peer name */ | 
 |                         ffc,                    /* byte count */ | 
 |                         fnam,			/* full pathname of file */ | 
 |                         (binary ? 'b' : 'a'),   /* binary or ascii */ | 
 |                         "_",                    /* options = none */ | 
 |                         n == ZIFILE ? 'o' : 'i', /* in/out */ | 
 | #ifdef CK_LOGIN | 
 |                         (isguest ? 'a' : 'r'),  /* User type */ | 
 | #else | 
 |                         'r', | 
 | #endif /* CK_LOGIN */ | 
 |                         p,                      /* Username or guest passwd */ | 
 | #ifdef CK_LOGIN | 
 |                         logged_in ? "iks" : "kermit", /* Record ID */ | 
 | #else | 
 |                         "kermit", | 
 | #endif /* CK_LOGIN */ | 
 |                         0,              /* User ID on client system unknown */ | 
 |                         "*"             /* Ditto */ | 
 |                         ); | 
 |                 debug(F110,"zclose iksdmsg",iksdmsg,0); | 
 |                 write(xferlog, iksdmsg, (int)strlen(iksdmsg)); | 
 |             } | 
 |         } | 
 |         debug(F101,"zclose returns","",1); | 
 |         return(1); | 
 |     } | 
 | } | 
 |  | 
 | /*  Z C H I N  --  Get a character from the input file.  */ | 
 |  | 
 | /*  Returns -1 if EOF, 0 otherwise with character returned in argument  */ | 
 |  | 
 | int | 
 | zchin(n,c) int n; int *c; { | 
 |     int a; | 
 |  | 
 | #ifdef IKSD | 
 |     if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { | 
 |         a = coninc(0); | 
 |         if (*c < 0) | 
 |           return(-1); | 
 |     } else | 
 | #endif /* IKSD */ | 
 |     /* (PWP) Just in case this gets called when it shouldn't. */ | 
 |     if (n == ZIFILE) { | 
 |         a = zminchar();			/* Note: this catches Ctrl-Z */ | 
 |         if (a < 0)			/* (See zinfill()...) */ | 
 | 	  return(-1); | 
 |     } else { | 
 | 	a = getc(fp[n]); | 
 | 	if (a == EOF) return(-1); | 
 | #ifdef CK_CTRLZ | 
 | 	/* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */ | 
 | 	if (!binary && a == 0x1A && eofmethod == XYEOF_Z) | 
 | 	  return(-1); | 
 | #endif /* CK_CTRLZ */ | 
 |     } | 
 |     *c = (CHAR) a & 0377; | 
 |     return(0); | 
 | } | 
 |  | 
 | /*  Z S I N L  --  Read a line from a file  */ | 
 |  | 
 | /* | 
 |   Writes the line into the address provided by the caller. | 
 |   n is the Kermit "channel number". | 
 |   Writing terminates when newline is encountered, newline is not copied. | 
 |   Writing also terminates upon EOF or if length x is exhausted. | 
 |   Returns 0 on success, -1 on EOF or error. | 
 | */ | 
 | int | 
 | zsinl(n,s,x) int n, x; char *s; { | 
 |     int a, z = 0;                       /* z is return code. */ | 
 |     int count = 0; | 
 |     int len = 0; | 
 |     char *buf; | 
 |     extern CHAR feol;                   /* Line terminator */ | 
 |  | 
 |     if (!s || chkfn(n) < 1)             /* Make sure file is open, etc */ | 
 |       return(-1); | 
 |     buf = s; | 
 |     s[0] = '\0';                        /* Don't return junk */ | 
 |  | 
 |     a = -1;                             /* Current character, none yet. */ | 
 |     while (x--) {                       /* Up to given length */ | 
 |         int old = 0; | 
 |         if (feol)                       /* Previous character */ | 
 |           old = a; | 
 |         if (zchin(n,&a) < 0) {          /* Read a character from the file */ | 
 |             debug(F101,"zsinl zchin fail","",count); | 
 |             if (count == 0) | 
 |               z = -1;                   /* EOF or other error */ | 
 |             break; | 
 |         } else | 
 |           count++; | 
 |         if (feol) {                     /* Single-character line terminator */ | 
 |             if (a == feol) | 
 |               break; | 
 |         } else {                        /* CRLF line terminator */ | 
 |             if (a == '\015')            /* CR, get next character */ | 
 |               continue; | 
 |             if (old == '\015') {        /* Previous character was CR */ | 
 |                 if (a == '\012') {      /* This one is LF, so we have a line */ | 
 |                     break; | 
 |                 } else {                /* Not LF, deposit CR */ | 
 |                     *s++ = '\015'; | 
 |                     x--; | 
 |                     len++; | 
 |                 } | 
 |             } | 
 |         } | 
 |         *s = a;                         /* Deposit character */ | 
 |         s++; | 
 |         len++; | 
 |     } | 
 |     *s = '\0';                          /* Terminate the string */ | 
 |     debug(F011,"zsinl",buf,len); | 
 |     return(z); | 
 | } | 
 |  | 
 | /*  Z X I N  --  Read x bytes from a file  */ | 
 |  | 
 | /* | 
 |   Reads x bytes (or less) from channel n and writes them | 
 |   to the address provided by the caller. | 
 |   Returns number of bytes read on success, 0 on EOF or error. | 
 | */ | 
 | int | 
 | zxin(n,s,x) int n, x; char *s; { | 
 | #ifdef IKSD | 
 |     if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { | 
 |         int a, i; | 
 |         a = ttchk(); | 
 |         if (a < 1) return(0); | 
 |         for (i = 0; i < a && i < x; i++) | 
 |           s[i] = coninc(0); | 
 |         return(i); | 
 |     } | 
 | #endif /* IKSD */ | 
 |  | 
 |     return(fread(s, sizeof (char), x, fp[n])); | 
 | } | 
 |  | 
 | /* | 
 |   Z I N F I L L  --  Buffered file input. | 
 |  | 
 |   (re)fill the file input buffer with data.  All file input | 
 |   should go through this routine, usually by calling the zminchar() | 
 |   macro defined in ckcker.h.  Returns: | 
 |  | 
 |   Value 0..255 on success, the character that was read. | 
 |   -1 on end of file. | 
 |   -2 on any kind of error other than end of file. | 
 |   -3 timeout when reading from pipe (Kermit packet mode only). | 
 | */ | 
 | int | 
 | zinfill() { | 
 |     extern int kactive, srvping; | 
 |     errno = 0; | 
 |  | 
 | #ifdef ZDEBUG | 
 |     printf("ZINFILL fp[%d]=%ld\n",ZIFILE,fp[ZIFILE]); | 
 | #endif /* ZDEBUG */ | 
 |  | 
 | #ifdef IKSD | 
 |     if (inserver && !local && fp[ZIFILE] == stdin) { | 
 |         int a, i; | 
 |         a = ttchk(); | 
 |         if (a < 0) return(-2); | 
 |         for (i = 0; i < a && i < INBUFSIZE; i++) { | 
 |             zinbuffer[i] = coninc(0); | 
 |         } | 
 |         zincnt = i; | 
 |         /* set pointer to beginning, (== &zinbuffer[0]) */ | 
 |         zinptr = zinbuffer; | 
 |         if (zincnt == 0) return(-1); | 
 |         zincnt--;                       /* One less char in buffer */ | 
 |         return((int)(*zinptr++) & 0377); /* because we return the first */ | 
 |     } | 
 | #endif /* IKSD */ | 
 |  | 
 |     debug(F101,"zinfill kactive","",kactive); | 
 |  | 
 |     if (!(kactive && ispipe[ZIFILE])) { | 
 |         if (feof(fp[ZIFILE])) { | 
 |             debug(F100,"ZINFILL feof","",0); | 
 | #ifdef ZDEBUG | 
 |             printf("ZINFILL EOF\n"); | 
 | #endif /* ZDEBUG */ | 
 |             return(-1); | 
 |         } | 
 |     } | 
 |     clearerr(fp[ZIFILE]); | 
 |  | 
 | #ifdef SELECT | 
 |     /* Here we can call select() to get a timeout... */ | 
 |     if (kactive && ispipe[ZIFILE]) { | 
 |         int secs, z = 0; | 
 | #ifndef NOXFER | 
 |         if (srvping) { | 
 |             secs = 1; | 
 |             debug(F101,"zinfill calling ttwait","",secs); | 
 |             z = ttwait(fileno(fp[ZIFILE]),secs); | 
 |             debug(F101,"zinfill ttwait","",z); | 
 |         } | 
 | #endif /* NOXFER */ | 
 |         if (z == 0) | 
 |           return(-3); | 
 |     } | 
 | #endif /* SELECT */ | 
 |  | 
 | #ifdef DEBUG | 
 |     if (deblog) { | 
 |         int i; | 
 |         debug(F101,"ZINFILL INBUFSIZE","",INBUFSIZE); | 
 | #ifdef USE_MEMCPY | 
 |         memset(zinbuffer, 0xFF, INBUFSIZE); | 
 | #else | 
 |         for (i = 0; i < INBUFSIZE; i++) { | 
 |             zinbuffer[i] = 0xFF; | 
 | #ifdef COMMENT				/* Too much! */ | 
 |             debug(F101,"ZINFILL zinbuffer[i]","",i); | 
 | #endif /* COMMENT */ | 
 |         } | 
 | #endif /* USE_MEMCPY */ | 
 | 	ckstrncpy(zinbuffer,"zinbuffer is a valid buffer",INBUFSIZE); | 
 | 	debug(F111,"ZINFILL about to call fread",zinbuffer,zinbuffer); | 
 |     } | 
 | #endif /* DEBUG */ | 
 |  | 
 | /* | 
 |   Note: The following read MUST be nonblocking when reading from a pipe | 
 |   and we want timeouts to work.  See zxcmd(). | 
 | */ | 
 |     zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]); | 
 |     debug(F101,"ZINFILL fread","",zincnt); /* Just the size */ | 
 | #ifdef ZDEBUG | 
 |     printf("FREAD=%d\n",zincnt); | 
 | #endif /* ZDEBUG */ | 
 | #ifdef CK_CTRLZ | 
 |     /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */ | 
 |     if (zincnt > 0 && !binary && eofmethod == XYEOF_Z) { | 
 | 	register int i; | 
 | 	for (i = 0; i < zincnt; i++) { | 
 | 	    if (zinbuffer[i] == SUB) { | 
 | 		zincnt = i;		/* Stop at first Ctrl-Z */ | 
 | 		if (i == 0) | 
 | 		  return(-1); | 
 | 		break; | 
 | 	    } | 
 |         } | 
 |     } | 
 | #endif /* CK_CTRLZ */ | 
 |  | 
 |     if (zincnt == 0) {                  /* Got nothing? */ | 
 |         if (ferror(fp[ZIFILE])) { | 
 |             debug(F100,"ZINFILL ferror","",0); | 
 |             debug(F101,"ZINFILL errno","",errno); | 
 | #ifdef ZDEBUG | 
 |             printf("ZINFILL errno=%d\n",errno); | 
 | #endif /* ZDEBUG */ | 
 | #ifdef EWOULDBLOCK | 
 |             return((errno == EWOULDBLOCK) ? -3 : -2); | 
 | #else | 
 |             return(-2); | 
 | #endif /* EWOULDBLOCK */ | 
 |         } | 
 |  | 
 |     /* In case feof() didn't work just above -- sometimes it doesn't... */ | 
 |  | 
 |         if (feof(fp[ZIFILE]) ) { | 
 |             debug(F100,"ZINFILL count 0 EOF return -1","",0); | 
 |             return (-1); | 
 |         } else { | 
 |             debug(F100,"ZINFILL count 0 not EOF return -2","",0); | 
 |             return(-2); | 
 |         } | 
 |     } | 
 |     zinptr = zinbuffer;    /* set pointer to beginning, (== &zinbuffer[0]) */ | 
 |     zincnt--;                           /* One less char in buffer */ | 
 |     return((int)(*zinptr++) & 0377);    /* because we return the first */ | 
 | } | 
 |  | 
 | /*  Z S O U T  --  Write a string out to the given file, buffered.  */ | 
 |  | 
 | int | 
 | zsout(n,s) int n; char *s; { | 
 |     int rc = 0; | 
 |     rc = chkfn(n); | 
 |     if (rc < 1) return(-1);             /* Keep this, prevents memory faults */ | 
 |     if (!s) return(0);                  /* Null pointer, do nothing, succeed */ | 
 |     if (!*s) return(0);                 /* empty string, ditto */ | 
 |  | 
 | #ifdef IKSD | 
 |     /* | 
 |       This happens with client-side Kermit server when a REMOTE command | 
 |       was sent from the server to the client and the server is supposed to | 
 |       display the text, but of course there is no place to display it | 
 |       since it is in remote mode executing Kermit protocol. | 
 |     */ | 
 |     if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { | 
 | #ifdef COMMENT | 
 |         return(ttol(s,((int)strlen(s)) < 0) ? -1 : 0); | 
 | #else | 
 |         return(0); | 
 | #endif /* COMMENT */ | 
 |     } | 
 | #endif /* IKSD */ | 
 |  | 
 |     if (n == ZSFILE) | 
 |       return(write(fileno(fp[n]),s,(int)strlen(s))); | 
 |     rc = fputs(s,fp[n]) == EOF ? -1 : 0; | 
 |     if (n == ZWFILE) | 
 |       fflush(fp[n]); | 
 |     return(rc); | 
 | } | 
 |  | 
 | /*  Z S O U T L  --  Write string to file, with line terminator, buffered  */ | 
 |  | 
 | int | 
 | zsoutl(n,s) int n; char *s; { | 
 |     if (zsout(n,s) < 0) | 
 |         return(-1); | 
 |  | 
 | #ifdef IKSD | 
 |     if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { | 
 | #ifdef COMMENT | 
 |         return(ttoc(LF)); | 
 | #else | 
 |         return(0);                      /* See comments in zsout() */ | 
 | #endif /* COMMENT */ | 
 |     } | 
 | #endif /* IKSD */ | 
 |  | 
 |     if (n == ZSFILE)                    /* Session log is unbuffered */ | 
 |       return(write(fileno(fp[n]),"\n",1)); | 
 |     else if (fputs("\n",fp[n]) == EOF) | 
 |       return(-1); | 
 |     if (n == ZDIFIL || n == ZWFILE)     /* Flush connection log records */ | 
 |       fflush(fp[n]); | 
 |     return(0); | 
 | } | 
 |  | 
 | /*  Z S O U T X  --  Write x characters to file, unbuffered.  */ | 
 |  | 
 | int | 
 | zsoutx(n,s,x) int n, x; char *s; { | 
 | #ifdef IKSD | 
 |     if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { | 
 | #ifdef COMMENT | 
 |         return(ttol(s,x));              /* See comments in zsout() */ | 
 | #else | 
 |         return(x); | 
 | #endif /* COMMENT */ | 
 |     } | 
 | #endif /* IKSD */ | 
 |  | 
 | #ifdef COMMENT | 
 |     if (chkfn(n) < 1) return(-1); | 
 |     return(write(fp[n]->_file,s,x)); | 
 | #endif /* COMMENT */ | 
 |     return(write(fileno(fp[n]),s,x) == x ? x : -1); | 
 | } | 
 |  | 
 | /*  Z C H O U T  --  Add a character to the given file.  */ | 
 |  | 
 | /*  Should return 0 or greater on success, -1 on failure (e.g. disk full)  */ | 
 |  | 
 | int | 
 | #ifdef CK_ANSIC | 
 | zchout(register int n, char c) | 
 | #else | 
 | zchout(n,c) register int n; char c; | 
 | #endif /* CK_ANSIC */ | 
 | /* zchout() */ { | 
 |     /* if (chkfn(n) < 1) return(-1); */ | 
 |  | 
 | #ifdef IKSD | 
 |     if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { | 
 | #ifdef COMMENT | 
 |         return(ttoc(c)); | 
 | #else | 
 |         return(0);                      /* See comments in zsout() */ | 
 | #endif /* COMMENT */ | 
 |     } | 
 | #endif /* IKSD */ | 
 |  | 
 |     if (n == ZSFILE)                    /* Use unbuffered for session log */ | 
 |       return(write(fileno(fp[n]),&c,1) == 1 ? 0 : -1); | 
 |                                 /* Buffered for everything else */ | 
 |     if (putc(c,fp[n]) == EOF)   /* If true, maybe there was an error */ | 
 |       return(ferror(fp[n])?-1:0);       /* Check to make sure */ | 
 |     else                                /* Otherwise... */ | 
 |       return(0);                        /* There was no error. */ | 
 | } | 
 |  | 
 | /* (PWP) buffered character output routine to speed up file IO */ | 
 |  | 
 | int | 
 | zoutdump() { | 
 |     int x; | 
 |     char * zp; | 
 |     zoutptr = zoutbuffer;               /* Reset buffer pointer in all cases */ | 
 | #ifdef DEBUG | 
 |     if (deblog) | 
 |       debug(F101,"zoutdump zoutcnt","",zoutcnt); | 
 | #endif /* DEBUG */ | 
 |     if (zoutcnt == 0) {                 /* Nothing to output */ | 
 |         return(0); | 
 |     } else if (zoutcnt < 0) {           /* Unexpected negative argument */ | 
 |         zoutcnt = 0;                    /* Reset output buffer count */ | 
 |         return(-1);                     /* and fail. */ | 
 |     } | 
 |  | 
 | #ifdef IKSD | 
 |     if (inserver && !local && fp[ZOFILE] == stdout) { | 
 | #ifdef COMMENT | 
 |         x = ttol(zoutbuffer,zoutcnt); | 
 | #else | 
 |         x = 1;                          /* See comments in zsout() */ | 
 | #endif /* COMMENT */ | 
 |         zoutcnt = 0; | 
 |         return(x > 0 ? 0 : -1); | 
 |     } | 
 | #endif /* IKSD */ | 
 |  | 
 | /* | 
 |   Frank Prindle suggested that replacing this fwrite() by an fflush() | 
 |   followed by a write() would improve the efficiency, especially when | 
 |   writing to stdout.  Subsequent tests showed a 5-fold improvement. | 
 | */ | 
 | #ifdef COMMENT | 
 |     if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) ... | 
 | #endif /* COMMENT */ | 
 |  | 
 | #ifndef CK_NONBLOCK | 
 |     fflush(fp[ZOFILE]); | 
 | #endif /* CK_NONBLOCK */ | 
 |     zp = zoutbuffer; | 
 |     while (zoutcnt > 0) { | 
 |         if ((x = write(fileno(fp[ZOFILE]),zp,zoutcnt)) > -1) { | 
 | #ifdef DEBUG | 
 |             if (deblog)                 /* Save a function call... */ | 
 |               debug(F101,"zoutdump wrote","",x); | 
 | #endif /* DEBUG */ | 
 |             zoutcnt -= x;               /* Adjust output buffer count */ | 
 |             zp += x;                    /* and pointer */ | 
 |         } else { | 
 | #ifdef DEBUG | 
 |             if (deblog) { | 
 |                 debug(F101,"zoutdump write error","",errno); | 
 |                 debug(F101,"zoutdump write returns","",x); | 
 |             } | 
 | #endif /* DEBUG */ | 
 |             zoutcnt = 0;                /* Reset output buffer count */ | 
 |             return(-1);                 /* write() failed */ | 
 |         } | 
 |     } | 
 |     return(0); | 
 | } | 
 |  | 
 | /*  C H K F N  --  Internal function to verify file number is ok  */ | 
 |  | 
 | /* | 
 |  Returns: | 
 |   -1: File number n is out of range | 
 |    0: n is in range, but file is not open | 
 |    1: n in range and file is open | 
 | */ | 
 | int | 
 | chkfn(n) int n; { | 
 |     /* if (n != ZDFILE) debug(F101,"chkfn","",n); */ | 
 |     if (n < 0 || n >= ZNFILS) { | 
 |         if (n != ZDFILE) debug(F101,"chkfn out of range","",n); | 
 |         return(-1); | 
 |     } else { | 
 |         /* if (n != ZDFILE) debug(F101,"chkfn fp[n]","",fp[n]); */ | 
 |         return((fp[n] == NULL) ? 0 : 1); | 
 |     } | 
 | } | 
 |  | 
 | /*  Z G E T F S -- Return file size regardless of accessibility */ | 
 | /* | 
 |   Used for directory listings, etc. | 
 |   Returns: | 
 |     The size of the file in bytes, 0 or greater, if the size can be learned. | 
 |     -1 if the file size can not be obtained. | 
 |   Also (and this is a hack just for UNIX): | 
 |     If the argument is the name of a symbolic link, | 
 |     the global variable issymlink is set to 1, | 
 |     and the global buffer linkname[] gets the link value. | 
 |     And it sets zgfs_dir to 1 if it's a directory, otherwise 0. | 
 |   This lets us avoid numerous redundant calls to stat(). | 
 | */ | 
 | int zgfs_link = 0; | 
 | int zgfs_dir = 0; | 
 | time_t zgfs_mtime = 0; | 
 | unsigned int zgfs_mode = 0; | 
 |  | 
 | #ifdef CKSYMLINK | 
 | char linkname[CKMAXPATH+1]; | 
 | #ifndef _IFLNK | 
 | #define _IFLNK 0120000 | 
 | #endif /* _IFLNK */ | 
 | #endif /* CKSYMLINK */ | 
 |  | 
 | long | 
 | zgetfs(name) char *name; { | 
 |     struct stat buf; | 
 |     char fnam[CKMAXPATH+4]; | 
 |     long size = -1L; | 
 |     int x; | 
 |     int needrlink = 0; | 
 |     char * s; | 
 |  | 
 |     if (!name) name = ""; | 
 |     if (!*name) return(-1); | 
 |  | 
 | #ifdef UNIX | 
 |     x = strlen(name); | 
 |     if (x == 9 && !strcmp(name,"/dev/null")) | 
 |       return(0); | 
 | #endif /* UNIX */ | 
 |  | 
 |     s = name; | 
 | #ifdef DTILDE | 
 |     if (*s == '~') { | 
 |         s = tilde_expand(s); | 
 |         if (!s) s = ""; | 
 |         if (!*s) s = name; | 
 |     } | 
 | #endif /* DTILDE */ | 
 |     x = ckstrncpy(fnam,s,CKMAXPATH); | 
 |     s = fnam; | 
 |     debug(F111,"zgetfs fnam",s,x); | 
 |     if (x > 0 && s[x-1] == '/') | 
 |       s[x-1] = '\0'; | 
 |  | 
 |     zgfs_dir = 0;                       /* Assume it's not a directory */ | 
 |     zgfs_link = 0;                      /* Assume it's not a symlink */ | 
 |     zgfs_mtime = 0;			/* No time yet */ | 
 |     zgfs_mode = 0;			/* No permission bits yet */ | 
 |  | 
 | #ifdef CKSYMLINK                        /* We're doing symlinks? */ | 
 | #ifdef USE_LSTAT                        /* OK to use lstat()? */ | 
 |     x = lstat(s,&buf); | 
 |     debug(F101,"STAT","",1); | 
 |     if (x < 0)                          /* stat() failed */ | 
 |       return(-1); | 
 |     if (                                /* Now see if it's a symlink */ | 
 | #ifdef S_ISLNK | 
 |         S_ISLNK(buf.st_mode) | 
 | #else | 
 | #ifdef _IFLNK | 
 |         ((_IFMT & buf.st_mode) == _IFLNK) | 
 | #endif /* _IFLNK */ | 
 | #endif /* S_ISLNK */ | 
 |         ) { | 
 |         zgfs_link = 1;                  /* It's a symlink */ | 
 |         linkname[0] = '\0';             /* Get the name */ | 
 |         x = readlink(s,linkname,CKMAXPATH); | 
 |         debug(F101,"zgetfs readlink",s,x); | 
 |         if (x > -1 && x < CKMAXPATH) {  /* It's a link */ | 
 |             linkname[x] = '\0'; | 
 |             size = buf.st_size;         /* Remember size of link */ | 
 |             x = stat(s,&buf);           /* Now stat the linked-to file */ | 
 | 	    debug(F101,"STAT","",2); | 
 |             if (x < 0)                  /* so we can see if it's a directory */ | 
 |               return(-1); | 
 |         } else { | 
 |             ckstrncpy(linkname,"(lookup failed)",CKMAXPATH); | 
 |         } | 
 |     } | 
 | #else  /* !USE_LSTAT */ | 
 |     x = stat(s,&buf);                   /* No lstat(), use stat() instead */ | 
 |     debug(F101,"STAT","",3); | 
 |     if (x < 0) | 
 |       return(-1); | 
 | #endif /* USE_LSTAT */ | 
 |  | 
 |     /* Do we need to call readlink()? */ | 
 |  | 
 | #ifdef NOLINKBITS | 
 | /* | 
 |   lstat() does not work in SCO operating systems.  From "man NS lstat": | 
 |  | 
 |   lstat obtains information about the file named by path. In the case of a | 
 |   symbolic link, lstat returns information about the link, and not the file | 
 |   named by the link. It is only used by the NFS automount daemon and should | 
 |   not be utilized by users. | 
 | */ | 
 |     needrlink = 1; | 
 |     debug(F101,"zgetfs forced needrlink","",needrlink); | 
 | #else | 
 | #ifdef S_ISLNK | 
 |     needrlink = S_ISLNK(buf.st_mode); | 
 |     debug(F101,"zgetfs S_ISLNK needrlink","",needrlink); | 
 | #else | 
 | #ifdef _IFLNK | 
 |     needrlink = (_IFMT & buf.st_mode) == _IFLNK; | 
 |     debug(F101,"zgetfs _IFLNK needrlink","",needrlink); | 
 | #else | 
 |     needrlink = 1; | 
 |     debug(F101,"zgetfs default needrlink","",needrlink); | 
 | #endif /* _IFLNK */ | 
 | #endif /* S_ISLNK */ | 
 | #endif /* NOLINKBITS */ | 
 |  | 
 |     if (needrlink) { | 
 |         linkname[0] = '\0'; | 
 |         errno = 0; | 
 |         x = readlink(s,linkname,CKMAXPATH); | 
 | #ifdef DEBUG | 
 |         debug(F111,"zgetfs readlink",s,x); | 
 |         if (x < 0) | 
 |           debug(F101,"zgetfs readlink errno","",errno); | 
 |         else | 
 |           debug(F110,"zgetfs readlink result",linkname,0); | 
 | #endif /* DEBUG */ | 
 |         if (x > -1 && x < CKMAXPATH) { | 
 |             zgfs_link = 1; | 
 |             linkname[x] = '\0'; | 
 |         } | 
 |     } | 
 | #else  /* !CKSYMLINK */ | 
 |     x = stat(s,&buf);                   /* Just stat the file */ | 
 |     debug(F111,"zgetfs stat",s,x); | 
 |     if (x < 0)                          /* and get the size */ | 
 |       return(-1); | 
 | #endif /* CKSYMLINK */ | 
 |  | 
 |     zgfs_mtime = buf.st_mtime; | 
 |     zgfs_mode = buf.st_mode; | 
 |     zgfs_dir = (S_ISDIR(buf.st_mode)) ? 1 : 0; /* Set "is directory" flag */ | 
 |     debug(F111,"zgetfs size",s,size); | 
 |     debug(F111,"zgetfs st_size",s,buf.st_size); | 
 |     return((size < 0L) ? buf.st_size : size); /* Return the size */ | 
 | } | 
 |  | 
 |  | 
 | /*  Z C H K I  --  Check if input file exists and is readable  */ | 
 |  | 
 | /* | 
 |   Returns: | 
 |    >= 0 if the file can be read (returns the size). | 
 |      -1 if file doesn't exist or can't be accessed, | 
 |      -2 if file exists but is not readable (e.g. a directory file). | 
 |      -3 if file exists but protected against read access. | 
 |  | 
 |   For Berkeley Unix, a file must be of type "regular" to be readable. | 
 |   Directory files, special files, and symbolic links are not readable. | 
 | */ | 
 | long | 
 | zchki(name) char *name; { | 
 |     struct stat buf; | 
 |     char * s; | 
 |     int x, itsadir = 0; | 
 |     extern int zchkid, diractive, matchfifo; | 
 |  | 
 |     if (!name) | 
 |       return(-1); | 
 |     x = strlen(name); | 
 |     if (x < 1) | 
 |       return(-1); | 
 |     s = name; | 
 |  | 
 | #ifdef UNIX | 
 |     if (x == 9 && !strcmp(s,"/dev/null")) | 
 |       return(0); | 
 |     if (x == 8 && !strcmp(s,"/dev/tty")) | 
 |       return(0); | 
 | #endif /* UNIX */ | 
 |  | 
 | #ifdef DTILDE | 
 |     if (*s == '~') { | 
 |         s = tilde_expand(s); | 
 |         if (!s) s = ""; | 
 |         if (!*s) s = name; | 
 |     } | 
 | #endif /* DTILDE */ | 
 |  | 
 | #ifdef CKROOT | 
 |     debug(F111,"zchki setroot",ckroot,ckrootset); | 
 |     if (ckrootset) if (!zinroot(name)) { | 
 | 	debug(F110,"zchki setroot violation",name,0); | 
 | 	return(-1); | 
 |     } | 
 | #endif /* CKROOT */ | 
 |  | 
 |     x = stat(s,&buf); | 
 |     debug(F101,"STAT","",5); | 
 |     if (x < 0) { | 
 |         debug(F111,"zchki stat fails",s,errno); | 
 |         return(-1); | 
 |     } | 
 |     if (S_ISDIR (buf.st_mode)) | 
 |       itsadir = 1; | 
 |  | 
 |     if (!(itsadir && zchkid)) {         /* Unless this... */ | 
 |         if (!S_ISREG (buf.st_mode)      /* Must be regular file */ | 
 | #ifdef S_ISFIFO | 
 |             && (!matchfifo || !S_ISFIFO (buf.st_mode))  /* or FIFO */ | 
 | #endif /* S_ISFIFO */ | 
 |             ) { | 
 |             debug(F111,"zchki not regular file (or fifo)",s,matchfifo); | 
 |             return(-2); | 
 |         } | 
 |     } | 
 |     debug(F111,"zchki stat ok:",s,x); | 
 |  | 
 |     if (diractive) {			/* If listing don't check access */ | 
 | 	x = 1; | 
 |     } else { | 
 | #ifdef SW_ACC_ID | 
 | 	debug(F100,"zchki swapping ids for access()","",0); | 
 | 	priv_on(); | 
 | #endif /* SW_ACC_ID */ | 
 | 	if ((x = access(s,R_OK)) < 0) | 
 | 	  x = access(s,X_OK);		/* For RUN-class commands */ | 
 | #ifdef SW_ACC_ID | 
 | 	priv_off(); | 
 | 	debug(F100,"zchki swapped ids restored","",0); | 
 | #endif /* SW_ACC_ID */ | 
 |     } | 
 |     if (x < 0) {			/* Is the file accessible? */ | 
 |         debug(F111,"zchki access failed:",s,x); /* No */ | 
 |         return(-3); | 
 |     } else { | 
 |         iflen = buf.st_size;            /* Yes, remember size */ | 
 |         ckstrncpy(nambuf,s,CKMAXPATH);  /* and name globally. */ | 
 |         debug(F111,"zchki access ok:",s,iflen); | 
 |         return((iflen > -1L) ? iflen : 0L); | 
 |     } | 
 | } | 
 |  | 
 | /*  Z C H K O  --  Check if output file can be created  */ | 
 |  | 
 | /* | 
 |   Returns -1 if write permission for the file would be denied, 0 otherwise. | 
 |  | 
 |   NOTE: The design is flawed.  There is no distinction among: | 
 |    . Can I overwrite an existing file? | 
 |    . Can I create a file (or directory) in an existing directory? | 
 |    . Can I create a file (or directory) and its parent(s)? | 
 | */ | 
 | int | 
 | zchko(name) char *name; { | 
 |     int i, x, itsadir = 0; | 
 |     char *s; | 
 |     char * oname; | 
 |     extern int zchkod;                  /* Used by IF WRITEABLE */ | 
 |  | 
 |     debug(F110,"zchko entry",name,0); | 
 |  | 
 |     if (!name) return(-1);              /* Watch out for null pointer. */ | 
 |  | 
 |     oname = name; | 
 |  | 
 | #ifdef CKROOT | 
 |     debug(F111,"zchko setroot",ckroot,ckrootset); | 
 |     if (ckrootset) if (!zinroot(name)) { | 
 | 	debug(F110,"zchko setroot violation",name,0); | 
 | 	errno = EACCES; | 
 | 	return(-1); | 
 |     } | 
 | #endif /* CKROOT */ | 
 |  | 
 |     x = (int)strlen(name);              /* Get length of filename */ | 
 |     debug(F111,"zchko len",name,x); | 
 |     debug(F111,"zchko zchkod",name,zchkod); | 
 |  | 
 | #ifdef UNIX | 
 | /* | 
 |   Writing to null device is OK. | 
 | */ | 
 |     if (x == 9 && !strcmp(name,"/dev/null")) | 
 |       return(0); | 
 |     if (x == 8 && !strcmp(name,"/dev/tty")) | 
 |       return(0); | 
 | #endif /* UNIX */ | 
 |  | 
 |     s = name; | 
 | #ifdef DTILDE | 
 |     if (*s == '~') { | 
 |         s = tilde_expand(s); | 
 |         if (!s) s = ""; | 
 |         if (!*s) s = name; | 
 | 	x = strlen(s); | 
 |     } | 
 | #endif /* DTILDE */ | 
 |     name = s; | 
 |     s = NULL; | 
 | /* | 
 |   zchkod is a global flag meaning we're checking not to see if the directory | 
 |   file is writeable, but if it's OK to create files IN the directory. | 
 | */ | 
 |     if (!zchkod && isdir(name))         /* Directories are not writeable */ | 
 |       return(-1); | 
 |  | 
 |     s = malloc(x+3);                    /* Must copy because we can't */ | 
 |     if (!s) {                           /* write into our argument. */ | 
 |         fprintf(stderr,"zchko: Malloc error 46\n"); | 
 |         return(-1); | 
 |     } | 
 |     ckstrncpy(s,name,x+3); | 
 |  | 
 |     for (i = x; i > 0; i--) {           /* Strip filename from right. */ | 
 |         if (ISDIRSEP(s[i-1])) { | 
 |             itsadir = 1; | 
 |             break; | 
 |         } | 
 |     } | 
 |     debug(F101,"zchko i","",i); | 
 |     debug(F101,"zchko itsadir","",itsadir); | 
 |  | 
 | #ifdef COMMENT | 
 | /* X/OPEN XPG3-compliant systems fail if argument ends with "/"...  */ | 
 |     if (i == 0)                         /* If no path, use current directory */ | 
 |       strcpy(s,"./"); | 
 |     else                                /* Otherwise, use given one. */ | 
 |       s[i] = '\0'; | 
 | #else | 
 | #ifdef COMMENT | 
 | /* | 
 |   The following does not work for "foo/bar" where the foo directory does | 
 |   not exist even though we could create it: access("foo/.") fails, but | 
 |   access("foo") works OK. | 
 | */ | 
 | /* So now we use "path/." if path given, or "." if no path given. */ | 
 |     s[i++] = '.';                       /* Append "." to path. */ | 
 |     s[i] = '\0'; | 
 | #else | 
 | /* So NOW we strip path segments from the right as long as they don't */ | 
 | /* exist -- we only call access() for path segments that *do* exist.. */ | 
 | /* (But this isn't quite right either since now zchko(/foo/bar/baz/xxx) */ | 
 | /* succeeds when I have write access to foo and bar but baz doesn't exit.) */ | 
 |  | 
 |     if (itsadir && i > 0) { | 
 |         s[i-1] = '\0'; | 
 |         while (s[0] && !isdir(s)) { | 
 |             for (i = (int)strlen(s); i > 0; i--) { | 
 |                 if (ISDIRSEP(s[i-1])) { | 
 |                     s[i-1] = '\0'; | 
 |                     break; | 
 |                 } | 
 |             } | 
 |             if (i == 0) | 
 |               s[0] = '\0'; | 
 |         } | 
 |     } else { | 
 |         s[i++] = '.';                   /* Append "." to path. */ | 
 |         s[i] = '\0'; | 
 |     } | 
 | #endif /* COMMENT */ | 
 | #endif /* COMMENT */ | 
 |  | 
 |     if (!s[0]) | 
 |       ckstrncpy(s,".",x+3); | 
 |  | 
 | #ifdef SW_ACC_ID | 
 |     debug(F100,"zchko swapping ids for access()","",0); | 
 |     priv_on(); | 
 | #endif /* SW_ACC_ID */ | 
 |  | 
 |     x = access(s,W_OK);                 /* Check access of path. */ | 
 |  | 
 | #ifdef SW_ACC_ID | 
 |     priv_off(); | 
 |     debug(F100,"zchko swapped ids restored","",0); | 
 | #endif /* SW_ACC_ID */ | 
 |  | 
 |     if (x < 0) | 
 |       debug(F111,"zchko access failed:",s,errno); | 
 |     else | 
 |       debug(F111,"zchko access ok:",s,x); | 
 |     free(s);                            /* Free temporary storage */ | 
 |  | 
 |     return((x < 0) ? -1 : 0);           /* and return. */ | 
 | } | 
 |  | 
 | /*  Z D E L E T  --  Delete the named file.  */ | 
 |  | 
 | /* Returns: -1 on error, 0 on success */ | 
 |  | 
 | int | 
 | zdelet(name) char *name; { | 
 |     int x; | 
 | #ifdef CK_LOGIN | 
 |     if (isguest) | 
 |       return(-1); | 
 | #endif /* CK_LOGIN */ | 
 |  | 
 | #ifdef CKROOT | 
 |     debug(F111,"zdelet setroot",ckroot,ckrootset); | 
 |     if (ckrootset) if (!zinroot(name)) { | 
 | 	debug(F110,"zdelet setroot violation",name,0); | 
 | 	return(-1); | 
 |     } | 
 | #endif /* CKROOT */ | 
 |  | 
 |     x = unlink(name); | 
 |     debug(F111,"zdelet",name,x); | 
 | #ifdef CKSYSLOG | 
 |     if (ckxsyslog >= SYSLG_FC && ckxlogging) { | 
 |         fullname[0] = '\0'; | 
 |         zfnqfp(name,CKMAXPATH,fullname); | 
 |         debug(F110,"zdelet fullname",fullname,0); | 
 |         if (x < 0) | 
 |           syslog(LOG_INFO, "file[] %s: delete failed (%m)", fullname); | 
 |         else | 
 |           syslog(LOG_INFO, "file[] %s: delete ok", fullname); | 
 |     } | 
 | #endif /* CKSYSLOG */ | 
 |     return(x); | 
 | } | 
 |  | 
 | /*  Z R T O L  --  Convert remote filename into local form  */ | 
 |  | 
 | VOID | 
 | zrtol(name,name2) char *name, *name2; { | 
 |     nzrtol(name,name2,1,0,CKMAXPATH); | 
 | } | 
 |  | 
 | VOID | 
 | nzrtol(name,name2,fncnv,fnrpath,max) | 
 |     char *name, *name2; int fncnv, fnrpath, max; | 
 | { /* nzrtol */ | 
 |     char *s, *p; | 
 |     int flag = 0, n = 0; | 
 |     char fullname[CKMAXPATH+1]; | 
 |     int devnull = 0; | 
 |     int acase = 0; | 
 |     if (!name2) return; | 
 |     if (!name) name = ""; | 
 |  | 
 |     debug(F111,"nzrtol name",name,fncnv); | 
 |  | 
 | #ifdef DTILDE | 
 |     s = name; | 
 |     if (*s == '~') { | 
 |         s = tilde_expand(s); | 
 |         if (!s) s = ""; | 
 |         if (*s) name = s; | 
 |     } | 
 | #endif /* DTILDE */ | 
 |  | 
 |     /* Handle the path -- we don't have to convert its format, since */ | 
 |     /* the standard path format and our (UNIX) format are the same. */ | 
 |  | 
 |     fullname[0] = NUL; | 
 |     devnull = !strcmp(name,"/dev/null"); | 
 |  | 
 |     if (!devnull && fnrpath == PATH_OFF) { /* RECEIVE PATHNAMES OFF */ | 
 |         zstrip(name,&p); | 
 |         strncpy(fullname,p,CKMAXPATH); | 
 |     } else if (!devnull && fnrpath == PATH_ABS) { /* REC PATHNAMES ABSOLUTE */ | 
 |         strncpy(fullname,name,CKMAXPATH); | 
 |     } else if (!devnull && isabsolute(name)) { /* RECEIVE PATHNAMES RELATIVE */ | 
 | 	ckmakmsg(fullname,CKMAXPATH,".",name,NULL,NULL); | 
 |     } else {                            /* Ditto */ | 
 |         ckstrncpy(fullname,name,CKMAXPATH); | 
 |     } | 
 |     fullname[CKMAXPATH] = NUL; | 
 |     debug(F110,"nzrtol fullname",fullname,0); | 
 |  | 
 | #ifndef NOTRUNCATE | 
 | /* | 
 |   The maximum length for any segment of a filename is MAXNAMLEN, defined | 
 |   above.  On some platforms (at least QNX) if a segment exceeds this limit, | 
 |   the open fails with ENAMETOOLONG, so we must prevent it by truncating each | 
 |   overlong name segment to the maximum segment length before passing the | 
 |   name to open().  This must be done even when file names are literal, so as | 
 |   not to halt a file transfer unnecessarily. | 
 | */ | 
 |     { | 
 |         char buf[CKMAXPATH+1];          /* New temporary buffer on stack */ | 
 |         char *p = fullname;             /* Source and  */ | 
 |         char *s = buf;                  /* destination pointers */ | 
 |         int i = 0, n = 0; | 
 |         debug(F101,"nzrtol sizing MAXNAMLEN","",MAXNAMLEN); | 
 |         while (*p && n < CKMAXPATH) {   /* Copy name to new buffer */ | 
 |             if (++i > MAXNAMLEN) {      /* If this segment too long */ | 
 |                 while (*p && *p != '/') /* skip past the rest... */ | 
 |                   p++; | 
 |                 i = 0;                  /* and reset counter. */ | 
 |             } else if (*p == '/') {     /* End of this segment. */ | 
 |                 i = 0;                  /* Reset counter. */ | 
 |             } | 
 |             *s++ = *p++;                /* Copy this character. */ | 
 |             n++; | 
 |         } | 
 |         *s = NUL; | 
 |         ckstrncpy(fullname,buf,CKMAXPATH); /* Copy back to original buffer. */ | 
 |         debug(F111,"nzrtol sizing",fullname,n); | 
 |     } | 
 | #endif /* NOTRUNCATE */ | 
 |  | 
 |     if (!fncnv || devnull) {            /* Not converting */ | 
 |         ckstrncpy(name2,fullname,max);  /* We're done. */ | 
 |         return; | 
 |     } | 
 |     name = fullname;                    /* Converting */ | 
 |  | 
 |     p = name2; | 
 |     for (; *name != '\0' && n < maxnam; name++) { | 
 |         if (*name > SP) flag = 1;       /* Strip leading blanks and controls */ | 
 |         if (flag == 0 && *name < '!') | 
 |           continue; | 
 | 	if (fncnv > 0) { | 
 | 	    if (*name == SP) { | 
 | 		*p++ = '_'; | 
 | 		n++; | 
 | 		continue; | 
 | 	    } | 
 | 	    if (isupper(*name))		/* Check for mixed case */ | 
 | 	      acase |= 1; | 
 | 	    else if (islower(*name)) | 
 | 	      acase |= 2; | 
 | 	} | 
 |         *p++ = *name; | 
 |         n++; | 
 |     } | 
 |     *p-- = '\0';                        /* Terminate */ | 
 |     while (*p < '!' && p > name2)       /* Strip trailing blanks & controls */ | 
 |       *p-- = '\0'; | 
 |  | 
 |     if (*name2 == '\0') {               /* Nothing left? */ | 
 |         ckstrncpy(name2,"NONAME",max);	/* do this... */ | 
 |     } else if (acase == 1) {            /* All uppercase? */ | 
 |         p = name2;                      /* So convert all letters to lower */ | 
 |         while (*p) { | 
 |             if (isupper(*p)) | 
 |               *p = tolower(*p); | 
 |             p++; | 
 |         } | 
 |     } | 
 |     debug(F110,"nzrtol new name",name2,0); | 
 | } | 
 |  | 
 |  | 
 | /*  Z S T R I P  --  Strip device & directory name from file specification */ | 
 |  | 
 | /*  Strip pathname from filename "name", return pointer to result in name2 */ | 
 |  | 
 | static char work[CKMAXPATH+1]; | 
 |  | 
 | VOID | 
 | zstrip(name,name2) char *name, **name2; { | 
 |     char *cp, *pp; | 
 |     int n = 0; | 
 |  | 
 |     debug(F110,"zstrip before",name,0); | 
 |     if (!name) { *name2 = ""; return; } | 
 |     pp = work; | 
 | #ifdef DTILDE | 
 |     /* Strip leading tilde */ | 
 |     if (*name == '~') name++; | 
 |     debug(F110,"zstrip after tilde-stripping",name,0); | 
 | #endif /* DTILDE */ | 
 |     for (cp = name; *cp; cp++) { | 
 |         if (ISDIRSEP(*cp)) { | 
 |             pp = work; | 
 |             n = 0; | 
 |         } else { | 
 |             *pp++ = *cp; | 
 |             if (n++ >= CKMAXPATH) | 
 |               break; | 
 |         } | 
 |     } | 
 |     *pp = '\0';                         /* Terminate the string */ | 
 |     *name2 = work; | 
 |     debug(F110,"zstrip after",*name2,0); | 
 | } | 
 |  | 
 | /*  Z L T O R  --  Local TO Remote */ | 
 |  | 
 | VOID | 
 | zltor(name,name2) char *name, *name2; { | 
 |     nzltor(name,name2,1,0,CKMAXPATH); | 
 | } | 
 |  | 
 | /*  N Z L T O R  --  New Local TO Remote */ | 
 |  | 
 | /* | 
 |   fncnv = 0 for no conversion, > 0 for regular conversion, < 0 for minimal. | 
 | */ | 
 | VOID | 
 | nzltor(name,name2,fncnv,fnspath,max) | 
 |     char *name, *name2; int fncnv, fnspath, max; | 
 | { /* nzltor */ | 
 |     char *cp, *pp; | 
 | #ifdef COMMENT | 
 |     int dc = 0; | 
 | #endif /* COMMENT */ | 
 |     int n = 0; | 
 |     char *dotp = NULL; | 
 |     char *dirp = NULL; | 
 |     char fullname[CKMAXPATH+1]; | 
 |     char *p; | 
 |     CHAR c; | 
 |  | 
 | #ifndef NOCSETS | 
 |     extern int fcharset, /* tcharset, */ language; | 
 |     int langsv; | 
 |     _PROTOTYP ( CHAR (*sxo), (CHAR) ) = NULL; /* Translation functions */ | 
 | #ifdef CK_ANSIC | 
 |     extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); | 
 | #else | 
 |     extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(); | 
 | #endif /* CK_ANSIC */ | 
 |     langsv = language; | 
 |     language = L_USASCII; | 
 | #ifdef COMMENT | 
 |     /* Proper translation of filenames must be done elsewhere */ | 
 |     n = tcharset ? tcharset : TC_USASCII; | 
 |     sxo = xls[n][fcharset]; | 
 | #else | 
 |     sxo = xls[TC_USASCII][fcharset]; | 
 | #endif /* COMMENT */ | 
 | #endif /* NOCSETS */ | 
 |  | 
 |     debug(F110,"nzltor name",name,0); | 
 |  | 
 |     /* Handle pathname */ | 
 |  | 
 |     fullname[0] = NUL; | 
 |     if (fnspath == PATH_OFF) {          /* PATHNAMES OFF */ | 
 |         zstrip(name,&p); | 
 |         ckstrncpy(fullname,p,CKMAXPATH); | 
 |     } else {                            /* PATHNAMES RELATIVE or ABSOLUTE */ | 
 |         char * p = name; | 
 |         while (1) { | 
 |             if (!strncmp(p,"../",3)) | 
 |               p += 3; | 
 |             else if (!strncmp(p,"./",2)) | 
 |               p += 2; | 
 |             else | 
 |               break; | 
 |         } | 
 |         if (fnspath == PATH_ABS) {      /* ABSOLUTE */ | 
 |             zfnqfp(p,CKMAXPATH,fullname); | 
 |         } else {                        /* RELATIVE */ | 
 |             ckstrncpy(fullname,p,CKMAXPATH); | 
 |         } | 
 |     } | 
 |     debug(F110,"nzltor fullname",fullname,0); | 
 |  | 
 |     if (!fncnv) {                       /* Not converting */ | 
 |         ckstrncpy(name2,fullname,max);  /* We're done. */ | 
 | #ifndef NOCSETS | 
 |         langsv = language; | 
 | #endif /* NOCSETS */ | 
 |         return; | 
 |     } | 
 |     name = fullname;                    /* Converting */ | 
 |  | 
 | #ifdef aegis | 
 |     char *namechars; | 
 |     int tilde = 0, bslash = 0; | 
 |  | 
 |     if ((namechars = getenv("NAMECHARS")) != NULL) { | 
 |         if (ckstrchr(namechars, '~' ) != NULL) tilde  = '~'; | 
 |         if (ckstrchr(namechars, '\\') != NULL) bslash = '\\'; | 
 |     } else { | 
 |         tilde = '~'; | 
 |         bslash = '\\'; | 
 |     } | 
 | #endif /* aegis */ | 
 |  | 
 |     pp = work;                          /* Output buffer */ | 
 |     for (cp = name, n = 0; *cp && n < max; cp++,n++) { /* Convert name chars */ | 
 |         c = *cp; | 
 | #ifndef NOCSETS | 
 |         if (sxo) c = (*sxo)(c);         /* Convert to ASCII */ | 
 | #endif /* NOCSETS */ | 
 |         if (fncnv > 0 && islower(c))	/* Uppercase letters */ | 
 |           *pp++ = toupper(c);           /* Change tilde to hyphen */ | 
 |         else if (c == '~') | 
 |           *pp++ = '-'; | 
 |         else if (fncnv > 0 && c == '#')	/* Change number sign to 'X' */ | 
 |           *pp++ = 'X'; | 
 |         else if (c == '*' || c == '?')  /* Change wildcard chars to 'X' */ | 
 |           *pp++ = 'X'; | 
 |         else if (c == ' ')              /* Change space to underscore */ | 
 |           *pp++ = '_'; | 
 |         else if (c < ' ')               /* Change controls to 'X' */ | 
 |           *pp++ = 'X'; | 
 |         else if (fncnv > 0 && c == '.') { /* Change dot to underscore */ | 
 |             dotp = pp;                  /* Remember where we last did this */ | 
 |             *pp++ = '_'; | 
 |         } else { | 
 |             if (c == '/') | 
 |               dirp = pp; | 
 |             *pp++ = c; | 
 |         } | 
 |     } | 
 |     *pp = NUL;                          /* Tie it off. */ | 
 | #ifdef COMMENT | 
 |     if (dotp) *dotp = '.';              /* Restore last dot (if any) */ | 
 | #else | 
 |     if (dotp > dirp) *dotp = '.';       /* Restore last dot in file name */ | 
 | #endif /* COMMENT */ | 
 |     cp = name2;                         /* If nothing before dot, */ | 
 |     if (*work == '.') *cp++ = 'X';      /* insert 'X' */ | 
 |     ckstrncpy(cp,work,max); | 
 | #ifndef NOCSETS | 
 |     language = langsv; | 
 | #endif /* NOCSETS */ | 
 |     debug(F110,"nzltor name2",name2,0); | 
 | } | 
 |  | 
 |  | 
 | /*  Z C H D I R  --  Change directory  */ | 
 | /* | 
 |   Call with: | 
 |     dirnam = pointer to name of directory to change to, | 
 |       which may be "" or NULL to indicate user's home directory. | 
 |   Returns: | 
 |     0 on failure | 
 |     1 on success | 
 | */ | 
 | int | 
 | zchdir(dirnam) char *dirnam; { | 
 |     char *hd, *sp; | 
 | #ifdef IKSDB | 
 |     _PROTOTYP (int slotdir,(char *,char *)); | 
 | #endif /* IKSDB */ | 
 | #ifndef NOSPL | 
 |     extern struct mtab *mactab;             /* Main macro table */ | 
 |     extern int nmac;                        /* Number of macros */ | 
 | #endif /* NOSPL */ | 
 |  | 
 |     debug(F110,"zchdir",dirnam,0); | 
 |     if (!dirnam) dirnam = ""; | 
 |     if (!*dirnam)                       /* If argument is null or empty, */ | 
 |       dirnam = zhome();                 /* use user's home directory. */ | 
 |     sp = dirnam; | 
 |     debug(F110,"zchdir 2",dirnam,0); | 
 |  | 
 | #ifdef DTILDE | 
 |     hd = tilde_expand(dirnam);          /* Attempt to expand tilde */ | 
 |     if (!hd) hd = ""; | 
 |     if (*hd == '\0') hd = dirnam;       /* in directory name. */ | 
 | #else | 
 |     hd = dirnam; | 
 | #endif /* DTILDE */ | 
 |     debug(F110,"zchdir 3",hd,0); | 
 |  | 
 | #ifdef CKROOT | 
 |     debug(F111,"zchdir setroot",ckroot,ckrootset); | 
 |     if (ckrootset) if (!zinroot(hd)) { | 
 | 	debug(F110,"zchdir setroot violation",hd,0); | 
 | 	return(0); | 
 |     } | 
 | #endif /* CKROOT */ | 
 |  | 
 | #ifdef pdp11 | 
 |     /* Just to save some space */ | 
 |     return((chdir(hd) == 0) ? 1 : 0); | 
 | #else | 
 |     if (chdir(hd) == 0) {                       /* Try to cd */ | 
 | #ifdef IKSDB | 
 | #ifdef CK_LOGIN | 
 |         if (inserver && ikdbopen) | 
 |           slotdir(isguest ? anonroot : "", zgtdir()); | 
 | #endif /* CK_LOGIN */ | 
 | #endif /* IKSDB */ | 
 |  | 
 | #ifndef NOSPL | 
 |         if (nmac) {			/* Any macros defined? */ | 
 |             int k;			/* Yes */ | 
 |             static int on_cd = 0; | 
 |             if (!on_cd) { | 
 |                 on_cd = 1; | 
 |                 k = mlook(mactab,"on_cd",nmac);   /* Look this up */ | 
 |                 if (k >= 0) {                     /* If found, */ | 
 |                     if (dodo(k,zgtdir(),0) > -1)  /* set it up, */ | 
 | 		      parser(1);                  /* and execute it */ | 
 |                 } | 
 |                 on_cd = 0; | 
 |             } | 
 |         } | 
 | #endif /* NOSPL */ | 
 |         return(1); | 
 |     } | 
 |     return(0); | 
 | #endif /* pdp11 */ | 
 | } | 
 |  | 
 | int | 
 | #ifdef CK_ANSIC | 
 | zchkpid(unsigned long xpid) | 
 | #else | 
 | zchkpid(xpid) unsigned long xpid; | 
 | #endif /* CK_ANSIC */ | 
 | { | 
 |     return((kill((PID_T)xpid,0) < 0) ? 0 : 1); | 
 | } | 
 |  | 
 |  | 
 | /*  Z H O M E  --  Return pointer to user's home directory  */ | 
 |  | 
 | static char * zhomdir = NULL; | 
 |  | 
 | char * | 
 | zhome() { | 
 |     char * home; | 
 |  | 
 | #ifdef CKROOT | 
 |     if (ckrootset) | 
 |       return((char *)ckroot); | 
 | #endif /* CKROOT */ | 
 |  | 
 | #ifdef Plan9 | 
 |     home = getenv("home"); | 
 | #else | 
 |     home = getenv("HOME"); | 
 | #endif /* Plan9 */ | 
 |     makestr(&zhomdir,home); | 
 |     return(home ? zhomdir : "."); | 
 | } | 
 |  | 
 | /*  Z G T D I R  --  Returns a pointer to the current directory  */ | 
 |  | 
 | /* | 
 |   The "preferred" interface for getting the current directory in modern UNIX | 
 |   is getcwd() [POSIX 1003.1 5.2.2].  However, on certain platforms (such as | 
 |   SunOS), it is implemented by forking a shell, feeding it the pwd command, | 
 |   and returning the result, which is not only inefficient but also can result | 
 |   in stray messages to the terminal.  In such cases -- as well as when | 
 |   getcwd() is not available at all -- getwd() can be used instead by defining | 
 |   USE_GETWD.  However, note that getwd() provides no buffer-length argument | 
 |   and therefore no safeguard against memory leaks. | 
 | */ | 
 | #ifndef USE_GETWD | 
 | #ifdef BSD42 | 
 | #define USE_GETWD | 
 | #else | 
 | #ifdef SUNOS4 | 
 | #define USE_GETWD | 
 | #endif /* SUNOS4 */ | 
 | #endif /* BSD42 */ | 
 | #endif /* USE_GETWD */ | 
 |  | 
 | #ifdef pdp11 | 
 | #define CWDBL 80                        /* Save every byte we can... */ | 
 | #else | 
 | #define CWDBL CKMAXPATH | 
 | #endif /* pdp11 */ | 
 | static char cwdbuf[CWDBL+2]; | 
 | /* | 
 |   NOTE: The getcwd() prototypes are commented out on purpose.  If you get | 
 |   compile-time warnings, search through your system's header files to see | 
 |   which one has the needed prototype, and #include it.  Usually it is | 
 |   <unistd.h>.  See the section for including <unistd.h> in ckcdeb.h and | 
 |   make any needed adjustments there (and report them). | 
 | */ | 
 | char * | 
 | zgtdir() { | 
 |     char * buf = cwdbuf; | 
 |     char * s; | 
 |  | 
 | #ifdef USE_GETWD | 
 |     extern char *getwd(); | 
 |     s = getwd(buf); | 
 |     debug(F110,"zgtdir BSD4 getwd()",s,0); | 
 |     if (!s) s = "./"; | 
 |     return(s); | 
 | #else | 
 | #ifdef BSD44 | 
 | #ifdef DCLGETCWD | 
 | _PROTOTYP( char * getcwd, (char *, SIZE_T) ); | 
 | #endif /* DCLGETCWD */ | 
 |     debug(F101,"zgtdir BSD44 CWDBL","",CWDBL); | 
 |     s = getcwd(buf,CWDBL); | 
 |     if (!s) s = "./"; | 
 |     return(s); | 
 | #else | 
 | #ifdef MINIX2 | 
 | #ifdef DCLGETCWD | 
 |     _PROTOTYP( char * getcwd, (char *, SIZE_T) ); | 
 | #endif /* DCLGETCWD */ | 
 |     debug(F101,"zgtdir MINIX2 CWDBL","",CWDBL); | 
 |     s = getcwd(buf,CWDBL); | 
 |     if (!s) s = "./"; | 
 |     return(s); | 
 | #else | 
 | #ifdef SVORPOSIX | 
 | #ifdef COMMENT | 
 | /* This non-ANSI prototype can be fatal at runtime! (e.g. in SCO3.2v5.0.5). */ | 
 | /* Anyway it's already prototyped in some header file that we have included. */ | 
 |     extern char *getcwd(); | 
 | #else | 
 | #ifdef DCLGETCWD | 
 |     _PROTOTYP( char * getcwd, (char *, SIZE_T) ); | 
 | #endif /* DCLGETCWD */ | 
 | #endif /* COMMENT */ | 
 |     debug(F101,"zgtdir SVORPOSIX CWDBL","",CWDBL); | 
 |     s = getcwd(buf,CWDBL); | 
 |     if (!s) s = "./"; | 
 |     return(s); | 
 | #else | 
 | #ifdef COHERENT | 
 | #ifdef _I386 | 
 | #ifdef DCLGETCWD | 
 |     extern char *getcwd(); | 
 | #endif /* DCLGETCWD */ | 
 |     debug(F101,"zgtdir COHERENT _I386 CWDBL","",CWDBL); | 
 |     s = getcwd(buf,CWDBL); | 
 |     if (!s) s = "./"; | 
 |     return(s); | 
 | #else | 
 |     extern char *getwd(); | 
 |     debug(F101,"zgtdir COHERENT CWDBL","",CWDBL); | 
 |     s = getwd(buf); | 
 |     if (!s) s = "./"; | 
 |     return(s); | 
 | #endif /* _I386 */ | 
 | #else | 
 | #ifdef SUNOS4 | 
 |     debug(F101,"zgtdir SUNOS CWDBL","",CWDBL); | 
 |     s = getcwd(buf,CWDBL); | 
 |     if (!s) s = "./"; | 
 |     return(s); | 
 | #else | 
 |     return("./"); | 
 | #endif /* SUNOS4 */ | 
 | #endif /* COHERENT */ | 
 | #endif /* SYSVORPOSIX */ | 
 | #endif /* MINIX2 */ | 
 | #endif /* BSD44 */ | 
 | #endif /* USE_GETWD */ | 
 | } | 
 |  | 
 | /*  Z X C M D -- Run a system command so its output can be read like a file */ | 
 |  | 
 | #ifndef NOPUSH | 
 | int | 
 | zxcmd(filnum,comand) int filnum; char *comand; { | 
 |     int out; | 
 |     int pipes[2]; | 
 |     extern int kactive;                 /* From ckcpro.w and ckcmai.c */ | 
 |  | 
 |     if (nopush) { | 
 |         debug(F100,"zxcmd fails: nopush","",0); | 
 |         return(-1); | 
 |     } | 
 |     debug(F111,"zxcmd",comand,filnum); | 
 |     if (chkfn(filnum) < 0) return(-1);  /* Need a valid Kermit file number. */ | 
 |     if (filnum == ZSTDIO || filnum == ZCTERM) /* But not one of these. */ | 
 |       return(0); | 
 |  | 
 |     out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ; | 
 |     debug(F101,"zxcmd out",comand,out); | 
 |  | 
 | /* Output to a command */ | 
 |  | 
 |     if (out) {                          /* Need popen() to do this. */ | 
 | 	ckstrncpy(fullname,"(pipe)",CKMAXPATH); | 
 | #ifdef NOPOPEN | 
 |         return(0);                      /* no popen(), fail. */ | 
 | #else | 
 | /* Use popen() to run the command. */ | 
 |  | 
 | #ifdef _POSIX_SOURCE | 
 | /* Strictly speaking, popen() is not available in POSIX.1 */ | 
 | #define DCLPOPEN | 
 | #endif /* _POSIX_SOURCE */ | 
 |  | 
 | 	debug(F110,"zxcmd out",comand,0); | 
 |  | 
 |         if (priv_chk()) { | 
 | 	    debug(F100,"zxcmd priv_chk failed","",0); | 
 |             return(0); | 
 | 	}	 | 
 | 	errno = 0; | 
 |         fp[filnum] = popen(comand,"w"); | 
 | 	debug(F111,"zxcmd popen",fp[filnum] ? "OK" : "Failed", errno); | 
 | 	if (fp[filnum] == NULL) | 
 | 	  return(0); | 
 | #ifdef COMMENT | 
 | /* I wonder what this is all about... */ | 
 | 	close(pipes[0]);		/* Don't need the input side */ | 
 | 	fp[filnum] = fdopen(pipes[1],"w"); /* Open output stream. */ | 
 | 	fp[ZSYSFN] = fp[filnum];           /* Remember. */ | 
 | #endif /* COMMENT */ | 
 | 	ispipe[filnum] = 1; | 
 | 	zoutcnt = 0;			/* (PWP) reset input buffer */ | 
 | 	zoutptr = zoutbuffer; | 
 | 	return(1); | 
 | #endif /* NOPOPEN */ | 
 |     } | 
 |  | 
 | /* Input from a command */ | 
 |  | 
 | #ifdef SNI541 | 
 |     /* SINIX-L 5.41 does not like fdopen() */ | 
 |     return(0); | 
 | #else | 
 |     if (pipe(pipes) != 0) { | 
 |         debug(F100,"zxcmd pipe failure","",0); | 
 |         return(0);                      /* can't make pipe, fail */ | 
 |     } | 
 |  | 
 | /* Create a fork in which to run the named process */ | 
 |  | 
 |     if (( | 
 | #ifdef aegis | 
 |          pid = vfork()                  /* child */ | 
 | #else | 
 |          pid = fork()                   /* child */ | 
 | #endif /* aegis */ | 
 |          ) == 0) { | 
 |  | 
 | /* We're in the fork. */ | 
 |  | 
 |         char *shpath, *shname, *shptr;  /* Find user's preferred shell */ | 
 | #ifndef aegis | 
 |         struct passwd *p; | 
 |         char *defshell; | 
 | #ifdef HPUX10                           /* Default shell */ | 
 |         defshell = "/usr/bin/sh"; | 
 | #else | 
 | #ifdef Plan9 | 
 |         defshell = "/bin/rc"; | 
 | #else | 
 |         defshell = "/bin/sh"; | 
 | #endif /* Plan9 */ | 
 | #endif /* HPUX10 */ | 
 | #endif /* aegis */ | 
 |         if (priv_can()) exit(1);        /* Turn off any privileges! */ | 
 |         debug(F101,"zxcmd pid","",pid); | 
 |         close(pipes[0]);                /* close input side of pipe */ | 
 |         close(0);                       /* close stdin */ | 
 |         if (open("/dev/null",0) < 0) return(0); /* replace input by null */ | 
 | #ifndef OXOS | 
 | #ifndef SVORPOSIX | 
 |         dup2(pipes[1],1);               /* BSD: replace stdout & stderr */ | 
 |         dup2(pipes[1],2);               /* by the pipe */ | 
 | #else | 
 |         close(1);                       /* AT&T: close stdout */ | 
 |         if (dup(pipes[1]) != 1)         /* Send stdout to the pipe */ | 
 |           return(0); | 
 |         close(2);                       /* Send stderr to the pipe */ | 
 |         if (dup(pipes[1]) != 2) | 
 |           return(0); | 
 | #endif /* SVORPOSIX */ | 
 | #else /* OXOS */ | 
 |         dup2(pipes[1],1); | 
 |         dup2(pipes[1],2); | 
 | #endif /* OXOS */ | 
 |         close(pipes[1]);                /* Don't need this any more. */ | 
 |  | 
 | #ifdef aegis | 
 |         if ((shpath = getenv("SERVERSHELL")) == NULL) | 
 |           shpath = "/bin/sh"; | 
 | #else | 
 |         shpath = getenv("SHELL");       /* What shell? */ | 
 |         if (shpath == NULL) { | 
 |             p = getpwuid( real_uid() ); /* Get login data */ | 
 |             debug(F111,"zxcmd shpath","getpwuid()",p); | 
 |             if (p == (struct passwd *)NULL || !*(p->pw_shell)) | 
 |               shpath = defshell; | 
 |             else shpath = p->pw_shell; | 
 |         } | 
 | #endif /* aegis */ | 
 |         shptr = shname = shpath; | 
 |         while (*shptr != '\0') | 
 |           if (*shptr++ == '/') | 
 |             shname = shptr; | 
 |         debug(F110,shpath,shname,0); | 
 | 	restorsigs();			/* Restore ignored signals */ | 
 |         execl(shpath,shname,"-c",comand,(char *)NULL); /* Execute the cmd */ | 
 |         exit(0);                        /* just punt if it failed. */ | 
 |     } else if (pid == (PID_T) -1) { | 
 |         debug(F100,"zxcmd fork failure","",0); | 
 |         return(0); | 
 |     } | 
 |     debug(F101,"zxcmd pid","",pid); | 
 |     close(pipes[1]);                    /* Don't need the output side */ | 
 |     ispipe[filnum] = 1;                 /* Remember it's a pipe */ | 
 |     fp[filnum] = fdopen(pipes[0],"r");	/* Open a stream for input. */ | 
 |  | 
 | #ifdef DONDELAY | 
 | #ifdef SELECT | 
 |     if (filnum == ZIFILE && kactive) {  /* Make pipe reads nonblocking */ | 
 |         int flags, x; | 
 |         if ((flags = fcntl(fileno(fp[filnum]),F_GETFL,0)) > -1) { | 
 |             debug(F101,"zxcmd fcntl 1 pipe flags","",flags); | 
 |             x = fcntl(fileno(fp[filnum]),F_SETFL, flags | | 
 | #ifdef QNX | 
 |                   O_NONBLOCK | 
 | #else | 
 |                   O_NDELAY | 
 | #endif /* QNX */ | 
 |                   ); | 
 |             debug(F101,"zxcmd fcntl 2 result","",x); | 
 |         } | 
 |     } | 
 | #endif /* SELECT */ | 
 | #endif /* DONDELAY */ | 
 | #endif /* SNI541 */ | 
 |     fp[ZSYSFN] = fp[filnum];            /* Remember. */ | 
 |     zincnt = 0;                         /* (PWP) reset input buffer */ | 
 |     zinptr = zinbuffer; | 
 |     fullname[0] = '\0'; | 
 |     return(1); | 
 | } /* zxcmd */ | 
 |  | 
 | /*  Z C L O S F  - wait for the child fork to terminate and close the pipe. */ | 
 |  | 
 | /*  Used internally by zclose - returns -1 on failure, 1 on success. */ | 
 |  | 
 | int | 
 | zclosf(filnum) int filnum; { | 
 |     int wstat, out; | 
 |     int statusp; | 
 |  | 
 |     debug(F101,"zclosf filnum","",filnum); | 
 |     out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ; | 
 |     debug(F101,"zclosf out","",out); | 
 |  | 
 | #ifndef NOPOPEN | 
 |     if (ispipe[filnum] | 
 |         /* In UNIX we use popen() only for output files */ | 
 |         && out | 
 |         ) { | 
 |         int x; | 
 |         x = pclose(fp[filnum]); | 
 |         pexitstat = x >> 8; | 
 |         debug(F101,"zclosf pclose","",x); | 
 |         debug(F101,"zclosf pexitstat","",pexitstat); | 
 |         fp[filnum] = fp[ZSYSFN] = NULL; | 
 |         ispipe[filnum] = 0; | 
 |         return((x != 0) ? -1 : 1); | 
 |     } | 
 | #endif /* NOPOPEN */ | 
 |     debug(F101,"zclosf fp[filnum]","", fp[filnum]); | 
 |     debug(F101,"zclosf fp[ZSYSFN]","", fp[ZSYSFN]); | 
 |  | 
 |     if (pid != (PID_T) 0) { | 
 |         debug(F101,"zclosf killing pid","",pid); | 
 | #ifdef Plan9 | 
 |         kill(pid, SIGKILL); | 
 | #else | 
 |         kill(pid,9); | 
 | #endif /* Plan9 */ | 
 |  | 
 | #ifndef CK_CHILD | 
 | /* | 
 |   This is the original code (before 20 April 1997) and has proven totally | 
 |   portable.  But it does not give us the process's return code. | 
 | */ | 
 |         while ((wstat = wait((WAIT_T *)0)) != pid && wstat != -1) ; | 
 | #else | 
 | /* Here we try to get the return code.  Let's hope this is portable too. */ | 
 |         while ((wstat = wait(&statusp)) != pid && wstat != -1) ; | 
 |         pexitstat = (statusp & 0xff) ? statusp : statusp >> 8; | 
 |         debug(F101,"zclosf wait statusp","",statusp); | 
 |         debug(F101,"zclosf wait pexitstat","",pexitstat); | 
 | #endif /* CK_CHILD */ | 
 |         pid = 0; | 
 |     } | 
 |     fclose(fp[filnum]); | 
 |     fp[filnum] = fp[ZSYSFN] = NULL; | 
 |  | 
 |     ispipe[filnum] = 0; | 
 |     debug(F101,"zclosf fp[filnum]","",fp[filnum]); | 
 | #ifdef CK_CHILD | 
 |     return(pexitstat == 0 ? 1 : -1); | 
 | #else | 
 |     return(1); | 
 | #endif /* CK_CHILD */ | 
 | } | 
 |  | 
 | #else  /* NOPUSH */ | 
 |  | 
 | int | 
 | zxcmd(filnum,comand) int filnum; char *comand; { | 
 |     return(0); | 
 | } | 
 | int | 
 | zclosf(filnum) int filnum; { | 
 |     return(EOF); | 
 | } | 
 | #endif /* NOPUSH */ | 
 |  | 
 |  | 
 | /*  Z X P A N D  --  Expand a wildcard string into an array of strings  */ | 
 | /* | 
 |   As of C-Kermit 7.0, this API is obsolete, replaced by nzxpand(), and this | 
 |   function is only used internally.  See nzxpand() below. | 
 |  | 
 |   Returns the number of files that match fnarg, with data structures set up | 
 |   so that first file (if any) will be returned by the next znext() call. | 
 |  | 
 |   Depends on external variable wildxpand: 0 means we expand wildcards | 
 |   internally, nonzero means we call the shell to do it. | 
 | */ | 
 | static int xdironly = 0; | 
 | static int xfilonly = 0; | 
 | static int xmatchdot = 0; | 
 | static int xrecursive = 0; | 
 | static int xnobackup = 0; | 
 | static int xnolinks = 0; | 
 |  | 
 | static char *freeptr = NULL, **resptr = NULL; /* Copies of caller's args */ | 
 | static int remlen;                      /* Remaining space in caller's array */ | 
 | static int numfnd = 0;			/* Number of matches found */ | 
 |  | 
 | #define MINSPACE 1024 | 
 |  | 
 | static int | 
 | initspace(resarry,len) char * resarry[]; int len; { | 
 | #ifdef DYNAMIC | 
 |     if (len < MINSPACE) len = MINSPACE; | 
 |     if (!sspace) {                      /* Need to allocate string space? */ | 
 |         while (len >= MINSPACE) { | 
 |             if ((sspace = malloc(len+2))) { /* Got it. */ | 
 |                 debug(F101,"fgen string space","",len); | 
 |                 break; | 
 |             } | 
 |             len = (len / 2) + (len / 4); /* Didn't, reduce by 3/4 */ | 
 |         } | 
 |         if (len <= MINSPACE) {		/* Did we get it? */ | 
 |             fprintf(stderr,"fgen can't malloc string space\n"); | 
 |             return(-1); | 
 |         } | 
 | 	ssplen = len; | 
 |     } | 
 | #endif /* DYNAMIC */ | 
 |  | 
 |     freeptr = sspace;                   /* This is where matches are copied. */ | 
 |     resptr = resarry;			/* Static copies of these so */ | 
 |     remlen = len;                       /* recursive calls can alter them. */ | 
 |     debug(F101,"initspace ssplen","",ssplen); | 
 |     return(0); | 
 | } | 
 |  | 
 | /* | 
 |   Z S E T F I L  --  Query or change the size of file list buffers. | 
 |  | 
 |   fc = 1: Change current string space to n, return new size. | 
 |   fc = 2: Return current string space size. | 
 |   fc = 3: Change current maxnames to n, return new maxnames. | 
 |   fc = 4: Return current maxnames. | 
 |   Returns < 0 on error. | 
 | */ | 
 | int | 
 | zsetfil(n, fc) int n, fc; { | 
 | #ifdef DYNAMIC | 
 |     switch (fc) { | 
 |       case 1:				/* Stringspace */ | 
 | 	if (sspace) { | 
 | 	    free(sspace); | 
 | 	    sspace = NULL; | 
 | 	} | 
 | 	if (initspace(mtchs,n) < 0) | 
 | 	  return(-1); | 
 |       case 2:				/* Fall thru deliberately */ | 
 | 	return(ssplen); | 
 |       case 3:				/* Listsize */ | 
 | 	if (mtchs) { | 
 | 	    free((char *)mtchs); | 
 | 	    mtchs = NULL; | 
 | 	} | 
 | 	mtchs = (char **)malloc(n * sizeof(char *)); | 
 | 	if (!mtchs) | 
 | 	  return(-1); | 
 | 	maxnames = n; | 
 |       case 4:				/* Fall thru deliberately */ | 
 | 	return(maxnames); | 
 |     } | 
 | #endif /* DYNAMIC */ | 
 |     return(-1); | 
 | } | 
 |  | 
 |  | 
 |  | 
 | #ifndef NONZXPAND | 
 | #ifndef pdp11 | 
 | static | 
 | #endif /* pdp11 */ | 
 | #endif /* NONZXPAND */ | 
 | int | 
 | zxpand(fnarg) char *fnarg; { | 
 |     extern int diractive; | 
 |     char fnbuf[CKMAXPATH+8], * fn, * p; | 
 |  | 
 | #ifdef DTILDE                           /* Built with tilde-expansion? */ | 
 |     char *tnam; | 
 | #endif /* DTILDE */ | 
 |     int x; | 
 |     int haveonedir = 0; | 
 |  | 
 |     if (!fnarg) {                       /* If no argument provided */ | 
 | 	nxpand = fcount = 0; | 
 | 	return(0);			/* Return zero files found */ | 
 |     } | 
 |     debug(F110,"zxpand entry",fnarg,0); | 
 |     debug(F101,"zxpand xdironly","",xdironly); | 
 |     debug(F101,"zxpand xfilonly","",xfilonly); | 
 |  | 
 |     if (!*fnarg) {			/* If no argument provided */ | 
 | 	nxpand = fcount = 0; | 
 | 	return(0);			/* Return zero files found */ | 
 |     } | 
 |  | 
 | #ifdef CKROOT | 
 |     debug(F111,"zxpand setroot",ckroot,ckrootset); | 
 |     if (ckrootset) if (!zinroot(fnarg)) { | 
 | 	debug(F110,"zxpand setroot violation",fnarg,0); | 
 | 	nxpand = fcount = 0; | 
 | 	return(0); | 
 |     } | 
 | #endif /* CKROOT */ | 
 |  | 
 | #ifdef COMMENT | 
 | /* | 
 |   This would have been perfect, except it makes us return fully qualified | 
 |   pathnames for all files. | 
 | */ | 
 |     zfnqfp(fnarg,CKMAXPATH,fnbuf); | 
 |     debug(F110,"zxpand zfnqfp",fnbuf,0); | 
 |     s = zgtdir(); | 
 |     debug(F110,"zxpand zgtdir",s,0); | 
 |     p = fnbuf; | 
 |     while (*p && *s)                    /* Make it relative */ | 
 |       if (*s++ != *p++) | 
 |         break; | 
 |     fn = (*s) ? fnbuf : p; | 
 |     debug(F110,"zxpand fn 0",fn,0); | 
 |     if (!*fn) { | 
 |         fn = fnbuf; | 
 |         fnbuf[0] = '*'; | 
 |         fnbuf[1] = '\0'; | 
 |     } | 
 |     debug(F110,"zxpand fn 0.5",fn,0); | 
 | #else | 
 | #ifdef DTILDE                           /* Built with tilde-expansion? */ | 
 |     if (*fnarg == '~') {                /* Starts with tilde? */ | 
 |         tnam = tilde_expand(fnarg);     /* Try to expand it. */ | 
 |         ckstrncpy(fnbuf,tnam,CKMAXPATH); | 
 |     } else | 
 | #endif /* DTILDE */ | 
 |       ckstrncpy(fnbuf,fnarg,CKMAXPATH); | 
 |     fn = fnbuf;                         /* Point to what we'll work with */ | 
 | #endif /* COMMENT */ | 
 |     debug(F110,"zxpand fn 1",fn,0); | 
 |  | 
 |     if (!*fn)                           /* But make sure something is there */ | 
 |       return(0); | 
 |  | 
 |     p = fn + (int)strlen(fn) - 1; | 
 |     if (*p == '/') {                    /* If last char = / it must be a dir */ | 
 | 	if (!xfilonly && !iswild(p)) haveonedir++; | 
 |         ckstrncat(fn, "*", CKMAXPATH+8); /* so append '*' */ | 
 |     } else if (p > fn) {                /* If ends in "/." */ | 
 |         if (*(p-1) == '/' && *p == '.') /* change '.' to '*' */ | 
 |           *p = '*'; | 
 |     } else if (p == fn) {               /* If it's '.' alone */ | 
 |         if (*p == '.')                  /* change '.' to '*' */ | 
 |           *p = '*'; | 
 |     } | 
 |     debug(F110,"zxpand fn 2",fn,0); | 
 |     x = isdir(fn);                      /* Is it a directory? */ | 
 |     debug(F111,"zxpand isdir 1",fn,x); | 
 |     if (x) {                            /* If so, make it into a wildcard */ | 
 | 	if (!xfilonly && !iswild(p)) | 
 | 	  haveonedir++; | 
 |         if ((x = strlen(fn)) > 0) { | 
 |             if (!ISDIRSEP(fn[x-1])) | 
 |               fn[x++] = DIRSEP; | 
 |             fn[x++] = '*'; | 
 |             fn[x] = '\0'; | 
 |         } | 
 |     } | 
 |     debug(F111,"zxpand fn 3",fn,haveonedir); | 
 | /* | 
 |   The following allows us to parse a single directory name without opening | 
 |   the directory and looking at its contents.  The diractive flag is a horrible | 
 |   hack (especially since DIR /NORECURSIVE turns it off), but otherwise we'd | 
 |   have to change the API. | 
 | */ | 
 |     if (!diractive && haveonedir) { | 
 | #ifdef COMMENT | 
 | 	fcount = (mtchs == NULL && | 
 | 		  (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL) | 
 | 	  ? 0 : 1; | 
 | #else | 
 | 	fcount = 0; | 
 | 	if (!mtchs) { | 
 | 	    mtchs = (char **)malloc(maxnames * sizeof(*mtchs)); | 
 | 	    if (mtchs) | 
 | 	      fcount = 1; | 
 | 	    if (!fcount) | 
 | 	      return(nxpand = fcount); | 
 | 	} | 
 | #endif /* COMMENT */ | 
 | 	debug(F110,"zxpand haveonedir A1",fnarg,0); | 
 | 	initspace(mtchs,ssplen); | 
 | 	addresult(fnarg,1); | 
 | 	if (numfnd < 0) return(-1); | 
 | 	mtchptr = mtchs;		/* Save pointer for next. */ | 
 | 	debug(F110,"zxpand haveonedir A2",*mtchptr,0); | 
 | 	return(nxpand = fcount); | 
 |     } | 
 |  | 
 | #ifndef NOPUSH | 
 |     if (!nopush && wildxpand)           /* Who is expanding wildcards? */ | 
 |       fcount = (mtchs == NULL &&        /* Shell */ | 
 |                 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL) | 
 |         ? 0 | 
 |           :  shxpand(fn,mtchs,maxnames); | 
 |     else | 
 | #endif /* NOPUSH */ | 
 |       fcount = (mtchs == NULL &&        /* Kermit */ | 
 |                 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL) | 
 |         ? 0 | 
 |           : fgen(fn,mtchs,maxnames);      /* Look up the file. */ | 
 |  | 
 |     if (fcount == 0 && haveonedir) { | 
 | 	fcount = 1; | 
 | 	debug(F110,"zxpand haveonedir B",fnarg,0); | 
 | 	addresult(fnarg,1); | 
 | 	if (numfnd < 0) return(-1); | 
 |     } | 
 |     mtchptr = mtchs;                    /* Save pointer for next. */ | 
 |     nxpand = fcount; | 
 |  | 
 | #ifdef DEBUG | 
 |     if (deblog) { | 
 |         if (fcount > 1) | 
 |           debug(F111,"zxpand ok",mtchs[0],fcount); | 
 |         else | 
 |           debug(F101,"zxpand fcount","",fcount); | 
 |     } | 
 | #endif /* DEBUG */ | 
 |     return(fcount); | 
 | } | 
 |  | 
 | #ifndef NONZXPAND | 
 | /*  N Z X P A N D  --  Expand a file list, with options.  */ | 
 | /* | 
 |   Call with: | 
 |    s = pointer to filename or pattern. | 
 |    flags = option bits: | 
 |  | 
 |      flags & ZX_FILONLY   Match regular files | 
 |      flags & ZX_DIRONLY   Match directories | 
 |      flags & ZX_RECURSE   Descend through directory tree | 
 |      flags & ZX_MATCHDOT  Match "dot files" | 
 |      flags & ZX_NOBACKUP  Don't match "backup files" | 
 |      flags & ZX_NOLINKS   Don't follow symlinks. | 
 |  | 
 |    Returns the number of files that match s, with data structures set up | 
 |    so that first file (if any) will be returned by the next znext() call. | 
 | */ | 
 | int | 
 | nzxpand(s,flags) char * s; int flags; { | 
 |     char * p; | 
 |     int x; | 
 |  | 
 |     debug(F111,"nzxpand",s,flags); | 
 |     x = flags & (ZX_DIRONLY|ZX_FILONLY); | 
 |     xdironly = (x == ZX_DIRONLY); | 
 |     xfilonly = (x == ZX_FILONLY); | 
 |     if (xdironly && xfilonly) { | 
 |         xdironly = 0; | 
 |         xfilonly = 0; | 
 |     } | 
 |     xmatchdot  = (flags & ZX_MATCHDOT); | 
 |     debug(F111,"nzxpand xmatchdot 1",s,xmatchdot); | 
 |     /* If xmatchdot not set by caller but pattern implies it, set it anyway */ | 
 |     if (!xmatchdot && ((p = ckstrchr(s,'.')))) { | 
 | 	if (p == s && p[1] != '/') { | 
 | 	    xmatchdot = 1; | 
 | 	    debug(F111,"nzxpand xmatchdot 2",s,xmatchdot); | 
 | 	} else if (p > s) { | 
 | 	    xmatchdot = (*(p-1) == ',') || (*(p-1) == '{') || (*(p-1) == '/'); | 
 | 	    debug(F111,"nzxpand xmatchdot 3",s,xmatchdot); | 
 | 	} | 
 |     } | 
 |     xrecursive = (flags & ZX_RECURSE); | 
 |     xnobackup  = (flags & ZX_NOBACKUP); | 
 |     xnolinks   = (flags & ZX_NOLINKS); | 
 |  | 
 | #ifdef DEBUG | 
 |     if (deblog) { | 
 | 	debug(F101,"nzxpand xdironly","",xdironly); | 
 | 	debug(F101,"nzxpand xfilonly","",xfilonly); | 
 | 	debug(F101,"nzxpand xmatchdot","",xmatchdot); | 
 | 	debug(F101,"nzxpand xrecursive","",xrecursive); | 
 | 	debug(F101,"nzxpand xnobackup","",xnobackup); | 
 | 	debug(F101,"nzxpand xnolinks","",xnolinks); | 
 |     } | 
 | #endif /* DEBUG */ | 
 |  | 
 |     x = zxpand(s); | 
 |     if (x > 1) | 
 |       sh_sort(mtchs,NULL,x,0,0,1);	/* Alphabetize the list */ | 
 |     xdironly = 0; | 
 |     xfilonly = 0; | 
 |     xmatchdot = 0; | 
 |     xrecursive = 0; | 
 |     xnobackup = 0; | 
 |     xnolinks = 0; | 
 |     return(x); | 
 | } | 
 | #endif /* NONZXPAND */ | 
 |  | 
 | #ifndef NOZXREWIND | 
 | /*  Z X R E W I N D  --  Rewinds the zxpand() list */ | 
 |  | 
 | int | 
 | zxrewind() { | 
 |     /* if (!mtchs) return(-1); */ | 
 |     fcount = nxpand; | 
 |     mtchptr = mtchs; | 
 |     return(nxpand); | 
 | } | 
 | #endif /* NOZXREWIND */ | 
 |  | 
 | /*  Z N E X T  --  Get name of next file from list created by zxpand(). */ | 
 | /* | 
 |   Returns >0 if there's another file, with its name copied into the arg string, | 
 |   or 0 if no more files in list. | 
 | */ | 
 | int | 
 | znext(fn) char *fn; { | 
 |     if (fcount-- > 0) { | 
 |         ckstrncpy(fn,*mtchptr++,CKMAXPATH); | 
 |     } else { | 
 |         fn[0] = '\0'; | 
 |     } | 
 | #ifndef COMMENT | 
 |     debug(F111,"znext",fn,fcount+1); | 
 |     return(fcount+1); | 
 | #else | 
 |     debug(F111,"znext",fn,fcount);      /* Return 0 if no filename to return */ | 
 |     return(fcount); | 
 | #endif /* COMMENT */ | 
 | } | 
 |  | 
 | /*  Z C H K S P A  --  Check if there is enough space to store the file  */ | 
 |  | 
 | /* | 
 |  Call with file specification f, size n in bytes. | 
 |  Returns -1 on error, 0 if not enough space, 1 if enough space. | 
 | */ | 
 | /*ARGSUSED*/ | 
 | int | 
 | #ifdef CK_ANSIC | 
 | zchkspa(char *f, long n) | 
 | #else | 
 | zchkspa(f,n) char *f; long n; | 
 | #endif /* CK_ANSIC */ | 
 | /* zchkspa() */ { | 
 |     /* In UNIX there is no good (and portable) way. */ | 
 |     return(1);                          /* Always say OK. */ | 
 | } | 
 |  | 
 | #ifdef COMMENT				/* (not used) */ | 
 |  | 
 | /*  I S B A C K U P  --  Tells if given file has a backup suffix  */ | 
 | /* | 
 |    Returns: | 
 |    -1: Invalid argument | 
 |     0: File does not have a backup suffix | 
 |    >0: Backup suffix number | 
 | */ | 
 | int | 
 | isbackup(fn) char * fn; {		/* Get backup suffix number */ | 
 |     int i, j, k, x, state, flag; | 
 |  | 
 |     if (!fn)				/* Watch out for null pointers. */ | 
 |       return(-1); | 
 |     if (!*fn)				/* And empty names. */ | 
 |       return(-1); | 
 |  | 
 |     flag = state = 0; | 
 |     for (i = (int)strlen(fn) - 1; (!flag && (i > 0)); i--) { | 
 | 	switch (state) { | 
 | 	  case 0:			/* State 0 - final char */ | 
 | 	    if (fn[i] == '~')		/* Is tilde */ | 
 | 	      state = 1;		/* Switch to next state */ | 
 | 	    else			/* Otherwise */ | 
 | 	      flag = 1;			/* Quit - no backup suffix. */ | 
 | 	    break; | 
 | 	  case 1:			/* State 1 - digits */ | 
 | 	    if (fn[i] == '~'  && fn[i-1] == '.') { /* Have suffix */ | 
 | 		return(atoi(&fn[i+1])); | 
 | 	    } else if (fn[i] >= '0' && fn[i] <= '9') { /* In number part */ | 
 | 		continue;		/* Keep going */ | 
 | 	    } else {			/* Something else */ | 
 | 		flag = 1;		/* Not a backup suffix - quit. */ | 
 | 	    } | 
 | 	    break; | 
 | 	} | 
 |     } | 
 |     return(0); | 
 | } | 
 | #endif /* COMMENT */ | 
 |  | 
 |  | 
 | /*  Z N E W N  --  Make a new name for the given file  */ | 
 |  | 
 | /* | 
 |   Given the name, fn, of a file that already exists, this function builds a | 
 |   new name of the form "<oldname>.~<n>~", where <oldname> is argument name | 
 |   (fn), and <n> is a version number, one higher than any existing version | 
 |   number for that file, up to 99999.  This format is consistent with that used | 
 |   by GNU EMACS.  If the constructed name is too long for the system's maximum, | 
 |   enough characters are truncated from the end of <fn> to allow the version | 
 |   number to fit.  If no free version numbers exist between 1 and 99999, a | 
 |   version number of "xxxx" is used.  Returns a pointer to the new name in | 
 |   argument s. | 
 | */ | 
 | #ifdef pdp11 | 
 | #define ZNEWNBL 63                      /* Name buffer length */ | 
 | #define ZNEWNMD 3                       /* Max digits for version number */ | 
 | #else | 
 | #define ZNEWNBL CKMAXPATH | 
 | #define ZNEWNMD 4 | 
 | #endif /* pdp11 */ | 
 |  | 
 | #define MAXBUDIGITS 5 | 
 |  | 
 | static char znewbuf[ZNEWNBL+12]; | 
 |  | 
 | VOID | 
 | znewn(fn,s) char *fn, **s; { | 
 |     char * buf;				/* Pointer to buffer for new name */ | 
 |     char * xp, * namepart = NULL;       /* Pointer to filename part */ | 
 |     struct zfnfp * fnfp;                /* znfqfp() result struct pointer */ | 
 |     int d = 0, t, fnlen, buflen; | 
 |     int n, i, k, flag, state; | 
 |     int max = MAXNAMLEN;                /* Maximum name length */ | 
 |     char * dname = NULL; | 
 |  | 
 |     buf = znewbuf; | 
 |     *s = NULL;                          /* Initialize return value */ | 
 |     if (!fn) fn = "";                   /* Check filename argument */ | 
 |     i = strlen(fn); | 
 |  | 
 | /* If incoming file already has a backup suffix, remove it. */ | 
 | /* Then we'll tack a new on later, which will be the highest for this file. */ | 
 |  | 
 |     if (i <= max && i > 0 && fn[i-1] == '~') { | 
 | 	char * p; | 
 | 	i--; | 
 | 	debug(F111,"znewn suffix removal",fn,i); | 
 | 	if ((dname = (char *)malloc(i+1))) { | 
 | 	    ckstrncpy(dname,fn,i+1); | 
 | 	    p = dname; | 
 | 	    for (flag = state = 0; (!flag && (i > 0)); i--) { | 
 | 		switch (state) { | 
 | 		  case 0:		/* State 0 - final char */ | 
 | 		    if (p[i] == '~')	/* Is tilde */ | 
 | 		      state = 1;	/* Switch to next state */ | 
 | 		    else		/* Otherwise */ | 
 | 		      flag = 1;		/* Quit - no backup suffix. */ | 
 | 		    break; | 
 | 		  case 1:		/* State 1 - digits */ | 
 | 		    if (p[i] == '~'  && p[i-1] == '.') { /* Have suffix */ | 
 | 			p[i-1] = NUL;	/* Trim it */ | 
 | 			fn = dname; | 
 | 			debug(F111,"znewn suffix removal 2",fn,i); | 
 | 			flag = 1;	/* done */ | 
 | 		    } else if (p[i] >= '0' && p[i] <= '9') { /* Number part */ | 
 | 			continue;	/* Keep going */ | 
 | 		    } else {		/* Something else */ | 
 | 			flag = 1;	/* Not a backup suffix - quit. */ | 
 | 		    } | 
 | 		    break; | 
 | 		} | 
 | 	    } | 
 | 	} | 
 |     } | 
 |     if ((fnlen = strlen(fn)) < 1) {	/* Get length */ | 
 | 	if (dname) free(dname); | 
 | 	return; | 
 |     } | 
 |     debug(F111,"znewn",fn,fnlen); | 
 |  | 
 |     debug(F101,"znewn max 1","",max); | 
 |     if (max < 14) max = 14;             /* Make max reasonable for any UNIX */ | 
 |     if (max > ZNEWNBL) max = ZNEWNBL; | 
 |     debug(F101,"znewn max 2","",max); | 
 |  | 
 |     if ((fnfp = zfnqfp(fn, ZNEWNBL, buf))) { /* Get fully qualified name */ | 
 |         namepart = fnfp->fname;         /* Isolate the filename */ | 
 |         k = strlen(fn);                 /* Length of name part */ | 
 |         debug(F111,"znewn namepart",namepart,k); | 
 |     } else { | 
 | 	if (dname) free(dname); | 
 | 	return; | 
 |     } | 
 |     buflen = fnfp->len;                 /* Length of fully qualified name */ | 
 |     debug(F111,"znewn len",buf,buflen); | 
 |  | 
 |     if (k + MAXBUDIGITS + 3 < max) {    /* Backup name fits - no overflow */ | 
 | 	/* Make pattern for backup names */ | 
 |         ckstrncpy(buf+buflen,".~*~",ZNEWNBL+12-buflen); | 
 |         n = nzxpand(buf,ZX_FILONLY);    /* Expand the pattern */ | 
 |         debug(F111,"znewn A matches",buf,n); | 
 |         while (n-- > 0) {               /* Find any existing name.~n~ files */ | 
 |             xp = *mtchptr++;            /* Point at matching name */ | 
 |             t = atoi(xp+buflen+2);      /* Get number */ | 
 |             if (t > d) d = t;           /* Save d = highest version number */ | 
 |         } | 
 |         sprintf(buf+buflen,".~%d~",d+1); /* Yes, make "name.~<d+1>~" */ | 
 |         debug(F110,"znewn A newname",buf,0); | 
 |     } else {                            /* Backup name would be too long */ | 
 |         int xlen;                       /* So we have to eat back into it */ | 
 |         int delta; | 
 |         char buf2[ZNEWNBL+12]; | 
 |  | 
 |         delta = max - k; | 
 |         debug(F101,"znewn B delta","",delta); | 
 |  | 
 |         for (i = MAXBUDIGITS; i > 0; i--) { /* In this case the format of */ | 
 |             ckstrncpy(buf2,buf,ZNEWNBL+12); /* the backup name depends on */ | 
 |             xlen = buflen - i - 3 + delta;  /* how many digits are in the */ | 
 |             ckstrncpy(buf2+xlen,".~*~",ZNEWNBL+12-xlen); /* backup number */ | 
 |             n = nzxpand(buf2,ZX_FILONLY); | 
 |             debug(F111,"znewn B matches",buf2,n); | 
 |             if (n > 0) | 
 |               break; | 
 |         } | 
 |         while (n-- > 0) {               /* Find any existing name.~n~ files */ | 
 |             xp = *mtchptr++;            /* Point at matching name */ | 
 |             t = atoi(xp+xlen+2);        /* Get number */ | 
 |             if (t > d) d = t;           /* Save d = highest version number */ | 
 |         } | 
 |         if (d > 0)                      /* If the odometer turned over... */ | 
 |           if ((d % 10) == 9)            /* back up one space. */ | 
 |             xlen--; | 
 |         sprintf(buf2+xlen,".~%d~",d+1); /* This just fits */ | 
 |         ckstrncpy(buf,buf2,ZNEWNBL+12);	/* (we could be more clever here...) */ | 
 |         debug(F110,"znewn B new name",buf,0); | 
 |     } | 
 |     *s = buf;                           /* Point to new name */ | 
 |     ck_znewn = d+1;                     /* Also make it available globally */ | 
 |     if (dname) free(dname); | 
 |     return; | 
 | } | 
 |  | 
 | /*  Z R E N A M E  --  Rename a file  */ | 
 | /* | 
 |    Call with old and new names. | 
 |    If new name is the name of a directory, the 'old' file is moved to | 
 |    that directory. | 
 |    Returns 0 on success, -1 on failure. | 
 | */ | 
 | int | 
 | zrename(old,new) char *old, *new; { | 
 |     char *p, *s; | 
 |     int x; | 
 |  | 
 |     if (!old) old = ""; | 
 |     if (!new) new = ""; | 
 |     debug(F110,"zrename old",old,0); | 
 |     debug(F110,"zrename new",new,0); | 
 |     if (!*old) return(-1); | 
 |     if (!*new) return(-1); | 
 |  | 
 | #ifdef IKSD | 
 | #ifdef CK_LOGIN | 
 |     if (inserver && isguest) | 
 |       return(-1); | 
 | #endif /* CK_LOGIN */ | 
 | #endif /* IKSD */ | 
 |  | 
 | #ifdef CKROOT | 
 |     debug(F111,"zrename setroot",ckroot,ckrootset); | 
 |     if (ckrootset) { | 
 | 	if (!zinroot(old)) { | 
 | 	    debug(F110,"zrename old: setroot violation",old,0); | 
 | 	    return(-1); | 
 | 	} | 
 | 	if (!zinroot(new)) { | 
 | 	    debug(F110,"zrename new: setroot violation",new,0); | 
 | 	    return(-1); | 
 | 	} | 
 |     } | 
 | #endif /* CKROOT */ | 
 |  | 
 |     p = NULL; | 
 |     s = new; | 
 |  | 
 |     if (isdir(new)) { | 
 |         char *q = NULL; | 
 |         x = strlen(new); | 
 |         if (!(p = malloc(strlen(new) + strlen(old) + 2))) | 
 |           return(-1); | 
 |         strcpy(p,new);                  /* (safe) Directory part */ | 
 |         if (!ISDIRSEP(*(new+x-1)))      /* Separator, if needed */ | 
 |           strcat(p,"/");		/* (safe) */ | 
 |         zstrip(old,&q);                 /* Strip path part from old name */ | 
 |         strcat(p,q);                    /* cat to new directory (safe) */ | 
 |         s = p; | 
 |         debug(F110,"zrename dir",s,0); | 
 |     } | 
 | #ifdef DEBUG | 
 |     else debug(F110,"zrename no dir",s,0); | 
 | #endif /* DEBUG */ | 
 |  | 
 | #ifdef IKSD | 
 |     if (inserver && (!ENABLED(en_del))) { | 
 | 	if (zchki(s) > -1)		/* Destination file exists? */ | 
 | 	  return(-1); | 
 |     } | 
 | #endif /* IKSD */ | 
 |  | 
 |     x = -1;                             /* Return code. */ | 
 | #ifdef RENAME | 
 | /* Atomic, preferred, uses a single system call, rename(), if available. */ | 
 |     x = rename(old,s); | 
 |     debug(F111,"zrename rename()",old,x); | 
 |     if (x) x = -1; | 
 | #endif /* RENAME */ | 
 |  | 
 |     /* If rename() failed or not available try link()/unlink() */ | 
 |  | 
 |     if (x < 0) { | 
 | 	if (zchko(old) > -1) {		/* Requires write access to orignal */ | 
 | 	    x = link(old,s); | 
 | 	    debug(F111,"zrename link()",old,x); | 
 | 	    if (x > -1) {		/* Make a link with the new name. */ | 
 | 		x = unlink(old); | 
 | 		debug(F111,"zrename unlink()",old,x); | 
 | 	    } | 
 | 	    /* If link/unlink failed copy and delete */ | 
 | 	    if (x < 0) { | 
 | 		x = zcopy(old,s); | 
 | 		debug(F111,"zrename zcopy()",old,x); | 
 | 		if (x > -1) { | 
 | 		    x = zdelet(old); | 
 | 		    debug(F111,"zrename zdelet()",old,x); | 
 | 		} | 
 | 	    } | 
 | 	} | 
 |     } | 
 |     fullname[0] = '\0';			/* Clear this out for next time. */ | 
 |  | 
 | #ifdef CKSYSLOG | 
 |     if (ckxsyslog >= SYSLG_FC && ckxlogging) { | 
 |         zfnqfp(old,CKMAXPATH,fullname); | 
 |         tmp2[0] = '\0'; | 
 |         zfnqfp(s,CKMAXPATH,tmp2); | 
 |         if (x > -1) | 
 |           syslog(LOG_INFO,"file[] %s: renamed to %s ok", fullname, tmp2); | 
 |         else | 
 |           syslog(LOG_INFO,"file[] %s: rename to %s failed (%m)",fullname,tmp2); | 
 |     } | 
 | #endif /* CKSYSLOG */ | 
 |  | 
 |     if (p) free(p); | 
 |     return(x); | 
 | } | 
 |  | 
 | /*  Z C O P Y  --  Copy a single file. */ | 
 | /* | 
 |   Call with source and destination names. | 
 |   If destination is a directory, the source file is | 
 |   copied to that directory with its original name. | 
 |   Returns: | 
 |    0 on success. | 
 |   <0 on failure: | 
 |   -2 = source file is not a regular file. | 
 |   -3 = source file not found. | 
 |   -4 = permission denied. | 
 |   -5 = source and destination are the same file. | 
 |   -6 = i/o error. | 
 |   -1 = other error. | 
 | */ | 
 | int | 
 | zcopy(source,destination) char *source, *destination; { | 
 |     char *src, *dst;			/* Local pointers to filenames */ | 
 |     int x, y, rc;                       /* Workers */ | 
 |     int in = -1, out = -1;              /* i/o file descriptors */ | 
 |     struct stat srcbuf;                 /* Source file info buffer */ | 
 |     int perms;                          /* Output file permissions */ | 
 |     char buf[1024];                     /* File copying buffer */ | 
 |  | 
 |     if (!source) source = ""; | 
 |     if (!destination) destination = ""; | 
 |  | 
 |     debug(F110,"zcopy src arg",source,0); | 
 |     debug(F110,"zcopy dst arg",destination,0); | 
 |  | 
 |     if (!*source) return(-1); | 
 |     if (!*destination) return(-1); | 
 |  | 
 | #ifdef IKSD | 
 | #ifdef CK_LOGIN | 
 |     if (inserver && isguest) | 
 |       return(-4); | 
 | #endif /* CK_LOGIN */ | 
 | #endif /* IKSD */ | 
 |  | 
 | #ifdef CKROOT | 
 |     debug(F111,"zcopy setroot",ckroot,ckrootset); | 
 |     if (ckrootset) { | 
 | 	if (!zinroot(source)) { | 
 | 	    debug(F110,"zcopy source: setroot violation",source,0); | 
 | 	    return(-1); | 
 | 	} | 
 | 	if (!zinroot(destination)) { | 
 | 	    debug(F110,"zcopy destination: setroot violation",destination,0); | 
 | 	    return(-1); | 
 | 	} | 
 |     } | 
 | #endif /* CKROOT */ | 
 |  | 
 |     src = source; | 
 |     dst = destination; | 
 |  | 
 |     if (stat(src,&srcbuf) == 0) {       /* Get source file info */ | 
 |         struct stat dstbuf;             /* Destination file info buffer */ | 
 | 	debug(F101,"STAT","",6); | 
 |         if (stat(dst,&dstbuf) == 0) { | 
 | 	    debug(F101,"STAT","",7); | 
 |             if (srcbuf.st_dev == dstbuf.st_dev) | 
 |               if (srcbuf.st_ino == dstbuf.st_ino) { | 
 |                   debug(F100,"zcopy files identical: stat()","",0); | 
 |                   return(-5); | 
 |               } | 
 |         } | 
 |     } else {                            /* stat() failed... */ | 
 | 	debug(F101,"STAT","",8); | 
 |         debug(F111,"source file not found",src,errno); | 
 |         return(-3); | 
 |     } | 
 |     fullname[0] = '\0';                 /* Get full pathnames */ | 
 |     if (zfnqfp(source,CKMAXPATH,fullname)) | 
 |       src = fullname; | 
 |     debug(F110,"zcopy src",src,0); | 
 |     tmp2[0] = '\0'; | 
 |     if (zfnqfp(destination,CKMAXPATH,tmp2)) | 
 |       dst = tmp2; | 
 |     debug(F110,"zcopy dst 1",dst,0); | 
 |     if (!strcmp(src,dst)) {             /* Src and dst are same file? */ | 
 |         debug(F100,"zcopy files identical: strcmp()","",0); /* This... */ | 
 |         return(-5);                     /* should not happen. */ | 
 |     } | 
 |     if (isdir(src)) {                   /* Source file is a directory? */ | 
 |         debug(F110,"zcopy source is directory",src,0); | 
 |         return(-2);                     /* Fail */ | 
 |     } | 
 |     if (isdir(dst)) {                   /* Destination is a directory? */ | 
 |         char *q = NULL;                 /* Yes, add filename to it. */ | 
 |         x = strlen(dst); | 
 | 	if (x < 1) return(-1); | 
 |         if (!ISDIRSEP(*(dst+x-1))) {    /* Add separator if needed */ | 
 |             tmp2[x++] = '/'; | 
 |             tmp2[x] = '\0'; | 
 |         } | 
 | 	debug(F111,"zcopy dst 2",dst,x); | 
 |         zstrip(src,&q);                 /* Strip path part from old name */ | 
 |         ckstrncpy(tmp2+x,q,CKMAXPATH-x); /* Concatenate it to new name */ | 
 |     } | 
 |     debug(F110,"zcopy dst 3",dst,0); | 
 |  | 
 | #ifdef IKSD | 
 |     if (inserver && (!ENABLED(en_del))) { | 
 | 	if (zchki(dst) > -1)		/* Destination file exists? */ | 
 | 	  return(-4); | 
 |     } | 
 | #endif /* IKSD */ | 
 |  | 
 |     perms = umask(0);                   /* Get user's umask */ | 
 |     umask(perms);			/* Put it back! */ | 
 |     perms ^= 0777;                      /* Flip the bits */ | 
 |     perms &= 0666;                      /* Zero execute bits from umask */ | 
 |     perms |= (srcbuf.st_mode & 0111);   /* OR in source file's execute bits */ | 
 |     rc = -1;                            /* Default return code */ | 
 |     errno = 0;                          /* Reset errno */ | 
 |     in = open(src, O_RDONLY, 0);        /* Open source file */ | 
 |     debug(F111,"zcopy open source",src,in); | 
 |     if (in > -1) {                      /* If open... */ | 
 | 	/* Open destination file */ | 
 | #ifdef O_TRUNC | 
 |         out = open(dst, O_WRONLY|O_CREAT|O_TRUNC, perms); | 
 | #else | 
 |         out = open(dst, O_WRONLY|O_CREAT, perms); | 
 | #endif /* O_TRUNC */ | 
 |         debug(F111,"zcopy open dest",dst,out); | 
 |         if (out > -1) {                 /* If open... */ | 
 |             while ((x = read(in,buf,1024)) > 0) { /* Copy in 1K blocks */ | 
 |                 y = write(out,buf,x); | 
 |                 if (y < 0) {            /* On write failure */ | 
 |                     x = -1; | 
 |                     rc = -6;            /* Indicate i/o error */ | 
 |                     break; | 
 |                 } | 
 |             } | 
 |             debug(F101,"zcopy final read","",x); | 
 |             debug(F101,"zcopy errno","",errno); | 
 |             rc = (x == 0) ? 0 : -6;     /* In case of read failure */ | 
 |         } | 
 |     } | 
 |     if (in > -1) close(in);             /* Close files */ | 
 |     if (out > -1) close(out); | 
 |     if (rc == -1) {                     /* Set return code */ | 
 |         switch (errno) { | 
 |           case ENOENT: rc = -3; break; | 
 |           case EACCES: rc = -4; break; | 
 |           case EIO:    rc = -6; | 
 |         } | 
 |     } | 
 |  | 
 | #ifdef CKSYSLOG | 
 |     if (rc > -1 && ckxsyslog >= SYSLG_FC && ckxlogging) { | 
 |         if (rc) | 
 |           syslog(LOG_INFO,"file[] %s: copy to %s failed (%m)", fullname, tmp2); | 
 |         else | 
 |           syslog(LOG_INFO,"file[] %s: copy to %s ok", fullname, tmp2); | 
 |     } | 
 | #endif /* CKSYSLOG */ | 
 |  | 
 |     return(rc); | 
 | } | 
 |  | 
 | /*  Z S A T T R */ | 
 | /* | 
 |  Fills in a Kermit file attribute structure for the file which is to be sent. | 
 |  Returns 0 on success with the structure filled in, or -1 on failure. | 
 |  If any string member is null, then it should be ignored. | 
 |  If any numeric member is -1, then it should be ignored. | 
 | */ | 
 | #ifdef CK_PERMS | 
 |  | 
 | #ifdef CK_GPERMS | 
 | #undef CK_GPERMS | 
 | #endif /* CK_GPERMS */ | 
 |  | 
 | #ifdef UNIX | 
 | #ifndef S_IRUSR | 
 | #define S_IRUSR 0400 | 
 | #endif /* S_IRUSR */ | 
 | #ifndef S_IWUSR | 
 | #define S_IXUSR 0200 | 
 | #endif /* S_IWUSR */ | 
 | #ifndef S_IXUSR | 
 | #define S_IXUSR 0100 | 
 | #endif /* S_IXUSR */ | 
 | #endif /* UNIX */ | 
 |  | 
 | #ifdef S_IRUSR | 
 | #ifdef S_IWUSR | 
 | #ifdef S_IXUSR | 
 | #define CK_GPERMS | 
 | #endif /* S_IXUSR */ | 
 | #endif /* S_IWUSR */ | 
 | #endif /* S_IRUSR */ | 
 |  | 
 | static char gperms[2]; | 
 |  | 
 | #endif /* CK_GPERMS */ | 
 |  | 
 | static char lperms[24]; | 
 |  | 
 | #ifdef CK_PERMS | 
 | static char xlperms[24]; | 
 |  | 
 | /*  Z S E T P E R M  --  Set permissions of a file  */ | 
 |  | 
 | int | 
 | zsetperm(f,code) char * f; int code; { | 
 |     int x; | 
 | #ifdef CK_SCO32V4 | 
 |     mode_t mask; | 
 | #else | 
 |     int mask; | 
 | #endif /* CK_SCO32V4 */ | 
 |     mask = code; | 
 |     if (inserver && guest) { | 
 | 	debug(F110,"zsetperm guest",f,0); | 
 | 	return(0); | 
 |     } | 
 |     x = chmod(f,mask); | 
 |     if (x < 0) { | 
 | 	debug(F111,"zsetperm error",f,errno); | 
 | 	return(0); | 
 |     } | 
 |     debug(F111,"zsetperm ok",f,mask); | 
 |     return(1); | 
 | } | 
 |  | 
 | /*  Z G P E R M  --  Get permissions of a file as an octal string  */ | 
 |  | 
 | char * | 
 | zgperm(f) char *f; { | 
 |     extern int diractive; | 
 |     int x; char *s = (char *)xlperms; | 
 |     struct stat buf; | 
 |     debug(F110,"zgperm",f,0); | 
 |     if (!f) return("----------"); | 
 |     if (!*f) return("----------"); | 
 |  | 
 | #ifdef CKROOT | 
 |     debug(F111,"zgperm setroot",ckroot,ckrootset); | 
 |     if (ckrootset) if (!zinroot(f)) { | 
 | 	debug(F110,"zgperm setroot violation",f,0); | 
 | 	return("----------"); | 
 |     } | 
 | #endif /* CKROOT */ | 
 |  | 
 | #ifdef USE_LSTAT | 
 |     if (diractive) | 
 |       x = lstat(f,&buf); | 
 |     else | 
 | #endif /* USE_LSTAT */ | 
 |       x = stat(f,&buf); | 
 |     debug(F101,"STAT","",9); | 
 |     if (x < 0) | 
 |       return("----------"); | 
 |     sprintf(s,"%o",buf.st_mode); | 
 |     debug(F110,"zgperm",s,0); | 
 |     return(s); | 
 | } | 
 |  | 
 | /* Like zgperm() but returns permissions in "ls -l" string format */ | 
 |  | 
 | static char xsperms[24]; | 
 |  | 
 | char * | 
 | ziperm(f) char * f; { | 
 |     extern int diractive; | 
 |     int x; char *s = (char *)xsperms; | 
 |     struct stat buf; | 
 |     unsigned int perms = 0; | 
 |  | 
 |     debug(F110,"ziperm",f,0); | 
 |  | 
 |     if (!f) return(NULL); | 
 |     if (!*f) return(NULL); | 
 |  | 
 |     if (diractive && zgfs_mode != 0) { | 
 | 	perms = zgfs_mode;		/* zgetfs() already got them */ | 
 |     } else { | 
 | #ifdef USE_LSTAT | 
 | 	if (diractive) | 
 | 	  x = lstat(f,&buf); | 
 | 	else | 
 | #endif /* USE_LSTAT */ | 
 | 	  x = stat(f,&buf); | 
 | 	debug(F101,"STAT","",10); | 
 | 	if (x < 0) | 
 | 	  return("----------"); | 
 | 	perms = buf.st_mode; | 
 |     } | 
 |     switch (perms & S_IFMT) { | 
 |       case S_IFDIR: | 
 |         *s++ = 'd'; | 
 |         break; | 
 |       case S_IFCHR:                     /* Character special */ | 
 |         *s++ = 'c'; | 
 |         break; | 
 |       case S_IFBLK:                     /* Block special */ | 
 |         *s++ = 'b'; | 
 |         break; | 
 |       case S_IFREG:                     /* Regular */ | 
 |         *s++ = '-'; | 
 |         break; | 
 | #ifdef S_IFLNK | 
 |       case S_IFLNK:                     /* Symbolic link */ | 
 |         *s++ = 'l'; | 
 |         break; | 
 | #endif /* S_IFLNK */ | 
 | #ifdef S_IFSOCK | 
 |       case S_IFSOCK:                    /* Socket */ | 
 |         *s++ = 's'; | 
 |         break; | 
 | #endif /* S_IFSOCK */ | 
 | #ifdef S_IFIFO | 
 | #ifndef Plan9 | 
 | #ifndef COHERENT | 
 |       case S_IFIFO:                     /* FIFO */ | 
 |         *s++ = 'p'; | 
 |         break; | 
 | #endif /* COHERENT */ | 
 | #endif /* Plan9 */ | 
 | #endif /* S_IFIFO */ | 
 | #ifdef S_IFWHT | 
 |       case S_IFWHT:                     /* Whiteout */ | 
 |         *s++ = 'w'; | 
 |         break; | 
 | #endif /* S_IFWHT */ | 
 |       default:                          /* Unknown */ | 
 |         *s++ = '?'; | 
 |         break; | 
 |     } | 
 |     if (perms & S_IRUSR)          /* Owner's permissions */ | 
 |       *s++ = 'r'; | 
 |     else | 
 |       *s++ = '-'; | 
 |     if (perms & S_IWUSR) | 
 |       *s++ = 'w'; | 
 |     else | 
 |       *s++ = '-'; | 
 |     switch (perms & (S_IXUSR | S_ISUID)) { | 
 |       case 0: | 
 |         *s++ = '-'; | 
 |         break; | 
 |       case S_IXUSR: | 
 |         *s++ = 'x'; | 
 |         break; | 
 |       case S_ISUID: | 
 |         *s++ = 'S'; | 
 |         break; | 
 |       case S_IXUSR | S_ISUID: | 
 |         *s++ = 's'; | 
 |         break; | 
 |     } | 
 |     if (perms & S_IRGRP)          /* Group permissions */ | 
 |       *s++ = 'r'; | 
 |     else | 
 |       *s++ = '-'; | 
 |     if (perms & S_IWGRP) | 
 |       *s++ = 'w'; | 
 |     else | 
 |       *s++ = '-'; | 
 |     switch (perms & (S_IXGRP | S_ISGID)) { | 
 |       case 0: | 
 |         *s++ = '-'; | 
 |         break; | 
 |       case S_IXGRP: | 
 |         *s++ = 'x'; | 
 |         break; | 
 |       case S_ISGID: | 
 |         *s++ = 'S'; | 
 |         break; | 
 |       case S_IXGRP | S_ISGID: | 
 |         *s++ = 's'; | 
 |         break; | 
 |     } | 
 |     if (perms & S_IROTH)          /* World permissions */ | 
 |       *s++ = 'r'; | 
 |     else | 
 |       *s++ = '-'; | 
 |     if (perms & S_IWOTH) | 
 |       *s++ = 'w'; | 
 |     else | 
 |       *s++ = '-'; | 
 |     switch ( | 
 | #ifdef Plan9 | 
 |             perms & (S_IXOTH) | 
 | #else | 
 |             perms & (S_IXOTH | S_ISVTX) | 
 | #endif | 
 |             ) { | 
 |       case 0: | 
 |         *s++ = '-'; | 
 |         break; | 
 |       case S_IXOTH: | 
 |         *s++ = 'x'; | 
 |         break; | 
 | #ifndef Plan9 | 
 |       case S_ISVTX: | 
 |         *s++ = 'T'; | 
 |         break; | 
 |       case S_IXOTH | S_ISVTX: | 
 |         *s++ = 't'; | 
 |         break; | 
 | #endif /* Plan9 */ | 
 |     } | 
 |     *s = '\0'; | 
 |     debug(F110,"ziperm",xsperms,0); | 
 |     return((char *)xsperms); | 
 | } | 
 |  | 
 | #else | 
 |  | 
 | char * | 
 | zgperm(f) char *f; { | 
 |     return("----------"); | 
 | } | 
 | char * | 
 | ziperms(f) char *f; { | 
 |     return("----------"); | 
 | } | 
 | #endif /* CK_PERMS */ | 
 |  | 
 | int | 
 | zsattr(xx) struct zattr *xx; { | 
 |     long k; int x; | 
 |     struct stat buf; | 
 |  | 
 |     k = iflen % 1024L;                  /* File length in K */ | 
 |     if (k != 0L) k = 1L; | 
 |     xx->lengthk = (iflen / 1024L) + k; | 
 |     xx->type.len = 0;                   /* File type can't be filled in here */ | 
 |     xx->type.val = ""; | 
 |     if (*nambuf) { | 
 |         xx->date.val = zfcdat(nambuf);  /* File creation date */ | 
 |         xx->date.len = (int)strlen(xx->date.val); | 
 |     } else { | 
 |         xx->date.len = 0; | 
 |         xx->date.val = ""; | 
 |     } | 
 |     xx->creator.len = 0;                /* File creator */ | 
 |     xx->creator.val = ""; | 
 |     xx->account.len = 0;                /* File account */ | 
 |     xx->account.val = ""; | 
 |     xx->area.len = 0;                   /* File area */ | 
 |     xx->area.val = ""; | 
 |     xx->password.len = 0;               /* Area password */ | 
 |     xx->password.val = ""; | 
 |     xx->blksize = -1L;                  /* File blocksize */ | 
 |     xx->xaccess.len = 0;                /* File access */ | 
 |     xx->xaccess.val = ""; | 
 |     xx->encoding.len = 0;               /* Transfer syntax */ | 
 |     xx->encoding.val = 0; | 
 |     xx->disp.len = 0;                   /* Disposition upon arrival */ | 
 |     xx->disp.val = ""; | 
 |     xx->lprotect.len = 0;               /* Local protection */ | 
 |     xx->lprotect.val = ""; | 
 |     xx->gprotect.len = 0;               /* Generic protection */ | 
 |     xx->gprotect.val = ""; | 
 |     x = -1; | 
 |     if (*nambuf) x = stat(nambuf,&buf); | 
 |     debug(F101,"STAT","",11); | 
 |     if (x >= 0) { | 
 |         debug(F111,"zsattr buf.st_mode & 0777",nambuf,buf.st_mode & 0777); | 
 |         /* UNIX filemode as an octal string without filetype bits */ | 
 |         sprintf(lperms,"%o",buf.st_mode & 0777); | 
 |         xx->lprotect.len = (int)strlen(lperms); | 
 |         xx->lprotect.val = (char *)lperms; | 
 |         x = 0; | 
 | #ifdef CK_GPERMS | 
 |         /* Generic permissions only if we have stat.h symbols defined */ | 
 |         if (buf.st_mode & S_IRUSR) x |= 1;      /* Read */ | 
 |         if (buf.st_mode & S_IWUSR) x |= (2+16); /* Write and Delete */ | 
 |         if (buf.st_mode & S_IXUSR) x |= 4;      /* Execute */ | 
 |         gperms[0] = tochar(x); | 
 |         gperms[1] = NUL; | 
 |         xx->gprotect.len = 1; | 
 |         xx->gprotect.val = (char *)gperms; | 
 | #endif /* CK_GPERMS */ | 
 |     } | 
 |     debug(F111,"zsattr lperms",xx->lprotect.val,xx->lprotect.len); | 
 |     debug(F111,"zsattr gperms",xx->gprotect.val,xx->gprotect.len); | 
 |     xx->systemid.val = "U1";            /* U1 = UNIX */ | 
 |     xx->systemid.len = 2;               /* System ID */ | 
 |     xx->recfm.len = 0;                  /* Record format */ | 
 |     xx->recfm.val = ""; | 
 |     xx->sysparam.len = 0;               /* System-dependent parameters */ | 
 |     xx->sysparam.val = ""; | 
 |     xx->length = iflen;                 /* Length */ | 
 |     return(0); | 
 | } | 
 |  | 
 | /* Z F C D A T  --  Get file creation date */ | 
 | /* | 
 |   Call with pointer to filename. | 
 |   On success, returns pointer to modification date in yyyymmdd hh:mm:ss format. | 
 |   On failure, returns pointer to null string. | 
 | */ | 
 | static char datbuf[40]; | 
 |  | 
 | char * | 
 | #ifdef CK_ANSIC | 
 | zdtstr(time_t timearg) | 
 | #else | 
 | zdtstr(timearg) time_t timearg; | 
 | #endif /* CK_ANSIC */ | 
 | /* zdtstr */ { | 
 | #ifndef TIMESTAMP | 
 |     return(""); | 
 | #else | 
 |     struct tm * time_stamp; | 
 |     struct tm * localtime(); | 
 |     int yy, ss; | 
 |  | 
 |     debug(F101,"zdtstr timearg","",timearg); | 
 |     if (timearg < 0) | 
 |       return(""); | 
 |     time_stamp = localtime(&(timearg)); | 
 |     if (!time_stamp) { | 
 |         debug(F100,"localtime returns null","",0); | 
 |         return(""); | 
 |     } | 
 | /* | 
 |   We assume that tm_year is ALWAYS years since 1900. | 
 |   Any platform where this is not the case will have problems | 
 |   starting in 2000. | 
 | */ | 
 |     yy = time_stamp->tm_year;           /* Year - 1900 */ | 
 |     debug(F101,"zdtstr tm_year","",time_stamp->tm_year); | 
 |     if (yy > 1000) { | 
 |         debug(F101,"zstrdt YEAR-2000 ALERT 1: localtime year","",yy); | 
 |     } | 
 |     yy += 1900; | 
 |     debug(F101,"zdatstr year","",yy); | 
 |  | 
 |     if (time_stamp->tm_mon  < 0 || time_stamp->tm_mon  > 11) | 
 |       return(""); | 
 |     if (time_stamp->tm_mday < 0 || time_stamp->tm_mday > 31) | 
 |       return(""); | 
 |     if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 23) | 
 |       return(""); | 
 |     if (time_stamp->tm_min  < 0 || time_stamp->tm_min  > 59) | 
 |       return(""); | 
 |     ss = time_stamp->tm_sec;            /* Seconds */ | 
 |     if (ss < 0 || ss  > 59)             /* Some systems give a BIG number */ | 
 |       ss = 0; | 
 |     sprintf(datbuf, | 
 | #ifdef pdp11 | 
 | /* For some reason, 2.1x BSD sprintf gets the last field wrong. */ | 
 |             "%04d%02d%02d %02d:%02d:00", | 
 | #else | 
 |             "%04d%02d%02d %02d:%02d:%02d", | 
 | #endif /* pdp11 */ | 
 |             yy, | 
 |             time_stamp->tm_mon + 1, | 
 |             time_stamp->tm_mday, | 
 |             time_stamp->tm_hour, | 
 |             time_stamp->tm_min | 
 | #ifndef pdp11 | 
 |             , ss | 
 | #endif /* pdp11 */ | 
 |             ); | 
 |     yy = (int)strlen(datbuf); | 
 |     debug(F111,"zdatstr",datbuf,yy); | 
 |     if (yy > 17) datbuf[17] = '\0'; | 
 |     return(datbuf); | 
 | #endif /* TIMESTAMP */ | 
 | } | 
 |  | 
 | char * | 
 | zfcdat(name) char *name; { | 
 | #ifdef TIMESTAMP | 
 |     struct stat buffer; | 
 |     extern int diractive; | 
 |     unsigned int mtime; | 
 |     int x; | 
 |     char * s; | 
 |  | 
 |     if (!name) | 
 |       return(""); | 
 |     s = name; | 
 |     if (!*s) | 
 |       return(""); | 
 |  | 
 | #ifdef CKROOT | 
 |     debug(F111,"zfcdat setroot",ckroot,ckrootset); | 
 |     if (ckrootset) if (!zinroot(name)) { | 
 | 	debug(F110,"zfcdat setroot violation",name,0); | 
 | 	return(""); | 
 |     } | 
 | #endif /* CKROOT */ | 
 |  | 
 | #ifdef DTILDE | 
 |     if (*s == '~') { | 
 |         s = tilde_expand(s); | 
 |         if (!s) s = ""; | 
 |         if (!*s) s = name; | 
 |     } | 
 | #endif /* DTILDE */ | 
 |  | 
 |     datbuf[0] = '\0'; | 
 |     x = 0; | 
 |     debug(F111,"zfcdat",s,diractive); | 
 |  | 
 |     if (diractive && zgfs_mtime) { | 
 | 	mtime = zgfs_mtime; | 
 |     } else { | 
 | #ifdef USE_LSTAT | 
 | 	if (diractive) { | 
 | 	    x = lstat(s,&buffer); | 
 | 	    debug(F101,"STAT","",12); | 
 | 	    debug(F101,"zfcdat lstat","",x); | 
 | 	} else { | 
 | #endif /* USE_LSTAT */ | 
 | 	    x = stat(s,&buffer); | 
 | 	    debug(F101,"STAT","",13); | 
 | 	    debug(F101,"zfcdat stat","",x); | 
 | #ifdef USE_LSTAT | 
 | 	} | 
 | #endif /* USE_LSTAT */ | 
 | 	if (x != 0) { | 
 | #ifdef USE_LSTAT | 
 | 	    debug(F111,"zfcdat stat failed",s,errno); | 
 | #else | 
 | 	    debug(F111,"zfcdat lstat failed",s,errno); | 
 | #endif /* USE_LSTAT */ | 
 | 	    return(""); | 
 | 	} | 
 | 	debug(F101,"zfcdat buffer.st_mtime","",buffer.st_mtime); | 
 | 	mtime = buffer.st_mtime; | 
 |     } | 
 |     return(zdtstr(mtime)); | 
 | #else | 
 |     return(""); | 
 | #endif /* TIMESTAMP */ | 
 | } | 
 |  | 
 | #ifndef NOTIMESTAMP | 
 |  | 
 | /* Z S T R D T  --  Converts local date string to internal representation */ | 
 | /* | 
 |   In our case (UNIX) this is seconds since midnite 1 Jan 1970 UTC, | 
 |   suitable for comparison with UNIX file dates.  As far as I know, there is | 
 |   no library or system call -- at least nothing reasonably portable -- to | 
 |   convert local time to UTC. | 
 | */ | 
 | time_t | 
 | zstrdt(date,len) char * date; int len; { | 
 | #ifdef M_UNIX | 
 | /* | 
 |   SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime(). | 
 |   ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in | 
 |   dependence on the XPG4 supplement presence.  So always use | 
 |   what the system header file supplies in ODT 3.0... | 
 | */ | 
 | #ifndef ODT30 | 
 | #ifndef _SCO_DS | 
 |     extern void ftime();  /* extern void ftime(struct timeb *) */ | 
 | #endif /* _SCO_DS */ | 
 | #endif /* ODT30 */ | 
 | #else | 
 | #ifndef M_XENIX | 
 |     extern int ftime(); | 
 | #endif /* M_XENIX */ | 
 | #endif /* M_UNIX */ | 
 |     extern struct tm * localtime(); | 
 |  | 
 |     /* And this should have been declared always through a header file */ | 
 | #ifdef HPUX10 | 
 |     time_t tmx; | 
 |     long days; | 
 | #else | 
 | #ifdef BSD44 | 
 |     time_t tmx; | 
 |     long days; | 
 | #else | 
 |     long tmx, days; | 
 | #endif /* BSD44 */ | 
 | #endif /* HPUX10 */ | 
 |     int i, n, isleapyear; | 
 |                    /*       J  F  M  A   M   J   J   A   S   O   N   D   */ | 
 |                    /*      31 28 31 30  31  30  31  31  30  31  30  31   */ | 
 |     static | 
 |     int monthdays [13] = {  0,0,31,59,90,120,151,181,212,243,273,304,334 }; | 
 |     char s[5]; | 
 |     struct tm *time_stamp; | 
 |  | 
 | #ifdef BSD44 | 
 |     struct timeval tp[2]; | 
 |     long xtimezone = 0L; | 
 | #else | 
 | #ifdef V7 | 
 |     struct utimbuf { | 
 |       time_t timep[2];          /* New access and modificaton time */ | 
 |     } tp; | 
 |     char *tz; | 
 |     long timezone;              /* In case timezone not defined in .h file */ | 
 | #else | 
 | #ifdef SYSUTIMEH | 
 |     struct utimbuf tp; | 
 | #else | 
 |     struct utimbuf { | 
 |         time_t atime; | 
 |         time_t mtime; | 
 |     } tp; | 
 | #endif /* SYSUTIMEH */ | 
 | #endif /* V7 */ | 
 | #endif /* BSD44 */ | 
 |  | 
 | #ifdef ANYBSD | 
 |     long timezone = 0L; | 
 |     static struct timeb tbp; | 
 | #endif /* ANYBSD */ | 
 |  | 
 | #ifdef BEBOX | 
 |     long timezone = 0L; | 
 | #endif /* BEBOX */ | 
 |  | 
 |     debug(F111,"zstrdt",date,len); | 
 |  | 
 |     if ((len == 0) | 
 |         || (len != 17) | 
 |         || (date[8] != ' ') | 
 |         || (date[11] != ':') | 
 |         || (date[14] != ':') ) { | 
 |         debug(F111,"Bad creation date ",date,len); | 
 |         return(-1); | 
 |     } | 
 |     debug(F111,"zstrdt date check 1",date,len); | 
 |     for(i = 0; i < 8; i++) { | 
 |         if (!isdigit(date[i])) { | 
 |             debug(F111,"Bad creation date ",date,len); | 
 |             return(-1); | 
 |         } | 
 |     } | 
 |     debug(F111,"zstrdt date check 2",date,len); | 
 |     i++; | 
 |  | 
 |     for (; i < 16; i += 3) { | 
 |         if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) { | 
 |             debug(F111,"Bad creation date ",date,len); | 
 |             return(-1); | 
 |         } | 
 |     } | 
 |     debug(F111,"zstrdt date check 3",date,len); | 
 |  | 
 |  | 
 | #ifdef COMMENT /* was BSD44 */ | 
 | /* | 
 |    man gettimeofday on BSDI 3.1 says: | 
 |    "The timezone field is no longer used; timezone information is stored out- | 
 |      side the kernel.  See ctime(3) for more information."  So this chunk of | 
 |    code is effectively a no-op, at least in BSDI 3.x. | 
 | */ | 
 |     { | 
 |         int x; | 
 |         struct timezone tzp; | 
 |         x = gettimeofday(NULL, &tzp); | 
 |         debug(F101,"zstrdt BSD44 gettimeofday","",x); | 
 |         if (x > -1) | 
 |           xtimezone = tzp.tz_minuteswest * 60L; | 
 |         else | 
 |           xtimezone = 0L; | 
 |         debug(F101,"zstrdt BSD44 timezone","",xtimezone); | 
 |     } | 
 | #else | 
 | #ifdef ANYBSD | 
 |     debug(F100,"zstrdt BSD calling ftime","",0); | 
 |     ftime(&tbp); | 
 |     debug(F100,"zstrdt BSD back from ftime","",0); | 
 |     timezone = tbp.timezone * 60L; | 
 |     debug(F101,"zstrdt BSD timezone","",timezone); | 
 | #else | 
 | #ifdef SVORPOSIX | 
 |     tzset();                            /* Set timezone */ | 
 | #else | 
 | #ifdef V7 | 
 |     if ((tz = getenv("TZ")) == NULL) | 
 |       timezone = 0;                     /* UTC/GMT */ | 
 |     else | 
 |       timezone = atoi(&tz[3]);          /* Set 'timezone'. */ | 
 |     timezone *= 60L; | 
 | #endif /* V7 */ | 
 | #endif /* SVORPOSIX */ | 
 | #endif /* ANYBSD */ | 
 | #endif /* COMMENT (was BSD44) */ | 
 |  | 
 |     debug(F100,"zstrdt so far so good","",0); | 
 |  | 
 |     s[4] = '\0'; | 
 |     for (i = 0; i < 4; i++)             /* Fix the year */ | 
 |       s[i] = date[i]; | 
 |  | 
 |     n = atoi(s); | 
 |     debug(F111,"zstrdt year",s,n); | 
 |     if (n < 1970) { | 
 |         debug(F100,"zstrdt fails - year","",n); | 
 |         return(-1); | 
 |     } | 
 |  | 
 | /*  Previous year's leap days.  This won't work after year 2100. */ | 
 |  | 
 |     isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0); | 
 |     days = (long) (n - 1970) * 365; | 
 |     days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400; | 
 |  | 
 |     s[2] = '\0'; | 
 |  | 
 |     for (i = 4; i < 16; i += 2) { | 
 |         s[0] = date[i]; | 
 |         s[1] = date[i + 1]; | 
 |         n = atoi(s); | 
 |         switch (i) { | 
 |           case 4:                       /* MM: month */ | 
 |             if ((n < 1 ) || ( n > 12)) { | 
 |                 debug(F111,"zstrdt 4 bad date ",date,len); | 
 |                 return(-1); | 
 |             } | 
 |             days += monthdays [n]; | 
 |             if (isleapyear && n > 2) | 
 |               ++days; | 
 |             continue; | 
 |  | 
 |           case 6:                       /* DD: day */ | 
 |             if ((n < 1 ) || ( n > 31)) { | 
 |                 debug(F111,"zstrdt 6 bad date ",date,len); | 
 |                 return(-1); | 
 |             } | 
 |             tmx = (days + n - 1) * 24L * 60L * 60L; | 
 |             i++;                        /* Skip the space */ | 
 |             continue; | 
 |  | 
 |           case 9:                       /* hh: hour */ | 
 |             if ((n < 0 ) || ( n > 23)) { | 
 |                 debug(F111,"zstrdt 9 bad date ",date,len); | 
 |                 return(-1); | 
 |             } | 
 |             tmx += n * 60L * 60L; | 
 |             i++;                        /* Skip the colon */ | 
 |             continue; | 
 |  | 
 |           case 12:                      /* mm: minute */ | 
 |             if ((n < 0 ) || ( n > 59)) { | 
 |                 debug(F111,"zstrdt 12 bad date ",date,len); | 
 |                 return(-1); | 
 |             } | 
 | #ifdef COMMENT /* (was BSD44) */        /* Correct for time zone */ | 
 |             tmx += xtimezone; | 
 |             debug(F101,"zstrdt BSD44 tmx","",tmx); | 
 | #else | 
 | #ifdef ANYBSD | 
 |             tmx += timezone; | 
 | #else | 
 | #ifndef CONVEX9 /* Don't yet know how to do this here */ | 
 | #ifdef ultrix | 
 |             tmx += (long) timezone; | 
 | #else | 
 | #ifdef Plan9 | 
 |             { | 
 |                 extern time_t tzoffset; | 
 |                 tmx += tzoffset; | 
 |             } | 
 | #else | 
 | #ifndef BSD44 | 
 |             tmx += timezone; | 
 | #endif /* BSD44 */ | 
 | #endif /* Plan9 */ | 
 | #endif /* ultrix */ | 
 | #endif /* CONVEX9 */ | 
 | #endif /* ANYBSD */ | 
 | #endif /* COMMENT (was BSD44) */ | 
 |             tmx += n * 60L; | 
 |             i++;                        /* Skip the colon */ | 
 |             continue; | 
 |  | 
 |           case 15:                      /* ss: second */ | 
 |             if ((n < 0 ) || ( n > 59)) { | 
 |                 debug(F111,"zstrdt 15 bad date ",date,len); | 
 |                 return(-1); | 
 |             } | 
 |             tmx += n; | 
 |         } | 
 |         time_stamp = localtime(&tmx); | 
 |         debug(F101,"zstrdt tmx 1","",tmx); | 
 |         if (!time_stamp) | 
 |           return(-1); | 
 | #ifdef COMMENT | 
 |         /* Why was this here? */ | 
 |         time_stamp = localtime(&tmx); | 
 |         debug(F101,"zstrdt tmx 2","",tmx); | 
 | #endif /* COMMENT */ | 
 | #ifdef BSD44 | 
 |         {   /* New to 7.0 - Works in at at least BSDI 3.1 and FreeBSD 2.2.7 */ | 
 |             long zz; | 
 |             zz = time_stamp->tm_gmtoff; /* Seconds away from Zero Meridian */ | 
 |             debug(F101,"zstrdt BSD44 tm_gmtoff","",zz); | 
 |             tmx -= zz; | 
 |             debug(F101,"zstrdt BSD44 tmx 3 (GMT)","",tmx); | 
 |         } | 
 | #else | 
 |         /* | 
 |            Daylight Savings Time adjustment. | 
 |            Do this everywhere BUT in BSD44 because in BSD44, | 
 |            tm_gmtoff also includes the DST adjustment. | 
 |         */ | 
 |         if (time_stamp->tm_isdst) { | 
 |             tmx -= 60L * 60L; | 
 |             debug(F101,"zstrdt tmx 3 (DST)","",tmx); | 
 |         } | 
 | #endif /* BSD44 */ | 
 |         n = time_stamp->tm_year; | 
 |         if (n < 300) { | 
 |             n += 1900; | 
 |         } | 
 |     } | 
 |     return(tmx); | 
 | } | 
 |  | 
 |  | 
 | #ifdef ZLOCALTIME | 
 | /* Z L O C A L T I M E  --  GMT/UTC time string to local time string */ | 
 |  | 
 | /* | 
 |    Call with: "yyyymmdd hh:mm:ss" GMT/UTC date-time. | 
 |    Returns:   "yyyymmdd hh:mm:ss" local date-time on success, NULL on failure. | 
 | */ | 
 | static char zltimbuf[64]; | 
 |  | 
 | char * | 
 | zlocaltime(gmtstring) char * gmtstring; { | 
 | #ifdef M_UNIX | 
 | /* | 
 |   SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime(). | 
 |   ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in | 
 |   dependence on the XPG4 supplement presence.  So always use | 
 |   what the system header file supplies in ODT 3.0... | 
 | */ | 
 | #ifndef ODT30 | 
 | #ifndef _SCO_DS | 
 |     extern void ftime();  /* extern void ftime(struct timeb *) */ | 
 | #endif /* _SCO_DS */ | 
 | #endif /* ODT30 */ | 
 | #else | 
 | #ifndef M_XENIX | 
 |     extern int ftime(); | 
 | #endif /* M_XENIX */ | 
 | #endif /* M_UNIX */ | 
 |     extern struct tm * localtime(); | 
 |  | 
 |     /* And this should have been declared always through a header file */ | 
 | #ifdef HPUX10 | 
 |     time_t tmx; | 
 |     long days; | 
 | #else | 
 | #ifdef BSD44 | 
 |     time_t tmx; | 
 |     long days; | 
 | #else | 
 |     long tmx, days; | 
 | #endif /* BSD44 */ | 
 | #endif /* HPUX10 */ | 
 |     int i, n, x, isleapyear; | 
 |                    /*       J  F  M  A   M   J   J   A   S   O   N   D   */ | 
 |                    /*      31 28 31 30  31  30  31  31  30  31  30  31   */ | 
 |     static | 
 |     int monthdays [13] = {  0,0,31,59,90,120,151,181,212,243,273,304,334 }; | 
 |     char s[5]; | 
 |     struct tm *time_stamp; | 
 |  | 
 | #ifdef BSD44 | 
 |     struct timeval tp[2]; | 
 | #else | 
 | #ifdef V7 | 
 |     struct utimbuf { | 
 |       time_t timep[2];          /* New access and modificaton time */ | 
 |     } tp; | 
 | #else | 
 | #ifdef SYSUTIMEH | 
 |     struct utimbuf tp; | 
 | #else | 
 |     struct utimbuf { | 
 |         time_t atime; | 
 |         time_t mtime; | 
 |     } tp; | 
 | #endif /* SYSUTIMEH */ | 
 | #endif /* V7 */ | 
 | #endif /* BSD44 */ | 
 |  | 
 | #ifdef ANYBSD | 
 |     static struct timeb tbp; | 
 | #endif /* ANYBSD */ | 
 |  | 
 |     char * date = gmtstring; | 
 |     int len; | 
 |  | 
 |     len = strlen(date); | 
 |     debug(F111,"zlocaltime",date,len); | 
 |  | 
 |     if ((len == 0) | 
 |         || (len != 17) | 
 |         || (date[8] != ' ') | 
 |         || (date[11] != ':') | 
 |         || (date[14] != ':') ) { | 
 |         debug(F111,"Bad creation date ",date,len); | 
 |         return(NULL); | 
 |     } | 
 |     debug(F111,"zlocaltime date check 1",date,len); | 
 |     for(i = 0; i < 8; i++) { | 
 |         if (!isdigit(date[i])) { | 
 |             debug(F111,"Bad creation date ",date,len); | 
 |             return(NULL); | 
 |         } | 
 |     } | 
 |     debug(F111,"zlocaltime date check 2",date,len); | 
 |     i++; | 
 |  | 
 |     for (; i < 16; i += 3) { | 
 |         if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) { | 
 |             debug(F111,"Bad creation date ",date,len); | 
 | 	    return(NULL); | 
 |         } | 
 |     } | 
 |     debug(F111,"zlocaltime date check 3",date,len); | 
 |  | 
 |     debug(F100,"zlocaltime so far so good","",0); | 
 |  | 
 |     s[4] = '\0'; | 
 |     for (i = 0; i < 4; i++)             /* Fix the year */ | 
 |       s[i] = date[i]; | 
 |  | 
 |     n = atoi(s); | 
 |     debug(F111,"zlocaltime year",s,n); | 
 |     if (n < 1970) { | 
 |         debug(F100,"zlocaltime fails - year","",n); | 
 |         return(NULL); | 
 |     } | 
 |  | 
 | /*  Previous year's leap days.  This won't work after year 2100. */ | 
 |  | 
 |     isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0); | 
 |     days = (long) (n - 1970) * 365; | 
 |     days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400; | 
 |  | 
 |     s[2] = '\0'; | 
 |  | 
 |     for (i = 4; i < 16; i += 2) { | 
 |         s[0] = date[i]; | 
 |         s[1] = date[i + 1]; | 
 |         n = atoi(s); | 
 |         switch (i) { | 
 |           case 4:                       /* MM: month */ | 
 |             if ((n < 1 ) || ( n > 12)) { | 
 |                 debug(F111,"zlocaltime 4 bad date ",date,len); | 
 |                 return(NULL); | 
 |             } | 
 |             days += monthdays [n]; | 
 |             if (isleapyear && n > 2) | 
 |               ++days; | 
 |             continue; | 
 |  | 
 |           case 6:                       /* DD: day */ | 
 |             if ((n < 1 ) || ( n > 31)) { | 
 |                 debug(F111,"zlocaltime 6 bad date ",date,len); | 
 |                 return(NULL); | 
 |             } | 
 |             tmx = (days + n - 1) * 24L * 60L * 60L; | 
 |             i++;                        /* Skip the space */ | 
 |             continue; | 
 |  | 
 |           case 9:                       /* hh: hour */ | 
 |             if ((n < 0 ) || ( n > 23)) { | 
 |                 debug(F111,"zlocaltime 9 bad date ",date,len); | 
 |                 return(NULL); | 
 |             } | 
 |             tmx += n * 60L * 60L; | 
 |             i++;                        /* Skip the colon */ | 
 |             continue; | 
 |  | 
 |           case 12:                      /* mm: minute */ | 
 |             if ((n < 0 ) || ( n > 59)) { | 
 |                 debug(F111,"zlocaltime 12 bad date ",date,len); | 
 |                 return(NULL); | 
 |             } | 
 |             tmx += n * 60L; | 
 |             i++;                        /* Skip the colon */ | 
 |             continue; | 
 |  | 
 |           case 15:                      /* ss: second */ | 
 |             if ((n < 0 ) || ( n > 59)) { | 
 |                 debug(F111,"zlocaltime 15 bad date ",date,len); | 
 |                 return(NULL); | 
 |             } | 
 |             tmx += n; | 
 |         } | 
 |  | 
 | /* | 
 |   At this point tmx is the time_t representation of the argument date-time | 
 |   string without any timezone or DST adjustments.  Therefore it should be | 
 |   the same as the time_t representation of the GMT/UTC time.  Now we should | 
 |   be able to feed it to localtime() and have it converted to a struct tm | 
 |   representing the local time equivalent of the given UTC time. | 
 | */ | 
 |         time_stamp = localtime(&tmx); | 
 |         if (!time_stamp) | 
 |           return(NULL); | 
 |     } | 
 |  | 
 | /* Now we simply reformat the struct tm to a string */ | 
 |  | 
 |     x = time_stamp->tm_year; | 
 |     if (time_stamp->tm_year < 70 || time_stamp->tm_year > 8099) | 
 |       return(NULL); | 
 |     if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11) | 
 |       return(NULL); | 
 |     if (time_stamp->tm_mday < 1 || time_stamp->tm_mday > 31) | 
 |       return(NULL); | 
 |     if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 24) | 
 |       return(NULL); | 
 |     if (time_stamp->tm_min < 0 || time_stamp->tm_min > 60) | 
 |       return(NULL); | 
 |     if (time_stamp->tm_sec < 0 || time_stamp->tm_sec > 60) | 
 |       return(NULL); | 
 |     sprintf(zltimbuf,"%04d%02d%02d %02d:%02d:%02d", | 
 | 	    time_stamp->tm_year + 1900, | 
 | 	    time_stamp->tm_mon + 1, | 
 | 	    time_stamp->tm_mday, | 
 | 	    time_stamp->tm_hour, | 
 | 	    time_stamp->tm_min, | 
 | 	    time_stamp->tm_sec | 
 | 	    ); | 
 |     return((char *)zltimbuf); | 
 | } | 
 | #endif /* ZLOCALTIME */ | 
 | #endif /* NOTIMESTAMP */ | 
 |  | 
 | /* Z S T I M E  --  Set modification date/time+permissions for incoming file */ | 
 | /* | 
 |  Call with: | 
 |  f  = pointer to name of existing file. | 
 |  yy = pointer to a Kermit file attribute structure in which yy->date.val | 
 |       is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00. | 
 |       yy->lprotect.val & yy->gprotect.val are permission/protection values. | 
 |  x  = is a function code: 0 means to set the file's attributes as given. | 
 |       1 means compare the date in struct yy with the file creation date. | 
 |  Returns: | 
 |  -1 on any kind of error. | 
 |   0 if x is 0 and the attributes were set successfully. | 
 |   0 if x is 1 and date from attribute structure <= file creation date. | 
 |   1 if x is 1 and date from attribute structure > file creation date. | 
 | */ | 
 | int | 
 | zstime(f,yy,x) | 
 |     char *f; struct zattr *yy; int x; | 
 | /* zstime */ { | 
 |     int r = -1;                         /* Return code */ | 
 | #ifdef CK_PERMS | 
 |     int setperms = 0; | 
 | #endif /* CK_PERMS */ | 
 |     int setdate = 0; | 
 |  | 
 | /* It is ifdef'd TIMESTAMP because it might not work on V7. bk@kullmar.se.  */ | 
 |  | 
 | #ifdef TIMESTAMP | 
 | #ifdef BSD44 | 
 |     extern int utimes(); | 
 | #else | 
 |     extern int utime(); | 
 | #endif /* BSD44 */ | 
 |  | 
 |     struct stat sb; | 
 |  | 
 | /* At least, the declarations for int functions are not needed anyway */ | 
 |  | 
 | #ifdef BSD44 | 
 |     struct timeval tp[2]; | 
 |     long xtimezone; | 
 | #else | 
 | #ifdef V7 | 
 |     struct utimbuf { | 
 | 	time_t timep[2];		/* New access and modificaton time */ | 
 |     } tp; | 
 |     char *tz; | 
 |     long timezone;                      /* In case not defined in .h file */ | 
 | #else | 
 | #ifdef SYSUTIMEH | 
 |     struct utimbuf tp; | 
 | #else | 
 |     struct utimbuf { | 
 |         time_t atime; | 
 |         time_t mtime; | 
 |     } tp; | 
 | #endif /* SYSUTIMEH */ | 
 | #endif /* V7 */ | 
 | #endif /* BSD44 */ | 
 |  | 
 |     long tm = 0L; | 
 |  | 
 |     if (!f) f = ""; | 
 |     if (!*f) return(-1); | 
 |     if (!yy) return(-1); | 
 |  | 
 |     debug(F110,"zstime",f,0); | 
 |     debug(F111,"zstime date",yy->date.val,yy->date.len); | 
 |  | 
 | #ifdef CKROOT | 
 |     debug(F111,"zstime setroot",ckroot,ckrootset); | 
 |     if (ckrootset) if (!zinroot(f)) { | 
 | 	debug(F110,"zstime setroot violation",f,0); | 
 | 	return(0); | 
 |     } | 
 | #endif /* CKROOT */ | 
 |  | 
 |     if (yy->date.len == 0) {            /* No date in struct */ | 
 |         if (yy->lprotect.len != 0) {    /* So go do permissions */ | 
 |             goto zsperms; | 
 |         } else { | 
 |             debug(F100,"zstime: nothing to do","",0); | 
 |             return(0); | 
 |         } | 
 |     } | 
 |     if ((tm = zstrdt(yy->date.val,yy->date.len)) < 0) { | 
 |         debug(F101,"zstime: zstrdt fails","",0); | 
 |         return(-1); | 
 |     } | 
 |     debug(F101,"zstime: tm","",tm); | 
 |     debug(F111,"zstime: A-pkt date ok ",yy->date.val,yy->date.len); | 
 |  | 
 |     if (stat(f,&sb)) {                  /* Get the time for the file */ | 
 | 	debug(F101,"STAT","",14); | 
 |         debug(F111,"zstime: Can't stat file:",f,errno); | 
 |         return(-1); | 
 |     } | 
 |     debug(F101,"STAT","",15); | 
 |     setdate = 1; | 
 |  | 
 |   zsperms: | 
 | #ifdef CK_PERMS | 
 |     { | 
 |         int i, x = 0, xx, flag = 0; | 
 |         char * s; | 
 | #ifdef DEBUG | 
 |         char obuf[24]; | 
 |         if (deblog) { | 
 |             debug(F111,"zstime lperms",yy->lprotect.val,yy->lprotect.len); | 
 |             debug(F111,"zstime gperms",yy->gprotect.val,yy->gprotect.len); | 
 |             debug(F110,"zstime system id",yy->systemid.val,0); | 
 |             sprintf(obuf,"%o",sb.st_mode); | 
 |             debug(F110,"zstime file perms before",obuf,0); | 
 |         } | 
 | #endif /* DEBUG */ | 
 |  | 
 | #ifdef CK_LOGIN | 
 |         debug(F101,"zstime isguest","",isguest); | 
 |         debug(F101,"zstime ckxperms","",ckxperms); | 
 |         if (isguest) { | 
 | #ifdef COMMENT | 
 |             /* Clear owner permissions */ | 
 |             sb.st_mode &= (unsigned) 0177077; /* (16 bits) */ | 
 | #else | 
 |             /* Set permissions from ckxperms variable */ | 
 |             sb.st_mode = ckxperms; | 
 | #endif /* COMMENT */ | 
 |             debug(F101,"zstime isguest sb.st_mode","",sb.st_mode); | 
 | #ifdef COMMENT | 
 |             /* We already set them in zopeno() */ | 
 |             setperms = 1; | 
 | #endif /* COMMENT */ | 
 |             flag = 0; | 
 |         } else | 
 | #endif /* CK_LOGIN */ | 
 |           if ((yy->lprotect.len > 0 &&  /* Have local-format permissions */ | 
 |             yy->systemid.len > 0 &&     /* from A-packet... */ | 
 | #ifdef UNIX | 
 |             !strcmp(yy->systemid.val,"U1") /* AND you are same as me */ | 
 | #else | 
 |             0 | 
 | #endif /* UNIX */ | 
 |              ) || (yy->lprotect.len < 0) /* OR by inheritance from old file */ | 
 |             ) { | 
 |             flag = 1; | 
 |             s = yy->lprotect.val;       /* UNIX filemode */ | 
 |             xx = yy->lprotect.len; | 
 |             if (xx < 0)                 /* len < 0 means inheritance */ | 
 |               xx = 0 - xx; | 
 |             for (i = 0; i < xx; i++) {  /* Decode octal string */ | 
 |                 if (*s <= '7' && *s >= '0') { | 
 |                     x = 8 * x + (int)(*s) - '0'; | 
 |                 } else { | 
 |                     flag = 0; | 
 |                     break; | 
 |                 } | 
 |                 s++; | 
 |             } | 
 | #ifdef DEBUG | 
 |             sprintf(obuf,"%o",x); | 
 |             debug(F110,"zstime octal lperm",obuf,0); | 
 | #endif /* DEBUG */ | 
 |         } else if (!flag && yy->gprotect.len > 0) { | 
 |             int g; | 
 | #ifdef CK_SCO32V4 | 
 |             mode_t mask; | 
 | #else | 
 |             int mask; | 
 | #endif /* CK_SCO32V4 */ | 
 |             mask = umask(0);            /* Get umask */ | 
 |             debug(F101,"zstime mask 1","",mask); | 
 |             umask(mask);                /* Put it back */ | 
 |             mask ^= 0777;               /* Flip the bits */ | 
 |             debug(F101,"zstime mask 2","",mask); | 
 |             g = xunchar(*(yy->gprotect.val)); /* Decode generic protection */ | 
 |             debug(F101,"zstime gprotect","",g); | 
 | #ifdef S_IRUSR | 
 |             debug(F100,"zstime S_IRUSR","",0); | 
 |             if (g & 1) x |= S_IRUSR;    /* Read permission */ | 
 |             flag = 1; | 
 | #endif /* S_IRUSR */ | 
 | #ifdef S_IWUSR | 
 |             debug(F100,"zstime S_IWUSR","",0); | 
 |             if (g & 2) x |= S_IWUSR;    /* Write permission */ | 
 |             if (g & 16) x |= S_IWUSR;   /* Delete permission */ | 
 |             flag = 1; | 
 | #endif /* S_IWUSR */ | 
 | #ifdef S_IXUSR | 
 |             debug(F100,"zstime S_IXUSR","",0); | 
 |             if (g & 4)                  /* Has execute permission bit */ | 
 |               x |= S_IXUSR; | 
 |             else                        /* Doesn't have it */ | 
 |               mask &= 0666;             /* so also clear it out of mask */ | 
 |             flag = 1; | 
 | #endif /* S_IXUSR */ | 
 |             debug(F101,"zstime mask x","",x); | 
 |             x |= mask; | 
 |             debug(F101,"zstime mask x|mask","",x); | 
 |         } | 
 |         debug(F101,"zstime flag","",flag); | 
 |         if (flag) { | 
 | #ifdef S_IFMT | 
 |             debug(F101,"zstime S_IFMT x","",x); | 
 |             sb.st_mode = (sb.st_mode & S_IFMT) | x; | 
 |             setperms = 1; | 
 | #else | 
 | #ifdef _IFMT | 
 |             debug(F101,"zstime _IFMT x","",x); | 
 |             sb.st_mode = (sb.st_mode & _IFMT) | x; | 
 |             setperms = 1; | 
 | #endif /* _IFMT */ | 
 | #endif /* S_IFMT */ | 
 |         } | 
 | #ifdef DEBUG | 
 |         sprintf(obuf,"%04o",sb.st_mode); | 
 |         debug(F111,"zstime file perms after",obuf,setperms); | 
 | #endif /* DEBUG */ | 
 |     } | 
 | #endif /* CK_PERMS */ | 
 |  | 
 |     debug(F101,"zstime: sb.st_atime","",sb.st_atime); | 
 |  | 
 | #ifdef BSD44 | 
 |     tp[0].tv_sec = sb.st_atime;         /* Access time first */ | 
 |     tp[1].tv_sec = tm;                  /* Update time second */ | 
 |     debug(F100,"zstime: BSD44 modtime","",0); | 
 | #else | 
 | #ifdef V7 | 
 |     tp.timep[0] = tm;                   /* Set modif. time to creation date */ | 
 |     tp.timep[1] = sb.st_atime;          /* Don't change the access time */ | 
 |     debug(F100,"zstime: V7 modtime","",0); | 
 | #else | 
 | #ifdef SYSUTIMEH | 
 |     tp.modtime = tm;                    /* Set modif. time to creation date */ | 
 |     tp.actime = sb.st_atime;            /* Don't change the access time */ | 
 |     debug(F100,"zstime: SYSUTIMEH modtime","",0); | 
 | #else | 
 |     tp.mtime = tm;                      /* Set modif. time to creation date */ | 
 |     tp.atime = sb.st_atime;             /* Don't change the access time */ | 
 |     debug(F100,"zstime: default modtime","",0); | 
 | #endif /* SYSUTIMEH */ | 
 | #endif /* V7 */ | 
 | #endif /* BSD44 */ | 
 |  | 
 |     switch (x) {                        /* Execute desired function */ | 
 |       case 0:                           /* Set the creation date of the file */ | 
 | #ifdef CK_PERMS                         /* And permissions */ | 
 | /* | 
 |   NOTE: If we are inheriting permissions from a previous file, and the | 
 |   previous file was a directory, this would turn the new file into a directory | 
 |   too, but it's not, so we try to unset the right bit.  Luckily, this code | 
 |   will probably never be executed since the upper level modules do not allow | 
 |   reception of a file that has the same name as a directory. | 
 |  | 
 |   NOTE 2: We change the permissions *before* we change the modification time, | 
 |   otherwise changing the permissions would set the mod time to the present | 
 |   time. | 
 | */ | 
 |         { | 
 |             int x; | 
 |             debug(F101,"zstime setperms","",setperms); | 
 |             if (S_ISDIR(sb.st_mode)) { | 
 |                 debug(F101,"zstime DIRECTORY bit on","",sb.st_mode); | 
 |                 sb.st_mode ^= 0040000; | 
 |                 debug(F101,"zstime DIRECTORY bit off","",sb.st_mode); | 
 |             } | 
 |             if (setperms) { | 
 |                 x = chmod(f,sb.st_mode); | 
 |                 debug(F101,"zstime chmod","",x); | 
 |             } | 
 |         } | 
 |         if (x < 0) return(-1); | 
 | #endif /* CK_PERMS */ | 
 |  | 
 |         if (!setdate)                   /* We don't have a date */ | 
 |           return(0);                    /* so skip the following... */ | 
 |  | 
 |         if ( | 
 | #ifdef BSD44 | 
 |             utimes(f,tp) | 
 | #else | 
 |             utime(f,&tp) | 
 | #endif /* BSD44 */ | 
 |             ) {                         /* Fix modification time */ | 
 |             debug(F111,"zstime 0: can't set modtime for file",f,errno); | 
 |             r = -1; | 
 |         } else  { | 
 | 	    /* Including the modtime here is not portable */ | 
 |             debug(F110,"zstime 0: modtime set for file",f,0); | 
 |             r = 0; | 
 |         } | 
 |         break; | 
 |  | 
 |       case 1:                           /* Compare the dates */ | 
 | /* | 
 |   This was st_atime, which was wrong.  We want the file-data modification | 
 |   time, st_mtime. | 
 | */ | 
 |         debug(F111,"zstime 1: compare",f,sb.st_mtime); | 
 |         debug(F111,"zstime 1: compare","packet",tm); | 
 |  | 
 |         r = (sb.st_mtime < tm) ? 0 : 1; | 
 |         break; | 
 |  | 
 |       default:                          /* Error */ | 
 |         r = -1; | 
 |     } | 
 | #endif /* TIMESTAMP */ | 
 |     return(r); | 
 | } | 
 |  | 
 | /* Find initialization file. */ | 
 |  | 
 | #ifdef NOTUSED | 
 | int | 
 | zkermini() { | 
 | /*  nothing here for Unix.  This function added for benefit of VMS Kermit.  */ | 
 |     return(0); | 
 | } | 
 | #endif /* NOTUSED */ | 
 |  | 
 | #ifndef UNIX | 
 | /* Historical -- not used in Unix any more (2001-11-03) */ | 
 | #ifndef NOFRILLS | 
 | int | 
 | zmail(p,f) char *p; char *f; {          /* Send file f as mail to address p */ | 
 | /* | 
 |   Returns 0 on success | 
 |    2 if mail delivered but temp file can't be deleted | 
 |   -2 if mail can't be delivered | 
 |   -1 on file access error | 
 |   The UNIX version always returns 0 because it can't get a good return | 
 |   code from zsyscmd. | 
 | */ | 
 |     int n; | 
 |  | 
 | #ifdef CK_LOGIN | 
 |     if (isguest) | 
 |       return(-2); | 
 | #endif /* CK_LOGIN */ | 
 |  | 
 |     if (!f) f = ""; | 
 |     if (!*f) return(-1); | 
 |  | 
 | #ifdef CKROOT | 
 |     debug(F111,"zmail setroot",ckroot,ckrootset); | 
 |     if (ckrootset) if (!zinroot(f)) { | 
 | 	debug(F110,"zmail setroot violation",f,0); | 
 | 	return(-1); | 
 |     } | 
 | #endif /* CKROOT */ | 
 |  | 
 | #ifdef BSD4 | 
 | /* The idea is to use /usr/ucb/mail, rather than regular mail, so that   */ | 
 | /* a subject line can be included with -s.  Since we can't depend on the */ | 
 | /* user's path, we use the convention that /usr/ucb/Mail = /usr/ucb/mail */ | 
 | /* and even if Mail has been moved to somewhere else, this should still  */ | 
 | /* find it...  The search could be made more reliable by actually using  */ | 
 | /* access() to see if /usr/ucb/Mail exists. */ | 
 |  | 
 |     n = strlen(f); | 
 |     n = n + n + 15 + (int)strlen(p); | 
 |  | 
 |     if (n > ZMBUFLEN) | 
 |       return(-2); | 
 |  | 
 | #ifdef DGUX540 | 
 |     sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f); | 
 | #else | 
 |     sprintf(zmbuf,"Mail -s %c%s%c %s < %s", '"', f, '"', p, f); | 
 | #endif /* DGUX540 */ | 
 |     zsyscmd(zmbuf); | 
 | #else | 
 | #ifdef SVORPOSIX | 
 | #ifndef OXOS | 
 |     sprintf(zmbuf,"mail %s < %s", p, f); | 
 | #else /* OXOS */ | 
 |     sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f); | 
 | #endif /* OXOS */ | 
 |     zsyscmd(zmbuf); | 
 | #else | 
 |     *zmbuf = '\0'; | 
 | #endif | 
 | #endif | 
 |     return(0); | 
 | } | 
 | #endif /* NOFRILLS */ | 
 | #endif /* UNIX */ | 
 |  | 
 | #ifndef NOFRILLS | 
 | int | 
 | zprint(p,f) char *p; char *f; {         /* Print file f with options p */ | 
 |     extern char * printername;          /* From ckuus3.c */ | 
 |     extern int printpipe; | 
 |     int n; | 
 |  | 
 | #ifdef CK_LOGIN | 
 |     if (isguest) | 
 |       return(-2); | 
 | #endif /* CK_LOGIN */ | 
 |  | 
 |     if (!f) f = ""; | 
 |     if (!*f) return(-1); | 
 |  | 
 | #ifdef CKROOT | 
 |     debug(F111,"zprint setroot",ckroot,ckrootset); | 
 |     if (ckrootset) if (!zinroot(f)) { | 
 | 	debug(F110,"zprint setroot violation",f,0); | 
 | 	return(-1); | 
 |     } | 
 | #endif /* CKROOT */ | 
 |  | 
 |     debug(F110,"zprint file",f,0); | 
 |     debug(F110,"zprint flags",p,0); | 
 |     debug(F110,"zprint printername",printername,0); | 
 |     debug(F101,"zprint printpipe","",printpipe); | 
 |  | 
 | #ifdef UNIX | 
 | /* | 
 |   Note use of standard input redirection.  In some systems, lp[r] runs | 
 |   setuid to lp (or ...?), so if user has sent a file into a directory | 
 |   that lp does not have read access to, it can't be printed unless it is | 
 |   fed to lp[r] as standard input. | 
 | */ | 
 |     if (printpipe && printername) { | 
 | 	n = 8 + (int)strlen(f) + (int)strlen(printername); | 
 | 	if (n > ZMBUFLEN) | 
 | 	  return(-2); | 
 |         sprintf(zmbuf,"cat %s | %s", f, printername); | 
 |     } else if (printername) { | 
 | 	n = 8 + (int)strlen(f) + (int)strlen(printername); | 
 | 	if (n > ZMBUFLEN) | 
 | 	  return(-2); | 
 |         sprintf(zmbuf,"cat %s >> %s", f, printername); | 
 |     } else { | 
 | 	n = 4 + (int)strlen(PRINTCMD) + (int)strlen(p) + (int)strlen(f); | 
 | 	if (n > ZMBUFLEN) | 
 | 	  return(-2); | 
 |         sprintf(zmbuf,"%s %s < %s", PRINTCMD, p, f); | 
 |     } | 
 |     debug(F110,"zprint command",zmbuf,0); | 
 |     zsyscmd(zmbuf); | 
 | #else /* Not UNIX */ | 
 |     *zmbuf = '\0'; | 
 | #endif /* UNIX */ | 
 |     return(0); | 
 | } | 
 | #endif /* NOFRILLS */ | 
 |  | 
 | /*  Wildcard expansion functions...  */ | 
 |  | 
 | static char scratch[MAXPATH+4];         /* Used by both methods */ | 
 |  | 
 | static int oldmtchs = 0;                /* Let shell (ls) expand them. */ | 
 | #ifdef COMMENT | 
 | static char *lscmd = "/bin/ls -d";      /* Command to use. */ | 
 | #else | 
 | static char *lscmd = "echo";            /* Command to use. */ | 
 | #endif /* COMMENT */ | 
 |  | 
 | #ifndef NOPUSH | 
 | int | 
 | shxpand(pat,namlst,len) char *pat, *namlst[]; int len; { | 
 |     char *fgbuf = NULL;                 /* Buffer for forming ls command */ | 
 |     char *p, *q;                        /* Workers */ | 
 |  | 
 |     int i, x, retcode, itsadir; | 
 |     char c; | 
 |  | 
 |     x = (int)strlen(pat) + (int)strlen(lscmd) + 3; /* Length of ls command */ | 
 |     for (i = 0; i < oldmtchs; i++) {    /* Free previous file list */ | 
 |         if (namlst[i] ) {               /* If memory is allocated  */ | 
 |             free(namlst[i]);            /* Free the memory         */ | 
 |             namlst[i] = NULL ;          /* Remember no memory is allocated */ | 
 |         } | 
 |     } | 
 |     oldmtchs = 0 ;                      /* Remember there are no matches */ | 
 |     fgbuf = malloc(x);                  /* Get buffer for command */ | 
 |     if (!fgbuf) return(-1);             /* Fail if cannot */ | 
 |     ckmakmsg(fgbuf,x,lscmd," ",pat,NULL); /* Form the command */ | 
 |     zxcmd(ZIFILE,fgbuf);                /* Start the command */ | 
 |     i = 0;                              /* File counter */ | 
 |     p = scratch;                        /* Point to scratch area */ | 
 |     retcode = -1;                       /* Assume failure */ | 
 |     while ((x = zminchar()) != -1) {    /* Read characters from command */ | 
 |         c = (char) x; | 
 |         if (c == ' ' || c == '\n') {    /* Got newline or space? */ | 
 |             *p = '\0';                  /* Yes, terminate string */ | 
 |             p = scratch;                /* Point back to beginning */ | 
 |             if (zchki(p) == -1)         /* Does file exist? */ | 
 |               continue;                 /* No, continue */ | 
 |             itsadir = isdir(p);         /* Yes, is it a directory? */ | 
 |             if (xdironly && !itsadir)   /* Want only dirs but this isn't */ | 
 |               continue;                 /* so skip. */ | 
 |             if (xfilonly && itsadir)    /* It's a dir but want only files */ | 
 |               continue;                 /* so skip. */ | 
 |             x = (int)strlen(p);         /* Keep - get length of name */ | 
 |             q = malloc(x+1);            /* Allocate space for it */ | 
 |             if (!q) goto shxfin;        /* Fail if space can't be obtained */ | 
 |             strcpy(q,scratch);          /* (safe) Copy name to space */ | 
 |             namlst[i++] = q;            /* Copy pointer to name into array */ | 
 |             if (i >= len) goto shxfin;  /* Fail if too many */ | 
 |         } else {                        /* Regular character */ | 
 |             *p++ = c;                   /* Copy it into scratch area */ | 
 |         } | 
 |     } | 
 |     retcode = i;                        /* Return number of matching files */ | 
 | shxfin:                                 /* Common exit point */ | 
 |     free(fgbuf);                        /* Free command buffer */ | 
 |     fgbuf = NULL; | 
 |     zclosf(ZIFILE);                     /* Delete the command fork. */ | 
 |     oldmtchs = i;                       /* Remember how many files */ | 
 |     return(retcode); | 
 | } | 
 | #endif /* NOPUSH */ | 
 |  | 
 | /* | 
 |   Directory-reading functions for UNIX originally written for C-Kermit 4.0 | 
 |   by Jeff Damens, CUCCA, 1984. | 
 | */ | 
 | static char * xpat = NULL;              /* Global copy of fgen() pattern */ | 
 | static char * xpatlast = NULL;          /* Rightmost segment of pattern*/ | 
 | static int xpatslash = 0;               /* Slash count in pattern */ | 
 | static int xpatwild = 0;                /* Original pattern is wild */ | 
 | static int xleafwild = 0;               /* Last segment of pattern is wild */ | 
 | static int xpatabsolute = 0; | 
 |  | 
 | #ifdef aegis | 
 | static char bslash; | 
 | #endif /* aegis */ | 
 |  | 
 |  | 
 | /*  S P L I T P A T H  */ | 
 |  | 
 | /* | 
 |   Splits the slash-separated portions of the argument string into | 
 |   a list of path structures.  Returns the head of the list.  The | 
 |   structures are allocated by malloc, so they must be freed. | 
 |   Splitpath is used internally by the filename generator. | 
 |  | 
 |   Input: | 
 |     A path string. | 
 |  | 
 |   Returns: | 
 |     A linked list of the slash-separated segments of the input. | 
 | */ | 
 | static struct path * | 
 | splitpath(p) char *p; { | 
 |     struct path *head,*cur,*prv; | 
 |     int i; | 
 |  | 
 |     debug(F111,"splitpath",p,xrecursive); | 
 |     head = prv = NULL; | 
 |  | 
 |     if (!p) return(NULL); | 
 |     if (!*p) return(NULL); | 
 |  | 
 |     if (!strcmp(p,"**")) {              /* Fix this */ | 
 |         p = "*"; | 
 |     } | 
 |     if (ISDIRSEP(*p)) p++;              /* Skip leading slash if any */ | 
 |  | 
 |     /* Make linked list of path segments from pattern */ | 
 |  | 
 |     while (*p) { | 
 |         cur = (struct path *) malloc(sizeof (struct path)); | 
 |         debug(F101,"splitpath malloc","",cur); | 
 |         if (cur == NULL) { | 
 |             debug(F100,"splitpath malloc failure","",0); | 
 |             prv -> fwd = NULL; | 
 |             return((struct path *)NULL); | 
 |         } | 
 |         cur -> fwd = NULL; | 
 |         if (head == NULL)               /* First, make list head */ | 
 |           head = cur; | 
 |         else                            /* Not first, link into chain */ | 
 |           prv -> fwd = cur; | 
 |         prv = cur;                      /* Link from previous to this one */ | 
 |  | 
 | #ifdef aegis | 
 |         /* treat backslash as "../" */ | 
 |         if (bslash && *p == bslash) { | 
 |             strcpy(cur->npart, "..");	/* safe */ | 
 |             ++p; | 
 |         } else { | 
 |             for (i=0; i < MAXNAMLEN && *p && *p != '/' && *p != bslash; i++) | 
 |               cur -> npart[i] = *p++; | 
 |             cur -> npart[i] = '\0';     /* end this segment */ | 
 |             if (i >= MAXNAMLEN) | 
 |               while (*p && *p != '/' && *p != bslash) | 
 |                 p++; | 
 |         } | 
 |         if (*p == '/') p++; | 
 | #else | 
 |         /* General case (UNIX) */ | 
 |         for (i = 0; i < MAXNAMLEN && !ISDIRSEP(*p) && *p != '\0'; i++) { | 
 |             cur -> npart[i] = *p++; | 
 |         } | 
 |  | 
 |         cur -> npart[i] = '\0';         /* End this path segment */ | 
 |         if (i >= MAXNAMLEN) | 
 |           while (!ISDIRSEP(*p) && *p != '\0') p++; | 
 |         if (ISDIRSEP(*p)) | 
 |           p++; | 
 |  | 
 | #endif /* aegis */ | 
 |     } | 
 |     if (prv) { | 
 |         makestr(&xpatlast,prv -> npart); | 
 |         debug(F110,"splitpath xpatlast",xpatlast,0); | 
 |     } | 
 | #ifdef DEBUG | 
 |     /* Show original path list */ | 
 |     if (deblog) { | 
 |         for (i = 0, cur = head; cur; i++) { | 
 |             debug(F111,"SPLITPATH",cur -> npart, i); | 
 |             cur = cur -> fwd; | 
 |         } | 
 |     } | 
 | #endif /* DEBUG */ | 
 |     return(head); | 
 | } | 
 |  | 
 | /*  F G E N  --  Generate File List  */ | 
 |  | 
 | /* | 
 |   File name generator.  It is passed a string, possibly containing wildcards, | 
 |   and an array of character pointers.  It finds all the matching filenames and | 
 |   stores pointers to them in the array.  The returned strings are allocated | 
 |   from a static buffer local to this module (so the caller doesn't have to | 
 |   worry about deallocating them); this means that successive calls to fgen | 
 |   will wipe out the results of previous calls. | 
 |  | 
 |   Input: | 
 |     A wildcard string, an array to write names to, the length of the array. | 
 |  | 
 |   Returns: | 
 |     The number of matches. | 
 |     The array is filled with filenames that matched the pattern. | 
 |     If there wasn't enough room in the array, -1 is returned. | 
 |  | 
 |   Originally by: Jeff Damens, CUCCA, 1984.  Many changes since then. | 
 | */ | 
 | static int | 
 | fgen(pat,resarry,len) char *pat,*resarry[]; int len; { | 
 |     struct path *head; | 
 |     char *sptr, *s; | 
 |     int n; | 
 |  | 
 | #ifdef aegis | 
 |     char *namechars; | 
 |     int tilde = 0, bquote = 0; | 
 |  | 
 |     if ((namechars = getenv("NAMECHARS")) != NULL) { | 
 |         if (ckstrchr(namechars, '~' ) != NULL) tilde  = '~'; | 
 |         if (ckstrchr(namechars, '\\') != NULL) bslash = '\\'; | 
 |         if (ckstrchr(namechars, '`' ) != NULL) bquote = '`'; | 
 |     } else { | 
 |         tilde = '~'; bslash = '\\'; bquote = '`'; | 
 |     } | 
 |     sptr = scratch; | 
 |  | 
 |     /* copy "`node_data", etc. anchors */ | 
 |     if (bquote && *pat == bquote) | 
 |       while (*pat && *pat != '/' && *pat != bslash) | 
 |         *sptr++ = *pat++; | 
 |     else if (tilde && *pat == tilde) | 
 |       *sptr++ = *pat++; | 
 |     while (*pat == '/') | 
 |       *sptr++ = *pat++; | 
 |     if (sptr == scratch) { | 
 |         strcpy(scratch,"./");		/* safe */ | 
 |         sptr = scratch+2; | 
 |     } | 
 |     if (!(head = splitpath(pat))) return(-1); | 
 |  | 
 | #else /* not aegis */ | 
 |  | 
 |     debug(F111,"fgen pat",pat,len); | 
 |     debug(F110,"fgen current directory",zgtdir(),0); | 
 |     debug(F101,"fgen stathack","",stathack); | 
 |  | 
 |     scratch[0] = '\0'; | 
 |     xpatwild = 0; | 
 |     xleafwild = 0; | 
 |     xpatabsolute = 0; | 
 |  | 
 |     if (!(head = splitpath(pat)))       /* Make the path segment list */ | 
 | 	return(-1); | 
 |  | 
 |     sptr = scratch; | 
 |  | 
 | #ifdef COMMENT | 
 |     if (strncmp(pat,"./",2) && strncmp(pat,"../",3)) { | 
 | #endif /* COMMENT */ | 
 | 	if (!ISDIRSEP(*pat))		/* If name is not absolute */ | 
 | 	  *sptr++ = '.';		/* put "./" in front. */ | 
 | 	*sptr++ = DIRSEP; | 
 | #ifdef COMMENT | 
 |     } | 
 | #endif /* COMMENT */ | 
 |     *sptr = '\0'; | 
 | #endif /* aegis */ | 
 |  | 
 |     makestr(&xpat,pat);                 /* Save copy of original pattern */ | 
 |     debug(F110,"fgen scratch",scratch,0); | 
 |  | 
 |     for (n = 0, s = xpat; *s; s++)      /* How many slashes in the pattern */ | 
 |       if (*s == DIRSEP)                 /* since these are fences for */ | 
 |         n++;                            /* pattern matching */ | 
 |     xpatslash = n; | 
 |     debug(F101,"fgen xpatslash","",xpatslash); | 
 |  | 
 |     numfnd = 0;                         /* None found yet */ | 
 |  | 
 |     if (initspace(resarry,ssplen) < 0) | 
 |       return(-1); | 
 |  | 
 |     xpatwild = iswild(xpat);		/* Original pattern is wild? */ | 
 |     xpatabsolute = isabsolute(xpat); | 
 |     xleafwild = iswild(xpatlast); | 
 |  | 
 |     debug(F111,"fgen xpat",xpat,xpatwild); | 
 |     debug(F111,"fgen xpatlast",xpatlast,xleafwild); | 
 |     debug(F101,"fgen xpatabsolute","",xpatabsolute); | 
 |  | 
 |     traverse(head,scratch,sptr);        /* Go walk the directory tree. */ | 
 |     while (head != NULL) {              /* Done - free path segment list. */ | 
 |         struct path *next = head -> fwd; | 
 |         free((char *)head); | 
 |         head = next; | 
 |     } | 
 |     debug(F101,"fgen","",numfnd); | 
 |     return(numfnd);                     /* Return the number of matches */ | 
 | } | 
 |  | 
 | /* Define LONGFN (long file names) automatically for BSD 2.9 and 4.2 */ | 
 | /* LONGFN can also be defined on the cc command line. */ | 
 |  | 
 | #ifdef BSD29 | 
 | #ifndef LONGFN | 
 | #define LONGFN | 
 | #endif | 
 | #endif | 
 |  | 
 | #ifdef BSD42 | 
 | #ifndef LONGFN | 
 | #define LONGFN | 
 | #endif | 
 | #endif | 
 |  | 
 | /* | 
 |    T R A V E R S E  --  Traverse a directory tree. | 
 |  | 
 |    Walks the directory tree looking for matches to its arguments. | 
 |    The algorithm is, briefly: | 
 |  | 
 |     If the current pattern segment contains no wildcards, that | 
 |     segment is added to what we already have.  If the name so far | 
 |     exists, we call ourselves recursively with the next segment | 
 |     in the pattern string; otherwise, we just return. | 
 |  | 
 |     If the current pattern segment contains wildcards, we open the name | 
 |     we've accumulated so far (assuming it is really a directory), then read | 
 |     each filename in it, and, if it matches the wildcard pattern segment, add | 
 |     that filename to what we have so far and call ourselves recursively on | 
 |     the next segment. | 
 |  | 
 |     Finally, when no more pattern segments remain, we add what's accumulated | 
 |     so far to the result array and increment the number of matches. | 
 |  | 
 |   Inputs: | 
 |     A pattern path list (as generated by splitpath), a string pointer that | 
 |     points to what we've traversed so far (this can be initialized to "/" | 
 |     to start the search at the root directory, or to "./" to start the | 
 |     search at the current directory), and a string pointer to the end of | 
 |     the string in the previous argument, plus the global "recursive", | 
 |     "xmatchdot", and "xdironly" flags. | 
 |  | 
 |   Returns: void, with: | 
 |     mtchs[] containing the array of filename string pointers, and: | 
 |     numfnd containing the number of filenames. | 
 |  | 
 |   Although it might be poor practice, the mtchs[] array is revealed to the | 
 |   outside in case it needs it; for example, to be sorted prior to use. | 
 |   (It is poor practice because not all platforms implement file lists the | 
 |   same way; some don't use an array at all.) | 
 |  | 
 |   Note that addresult() acts as a second-level filter; due to selection | 
 |   criteria outside of the pattern, it might decline to add files that | 
 |   this routine asks it to, e.g. because we are collecting only directory | 
 |   names but not the names of regular files. | 
 |  | 
 |   WARNING: In the course of C-Kermit 7.0 development, this routine became | 
 |   ridiculously complex, in order to meet approximately sixty specific | 
 |   requirements.  DON'T EVEN THINK ABOUT MODIFYING THIS ROUTINE!  Trust me; | 
 |   it is not possible to fix anything in it without breaking something else. | 
 |   This routine badly needs a total redesign and rewrite.  Note: There may | 
 |   be some good applications for realpath() and/or scandir() and/or fts_blah() | 
 |   here, on platforms where they are available. | 
 | */ | 
 | static VOID | 
 | traverse(pl,sofar,endcur) struct path *pl; char *sofar, *endcur; { | 
 |  | 
 | /* Appropriate declarations for directory routines and structures */ | 
 | /* #define OPENDIR means to use opendir(), readdir(), closedir()  */ | 
 | /* If OPENDIR not defined, we use open(), read(), close() */ | 
 |  | 
 | #ifdef DIRENT                           /* New way, <dirent.h> */ | 
 | #define OPENDIR | 
 |     DIR *fd, *opendir(); | 
 |     struct dirent *dirbuf; | 
 |     struct dirent *readdir(); | 
 | #else /* !DIRENT */ | 
 | #ifdef LONGFN                           /* Old way, <dir.h> with opendir() */ | 
 | #define OPENDIR | 
 |     DIR *fd, *opendir(); | 
 |     struct direct *dirbuf; | 
 | #else /* !LONGFN */ | 
 |     int fd;                             /* Old way, <dir.h> with open() */ | 
 |     struct direct dir_entry; | 
 |     struct direct *dirbuf = &dir_entry; | 
 | #endif /* LONGFN */ | 
 | #endif /* DIRENT */ | 
 |     int mopts = 0;			/* ckmatch() opts */ | 
 |     int depth = 0;			/* Directory tree depth */ | 
 |  | 
 |     char nambuf[MAXNAMLEN+4];           /* Buffer for a filename */ | 
 |     int itsadir = 0, segisdir = 0, itswild = 0, mresult, n, x /* , y */ ; | 
 |     struct stat statbuf;                /* For file info. */ | 
 |  | 
 |     debug(F101,"STAT","",16); | 
 |     if (pl == NULL) {                   /* End of path-segment list */ | 
 |         *--endcur = '\0'; /* Terminate string, overwrite trailing slash */ | 
 |         debug(F110,"traverse add: end of path segment",sofar,0); | 
 |         addresult(sofar,-1); | 
 |         return; | 
 |     } | 
 |     if (stathack) { | 
 | 	/* This speeds up the search a lot and we still get good results */ | 
 | 	/* but it breaks the tagging of directory names done in addresult */ | 
 | 	if (xrecursive || xfilonly || xdironly || xpatslash) { | 
 | 	    itsadir = xisdir(sofar); | 
 | 	    debug(F101,"STAT","",17); | 
 | 	} else | 
 | 	  itsadir = (strncmp(sofar,"./",2) == 0); | 
 |     } else { | 
 | 	itsadir = xisdir(sofar); | 
 | 	debug(F101,"STAT","",18); | 
 |     } | 
 |     debug(F111,"traverse entry sofar",sofar,itsadir); | 
 |  | 
 | #ifdef CKSYMLINK                        /* We're doing symlinks? */ | 
 | #ifdef USE_LSTAT                        /* OK to use lstat()? */ | 
 |     if (itsadir && xnolinks) {		/* If not following symlinks */ | 
 | 	int x; | 
 | 	struct stat buf; | 
 | 	x = lstat(sofar,&buf); | 
 | 	debug(F111,"traverse lstat 1",sofar,x); | 
 | 	if (x > -1 && | 
 | #ifdef S_ISLNK | 
 | 	    S_ISLNK(buf.st_mode) | 
 | #else | 
 | #ifdef _IFLNK | 
 | 	    ((_IFMT & buf.st_mode) == _IFLNK) | 
 | #endif /* _IFLNK */ | 
 | #endif /* S_ISLNK */ | 
 | 	    ) | 
 | 	  itsadir = 0; | 
 |     } | 
 | #endif /* USE_LSTAT */ | 
 | #endif /* CKSYMLINK */ | 
 |  | 
 |     if (!xmatchdot && xpatlast[0] == '.') | 
 |       xmatchdot = 1; | 
 |     if (!xmatchdot && xpat[0] == '.' && xpat[1] != '/' && xpat[1] != '.') | 
 |       xmatchdot = 1; | 
 |  | 
 |     /* ckmatch() options */ | 
 |  | 
 |     if (xmatchdot)   mopts |= 1;	/* Match dot */ | 
 |     if (!xrecursive) mopts |= 2;	/* Dirsep is fence */ | 
 |  | 
 |     debug(F111,"traverse entry xpat",xpat,xpatslash); | 
 |     debug(F111,"traverse entry xpatlast",xpatlast,xmatchdot); | 
 |     debug(F110,"traverse entry pl -> npart",pl -> npart,0); | 
 |  | 
 | #ifdef RECURSIVE | 
 |     if (xrecursive > 0 && !itsadir) { | 
 |         char * s;         /* Recursive descent and this is a regular file */ | 
 |         *--endcur = '\0'; /* Terminate string, overwrite trailing slash */ | 
 |  | 
 |         /* Find the nth slash from the right and match from there... */ | 
 |         /* (n == the number of slashes in the original pattern - see fgen) */ | 
 |         if (*sofar == '/') { | 
 |             debug(F110,"traverse xpatslash absolute",sofar,0); | 
 |             s = sofar; | 
 |         } else { | 
 |             debug(F111,"traverse xpatslash relative",sofar,xpatslash); | 
 |             for (s = endcur - 1, n = 0; s >= sofar; s--) { | 
 |                 if (*s == '/') { | 
 |                     if (++n >= xpatslash) { | 
 |                         s++; | 
 |                         break; | 
 |                     } | 
 |                 } | 
 |             } | 
 |         } | 
 | #ifndef NOSKIPMATCH | 
 | 	/* This speeds things up a bit. */ | 
 | 	/* If it causes trouble define NOSKIPMATCH and rebuild. */ | 
 | 	if (xpat[0] == '*' && !xpat[1]) | 
 | 	  x = xmatchdot ? 1 : (s[0] != '.'); | 
 | 	else | 
 | #endif /* NOSKIPMATCH */ | 
 | 	  x = ckmatch(xpat, s, 1, mopts); /* Match with original pattern */ | 
 |         debug(F111,"traverse xpatslash ckmatch",s,x); | 
 |         if (x > 0) { | 
 |             debug(F110,"traverse add: recursive, match, && !isdir",sofar,0); | 
 |             addresult(sofar,itsadir); | 
 |         } | 
 |         return; | 
 |     } | 
 | #endif /* RECURSIVE */ | 
 |  | 
 |     debug(F111,"traverse sofar 2",sofar,0); | 
 |  | 
 |     segisdir = ((pl -> fwd) == NULL) ? 0 : 1; | 
 |     itswild = iswild(pl -> npart); | 
 |  | 
 |     debug(F111,"traverse segisdir",sofar,segisdir); | 
 |     debug(F111,"traverse itswild ",pl -> npart,itswild); | 
 |  | 
 | #ifdef RECURSIVE | 
 |     if (xrecursive > 0) {               /* If recursing and... */ | 
 |         if (segisdir && itswild)        /* this is a dir and npart is wild */ | 
 |           goto blah;                    /* or... */ | 
 |         else if (!xpatabsolute && !xpatwild) /* search object is nonwild */ | 
 |           goto blah;                    /* then go recurse */ | 
 |     } | 
 | #endif /* RECURSIVE */ | 
 |  | 
 |     if (!itswild) {                     /* This path segment not wild? */ | 
 | #ifdef COMMENT | 
 |         strcpy(endcur,pl -> npart);     /* (safe) Append next part. */ | 
 |         endcur += (int)strlen(pl -> npart); /* Advance end pointer */ | 
 | #else | 
 | /* | 
 |   strcpy() does not account for quoted metacharacters. | 
 |   We must remove the quotes before doing the stat(). | 
 | */ | 
 | 	{ | 
 | 	    int quote = 0; | 
 | 	    char c, * s; | 
 | 	    s = pl -> npart; | 
 | 	    while ((c = *s++)) { | 
 | 		if (!quote) { | 
 | 		    if (c == CMDQ) { | 
 | 			quote = 1; | 
 | 			continue; | 
 | 		    } | 
 | 		} | 
 | 		*endcur++ = c; | 
 | 		quote = 0; | 
 | 	    } | 
 | 	} | 
 | #endif /* COMMENT */ | 
 |         *endcur = '\0';                 /* End new current string. */ | 
 |  | 
 |         if (stat(sofar,&statbuf) == 0) { /* If this piece exists... */ | 
 |             debug(F110,"traverse exists",sofar,0); | 
 |             *endcur++ = DIRSEP;         /* add slash to end */ | 
 |             *endcur = '\0';             /* and end the string again. */ | 
 |             traverse(pl -> fwd, sofar, endcur); | 
 |         } | 
 | #ifdef DEBUG | 
 |         else debug(F110,"traverse not found", sofar, 0); | 
 | #endif /* DEBUG */ | 
 |         return; | 
 |     } | 
 |  | 
 |     *endcur = '\0';                     /* End current string */ | 
 |     debug(F111,"traverse sofar 3",sofar,0); | 
 |  | 
 |     if (!itsadir) | 
 |       return; | 
 |  | 
 |     /* Search is recursive or ... */ | 
 |     /* path segment contains wildcards, have to open and search directory. */ | 
 |  | 
 |   blah: | 
 |  | 
 |     debug(F110,"traverse opening directory", sofar, 0); | 
 |  | 
 | #ifdef OPENDIR | 
 |     debug(F110,"traverse opendir()",sofar,0); | 
 |     if ((fd = opendir(sofar)) == NULL) {        /* Can't open, fail. */ | 
 |         debug(F101,"traverse opendir() failed","",errno); | 
 |         return; | 
 |     } | 
 |     while ((dirbuf = readdir(fd))) | 
 | #else /* !OPENDIR */ | 
 |     debug(F110,"traverse directory open()",sofar,0); | 
 |     if ((fd = open(sofar,O_RDONLY)) < 0) { | 
 |         debug(F101,"traverse directory open() failed","",errno); | 
 |         return; | 
 |     } | 
 |     while (read(fd, (char *)dirbuf, sizeof dir_entry)) | 
 | #endif /* OPENDIR */ | 
 |       {                         /* Read each entry in this directory */ | 
 |           int exists; | 
 |           char *eos, *s; | 
 |           exists = 0; | 
 |  | 
 |           /* On some platforms, the read[dir]() can return deleted files, */ | 
 |           /* e.g. HP-UX 5.00.  There is no point in grinding through this */ | 
 |           /* routine when the file doesn't exist... */ | 
 |  | 
 |           if (          /* There  actually is an inode... */ | 
 | #ifdef BSD42 | 
 |                          dirbuf->d_ino != -1 | 
 | #else | 
 | #ifdef unos | 
 |                          dirbuf->d_ino != -1 | 
 | #else | 
 | #ifdef QNX | 
 |                          dirbuf->d_stat.st_ino != 0 | 
 | #else | 
 | #ifdef SOLARIS | 
 |                          dirbuf->d_ino != 0 | 
 | #else | 
 | #ifdef sun | 
 |                          dirbuf->d_fileno != 0 | 
 | #else | 
 | #ifdef bsdi | 
 |                          dirbuf->d_fileno != 0 | 
 | #else | 
 | #ifdef __386BSD__ | 
 |                          dirbuf->d_fileno != 0 | 
 | #else | 
 | #ifdef __FreeBSD__ | 
 |                          dirbuf->d_fileno != 0 | 
 | #else | 
 | #ifdef ultrix | 
 |                          dirbuf->gd_ino != 0 | 
 | #else | 
 | #ifdef Plan9 | 
 |                          1 | 
 | #else | 
 |                          dirbuf->d_ino != 0 | 
 | #endif /* Plan9 */ | 
 | #endif /* ultrix */ | 
 | #endif /* __FreeBSD__ */ | 
 | #endif /* __386BSD__ */ | 
 | #endif /* bsdi */ | 
 | #endif /* sun */ | 
 | #endif /* SOLARIS */ | 
 | #endif /* QNX */ | 
 | #endif /* unos */ | 
 | #endif /* BSD42 */ | 
 |               ) | 
 |             exists = 1; | 
 |           if (!exists) | 
 |             continue; | 
 |  | 
 |           ckstrncpy(nambuf,             /* Copy the name */ | 
 |                   dirbuf->d_name, | 
 |                   MAXNAMLEN | 
 |                   ); | 
 |           if (nambuf[0] == '.') { | 
 |               if (!nambuf[1] || (nambuf[1] == '.' && !nambuf[2])) { | 
 |                   debug(F110,"traverse skipping",nambuf,0); | 
 |                   continue;             /* skip "." and ".." */ | 
 |               } | 
 |           } | 
 |           s = nambuf;                   /* Copy name to end of sofar */ | 
 |           eos = endcur; | 
 |           while ((*eos = *s)) { | 
 |               s++; | 
 |               eos++; | 
 |           } | 
 | /* | 
 |   Now we check the file for (a) whether it is a directory, and (b) whether | 
 |   its name matches our pattern.  If it is a directory, and if we have been | 
 |   told to build a recursive list, then we must descend regardless of whether | 
 |   it matches the pattern.  If it is not a directory and it does not match | 
 |   our pattern, we skip it.  Note: sofar is the full pathname, nambuf is | 
 |   the name only. | 
 | */ | 
 |           /* Do this first to save pointless function calls */ | 
 |           if (nambuf[0] == '.' && !xmatchdot) /* Dir name starts with '.' */ | 
 |             continue; | 
 | 	  if (stathack) { | 
 | 	      if (xrecursive || xfilonly || xdironly || xpatslash) { | 
 | 		  itsadir = xisdir(sofar); /* See if it's a directory */ | 
 | 		  debug(F101,"STAT","",19); | 
 | 	      } else { | 
 | 		  itsadir = 0; | 
 | 	      } | 
 | 	  } else { | 
 | 	      itsadir = xisdir(sofar); | 
 | 	      debug(F101,"STAT","",20); | 
 | 	  } | 
 |  | 
 | #ifdef CKSYMLINK | 
 | #ifdef USE_LSTAT | 
 | 	  if (itsadir && xnolinks) {		/* If not following symlinks */ | 
 | 	      int x; | 
 | 	      struct stat buf; | 
 | 	      x = lstat(sofar,&buf); | 
 | 	      debug(F111,"traverse lstat 2",sofar,x); | 
 | 	      if (x > -1 && | 
 | #ifdef S_ISLNK | 
 | 		  S_ISLNK(buf.st_mode) | 
 | #else | 
 | #ifdef _IFLNK | 
 | 		  ((_IFMT & buf.st_mode) == _IFLNK) | 
 | #endif /* _IFLNK */ | 
 | #endif /* S_ISLNK */ | 
 | 		  ) | 
 | 		itsadir = 0; | 
 | 	  } | 
 | #endif /* USE_LSTAT */ | 
 | #endif /* CKSYMLINK */ | 
 |  | 
 | #ifdef RECURSIVE | 
 |           if (xrecursive > 0 && itsadir && | 
 |               (xpatlast[0] == '*') && !xpatlast[1] | 
 |               ) { | 
 |               debug(F110, | 
 |                     "traverse add: recursive && isdir && segisdir or match", | 
 |                     sofar, | 
 |                     segisdir | 
 |                     ); | 
 | 	      addresult(sofar,itsadir); | 
 | 	      if (numfnd < 0) return; | 
 |           } | 
 | #endif /* RECURSIVE */ | 
 |  | 
 |           debug(F111,"traverse mresult xpat",xpat,xrecursive); | 
 |           debug(F111,"traverse mresult pl -> npart", | 
 |                 pl -> npart, | 
 |                 ((pl -> fwd) ? 9999 : 0) | 
 |                 ); | 
 |           debug(F111,"traverse mresult sofar segisdir",sofar,segisdir); | 
 |           debug(F111,"traverse mresult sofar itsadir",sofar,itsadir); | 
 |           debug(F101,"traverse mresult xmatchdot","",xmatchdot); | 
 | /* | 
 |   Match the path so far with the pattern after stripping any leading "./" | 
 |   from either or both.  The pattern chosen is the full original pattern if | 
 |   the match candidate (sofar) is not a directory, or else just the name part | 
 |   (pl->npart) if it is. | 
 | */ | 
 | 	  { | 
 | 	      char * s1;		/* The pattern */ | 
 | 	      char * s2 = sofar;	/* The path so far */ | 
 | 	      char * s3;		/* Worker */ | 
 | 	      int opts;			/* Match options */ | 
 |  | 
 | 	      s1 = itsadir ? pl->npart : xpat; | 
 |  | 
 | #ifndef COMMENT | 
 | 	      /* I can't explain this but it unbreaks "cd blah/sub<Esc>" */ | 
 | 	      if (itsadir && !xrecursive && xpatslash > 0 && | 
 | 		  segisdir == 0 && itswild) { | 
 | 		  s1 = xpat; | 
 | 		  debug(F110,"traverse mresult s1 kludge",s1,0); | 
 | 	      } | 
 | #endif /* COMMENT */ | 
 |  | 
 | 	      if (xrecursive && xpatslash == 0) | 
 | 		s2 = nambuf; | 
 | 	      while ((s1[0] == '.') && (s1[1] == '/')) /* Strip "./" */ | 
 | 		s1 += 2; | 
 | 	      while ((s2[0] == '.') && (s2[1] == '/')) /* Ditto */ | 
 | 		s2 += 2; | 
 | 	      opts = mopts;		/* Match options */ | 
 | 	      if (itsadir) 		/* Current segment is a directory */ | 
 | 		opts = mopts & 1;	/* No fences */ | 
 | 	      s3 = s2;			/* Get segment depth */ | 
 | 	      depth = 0; | 
 | 	      while (*s3) { if (*s3++ == '/') depth++; } | 
 | #ifndef NOSKIPMATCH | 
 | 	      /* This speeds things up a bit. */ | 
 | 	      /* If it causes trouble define NOSKIPMATCH and rebuild. */ | 
 | 	      if (depth == 0 && (s1[0] == '*') && !s1[1]) | 
 | 		mresult = xmatchdot ? 1 : (s2[0] != '.'); | 
 | 	      else | 
 | #endif /* NOSKIPMATCH */ | 
 | 		mresult = ckmatch(s1,s2,1,opts); /* Match */ | 
 | 	  } | 
 | #ifdef DEBUG | 
 | 	  if (deblog) { | 
 | 	      debug(F111,"traverse mresult depth",sofar,depth); | 
 | 	      debug(F101,"traverse mresult xpatslash","",xpatslash); | 
 | 	      debug(F111,"traverse mresult nambuf",nambuf,mresult); | 
 | 	      debug(F111,"traverse mresult itswild",pl -> npart,itswild); | 
 | 	      debug(F111,"traverse mresult segisdir",pl -> npart,segisdir); | 
 | 	  } | 
 | #endif /* DEBUG */ | 
 |           if (mresult ||		/* If match succeeded */ | 
 | 	      xrecursive ||		/* Or search is recursive */ | 
 | 	      depth < xpatslash		/* Or not deep enough to match... */ | 
 | 	      ) { | 
 |               if (                      /* If it's not a directory... */ | 
 | /* | 
 |   The problem here is that segisdir is apparently not set appropriately. | 
 |   If I leave in the !segisdir test, then "dir /recursive blah" (where blah is | 
 |   a directory name) misses some regular files because sometimes segisdir | 
 |   is set and sometimes it's not.  But if I comment it out, then | 
 |   "dir <star>/<star>.txt lists every file in * and does not even open up the | 
 |   subdirectories.  However, "dir /rec <star>/<star>.txt" works right. | 
 | */ | 
 | #ifdef COMMENT | 
 |                   mresult && (!itsadir && !segisdir) | 
 | #else | 
 |                   mresult &&		/* Matched */ | 
 |                   !itsadir &&		/* sofar is not a directory */ | 
 |                   ((!xrecursive && !segisdir) || xrecursive) | 
 | #endif /* COMMENT */ | 
 |                   ) { | 
 | 		  debug(F110, | 
 | 			"traverse add: match && !itsadir",sofar,0); | 
 | 		  addresult(sofar,itsadir); | 
 | 		  if (numfnd < 0) return; | 
 |               } else if (itsadir && (xrecursive || mresult)) { | 
 |                   struct path * xx = NULL; | 
 |                   *eos++ = DIRSEP;      /* Add directory separator */ | 
 |                   *eos = '\0';          /* to end of segment */ | 
 | #ifdef RECURSIVE | 
 |                   /* Copy previous pattern segment to this new directory */ | 
 |  | 
 |                   if (xrecursive > 0 && !(pl -> fwd)) { | 
 |                       xx = (struct path *) malloc(sizeof (struct path)); | 
 |                       pl -> fwd = xx; | 
 |                       if (xx) { | 
 |                           xx -> fwd = NULL; | 
 |                           strcpy(xx -> npart, pl -> npart); /* safe */ | 
 |                       } | 
 |                   } | 
 | #endif /* RECURSIVE */ | 
 |                   traverse(pl -> fwd, sofar, eos); /* Traverse new directory */ | 
 |               } | 
 |           } | 
 |       } | 
 | #ifdef OPENDIR | 
 |     closedir(fd); | 
 | #else /* !OPENDIR */ | 
 |     close(fd); | 
 | #endif /* OPENDIR */ | 
 | } | 
 |  | 
 | /* | 
 |  * addresult: | 
 |  *  Adds a result string to the result array.  Increments the number | 
 |  *  of matches found, copies the found string into our string | 
 |  *  buffer, and puts a pointer to the buffer into the caller's result | 
 |  *  array.  Our free buffer pointer is updated.  If there is no | 
 |  *  more room in the caller's array, the number of matches is set to -1. | 
 |  * Input: a result string. | 
 |  * Returns: nothing. | 
 |  */ | 
 | static VOID | 
 | addresult(str,itsadir) char *str; int itsadir; { | 
 |     int len; | 
 |  | 
 |     if (!freeptr) { | 
 | 	debug(F100,"addresult string space not init'd","",0); | 
 | 	initspace(mtchs,ssplen); | 
 |     } | 
 |     if (!str) str = ""; | 
 |     debug(F111,"addresult",str,itsadir); | 
 |     if (!*str) | 
 |       return; | 
 |  | 
 |     if (itsadir < 0) { | 
 | 	itsadir = xisdir(str); | 
 |     } | 
 |     if ((xdironly && !itsadir) || (xfilonly && itsadir)) { | 
 |         debug(F111,"addresult skip",str,itsadir); | 
 |         return; | 
 |     } | 
 |     while (str[0] == '.' && ISDIRSEP(str[1])) /* Strip all "./" from front */ | 
 |       str += 2; | 
 |     if (--remlen < 0) {                 /* Elements left in array of names */ | 
 |         debug(F111,"addresult ARRAY FULL",str,numfnd); | 
 |         numfnd = -1; | 
 |         return; | 
 |     } | 
 |     len = (int)strlen(str);		/* Space this will use */ | 
 |     debug(F111,"addresult len",str,len); | 
 |  | 
 |     if (len < 1) | 
 |       return; | 
 |  | 
 |     if ((freeptr + len + itsadir + 1) > (sspace + ssplen)) { | 
 |         debug(F111,"addresult OUT OF SPACE",str,numfnd); | 
 | #ifdef DYNAMIC | 
 | 	printf( | 
 | "?String space %d exhausted - use SET FILE STRINGSPACE to increase\n",ssplen); | 
 | #else | 
 | 	printf("?String space %d exhausted\n",ssplen); | 
 | #endif /* DYNAMIC */ | 
 |         numfnd = -1;                    /* Do not record if not enough space */ | 
 |         return; | 
 |     } | 
 |     strcpy(freeptr,str);		/* safe */ | 
 |  | 
 |     /* Tag directory names by putting '/' at the end */ | 
 |  | 
 |     if (itsadir && (freeptr[len-1] == '/')) { | 
 |         freeptr[len++] = DIRSEP; | 
 |         freeptr[len] = '\0'; | 
 |     } | 
 |     if (numfnd >= maxnames) { | 
 | #ifdef DYNAMIC | 
 | 	printf( | 
 | "?Too many files (%d max) - use SET FILE LISTSIZE to increase\n",maxnames); | 
 | #else | 
 | 	printf("?Too many files - %d max\n",maxnames); | 
 | #endif /* DYNAMIC */ | 
 |         numfnd = -1; | 
 |         return; | 
 |     } | 
 |     str = freeptr; | 
 |     *resptr++ = freeptr; | 
 |     freeptr += (len + 1); | 
 |     numfnd++; | 
 |     debug(F111,"addresult ADD",str,numfnd); | 
 | } | 
 |  | 
 | #ifdef COMMENT | 
 | /* | 
 |  * match(pattern,string): | 
 |  *  pattern matcher.  Takes a string and a pattern possibly containing | 
 |  *  the wildcard characters '*' and '?'.  Returns true if the pattern | 
 |  *  matches the string, false otherwise. | 
 |  * Orignally by: Jeff Damens, CUCCA, 1984 | 
 |  * No longer used as of C-Kermit 7.0, now we use ckmatch() instead (ckclib.c). | 
 |  * | 
 |  * Input: a string and a wildcard pattern. | 
 |  * Returns: 1 if match, 0 if no match. | 
 |  */ | 
 | static int | 
 | match(pattern, string) char *pattern, *string; { | 
 |     char *psave = NULL, *ssave = NULL;  /* Backup pointers for failure */ | 
 |     int q = 0;                          /* Quote flag */ | 
 |  | 
 |     if (*string == '.' && *pattern != '.' && !xmatchdot) { | 
 |         debug(F110,"match skip",string,0); | 
 |         return(0); | 
 |     } | 
 |     while (1) { | 
 |         for (; *pattern == *string; pattern++,string++) /* Skip first */ | 
 |           if (*string == '\0') return(1); /* End of strings, succeed */ | 
 |  | 
 |         if (*pattern == '\\' && q == 0) { /* Watch out for quoted */ | 
 |             q = 1;                      /* metacharacters */ | 
 |             pattern++;                  /* advance past quote */ | 
 |             if (*pattern != *string) return(0); | 
 |             continue; | 
 |         } else q = 0; | 
 |  | 
 |         if (q) { | 
 |             return(0); | 
 |         } else { | 
 |             if (*string != '\0' && *pattern == '?') { | 
 |                 pattern++;              /* '?', let it match */ | 
 |                 string++; | 
 |             } else if (*pattern == '*') { /* '*' ... */ | 
 |                 psave = ++pattern;      /* remember where we saw it */ | 
 |                 ssave = string;         /* let it match 0 chars */ | 
 |             } else if (ssave != NULL && *ssave != '\0') { /* if not at end  */ | 
 |                                         /* ...have seen a star */ | 
 |                 string = ++ssave;       /* skip 1 char from string */ | 
 |                 pattern = psave;        /* and back up pattern */ | 
 |             } else return(0);           /* otherwise just fail */ | 
 |         } | 
 |     } | 
 | } | 
 | #endif /* COMMENT */ | 
 |  | 
 | /* | 
 |   The following two functions are for expanding tilde in filenames | 
 |   Contributed by Howie Kaye, CUCCA, developed for CCMD package. | 
 | */ | 
 |  | 
 | /*  W H O A M I  --  Get user's username.  */ | 
 |  | 
 | /* | 
 |   1) Get real uid | 
 |   2) See if the $USER environment variable is set ($LOGNAME on AT&T) | 
 |   3) If $USER's uid is the same as ruid, realname is $USER | 
 |   4) Otherwise get logged in user's name | 
 |   5) If that name has the same uid as the real uid realname is loginname | 
 |   6) Otherwise, get a name for ruid from /etc/passwd | 
 | */ | 
 | char * | 
 | whoami() { | 
 | #ifdef DTILDE | 
 | #ifdef pdp11 | 
 | #define WHOLEN 100 | 
 | #else | 
 | #define WHOLEN 257 | 
 | #endif /* pdp11 */ | 
 |     static char realname[UIDBUFLEN+1];  /* user's name */ | 
 |     static int ruid = -1;               /* user's real uid */ | 
 |     char loginname[UIDBUFLEN+1], envname[256]; /* temp storage */ | 
 |     char *c; | 
 |     struct passwd *p; | 
 |     _PROTOTYP(extern char * getlogin, (void) ); | 
 |  | 
 |     if (ruid != -1) | 
 |       return(realname); | 
 |  | 
 |     ruid = real_uid();                  /* get our uid */ | 
 |  | 
 |   /* how about $USER or $LOGNAME? */ | 
 |     if ((c = getenv(NAMEENV)) != NULL) { /* check the env variable */ | 
 |         ckstrncpy(envname, c, 255); | 
 |         if ((p = getpwnam(envname)) != NULL) { | 
 |             if (p->pw_uid == ruid) {    /* get passwd entry for envname */ | 
 |                 ckstrncpy(realname, envname, UIDBUFLEN); /* uid's are same */ | 
 |                 return(realname); | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |   /* can we use loginname() ? */ | 
 |  | 
 |     if ((c =  getlogin()) != NULL) {    /* name from utmp file */ | 
 |         ckstrncpy (loginname, c, UIDBUFLEN); | 
 |         if ((p = getpwnam(loginname)) != NULL) /* get passwd entry */ | 
 |           if (p->pw_uid == ruid)        /* for loginname */ | 
 |             ckstrncpy(realname, envname, UIDBUFLEN); /* if uid's are same */ | 
 |     } | 
 |  | 
 |   /* Use first name we get for ruid */ | 
 |  | 
 |     if ((p = getpwuid(ruid)) == NULL) { /* name for uid */ | 
 |         realname[0] = '\0';             /* no user name */ | 
 |         ruid = -1; | 
 |         return(NULL); | 
 |     } | 
 |     ckstrncpy(realname, p->pw_name, UIDBUFLEN); | 
 |     return(realname); | 
 | #else | 
 |     return(NULL); | 
 | #endif /* DTILDE */ | 
 | } | 
 |  | 
 | /*  T I L D E _ E X P A N D  --  expand ~user to the user's home directory. */ | 
 |  | 
 | char * | 
 | tilde_expand(dirname) char *dirname; { | 
 | #ifdef DTILDE | 
 | #ifdef pdp11 | 
 | #define BUFLEN 100 | 
 | #else | 
 | #define BUFLEN 257 | 
 | #endif /* pdp11 */ | 
 |     struct passwd *user; | 
 |     static char olddir[BUFLEN+1]; | 
 |     static char oldrealdir[BUFLEN+1]; | 
 |     static char temp[BUFLEN+1]; | 
 |     int i, j; | 
 |  | 
 |     debug(F111,"tilde_expand",dirname,dirname[0]); | 
 |  | 
 |     if (dirname[0] != '~')              /* Not a tilde...return param */ | 
 |       return(dirname); | 
 |     if (!strcmp(olddir,dirname)) {      /* Same as last time */ | 
 |       return(oldrealdir);               /* so return old answer. */ | 
 |     } else { | 
 |         j = (int)strlen(dirname); | 
 |         for (i = 0; i < j; i++)         /* find username part of string */ | 
 |           if (!ISDIRSEP(dirname[i])) | 
 |             temp[i] = dirname[i]; | 
 |           else break; | 
 |         temp[i] = '\0';                 /* tie off with a NULL */ | 
 |         if (i == 1) {                   /* if just a "~" */ | 
 | #ifdef IKSD | 
 |             if (inserver) | 
 |               user = getpwnam(uidbuf);  /* Get info on current user */ | 
 |             else | 
 | #endif /* IKSD */ | 
 |             { | 
 |                 char * p = whoami(); | 
 |                 if (p) | 
 | 		  user = getpwnam(p); | 
 |                 else | 
 | 		  user = NULL; | 
 |             } | 
 |         } else { | 
 |             user = getpwnam(&temp[1]);  /* otherwise on the specified user */ | 
 |         } | 
 |     } | 
 |     if (user != NULL) {                 /* valid user? */ | 
 |         ckstrncpy(olddir, dirname, BUFLEN); /* remember the directory */ | 
 |         ckstrncpy(oldrealdir,user->pw_dir, BUFLEN); /* and home directory */ | 
 |         ckstrncat(oldrealdir,&dirname[i], BUFLEN); | 
 |         oldrealdir[BUFLEN] = '\0'; | 
 |         return(oldrealdir); | 
 |     } else {                            /* invalid? */ | 
 |         ckstrncpy(olddir, dirname, BUFLEN); /* remember for next time */ | 
 |         ckstrncpy(oldrealdir, dirname, BUFLEN); | 
 |         return(oldrealdir); | 
 |     } | 
 | #else | 
 |     return(NULL); | 
 | #endif /* DTILDE */ | 
 | } | 
 |  | 
 | /* | 
 |   Functions for executing system commands. | 
 |   zsyscmd() executes the system command in the normal, default way for | 
 |   the system.  In UNIX, it does what system() does.  Thus, its results | 
 |   are always predictable. | 
 |   zshcmd() executes the command using the user's preferred shell. | 
 | */ | 
 | int | 
 | zsyscmd(s) char *s; { | 
 | #ifdef aegis | 
 |     if (nopush) return(-1); | 
 |     if (!priv_chk()) return(system(s)); | 
 | #else | 
 |     PID_T shpid; | 
 | #ifdef COMMENT | 
 | /* This doesn't work... */ | 
 |     WAIT_T status; | 
 | #else | 
 |     int status; | 
 | #endif /* COMMENT */ | 
 |  | 
 |     if (nopush) return(-1); | 
 |     if ((shpid = fork())) { | 
 |         if (shpid < (PID_T)0) return(-1); /* Parent */ | 
 |         while (shpid != (PID_T) wait(&status)) | 
 |          ; | 
 |         return(status); | 
 |     } | 
 |     if (priv_can()) {                   /* Child: cancel any priv's */ | 
 |         printf("?Privilege cancellation failure\n"); | 
 |         _exit(255); | 
 |     } | 
 |     restorsigs();			/* Restore ignored signals */ | 
 | #ifdef HPUX10 | 
 |     execl("/usr/bin/sh","sh","-c",s,NULL); | 
 |     perror("/usr/bin/sh"); | 
 | #else | 
 | #ifdef Plan9 | 
 |     execl("/bin/rc", "rc", "-c", s, NULL); | 
 |     perror("/bin/rc"); | 
 | #else | 
 |     execl("/bin/sh","sh","-c",s,NULL); | 
 |     perror("/bin/sh"); | 
 | #endif /* Plan9 */ | 
 | #endif /* HPUX10 */ | 
 |     _exit(255); | 
 |     return(0);                          /* Shut up ANSI compilers. */ | 
 | #endif /* aegis */ | 
 | } | 
 |  | 
 |  | 
 | /*  Z _ E X E C  --  Overlay ourselves with another program  */ | 
 |  | 
 | #ifndef NOZEXEC | 
 | #ifdef HPUX5 | 
 | #define NOZEXEC | 
 | #else | 
 | #ifdef ATT7300 | 
 | #define NOZEXEC | 
 | #endif /* ATT7300 */ | 
 | #endif /* HPUX5 */ | 
 | #endif /* NOZEXEC */ | 
 |  | 
 | VOID | 
 | z_exec(p,s,t) char * p, ** s; int t; {  /* Overlay ourselves with "p s..." */ | 
 | #ifdef NOZEXEC | 
 |     printf("EXEC /REDIRECT NOT IMPLEMENTED IN THIS VERSION OF C-KERMIT\n"); | 
 |     debug(F110,"z_exec NOT IMPLEMENTED",p,0); | 
 | #else | 
 |     int x; | 
 |     extern int ttyfd; | 
 |     debug(F110,"z_exec command",p,0); | 
 |     debug(F110,"z_exec arg 0",s[0],0); | 
 |     debug(F110,"z_exec arg 1",s[1],0); | 
 |     debug(F101,"z_exec t","",t); | 
 |     errno = 0; | 
 |     if (t) { | 
 |         if (ttyfd > 2) { | 
 |             dup2(ttyfd, 0); | 
 |             dup2(ttyfd, 1); | 
 |             /* dup2(ttyfd, 2); */ | 
 |             close(ttyfd); | 
 |         } | 
 |     } | 
 |     restorsigs();			/* Restore ignored signals */ | 
 |     x = execvp(p,s); | 
 |     if (x < 0) debug(F101,"z_exec errno","",errno); | 
 | #endif /* NOZEXEC */ | 
 | } | 
 |  | 
 | /* | 
 |   Z S H C M D  --  Execute a shell command (or program thru the shell). | 
 |  | 
 |   Original UNIX code by H. Fischer; copyright rights assigned to Columbia U. | 
 |   Adapted to use getpwuid to find login shell because many systems do not | 
 |   have SHELL in environment, and to use direct calling of shell rather | 
 |   than intermediate system() call. -- H. Fischer (1985); many changes since | 
 |   then.  Call with s pointing to command to execute.  Returns: | 
 |    -1 on failure to start the command (can't find, can't fork, can't run). | 
 |     1 if command ran and gave an exit status of 0. | 
 |     0 if command ran and gave a nonzero exit status. | 
 |   with pexitstatus containing the command's exit status. | 
 | */ | 
 | int | 
 | zshcmd(s) char *s; { | 
 |     PID_T pid; | 
 |  | 
 | #ifdef NOPUSH | 
 |     return(0); | 
 | #else | 
 |     if (nopush) return(-1); | 
 |     debug(F110,"zshcmd command",s,0); | 
 |  | 
 | #ifdef aegis | 
 |     if ((pid = vfork()) == 0) {         /* Make child quickly */ | 
 |         char *shpath, *shname, *shptr;  /* For finding desired shell */ | 
 |  | 
 |         if (priv_can()) exit(1);        /* Turn off privs. */ | 
 |         if ((shpath = getenv("SHELL")) == NULL) shpath = "/com/sh"; | 
 |  | 
 | #else                                   /* All Unix systems */ | 
 |     if ((pid = fork()) == 0) {          /* Make child */ | 
 |         char *shpath, *shname, *shptr;  /* For finding desired shell */ | 
 |         struct passwd *p; | 
 | #ifdef HPUX10                           /* Default */ | 
 |         char *defshell = "/usr/bin/sh"; | 
 | #else | 
 | #ifdef Plan9 | 
 |         char *defshell = "/bin/rc"; | 
 | #else | 
 |         char *defshell = "/bin/sh"; | 
 | #endif /* Plan9 */ | 
 | #endif /* HPUX10 */ | 
 |         if (priv_can()) exit(1);        /* Turn off privs. */ | 
 | #ifdef COMMENT | 
 | /* Old way always used /etc/passwd shell */ | 
 |         p = getpwuid(real_uid());       /* Get login data */ | 
 |         if (p == (struct passwd *) NULL || !*(p->pw_shell)) | 
 |           shpath = defshell; | 
 |         else | 
 |           shpath = p->pw_shell; | 
 | #else | 
 | /* New way lets user override with SHELL variable, but does not rely on it. */ | 
 | /* This allows user to specify a different shell. */ | 
 |         shpath = getenv("SHELL");       /* What shell? */ | 
 | 	debug(F110,"zshcmd SHELL",shpath,0); | 
 |         if (shpath == NULL) { | 
 |             p = getpwuid( real_uid() ); /* Get login data */ | 
 |             if (p == (struct passwd *)NULL || !*(p->pw_shell)) | 
 |               shpath = defshell; | 
 |             else shpath = p->pw_shell; | 
 | 	    debug(F110,"zshcmd shpath",shpath,0); | 
 |         } | 
 | #endif /* COMMENT */ | 
 | #endif /* aegis */ | 
 |         shptr = shname = shpath; | 
 |         while (*shptr != '\0') | 
 |           if (*shptr++ == DIRSEP) | 
 |             shname = shptr; | 
 | 	restorsigs();			/* Restore ignored signals */ | 
 | 	debug(F110,"zshcmd shname",shname,0); | 
 |         if (s == NULL || *s == '\0') {  /* Interactive shell requested? */ | 
 |             execl(shpath,shname,"-i",NULL); /* Yes, do that */ | 
 |         } else {                        /* Otherwise, */ | 
 |             execl(shpath,shname,"-c",s,NULL); /* exec the given command */ | 
 |         }                               /* If execl() failed, */ | 
 |         exit(BAD_EXIT);                 /* return bad return code. */ | 
 |  | 
 |     } else {                            /* Parent */ | 
 |  | 
 |         int wstat;                      /* ... must wait for child */ | 
 | #ifdef CK_CHILD | 
 |         int child;                      /* Child's exit status */ | 
 | #endif /* CK_CHILD */ | 
 |         SIGTYP (*istat)(), (*qstat)(); | 
 |  | 
 |         if (pid == (PID_T) -1) return(-1); /* fork() failed? */ | 
 |  | 
 |         istat = signal(SIGINT,SIG_IGN); /* Let the fork handle keyboard */ | 
 |         qstat = signal(SIGQUIT,SIG_IGN); /* interrupts itself... */ | 
 |  | 
 | #ifdef CK_CHILD | 
 |         while (((wstat = wait(&child)) != pid) && (wstat != -1)) | 
 | #else | 
 |         while (((wstat = wait((WAIT_T *)0)) != pid) && (wstat != -1)) | 
 | #endif /* CK_CHILD */ | 
 |           ;                             /* Wait for fork */ | 
 |         signal(SIGINT,istat);           /* Restore interrupts */ | 
 |         signal(SIGQUIT,qstat); | 
 | #ifdef CK_CHILD | 
 |         pexitstat = (child & 0xff) ? child : child >> 8; | 
 | 	debug(F101,"zshcmd exit status","",pexitstat); | 
 |         return(child == 0 ? 1 : 0);     /* Return child's status */ | 
 | #endif /* CK_CHILD */ | 
 |     } | 
 |     return(1); | 
 | #endif /* NOPUSH */ | 
 | } | 
 |  | 
 | /*  I S W I L D  --  Check if filespec is "wild"  */ | 
 |  | 
 | /* | 
 |   Returns: | 
 |     0 if argument is empty or is the name of a single file; | 
 |     1 if it contains wildcard characters. | 
 |   Note: must match the algorithm used by match(), hence no [a-z], etc. | 
 | */ | 
 | int | 
 | iswild(filespec) char *filespec; { | 
 |     char c, *p, *f; int x; | 
 |     int quo = 0; | 
 |     if (!filespec) | 
 |       return(0); | 
 |     f = filespec; | 
 |     if (wildxpand) {			/* Shell handles wildcarding */ | 
 |         if ((x = nzxpand(filespec,0)) > 1) | 
 |           return(1); | 
 |         if (x == 0) return(0);          /* File does not exist */ | 
 |         p = malloc(MAXNAMLEN + 20); | 
 |         znext(p); | 
 |         x = (strcmp(filespec,p) != 0); | 
 |         free(p); | 
 |         p = NULL; | 
 |         return(x); | 
 |     } else {				/* We do it ourselves */ | 
 |         while ((c = *filespec++) != '\0') { | 
 |             if (c == '\\' && quo == 0) { | 
 |                 quo = 1; | 
 |                 continue; | 
 |             } | 
 |             if (!quo && (c == '*' || c == '?' | 
 | #ifdef CKREGEX | 
 | #ifndef VMS | 
 |                          || c == '[' | 
 | #endif /* VMS */ | 
 | 			 || c == '{' | 
 | #endif /* CKREGEX */ | 
 |                          )) { | 
 | 		debug(F111,"iswild",f,1); | 
 | 		return(1); | 
 | 	    } | 
 |             quo = 0; | 
 |         } | 
 | 	debug(F111,"iswild",f,0); | 
 |         return(0); | 
 |     } | 
 | } | 
 |  | 
 | /* | 
 |   I S D I R  --  Is a Directory. | 
 |  | 
 |   Tell if string pointer s is the name of an existing directory.  Returns 1 if | 
 |   directory, 0 if not a directory. | 
 |  | 
 |   The following no longer applies: | 
 |  | 
 |   If the file is a symlink, we return 1 if | 
 |   it is a directory OR if it is a link to a directory and the "xrecursive" flag | 
 |   is NOT set.  This is to allow parsing a link to a directory as if it were a | 
 |   directory (e.g. in the CD or IF DIRECTORY command) but still prevent | 
 |   recursive traversal from visiting the same directory twice. | 
 | */ | 
 |  | 
 | #ifdef ISDIRCACHE | 
 | /* This turns out to be unsafe and gives little benefit anyway. */ | 
 | /* See notes 28 Sep 2003.  Thus ISDIRCACHE is not defined. */ | 
 |  | 
 | static char prevpath[CKMAXPATH+4] = { '\0', '\0' }; | 
 | static int prevstat = -1; | 
 | int | 
 | clrdircache() { | 
 |     debug(F100,"CLEAR ISDIR CACHE","",0); | 
 |     prevstat = -1; | 
 |     prevpath[0] = NUL; | 
 | } | 
 | #endif /* ISDIRCACHE */ | 
 |  | 
 | int | 
 | isdir(s) char *s; { | 
 |     int x, needrlink = 0, islink = 0; | 
 |     struct stat statbuf; | 
 |     char fnam[CKMAXPATH+4]; | 
 |  | 
 |     if (!s) return(0); | 
 |     if (!*s) return(0); | 
 |  | 
 | #ifdef ISDIRCACHE | 
 |     if (prevstat > -1) { | 
 | 	if (s[0] == prevpath[0]) { | 
 | 	    if (!strcmp(s,prevpath)) { | 
 | 		debug(F111,"isdir cache hit",s,prevstat); | 
 | 		return(prevstat); | 
 | 	    } | 
 | 	} | 
 |     } | 
 | #endif /* ISDIRCACHE */ | 
 |  | 
 | #ifdef CKSYMLINK | 
 | #ifdef COMMENT | 
 | /* | 
 |   The following over-clever bit has been commented out because it presumes | 
 |   to know when a symlink might be redundant, which it can't possibly know. | 
 |   Using plain old stat() gives Kermit the same results as ls and ls -R, which | 
 |   is just fine: no surprises. | 
 | */ | 
 | #ifdef USE_LSTAT | 
 |     if (xrecursive) { | 
 |         x = lstat(s,&statbuf); | 
 |         debug(F111,"isdir lstat",s,x); | 
 |     } else { | 
 | #endif /* USE_LSTAT */ | 
 |         x = stat(s,&statbuf); | 
 |         debug(F111,"isdir stat",s,x); | 
 | #ifdef USE_LSTAT | 
 |     } | 
 | #endif /* USE_LSTAT */ | 
 | #else | 
 |     x = stat(s,&statbuf); | 
 |     debug(F111,"isdir stat",s,x); | 
 | #endif /* COMMENT */ | 
 |     if (x == -1) { | 
 |         debug(F101,"isdir errno","",errno); | 
 |         return(0); | 
 |     } | 
 |     islink = 0; | 
 |     if (xrecursive) { | 
 | #ifdef NOLINKBITS | 
 |         needrlink = 1; | 
 | #else | 
 | #ifdef S_ISLNK | 
 |         islink = S_ISLNK(statbuf.st_mode); | 
 |         debug(F101,"isdir S_ISLNK islink","",islink); | 
 | #else | 
 | #ifdef _IFLNK | 
 |         islink = (_IFMT & statbuf.st_mode) == _IFLNK; | 
 |         debug(F101,"isdir _IFLNK islink","",islink); | 
 | #endif /* _IFLNK */ | 
 | #endif /* S_ISLNK */ | 
 | #endif /* NOLINKBITS */ | 
 |         if (needrlink) { | 
 |             if (readlink(s,fnam,CKMAXPATH) > -1) | 
 |               islink = 1; | 
 |         } | 
 |     } | 
 | #else | 
 |     x = stat(s,&statbuf); | 
 |     if (x == -1) { | 
 |         debug(F101,"isdir errno","",errno); | 
 |         return(0); | 
 |     } | 
 |     debug(F111,"isdir stat",s,x); | 
 | #endif /* CKSYMLINK */ | 
 |     debug(F101,"isdir islink","",islink); | 
 |     debug(F101,"isdir statbuf.st_mode","",statbuf.st_mode); | 
 |     x = islink ? 0 : (S_ISDIR (statbuf.st_mode) ? 1 : 0); | 
 | #ifdef ISDIRCACHE | 
 |     prevstat = x; | 
 |     ckstrncpy(prevpath,s,CKMAXPATH+1); | 
 | #endif /* ISDIRCACHE */ | 
 |     return(x); | 
 | } | 
 |  | 
 | #ifdef CK_MKDIR | 
 | /* Some systems don't have mkdir(), e.g. Tandy Xenix 3.2.. */ | 
 |  | 
 | /* Z M K D I R  --  Create directory(s) if necessary */ | 
 | /* | 
 |    Call with: | 
 |     A pointer to a file specification that might contain directory | 
 |     information.  The filename is expected to be included. | 
 |     If the file specification does not include any directory separators, | 
 |     then it is assumed to be a plain file. | 
 |     If one or more directories are included in the file specification, | 
 |     this routine tries to create them if they don't already exist. | 
 |    Returns: | 
 |     0 or greater on success, i.e. the number of directories created. | 
 |    -1 on failure to create the directory | 
 | */ | 
 | int | 
 | zmkdir(path) char *path; { | 
 |     char *xp, *tp, c; | 
 |     int x, count = 0; | 
 |  | 
 |     if (!path) path = ""; | 
 |     if (!*path) return(-1); | 
 |  | 
 | #ifdef CKROOT | 
 |     debug(F111,"zmkdir setroot",ckroot,ckrootset); | 
 |     if (ckrootset) if (!zinroot(path)) { | 
 | 	debug(F110,"zmkdir setroot violation",path,0); | 
 | 	return(-1); | 
 |     } | 
 | #endif /* CKROOT */ | 
 |  | 
 |     x = strlen(path); | 
 |     debug(F111,"zmkdir",path,x); | 
 |     if (x < 1 || x > MAXPATH)           /* Check length */ | 
 |       return(-1); | 
 |     if (!(tp = malloc(x+1)))            /* Make a temporary copy */ | 
 |       return(-1); | 
 |     strcpy(tp,path);			/* safe (prechecked) */ | 
 | #ifdef DTILDE | 
 |     if (*tp == '~') {                   /* Starts with tilde? */ | 
 |         xp = tilde_expand(tp);          /* Attempt to expand tilde */ | 
 |         if (!xp) xp = ""; | 
 |         if (*xp) { | 
 |             char *zp; | 
 |             debug(F110,"zmkdir tilde_expand",xp,0); | 
 |             if (!(zp = malloc(strlen(xp) + 1))) { /* Make a place for it */ | 
 |                 free(tp); | 
 |                 tp = NULL; | 
 |                 return(-1); | 
 |             } | 
 |             free(tp);                   /* Free previous buffer */ | 
 |             tp = zp;                    /* Point to new one */ | 
 |             strcpy(tp,xp);              /* Copy expanded name to new buffer */ | 
 |         } | 
 |     } | 
 | #endif /* DTILDE */ | 
 |     debug(F110,"zmkdir tp after tilde_expansion",tp,0); | 
 |     xp = tp; | 
 |     if (ISDIRSEP(*xp))                  /* Don't create root directory! */ | 
 |       xp++; | 
 |  | 
 |     /* Go thru filespec from left to right... */ | 
 |  | 
 |     for (; *xp; xp++) {                 /* Create parts that don't exist */ | 
 |         if (!ISDIRSEP(*xp))             /* Find next directory separator */ | 
 |           continue; | 
 |         c = *xp;                        /* Got one. */ | 
 |         *xp = NUL;                      /* Make this the end of the string. */ | 
 |         if (!isdir(tp)) {               /* This directory exists already? */ | 
 | #ifdef CK_LOGIN | 
 |             if (isguest)                    /* Not allowed for guests */ | 
 | 	      return(-1); | 
 | #ifndef NOXFER | 
 |             /* Nor if MKDIR and/or CD are disabled */ | 
 |             else | 
 | #endif /* CK_LOGIN */ | 
 | 	      if ((server | 
 | #ifdef IKSD | 
 | 		   || inserver | 
 | #endif /* IKSD */ | 
 | 		   ) && (!ENABLED(en_mkd) || !ENABLED(en_cwd))) | 
 | 		return(-1); | 
 | #endif /* IKSD */ | 
 |  | 
 |             debug(F110,"zmkdir making",tp,0); | 
 |             x =                         /* No, try to create it */ | 
 | #ifdef NOMKDIR | 
 |                -1                       /* Systems without mkdir() */ | 
 | #else | 
 |                mkdir(tp,0777)           /* UNIX */ | 
 | #endif /* NOMKDIR */ | 
 |                  ; | 
 |             if (x < 0) { | 
 |                 debug(F101,"zmkdir failed, errno","",errno); | 
 |                 free(tp);               /* Free temporary buffer. */ | 
 |                 tp = NULL; | 
 |                 return(-1);             /* Return failure code. */ | 
 |             } else | 
 |               count++; | 
 |         } | 
 |         *xp = c;                        /* Replace the separator. */ | 
 |     } | 
 |     free(tp);                           /* Free temporary buffer. */ | 
 |     return(count);                      /* Return success code. */ | 
 | } | 
 | #endif /* CK_MKDIR */ | 
 |  | 
 | int | 
 | zrmdir(path) char *path; { | 
 | #ifdef CK_LOGIN | 
 |     if (isguest) | 
 |       return(-1); | 
 | #endif /* CK_LOGIN */ | 
 |  | 
 |     if (!path) path = ""; | 
 |     if (!*path) return(-1); | 
 |  | 
 | #ifdef CKROOT | 
 |     debug(F111,"zrmdir setroot",ckroot,ckrootset); | 
 |     if (ckrootset) if (!zinroot(path)) { | 
 | 	debug(F110,"zrmdir setroot violation",path,0); | 
 | 	return(-1); | 
 |     } | 
 | #endif /* CKROOT */ | 
 |  | 
 | #ifndef NOMKDIR | 
 |     return(rmdir(path)); | 
 | #else | 
 |     return(-1); | 
 | #endif /* NOMKDIR */ | 
 | } | 
 |  | 
 | /* Z F S E E K  --  Position input file pointer */ | 
 | /* | 
 |    Call with: | 
 |     Long int, 0-based, indicating desired position. | 
 |    Returns: | 
 |     0 on success. | 
 |    -1 on failure. | 
 | */ | 
 | #ifndef NORESEND | 
 | int | 
 | #ifdef CK_ANSIC | 
 | zfseek(long pos) | 
 | #else | 
 | zfseek(pos) long pos; | 
 | #endif /* CK_ANSIC */ | 
 | /* zfseek */ { | 
 |     zincnt = -1;                        /* Must empty the input buffer */ | 
 |     debug(F101,"zfseek","",pos); | 
 |     return(fseek(fp[ZIFILE], pos, 0)?-1:0); | 
 | } | 
 | #endif /* NORESEND */ | 
 |  | 
 | /*  Z F N Q F P  --  Convert filename to fully qualified absolute pathname */ | 
 |  | 
 | static struct zfnfp fnfp = { 0, NULL, NULL }; | 
 |  | 
 | struct zfnfp * | 
 | zfnqfp(fname, buflen, buf)  char * fname; int buflen; char * buf; { | 
 |     char * s; | 
 |     int len; | 
 | #ifdef MAXPATHLEN | 
 |     char zfntmp[MAXPATHLEN+4]; | 
 | #else | 
 |     char zfntmp[CKMAXPATH+4]; | 
 | #endif /* MAXPATHLEN */ | 
 |  | 
 |     char sb[32], * tmp; | 
 |     int i = 0, j = 0, k = 0, x = 0, y = 0; | 
 |     int itsadir = 0; | 
 |  | 
 |     s = fname; | 
 |     if (!s) | 
 |       return(NULL); | 
 |     if (!*s) | 
 |       return(NULL); | 
 |     if (!buf) | 
 |       return(NULL); | 
 |  | 
 |     /* Initialize the data structure */ | 
 |  | 
 |     fnfp.len = ckstrncpy(buf,fname,buflen); | 
 |     fnfp.fpath = buf; | 
 |     fnfp.fname = NULL; | 
 |     len = buflen; | 
 |     debug(F111,"zfnqfp fname",fname,len); | 
 |  | 
 | #ifdef DTILDE | 
 |     if (*s == '~') {                    /* Starts with tilde? */ | 
 |         char * xp; | 
 |         xp = tilde_expand(s);           /* Attempt to expand tilde */ | 
 |         debug(F110,"zfnqfp xp",xp,0);   /* (realpath() doesn't do this) */ | 
 |         if (!xp) xp = ""; | 
 |         if (*xp) | 
 |           s = xp; | 
 |     } | 
 | #endif /* DTILDE */ | 
 |  | 
 | #ifdef CKREALPATH | 
 |  | 
 | /* N.B.: The realpath() result buffer MUST be MAXPATHLEN bytes long */ | 
 | /* otherwise we write over memory. */ | 
 |  | 
 |     if (!realpath(s,zfntmp)) { | 
 |         debug(F111,"zfnqfp realpath fails",s,errno); | 
 | #ifdef COMMENT | 
 | 	if (errno != ENOENT) | 
 | 	  return(NULL); | 
 | #else | 
 | 	/* If realpath() fails use the do-it-yourself method */ | 
 | 	/* 16 Jan 2002 */ | 
 | 	goto norealpath; | 
 | #endif /* COMMENT */ | 
 |     } | 
 |     len = strlen(zfntmp); | 
 |     if (len > buflen) { | 
 | 	debug(F111,"zfnqfp result too long",ckitoa(buflen),len); | 
 | 	return(NULL); | 
 |     } else { | 
 | 	ckstrncpy(buf,zfntmp,buflen); | 
 |     } | 
 |     if (buf[len-1] != '/') { | 
 | 	if ((itsadir = isdir(buf)) && len < (buflen - 1)) { | 
 | 	    buf[len++] = '/'; | 
 | 	    buf[len] = NUL; | 
 | 	} | 
 |     } | 
 |     fnfp.len = len; | 
 |     fnfp.fpath = buf; | 
 |     debug(F110,"zfnqfp realpath path",fnfp.fpath,0); | 
 |     tmp = buf + fnfp.len - 1; | 
 |     if (!itsadir) { | 
 | 	while (tmp >= buf) { | 
 | 	    if (*tmp == '/') { | 
 | 		fnfp.fname = tmp + 1; | 
 | 		debug(F110,"zfnqfp realpath name",fnfp.fname,0); | 
 | 		break; | 
 | 	    } | 
 | 	    tmp--; | 
 | 	} | 
 |     } | 
 |     return(&fnfp); | 
 |  | 
 | #endif /* CKREALPATH */ | 
 |  | 
 |   norealpath: | 
 |  | 
 |     tmp = zfntmp; | 
 |     while (*s) {                        /* Remove leading "./" (0 or more) */ | 
 |         debug(F110,"zfnqfp while *s",s,0); | 
 |         if (*s == '.' && *(s+1) == '/') { | 
 |             s += 2; | 
 |             while (*s == '/') s++; | 
 |         } else | 
 |           break; | 
 |     } | 
 |     if (!*s) return(NULL); | 
 |     if (*s == '/') {                    /* Pathname is absolute */ | 
 |         ckstrncpy(buf,s,len); | 
 |         x = strlen(buf); | 
 |         y = 0; | 
 |     } else {                            /* Pathname is relative */ | 
 |         char * p; | 
 |         if (p = zgtdir()) {             /* So get current directory */ | 
 |             debug(F110,"zfnqfp zgtdir",p,0); | 
 |             x = ckstrncpy(buf,p,len); | 
 |             buf[x++] = '/'; | 
 |             debug(F110,"zfnqfp buf 1",buf,0); | 
 |             len -= x;                   /* How much room left in buffer */ | 
 |             if ((y = (int)strlen(s)) > len) /* If enough room... */ | 
 |               return(NULL); | 
 |             ckstrncpy(buf+x,s,len);     /* ... append the filename */ | 
 |             debug(F110,"zfnqfp buf 2",buf,0); | 
 |         } else { | 
 |             return(NULL); | 
 |         } | 
 |     } | 
 |  | 
 |     /* Buf now holds full path but maybe containing some . or .. tricks */ | 
 |  | 
 |     j = x + y;                          /* Length of what's in buf */ | 
 |     len = j; | 
 |     debug(F101,"zfnqfp len","",len); | 
 |  | 
 |     /* Catch dangling "/." or "/.." */ | 
 |     if ((j > 1 && buf[j-1] == '.' && buf[j-2] == '/') || | 
 |         (j > 2 && buf[j-1] == '.' && buf[j-2] == '.' && buf[j-3] == '/')) { | 
 |         if (j < buflen - 2) { | 
 |             buf[j] = '/'; | 
 |             buf[j+1] = NUL; | 
 |         } | 
 |     } | 
 |     j = -1;                             /* j = position of rightmost "/" */ | 
 |     i = 0;                              /* i = destination index */ | 
 |     tmp[i] = NUL;                       /* destination is temporary buffer  */ | 
 |  | 
 |     for (x = 0; x < len; x++) {         /* x = source index */ | 
 |         if (buf[x] == '/') { | 
 |             for (k = 0; k < 4; k++) { | 
 |                 sb[k] = buf[x+k]; | 
 |                 sb[k+1] = '\0'; | 
 |                 if (!sb[k]) break; | 
 |             } | 
 |             if (!strncmp(sb,"/./",3)) { /* Eliminate "./" in "/./" */ | 
 |                 x += 1; | 
 |                 continue; | 
 |             } else if (!strncmp(sb,"//",2)) { /* Change "//" to "/" */ | 
 |                 continue; | 
 |             } else if (!strncmp(sb,"/../",4)) { /* ".." in path */ | 
 |                 for (k = i - 1; k >= 0; k--) { /* Back up one level */ | 
 |                     if (tmp[k] == '/') { | 
 |                         i = k; | 
 |                         tmp[i] = NUL; | 
 |                         break; | 
 |                     } | 
 |                 } | 
 |                 x += 2; | 
 |                 continue; | 
 |             } | 
 |         } | 
 |         if (i >= (buflen - 1)) { | 
 |             debug(F111,"zfnqfp overflow",tmp,i); | 
 |             return(NULL); | 
 |         } | 
 |         tmp[i++] = buf[x];              /* Regular character, copy */ | 
 |         tmp[i] = NUL; | 
 |         if (buf[x] == '/')              /* Remember rightmost "/" */ | 
 |           j = i; | 
 |     } | 
 |     ckstrncpy(buf,tmp,buflen-1);        /* Copy the result back */ | 
 |  | 
 |     buf[buflen-1] = NUL; | 
 |     if (!buf[0]) {                      /* If empty, say root */ | 
 |         buf[0] = '/'; | 
 |         buf[2] = NUL; | 
 |         j = 0; | 
 |         i = 1; | 
 |     } | 
 |     if ((itsadir = isdir(buf))) { | 
 | 	if (buf[i-1] != '/' && i < (buflen - 1)) { | 
 | 	    buf[i++] = '/'; | 
 | 	    buf[i] = NUL; | 
 | 	} | 
 |     } | 
 |     if (!itsadir && (j > -1)) {		/* Set pointer to basename */ | 
 |         fnfp.fname = (char *)(buf + j); | 
 |         fnfp.fpath = (char *)buf; | 
 |         fnfp.len = i; | 
 |         debug(F111,"zfnqfp path",fnfp.fpath,i); | 
 |         debug(F110,"zfnqfp name",fnfp.fname,0); | 
 |         return(&fnfp); | 
 |     } | 
 |     return(NULL); | 
 | } | 
 |  | 
 | /*  Z C M P F N  --  Compare two filenames  */ | 
 |  | 
 | /*  Returns 1 if the two names refer to the same existing file, 0 otherwise. */ | 
 |  | 
 | int | 
 | zcmpfn(s1,s2) char * s1, * s2; { | 
 |     char buf1[CKMAXPATH+1]; | 
 |     char buf2[CKMAXPATH+1]; | 
 |  | 
 | #ifdef USE_LSTAT | 
 |     char linkname[CKMAXPATH+1]; | 
 |     struct stat buf; | 
 | #endif /* USE_LSTAT */ | 
 |     int x, rc = 0; | 
 |  | 
 |     if (!s1) s1 = ""; | 
 |     if (!s2) s2 = ""; | 
 |     if (!*s1 || !*s2) return(0); | 
 |  | 
 | #ifdef CKSYMLINK                        /* We're doing symlinks? */ | 
 | #ifdef USE_LSTAT                        /* OK to use lstat()? */ | 
 |     x = lstat(s1,&buf); | 
 |     if (x > -1 &&			/* Now see if it's a symlink */ | 
 | #ifdef S_ISLNK | 
 |         S_ISLNK(buf.st_mode) | 
 | #else | 
 | #ifdef _IFLNK | 
 |         ((_IFMT & buf.st_mode) == _IFLNK) | 
 | #endif /* _IFLNK */ | 
 | #endif /* S_ISLNK */ | 
 |         ) { | 
 |         linkname[0] = '\0';             /* Get the name */ | 
 |         x = readlink(s1,linkname,CKMAXPATH); | 
 |         if (x > -1 && x < CKMAXPATH) {  /* It's a link */ | 
 |             linkname[x] = '\0'; | 
 | 	    s1 = linkname; | 
 |         } | 
 |     } | 
 | #endif /* USE_LSTAT */ | 
 | #endif /* CKSYMLINK */ | 
 |  | 
 |     if (zfnqfp(s1,CKMAXPATH,buf1)) {	/* Convert to full pathname */ | 
 |  | 
 | #ifdef CKSYMLINK			/* Same deal for second name... */ | 
 | #ifdef USE_LSTAT | 
 | 	x = lstat(s2,&buf); | 
 | 	if (x > -1 && | 
 | #ifdef S_ISLNK | 
 | 	    S_ISLNK(buf.st_mode) | 
 | #else | 
 | #ifdef _IFLNK | 
 | 	    ((_IFMT & buf.st_mode) == _IFLNK) | 
 | #endif /* _IFLNK */ | 
 | #endif /* S_ISLNK */ | 
 | 	    ) { | 
 | 	    linkname[0] = '\0'; | 
 | 	    x = readlink(s2,linkname,CKMAXPATH); | 
 | 	    if (x > -1 && x < CKMAXPATH) { | 
 | 		linkname[x] = '\0'; | 
 | 		s2 = linkname; | 
 | 	    } | 
 | 	} | 
 | #endif /* USE_LSTAT */ | 
 | #endif /* CKSYMLINK */ | 
 | 	if (zfnqfp(s2,CKMAXPATH,buf2)) { | 
 | 	    debug(F110,"zcmpfn s1",buf1,0); | 
 | 	    debug(F110,"zcmpfn s2",buf2,0); | 
 | 	    if (!strncmp(buf1,buf2,CKMAXPATH)) | 
 | 	      rc = 1; | 
 | 	} | 
 |     } | 
 |     debug(F101,"zcmpfn result","",rc); | 
 |     return(rc); | 
 | } | 
 |  | 
 | #ifdef CKROOT | 
 |  | 
 | /* User-mode chroot() implementation */ | 
 |  | 
 | int | 
 | zsetroot(s) char * s; {			/* Sets the root */ | 
 |     char buf[CKMAXPATH+1]; | 
 |     if (!s) return(-1); | 
 |     if (!*s) return(-1); | 
 |     debug(F110,"zsetroot",s,0); | 
 |     if (!isdir(s)) return(-2); | 
 |     if (!zfnqfp(s,CKMAXPATH,buf))	/* Get full, real path */ | 
 |       return(-3); | 
 |     if (access(buf,R_OK) < 0) {		/* Check access */ | 
 | 	debug(F110,"zsetroot access denied",buf,0); | 
 | 	return(-4); | 
 |     } | 
 |     s = buf; | 
 |     if (ckrootset) {			/* If root already set */ | 
 | 	if (!zinroot(s)) {		/* make sure new root is in it */ | 
 | 	    debug(F110,"zsetroot new root not in root",ckroot,0); | 
 | 	    return(-5); | 
 | 	} | 
 |     } | 
 |     if (zchdir(buf) < 1) return(-4);	/* Change directory to new root */ | 
 |     ckrootset = ckstrncpy(ckroot,buf,CKMAXPATH); /* Now set the new root */ | 
 |     if (ckroot[ckrootset-1] != '/') { | 
 | 	ckroot[ckrootset++] = '/'; | 
 | 	ckroot[ckrootset] = '\0'; | 
 |     } | 
 |     debug(F111,"zsetroot rootset",ckroot,ckrootset); | 
 |     ckrooterr = 0;			/* Reset error flag */ | 
 |     return(1); | 
 | } | 
 |  | 
 | char * | 
 | zgetroot() {				/* Returns the root */ | 
 |     if (!ckrootset) | 
 |       return(NULL); | 
 |     return((char *)ckroot); | 
 | } | 
 |  | 
 | int | 
 | zinroot(s) char * s; {			/* Checks if file s is in the root */ | 
 |     int x, n; | 
 |     struct zfnfp * f = NULL; | 
 |     char buf[CKMAXPATH+2]; | 
 |  | 
 |     debug(F111,"zinroot setroot",ckroot,ckrootset); | 
 |     ckrooterr = 0;			/* Reset global error flag */ | 
 |     if (!ckrootset)			/* Root not set */ | 
 |       return(1);			/* so it's ok - no need to check */ | 
 |     if (!(f = zfnqfp(s,CKMAXPATH,buf)))	/* Get full and real pathname */ | 
 |       return(0);			/* Fail if we can't  */ | 
 |     n = f->len;				/* Length of full pathname */ | 
 |     debug(F111,"zinroot n",buf,n); | 
 |     if (n < (ckrootset - 1) || n > CKMAXPATH) {	/* Bad length */ | 
 | 	ckrooterr = 1;			        /* Fail */ | 
 | 	return(0); | 
 |     } | 
 |     if (isdir(buf) && buf[n-1] != '/') {  /* If it's a directory name */ | 
 | 	buf[n++] = '/';			  /* make sure it ends with '/' */ | 
 | 	buf[n] = '\0'; | 
 |     } | 
 |     x = strncmp(buf,ckroot,ckrootset);	/* Compare, case-sensitive */ | 
 |     debug(F111,"zinroot checked",buf,x); | 
 |     if (x == 0)				/* OK */ | 
 |       return(1); | 
 |     ckrooterr = 1;			/* Not OK */ | 
 |     return(0); | 
 | } | 
 | #endif /* CKROOT */ | 
 |  | 
 | #ifdef CK_LOGIN | 
 | /* | 
 |   The following code provides support for user login and logout | 
 |   including anonymous accounts.  If this feature is to be supported | 
 |   outside of UNIX, it should be spread out among the ck?fio.c modules... | 
 | */ | 
 | #ifndef _PATH_BSHELL | 
 | #define _PATH_BSHELL    "/usr/bin/bash" | 
 | #endif /* _PATH_BSHELL */ | 
 | #ifndef _PATH_FTPUSERS | 
 | #define _PATH_FTPUSERS  "/etc/ftpusers" | 
 | #endif /* _PATH_FTPUSERS */ | 
 |  | 
 | /* | 
 |  * Helper function for sgetpwnam(). | 
 |  */ | 
 | char * | 
 | sgetsave(s) char *s; { | 
 |     char *new = malloc((unsigned) strlen(s) + 1); | 
 |     if (new == NULL) { | 
 |         printf("?Local resource failure: malloc\n"); | 
 |         exit(1); | 
 |         /* NOTREACHED */ | 
 |     } | 
 |     (void) strcpy(new, s);		/* safe */ | 
 |     return (new); | 
 | } | 
 |  | 
 | /* | 
 |  * Save the result of getpwnam().  Used for USER command, since | 
 |  * the data returned must not be clobbered by any other command | 
 |  * (e.g., globbing). | 
 |  */ | 
 | struct passwd * | 
 | sgetpwnam(name) char *name; { | 
 |     static struct passwd save; | 
 |     register struct passwd *p; | 
 | #ifdef CK_SHADOW | 
 |     register struct spwd *sp; | 
 | #endif /* CK_SHADOW */ | 
 |     char *sgetsave(); | 
 |  | 
 | #ifdef HPUX10_TRUSTED | 
 |     struct pr_passwd *pr; | 
 | #endif /* HPUX10_TRUSTED */ | 
 |  | 
 | #ifdef CK_SHADOW | 
 |     sp = getspnam(name); | 
 |     debug(F111,"sgetpwnam","getspnam()",sp); | 
 |     if (sp == NULL) | 
 |       return (NULL); | 
 | #endif /* CK_SHADOW */ | 
 |  | 
 | #ifdef HPUX10_TRUSTED | 
 |     if ((pr = getprpwnam(name)) == NULL) | 
 |       return(NULL); | 
 | #endif /* HPUX10_TRUSTED */ | 
 |  | 
 |     p = getpwnam(name); | 
 |     debug(F111,"sgetpwnam","getpwnam()",p); | 
 |     if (p == NULL) | 
 |       return(NULL); | 
 |     if (save.pw_name) { | 
 |         free(save.pw_name); | 
 |         free(save.pw_passwd); | 
 |         free(save.pw_gecos); | 
 |         free(save.pw_dir); | 
 |         free(save.pw_shell); | 
 |     } | 
 |     save = *p; | 
 |     save.pw_name = sgetsave(p->pw_name); | 
 | #ifdef CK_SHADOW | 
 |     save.pw_passwd = sgetsave(sp->sp_pwdp); | 
 | #else /* CK_SHADOW */ | 
 | #ifdef HPUX10_TRUSTED | 
 |     if (pr->uflg.fg_encrypt && pr->ufld.fd_encrypt && *pr->ufld.fd_encrypt) | 
 |       save.pw_passwd = sgetsave(pr->ufld.fd_encrypt); | 
 |     else | 
 |       save.pw_passwd = sgetsave(""); | 
 | #else /* HPUX10_TRUSTED */ | 
 |     save.pw_passwd = sgetsave(p->pw_passwd); | 
 | #endif /* HPUX10_TRUSTED */ | 
 | #endif /* CK_SHADOW */ | 
 |     save.pw_gecos = sgetsave(p->pw_gecos); | 
 |     save.pw_dir = sgetsave(p->pw_dir); | 
 |     save.pw_shell = sgetsave(p->pw_shell); | 
 |     return(&save); | 
 | } | 
 |  | 
 | #define CKXLOGBSIZ 256 | 
 |  | 
 | struct passwd * pw = NULL; | 
 | char * home = NULL;                     /* Home directory pointer for glob */ | 
 | #ifdef CMASK | 
 | #undef CMASK | 
 | #endif /* CMASK */ | 
 |  | 
 | #define CMASK 027 | 
 |  | 
 | int defumask = CMASK;                   /* Default umask value */ | 
 |  | 
 | /*  Z V U S E R  --  Verify user, Returns 1 if user OK, 0 otherwise.  */ | 
 |  | 
 | /* Sets global passwd pointer pw if named account exists and is acceptable; | 
 |  * sets askpasswd if a PASS command is expected.  If logged in previously, | 
 |  * need to reset state.  If name is "ftp" or "anonymous", the name is not in | 
 |  * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. | 
 |  * If account doesn't exist, ask for passwd anyway.  Otherwise, check user | 
 |  * requesting login privileges.  Disallow anyone who does not have a standard | 
 |  * shell as returned by getusershell().  Disallow anyone mentioned in the file | 
 |  * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. | 
 |  */ | 
 | _PROTOTYP(static int checkuser, (char *) ); | 
 |  | 
 | char zvuname[64] = { NUL, NUL }; | 
 | char zvhome[CKMAXPATH+1] = { NUL, NUL }; | 
 | #define ZENVUSER 70 | 
 | #define ZENVHOME CKMAXPATH+12 | 
 | #define ZENVLOGNAME 74 | 
 | static char zenvuser[ZENVUSER]; | 
 | static char zenvhome[ZENVHOME]; | 
 | static char zenvlogname[ZENVLOGNAME]; | 
 |  | 
 | #ifdef CK_PAM | 
 | static char pam_data[500]; | 
 | struct pam_conv pam_conv = {pam_cb, pam_data}; /* PAM structure */ | 
 | struct pam_handle * pamh = NULL;               /* PAM reference handle */ | 
 | #endif /* CK_PAM */ | 
 |  | 
 | int | 
 | zvuser(name) char *name; { | 
 |     register char *cp = NULL; | 
 |     int x; | 
 |     char *shell; | 
 | #ifdef GETUSERSHELL | 
 |     char *getusershell(); | 
 | #endif /* GETUSERSHELL */ | 
 |  | 
 | #ifdef CK_PAM | 
 |     int pam_status; | 
 |     const char * reply = NULL; | 
 | #endif /* CK_PAM */ | 
 |  | 
 |     debug(F111,"user",name,logged_in); | 
 |  | 
 |     if (!name) name = ""; | 
 |     zvuname[0] = NUL; | 
 |  | 
 |     debug(F101,"zvuser ckxsyslog","",ckxsyslog); | 
 |  | 
 | #ifdef CKSYSLOG | 
 |     debug(F100,"zvuser CKSYSLOG defined","",0); | 
 | #endif /* CKSYSLOG */ | 
 |  | 
 |     if (logged_in)                      /* Should not be called if logged in */ | 
 |       return(0); | 
 |  | 
 | #ifdef CKSYSLOG | 
 |     if (ckxsyslog && ckxlogging) { | 
 |         syslog(LOG_INFO, | 
 |                 "login: user %s",name | 
 |                 ); | 
 |     } | 
 | #endif /* CKSYSLOG */ | 
 |  | 
 |     guest = 0;                          /* Assume not guest */ | 
 |     askpasswd = 0; | 
 |  | 
 |     if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { | 
 |         debug(F101,"zvuser anonymous ckxanon","",ckxanon); | 
 |         if (!ckxanon) {                 /* Anonymous login not allowed */ | 
 | #ifdef CKSYSLOG | 
 |             if (ckxsyslog && ckxlogging) { | 
 |                 syslog(LOG_INFO, | 
 |                        "login: anonymous login not allowed: %s", | 
 |                        clienthost ? clienthost : "(unknown host)" | 
 |                        ); | 
 |             } | 
 | #endif /* CKSYSLOG */ | 
 |             return(0); | 
 |         } | 
 |         if (checkuser("ftp") || checkuser("anonymous")) { | 
 |             debug(F100,"zvuser anon forbidden by ftpusers file","",0); | 
 | #ifdef CKSYSLOG | 
 |             if (ckxsyslog && ckxlogging) { | 
 |                 syslog(LOG_INFO, | 
 |                        "login: anonymous login forbidden by ftpusers file: %s", | 
 |                        clienthost ? clienthost : "(unknown host)" | 
 |                        ); | 
 |             } | 
 | #endif /* CKSYSLOG */ | 
 |             return(0); | 
 | 	} else if ((pw = sgetpwnam("ftp")) != NULL) { | 
 |             debug(F100,"zvuser anon sgetpwnam(ftp) OK","",0); | 
 |             guest = 1; | 
 |             askpasswd = 1; | 
 |             ckstrncpy(zvuname,"anonymous",64); | 
 |             return(1); | 
 |         } else { | 
 |             debug(F100,"zvuser anon sgetpwnam(ftp) FAILED","",0); | 
 | #ifdef CKSYSLOG | 
 |             if (ckxsyslog && ckxlogging) { | 
 |                 syslog(LOG_INFO, | 
 |                        "login: anonymous getpwnam(ftp) failed: %s", | 
 |                        clienthost ? clienthost : "(unknown host)" | 
 |                        ); | 
 |             } | 
 | #endif /* CKSYSLOG */ | 
 |             return(0); | 
 |         } | 
 |     } | 
 |     pw = sgetpwnam(name); | 
 |     if (pw) { | 
 | /* | 
 |   Of course some UNIX platforms (like AIX) don't have getusershell(). | 
 |   In that case we can't check if the user's account has been "turned off" | 
 |   or somesuch, e.g. by setting their shell to "/etc/nologin" or somesuch, | 
 |   which runs (usually just printing a message and exiting), but which is | 
 |   not listed in /etc/shells.  For that matter, if getusershell() is not | 
 |   available, then probably neither is /etc/shells. | 
 | */ | 
 |         debug(F100,"zvuser sgetpwnam ok","",0); | 
 |         shell = pw->pw_shell; | 
 |         if (!shell) shell = ""; | 
 |         if (!*shell) | 
 |           shell = _PATH_BSHELL; | 
 |         debug(F110,"zvuser shell",shell,0); | 
 | #ifdef GETUSERSHELL | 
 |         while ((cp = getusershell()) != NULL) { | 
 |             debug(F110,"zvuser getusershell",cp,0); | 
 |             if (strcmp(cp, shell) == 0) | 
 |               break; | 
 |         } | 
 |         debug(F100,"zvuser endusershell 1","",0); | 
 |         endusershell(); | 
 |         debug(F100,"zvuser endusershell 2","",0); | 
 | #else /* GETUSERSHELL */ | 
 |         cp = "";                        /* Do not refuse if we cannot check */ | 
 | #endif /* GETUSERSHELL */ | 
 |         x = checkuser(name); | 
 |         debug(F101,"zvuser checkuser","",x); | 
 |         if (cp == NULL) { | 
 |             debug(F100,"zvuser refused 1","",0); | 
 |             pw = (struct passwd *) NULL; | 
 | #ifdef CKSYSLOG | 
 |             if (ckxsyslog && ckxlogging) { | 
 |                 syslog(LOG_INFO, | 
 |                        "login: invalid shell %s for %s %s",shell, name, | 
 |                        clienthost ? clienthost : "(unknown host)" | 
 |                        ); | 
 |             } | 
 | #endif /* CKSYSLOG */ | 
 |             return(0); | 
 |         } else if (x) { | 
 |             debug(F100,"zvuser refused 2","",0); | 
 |             pw = (struct passwd *) NULL; | 
 | #ifdef CKSYSLOG | 
 |             if (ckxsyslog && ckxlogging) { | 
 |                 syslog(LOG_INFO, | 
 |                        "login: %s login forbidden by ftpusers file: %s", | 
 |                        name, clienthost ? clienthost : "(unknown host)" | 
 |                        ); | 
 |             } | 
 | #endif /* CKSYSLOG */ | 
 |             return(0); | 
 |         } else { | 
 |             x = 0; | 
 | #ifdef CK_PAM | 
 |             /* Get PAM authentication details */ | 
 |             debug(F110,"zvuser","calling pam_start",0); | 
 |             if ((pam_status = | 
 |                  pam_start(PAM_SERVICE_TYPE,name,&pam_conv,&pamh)) | 
 |                 != PAM_SUCCESS) { | 
 |                 reply = pam_strerror(NULL, pam_status); | 
 |                 debug(F110,"zvuser PAM failure",reply,0); | 
 |                 printf("%s\n",reply); | 
 | #ifdef CKSYSLOG | 
 |                 if (ckxsyslog && ckxlogging) { | 
 |                     syslog(LOG_INFO, | 
 |                            "login: %s refused by PAM \"%s\": %s", | 
 |                            name,reply, | 
 |                            clienthost ? clienthost : "(unknown host)" | 
 |                            ); | 
 |                 } | 
 | #endif /* CKSYSLOG */ | 
 |                 return(0); | 
 |             } | 
 | #endif /* CK_PAM */ | 
 |             askpasswd = 1; | 
 |             ckstrncpy(zvuname,name,64); | 
 |             return(1); | 
 |         } | 
 |     } else { | 
 |         x = 0; | 
 |         debug(F100,"zvuser sgetpwnam NULL","",0); | 
 | #ifdef CKSYSLOG | 
 |         if (ckxsyslog && ckxlogging) { | 
 |             syslog(LOG_INFO, | 
 |                    "login: getpwnam(%s) failed: %s",name, | 
 |                    clienthost ? clienthost : "(unknown host)" | 
 |                    ); | 
 |         } | 
 | #endif /* CKSYSLOG */ | 
 |         return(0); | 
 |     } | 
 |  | 
 | #ifdef FTP_KERBEROS | 
 |     if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) { | 
 | #ifdef COMMENT | 
 | 	/* Why sprintf and then printf? */ | 
 | 	/* Also, what is kerb_ok?  And is the test on it right? */ | 
 |         char buf[CKXLOGBSIZ]; | 
 |         sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s%s", | 
 |                  kdata.pname, *kdata.pinst ? "." : "", | 
 |                  kdata.pinst, kdata.prealm, | 
 |                  (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not", | 
 |                  name, kerb_ok ? "" : "; Password required."); | 
 |         printf("%s", buf); | 
 | #else | 
 |         printf("Kerberos user %s%s%s@%s is%s authorized as %s%s", | 
 |                  kdata.pname, *kdata.pinst ? "." : "", | 
 |                  kdata.pinst, kdata.prealm, | 
 |                  (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not", | 
 |                  name, kerb_ok ? "" : "; Password required."); | 
 | #endif /* COMMENT */ | 
 |         if (kerb_ok) return(1); | 
 |     } else | 
 |       return(0); | 
 | #endif /* FTP_KERBEROS */ | 
 | } | 
 |  | 
 | /* Check if the given user is in the forbidden-user file */ | 
 |  | 
 | static int | 
 | checkuser(name) char *name; { | 
 |     extern char * userfile; | 
 |     FILE *fd; | 
 |     int i; | 
 |     char line[CKXLOGBSIZ+1]; | 
 |  | 
 |     if (!name) | 
 |       name = ""; | 
 |     i = strlen(name); | 
 |     debug(F111,"checkuser name",name,i); | 
 |     if (!*name) | 
 |       return(1); | 
 |  | 
 |     fd = fopen(userfile ? userfile : _PATH_FTPUSERS, "r"); | 
 |     debug(F111,"checkuser userfile",userfile,fd); | 
 |     if (fd) { | 
 |         line[0] = '\0'; | 
 |         while (fgets(line, sizeof(line), fd)) { | 
 |             debug(F110,"checkuser line",line,0); | 
 |             if (line[0] <= '#') | 
 |               continue; | 
 |             if (strncmp(line, name, i) == 0) { | 
 |                 debug(F110,"checkuser REFUSED",name,0); | 
 |                 return(1); | 
 |             } | 
 |             line[0] = '\0'; | 
 |         } | 
 |         (VOID) fclose(fd); | 
 |     } | 
 |     debug(F110,"checkuser OK",name,0); | 
 |     return(0); | 
 | } | 
 |  | 
 | /*  Z V L O G O U T  --  Log out from Internet Kermit Service  */ | 
 |  | 
 | VOID | 
 | zvlogout() { | 
 | #ifdef COMMENT | 
 |     /* This could be dangerous */ | 
 |     if (setuid((UID_T)0) < 0) { | 
 |         debug(F100,"zvlogout setuid FAILED","",0); | 
 |         goto bad; | 
 |     } | 
 |     debug(F100,"zvlogout setuid OK","",0); | 
 | #endif /* COMMENT */ | 
 | #ifdef CKSYSLOG | 
 |     if (ckxsyslog >= SYSLG_LI && ckxlogging) { | 
 |         cksyslog(SYSLG_LI, 1, "logout",(char *) uidbuf, clienthost); | 
 |     } | 
 | #endif /* CKSYSLOG */ | 
 | #ifdef CKWTMP | 
 |     debug(F110,"WTMP logout",cksysline,logged_in); | 
 |     if (logged_in) | 
 |       logwtmp(cksysline, "", ""); | 
 | #endif /* CKWTMP */ | 
 |     pw = NULL; | 
 |     logged_in = 0; | 
 |     guest = 0; | 
 |     isguest = 0; | 
 | } | 
 |  | 
 | #ifdef FTP_KERBEROS | 
 | kpass(name, p) char *name, *p; { | 
 |     char instance[INST_SZ]; | 
 |     char realm[REALM_SZ]; | 
 |     char tkt_file[20]; | 
 |     KTEXT_ST ticket; | 
 |     AUTH_DAT authdata; | 
 |     unsigned long faddr; | 
 |     struct hostent *hp; | 
 |  | 
 |     if (krb_get_lrealm(realm, 1) != KSUCCESS) | 
 |       return(0); | 
 |  | 
 |     ckstrncpy(tkt_file, TKT_ROOT, 20); | 
 |     ckstrncat(tkt_file, "_ftpdXXXXXX", 20); | 
 |     krb_set_tkt_string(mktemp(tkt_file)); | 
 |  | 
 |     (VOID) ckstrncpy(instance, krb_get_phost(hostname), sizeof(instance)); | 
 |  | 
 |     if ((hp = gethostbyname(instance)) == NULL) | 
 |       return(0); | 
 |  | 
 | #ifdef HADDRLIST | 
 |     hp = ck_copyhostent(hp);		/* safe copy that won't change */ | 
 | #endif /* HADDRLIST */ | 
 |     bcopy((char *)hp->h_addr, (char *) &faddr, sizeof(faddr)); | 
 |  | 
 |     if (krb_get_pw_in_tkt(name, "", realm, "krbtgt", realm, 1, p) || | 
 |         krb_mk_req(&ticket, "rcmd", instance, realm, 33) || | 
 |         krb_rd_req(&ticket, "rcmd", instance, faddr, &authdata, "") || | 
 |         kuserok(&authdata, name)) { | 
 |         dest_tkt(); | 
 |         return(0); | 
 |     } | 
 |     dest_tkt(); | 
 |     return(1); | 
 | } | 
 | #endif /* FTP_KERBEROS */ | 
 |  | 
 | VOID | 
 | zsyslog() { | 
 | #ifdef CKSYSLOG | 
 |     if (ckxsyslog && !ckxlogging) { | 
 | #ifdef LOG_DAEMON | 
 |         openlog(inserver ? "iksd" : "ckermit", LOG_PID, LOG_DAEMON); | 
 | #else | 
 |         openlog(inserver ? "iksd" : "ckermit", LOG_PID); | 
 | #endif /* LOG_DAEMON */ | 
 |         ckxlogging = 1; | 
 |         debug(F100,"zsyslog syslog opened","",0); | 
 |     } | 
 | #endif /* CKSYSLOG */ | 
 | } | 
 |  | 
 | /*  Z V P A S S  --  Verify password; returns 1 if OK, 0 otherwise  */ | 
 |  | 
 | #ifndef AUTH_USER | 
 | #define AUTH_USER 3 | 
 | #endif /* AUTH_USER */ | 
 | #ifndef AUTH_VALID | 
 | #define AUTH_VALID 4 | 
 | #endif /* AUTH_VALID */ | 
 |  | 
 | int | 
 | zvpass(p) char *p; { | 
 |     char *xpasswd, *salt; | 
 |     char * dir = NULL; | 
 | #ifdef CK_PAM | 
 |     int pam_status; | 
 |     const char * reply = NULL; | 
 | #endif /* CK_PAM */ | 
 |  | 
 |     if (logged_in || askpasswd == 0) { | 
 |         return(0); | 
 |     } | 
 |     debug(F111,"zvpass",p ? (guest ? p : "xxxxxx") : "(null)",guest); | 
 |     if (!p) p = ""; | 
 |     askpasswd = 0; | 
 |     if (guest && !*p) {                 /* Guests must specify a password */ | 
 | #ifdef CKSYSLOG | 
 |         if (ckxsyslog && ckxlogging) { | 
 |             syslog(LOG_INFO, | 
 |                    "login: anonymous guests must specify a password" | 
 |                    ); | 
 |         } | 
 | #endif /* CKSYSLOG */ | 
 |         return(0); | 
 |     } | 
 |     if (!guest | 
 | #ifdef CK_AUTHENTICATION | 
 |         && ck_tn_auth_valid() != AUTH_VALID | 
 | #endif /* CK_AUTHENTICATION */ | 
 |         ) {                     /* "ftp" is only account allowed no password */ | 
 | #ifdef CK_PAM | 
 |         debug(F110,"zvpass","calling pam_set_item(AUTHTOK)",0); | 
 |         if ((pam_status = pam_set_item(pamh,PAM_AUTHTOK,p)) != PAM_SUCCESS) { | 
 |             reply = pam_strerror(pamh, pam_status); | 
 |             debug(F110,"zvpass PAM failure",reply,0); | 
 |             /* if no password given treat as non-fatal error */ | 
 |             /* pam will prompt for password in pam_authenticate() */ | 
 |             if (!p) { | 
 |                 printf("%s\n",reply); | 
 |                 pam_end(pamh, 0); | 
 |                 debug(F100,"zvpass denied","",0); | 
 |                 pw = NULL; | 
 |                 zvuname[0] = NUL; | 
 |                 return(0); | 
 |             } | 
 |         } | 
 |         debug(F110,"zvpass","calling pam_authenticate",0); | 
 |         if (*p) | 
 | 	  pam_pw = p; | 
 |         if ((pam_status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) { | 
 |             reply = pam_strerror(pamh, pam_status); | 
 |             debug(F110,"zvpass PAM failure",reply,0); | 
 |             printf("%s\n",reply); | 
 |             pam_end(pamh, 0); | 
 |             debug(F100,"zvpass denied","",0); | 
 |             pam_pw = NULL; | 
 |             pw = NULL; | 
 |             zvuname[0] = NUL; | 
 |             return(0); | 
 |         } | 
 |         pam_pw = NULL; | 
 |         debug(F110,"zvpass","calling pam_acct_mgmt",0); | 
 |         if ((pam_status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) { | 
 |             reply = pam_strerror(pamh, pam_status); | 
 |             debug(F110,"zvpass PAM failure",reply,0); | 
 |             printf("%s\n",reply); | 
 |             pam_end(pamh, 0); | 
 |             debug(F100,"zvpass denied","",0); | 
 |             pw = NULL; | 
 |             zvuname[0] = NUL; | 
 |             return(0); | 
 |         } | 
 |         debug(F110,"zvpass","PAM validates OK",0); | 
 |         pam_end(pamh,0); | 
 | #else /* CK_PAM */ | 
 |         if (pw == NULL) | 
 |           salt = "xx"; | 
 |         else | 
 |           salt = pw->pw_passwd; | 
 |  | 
 | #ifdef HPUX10_TRUSTED | 
 |         xpasswd = bigcrypt(p, salt); | 
 | #else | 
 | /* | 
 |   On 64-bit platforms this can give "cast to pointer from integer of | 
 |   different size" warning, but I'm not sure what the effect is at runtime, | 
 |   or what to do about it. | 
 |  */ | 
 |         xpasswd = (char *)crypt(p, salt); | 
 | #endif /* HPUX10_TRUSTED */ | 
 |  | 
 |         if ( | 
 | #ifdef FTP_KERBEROS | 
 |             /* null pw_passwd ok if Kerberos password ok */ | 
 |             pw == NULL || | 
 |             ((*pw->pw_passwd != '\0' || | 
 |               strcmp(xpasswd, pw->pw_passwd)) | 
 |              && !kpass(pw->pw_name, p)) | 
 | #else | 
 | #ifdef CK_SRP | 
 |             /* check with tpasswd first if there */ | 
 |             pw == NULL || *pw->pw_passwd == '\0' || | 
 |             t_verifypw (pw->pw_name, p) == 0 || | 
 |             (t_verifypw (pw->pw_name, p) < 0 && | 
 |             strcmp (xpasswd, pw->pw_passwd)) | 
 | #else /* CK_SRP */ | 
 |             /* The strcmp does not catch null passwords! */ | 
 |             (pw == NULL) || (*pw->pw_passwd == '\0') || | 
 |             strcmp(xpasswd, pw->pw_passwd) | 
 | #endif /* CK_SRP */ | 
 | #endif /* FTP_KERBEROS */ | 
 |             ) { | 
 |             debug(F100,"zvpass denied","",0); | 
 |             pw = NULL; | 
 |             zvuname[0] = NUL; | 
 |             return(0); | 
 |         } | 
 | #endif /* CK_PAM */ | 
 |     } | 
 |  | 
 |     (VOID) setgid((GID_T)pw->pw_gid);   /* Set group ID */ | 
 |  | 
 | #ifndef NOINITGROUPS | 
 |     (VOID) initgroups(pw->pw_name, pw->pw_gid); | 
 | #endif /* NOINITGROUPS */ | 
 |  | 
 |     logged_in = 1; | 
 |     dir = pw->pw_dir; | 
 |  | 
 | #ifdef CKWTMP | 
 |     /* Open wtmp before chroot */ | 
 |     if (ckxwtmp) { | 
 |         sprintf(cksysline,"iks_%04x", getpid()); /* safe */ | 
 |         logwtmp(cksysline, pw->pw_name, | 
 |                  clienthost ? clienthost : "(unknown host)" | 
 |                 ); | 
 |         debug(F110,"WTMP login",cksysline,logged_in); | 
 |     } | 
 | #endif /* CKWTMP */ | 
 | /* | 
 |   For anonymous users, we chroot to user ftp's home directory unless | 
 |   started with --anonroot:xxx, in which case we chroot to xxx.  We must | 
 |   immediately chdir() to the same directory we chroot() to or else the | 
 |   old current directory remains accessible as "." outside the new root. | 
 | */ | 
 |     if (guest) { | 
 |         if (anonroot)                   /* Non-default anonymous root */ | 
 |           dir = anonroot; | 
 |         else | 
 |           makestr(&anonroot,dir); | 
 |         errno = 0; | 
 |         debug(F110,"zvpass anon chroot",dir,0); | 
 |         if (chroot(dir) < 0) { | 
 |             debug(F111,"zvpass anon chroot FAILED",dir,errno); | 
 |             goto bad; | 
 |         } | 
 |         errno = 0; | 
 |         if (chdir("/") < 0) { | 
 |             debug(F111,"zvpass anon chdir FAILED",dir,errno); | 
 |             goto bad; | 
 |         } | 
 |         debug(F110,"zvpass anon chroot/chdir OK",dir,0); | 
 |     } else if (chdir(dir) < 0) {        /* Not guest */ | 
 | #ifdef COMMENT | 
 |         if (chdir("/") < 0) { | 
 |             debug(F110,"Non-guest chdir FAILED",dir,0); | 
 |             goto bad; | 
 |         } else | 
 |           printf("?No directory! Logging in with home=/\n"); | 
 | #else | 
 |         debug(F110,"zvpass non-guest chdir FAILED",dir,0); | 
 |         goto bad;                       /* Be conservative at first */ | 
 | #endif /* COMMENT */ | 
 |     } | 
 |     debug(F110,"zvpass non-guest chdir OK",dir,0); | 
 |     if (setuid((UID_T)pw->pw_uid) < 0) { | 
 |         debug(F101,"zvpass setuid FAILED","",pw->pw_uid); | 
 |         goto bad; | 
 |     } | 
 |     debug(F101,"zvpass setuid OK","",pw->pw_uid); | 
 |  | 
 |     guestpass[0] = '\0'; | 
 |     if (guest) { | 
 |         extern int fncact; | 
 |         isguest = 1; | 
 |         fncact = XYFX_R;                /* FILE COLLISION = RENAME */ | 
 |         debug(F110,"GUEST fncact=R",p,0); | 
 |         lset(guestpass,"anonymous:",10,32); | 
 |         ckstrncpy(&guestpass[10],p,GUESTPASS-10); | 
 |         home = "/"; | 
 |         printf("Anonymous login.\r\n"); | 
 |  | 
 | #ifdef SETPROCTITLE | 
 | 	/* proctitle declared where?  Obviously this code is never compiled. */ | 
 |         sprintf(proctitle, "%s: anonymous/%.*s", | 
 |                 clienthost ? clienthost : "(unk)", | 
 |                 sizeof(proctitle) - sizeof(clienthost) - | 
 |                 sizeof(": anonymous/"), p); | 
 |         setproctitle(proctitle); | 
 | #endif /* SETPROCTITLE */ | 
 |  | 
 | #ifdef CKSYSLOG | 
 |         if (ckxsyslog && ckxlogging) { | 
 |             syslog(LOG_INFO, | 
 |                    "login: anonymous %s %s", | 
 |                    clienthost ? clienthost : "(unknown host)", | 
 |                    p | 
 |                    ); | 
 |         } | 
 | #endif /* CKSYSLOG */ | 
 |  | 
 |     } else {                            /* Real user */ | 
 |         isguest = 0; | 
 |         home = dir; | 
 |         ckstrncpy(guestpass,zvuname,GUESTPASS); | 
 |  | 
 |         printf("User %s logged in.\r\n", pw->pw_name); | 
 | #ifdef SETPROCTITLE | 
 | 	/* not used */ | 
 |         sprintf(proctitle, "%s: %s", | 
 |                 clienthost ? clienthost : "(unk)", | 
 |                 pw->pw_name | 
 |                 ); | 
 |         setproctitle(proctitle); | 
 | #endif /* SETPROCTITLE */ | 
 |  | 
 | #ifdef CKSYSLOG | 
 |         if (ckxsyslog && ckxlogging) | 
 |           syslog(LOG_INFO, "login: %s %s", | 
 |                  pw->pw_name, | 
 |                  clienthost ? clienthost : "(unknown host)" | 
 |                  ); | 
 | #endif /* CKSYSLOG */ | 
 |     } | 
 |     ckstrncpy(zvhome,home,CKMAXPATH);   /* Set environment variables */ | 
 | #ifndef NOPUTENV | 
 |  | 
 |     ckmakmsg(zenvuser,ZENVUSER,"USER=",zvuname,NULL,NULL); | 
 |     putenv((char *)zenvuser); | 
 |     ckmakmsg(zenvlogname,ZENVLOGNAME,"LOGNAME=",zvuname,NULL,NULL); | 
 |     putenv((char *)zenvlogname); | 
 |     ckmakmsg(zenvhome,ZENVHOME,"HOME=",zvhome,NULL,NULL); | 
 |     putenv((char *)zenvhome); | 
 | #endif /* NOPUTENV */ | 
 |     /* homdir = (char *)zvhome; */ | 
 |     ckstrncpy((char *)uidbuf,(char *)zvuname,64); | 
 |     (VOID) umask(defumask); | 
 | #ifdef IKSDB | 
 |     if (ikdbopen) { | 
 |         char * p2; | 
 |         int k; | 
 |         extern char dbrec[]; | 
 |         extern unsigned long myflags; | 
 |         extern unsigned int mydbslot; | 
 |         extern struct iksdbfld dbfld[]; | 
 | #ifdef CK_AUTHENTICATION | 
 |         extern unsigned long myamode, myatype; | 
 | #endif /* CK_AUTHENTICATION */ | 
 |         myflags |= DBF_LOGGED; | 
 | #ifdef DEBUG | 
 | 	if (deblog) { | 
 | 	    debug(F101,"zvpass guest","",guest); | 
 | 	    debug(F111,"zvpass zvuname",zvuname,0); | 
 | 	    debug(F110,"zvpass guestpass",guestpass,0); | 
 | 	    debug(F110,"zvpass dir",dir,0); | 
 | 	    debug(F110,"zvpass home",home,0); | 
 | 	    debug(F110,"zvpass anonroot",anonroot,0); | 
 | 	} | 
 | #endif /* DEBUG */ | 
 |         p2 = guest ? guestpass : zvuname; | 
 |         if (guest) { | 
 |             p2 = (char *)guestpass; | 
 |             myflags &= ~DBF_USER; | 
 |         } else { | 
 |             p2 = (char *)zvuname; | 
 |             myflags |= DBF_USER; | 
 |         } | 
 |         k = strlen(p2); | 
 |         strncpy(&dbrec[DB_ULEN],ulongtohex((unsigned long)k,4),4); | 
 |         lset(&dbrec[dbfld[db_USER].off],p2,1024,32); | 
 |         strncpy(&dbrec[DB_FLAGS],ulongtohex(myflags,4),4); | 
 | #ifdef CK_AUTHENTICATION | 
 |         myamode = ck_tn_auth_valid(); | 
 |         strncpy(&dbrec[DB_AMODE],ulongtohex(myamode,4),4); | 
 |         myatype = ck_tn_authenticated(); | 
 |         strncpy(&dbrec[DB_ATYPE],ulongtohex(myatype,4),4); | 
 | #endif /* CK_AUTHENTICATION */ | 
 |         if (guest) { | 
 |             p2 = dir; | 
 |         } else { | 
 |             p2 = zgtdir(); | 
 |             if (!p2) p2 = ""; | 
 |             if (!*p2) p2 = home; | 
 |         } | 
 |         strncpy(&dbrec[DB_DLEN], | 
 |                 ulongtohex((unsigned long)strlen(p2),4), | 
 |                 4 | 
 |                 ); | 
 |         lset(&dbrec[dbfld[db_DIR].off],p2,1024,32); | 
 |         updslot(mydbslot); | 
 |     } | 
 | #endif /* IKSDB */ | 
 |     return(1); | 
 |  | 
 | bad:                                    /* Common failure exit */ | 
 |     zvuname[0] = NUL; | 
 |     zvlogout(); | 
 |     return(0); | 
 | } | 
 | #endif /* CK_LOGIN */ | 
 |  | 
 | /* Buggy Xenix 2.3.4 cc needs this line after the endif */ |