--- /dev/null
+/* 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 */