1 /* C K U F I O -- Kermit file system support for UNIX, Aegis, and Plan 9 */
3 #define CK_NONBLOCK /* See zoutdump() */
6 char *ckzv = "Aegis File support, 8.0.200, 4 Mar 2004";
9 char *ckzv = "Plan 9 File support, 8.0.200, 4 Mar 2004";
11 char *ckzv = "UNIX File support, 8.0.200, 4 Mar 2004";
15 Author: Frank da Cruz <fdc@columbia.edu>,
16 Columbia University Academic Information Systems, New York City,
17 and others noted in the comments below. Note: CUCCA = Previous name of
18 Columbia University Academic Information Systems.
20 Copyright (C) 1985, 2004,
21 Trustees of Columbia University in the City of New York.
22 All rights reserved. See the C-Kermit COPYING.TXT file or the
23 copyright text in the ckcmai.c module for disclaimer and permissions.
27 NOTE TO CONTRIBUTORS: This file, and all the other C-Kermit files, must be
28 compatible with C preprocessors that support only #ifdef, #else, #endif,
29 #define, and #undef. Please do not use #if, logical operators, or other
30 preprocessor features in any of the portable C-Kermit modules. You can,
31 of course, use these constructions in platform-specific modules where you
32 know they are supported.
49 /* This causes trouble in C-Kermit 8.0. I don't remember the original */
50 /* reason for this being here but it must have been needed at the time... */
55 #endif /* _NO_PROTO */
73 #include <sys/types.h>
85 Directory Separator macros, to allow this module to work with both UNIX and
86 OS/2: Because of ambiguity with the command line editor escape \ character,
87 the directory separator is currently left as / for OS/2 too, because the
88 OS/2 kernel also accepts / as directory separator. But this is subject to
89 change in future versions to conform to the normal OS/2 style.
95 #define ISDIRSEP(c) ((c)=='/')
103 #include <sys/ndir.h>
107 #else /* !NDIR, !XNDIR */
109 #include "/usr/lib/ndir.h"
110 #else /* !RTU, !NDIR, !XNDIR */
113 #include <sys/dirent.h>
124 #ifdef UNIX /* Pointer arg to wait() allowed */
125 #define CK_CHILD /* Assume this is safe in all UNIX */
128 extern int binary, recursive, stathack;
130 extern int eofmethod;
131 #endif /* CK_CTRLZ */
133 #include <pwd.h> /* Password file for shell name */
135 #include <t_pwd.h> /* SRP Password file */
138 #ifdef HPUX10_TRUSTED
139 #include <hpsecurity.h>
141 #endif /* HPUX10_TRUSTED */
144 /* Moved to ckcdeb.h */
154 #ifdef SYSUTIMEH /* <sys/utime.h> if requested, */
155 #include <sys/utime.h> /* for extra fields required by */
156 #else /* 88Open spec. */
157 #ifdef UTIMEH /* or <utime.h> if requested */
158 #include <utime.h> /* (SVR4, POSIX) */
161 /* Not sure why this is here. What it implies is that the code bracketed
162 by SYSUTIMEH is valid on all platforms on which we support time
163 functionality. But we know that is not true because the BSD44 and V7
164 platforms do not support sys/utime.h and the data structures which
165 are defined in them. Now this worked before because prior to today's
166 changes the UTIMEH definition for BSD44 and V7 did not take place
167 until after SYSUTIMEH was defined. It also would not have been a
168 problem if the ordering of all the time blocks was consistent. All but
169 one of the blocks were BSD44, V7, SYSUTIMEH, <OTHER>. That one case
170 is where this problem was triggered.
172 #define SYSUTIMEH /* Use this for both cases. */
176 #endif /* SYSUTIMEH */
185 #ifdef BSD44 /* BSD 4.4 */
187 #define TIMESTAMP /* Can do file dates */
188 #endif /* TIMESTAMP */
189 #include <sys/time.h>
190 #include <sys/timeb.h>
192 #else /* Not BSD44 */
194 #ifdef BSD4 /* BSD 4.3 and below */
195 #define TIMESTAMP /* Can do file dates */
196 #include <time.h> /* Need this */
197 #include <sys/timeb.h> /* Need this if really BSD */
199 #else /* Not BSD 4.3 and below */
201 #ifdef SVORPOSIX /* System V or POSIX */
204 #endif /* TIMESTAMP */
207 /* void tzset(); (the "void" type upsets some compilers) */
211 /* ConvexOS 9.0, supposedly POSIX, has extern char *timezone(int,int) */
213 extern long timezone;
218 #endif /* SVORPOSIX */
224 #endif /* COHERENT */
226 /* Is `y' a leap year? */
227 #define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
229 /* Number of leap years from 1970 to `y' (not including `y' itself). */
230 #define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
232 #endif /* NOTIMESTAMP */
235 #include <stat.h> /* File status */
237 #include <sys/stat.h>
240 /* Macro to alleviate isdir() calls internal to this module */
242 static struct stat STATBUF;
243 #define xisdir(a) ((stat(a,&STATBUF)==-1)?0:(S_ISDIR(STATBUF.st_mode)?1:0))
245 extern char uidbuf[];
247 extern char * xferfile;
249 static time_t timenow;
251 #define IKSDMSGLEN CKMAXPATH+512
253 static char iksdmsg[IKSDMSGLEN];
257 extern int server, en_mkd, en_cwd, en_del;
260 Functions (n is one of the predefined file numbers from ckcker.h):
262 zopeni(n,name) -- Opens an existing file for input.
263 zopeno(n,name,attr,fcb) -- Opens a new file for output.
264 zclose(n) -- Closes a file.
265 zchin(n,&c) -- Gets the next character from an input file.
266 zsinl(n,&s,x) -- Read a line from file n, max len x, into address s.
267 zsout(n,s) -- Write a null-terminated string to output file, buffered.
268 zsoutl(n,s) -- Like zsout, but appends a line terminator.
269 zsoutx(n,s,x) -- Write x characters to output file, unbuffered.
270 zchout(n,c) -- Add a character to an output file, unbuffered.
271 zchki(name) -- Check if named file exists and is readable, return size.
272 zchko(name) -- Check if named file can be created.
273 zchkspa(name,n) -- Check if n bytes available to create new file, name.
274 znewn(name,s) -- Make a new unique file name based on the given name.
275 zdelet(name) -- Delete the named file.
276 zxpand(string) -- Expands the given wildcard string into a list of files.
277 znext(string) -- Returns the next file from the list in "string".
278 zxrewind() -- Rewind zxpand list.
279 zxcmd(n,cmd) -- Execute the command in a lower fork on file number n.
280 zclosf() -- Close input file associated with zxcmd()'s lower fork.
281 zrtol(n1,n2) -- Convert remote filename into local form.
282 zltor(n1,n2) -- Convert local filename into remote form.
283 zchdir(dirnam) -- Change working directory.
284 zhome() -- Return pointer to home directory name string.
285 zkself() -- Kill self, log out own job.
286 zsattr(struct zattr *) -- Return attributes for file which is being sent.
287 zstime(f, struct zattr *, x) - Set file creation date from attribute packet.
288 zrename(old, new) -- Rename a file.
289 zcopy(source,destination) -- Copy a file.
290 zmkdir(path) -- Create the directory path if possible
291 zfnqfp(fname,len,fullpath) - Determine full path for file name.
292 zgetfs(name) -- return file size regardless of accessibility
293 zchkpid(pid) -- tell if PID is valid and active
296 /* Kermit-specific includes */
298 Definitions here supersede those from system include files.
299 ckcdeb.h is included above.
301 #include "ckcker.h" /* Kermit definitions */
302 #include "ckucmd.h" /* For keyword tables */
303 #include "ckuver.h" /* Version herald */
305 char *ckzsys = HERALD;
308 File access checking ... There are two calls to access() in this module.
309 If this program is installed setuid or setgid on a Berkeley-based UNIX
310 system that does NOT incorporate the saved-original-effective-uid/gid
311 feature, then, when we have swapped the effective and original uid/gid,
312 access() fails because it uses what it thinks are the REAL ids, but we have
313 swapped them. This occurs on systems where ANYBSD is defined, NOSETREU
314 is NOT defined, and SAVEDUID is NOT defined. So, in theory, we should take
315 care of this situation like so:
325 But we can't test such a general scheme everywhere, so let's only do this
326 when we know we have to...
328 #ifdef NEXT /* NeXTSTEP 1.0-3.0 */
332 /* Support for tilde-expansion in file and directory names */
335 #define NAMEENV "LOGNAME"
338 #define NAMEENV "USER"
341 #define NAMEENV "LOGNAME"
346 /* Berkeley Unix Version 4.x */
347 /* 4.1bsd support from Charles E Brooks, EDN-VAX */
352 #endif /* MAXNAMLEN */
355 /* Definitions of some system commands */
357 char *DELCMD = "rm -f "; /* For file deletion */
358 char *CPYCMD = "cp "; /* For file copy */
359 char *RENCMD = "mv "; /* For file rename */
360 char *PWDCMD = "pwd "; /* For saying where I am */
364 char *DIRCMD = "/usr/bin/ls -l "; /* For directory listing */
365 char *DIRCM2 = "/usr/bin/ls -l "; /* For directory listing, no args */
367 char *DIRCMD = "/bin/ls -l "; /* For directory listing */
368 char *DIRCM2 = "/bin/ls -l "; /* For directory listing, no args */
371 char *DIRCMD = "ls -l "; /* For directory listing */
372 char *DIRCM2 = "ls -l "; /* For directory listing, no args */
375 char *TYPCMD = "cat "; /* For typing a file */
378 char *MAILCMD = "mailx"; /* For sending mail */
381 char *MAILCMD = "mailx";
385 char *MAILCMD = CK_MAILCMD; /* CFLAGS override */
387 char *MAILCMD = "Mail"; /* Default */
388 #endif /* CK_MAILCMD */
396 #ifdef ANYBSD /* BSD uses lpr to spool */
397 #ifdef DGUX540 /* And DG/UX */
398 char * PRINTCMD = "lp";
400 char * PRINTCMD = "lpr";
402 #else /* Sys V uses lp */
403 #ifdef TRS16 /* except for Tandy-16/6000... */
404 char * PRINTCMD = "lpr";
406 char * PRINTCMD = "lp";
413 #ifdef FT18 /* Fortune For:Pro 1.8 */
418 char *SPACMD = "pwd ; df ."; /* Space in current directory */
421 char *SPACMD = "pwd ; du ; df .";
423 char *SPACMD = "df ";
427 char *SPACM2 = "df "; /* For space in specified directory */
434 char *WHOCMD = "finger ";
436 char *WHOCMD = "who ";
439 /* More system-dependent includes, which depend on symbols defined */
440 /* in the Kermit-specific includes. Oh what a tangled web we weave... */
442 #ifdef COHERENT /* <sys/file.h> */
444 #endif /* COHERENT */
459 #include <sys/file.h>
462 #ifndef is68k /* Whether to include <fcntl.h> */
463 #ifndef BSD41 /* All but a couple UNIXes have it. */
467 #endif /* COHERENT */
476 #include <sys/fcntl.h>
478 #endif /* COHERENT */
480 extern int inserver; /* I am IKSD */
481 int guest = 0; /* Anonymous user */
485 extern char * anonroot;
489 #define GUESTPASS 256
490 static char guestpass[GUESTPASS] = { NUL, NUL }; /* Anonymous "password" */
491 static int logged_in = 0; /* Set when user is logged in */
492 static int askpasswd = 0; /* Have OK user, must ask for passwd */
493 #endif /* CK_LOGIN */
496 static char ckroot[CKMAXPATH+1] = { NUL, NUL };
497 static int ckrootset = 0;
501 _PROTOTYP( VOID ignorsigs, (void) );
502 _PROTOTYP( VOID restorsigs, (void) );
505 Change argument to "(const char *)" if this causes trouble.
506 Or... if it causes trouble, then maybe it was already declared
507 in a header file after all, so you can remove this prototype.
509 #ifndef NDGPWNAM /* If not defined No Declare getpwnam... */
510 #ifndef _POSIX_SOURCE
513 /* POSIX <pwd.h> already gave prototypes for these. */
515 _PROTOTYP( struct passwd * getpwnam, (const char *) );
518 _PROTOTYP( struct passwd * getpwnam, (const char *) );
521 _PROTOTYP( struct passwd * getpwnam, (const char *) );
524 _PROTOTYP( struct passwd * getpwnam, (const char *) );
527 _PROTOTYP( struct passwd * getpwnam, (const char *) );
530 _PROTOTYP( struct passwd * getpwnam, (const char *) );
532 _PROTOTYP( struct passwd * getpwnam, (char *) );
533 #endif /* DCGPWNAM */
543 _PROTOTYP( struct passwd * getpwuid, (PWID_T) );
548 _PROTOTYP( struct passwd * getpwent, (void) );
551 #endif /* _POSIX_SOURCE */
552 #endif /* NDGPWNAM */
554 #ifdef CK_SHADOW /* Shadow Passwords... */
556 #endif /* CK_SHADOW */
557 #ifdef CK_PAM /* PAM... */
558 #include <security/pam_appl.h>
559 #ifndef PAM_SERVICE_TYPE /* Defines which PAM service we are */
560 #define PAM_SERVICE_TYPE "kermit"
561 #endif /* PAM_SERVICE_TYPE */
566 #define PAM_CONST CONST
569 static char * pam_pw = NULL;
574 PAM_CONST struct pam_message **msg,
575 struct pam_response **resp,
579 pam_cb(num_msg, msg, resp, appdata_ptr)
581 PAM_CONST struct pam_message **msg;
582 struct pam_response **resp;
584 #endif /* CK_ANSIC */
588 debug(F111,"pam_cb","num_msg",num_msg);
590 for (i = 0; i < num_msg; i++) {
591 char message[PAM_MAX_MSG_SIZE];
593 /* Issue prompt and get response */
594 debug(F111,"pam_cb","Message",i);
595 debug(F111,"pam_cb",msg[i]->msg,msg[i]->msg_style);
596 if (msg[i]->msg_style == PAM_ERROR_MSG) {
597 debug(F111,"pam_cb","PAM ERROR",0);
598 fprintf(stdout,"%s\n", msg[i]->msg);
600 } else if (msg[i]->msg_style == PAM_TEXT_INFO) {
601 debug(F111,"pam_cb","PAM TEXT INFO",0);
602 fprintf(stdout,"%s\n", msg[i]->msg);
604 } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) {
605 debug(F111,"pam_cb","Reading response, no echo",0);
606 /* Ugly hack. We check to see if a password has been pushed */
607 /* into zvpasswd(). This would be true if the password was */
608 /* received by REMOTE LOGIN. */
610 ckstrncpy(message,pam_pw,PAM_MAX_MSG_SIZE);
612 readpass((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
613 } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON) {
614 debug(F111,"pam_cb","Reading response, with echo",0);
615 readtext((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
617 debug(F111,"pam_cb","unknown style",0);
621 /* Allocate space for this message's response structure */
622 resp[i] = (struct pam_response *) malloc(sizeof (struct pam_response));
625 debug(F110,"pam_cb","malloc failure",0);
626 for (j = 0; j < i; j++) {
633 /* Allocate a buffer for the response */
634 resp[i]->resp = (char *) malloc((int)strlen(message) + 1);
635 if (!resp[i]->resp) {
637 debug(F110,"pam_cb","malloc failure",0);
638 for (j = 0; j < i; j++) {
645 /* Return the results back to PAM */
646 strcpy(resp[i]->resp, message); /* safe (prechecked) */
647 resp[i]->resp_retcode = 0;
649 debug(F110,"pam_cb","Exiting",0);
654 /* Define macros for getting file type */
658 Olivetti X/OS 2.3 has S_ISREG and S_ISDIR defined
659 incorrectly, so we force their redefinition.
665 #ifdef UTSV /* Same deal for Amdahl UTSV */
670 #ifdef UNISYS52 /* And for UNISYS UTS V 5.2 */
673 #endif /* UNISYS52 */
675 #ifdef ICLSVR3 /* And for old ICL versions */
680 #ifdef ISDIRBUG /* Also allow this from command line */
687 #endif /* ISDIRBUG */
693 #define _IFMT 0170000
698 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
701 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
704 /* The following mainly for NeXTSTEP... */
707 #define S_IWUSR 0000200
711 #define S_IRGRP 0000040
715 #define S_IWGRP 0000020
719 #define S_IXGRP 0000010
723 #define S_IROTH 0000004
727 #define S_IWOTH 0000002
731 #define S_IXOTH 0000001
734 Define maximum length for a file name if not already defined.
735 NOTE: This applies to a path segment (directory or file name),
736 not the entire path string, which can be CKMAXPATH bytes long.
740 #define MAXNAMLEN _MAX_FNAME
743 #endif /* _MAX_FNAME */
747 #define MAXNAMLEN 255
750 #define MAXNAMLEN FILENAME_MAX
753 #define MAXNAMLEN NAME_MAX
755 #ifdef _POSIX_NAME_MAX
756 #define MAXNAMLEN _POSIX_NAME_MAX
759 #define MAXNAMLEN _D_NAME_MAX
762 #define MAXNAMLEN DIRSIZ
766 #endif /* _D_NAME_MAX */
767 #endif /* _POSIX_NAME_MAX */
768 #endif /* NAME_MAX */
769 #endif /* FILENAME_MAX */
771 #endif /* MAXNAMLEN */
775 /* As of 2001-11-03 this is handled in ckcdeb.h */
776 /* Longest pathname ... */
778 Beware: MAXPATHLEN is one of UNIX's dirty little secrets. Where is it
779 defined? Who knows... <param.h>, <mod.h>, <unistd.h>, <limits.h>, ...
780 There is not necessarily even a definition for it anywhere, or it might have
781 another name. If you get it wrong, bad things happen with getcwd() and/or
782 getwd(). If you allocate a buffer that is too short, getwd() might write
783 over memory and getcwd() will fail with ERANGE. The definitions of these
784 functions (e.g. in SVID or POSIX.1) do not tell you how to determine the
785 maximum path length in order to allocate a buffer that is the right size.
788 #include <sys/param.h> /* For MAXPATHLEN */
791 #include <sys/param.h> /* for MAXPATHLEN, needed for -DDIRENT */
792 #endif /* COHERENT */
799 #define MAXPATH MAXPATHLEN
802 #define MAXPATH PATH_MAX
804 #ifdef _POSIX_PATH_MAX
805 #define MAXPATH _POSIX_PATH_MAX
816 #endif /* _POSIX_PATH_MAX */
817 #endif /* PATH_MAX */
818 #endif /* MAXPATHLEN */
820 /* Maximum number of filenames for wildcard expansion */
823 /* Already defined in ckcdeb.h so the following is superfluous. */
824 /* Don't expect changing them to have any effect. */
829 #define MAXWLD 102400
832 #endif /* BIGBUFOK */
833 #endif /* CK_SMALL */
836 static int maxnames = MAXWLD;
838 /* Define the size of the string space for filename expansion. */
851 #define SSPACE 10000 /* Size of string-generating buffer */
852 #else /* Default static buffer size */
854 #define SSPACE 65000 /* Size of string-generating buffer */
856 #define SSPACE 2000 /* size of string-generating buffer */
857 #endif /* BIGBUFOK */
862 static char sspace[SSPACE]; /* Buffer for generating filenames */
863 #else /* is DYNAMIC */
865 #define SSPACE 500000
868 #endif /* BIGBUFOK */
869 char *sspace = (char *)0;
871 static int ssplen = SSPACE; /* Length of string space buffer */
874 /* fdopen() needs declaring because it's not declared in <stdio.h> */
875 _PROTOTYP( FILE * fdopen, (int, char *) );
876 #endif /* DCLFDOPEN */
879 /* popen() needs declaring because it's not declared in <stdio.h> */
880 _PROTOTYP( FILE * popen, (char *, char *) );
881 #endif /* DCLPOPEN */
885 /* More internal function prototypes */
887 * The path structure is used to represent the name to match.
888 * Each slash-separated segment of the name is kept in one
889 * such structure, and they are linked together, to make
890 * traversing the name easier.
893 char npart[MAXNAMLEN+4]; /* name part of path segment */
894 struct path *fwd; /* forward ptr */
897 _PROTOTYP( int shxpand, (char *, char *[], int ) );
899 _PROTOTYP( static int fgen, (char *, char *[], int ) );
900 _PROTOTYP( static VOID traverse, (struct path *, char *, char *) );
901 _PROTOTYP( static VOID addresult, (char *, int) );
903 /* Replaced by ckmatch() */
904 _PROTOTYP( static int match, (char *, char *) );
906 _PROTOTYP( char * whoami, (void) );
907 _PROTOTYP( UID_T real_uid, (void) );
908 _PROTOTYP( static struct path *splitpath, (char *p) );
909 _PROTOTYP( char * zdtstr, (time_t) );
910 _PROTOTYP( time_t zstrdt, (char *, int) );
912 /* Some systems define these symbols in include files, others don't... */
915 #define R_OK 4 /* For access */
928 #endif /* O_RDONLY */
930 /* syslog and wtmp items for Internet Kermit Service */
932 extern char * clienthost; /* From ckcmai.c. */
934 static char fullname[CKMAXPATH+1];
935 static char tmp2[CKMAXPATH+1];
937 extern int ckxlogging;
939 #ifdef CKXPRINTF /* Our printf macro conflicts with */
940 #undef printf /* use of "printf" in syslog.h */
941 #endif /* CKXPRINTF */
944 #include <sys/syslog.h>
948 #endif /* CKSYSLOG */
950 #define printf ckxprintf
951 #endif /* CKXPRINTF */
953 int ckxanon = 1; /* Anonymous login ok */
954 int ckxperms = 0040; /* Anonymous file permissions */
955 int ckxpriv = 1; /* Priv'd login ok */
958 #define XFERFILE "/var/log/iksd.log"
959 #endif /* XFERFILE */
961 /* wtmp logging for IKSD... */
963 #ifndef CKWTMP /* wtmp logging not selected */
964 int ckxwtmp = 0; /* Know this at runtime */
965 #else /* wtmp file details */
967 #ifdef UTMPBUG /* Unfortunately... */
969 Some versions of Linux have a <utmp.h> file that contains
970 "enum utlogin { local, telnet, rlogin, screen, ... };" This clobbers
971 any program that uses any of these words as variable names, function
972 names, macro names, etc. (Other versions of Linux have this declaration
973 within #if 0 ... #endif.) There is nothing we can do about this other
974 than to not include the stupid file. But we need stuff from it, so...
976 #include <features.h>
977 #include <sys/types.h>
978 #define UT_LINESIZE 32
979 #define UT_NAMESIZE 32
980 #define UT_HOSTSIZE 256
988 short int e_termination; /* Process termination status. */
989 short int e_exit; /* Process exit status. */
993 short int ut_type; /* Type of login */
994 pid_t ut_pid; /* Pid of login process */
995 char ut_line[UT_LINESIZE]; /* NUL-terminated devicename of tty */
996 char ut_id[4]; /* Inittab id */
997 char ut_user[UT_NAMESIZE]; /* Username (not NUL terminated) */
999 char ut_host[UT_HOSTSIZE]; /* Hostname for remote login */
1000 struct exit_status ut_exit; /* Exit status */
1001 long ut_session; /* Session ID, used for windowing */
1002 struct timeval ut_tv; /* Time entry was made */
1003 int32_t ut_addr_v6[4]; /* Internet address of remote host */
1004 char pad[20]; /* Reserved */
1007 #define ut_time ut_tv.tv_sec /* Why should Linux be like anything else? */
1008 #define ut_name ut_user /* ... */
1011 logwtmp __P ((__const char *__ut_line, __const char *__ut_name,
1012 __const char *__ut_host));
1014 #else /* Not UTMPBUG */
1016 #ifndef HAVEUTMPX /* Who has <utmpx.h> */
1031 #endif /* UNIXWARE */
1032 #endif /* HPUX100 */
1033 #endif /* CK_SCOV5 */
1035 #endif /* SOLARIS */
1036 #endif /* HAVEUTMPX */
1041 /* Because the time_t in the utmp struct is 64 bits but time() wants 32 */
1042 #define __V40_OBJ_COMPAT 1
1046 #undef __V40_OBJ_COMPAT
1048 #endif /* HAVEUTMPX */
1049 #endif /* UTMPBUG */
1053 #define WTMPFILE "/usr/adm/wtmp.1"
1056 #define WTMPFILE "/var/log/wtmp"
1058 #define WTMPFILE "/usr/adm/wtmp"
1061 #endif /* WTMPFILE */
1062 char * wtmpfile = NULL;
1064 static int wtmpfd = 0;
1065 static char cksysline[32] = { NUL, NUL };
1067 #ifndef HAVEUTHOST /* Does utmp include ut_host[]? */
1068 #ifdef HAVEUTMPX /* utmpx always does */
1071 #ifdef LINUX /* Linux does */
1074 #ifdef SUNOS4 /* SunOS does */
1077 #ifdef AIX41 /* AIX 4.1 and later do */
1082 #endif /* HAVEUTMPX */
1083 #endif /* HAVEUTHOST */
1086 PID_T _vfork() { /* To satisfy a library foulup */
1087 return(fork()); /* in Unixware 2.0.x */
1093 logwtmp(const char * line, const char * name, const char * host)
1095 logwtmp(line, name, host) char *line, *name, *host;
1096 #endif /* CK_ANSIC */
1099 struct utmpx ut; /* Needed for ut_host[] */
1102 #endif /* HAVEUTMPX */
1104 /* time_t time(); */
1110 makestr(&wtmpfile,WTMPFILE);
1112 if (!line) line = "";
1113 if (!name) name = "";
1114 if (!host) host = "";
1116 if (!wtmpfd && (wtmpfd = open(wtmpfile, O_WRONLY|O_APPEND, 0)) < 0) {
1118 debug(F110,"WTMP open failed",line,0);
1121 if (!fstat(wtmpfd, &buf)) {
1122 ckstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
1123 ckstrncpy(ut.ut_name, name, sizeof(ut.ut_name));
1126 ckstrncpy(ut.ut_host, host, sizeof(ut.ut_host));
1127 #endif /* HAVEUTHOST */
1129 time(&ut.ut_tv.tv_sec);
1132 /* In light of the following comment perhaps the previous line should */
1133 /* be "#ifndef COMMENT". */
1136 * On 64-bit platforms sizeof(time_t) and sizeof(ut.ut_time)
1137 * are not the same and attempt to use an address of
1138 * ut.ut_time as an argument to time() call may cause
1139 * "unaligned access" trap.
1148 #endif /* HAVEUTMPX */
1149 if (write(wtmpfd, (char *)&ut, sizeof(struct utmp)) !=
1150 sizeof(struct utmp)) {
1153 ftruncate(wtmpfd, buf.st_size); /* Error, undo any partial write */
1155 chsize(wtmpfd, buf.st_size); /* Error, undo any partial write */
1156 #endif /* COHERENT */
1157 #endif /* NOFTRUNCATE */
1158 debug(F110,"WTMP write error",line,0);
1160 debug(F110,"WTMP record OK",line,0);
1169 C K S Y S L O G -- C-Kermit system logging function,
1171 For use by other modules.
1172 This module can, but doesn't have to, use it.
1174 n = SYSLG_xx values defined in ckcdeb.h
1175 s1, s2, s3: strings.
1178 cksyslog(n, m, s1, s2, s3) int n, m; char * s1, * s2, * s3; {
1181 if (!ckxlogging) /* syslogging */
1183 if (!s1) s1 = ""; /* Fix null args */
1186 switch (n) { /* Translate Kermit level */
1187 case SYSLG_DB: /* to syslog level */
1191 level = m ? LOG_INFO : LOG_ERR;
1193 debug(F110,"cksyslog s1",s1,0);
1194 debug(F110,"cksyslog s2",s2,0);
1195 debug(F110,"cksyslog s3",s3,0);
1197 syslog(level, "%s: %s %s", s1, s2, s3); /* Write syslog record */
1198 debug(F101,"cksyslog errno","",errno);
1200 #endif /* CKSYSLOG */
1205 int maxnam = MAXNAMLEN; /* Available to the outside */
1206 int maxpath = MAXPATH;
1210 char startupdir[MAXPATH+1];
1213 int pexitstat = -2; /* Process exit status */
1215 FILE *fp[ZNFILS] = { /* File pointers */
1216 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
1219 /* Flags for each file indicating whether it was opened with popen() */
1220 int ispipe[ZNFILS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1222 /* Buffers and pointers used in buffered file input and output. */
1224 extern char *zinbuffer, *zoutbuffer;
1226 extern char zinbuffer[], zoutbuffer[];
1227 #endif /* DYNAMIC */
1228 extern char *zinptr, *zoutptr;
1229 extern int zincnt, zoutcnt;
1230 extern int wildxpand;
1232 static long iflen = -1L; /* Input file length */
1234 static PID_T pid = 0; /* pid of child fork */
1235 static int fcount = 0; /* Number of files in wild group */
1236 static int nxpand = 0; /* Copy of fcount */
1237 static char nambuf[CKMAXPATH+4]; /* Buffer for a pathname */
1240 #define ZMBUFLEN 200
1241 static char zmbuf[ZMBUFLEN]; /* For mail, remote print strings */
1242 #endif /* NOFRILLS */
1244 char **mtchs = NULL; /* Matches found for filename */
1245 char **mtchptr = NULL; /* Pointer to current match */
1247 /* Z K S E L F -- Kill Self: log out own job, if possible. */
1249 /* Note, should get current pid, but if your system doesn't have */
1250 /* getppid(), then just kill(0,9)... */
1255 /* Already declared in unistd.h for SVR3 and POSIX */
1257 extern PID_T getppid(void);
1261 extern PID_T getppid();
1262 #endif /* COHERENT */
1263 #endif /* PS2AIX10 */
1264 #endif /* CK_ANSIC */
1270 zkself() { /* For "bye", but no guarantee! */
1287 return(kill((PID_T)getpid(),1));
1290 exit(kill((PID_T)getppid(),1));
1293 exit(kill(getppid(),1));
1305 getfullname(name) char * name; {
1306 char *p = (char *)fullname;
1309 /* If necessary we could also chase down symlinks here... */
1311 /* This works but is incompatible with wuftpd */
1312 if (isguest && anonroot) {
1313 ckstrncpy(fullname,anonroot,CKMAXPATH);
1314 len = strlen(fullname);
1316 if (fullname[len-1] == '/')
1320 #endif /* COMMENT */
1321 zfnqfp(name, CKMAXPATH - len, p);
1323 if (*p < '!') *p = '_';
1328 /* D O I K L O G -- Open Kermit-specific ftp-like transfer log. */
1330 VOID /* Called in ckcmai.c */
1332 if (iklogopen) /* Already open? */
1334 if (xferlog) { /* Open iksd log if requested */
1335 if (!xferfile) /* If no pathname given */
1336 makestr(&xferfile,XFERFILE); /* use this default */
1338 xferlog = open(xferfile, O_WRONLY | O_APPEND | O_CREAT, 0660);
1339 debug(F101,"doiklog open","",xferlog);
1342 syslog(LOG_ERR, "xferlog open failure %s: %m", xferfile);
1343 #endif /* CKSYSLOG */
1344 debug(F101,"doiklog open errno","",errno);
1351 if (xferlog && ckxlogging)
1352 syslog(LOG_INFO, "xferlog: %s open ok", xferfile);
1353 #endif /* CKSYSLOG */
1357 /* Z O P E N I -- Open an existing file for input. */
1359 /* Returns 1 on success, 0 on failure */
1362 zopeni(n,name) int n; char *name; {
1365 debug(F111,"zopeni",name,n);
1366 if ((x = chkfn(n)) != 0) {
1367 debug(F111,"zopeni chkfn",ckitoa(n),x);
1370 zincnt = 0; /* Reset input buffer */
1371 if (n == ZSYSFN) { /* Input from a system function? */
1373 /*** Note, this function should not be called with ZSYSFN ***/
1374 /*** Always call zxcmd() directly, and give it the real file number ***/
1375 /*** you want to use. ***/
1376 return(zxcmd(n,name)); /* Try to fork the command */
1378 debug(F110,"zopeni called with ZSYSFN, failing!",name,0);
1379 *nambuf = '\0'; /* No filename. */
1380 return(0); /* fail. */
1381 #endif /* COMMENT */
1383 if (n == ZSTDIO) { /* Standard input? */
1385 fprintf(stderr,"Terminal input not allowed");
1386 debug(F110,"zopeni: attempts input from unredirected stdin","",0);
1394 debug(F111,"zopeni setroot",ckroot,ckrootset);
1395 if (ckrootset) if (!zinroot(name)) {
1396 debug(F110,"zopeni setroot violation",name,0);
1400 fp[n] = fopen(name,"r"); /* Real file, open it. */
1401 debug(F111,"zopeni fopen", name, fp[n]);
1403 printf("ZOPENI fp[%d]=%ld\n",n,fp[n]);
1409 || ((ckxsyslog >= SYSLG_FA) && ckxlogging)
1410 #endif /* CKSYSLOG */
1413 debug(F110,"zopeni fullname",fullname,0);
1415 if (fp[n] == NULL) {
1417 if (ckxsyslog >= SYSLG_FA && ckxlogging) {
1418 syslog(LOG_INFO, "file[%d] %s: open failed (%m)", n, fullname);
1421 #endif /* CKSYSLOG */
1426 if (ckxsyslog >= SYSLG_FA && ckxlogging)
1427 syslog(LOG_INFO, "file[%d] %s: open read ok", n, fullname);
1428 #endif /* CKSYSLOG */
1439 #endif /* O_NDELAY */
1442 /* Z O P E N O -- Open a new file for output. */
1444 /*ARGSUSED*/ /* zz not used */
1446 zopeno(n,name,zz,fcb)
1447 /* zopeno */ int n; char *name; struct zattr *zz; struct filinfo *fcb; {
1452 /* As of Version 5A, the attribute structure and the file information */
1453 /* structure are included in the arglist. */
1456 debug(F111,"zopeno",name,n);
1458 debug(F101,"zopeno fcb disp","",fcb->dsp);
1459 debug(F101,"zopeno fcb type","",fcb->typ);
1460 debug(F101,"zopeno fcb char","",fcb->cs);
1462 debug(F100,"zopeno fcb is NULL","",0);
1466 if (chkfn(n) != 0) /* Already open? */
1467 return(0); /* Nothing to do. */
1469 if ((n == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */
1470 fp[ZOFILE] = stdout;
1473 /* This seems right but it breaks client server ops */
1476 #endif /* COMMENT */
1479 debug(F101,"zopeno fp[n]=stdout","",fp[n]);
1482 zoutptr = zoutbuffer;
1486 /* A real file. Open it in desired mode (create or append). */
1489 debug(F111,"zopeno setroot",ckroot,ckrootset);
1490 if (ckrootset) if (!zinroot(name)) {
1491 debug(F110,"zopeno setroot violation",name,0);
1496 ckstrncpy(p,"w",8); /* Assume write/create mode */
1497 if (fcb) { /* If called with an FCB... */
1498 if (fcb->dsp == XYFZ_A) { /* Does it say Append? */
1499 ckstrncpy(p,"a",8); /* Yes. */
1500 debug(F100,"zopeno append","",0);
1507 || ((ckxsyslog >= SYSLG_FC) && ckxlogging)
1508 #endif /* CKSYSLOG */
1511 debug(F110,"zopeno fullname",fullname,0);
1513 debug(F110,"zopeno fopen arg",p,0);
1514 fp[n] = fopen(name,p); /* Try to open the file */
1518 printf("ZOPENO fp[%d]=%ld\n",n,fp[n]);
1521 if (fp[n] == NULL) { /* Failed */
1522 debug(F101,"zopeno failed errno","",errno);
1524 if (ckxsyslog >= SYSLG_FC && ckxlogging)
1525 syslog(LOG_INFO, "file[%d] %s: %s failed (%m)",
1528 append ? "append" : "create"
1530 #endif /* CKSYSLOG */
1531 #ifdef COMMENT /* Let upper levels print message. */
1532 perror("Can't open output file");
1533 #endif /* COMMENT */
1534 } else { /* Succeeded */
1535 extern int zofbuffer, zofblock, zobufsize;
1536 debug(F101, "zopeno zobufsize", "", zobufsize);
1537 if (n == ZDFILE || n == ZTFILE) { /* If debug or transaction log */
1538 setbuf(fp[n],NULL); /* make it unbuffered. */
1540 } else if (n == ZOFILE && !zofblock) { /* blocking or nonblocking */
1542 if ((flags = fcntl(fileno(fp[n]),F_GETFL,0)) > -1)
1543 fcntl(fileno(fp[n]),F_SETFL, flags |
1550 debug(F100,"zopeno ZOFILE nonblocking","",0);
1551 #endif /* DONDELAY */
1552 } else if (n == ZOFILE && !zofbuffer) { /* buffered or unbuffered */
1554 debug(F100,"zopeno ZOFILE unbuffered","",0);
1558 /* Enforce anonymous file-creation permission */
1560 if (n == ZWFILE || n == ZMFILE ||
1561 n == ZOFILE || n == ZDFILE ||
1562 n == ZTFILE || n == ZPFILE ||
1564 chmod(name,ckxperms);
1565 #endif /* CK_LOGIN */
1567 if (ckxsyslog >= SYSLG_FC && ckxlogging)
1568 syslog(LOG_INFO, "file[%d] %s: %s ok",
1571 append ? "append" : "create"
1573 #endif /* CKSYSLOG */
1574 debug(F100, "zopeno ok", "", 0);
1576 zoutcnt = 0; /* (PWP) reset output buffer */
1577 zoutptr = zoutbuffer;
1578 return((fp[n] != NULL) ? 1 : 0);
1581 /* Z C L O S E -- Close the given file. */
1583 /* Returns 0 if arg out of range, 1 if successful, -1 if close failed. */
1590 debug(F101,"zclose file number","",n);
1591 if (chkfn(n) < 1) return(0); /* Check range of n */
1592 if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */
1595 if (fp[ZSYSFN] || ispipe[n]) { /* If file is really pipe */
1597 x = zclosf(n); /* do it specially */
1601 debug(F101,"zclose zclosf","",x);
1602 debug(F101,"zclose zclosf fp[n]","",fp[n]);
1604 if ((fp[n] != stdout) && (fp[n] != stdin))
1608 if (n == ZCTERM || n == ZSTDIO) /* See zopeno() */
1609 if (fp[ZOFILE] == stdout)
1611 #endif /* COMMENT */
1613 iflen = -1L; /* Invalidate file length */
1614 if (x == EOF) { /* if we got a close error */
1615 debug(F101,"zclose fclose fails","",x);
1617 } else if (x2 < 0) { /* or error flushing last buffer */
1618 debug(F101,"zclose error flushing last buffer","",x2);
1619 return(-1); /* then return an error */
1621 /* Print log record compatible with wu-ftpd */
1622 if (xferlog && (n == ZIFILE || n == ZOFILE)) {
1624 extern char ttname[];
1625 if (!iklogopen) (VOID) doiklog(); /* Open log if necessary */
1626 debug(F101,"zclose iklogopen","",iklogopen);
1631 timenow = time(NULL);
1636 #endif /* CK_LOGIN */
1645 #endif /* CK_LOGIN */
1648 len = 24 + 12 + (int)strlen(s) + 16
1649 + (int)strlen(fullname) + 1 + 1 + 1 + 1
1650 + (int)strlen(p) + 6 + 2 + 12;
1652 if (!*fnam) fnam = "(pipe)";
1654 if (len > IKSDMSGLEN)
1655 sprintf(iksdmsg, /* SAFE */
1656 "%.24s [BUFFER WOULD OVERFLOW]\n",ctime(&timenow));
1658 sprintf(iksdmsg, /* SAFE */
1659 "%.24s %d %s %ld %s %c %s %c %c %s %s %d %s\n",
1660 ctime(&timenow), /* date/time */
1661 gtimer(), /* elapsed secs */
1663 ffc, /* byte count */
1664 fnam, /* full pathname of file */
1665 (binary ? 'b' : 'a'), /* binary or ascii */
1666 "_", /* options = none */
1667 n == ZIFILE ? 'o' : 'i', /* in/out */
1669 (isguest ? 'a' : 'r'), /* User type */
1672 #endif /* CK_LOGIN */
1673 p, /* Username or guest passwd */
1675 logged_in ? "iks" : "kermit", /* Record ID */
1678 #endif /* CK_LOGIN */
1679 0, /* User ID on client system unknown */
1682 debug(F110,"zclose iksdmsg",iksdmsg,0);
1683 write(xferlog, iksdmsg, (int)strlen(iksdmsg));
1686 debug(F101,"zclose returns","",1);
1691 /* Z C H I N -- Get a character from the input file. */
1693 /* Returns -1 if EOF, 0 otherwise with character returned in argument */
1696 zchin(n,c) int n; int *c; {
1700 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1706 /* (PWP) Just in case this gets called when it shouldn't. */
1708 a = zminchar(); /* Note: this catches Ctrl-Z */
1709 if (a < 0) /* (See zinfill()...) */
1713 if (a == EOF) return(-1);
1715 /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
1716 if (!binary && a == 0x1A && eofmethod == XYEOF_Z)
1718 #endif /* CK_CTRLZ */
1720 *c = (CHAR) a & 0377;
1724 /* Z S I N L -- Read a line from a file */
1727 Writes the line into the address provided by the caller.
1728 n is the Kermit "channel number".
1729 Writing terminates when newline is encountered, newline is not copied.
1730 Writing also terminates upon EOF or if length x is exhausted.
1731 Returns 0 on success, -1 on EOF or error.
1734 zsinl(n,s,x) int n, x; char *s; {
1735 int a, z = 0; /* z is return code. */
1739 extern CHAR feol; /* Line terminator */
1741 if (!s || chkfn(n) < 1) /* Make sure file is open, etc */
1744 s[0] = '\0'; /* Don't return junk */
1746 a = -1; /* Current character, none yet. */
1747 while (x--) { /* Up to given length */
1749 if (feol) /* Previous character */
1751 if (zchin(n,&a) < 0) { /* Read a character from the file */
1752 debug(F101,"zsinl zchin fail","",count);
1754 z = -1; /* EOF or other error */
1758 if (feol) { /* Single-character line terminator */
1761 } else { /* CRLF line terminator */
1762 if (a == '\015') /* CR, get next character */
1764 if (old == '\015') { /* Previous character was CR */
1765 if (a == '\012') { /* This one is LF, so we have a line */
1767 } else { /* Not LF, deposit CR */
1774 *s = a; /* Deposit character */
1778 *s = '\0'; /* Terminate the string */
1779 debug(F011,"zsinl",buf,len);
1783 /* Z X I N -- Read x bytes from a file */
1786 Reads x bytes (or less) from channel n and writes them
1787 to the address provided by the caller.
1788 Returns number of bytes read on success, 0 on EOF or error.
1791 zxin(n,s,x) int n, x; char *s; {
1793 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1796 if (a < 1) return(0);
1797 for (i = 0; i < a && i < x; i++)
1803 return(fread(s, sizeof (char), x, fp[n]));
1807 Z I N F I L L -- Buffered file input.
1809 (re)fill the file input buffer with data. All file input
1810 should go through this routine, usually by calling the zminchar()
1811 macro defined in ckcker.h. Returns:
1813 Value 0..255 on success, the character that was read.
1815 -2 on any kind of error other than end of file.
1816 -3 timeout when reading from pipe (Kermit packet mode only).
1820 extern int kactive, srvping;
1824 printf("ZINFILL fp[%d]=%ld\n",ZIFILE,fp[ZIFILE]);
1828 if (inserver && !local && fp[ZIFILE] == stdin) {
1831 if (a < 0) return(-2);
1832 for (i = 0; i < a && i < INBUFSIZE; i++) {
1833 zinbuffer[i] = coninc(0);
1836 /* set pointer to beginning, (== &zinbuffer[0]) */
1838 if (zincnt == 0) return(-1);
1839 zincnt--; /* One less char in buffer */
1840 return((int)(*zinptr++) & 0377); /* because we return the first */
1844 debug(F101,"zinfill kactive","",kactive);
1846 if (!(kactive && ispipe[ZIFILE])) {
1847 if (feof(fp[ZIFILE])) {
1848 debug(F100,"ZINFILL feof","",0);
1850 printf("ZINFILL EOF\n");
1855 clearerr(fp[ZIFILE]);
1858 /* Here we can call select() to get a timeout... */
1859 if (kactive && ispipe[ZIFILE]) {
1864 debug(F101,"zinfill calling ttwait","",secs);
1865 z = ttwait(fileno(fp[ZIFILE]),secs);
1866 debug(F101,"zinfill ttwait","",z);
1877 debug(F101,"ZINFILL INBUFSIZE","",INBUFSIZE);
1879 memset(zinbuffer, 0xFF, INBUFSIZE);
1881 for (i = 0; i < INBUFSIZE; i++) {
1882 zinbuffer[i] = 0xFF;
1883 #ifdef COMMENT /* Too much! */
1884 debug(F101,"ZINFILL zinbuffer[i]","",i);
1885 #endif /* COMMENT */
1887 #endif /* USE_MEMCPY */
1888 ckstrncpy(zinbuffer,"zinbuffer is a valid buffer",INBUFSIZE);
1889 debug(F111,"ZINFILL about to call fread",zinbuffer,zinbuffer);
1894 Note: The following read MUST be nonblocking when reading from a pipe
1895 and we want timeouts to work. See zxcmd().
1897 zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]);
1898 debug(F101,"ZINFILL fread","",zincnt); /* Just the size */
1900 printf("FREAD=%d\n",zincnt);
1903 /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
1904 if (zincnt > 0 && !binary && eofmethod == XYEOF_Z) {
1906 for (i = 0; i < zincnt; i++) {
1907 if (zinbuffer[i] == SUB) {
1908 zincnt = i; /* Stop at first Ctrl-Z */
1915 #endif /* CK_CTRLZ */
1917 if (zincnt == 0) { /* Got nothing? */
1918 if (ferror(fp[ZIFILE])) {
1919 debug(F100,"ZINFILL ferror","",0);
1920 debug(F101,"ZINFILL errno","",errno);
1922 printf("ZINFILL errno=%d\n",errno);
1925 return((errno == EWOULDBLOCK) ? -3 : -2);
1928 #endif /* EWOULDBLOCK */
1931 /* In case feof() didn't work just above -- sometimes it doesn't... */
1933 if (feof(fp[ZIFILE]) ) {
1934 debug(F100,"ZINFILL count 0 EOF return -1","",0);
1937 debug(F100,"ZINFILL count 0 not EOF return -2","",0);
1941 zinptr = zinbuffer; /* set pointer to beginning, (== &zinbuffer[0]) */
1942 zincnt--; /* One less char in buffer */
1943 return((int)(*zinptr++) & 0377); /* because we return the first */
1946 /* Z S O U T -- Write a string out to the given file, buffered. */
1949 zsout(n,s) int n; char *s; {
1952 if (rc < 1) return(-1); /* Keep this, prevents memory faults */
1953 if (!s) return(0); /* Null pointer, do nothing, succeed */
1954 if (!*s) return(0); /* empty string, ditto */
1958 This happens with client-side Kermit server when a REMOTE command
1959 was sent from the server to the client and the server is supposed to
1960 display the text, but of course there is no place to display it
1961 since it is in remote mode executing Kermit protocol.
1963 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1965 return(ttol(s,((int)strlen(s)) < 0) ? -1 : 0);
1968 #endif /* COMMENT */
1973 return(write(fileno(fp[n]),s,(int)strlen(s)));
1974 rc = fputs(s,fp[n]) == EOF ? -1 : 0;
1980 /* Z S O U T L -- Write string to file, with line terminator, buffered */
1983 zsoutl(n,s) int n; char *s; {
1988 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1992 return(0); /* See comments in zsout() */
1993 #endif /* COMMENT */
1997 if (n == ZSFILE) /* Session log is unbuffered */
1998 return(write(fileno(fp[n]),"\n",1));
1999 else if (fputs("\n",fp[n]) == EOF)
2001 if (n == ZDIFIL || n == ZWFILE) /* Flush connection log records */
2006 /* Z S O U T X -- Write x characters to file, unbuffered. */
2009 zsoutx(n,s,x) int n, x; char *s; {
2011 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2013 return(ttol(s,x)); /* See comments in zsout() */
2016 #endif /* COMMENT */
2021 if (chkfn(n) < 1) return(-1);
2022 return(write(fp[n]->_file,s,x));
2023 #endif /* COMMENT */
2024 return(write(fileno(fp[n]),s,x) == x ? x : -1);
2027 /* Z C H O U T -- Add a character to the given file. */
2029 /* Should return 0 or greater on success, -1 on failure (e.g. disk full) */
2033 zchout(register int n, char c)
2035 zchout(n,c) register int n; char c;
2036 #endif /* CK_ANSIC */
2038 /* if (chkfn(n) < 1) return(-1); */
2041 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2045 return(0); /* See comments in zsout() */
2046 #endif /* COMMENT */
2050 if (n == ZSFILE) /* Use unbuffered for session log */
2051 return(write(fileno(fp[n]),&c,1) == 1 ? 0 : -1);
2052 /* Buffered for everything else */
2053 if (putc(c,fp[n]) == EOF) /* If true, maybe there was an error */
2054 return(ferror(fp[n])?-1:0); /* Check to make sure */
2055 else /* Otherwise... */
2056 return(0); /* There was no error. */
2059 /* (PWP) buffered character output routine to speed up file IO */
2065 zoutptr = zoutbuffer; /* Reset buffer pointer in all cases */
2068 debug(F101,"zoutdump zoutcnt","",zoutcnt);
2070 if (zoutcnt == 0) { /* Nothing to output */
2072 } else if (zoutcnt < 0) { /* Unexpected negative argument */
2073 zoutcnt = 0; /* Reset output buffer count */
2074 return(-1); /* and fail. */
2078 if (inserver && !local && fp[ZOFILE] == stdout) {
2080 x = ttol(zoutbuffer,zoutcnt);
2082 x = 1; /* See comments in zsout() */
2083 #endif /* COMMENT */
2085 return(x > 0 ? 0 : -1);
2090 Frank Prindle suggested that replacing this fwrite() by an fflush()
2091 followed by a write() would improve the efficiency, especially when
2092 writing to stdout. Subsequent tests showed a 5-fold improvement.
2095 if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) ...
2096 #endif /* COMMENT */
2100 #endif /* CK_NONBLOCK */
2102 while (zoutcnt > 0) {
2103 if ((x = write(fileno(fp[ZOFILE]),zp,zoutcnt)) > -1) {
2105 if (deblog) /* Save a function call... */
2106 debug(F101,"zoutdump wrote","",x);
2108 zoutcnt -= x; /* Adjust output buffer count */
2109 zp += x; /* and pointer */
2113 debug(F101,"zoutdump write error","",errno);
2114 debug(F101,"zoutdump write returns","",x);
2117 zoutcnt = 0; /* Reset output buffer count */
2118 return(-1); /* write() failed */
2124 /* C H K F N -- Internal function to verify file number is ok */
2128 -1: File number n is out of range
2129 0: n is in range, but file is not open
2130 1: n in range and file is open
2134 /* if (n != ZDFILE) debug(F101,"chkfn","",n); */
2135 if (n < 0 || n >= ZNFILS) {
2136 if (n != ZDFILE) debug(F101,"chkfn out of range","",n);
2139 /* if (n != ZDFILE) debug(F101,"chkfn fp[n]","",fp[n]); */
2140 return((fp[n] == NULL) ? 0 : 1);
2144 /* Z G E T F S -- Return file size regardless of accessibility */
2146 Used for directory listings, etc.
2148 The size of the file in bytes, 0 or greater, if the size can be learned.
2149 -1 if the file size can not be obtained.
2150 Also (and this is a hack just for UNIX):
2151 If the argument is the name of a symbolic link,
2152 the global variable issymlink is set to 1,
2153 and the global buffer linkname[] gets the link value.
2154 And it sets zgfs_dir to 1 if it's a directory, otherwise 0.
2155 This lets us avoid numerous redundant calls to stat().
2159 time_t zgfs_mtime = 0;
2160 unsigned int zgfs_mode = 0;
2163 char linkname[CKMAXPATH+1];
2165 #define _IFLNK 0120000
2167 #endif /* CKSYMLINK */
2170 zgetfs(name) char *name; {
2172 char fnam[CKMAXPATH+4];
2178 if (!name) name = "";
2179 if (!*name) return(-1);
2183 if (x == 9 && !strcmp(name,"/dev/null"))
2190 s = tilde_expand(s);
2195 x = ckstrncpy(fnam,s,CKMAXPATH);
2197 debug(F111,"zgetfs fnam",s,x);
2198 if (x > 0 && s[x-1] == '/')
2201 zgfs_dir = 0; /* Assume it's not a directory */
2202 zgfs_link = 0; /* Assume it's not a symlink */
2203 zgfs_mtime = 0; /* No time yet */
2204 zgfs_mode = 0; /* No permission bits yet */
2206 #ifdef CKSYMLINK /* We're doing symlinks? */
2207 #ifdef USE_LSTAT /* OK to use lstat()? */
2209 debug(F101,"STAT","",1);
2210 if (x < 0) /* stat() failed */
2212 if ( /* Now see if it's a symlink */
2214 S_ISLNK(buf.st_mode)
2217 ((_IFMT & buf.st_mode) == _IFLNK)
2219 #endif /* S_ISLNK */
2221 zgfs_link = 1; /* It's a symlink */
2222 linkname[0] = '\0'; /* Get the name */
2223 x = readlink(s,linkname,CKMAXPATH);
2224 debug(F101,"zgetfs readlink",s,x);
2225 if (x > -1 && x < CKMAXPATH) { /* It's a link */
2227 size = buf.st_size; /* Remember size of link */
2228 x = stat(s,&buf); /* Now stat the linked-to file */
2229 debug(F101,"STAT","",2);
2230 if (x < 0) /* so we can see if it's a directory */
2233 ckstrncpy(linkname,"(lookup failed)",CKMAXPATH);
2236 #else /* !USE_LSTAT */
2237 x = stat(s,&buf); /* No lstat(), use stat() instead */
2238 debug(F101,"STAT","",3);
2241 #endif /* USE_LSTAT */
2243 /* Do we need to call readlink()? */
2247 lstat() does not work in SCO operating systems. From "man NS lstat":
2249 lstat obtains information about the file named by path. In the case of a
2250 symbolic link, lstat returns information about the link, and not the file
2251 named by the link. It is only used by the NFS automount daemon and should
2252 not be utilized by users.
2255 debug(F101,"zgetfs forced needrlink","",needrlink);
2258 needrlink = S_ISLNK(buf.st_mode);
2259 debug(F101,"zgetfs S_ISLNK needrlink","",needrlink);
2262 needrlink = (_IFMT & buf.st_mode) == _IFLNK;
2263 debug(F101,"zgetfs _IFLNK needrlink","",needrlink);
2266 debug(F101,"zgetfs default needrlink","",needrlink);
2268 #endif /* S_ISLNK */
2269 #endif /* NOLINKBITS */
2274 x = readlink(s,linkname,CKMAXPATH);
2276 debug(F111,"zgetfs readlink",s,x);
2278 debug(F101,"zgetfs readlink errno","",errno);
2280 debug(F110,"zgetfs readlink result",linkname,0);
2282 if (x > -1 && x < CKMAXPATH) {
2287 #else /* !CKSYMLINK */
2288 x = stat(s,&buf); /* Just stat the file */
2289 debug(F111,"zgetfs stat",s,x);
2290 if (x < 0) /* and get the size */
2292 #endif /* CKSYMLINK */
2294 zgfs_mtime = buf.st_mtime;
2295 zgfs_mode = buf.st_mode;
2296 zgfs_dir = (S_ISDIR(buf.st_mode)) ? 1 : 0; /* Set "is directory" flag */
2297 debug(F111,"zgetfs size",s,size);
2298 debug(F111,"zgetfs st_size",s,buf.st_size);
2299 return((size < 0L) ? buf.st_size : size); /* Return the size */
2303 /* Z C H K I -- Check if input file exists and is readable */
2307 >= 0 if the file can be read (returns the size).
2308 -1 if file doesn't exist or can't be accessed,
2309 -2 if file exists but is not readable (e.g. a directory file).
2310 -3 if file exists but protected against read access.
2312 For Berkeley Unix, a file must be of type "regular" to be readable.
2313 Directory files, special files, and symbolic links are not readable.
2316 zchki(name) char *name; {
2320 extern int zchkid, diractive, matchfifo;
2330 if (x == 9 && !strcmp(s,"/dev/null"))
2332 if (x == 8 && !strcmp(s,"/dev/tty"))
2338 s = tilde_expand(s);
2345 debug(F111,"zchki setroot",ckroot,ckrootset);
2346 if (ckrootset) if (!zinroot(name)) {
2347 debug(F110,"zchki setroot violation",name,0);
2353 debug(F101,"STAT","",5);
2355 debug(F111,"zchki stat fails",s,errno);
2358 if (S_ISDIR (buf.st_mode))
2361 if (!(itsadir && zchkid)) { /* Unless this... */
2362 if (!S_ISREG (buf.st_mode) /* Must be regular file */
2364 && (!matchfifo || !S_ISFIFO (buf.st_mode)) /* or FIFO */
2365 #endif /* S_ISFIFO */
2367 debug(F111,"zchki not regular file (or fifo)",s,matchfifo);
2371 debug(F111,"zchki stat ok:",s,x);
2373 if (diractive) { /* If listing don't check access */
2377 debug(F100,"zchki swapping ids for access()","",0);
2379 #endif /* SW_ACC_ID */
2380 if ((x = access(s,R_OK)) < 0)
2381 x = access(s,X_OK); /* For RUN-class commands */
2384 debug(F100,"zchki swapped ids restored","",0);
2385 #endif /* SW_ACC_ID */
2387 if (x < 0) { /* Is the file accessible? */
2388 debug(F111,"zchki access failed:",s,x); /* No */
2391 iflen = buf.st_size; /* Yes, remember size */
2392 ckstrncpy(nambuf,s,CKMAXPATH); /* and name globally. */
2393 debug(F111,"zchki access ok:",s,iflen);
2394 return((iflen > -1L) ? iflen : 0L);
2398 /* Z C H K O -- Check if output file can be created */
2401 Returns -1 if write permission for the file would be denied, 0 otherwise.
2403 NOTE: The design is flawed. There is no distinction among:
2404 . Can I overwrite an existing file?
2405 . Can I create a file (or directory) in an existing directory?
2406 . Can I create a file (or directory) and its parent(s)?
2409 zchko(name) char *name; {
2410 int i, x, itsadir = 0;
2413 extern int zchkod; /* Used by IF WRITEABLE */
2415 debug(F110,"zchko entry",name,0);
2417 if (!name) return(-1); /* Watch out for null pointer. */
2422 debug(F111,"zchko setroot",ckroot,ckrootset);
2423 if (ckrootset) if (!zinroot(name)) {
2424 debug(F110,"zchko setroot violation",name,0);
2430 x = (int)strlen(name); /* Get length of filename */
2431 debug(F111,"zchko len",name,x);
2432 debug(F111,"zchko zchkod",name,zchkod);
2436 Writing to null device is OK.
2438 if (x == 9 && !strcmp(name,"/dev/null"))
2440 if (x == 8 && !strcmp(name,"/dev/tty"))
2447 s = tilde_expand(s);
2456 zchkod is a global flag meaning we're checking not to see if the directory
2457 file is writeable, but if it's OK to create files IN the directory.
2459 if (!zchkod && isdir(name)) /* Directories are not writeable */
2462 s = malloc(x+3); /* Must copy because we can't */
2463 if (!s) { /* write into our argument. */
2464 fprintf(stderr,"zchko: Malloc error 46\n");
2467 ckstrncpy(s,name,x+3);
2469 for (i = x; i > 0; i--) { /* Strip filename from right. */
2470 if (ISDIRSEP(s[i-1])) {
2475 debug(F101,"zchko i","",i);
2476 debug(F101,"zchko itsadir","",itsadir);
2479 /* X/OPEN XPG3-compliant systems fail if argument ends with "/"... */
2480 if (i == 0) /* If no path, use current directory */
2482 else /* Otherwise, use given one. */
2487 The following does not work for "foo/bar" where the foo directory does
2488 not exist even though we could create it: access("foo/.") fails, but
2489 access("foo") works OK.
2491 /* So now we use "path/." if path given, or "." if no path given. */
2492 s[i++] = '.'; /* Append "." to path. */
2495 /* So NOW we strip path segments from the right as long as they don't */
2496 /* exist -- we only call access() for path segments that *do* exist.. */
2497 /* (But this isn't quite right either since now zchko(/foo/bar/baz/xxx) */
2498 /* succeeds when I have write access to foo and bar but baz doesn't exit.) */
2500 if (itsadir && i > 0) {
2502 while (s[0] && !isdir(s)) {
2503 for (i = (int)strlen(s); i > 0; i--) {
2504 if (ISDIRSEP(s[i-1])) {
2513 s[i++] = '.'; /* Append "." to path. */
2516 #endif /* COMMENT */
2517 #endif /* COMMENT */
2520 ckstrncpy(s,".",x+3);
2523 debug(F100,"zchko swapping ids for access()","",0);
2525 #endif /* SW_ACC_ID */
2527 x = access(s,W_OK); /* Check access of path. */
2531 debug(F100,"zchko swapped ids restored","",0);
2532 #endif /* SW_ACC_ID */
2535 debug(F111,"zchko access failed:",s,errno);
2537 debug(F111,"zchko access ok:",s,x);
2538 free(s); /* Free temporary storage */
2540 return((x < 0) ? -1 : 0); /* and return. */
2543 /* Z D E L E T -- Delete the named file. */
2545 /* Returns: -1 on error, 0 on success */
2548 zdelet(name) char *name; {
2553 #endif /* CK_LOGIN */
2556 debug(F111,"zdelet setroot",ckroot,ckrootset);
2557 if (ckrootset) if (!zinroot(name)) {
2558 debug(F110,"zdelet setroot violation",name,0);
2564 debug(F111,"zdelet",name,x);
2566 if (ckxsyslog >= SYSLG_FC && ckxlogging) {
2568 zfnqfp(name,CKMAXPATH,fullname);
2569 debug(F110,"zdelet fullname",fullname,0);
2571 syslog(LOG_INFO, "file[] %s: delete failed (%m)", fullname);
2573 syslog(LOG_INFO, "file[] %s: delete ok", fullname);
2575 #endif /* CKSYSLOG */
2579 /* Z R T O L -- Convert remote filename into local form */
2582 zrtol(name,name2) char *name, *name2; {
2583 nzrtol(name,name2,1,0,CKMAXPATH);
2587 nzrtol(name,name2,fncnv,fnrpath,max)
2588 char *name, *name2; int fncnv, fnrpath, max;
2591 int flag = 0, n = 0;
2592 char fullname[CKMAXPATH+1];
2596 if (!name) name = "";
2598 debug(F111,"nzrtol name",name,fncnv);
2603 s = tilde_expand(s);
2609 /* Handle the path -- we don't have to convert its format, since */
2610 /* the standard path format and our (UNIX) format are the same. */
2613 devnull = !strcmp(name,"/dev/null");
2615 if (!devnull && fnrpath == PATH_OFF) { /* RECEIVE PATHNAMES OFF */
2617 strncpy(fullname,p,CKMAXPATH);
2618 } else if (!devnull && fnrpath == PATH_ABS) { /* REC PATHNAMES ABSOLUTE */
2619 strncpy(fullname,name,CKMAXPATH);
2620 } else if (!devnull && isabsolute(name)) { /* RECEIVE PATHNAMES RELATIVE */
2621 ckmakmsg(fullname,CKMAXPATH,".",name,NULL,NULL);
2622 } else { /* Ditto */
2623 ckstrncpy(fullname,name,CKMAXPATH);
2625 fullname[CKMAXPATH] = NUL;
2626 debug(F110,"nzrtol fullname",fullname,0);
2630 The maximum length for any segment of a filename is MAXNAMLEN, defined
2631 above. On some platforms (at least QNX) if a segment exceeds this limit,
2632 the open fails with ENAMETOOLONG, so we must prevent it by truncating each
2633 overlong name segment to the maximum segment length before passing the
2634 name to open(). This must be done even when file names are literal, so as
2635 not to halt a file transfer unnecessarily.
2638 char buf[CKMAXPATH+1]; /* New temporary buffer on stack */
2639 char *p = fullname; /* Source and */
2640 char *s = buf; /* destination pointers */
2642 debug(F101,"nzrtol sizing MAXNAMLEN","",MAXNAMLEN);
2643 while (*p && n < CKMAXPATH) { /* Copy name to new buffer */
2644 if (++i > MAXNAMLEN) { /* If this segment too long */
2645 while (*p && *p != '/') /* skip past the rest... */
2647 i = 0; /* and reset counter. */
2648 } else if (*p == '/') { /* End of this segment. */
2649 i = 0; /* Reset counter. */
2651 *s++ = *p++; /* Copy this character. */
2655 ckstrncpy(fullname,buf,CKMAXPATH); /* Copy back to original buffer. */
2656 debug(F111,"nzrtol sizing",fullname,n);
2658 #endif /* NOTRUNCATE */
2660 if (!fncnv || devnull) { /* Not converting */
2661 ckstrncpy(name2,fullname,max); /* We're done. */
2664 name = fullname; /* Converting */
2667 for (; *name != '\0' && n < maxnam; name++) {
2668 if (*name > SP) flag = 1; /* Strip leading blanks and controls */
2669 if (flag == 0 && *name < '!')
2677 if (isupper(*name)) /* Check for mixed case */
2679 else if (islower(*name))
2685 *p-- = '\0'; /* Terminate */
2686 while (*p < '!' && p > name2) /* Strip trailing blanks & controls */
2689 if (*name2 == '\0') { /* Nothing left? */
2690 ckstrncpy(name2,"NONAME",max); /* do this... */
2691 } else if (acase == 1) { /* All uppercase? */
2692 p = name2; /* So convert all letters to lower */
2699 debug(F110,"nzrtol new name",name2,0);
2703 /* Z S T R I P -- Strip device & directory name from file specification */
2705 /* Strip pathname from filename "name", return pointer to result in name2 */
2707 static char work[CKMAXPATH+1];
2710 zstrip(name,name2) char *name, **name2; {
2714 debug(F110,"zstrip before",name,0);
2715 if (!name) { *name2 = ""; return; }
2718 /* Strip leading tilde */
2719 if (*name == '~') name++;
2720 debug(F110,"zstrip after tilde-stripping",name,0);
2722 for (cp = name; *cp; cp++) {
2723 if (ISDIRSEP(*cp)) {
2728 if (n++ >= CKMAXPATH)
2732 *pp = '\0'; /* Terminate the string */
2734 debug(F110,"zstrip after",*name2,0);
2737 /* Z L T O R -- Local TO Remote */
2740 zltor(name,name2) char *name, *name2; {
2741 nzltor(name,name2,1,0,CKMAXPATH);
2744 /* N Z L T O R -- New Local TO Remote */
2747 fncnv = 0 for no conversion, > 0 for regular conversion, < 0 for minimal.
2750 nzltor(name,name2,fncnv,fnspath,max)
2751 char *name, *name2; int fncnv, fnspath, max;
2756 #endif /* COMMENT */
2760 char fullname[CKMAXPATH+1];
2765 extern int fcharset, /* tcharset, */ language;
2767 _PROTOTYP ( CHAR (*sxo), (CHAR) ) = NULL; /* Translation functions */
2769 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR);
2771 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])();
2772 #endif /* CK_ANSIC */
2774 language = L_USASCII;
2776 /* Proper translation of filenames must be done elsewhere */
2777 n = tcharset ? tcharset : TC_USASCII;
2778 sxo = xls[n][fcharset];
2780 sxo = xls[TC_USASCII][fcharset];
2781 #endif /* COMMENT */
2782 #endif /* NOCSETS */
2784 debug(F110,"nzltor name",name,0);
2786 /* Handle pathname */
2789 if (fnspath == PATH_OFF) { /* PATHNAMES OFF */
2791 ckstrncpy(fullname,p,CKMAXPATH);
2792 } else { /* PATHNAMES RELATIVE or ABSOLUTE */
2795 if (!strncmp(p,"../",3))
2797 else if (!strncmp(p,"./",2))
2802 if (fnspath == PATH_ABS) { /* ABSOLUTE */
2803 zfnqfp(p,CKMAXPATH,fullname);
2804 } else { /* RELATIVE */
2805 ckstrncpy(fullname,p,CKMAXPATH);
2808 debug(F110,"nzltor fullname",fullname,0);
2810 if (!fncnv) { /* Not converting */
2811 ckstrncpy(name2,fullname,max); /* We're done. */
2814 #endif /* NOCSETS */
2817 name = fullname; /* Converting */
2821 int tilde = 0, bslash = 0;
2823 if ((namechars = getenv("NAMECHARS")) != NULL) {
2824 if (ckstrchr(namechars, '~' ) != NULL) tilde = '~';
2825 if (ckstrchr(namechars, '\\') != NULL) bslash = '\\';
2832 pp = work; /* Output buffer */
2833 for (cp = name, n = 0; *cp && n < max; cp++,n++) { /* Convert name chars */
2836 if (sxo) c = (*sxo)(c); /* Convert to ASCII */
2837 #endif /* NOCSETS */
2838 if (fncnv > 0 && islower(c)) /* Uppercase letters */
2839 *pp++ = toupper(c); /* Change tilde to hyphen */
2842 else if (fncnv > 0 && c == '#') /* Change number sign to 'X' */
2844 else if (c == '*' || c == '?') /* Change wildcard chars to 'X' */
2846 else if (c == ' ') /* Change space to underscore */
2848 else if (c < ' ') /* Change controls to 'X' */
2850 else if (fncnv > 0 && c == '.') { /* Change dot to underscore */
2851 dotp = pp; /* Remember where we last did this */
2859 *pp = NUL; /* Tie it off. */
2861 if (dotp) *dotp = '.'; /* Restore last dot (if any) */
2863 if (dotp > dirp) *dotp = '.'; /* Restore last dot in file name */
2864 #endif /* COMMENT */
2865 cp = name2; /* If nothing before dot, */
2866 if (*work == '.') *cp++ = 'X'; /* insert 'X' */
2867 ckstrncpy(cp,work,max);
2870 #endif /* NOCSETS */
2871 debug(F110,"nzltor name2",name2,0);
2875 /* Z C H D I R -- Change directory */
2878 dirnam = pointer to name of directory to change to,
2879 which may be "" or NULL to indicate user's home directory.
2885 zchdir(dirnam) char *dirnam; {
2888 _PROTOTYP (int slotdir,(char *,char *));
2891 extern struct mtab *mactab; /* Main macro table */
2892 extern int nmac; /* Number of macros */
2895 debug(F110,"zchdir",dirnam,0);
2896 if (!dirnam) dirnam = "";
2897 if (!*dirnam) /* If argument is null or empty, */
2898 dirnam = zhome(); /* use user's home directory. */
2900 debug(F110,"zchdir 2",dirnam,0);
2903 hd = tilde_expand(dirnam); /* Attempt to expand tilde */
2905 if (*hd == '\0') hd = dirnam; /* in directory name. */
2909 debug(F110,"zchdir 3",hd,0);
2912 debug(F111,"zchdir setroot",ckroot,ckrootset);
2913 if (ckrootset) if (!zinroot(hd)) {
2914 debug(F110,"zchdir setroot violation",hd,0);
2920 /* Just to save some space */
2921 return((chdir(hd) == 0) ? 1 : 0);
2923 if (chdir(hd) == 0) { /* Try to cd */
2926 if (inserver && ikdbopen)
2927 slotdir(isguest ? anonroot : "", zgtdir());
2928 #endif /* CK_LOGIN */
2932 if (nmac) { /* Any macros defined? */
2934 static int on_cd = 0;
2937 k = mlook(mactab,"on_cd",nmac); /* Look this up */
2938 if (k >= 0) { /* If found, */
2939 if (dodo(k,zgtdir(),0) > -1) /* set it up, */
2940 parser(1); /* and execute it */
2954 zchkpid(unsigned long xpid)
2956 zchkpid(xpid) unsigned long xpid;
2957 #endif /* CK_ANSIC */
2959 return((kill((PID_T)xpid,0) < 0) ? 0 : 1);
2963 /* Z H O M E -- Return pointer to user's home directory */
2965 static char * zhomdir = NULL;
2973 return((char *)ckroot);
2977 home = getenv("home");
2979 home = getenv("HOME");
2981 makestr(&zhomdir,home);
2982 return(home ? zhomdir : ".");
2985 /* Z G T D I R -- Returns a pointer to the current directory */
2988 The "preferred" interface for getting the current directory in modern UNIX
2989 is getcwd() [POSIX 1003.1 5.2.2]. However, on certain platforms (such as
2990 SunOS), it is implemented by forking a shell, feeding it the pwd command,
2991 and returning the result, which is not only inefficient but also can result
2992 in stray messages to the terminal. In such cases -- as well as when
2993 getcwd() is not available at all -- getwd() can be used instead by defining
2994 USE_GETWD. However, note that getwd() provides no buffer-length argument
2995 and therefore no safeguard against memory leaks.
3005 #endif /* USE_GETWD */
3008 #define CWDBL 80 /* Save every byte we can... */
3010 #define CWDBL CKMAXPATH
3012 static char cwdbuf[CWDBL+2];
3014 NOTE: The getcwd() prototypes are commented out on purpose. If you get
3015 compile-time warnings, search through your system's header files to see
3016 which one has the needed prototype, and #include it. Usually it is
3017 <unistd.h>. See the section for including <unistd.h> in ckcdeb.h and
3018 make any needed adjustments there (and report them).
3022 char * buf = cwdbuf;
3026 extern char *getwd();
3028 debug(F110,"zgtdir BSD4 getwd()",s,0);
3034 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3035 #endif /* DCLGETCWD */
3036 debug(F101,"zgtdir BSD44 CWDBL","",CWDBL);
3037 s = getcwd(buf,CWDBL);
3043 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3044 #endif /* DCLGETCWD */
3045 debug(F101,"zgtdir MINIX2 CWDBL","",CWDBL);
3046 s = getcwd(buf,CWDBL);
3052 /* This non-ANSI prototype can be fatal at runtime! (e.g. in SCO3.2v5.0.5). */
3053 /* Anyway it's already prototyped in some header file that we have included. */
3054 extern char *getcwd();
3057 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3058 #endif /* DCLGETCWD */
3059 #endif /* COMMENT */
3060 debug(F101,"zgtdir SVORPOSIX CWDBL","",CWDBL);
3061 s = getcwd(buf,CWDBL);
3068 extern char *getcwd();
3069 #endif /* DCLGETCWD */
3070 debug(F101,"zgtdir COHERENT _I386 CWDBL","",CWDBL);
3071 s = getcwd(buf,CWDBL);
3075 extern char *getwd();
3076 debug(F101,"zgtdir COHERENT CWDBL","",CWDBL);
3083 debug(F101,"zgtdir SUNOS CWDBL","",CWDBL);
3084 s = getcwd(buf,CWDBL);
3090 #endif /* COHERENT */
3091 #endif /* SYSVORPOSIX */
3094 #endif /* USE_GETWD */
3097 /* Z X C M D -- Run a system command so its output can be read like a file */
3101 zxcmd(filnum,comand) int filnum; char *comand; {
3104 extern int kactive; /* From ckcpro.w and ckcmai.c */
3107 debug(F100,"zxcmd fails: nopush","",0);
3110 debug(F111,"zxcmd",comand,filnum);
3111 if (chkfn(filnum) < 0) return(-1); /* Need a valid Kermit file number. */
3112 if (filnum == ZSTDIO || filnum == ZCTERM) /* But not one of these. */
3115 out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
3116 debug(F101,"zxcmd out",comand,out);
3118 /* Output to a command */
3120 if (out) { /* Need popen() to do this. */
3121 ckstrncpy(fullname,"(pipe)",CKMAXPATH);
3123 return(0); /* no popen(), fail. */
3125 /* Use popen() to run the command. */
3127 #ifdef _POSIX_SOURCE
3128 /* Strictly speaking, popen() is not available in POSIX.1 */
3130 #endif /* _POSIX_SOURCE */
3132 debug(F110,"zxcmd out",comand,0);
3135 debug(F100,"zxcmd priv_chk failed","",0);
3139 fp[filnum] = popen(comand,"w");
3140 debug(F111,"zxcmd popen",fp[filnum] ? "OK" : "Failed", errno);
3141 if (fp[filnum] == NULL)
3144 /* I wonder what this is all about... */
3145 close(pipes[0]); /* Don't need the input side */
3146 fp[filnum] = fdopen(pipes[1],"w"); /* Open output stream. */
3147 fp[ZSYSFN] = fp[filnum]; /* Remember. */
3148 #endif /* COMMENT */
3150 zoutcnt = 0; /* (PWP) reset input buffer */
3151 zoutptr = zoutbuffer;
3153 #endif /* NOPOPEN */
3156 /* Input from a command */
3159 /* SINIX-L 5.41 does not like fdopen() */
3162 if (pipe(pipes) != 0) {
3163 debug(F100,"zxcmd pipe failure","",0);
3164 return(0); /* can't make pipe, fail */
3167 /* Create a fork in which to run the named process */
3171 pid = vfork() /* child */
3173 pid = fork() /* child */
3177 /* We're in the fork. */
3179 char *shpath, *shname, *shptr; /* Find user's preferred shell */
3183 #ifdef HPUX10 /* Default shell */
3184 defshell = "/usr/bin/sh";
3187 defshell = "/bin/rc";
3189 defshell = "/bin/sh";
3193 if (priv_can()) exit(1); /* Turn off any privileges! */
3194 debug(F101,"zxcmd pid","",pid);
3195 close(pipes[0]); /* close input side of pipe */
3196 close(0); /* close stdin */
3197 if (open("/dev/null",0) < 0) return(0); /* replace input by null */
3200 dup2(pipes[1],1); /* BSD: replace stdout & stderr */
3201 dup2(pipes[1],2); /* by the pipe */
3203 close(1); /* AT&T: close stdout */
3204 if (dup(pipes[1]) != 1) /* Send stdout to the pipe */
3206 close(2); /* Send stderr to the pipe */
3207 if (dup(pipes[1]) != 2)
3209 #endif /* SVORPOSIX */
3214 close(pipes[1]); /* Don't need this any more. */
3217 if ((shpath = getenv("SERVERSHELL")) == NULL)
3220 shpath = getenv("SHELL"); /* What shell? */
3221 if (shpath == NULL) {
3222 p = getpwuid( real_uid() ); /* Get login data */
3223 debug(F111,"zxcmd shpath","getpwuid()",p);
3224 if (p == (struct passwd *)NULL || !*(p->pw_shell))
3226 else shpath = p->pw_shell;
3229 shptr = shname = shpath;
3230 while (*shptr != '\0')
3231 if (*shptr++ == '/')
3233 debug(F110,shpath,shname,0);
3234 restorsigs(); /* Restore ignored signals */
3235 execl(shpath,shname,"-c",comand,(char *)NULL); /* Execute the cmd */
3236 exit(0); /* just punt if it failed. */
3237 } else if (pid == (PID_T) -1) {
3238 debug(F100,"zxcmd fork failure","",0);
3241 debug(F101,"zxcmd pid","",pid);
3242 close(pipes[1]); /* Don't need the output side */
3243 ispipe[filnum] = 1; /* Remember it's a pipe */
3244 fp[filnum] = fdopen(pipes[0],"r"); /* Open a stream for input. */
3248 if (filnum == ZIFILE && kactive) { /* Make pipe reads nonblocking */
3250 if ((flags = fcntl(fileno(fp[filnum]),F_GETFL,0)) > -1) {
3251 debug(F101,"zxcmd fcntl 1 pipe flags","",flags);
3252 x = fcntl(fileno(fp[filnum]),F_SETFL, flags |
3259 debug(F101,"zxcmd fcntl 2 result","",x);
3263 #endif /* DONDELAY */
3265 fp[ZSYSFN] = fp[filnum]; /* Remember. */
3266 zincnt = 0; /* (PWP) reset input buffer */
3272 /* Z C L O S F - wait for the child fork to terminate and close the pipe. */
3274 /* Used internally by zclose - returns -1 on failure, 1 on success. */
3277 zclosf(filnum) int filnum; {
3281 debug(F101,"zclosf filnum","",filnum);
3282 out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
3283 debug(F101,"zclosf out","",out);
3287 /* In UNIX we use popen() only for output files */
3291 x = pclose(fp[filnum]);
3293 debug(F101,"zclosf pclose","",x);
3294 debug(F101,"zclosf pexitstat","",pexitstat);
3295 fp[filnum] = fp[ZSYSFN] = NULL;
3297 return((x != 0) ? -1 : 1);
3299 #endif /* NOPOPEN */
3300 debug(F101,"zclosf fp[filnum]","", fp[filnum]);
3301 debug(F101,"zclosf fp[ZSYSFN]","", fp[ZSYSFN]);
3303 if (pid != (PID_T) 0) {
3304 debug(F101,"zclosf killing pid","",pid);
3313 This is the original code (before 20 April 1997) and has proven totally
3314 portable. But it does not give us the process's return code.
3316 while ((wstat = wait((WAIT_T *)0)) != pid && wstat != -1) ;
3318 /* Here we try to get the return code. Let's hope this is portable too. */
3319 while ((wstat = wait(&statusp)) != pid && wstat != -1) ;
3320 pexitstat = (statusp & 0xff) ? statusp : statusp >> 8;
3321 debug(F101,"zclosf wait statusp","",statusp);
3322 debug(F101,"zclosf wait pexitstat","",pexitstat);
3323 #endif /* CK_CHILD */
3327 fp[filnum] = fp[ZSYSFN] = NULL;
3330 debug(F101,"zclosf fp[filnum]","",fp[filnum]);
3332 return(pexitstat == 0 ? 1 : -1);
3335 #endif /* CK_CHILD */
3341 zxcmd(filnum,comand) int filnum; char *comand; {
3345 zclosf(filnum) int filnum; {
3351 /* Z X P A N D -- Expand a wildcard string into an array of strings */
3353 As of C-Kermit 7.0, this API is obsolete, replaced by nzxpand(), and this
3354 function is only used internally. See nzxpand() below.
3356 Returns the number of files that match fnarg, with data structures set up
3357 so that first file (if any) will be returned by the next znext() call.
3359 Depends on external variable wildxpand: 0 means we expand wildcards
3360 internally, nonzero means we call the shell to do it.
3362 static int xdironly = 0;
3363 static int xfilonly = 0;
3364 static int xmatchdot = 0;
3365 static int xrecursive = 0;
3366 static int xnobackup = 0;
3367 static int xnolinks = 0;
3369 static char *freeptr = NULL, **resptr = NULL; /* Copies of caller's args */
3370 static int remlen; /* Remaining space in caller's array */
3371 static int numfnd = 0; /* Number of matches found */
3373 #define MINSPACE 1024
3376 initspace(resarry,len) char * resarry[]; int len; {
3378 if (len < MINSPACE) len = MINSPACE;
3379 if (!sspace) { /* Need to allocate string space? */
3380 while (len >= MINSPACE) {
3381 if ((sspace = malloc(len+2))) { /* Got it. */
3382 debug(F101,"fgen string space","",len);
3385 len = (len / 2) + (len / 4); /* Didn't, reduce by 3/4 */
3387 if (len <= MINSPACE) { /* Did we get it? */
3388 fprintf(stderr,"fgen can't malloc string space\n");
3393 #endif /* DYNAMIC */
3395 freeptr = sspace; /* This is where matches are copied. */
3396 resptr = resarry; /* Static copies of these so */
3397 remlen = len; /* recursive calls can alter them. */
3398 debug(F101,"initspace ssplen","",ssplen);
3403 Z S E T F I L -- Query or change the size of file list buffers.
3405 fc = 1: Change current string space to n, return new size.
3406 fc = 2: Return current string space size.
3407 fc = 3: Change current maxnames to n, return new maxnames.
3408 fc = 4: Return current maxnames.
3409 Returns < 0 on error.
3412 zsetfil(n, fc) int n, fc; {
3415 case 1: /* Stringspace */
3420 if (initspace(mtchs,n) < 0)
3422 case 2: /* Fall thru deliberately */
3424 case 3: /* Listsize */
3426 free((char *)mtchs);
3429 mtchs = (char **)malloc(n * sizeof(char *));
3433 case 4: /* Fall thru deliberately */
3436 #endif /* DYNAMIC */
3446 #endif /* NONZXPAND */
3448 zxpand(fnarg) char *fnarg; {
3449 extern int diractive;
3450 char fnbuf[CKMAXPATH+8], * fn, * p;
3452 #ifdef DTILDE /* Built with tilde-expansion? */
3458 if (!fnarg) { /* If no argument provided */
3459 nxpand = fcount = 0;
3460 return(0); /* Return zero files found */
3462 debug(F110,"zxpand entry",fnarg,0);
3463 debug(F101,"zxpand xdironly","",xdironly);
3464 debug(F101,"zxpand xfilonly","",xfilonly);
3466 if (!*fnarg) { /* If no argument provided */
3467 nxpand = fcount = 0;
3468 return(0); /* Return zero files found */
3472 debug(F111,"zxpand setroot",ckroot,ckrootset);
3473 if (ckrootset) if (!zinroot(fnarg)) {
3474 debug(F110,"zxpand setroot violation",fnarg,0);
3475 nxpand = fcount = 0;
3482 This would have been perfect, except it makes us return fully qualified
3483 pathnames for all files.
3485 zfnqfp(fnarg,CKMAXPATH,fnbuf);
3486 debug(F110,"zxpand zfnqfp",fnbuf,0);
3488 debug(F110,"zxpand zgtdir",s,0);
3490 while (*p && *s) /* Make it relative */
3493 fn = (*s) ? fnbuf : p;
3494 debug(F110,"zxpand fn 0",fn,0);
3500 debug(F110,"zxpand fn 0.5",fn,0);
3502 #ifdef DTILDE /* Built with tilde-expansion? */
3503 if (*fnarg == '~') { /* Starts with tilde? */
3504 tnam = tilde_expand(fnarg); /* Try to expand it. */
3505 ckstrncpy(fnbuf,tnam,CKMAXPATH);
3508 ckstrncpy(fnbuf,fnarg,CKMAXPATH);
3509 fn = fnbuf; /* Point to what we'll work with */
3510 #endif /* COMMENT */
3511 debug(F110,"zxpand fn 1",fn,0);
3513 if (!*fn) /* But make sure something is there */
3516 p = fn + (int)strlen(fn) - 1;
3517 if (*p == '/') { /* If last char = / it must be a dir */
3518 if (!xfilonly && !iswild(p)) haveonedir++;
3519 ckstrncat(fn, "*", CKMAXPATH+8); /* so append '*' */
3520 } else if (p > fn) { /* If ends in "/." */
3521 if (*(p-1) == '/' && *p == '.') /* change '.' to '*' */
3523 } else if (p == fn) { /* If it's '.' alone */
3524 if (*p == '.') /* change '.' to '*' */
3527 debug(F110,"zxpand fn 2",fn,0);
3528 x = isdir(fn); /* Is it a directory? */
3529 debug(F111,"zxpand isdir 1",fn,x);
3530 if (x) { /* If so, make it into a wildcard */
3531 if (!xfilonly && !iswild(p))
3533 if ((x = strlen(fn)) > 0) {
3534 if (!ISDIRSEP(fn[x-1]))
3540 debug(F111,"zxpand fn 3",fn,haveonedir);
3542 The following allows us to parse a single directory name without opening
3543 the directory and looking at its contents. The diractive flag is a horrible
3544 hack (especially since DIR /NORECURSIVE turns it off), but otherwise we'd
3545 have to change the API.
3547 if (!diractive && haveonedir) {
3549 fcount = (mtchs == NULL &&
3550 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
3555 mtchs = (char **)malloc(maxnames * sizeof(*mtchs));
3559 return(nxpand = fcount);
3561 #endif /* COMMENT */
3562 debug(F110,"zxpand haveonedir A1",fnarg,0);
3563 initspace(mtchs,ssplen);
3565 if (numfnd < 0) return(-1);
3566 mtchptr = mtchs; /* Save pointer for next. */
3567 debug(F110,"zxpand haveonedir A2",*mtchptr,0);
3568 return(nxpand = fcount);
3572 if (!nopush && wildxpand) /* Who is expanding wildcards? */
3573 fcount = (mtchs == NULL && /* Shell */
3574 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
3576 : shxpand(fn,mtchs,maxnames);
3579 fcount = (mtchs == NULL && /* Kermit */
3580 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
3582 : fgen(fn,mtchs,maxnames); /* Look up the file. */
3584 if (fcount == 0 && haveonedir) {
3586 debug(F110,"zxpand haveonedir B",fnarg,0);
3588 if (numfnd < 0) return(-1);
3590 mtchptr = mtchs; /* Save pointer for next. */
3596 debug(F111,"zxpand ok",mtchs[0],fcount);
3598 debug(F101,"zxpand fcount","",fcount);
3605 /* N Z X P A N D -- Expand a file list, with options. */
3608 s = pointer to filename or pattern.
3609 flags = option bits:
3611 flags & ZX_FILONLY Match regular files
3612 flags & ZX_DIRONLY Match directories
3613 flags & ZX_RECURSE Descend through directory tree
3614 flags & ZX_MATCHDOT Match "dot files"
3615 flags & ZX_NOBACKUP Don't match "backup files"
3616 flags & ZX_NOLINKS Don't follow symlinks.
3618 Returns the number of files that match s, with data structures set up
3619 so that first file (if any) will be returned by the next znext() call.
3622 nzxpand(s,flags) char * s; int flags; {
3626 debug(F111,"nzxpand",s,flags);
3627 x = flags & (ZX_DIRONLY|ZX_FILONLY);
3628 xdironly = (x == ZX_DIRONLY);
3629 xfilonly = (x == ZX_FILONLY);
3630 if (xdironly && xfilonly) {
3634 xmatchdot = (flags & ZX_MATCHDOT);
3635 debug(F111,"nzxpand xmatchdot 1",s,xmatchdot);
3636 /* If xmatchdot not set by caller but pattern implies it, set it anyway */
3637 if (!xmatchdot && ((p = ckstrchr(s,'.')))) {
3638 if (p == s && p[1] != '/') {
3640 debug(F111,"nzxpand xmatchdot 2",s,xmatchdot);
3642 xmatchdot = (*(p-1) == ',') || (*(p-1) == '{') || (*(p-1) == '/');
3643 debug(F111,"nzxpand xmatchdot 3",s,xmatchdot);
3646 xrecursive = (flags & ZX_RECURSE);
3647 xnobackup = (flags & ZX_NOBACKUP);
3648 xnolinks = (flags & ZX_NOLINKS);
3652 debug(F101,"nzxpand xdironly","",xdironly);
3653 debug(F101,"nzxpand xfilonly","",xfilonly);
3654 debug(F101,"nzxpand xmatchdot","",xmatchdot);
3655 debug(F101,"nzxpand xrecursive","",xrecursive);
3656 debug(F101,"nzxpand xnobackup","",xnobackup);
3657 debug(F101,"nzxpand xnolinks","",xnolinks);
3663 sh_sort(mtchs,NULL,x,0,0,1); /* Alphabetize the list */
3672 #endif /* NONZXPAND */
3675 /* Z X R E W I N D -- Rewinds the zxpand() list */
3679 /* if (!mtchs) return(-1); */
3684 #endif /* NOZXREWIND */
3686 /* Z N E X T -- Get name of next file from list created by zxpand(). */
3688 Returns >0 if there's another file, with its name copied into the arg string,
3689 or 0 if no more files in list.
3692 znext(fn) char *fn; {
3694 ckstrncpy(fn,*mtchptr++,CKMAXPATH);
3699 debug(F111,"znext",fn,fcount+1);
3702 debug(F111,"znext",fn,fcount); /* Return 0 if no filename to return */
3704 #endif /* COMMENT */
3707 /* Z C H K S P A -- Check if there is enough space to store the file */
3710 Call with file specification f, size n in bytes.
3711 Returns -1 on error, 0 if not enough space, 1 if enough space.
3716 zchkspa(char *f, long n)
3718 zchkspa(f,n) char *f; long n;
3719 #endif /* CK_ANSIC */
3721 /* In UNIX there is no good (and portable) way. */
3722 return(1); /* Always say OK. */
3725 #ifdef COMMENT /* (not used) */
3727 /* I S B A C K U P -- Tells if given file has a backup suffix */
3730 -1: Invalid argument
3731 0: File does not have a backup suffix
3732 >0: Backup suffix number
3735 isbackup(fn) char * fn; { /* Get backup suffix number */
3736 int i, j, k, x, state, flag;
3738 if (!fn) /* Watch out for null pointers. */
3740 if (!*fn) /* And empty names. */
3744 for (i = (int)strlen(fn) - 1; (!flag && (i > 0)); i--) {
3746 case 0: /* State 0 - final char */
3747 if (fn[i] == '~') /* Is tilde */
3748 state = 1; /* Switch to next state */
3749 else /* Otherwise */
3750 flag = 1; /* Quit - no backup suffix. */
3752 case 1: /* State 1 - digits */
3753 if (fn[i] == '~' && fn[i-1] == '.') { /* Have suffix */
3754 return(atoi(&fn[i+1]));
3755 } else if (fn[i] >= '0' && fn[i] <= '9') { /* In number part */
3756 continue; /* Keep going */
3757 } else { /* Something else */
3758 flag = 1; /* Not a backup suffix - quit. */
3765 #endif /* COMMENT */
3768 /* Z N E W N -- Make a new name for the given file */
3771 Given the name, fn, of a file that already exists, this function builds a
3772 new name of the form "<oldname>.~<n>~", where <oldname> is argument name
3773 (fn), and <n> is a version number, one higher than any existing version
3774 number for that file, up to 99999. This format is consistent with that used
3775 by GNU EMACS. If the constructed name is too long for the system's maximum,
3776 enough characters are truncated from the end of <fn> to allow the version
3777 number to fit. If no free version numbers exist between 1 and 99999, a
3778 version number of "xxxx" is used. Returns a pointer to the new name in
3782 #define ZNEWNBL 63 /* Name buffer length */
3783 #define ZNEWNMD 3 /* Max digits for version number */
3785 #define ZNEWNBL CKMAXPATH
3789 #define MAXBUDIGITS 5
3791 static char znewbuf[ZNEWNBL+12];
3794 znewn(fn,s) char *fn, **s; {
3795 char * buf; /* Pointer to buffer for new name */
3796 char * xp, * namepart = NULL; /* Pointer to filename part */
3797 struct zfnfp * fnfp; /* znfqfp() result struct pointer */
3798 int d = 0, t, fnlen, buflen;
3799 int n, i, k, flag, state;
3800 int max = MAXNAMLEN; /* Maximum name length */
3801 char * dname = NULL;
3804 *s = NULL; /* Initialize return value */
3805 if (!fn) fn = ""; /* Check filename argument */
3808 /* If incoming file already has a backup suffix, remove it. */
3809 /* Then we'll tack a new on later, which will be the highest for this file. */
3811 if (i <= max && i > 0 && fn[i-1] == '~') {
3814 debug(F111,"znewn suffix removal",fn,i);
3815 if ((dname = (char *)malloc(i+1))) {
3816 ckstrncpy(dname,fn,i+1);
3818 for (flag = state = 0; (!flag && (i > 0)); i--) {
3820 case 0: /* State 0 - final char */
3821 if (p[i] == '~') /* Is tilde */
3822 state = 1; /* Switch to next state */
3823 else /* Otherwise */
3824 flag = 1; /* Quit - no backup suffix. */
3826 case 1: /* State 1 - digits */
3827 if (p[i] == '~' && p[i-1] == '.') { /* Have suffix */
3828 p[i-1] = NUL; /* Trim it */
3830 debug(F111,"znewn suffix removal 2",fn,i);
3831 flag = 1; /* done */
3832 } else if (p[i] >= '0' && p[i] <= '9') { /* Number part */
3833 continue; /* Keep going */
3834 } else { /* Something else */
3835 flag = 1; /* Not a backup suffix - quit. */
3842 if ((fnlen = strlen(fn)) < 1) { /* Get length */
3843 if (dname) free(dname);
3846 debug(F111,"znewn",fn,fnlen);
3848 debug(F101,"znewn max 1","",max);
3849 if (max < 14) max = 14; /* Make max reasonable for any UNIX */
3850 if (max > ZNEWNBL) max = ZNEWNBL;
3851 debug(F101,"znewn max 2","",max);
3853 if ((fnfp = zfnqfp(fn, ZNEWNBL, buf))) { /* Get fully qualified name */
3854 namepart = fnfp->fname; /* Isolate the filename */
3855 k = strlen(fn); /* Length of name part */
3856 debug(F111,"znewn namepart",namepart,k);
3858 if (dname) free(dname);
3861 buflen = fnfp->len; /* Length of fully qualified name */
3862 debug(F111,"znewn len",buf,buflen);
3864 if (k + MAXBUDIGITS + 3 < max) { /* Backup name fits - no overflow */
3865 /* Make pattern for backup names */
3866 ckstrncpy(buf+buflen,".~*~",ZNEWNBL+12-buflen);
3867 n = nzxpand(buf,ZX_FILONLY); /* Expand the pattern */
3868 debug(F111,"znewn A matches",buf,n);
3869 while (n-- > 0) { /* Find any existing name.~n~ files */
3870 xp = *mtchptr++; /* Point at matching name */
3871 t = atoi(xp+buflen+2); /* Get number */
3872 if (t > d) d = t; /* Save d = highest version number */
3874 sprintf(buf+buflen,".~%d~",d+1); /* Yes, make "name.~<d+1>~" */
3875 debug(F110,"znewn A newname",buf,0);
3876 } else { /* Backup name would be too long */
3877 int xlen; /* So we have to eat back into it */
3879 char buf2[ZNEWNBL+12];
3882 debug(F101,"znewn B delta","",delta);
3884 for (i = MAXBUDIGITS; i > 0; i--) { /* In this case the format of */
3885 ckstrncpy(buf2,buf,ZNEWNBL+12); /* the backup name depends on */
3886 xlen = buflen - i - 3 + delta; /* how many digits are in the */
3887 ckstrncpy(buf2+xlen,".~*~",ZNEWNBL+12-xlen); /* backup number */
3888 n = nzxpand(buf2,ZX_FILONLY);
3889 debug(F111,"znewn B matches",buf2,n);
3893 while (n-- > 0) { /* Find any existing name.~n~ files */
3894 xp = *mtchptr++; /* Point at matching name */
3895 t = atoi(xp+xlen+2); /* Get number */
3896 if (t > d) d = t; /* Save d = highest version number */
3898 if (d > 0) /* If the odometer turned over... */
3899 if ((d % 10) == 9) /* back up one space. */
3901 sprintf(buf2+xlen,".~%d~",d+1); /* This just fits */
3902 ckstrncpy(buf,buf2,ZNEWNBL+12); /* (we could be more clever here...) */
3903 debug(F110,"znewn B new name",buf,0);
3905 *s = buf; /* Point to new name */
3906 ck_znewn = d+1; /* Also make it available globally */
3907 if (dname) free(dname);
3911 /* Z R E N A M E -- Rename a file */
3913 Call with old and new names.
3914 If new name is the name of a directory, the 'old' file is moved to
3916 Returns 0 on success, -1 on failure.
3919 zrename(old,new) char *old, *new; {
3925 debug(F110,"zrename old",old,0);
3926 debug(F110,"zrename new",new,0);
3927 if (!*old) return(-1);
3928 if (!*new) return(-1);
3932 if (inserver && isguest)
3934 #endif /* CK_LOGIN */
3938 debug(F111,"zrename setroot",ckroot,ckrootset);
3940 if (!zinroot(old)) {
3941 debug(F110,"zrename old: setroot violation",old,0);
3944 if (!zinroot(new)) {
3945 debug(F110,"zrename new: setroot violation",new,0);
3957 if (!(p = malloc(strlen(new) + strlen(old) + 2)))
3959 strcpy(p,new); /* (safe) Directory part */
3960 if (!ISDIRSEP(*(new+x-1))) /* Separator, if needed */
3961 strcat(p,"/"); /* (safe) */
3962 zstrip(old,&q); /* Strip path part from old name */
3963 strcat(p,q); /* cat to new directory (safe) */
3965 debug(F110,"zrename dir",s,0);
3968 else debug(F110,"zrename no dir",s,0);
3972 if (inserver && (!ENABLED(en_del))) {
3973 if (zchki(s) > -1) /* Destination file exists? */
3978 x = -1; /* Return code. */
3980 /* Atomic, preferred, uses a single system call, rename(), if available. */
3982 debug(F111,"zrename rename()",old,x);
3986 /* If rename() failed or not available try link()/unlink() */
3989 if (zchko(old) > -1) { /* Requires write access to orignal */
3991 debug(F111,"zrename link()",old,x);
3992 if (x > -1) { /* Make a link with the new name. */
3994 debug(F111,"zrename unlink()",old,x);
3996 /* If link/unlink failed copy and delete */
3999 debug(F111,"zrename zcopy()",old,x);
4002 debug(F111,"zrename zdelet()",old,x);
4007 fullname[0] = '\0'; /* Clear this out for next time. */
4010 if (ckxsyslog >= SYSLG_FC && ckxlogging) {
4011 zfnqfp(old,CKMAXPATH,fullname);
4013 zfnqfp(s,CKMAXPATH,tmp2);
4015 syslog(LOG_INFO,"file[] %s: renamed to %s ok", fullname, tmp2);
4017 syslog(LOG_INFO,"file[] %s: rename to %s failed (%m)",fullname,tmp2);
4019 #endif /* CKSYSLOG */
4025 /* Z C O P Y -- Copy a single file. */
4027 Call with source and destination names.
4028 If destination is a directory, the source file is
4029 copied to that directory with its original name.
4033 -2 = source file is not a regular file.
4034 -3 = source file not found.
4035 -4 = permission denied.
4036 -5 = source and destination are the same file.
4041 zcopy(source,destination) char *source, *destination; {
4042 char *src, *dst; /* Local pointers to filenames */
4043 int x, y, rc; /* Workers */
4044 int in = -1, out = -1; /* i/o file descriptors */
4045 struct stat srcbuf; /* Source file info buffer */
4046 int perms; /* Output file permissions */
4047 char buf[1024]; /* File copying buffer */
4049 if (!source) source = "";
4050 if (!destination) destination = "";
4052 debug(F110,"zcopy src arg",source,0);
4053 debug(F110,"zcopy dst arg",destination,0);
4055 if (!*source) return(-1);
4056 if (!*destination) return(-1);
4060 if (inserver && isguest)
4062 #endif /* CK_LOGIN */
4066 debug(F111,"zcopy setroot",ckroot,ckrootset);
4068 if (!zinroot(source)) {
4069 debug(F110,"zcopy source: setroot violation",source,0);
4072 if (!zinroot(destination)) {
4073 debug(F110,"zcopy destination: setroot violation",destination,0);
4082 if (stat(src,&srcbuf) == 0) { /* Get source file info */
4083 struct stat dstbuf; /* Destination file info buffer */
4084 debug(F101,"STAT","",6);
4085 if (stat(dst,&dstbuf) == 0) {
4086 debug(F101,"STAT","",7);
4087 if (srcbuf.st_dev == dstbuf.st_dev)
4088 if (srcbuf.st_ino == dstbuf.st_ino) {
4089 debug(F100,"zcopy files identical: stat()","",0);
4093 } else { /* stat() failed... */
4094 debug(F101,"STAT","",8);
4095 debug(F111,"source file not found",src,errno);
4098 fullname[0] = '\0'; /* Get full pathnames */
4099 if (zfnqfp(source,CKMAXPATH,fullname))
4101 debug(F110,"zcopy src",src,0);
4103 if (zfnqfp(destination,CKMAXPATH,tmp2))
4105 debug(F110,"zcopy dst 1",dst,0);
4106 if (!strcmp(src,dst)) { /* Src and dst are same file? */
4107 debug(F100,"zcopy files identical: strcmp()","",0); /* This... */
4108 return(-5); /* should not happen. */
4110 if (isdir(src)) { /* Source file is a directory? */
4111 debug(F110,"zcopy source is directory",src,0);
4112 return(-2); /* Fail */
4114 if (isdir(dst)) { /* Destination is a directory? */
4115 char *q = NULL; /* Yes, add filename to it. */
4117 if (x < 1) return(-1);
4118 if (!ISDIRSEP(*(dst+x-1))) { /* Add separator if needed */
4122 debug(F111,"zcopy dst 2",dst,x);
4123 zstrip(src,&q); /* Strip path part from old name */
4124 ckstrncpy(tmp2+x,q,CKMAXPATH-x); /* Concatenate it to new name */
4126 debug(F110,"zcopy dst 3",dst,0);
4129 if (inserver && (!ENABLED(en_del))) {
4130 if (zchki(dst) > -1) /* Destination file exists? */
4135 perms = umask(0); /* Get user's umask */
4136 umask(perms); /* Put it back! */
4137 perms ^= 0777; /* Flip the bits */
4138 perms &= 0666; /* Zero execute bits from umask */
4139 perms |= (srcbuf.st_mode & 0111); /* OR in source file's execute bits */
4140 rc = -1; /* Default return code */
4141 errno = 0; /* Reset errno */
4142 in = open(src, O_RDONLY, 0); /* Open source file */
4143 debug(F111,"zcopy open source",src,in);
4144 if (in > -1) { /* If open... */
4145 /* Open destination file */
4147 out = open(dst, O_WRONLY|O_CREAT|O_TRUNC, perms);
4149 out = open(dst, O_WRONLY|O_CREAT, perms);
4150 #endif /* O_TRUNC */
4151 debug(F111,"zcopy open dest",dst,out);
4152 if (out > -1) { /* If open... */
4153 while ((x = read(in,buf,1024)) > 0) { /* Copy in 1K blocks */
4154 y = write(out,buf,x);
4155 if (y < 0) { /* On write failure */
4157 rc = -6; /* Indicate i/o error */
4161 debug(F101,"zcopy final read","",x);
4162 debug(F101,"zcopy errno","",errno);
4163 rc = (x == 0) ? 0 : -6; /* In case of read failure */
4166 if (in > -1) close(in); /* Close files */
4167 if (out > -1) close(out);
4168 if (rc == -1) { /* Set return code */
4170 case ENOENT: rc = -3; break;
4171 case EACCES: rc = -4; break;
4177 if (rc > -1 && ckxsyslog >= SYSLG_FC && ckxlogging) {
4179 syslog(LOG_INFO,"file[] %s: copy to %s failed (%m)", fullname, tmp2);
4181 syslog(LOG_INFO,"file[] %s: copy to %s ok", fullname, tmp2);
4183 #endif /* CKSYSLOG */
4190 Fills in a Kermit file attribute structure for the file which is to be sent.
4191 Returns 0 on success with the structure filled in, or -1 on failure.
4192 If any string member is null, then it should be ignored.
4193 If any numeric member is -1, then it should be ignored.
4199 #endif /* CK_GPERMS */
4203 #define S_IRUSR 0400
4204 #endif /* S_IRUSR */
4206 #define S_IXUSR 0200
4207 #endif /* S_IWUSR */
4209 #define S_IXUSR 0100
4210 #endif /* S_IXUSR */
4217 #endif /* S_IXUSR */
4218 #endif /* S_IWUSR */
4219 #endif /* S_IRUSR */
4221 static char gperms[2];
4223 #endif /* CK_GPERMS */
4225 static char lperms[24];
4228 static char xlperms[24];
4230 /* Z S E T P E R M -- Set permissions of a file */
4233 zsetperm(f,code) char * f; int code; {
4239 #endif /* CK_SCO32V4 */
4241 if (inserver && guest) {
4242 debug(F110,"zsetperm guest",f,0);
4247 debug(F111,"zsetperm error",f,errno);
4250 debug(F111,"zsetperm ok",f,mask);
4254 /* Z G P E R M -- Get permissions of a file as an octal string */
4257 zgperm(f) char *f; {
4258 extern int diractive;
4259 int x; char *s = (char *)xlperms;
4261 debug(F110,"zgperm",f,0);
4262 if (!f) return("----------");
4263 if (!*f) return("----------");
4266 debug(F111,"zgperm setroot",ckroot,ckrootset);
4267 if (ckrootset) if (!zinroot(f)) {
4268 debug(F110,"zgperm setroot violation",f,0);
4269 return("----------");
4277 #endif /* USE_LSTAT */
4279 debug(F101,"STAT","",9);
4281 return("----------");
4282 sprintf(s,"%o",buf.st_mode);
4283 debug(F110,"zgperm",s,0);
4287 /* Like zgperm() but returns permissions in "ls -l" string format */
4289 static char xsperms[24];
4292 ziperm(f) char * f; {
4293 extern int diractive;
4294 int x; char *s = (char *)xsperms;
4296 unsigned int perms = 0;
4298 debug(F110,"ziperm",f,0);
4300 if (!f) return(NULL);
4301 if (!*f) return(NULL);
4303 if (diractive && zgfs_mode != 0) {
4304 perms = zgfs_mode; /* zgetfs() already got them */
4310 #endif /* USE_LSTAT */
4312 debug(F101,"STAT","",10);
4314 return("----------");
4315 perms = buf.st_mode;
4317 switch (perms & S_IFMT) {
4321 case S_IFCHR: /* Character special */
4324 case S_IFBLK: /* Block special */
4327 case S_IFREG: /* Regular */
4331 case S_IFLNK: /* Symbolic link */
4334 #endif /* S_IFLNK */
4336 case S_IFSOCK: /* Socket */
4339 #endif /* S_IFSOCK */
4343 case S_IFIFO: /* FIFO */
4346 #endif /* COHERENT */
4348 #endif /* S_IFIFO */
4350 case S_IFWHT: /* Whiteout */
4353 #endif /* S_IFWHT */
4354 default: /* Unknown */
4358 if (perms & S_IRUSR) /* Owner's permissions */
4362 if (perms & S_IWUSR)
4366 switch (perms & (S_IXUSR | S_ISUID)) {
4376 case S_IXUSR | S_ISUID:
4380 if (perms & S_IRGRP) /* Group permissions */
4384 if (perms & S_IWGRP)
4388 switch (perms & (S_IXGRP | S_ISGID)) {
4398 case S_IXGRP | S_ISGID:
4402 if (perms & S_IROTH) /* World permissions */
4406 if (perms & S_IWOTH)
4414 perms & (S_IXOTH | S_ISVTX)
4427 case S_IXOTH | S_ISVTX:
4433 debug(F110,"ziperm",xsperms,0);
4434 return((char *)xsperms);
4440 zgperm(f) char *f; {
4441 return("----------");
4444 ziperms(f) char *f; {
4445 return("----------");
4447 #endif /* CK_PERMS */
4450 zsattr(xx) struct zattr *xx; {
4454 k = iflen % 1024L; /* File length in K */
4455 if (k != 0L) k = 1L;
4456 xx->lengthk = (iflen / 1024L) + k;
4457 xx->type.len = 0; /* File type can't be filled in here */
4460 xx->date.val = zfcdat(nambuf); /* File creation date */
4461 xx->date.len = (int)strlen(xx->date.val);
4466 xx->creator.len = 0; /* File creator */
4467 xx->creator.val = "";
4468 xx->account.len = 0; /* File account */
4469 xx->account.val = "";
4470 xx->area.len = 0; /* File area */
4472 xx->password.len = 0; /* Area password */
4473 xx->password.val = "";
4474 xx->blksize = -1L; /* File blocksize */
4475 xx->xaccess.len = 0; /* File access */
4476 xx->xaccess.val = "";
4477 xx->encoding.len = 0; /* Transfer syntax */
4478 xx->encoding.val = 0;
4479 xx->disp.len = 0; /* Disposition upon arrival */
4481 xx->lprotect.len = 0; /* Local protection */
4482 xx->lprotect.val = "";
4483 xx->gprotect.len = 0; /* Generic protection */
4484 xx->gprotect.val = "";
4486 if (*nambuf) x = stat(nambuf,&buf);
4487 debug(F101,"STAT","",11);
4489 debug(F111,"zsattr buf.st_mode & 0777",nambuf,buf.st_mode & 0777);
4490 /* UNIX filemode as an octal string without filetype bits */
4491 sprintf(lperms,"%o",buf.st_mode & 0777);
4492 xx->lprotect.len = (int)strlen(lperms);
4493 xx->lprotect.val = (char *)lperms;
4496 /* Generic permissions only if we have stat.h symbols defined */
4497 if (buf.st_mode & S_IRUSR) x |= 1; /* Read */
4498 if (buf.st_mode & S_IWUSR) x |= (2+16); /* Write and Delete */
4499 if (buf.st_mode & S_IXUSR) x |= 4; /* Execute */
4500 gperms[0] = tochar(x);
4502 xx->gprotect.len = 1;
4503 xx->gprotect.val = (char *)gperms;
4504 #endif /* CK_GPERMS */
4506 debug(F111,"zsattr lperms",xx->lprotect.val,xx->lprotect.len);
4507 debug(F111,"zsattr gperms",xx->gprotect.val,xx->gprotect.len);
4508 xx->systemid.val = "U1"; /* U1 = UNIX */
4509 xx->systemid.len = 2; /* System ID */
4510 xx->recfm.len = 0; /* Record format */
4512 xx->sysparam.len = 0; /* System-dependent parameters */
4513 xx->sysparam.val = "";
4514 xx->length = iflen; /* Length */
4518 /* Z F C D A T -- Get file creation date */
4520 Call with pointer to filename.
4521 On success, returns pointer to modification date in yyyymmdd hh:mm:ss format.
4522 On failure, returns pointer to null string.
4524 static char datbuf[40];
4528 zdtstr(time_t timearg)
4530 zdtstr(timearg) time_t timearg;
4531 #endif /* CK_ANSIC */
4536 struct tm * time_stamp;
4537 struct tm * localtime();
4540 debug(F101,"zdtstr timearg","",timearg);
4543 time_stamp = localtime(&(timearg));
4545 debug(F100,"localtime returns null","",0);
4549 We assume that tm_year is ALWAYS years since 1900.
4550 Any platform where this is not the case will have problems
4553 yy = time_stamp->tm_year; /* Year - 1900 */
4554 debug(F101,"zdtstr tm_year","",time_stamp->tm_year);
4556 debug(F101,"zstrdt YEAR-2000 ALERT 1: localtime year","",yy);
4559 debug(F101,"zdatstr year","",yy);
4561 if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11)
4563 if (time_stamp->tm_mday < 0 || time_stamp->tm_mday > 31)
4565 if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 23)
4567 if (time_stamp->tm_min < 0 || time_stamp->tm_min > 59)
4569 ss = time_stamp->tm_sec; /* Seconds */
4570 if (ss < 0 || ss > 59) /* Some systems give a BIG number */
4574 /* For some reason, 2.1x BSD sprintf gets the last field wrong. */
4575 "%04d%02d%02d %02d:%02d:00",
4577 "%04d%02d%02d %02d:%02d:%02d",
4580 time_stamp->tm_mon + 1,
4581 time_stamp->tm_mday,
4582 time_stamp->tm_hour,
4588 yy = (int)strlen(datbuf);
4589 debug(F111,"zdatstr",datbuf,yy);
4590 if (yy > 17) datbuf[17] = '\0';
4592 #endif /* TIMESTAMP */
4596 zfcdat(name) char *name; {
4599 extern int diractive;
4611 debug(F111,"zfcdat setroot",ckroot,ckrootset);
4612 if (ckrootset) if (!zinroot(name)) {
4613 debug(F110,"zfcdat setroot violation",name,0);
4620 s = tilde_expand(s);
4628 debug(F111,"zfcdat",s,diractive);
4630 if (diractive && zgfs_mtime) {
4635 x = lstat(s,&buffer);
4636 debug(F101,"STAT","",12);
4637 debug(F101,"zfcdat lstat","",x);
4639 #endif /* USE_LSTAT */
4640 x = stat(s,&buffer);
4641 debug(F101,"STAT","",13);
4642 debug(F101,"zfcdat stat","",x);
4645 #endif /* USE_LSTAT */
4648 debug(F111,"zfcdat stat failed",s,errno);
4650 debug(F111,"zfcdat lstat failed",s,errno);
4651 #endif /* USE_LSTAT */
4654 debug(F101,"zfcdat buffer.st_mtime","",buffer.st_mtime);
4655 mtime = buffer.st_mtime;
4657 return(zdtstr(mtime));
4660 #endif /* TIMESTAMP */
4665 /* Z S T R D T -- Converts local date string to internal representation */
4667 In our case (UNIX) this is seconds since midnite 1 Jan 1970 UTC,
4668 suitable for comparison with UNIX file dates. As far as I know, there is
4669 no library or system call -- at least nothing reasonably portable -- to
4670 convert local time to UTC.
4673 zstrdt(date,len) char * date; int len; {
4676 SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime().
4677 ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in
4678 dependence on the XPG4 supplement presence. So always use
4679 what the system header file supplies in ODT 3.0...
4683 extern void ftime(); /* extern void ftime(struct timeb *) */
4684 #endif /* _SCO_DS */
4689 #endif /* M_XENIX */
4691 extern struct tm * localtime();
4693 /* And this should have been declared always through a header file */
4705 int i, n, isleapyear;
4706 /* J F M A M J J A S O N D */
4707 /* 31 28 31 30 31 30 31 31 30 31 30 31 */
4709 int monthdays [13] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 };
4711 struct tm *time_stamp;
4714 struct timeval tp[2];
4715 long xtimezone = 0L;
4719 time_t timep[2]; /* New access and modificaton time */
4722 long timezone; /* In case timezone not defined in .h file */
4731 #endif /* SYSUTIMEH */
4737 static struct timeb tbp;
4744 debug(F111,"zstrdt",date,len);
4749 || (date[11] != ':')
4750 || (date[14] != ':') ) {
4751 debug(F111,"Bad creation date ",date,len);
4754 debug(F111,"zstrdt date check 1",date,len);
4755 for(i = 0; i < 8; i++) {
4756 if (!isdigit(date[i])) {
4757 debug(F111,"Bad creation date ",date,len);
4761 debug(F111,"zstrdt date check 2",date,len);
4764 for (; i < 16; i += 3) {
4765 if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) {
4766 debug(F111,"Bad creation date ",date,len);
4770 debug(F111,"zstrdt date check 3",date,len);
4773 #ifdef COMMENT /* was BSD44 */
4775 man gettimeofday on BSDI 3.1 says:
4776 "The timezone field is no longer used; timezone information is stored out-
4777 side the kernel. See ctime(3) for more information." So this chunk of
4778 code is effectively a no-op, at least in BSDI 3.x.
4782 struct timezone tzp;
4783 x = gettimeofday(NULL, &tzp);
4784 debug(F101,"zstrdt BSD44 gettimeofday","",x);
4786 xtimezone = tzp.tz_minuteswest * 60L;
4789 debug(F101,"zstrdt BSD44 timezone","",xtimezone);
4793 debug(F100,"zstrdt BSD calling ftime","",0);
4795 debug(F100,"zstrdt BSD back from ftime","",0);
4796 timezone = tbp.timezone * 60L;
4797 debug(F101,"zstrdt BSD timezone","",timezone);
4800 tzset(); /* Set timezone */
4803 if ((tz = getenv("TZ")) == NULL)
4804 timezone = 0; /* UTC/GMT */
4806 timezone = atoi(&tz[3]); /* Set 'timezone'. */
4809 #endif /* SVORPOSIX */
4811 #endif /* COMMENT (was BSD44) */
4813 debug(F100,"zstrdt so far so good","",0);
4816 for (i = 0; i < 4; i++) /* Fix the year */
4820 debug(F111,"zstrdt year",s,n);
4822 debug(F100,"zstrdt fails - year","",n);
4826 /* Previous year's leap days. This won't work after year 2100. */
4828 isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
4829 days = (long) (n - 1970) * 365;
4830 days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
4834 for (i = 4; i < 16; i += 2) {
4839 case 4: /* MM: month */
4840 if ((n < 1 ) || ( n > 12)) {
4841 debug(F111,"zstrdt 4 bad date ",date,len);
4844 days += monthdays [n];
4845 if (isleapyear && n > 2)
4849 case 6: /* DD: day */
4850 if ((n < 1 ) || ( n > 31)) {
4851 debug(F111,"zstrdt 6 bad date ",date,len);
4854 tmx = (days + n - 1) * 24L * 60L * 60L;
4855 i++; /* Skip the space */
4858 case 9: /* hh: hour */
4859 if ((n < 0 ) || ( n > 23)) {
4860 debug(F111,"zstrdt 9 bad date ",date,len);
4863 tmx += n * 60L * 60L;
4864 i++; /* Skip the colon */
4867 case 12: /* mm: minute */
4868 if ((n < 0 ) || ( n > 59)) {
4869 debug(F111,"zstrdt 12 bad date ",date,len);
4872 #ifdef COMMENT /* (was BSD44) */ /* Correct for time zone */
4874 debug(F101,"zstrdt BSD44 tmx","",tmx);
4879 #ifndef CONVEX9 /* Don't yet know how to do this here */
4881 tmx += (long) timezone;
4885 extern time_t tzoffset;
4894 #endif /* CONVEX9 */
4896 #endif /* COMMENT (was BSD44) */
4898 i++; /* Skip the colon */
4901 case 15: /* ss: second */
4902 if ((n < 0 ) || ( n > 59)) {
4903 debug(F111,"zstrdt 15 bad date ",date,len);
4908 time_stamp = localtime(&tmx);
4909 debug(F101,"zstrdt tmx 1","",tmx);
4913 /* Why was this here? */
4914 time_stamp = localtime(&tmx);
4915 debug(F101,"zstrdt tmx 2","",tmx);
4916 #endif /* COMMENT */
4918 { /* New to 7.0 - Works in at at least BSDI 3.1 and FreeBSD 2.2.7 */
4920 zz = time_stamp->tm_gmtoff; /* Seconds away from Zero Meridian */
4921 debug(F101,"zstrdt BSD44 tm_gmtoff","",zz);
4923 debug(F101,"zstrdt BSD44 tmx 3 (GMT)","",tmx);
4927 Daylight Savings Time adjustment.
4928 Do this everywhere BUT in BSD44 because in BSD44,
4929 tm_gmtoff also includes the DST adjustment.
4931 if (time_stamp->tm_isdst) {
4933 debug(F101,"zstrdt tmx 3 (DST)","",tmx);
4936 n = time_stamp->tm_year;
4946 /* Z L O C A L T I M E -- GMT/UTC time string to local time string */
4949 Call with: "yyyymmdd hh:mm:ss" GMT/UTC date-time.
4950 Returns: "yyyymmdd hh:mm:ss" local date-time on success, NULL on failure.
4952 static char zltimbuf[64];
4955 zlocaltime(gmtstring) char * gmtstring; {
4958 SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime().
4959 ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in
4960 dependence on the XPG4 supplement presence. So always use
4961 what the system header file supplies in ODT 3.0...
4965 extern void ftime(); /* extern void ftime(struct timeb *) */
4966 #endif /* _SCO_DS */
4971 #endif /* M_XENIX */
4973 extern struct tm * localtime();
4975 /* And this should have been declared always through a header file */
4987 int i, n, x, isleapyear;
4988 /* J F M A M J J A S O N D */
4989 /* 31 28 31 30 31 30 31 31 30 31 30 31 */
4991 int monthdays [13] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 };
4993 struct tm *time_stamp;
4996 struct timeval tp[2];
5000 time_t timep[2]; /* New access and modificaton time */
5010 #endif /* SYSUTIMEH */
5015 static struct timeb tbp;
5018 char * date = gmtstring;
5022 debug(F111,"zlocaltime",date,len);
5027 || (date[11] != ':')
5028 || (date[14] != ':') ) {
5029 debug(F111,"Bad creation date ",date,len);
5032 debug(F111,"zlocaltime date check 1",date,len);
5033 for(i = 0; i < 8; i++) {
5034 if (!isdigit(date[i])) {
5035 debug(F111,"Bad creation date ",date,len);
5039 debug(F111,"zlocaltime date check 2",date,len);
5042 for (; i < 16; i += 3) {
5043 if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) {
5044 debug(F111,"Bad creation date ",date,len);
5048 debug(F111,"zlocaltime date check 3",date,len);
5050 debug(F100,"zlocaltime so far so good","",0);
5053 for (i = 0; i < 4; i++) /* Fix the year */
5057 debug(F111,"zlocaltime year",s,n);
5059 debug(F100,"zlocaltime fails - year","",n);
5063 /* Previous year's leap days. This won't work after year 2100. */
5065 isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
5066 days = (long) (n - 1970) * 365;
5067 days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
5071 for (i = 4; i < 16; i += 2) {
5076 case 4: /* MM: month */
5077 if ((n < 1 ) || ( n > 12)) {
5078 debug(F111,"zlocaltime 4 bad date ",date,len);
5081 days += monthdays [n];
5082 if (isleapyear && n > 2)
5086 case 6: /* DD: day */
5087 if ((n < 1 ) || ( n > 31)) {
5088 debug(F111,"zlocaltime 6 bad date ",date,len);
5091 tmx = (days + n - 1) * 24L * 60L * 60L;
5092 i++; /* Skip the space */
5095 case 9: /* hh: hour */
5096 if ((n < 0 ) || ( n > 23)) {
5097 debug(F111,"zlocaltime 9 bad date ",date,len);
5100 tmx += n * 60L * 60L;
5101 i++; /* Skip the colon */
5104 case 12: /* mm: minute */
5105 if ((n < 0 ) || ( n > 59)) {
5106 debug(F111,"zlocaltime 12 bad date ",date,len);
5110 i++; /* Skip the colon */
5113 case 15: /* ss: second */
5114 if ((n < 0 ) || ( n > 59)) {
5115 debug(F111,"zlocaltime 15 bad date ",date,len);
5122 At this point tmx is the time_t representation of the argument date-time
5123 string without any timezone or DST adjustments. Therefore it should be
5124 the same as the time_t representation of the GMT/UTC time. Now we should
5125 be able to feed it to localtime() and have it converted to a struct tm
5126 representing the local time equivalent of the given UTC time.
5128 time_stamp = localtime(&tmx);
5133 /* Now we simply reformat the struct tm to a string */
5135 x = time_stamp->tm_year;
5136 if (time_stamp->tm_year < 70 || time_stamp->tm_year > 8099)
5138 if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11)
5140 if (time_stamp->tm_mday < 1 || time_stamp->tm_mday > 31)
5142 if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 24)
5144 if (time_stamp->tm_min < 0 || time_stamp->tm_min > 60)
5146 if (time_stamp->tm_sec < 0 || time_stamp->tm_sec > 60)
5148 sprintf(zltimbuf,"%04d%02d%02d %02d:%02d:%02d",
5149 time_stamp->tm_year + 1900,
5150 time_stamp->tm_mon + 1,
5151 time_stamp->tm_mday,
5152 time_stamp->tm_hour,
5156 return((char *)zltimbuf);
5158 #endif /* ZLOCALTIME */
5159 #endif /* NOTIMESTAMP */
5161 /* Z S T I M E -- Set modification date/time+permissions for incoming file */
5164 f = pointer to name of existing file.
5165 yy = pointer to a Kermit file attribute structure in which yy->date.val
5166 is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00.
5167 yy->lprotect.val & yy->gprotect.val are permission/protection values.
5168 x = is a function code: 0 means to set the file's attributes as given.
5169 1 means compare the date in struct yy with the file creation date.
5171 -1 on any kind of error.
5172 0 if x is 0 and the attributes were set successfully.
5173 0 if x is 1 and date from attribute structure <= file creation date.
5174 1 if x is 1 and date from attribute structure > file creation date.
5178 char *f; struct zattr *yy; int x;
5180 int r = -1; /* Return code */
5183 #endif /* CK_PERMS */
5186 /* It is ifdef'd TIMESTAMP because it might not work on V7. bk@kullmar.se. */
5190 extern int utimes();
5197 /* At least, the declarations for int functions are not needed anyway */
5200 struct timeval tp[2];
5205 time_t timep[2]; /* New access and modificaton time */
5208 long timezone; /* In case not defined in .h file */
5217 #endif /* SYSUTIMEH */
5224 if (!*f) return(-1);
5225 if (!yy) return(-1);
5227 debug(F110,"zstime",f,0);
5228 debug(F111,"zstime date",yy->date.val,yy->date.len);
5231 debug(F111,"zstime setroot",ckroot,ckrootset);
5232 if (ckrootset) if (!zinroot(f)) {
5233 debug(F110,"zstime setroot violation",f,0);
5238 if (yy->date.len == 0) { /* No date in struct */
5239 if (yy->lprotect.len != 0) { /* So go do permissions */
5242 debug(F100,"zstime: nothing to do","",0);
5246 if ((tm = zstrdt(yy->date.val,yy->date.len)) < 0) {
5247 debug(F101,"zstime: zstrdt fails","",0);
5250 debug(F101,"zstime: tm","",tm);
5251 debug(F111,"zstime: A-pkt date ok ",yy->date.val,yy->date.len);
5253 if (stat(f,&sb)) { /* Get the time for the file */
5254 debug(F101,"STAT","",14);
5255 debug(F111,"zstime: Can't stat file:",f,errno);
5258 debug(F101,"STAT","",15);
5264 int i, x = 0, xx, flag = 0;
5269 debug(F111,"zstime lperms",yy->lprotect.val,yy->lprotect.len);
5270 debug(F111,"zstime gperms",yy->gprotect.val,yy->gprotect.len);
5271 debug(F110,"zstime system id",yy->systemid.val,0);
5272 sprintf(obuf,"%o",sb.st_mode);
5273 debug(F110,"zstime file perms before",obuf,0);
5278 debug(F101,"zstime isguest","",isguest);
5279 debug(F101,"zstime ckxperms","",ckxperms);
5282 /* Clear owner permissions */
5283 sb.st_mode &= (unsigned) 0177077; /* (16 bits) */
5285 /* Set permissions from ckxperms variable */
5286 sb.st_mode = ckxperms;
5287 #endif /* COMMENT */
5288 debug(F101,"zstime isguest sb.st_mode","",sb.st_mode);
5290 /* We already set them in zopeno() */
5292 #endif /* COMMENT */
5295 #endif /* CK_LOGIN */
5296 if ((yy->lprotect.len > 0 && /* Have local-format permissions */
5297 yy->systemid.len > 0 && /* from A-packet... */
5299 !strcmp(yy->systemid.val,"U1") /* AND you are same as me */
5303 ) || (yy->lprotect.len < 0) /* OR by inheritance from old file */
5306 s = yy->lprotect.val; /* UNIX filemode */
5307 xx = yy->lprotect.len;
5308 if (xx < 0) /* len < 0 means inheritance */
5310 for (i = 0; i < xx; i++) { /* Decode octal string */
5311 if (*s <= '7' && *s >= '0') {
5312 x = 8 * x + (int)(*s) - '0';
5320 sprintf(obuf,"%o",x);
5321 debug(F110,"zstime octal lperm",obuf,0);
5323 } else if (!flag && yy->gprotect.len > 0) {
5329 #endif /* CK_SCO32V4 */
5330 mask = umask(0); /* Get umask */
5331 debug(F101,"zstime mask 1","",mask);
5332 umask(mask); /* Put it back */
5333 mask ^= 0777; /* Flip the bits */
5334 debug(F101,"zstime mask 2","",mask);
5335 g = xunchar(*(yy->gprotect.val)); /* Decode generic protection */
5336 debug(F101,"zstime gprotect","",g);
5338 debug(F100,"zstime S_IRUSR","",0);
5339 if (g & 1) x |= S_IRUSR; /* Read permission */
5341 #endif /* S_IRUSR */
5343 debug(F100,"zstime S_IWUSR","",0);
5344 if (g & 2) x |= S_IWUSR; /* Write permission */
5345 if (g & 16) x |= S_IWUSR; /* Delete permission */
5347 #endif /* S_IWUSR */
5349 debug(F100,"zstime S_IXUSR","",0);
5350 if (g & 4) /* Has execute permission bit */
5352 else /* Doesn't have it */
5353 mask &= 0666; /* so also clear it out of mask */
5355 #endif /* S_IXUSR */
5356 debug(F101,"zstime mask x","",x);
5358 debug(F101,"zstime mask x|mask","",x);
5360 debug(F101,"zstime flag","",flag);
5363 debug(F101,"zstime S_IFMT x","",x);
5364 sb.st_mode = (sb.st_mode & S_IFMT) | x;
5368 debug(F101,"zstime _IFMT x","",x);
5369 sb.st_mode = (sb.st_mode & _IFMT) | x;
5375 sprintf(obuf,"%04o",sb.st_mode);
5376 debug(F111,"zstime file perms after",obuf,setperms);
5379 #endif /* CK_PERMS */
5381 debug(F101,"zstime: sb.st_atime","",sb.st_atime);
5384 tp[0].tv_sec = sb.st_atime; /* Access time first */
5385 tp[1].tv_sec = tm; /* Update time second */
5386 debug(F100,"zstime: BSD44 modtime","",0);
5389 tp.timep[0] = tm; /* Set modif. time to creation date */
5390 tp.timep[1] = sb.st_atime; /* Don't change the access time */
5391 debug(F100,"zstime: V7 modtime","",0);
5394 tp.modtime = tm; /* Set modif. time to creation date */
5395 tp.actime = sb.st_atime; /* Don't change the access time */
5396 debug(F100,"zstime: SYSUTIMEH modtime","",0);
5398 tp.mtime = tm; /* Set modif. time to creation date */
5399 tp.atime = sb.st_atime; /* Don't change the access time */
5400 debug(F100,"zstime: default modtime","",0);
5401 #endif /* SYSUTIMEH */
5405 switch (x) { /* Execute desired function */
5406 case 0: /* Set the creation date of the file */
5407 #ifdef CK_PERMS /* And permissions */
5409 NOTE: If we are inheriting permissions from a previous file, and the
5410 previous file was a directory, this would turn the new file into a directory
5411 too, but it's not, so we try to unset the right bit. Luckily, this code
5412 will probably never be executed since the upper level modules do not allow
5413 reception of a file that has the same name as a directory.
5415 NOTE 2: We change the permissions *before* we change the modification time,
5416 otherwise changing the permissions would set the mod time to the present
5421 debug(F101,"zstime setperms","",setperms);
5422 if (S_ISDIR(sb.st_mode)) {
5423 debug(F101,"zstime DIRECTORY bit on","",sb.st_mode);
5424 sb.st_mode ^= 0040000;
5425 debug(F101,"zstime DIRECTORY bit off","",sb.st_mode);
5428 x = chmod(f,sb.st_mode);
5429 debug(F101,"zstime chmod","",x);
5432 if (x < 0) return(-1);
5433 #endif /* CK_PERMS */
5435 if (!setdate) /* We don't have a date */
5436 return(0); /* so skip the following... */
5444 ) { /* Fix modification time */
5445 debug(F111,"zstime 0: can't set modtime for file",f,errno);
5448 /* Including the modtime here is not portable */
5449 debug(F110,"zstime 0: modtime set for file",f,0);
5454 case 1: /* Compare the dates */
5456 This was st_atime, which was wrong. We want the file-data modification
5459 debug(F111,"zstime 1: compare",f,sb.st_mtime);
5460 debug(F111,"zstime 1: compare","packet",tm);
5462 r = (sb.st_mtime < tm) ? 0 : 1;
5465 default: /* Error */
5468 #endif /* TIMESTAMP */
5472 /* Find initialization file. */
5477 /* nothing here for Unix. This function added for benefit of VMS Kermit. */
5480 #endif /* NOTUSED */
5483 /* Historical -- not used in Unix any more (2001-11-03) */
5486 zmail(p,f) char *p; char *f; { /* Send file f as mail to address p */
5488 Returns 0 on success
5489 2 if mail delivered but temp file can't be deleted
5490 -2 if mail can't be delivered
5491 -1 on file access error
5492 The UNIX version always returns 0 because it can't get a good return
5500 #endif /* CK_LOGIN */
5503 if (!*f) return(-1);
5506 debug(F111,"zmail setroot",ckroot,ckrootset);
5507 if (ckrootset) if (!zinroot(f)) {
5508 debug(F110,"zmail setroot violation",f,0);
5514 /* The idea is to use /usr/ucb/mail, rather than regular mail, so that */
5515 /* a subject line can be included with -s. Since we can't depend on the */
5516 /* user's path, we use the convention that /usr/ucb/Mail = /usr/ucb/mail */
5517 /* and even if Mail has been moved to somewhere else, this should still */
5518 /* find it... The search could be made more reliable by actually using */
5519 /* access() to see if /usr/ucb/Mail exists. */
5522 n = n + n + 15 + (int)strlen(p);
5528 sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
5530 sprintf(zmbuf,"Mail -s %c%s%c %s < %s", '"', f, '"', p, f);
5531 #endif /* DGUX540 */
5536 sprintf(zmbuf,"mail %s < %s", p, f);
5538 sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
5547 #endif /* NOFRILLS */
5552 zprint(p,f) char *p; char *f; { /* Print file f with options p */
5553 extern char * printername; /* From ckuus3.c */
5554 extern int printpipe;
5560 #endif /* CK_LOGIN */
5563 if (!*f) return(-1);
5566 debug(F111,"zprint setroot",ckroot,ckrootset);
5567 if (ckrootset) if (!zinroot(f)) {
5568 debug(F110,"zprint setroot violation",f,0);
5573 debug(F110,"zprint file",f,0);
5574 debug(F110,"zprint flags",p,0);
5575 debug(F110,"zprint printername",printername,0);
5576 debug(F101,"zprint printpipe","",printpipe);
5580 Note use of standard input redirection. In some systems, lp[r] runs
5581 setuid to lp (or ...?), so if user has sent a file into a directory
5582 that lp does not have read access to, it can't be printed unless it is
5583 fed to lp[r] as standard input.
5585 if (printpipe && printername) {
5586 n = 8 + (int)strlen(f) + (int)strlen(printername);
5589 sprintf(zmbuf,"cat %s | %s", f, printername);
5590 } else if (printername) {
5591 n = 8 + (int)strlen(f) + (int)strlen(printername);
5594 sprintf(zmbuf,"cat %s >> %s", f, printername);
5596 n = 4 + (int)strlen(PRINTCMD) + (int)strlen(p) + (int)strlen(f);
5599 sprintf(zmbuf,"%s %s < %s", PRINTCMD, p, f);
5601 debug(F110,"zprint command",zmbuf,0);
5603 #else /* Not UNIX */
5608 #endif /* NOFRILLS */
5610 /* Wildcard expansion functions... */
5612 static char scratch[MAXPATH+4]; /* Used by both methods */
5614 static int oldmtchs = 0; /* Let shell (ls) expand them. */
5616 static char *lscmd = "/bin/ls -d"; /* Command to use. */
5618 static char *lscmd = "echo"; /* Command to use. */
5619 #endif /* COMMENT */
5623 shxpand(pat,namlst,len) char *pat, *namlst[]; int len; {
5624 char *fgbuf = NULL; /* Buffer for forming ls command */
5625 char *p, *q; /* Workers */
5627 int i, x, retcode, itsadir;
5630 x = (int)strlen(pat) + (int)strlen(lscmd) + 3; /* Length of ls command */
5631 for (i = 0; i < oldmtchs; i++) { /* Free previous file list */
5632 if (namlst[i] ) { /* If memory is allocated */
5633 free(namlst[i]); /* Free the memory */
5634 namlst[i] = NULL ; /* Remember no memory is allocated */
5637 oldmtchs = 0 ; /* Remember there are no matches */
5638 fgbuf = malloc(x); /* Get buffer for command */
5639 if (!fgbuf) return(-1); /* Fail if cannot */
5640 ckmakmsg(fgbuf,x,lscmd," ",pat,NULL); /* Form the command */
5641 zxcmd(ZIFILE,fgbuf); /* Start the command */
5642 i = 0; /* File counter */
5643 p = scratch; /* Point to scratch area */
5644 retcode = -1; /* Assume failure */
5645 while ((x = zminchar()) != -1) { /* Read characters from command */
5647 if (c == ' ' || c == '\n') { /* Got newline or space? */
5648 *p = '\0'; /* Yes, terminate string */
5649 p = scratch; /* Point back to beginning */
5650 if (zchki(p) == -1) /* Does file exist? */
5651 continue; /* No, continue */
5652 itsadir = isdir(p); /* Yes, is it a directory? */
5653 if (xdironly && !itsadir) /* Want only dirs but this isn't */
5654 continue; /* so skip. */
5655 if (xfilonly && itsadir) /* It's a dir but want only files */
5656 continue; /* so skip. */
5657 x = (int)strlen(p); /* Keep - get length of name */
5658 q = malloc(x+1); /* Allocate space for it */
5659 if (!q) goto shxfin; /* Fail if space can't be obtained */
5660 strcpy(q,scratch); /* (safe) Copy name to space */
5661 namlst[i++] = q; /* Copy pointer to name into array */
5662 if (i >= len) goto shxfin; /* Fail if too many */
5663 } else { /* Regular character */
5664 *p++ = c; /* Copy it into scratch area */
5667 retcode = i; /* Return number of matching files */
5668 shxfin: /* Common exit point */
5669 free(fgbuf); /* Free command buffer */
5671 zclosf(ZIFILE); /* Delete the command fork. */
5672 oldmtchs = i; /* Remember how many files */
5678 Directory-reading functions for UNIX originally written for C-Kermit 4.0
5679 by Jeff Damens, CUCCA, 1984.
5681 static char * xpat = NULL; /* Global copy of fgen() pattern */
5682 static char * xpatlast = NULL; /* Rightmost segment of pattern*/
5683 static int xpatslash = 0; /* Slash count in pattern */
5684 static int xpatwild = 0; /* Original pattern is wild */
5685 static int xleafwild = 0; /* Last segment of pattern is wild */
5686 static int xpatabsolute = 0;
5693 /* S P L I T P A T H */
5696 Splits the slash-separated portions of the argument string into
5697 a list of path structures. Returns the head of the list. The
5698 structures are allocated by malloc, so they must be freed.
5699 Splitpath is used internally by the filename generator.
5705 A linked list of the slash-separated segments of the input.
5707 static struct path *
5708 splitpath(p) char *p; {
5709 struct path *head,*cur,*prv;
5712 debug(F111,"splitpath",p,xrecursive);
5715 if (!p) return(NULL);
5716 if (!*p) return(NULL);
5718 if (!strcmp(p,"**")) { /* Fix this */
5721 if (ISDIRSEP(*p)) p++; /* Skip leading slash if any */
5723 /* Make linked list of path segments from pattern */
5726 cur = (struct path *) malloc(sizeof (struct path));
5727 debug(F101,"splitpath malloc","",cur);
5729 debug(F100,"splitpath malloc failure","",0);
5731 return((struct path *)NULL);
5734 if (head == NULL) /* First, make list head */
5736 else /* Not first, link into chain */
5738 prv = cur; /* Link from previous to this one */
5741 /* treat backslash as "../" */
5742 if (bslash && *p == bslash) {
5743 strcpy(cur->npart, ".."); /* safe */
5746 for (i=0; i < MAXNAMLEN && *p && *p != '/' && *p != bslash; i++)
5747 cur -> npart[i] = *p++;
5748 cur -> npart[i] = '\0'; /* end this segment */
5750 while (*p && *p != '/' && *p != bslash)
5755 /* General case (UNIX) */
5756 for (i = 0; i < MAXNAMLEN && !ISDIRSEP(*p) && *p != '\0'; i++) {
5757 cur -> npart[i] = *p++;
5760 cur -> npart[i] = '\0'; /* End this path segment */
5762 while (!ISDIRSEP(*p) && *p != '\0') p++;
5769 makestr(&xpatlast,prv -> npart);
5770 debug(F110,"splitpath xpatlast",xpatlast,0);
5773 /* Show original path list */
5775 for (i = 0, cur = head; cur; i++) {
5776 debug(F111,"SPLITPATH",cur -> npart, i);
5784 /* F G E N -- Generate File List */
5787 File name generator. It is passed a string, possibly containing wildcards,
5788 and an array of character pointers. It finds all the matching filenames and
5789 stores pointers to them in the array. The returned strings are allocated
5790 from a static buffer local to this module (so the caller doesn't have to
5791 worry about deallocating them); this means that successive calls to fgen
5792 will wipe out the results of previous calls.
5795 A wildcard string, an array to write names to, the length of the array.
5798 The number of matches.
5799 The array is filled with filenames that matched the pattern.
5800 If there wasn't enough room in the array, -1 is returned.
5802 Originally by: Jeff Damens, CUCCA, 1984. Many changes since then.
5805 fgen(pat,resarry,len) char *pat,*resarry[]; int len; {
5812 int tilde = 0, bquote = 0;
5814 if ((namechars = getenv("NAMECHARS")) != NULL) {
5815 if (ckstrchr(namechars, '~' ) != NULL) tilde = '~';
5816 if (ckstrchr(namechars, '\\') != NULL) bslash = '\\';
5817 if (ckstrchr(namechars, '`' ) != NULL) bquote = '`';
5819 tilde = '~'; bslash = '\\'; bquote = '`';
5823 /* copy "`node_data", etc. anchors */
5824 if (bquote && *pat == bquote)
5825 while (*pat && *pat != '/' && *pat != bslash)
5827 else if (tilde && *pat == tilde)
5831 if (sptr == scratch) {
5832 strcpy(scratch,"./"); /* safe */
5835 if (!(head = splitpath(pat))) return(-1);
5837 #else /* not aegis */
5839 debug(F111,"fgen pat",pat,len);
5840 debug(F110,"fgen current directory",zgtdir(),0);
5841 debug(F101,"fgen stathack","",stathack);
5848 if (!(head = splitpath(pat))) /* Make the path segment list */
5854 if (strncmp(pat,"./",2) && strncmp(pat,"../",3)) {
5855 #endif /* COMMENT */
5856 if (!ISDIRSEP(*pat)) /* If name is not absolute */
5857 *sptr++ = '.'; /* put "./" in front. */
5861 #endif /* COMMENT */
5865 makestr(&xpat,pat); /* Save copy of original pattern */
5866 debug(F110,"fgen scratch",scratch,0);
5868 for (n = 0, s = xpat; *s; s++) /* How many slashes in the pattern */
5869 if (*s == DIRSEP) /* since these are fences for */
5870 n++; /* pattern matching */
5872 debug(F101,"fgen xpatslash","",xpatslash);
5874 numfnd = 0; /* None found yet */
5876 if (initspace(resarry,ssplen) < 0)
5879 xpatwild = iswild(xpat); /* Original pattern is wild? */
5880 xpatabsolute = isabsolute(xpat);
5881 xleafwild = iswild(xpatlast);
5883 debug(F111,"fgen xpat",xpat,xpatwild);
5884 debug(F111,"fgen xpatlast",xpatlast,xleafwild);
5885 debug(F101,"fgen xpatabsolute","",xpatabsolute);
5887 traverse(head,scratch,sptr); /* Go walk the directory tree. */
5888 while (head != NULL) { /* Done - free path segment list. */
5889 struct path *next = head -> fwd;
5893 debug(F101,"fgen","",numfnd);
5894 return(numfnd); /* Return the number of matches */
5897 /* Define LONGFN (long file names) automatically for BSD 2.9 and 4.2 */
5898 /* LONGFN can also be defined on the cc command line. */
5913 T R A V E R S E -- Traverse a directory tree.
5915 Walks the directory tree looking for matches to its arguments.
5916 The algorithm is, briefly:
5918 If the current pattern segment contains no wildcards, that
5919 segment is added to what we already have. If the name so far
5920 exists, we call ourselves recursively with the next segment
5921 in the pattern string; otherwise, we just return.
5923 If the current pattern segment contains wildcards, we open the name
5924 we've accumulated so far (assuming it is really a directory), then read
5925 each filename in it, and, if it matches the wildcard pattern segment, add
5926 that filename to what we have so far and call ourselves recursively on
5929 Finally, when no more pattern segments remain, we add what's accumulated
5930 so far to the result array and increment the number of matches.
5933 A pattern path list (as generated by splitpath), a string pointer that
5934 points to what we've traversed so far (this can be initialized to "/"
5935 to start the search at the root directory, or to "./" to start the
5936 search at the current directory), and a string pointer to the end of
5937 the string in the previous argument, plus the global "recursive",
5938 "xmatchdot", and "xdironly" flags.
5940 Returns: void, with:
5941 mtchs[] containing the array of filename string pointers, and:
5942 numfnd containing the number of filenames.
5944 Although it might be poor practice, the mtchs[] array is revealed to the
5945 outside in case it needs it; for example, to be sorted prior to use.
5946 (It is poor practice because not all platforms implement file lists the
5947 same way; some don't use an array at all.)
5949 Note that addresult() acts as a second-level filter; due to selection
5950 criteria outside of the pattern, it might decline to add files that
5951 this routine asks it to, e.g. because we are collecting only directory
5952 names but not the names of regular files.
5954 WARNING: In the course of C-Kermit 7.0 development, this routine became
5955 ridiculously complex, in order to meet approximately sixty specific
5956 requirements. DON'T EVEN THINK ABOUT MODIFYING THIS ROUTINE! Trust me;
5957 it is not possible to fix anything in it without breaking something else.
5958 This routine badly needs a total redesign and rewrite. Note: There may
5959 be some good applications for realpath() and/or scandir() and/or fts_blah()
5960 here, on platforms where they are available.
5963 traverse(pl,sofar,endcur) struct path *pl; char *sofar, *endcur; {
5965 /* Appropriate declarations for directory routines and structures */
5966 /* #define OPENDIR means to use opendir(), readdir(), closedir() */
5967 /* If OPENDIR not defined, we use open(), read(), close() */
5969 #ifdef DIRENT /* New way, <dirent.h> */
5971 DIR *fd, *opendir();
5972 struct dirent *dirbuf;
5973 struct dirent *readdir();
5975 #ifdef LONGFN /* Old way, <dir.h> with opendir() */
5977 DIR *fd, *opendir();
5978 struct direct *dirbuf;
5980 int fd; /* Old way, <dir.h> with open() */
5981 struct direct dir_entry;
5982 struct direct *dirbuf = &dir_entry;
5985 int mopts = 0; /* ckmatch() opts */
5986 int depth = 0; /* Directory tree depth */
5988 char nambuf[MAXNAMLEN+4]; /* Buffer for a filename */
5989 int itsadir = 0, segisdir = 0, itswild = 0, mresult, n, x /* , y */ ;
5990 struct stat statbuf; /* For file info. */
5992 debug(F101,"STAT","",16);
5993 if (pl == NULL) { /* End of path-segment list */
5994 *--endcur = '\0'; /* Terminate string, overwrite trailing slash */
5995 debug(F110,"traverse add: end of path segment",sofar,0);
5996 addresult(sofar,-1);
6000 /* This speeds up the search a lot and we still get good results */
6001 /* but it breaks the tagging of directory names done in addresult */
6002 if (xrecursive || xfilonly || xdironly || xpatslash) {
6003 itsadir = xisdir(sofar);
6004 debug(F101,"STAT","",17);
6006 itsadir = (strncmp(sofar,"./",2) == 0);
6008 itsadir = xisdir(sofar);
6009 debug(F101,"STAT","",18);
6011 debug(F111,"traverse entry sofar",sofar,itsadir);
6013 #ifdef CKSYMLINK /* We're doing symlinks? */
6014 #ifdef USE_LSTAT /* OK to use lstat()? */
6015 if (itsadir && xnolinks) { /* If not following symlinks */
6018 x = lstat(sofar,&buf);
6019 debug(F111,"traverse lstat 1",sofar,x);
6022 S_ISLNK(buf.st_mode)
6025 ((_IFMT & buf.st_mode) == _IFLNK)
6027 #endif /* S_ISLNK */
6031 #endif /* USE_LSTAT */
6032 #endif /* CKSYMLINK */
6034 if (!xmatchdot && xpatlast[0] == '.')
6036 if (!xmatchdot && xpat[0] == '.' && xpat[1] != '/' && xpat[1] != '.')
6039 /* ckmatch() options */
6041 if (xmatchdot) mopts |= 1; /* Match dot */
6042 if (!xrecursive) mopts |= 2; /* Dirsep is fence */
6044 debug(F111,"traverse entry xpat",xpat,xpatslash);
6045 debug(F111,"traverse entry xpatlast",xpatlast,xmatchdot);
6046 debug(F110,"traverse entry pl -> npart",pl -> npart,0);
6049 if (xrecursive > 0 && !itsadir) {
6050 char * s; /* Recursive descent and this is a regular file */
6051 *--endcur = '\0'; /* Terminate string, overwrite trailing slash */
6053 /* Find the nth slash from the right and match from there... */
6054 /* (n == the number of slashes in the original pattern - see fgen) */
6055 if (*sofar == '/') {
6056 debug(F110,"traverse xpatslash absolute",sofar,0);
6059 debug(F111,"traverse xpatslash relative",sofar,xpatslash);
6060 for (s = endcur - 1, n = 0; s >= sofar; s--) {
6062 if (++n >= xpatslash) {
6070 /* This speeds things up a bit. */
6071 /* If it causes trouble define NOSKIPMATCH and rebuild. */
6072 if (xpat[0] == '*' && !xpat[1])
6073 x = xmatchdot ? 1 : (s[0] != '.');
6075 #endif /* NOSKIPMATCH */
6076 x = ckmatch(xpat, s, 1, mopts); /* Match with original pattern */
6077 debug(F111,"traverse xpatslash ckmatch",s,x);
6079 debug(F110,"traverse add: recursive, match, && !isdir",sofar,0);
6080 addresult(sofar,itsadir);
6084 #endif /* RECURSIVE */
6086 debug(F111,"traverse sofar 2",sofar,0);
6088 segisdir = ((pl -> fwd) == NULL) ? 0 : 1;
6089 itswild = iswild(pl -> npart);
6091 debug(F111,"traverse segisdir",sofar,segisdir);
6092 debug(F111,"traverse itswild ",pl -> npart,itswild);
6095 if (xrecursive > 0) { /* If recursing and... */
6096 if (segisdir && itswild) /* this is a dir and npart is wild */
6097 goto blah; /* or... */
6098 else if (!xpatabsolute && !xpatwild) /* search object is nonwild */
6099 goto blah; /* then go recurse */
6101 #endif /* RECURSIVE */
6103 if (!itswild) { /* This path segment not wild? */
6105 strcpy(endcur,pl -> npart); /* (safe) Append next part. */
6106 endcur += (int)strlen(pl -> npart); /* Advance end pointer */
6109 strcpy() does not account for quoted metacharacters.
6110 We must remove the quotes before doing the stat().
6116 while ((c = *s++)) {
6127 #endif /* COMMENT */
6128 *endcur = '\0'; /* End new current string. */
6130 if (stat(sofar,&statbuf) == 0) { /* If this piece exists... */
6131 debug(F110,"traverse exists",sofar,0);
6132 *endcur++ = DIRSEP; /* add slash to end */
6133 *endcur = '\0'; /* and end the string again. */
6134 traverse(pl -> fwd, sofar, endcur);
6137 else debug(F110,"traverse not found", sofar, 0);
6142 *endcur = '\0'; /* End current string */
6143 debug(F111,"traverse sofar 3",sofar,0);
6148 /* Search is recursive or ... */
6149 /* path segment contains wildcards, have to open and search directory. */
6153 debug(F110,"traverse opening directory", sofar, 0);
6156 debug(F110,"traverse opendir()",sofar,0);
6157 if ((fd = opendir(sofar)) == NULL) { /* Can't open, fail. */
6158 debug(F101,"traverse opendir() failed","",errno);
6161 while ((dirbuf = readdir(fd)))
6162 #else /* !OPENDIR */
6163 debug(F110,"traverse directory open()",sofar,0);
6164 if ((fd = open(sofar,O_RDONLY)) < 0) {
6165 debug(F101,"traverse directory open() failed","",errno);
6168 while (read(fd, (char *)dirbuf, sizeof dir_entry))
6169 #endif /* OPENDIR */
6170 { /* Read each entry in this directory */
6175 /* On some platforms, the read[dir]() can return deleted files, */
6176 /* e.g. HP-UX 5.00. There is no point in grinding through this */
6177 /* routine when the file doesn't exist... */
6179 if ( /* There actually is an inode... */
6187 dirbuf->d_stat.st_ino != 0
6193 dirbuf->d_fileno != 0
6196 dirbuf->d_fileno != 0
6199 dirbuf->d_fileno != 0
6202 dirbuf->d_fileno != 0
6213 #endif /* __FreeBSD__ */
6214 #endif /* __386BSD__ */
6217 #endif /* SOLARIS */
6226 ckstrncpy(nambuf, /* Copy the name */
6230 if (nambuf[0] == '.') {
6231 if (!nambuf[1] || (nambuf[1] == '.' && !nambuf[2])) {
6232 debug(F110,"traverse skipping",nambuf,0);
6233 continue; /* skip "." and ".." */
6236 s = nambuf; /* Copy name to end of sofar */
6238 while ((*eos = *s)) {
6243 Now we check the file for (a) whether it is a directory, and (b) whether
6244 its name matches our pattern. If it is a directory, and if we have been
6245 told to build a recursive list, then we must descend regardless of whether
6246 it matches the pattern. If it is not a directory and it does not match
6247 our pattern, we skip it. Note: sofar is the full pathname, nambuf is
6250 /* Do this first to save pointless function calls */
6251 if (nambuf[0] == '.' && !xmatchdot) /* Dir name starts with '.' */
6254 if (xrecursive || xfilonly || xdironly || xpatslash) {
6255 itsadir = xisdir(sofar); /* See if it's a directory */
6256 debug(F101,"STAT","",19);
6261 itsadir = xisdir(sofar);
6262 debug(F101,"STAT","",20);
6267 if (itsadir && xnolinks) { /* If not following symlinks */
6270 x = lstat(sofar,&buf);
6271 debug(F111,"traverse lstat 2",sofar,x);
6274 S_ISLNK(buf.st_mode)
6277 ((_IFMT & buf.st_mode) == _IFLNK)
6279 #endif /* S_ISLNK */
6283 #endif /* USE_LSTAT */
6284 #endif /* CKSYMLINK */
6287 if (xrecursive > 0 && itsadir &&
6288 (xpatlast[0] == '*') && !xpatlast[1]
6291 "traverse add: recursive && isdir && segisdir or match",
6295 addresult(sofar,itsadir);
6296 if (numfnd < 0) return;
6298 #endif /* RECURSIVE */
6300 debug(F111,"traverse mresult xpat",xpat,xrecursive);
6301 debug(F111,"traverse mresult pl -> npart",
6303 ((pl -> fwd) ? 9999 : 0)
6305 debug(F111,"traverse mresult sofar segisdir",sofar,segisdir);
6306 debug(F111,"traverse mresult sofar itsadir",sofar,itsadir);
6307 debug(F101,"traverse mresult xmatchdot","",xmatchdot);
6309 Match the path so far with the pattern after stripping any leading "./"
6310 from either or both. The pattern chosen is the full original pattern if
6311 the match candidate (sofar) is not a directory, or else just the name part
6312 (pl->npart) if it is.
6315 char * s1; /* The pattern */
6316 char * s2 = sofar; /* The path so far */
6317 char * s3; /* Worker */
6318 int opts; /* Match options */
6320 s1 = itsadir ? pl->npart : xpat;
6323 /* I can't explain this but it unbreaks "cd blah/sub<Esc>" */
6324 if (itsadir && !xrecursive && xpatslash > 0 &&
6325 segisdir == 0 && itswild) {
6327 debug(F110,"traverse mresult s1 kludge",s1,0);
6329 #endif /* COMMENT */
6331 if (xrecursive && xpatslash == 0)
6333 while ((s1[0] == '.') && (s1[1] == '/')) /* Strip "./" */
6335 while ((s2[0] == '.') && (s2[1] == '/')) /* Ditto */
6337 opts = mopts; /* Match options */
6338 if (itsadir) /* Current segment is a directory */
6339 opts = mopts & 1; /* No fences */
6340 s3 = s2; /* Get segment depth */
6342 while (*s3) { if (*s3++ == '/') depth++; }
6344 /* This speeds things up a bit. */
6345 /* If it causes trouble define NOSKIPMATCH and rebuild. */
6346 if (depth == 0 && (s1[0] == '*') && !s1[1])
6347 mresult = xmatchdot ? 1 : (s2[0] != '.');
6349 #endif /* NOSKIPMATCH */
6350 mresult = ckmatch(s1,s2,1,opts); /* Match */
6354 debug(F111,"traverse mresult depth",sofar,depth);
6355 debug(F101,"traverse mresult xpatslash","",xpatslash);
6356 debug(F111,"traverse mresult nambuf",nambuf,mresult);
6357 debug(F111,"traverse mresult itswild",pl -> npart,itswild);
6358 debug(F111,"traverse mresult segisdir",pl -> npart,segisdir);
6361 if (mresult || /* If match succeeded */
6362 xrecursive || /* Or search is recursive */
6363 depth < xpatslash /* Or not deep enough to match... */
6365 if ( /* If it's not a directory... */
6367 The problem here is that segisdir is apparently not set appropriately.
6368 If I leave in the !segisdir test, then "dir /recursive blah" (where blah is
6369 a directory name) misses some regular files because sometimes segisdir
6370 is set and sometimes it's not. But if I comment it out, then
6371 "dir <star>/<star>.txt lists every file in * and does not even open up the
6372 subdirectories. However, "dir /rec <star>/<star>.txt" works right.
6375 mresult && (!itsadir && !segisdir)
6377 mresult && /* Matched */
6378 !itsadir && /* sofar is not a directory */
6379 ((!xrecursive && !segisdir) || xrecursive)
6380 #endif /* COMMENT */
6383 "traverse add: match && !itsadir",sofar,0);
6384 addresult(sofar,itsadir);
6385 if (numfnd < 0) return;
6386 } else if (itsadir && (xrecursive || mresult)) {
6387 struct path * xx = NULL;
6388 *eos++ = DIRSEP; /* Add directory separator */
6389 *eos = '\0'; /* to end of segment */
6391 /* Copy previous pattern segment to this new directory */
6393 if (xrecursive > 0 && !(pl -> fwd)) {
6394 xx = (struct path *) malloc(sizeof (struct path));
6398 strcpy(xx -> npart, pl -> npart); /* safe */
6401 #endif /* RECURSIVE */
6402 traverse(pl -> fwd, sofar, eos); /* Traverse new directory */
6408 #else /* !OPENDIR */
6410 #endif /* OPENDIR */
6415 * Adds a result string to the result array. Increments the number
6416 * of matches found, copies the found string into our string
6417 * buffer, and puts a pointer to the buffer into the caller's result
6418 * array. Our free buffer pointer is updated. If there is no
6419 * more room in the caller's array, the number of matches is set to -1.
6420 * Input: a result string.
6424 addresult(str,itsadir) char *str; int itsadir; {
6428 debug(F100,"addresult string space not init'd","",0);
6429 initspace(mtchs,ssplen);
6432 debug(F111,"addresult",str,itsadir);
6437 itsadir = xisdir(str);
6439 if ((xdironly && !itsadir) || (xfilonly && itsadir)) {
6440 debug(F111,"addresult skip",str,itsadir);
6443 while (str[0] == '.' && ISDIRSEP(str[1])) /* Strip all "./" from front */
6445 if (--remlen < 0) { /* Elements left in array of names */
6446 debug(F111,"addresult ARRAY FULL",str,numfnd);
6450 len = (int)strlen(str); /* Space this will use */
6451 debug(F111,"addresult len",str,len);
6456 if ((freeptr + len + itsadir + 1) > (sspace + ssplen)) {
6457 debug(F111,"addresult OUT OF SPACE",str,numfnd);
6460 "?String space %d exhausted - use SET FILE STRINGSPACE to increase\n",ssplen);
6462 printf("?String space %d exhausted\n",ssplen);
6463 #endif /* DYNAMIC */
6464 numfnd = -1; /* Do not record if not enough space */
6467 strcpy(freeptr,str); /* safe */
6469 /* Tag directory names by putting '/' at the end */
6471 if (itsadir && (freeptr[len-1] == '/')) {
6472 freeptr[len++] = DIRSEP;
6473 freeptr[len] = '\0';
6475 if (numfnd >= maxnames) {
6478 "?Too many files (%d max) - use SET FILE LISTSIZE to increase\n",maxnames);
6480 printf("?Too many files - %d max\n",maxnames);
6481 #endif /* DYNAMIC */
6486 *resptr++ = freeptr;
6487 freeptr += (len + 1);
6489 debug(F111,"addresult ADD",str,numfnd);
6494 * match(pattern,string):
6495 * pattern matcher. Takes a string and a pattern possibly containing
6496 * the wildcard characters '*' and '?'. Returns true if the pattern
6497 * matches the string, false otherwise.
6498 * Orignally by: Jeff Damens, CUCCA, 1984
6499 * No longer used as of C-Kermit 7.0, now we use ckmatch() instead (ckclib.c).
6501 * Input: a string and a wildcard pattern.
6502 * Returns: 1 if match, 0 if no match.
6505 match(pattern, string) char *pattern, *string; {
6506 char *psave = NULL, *ssave = NULL; /* Backup pointers for failure */
6507 int q = 0; /* Quote flag */
6509 if (*string == '.' && *pattern != '.' && !xmatchdot) {
6510 debug(F110,"match skip",string,0);
6514 for (; *pattern == *string; pattern++,string++) /* Skip first */
6515 if (*string == '\0') return(1); /* End of strings, succeed */
6517 if (*pattern == '\\' && q == 0) { /* Watch out for quoted */
6518 q = 1; /* metacharacters */
6519 pattern++; /* advance past quote */
6520 if (*pattern != *string) return(0);
6527 if (*string != '\0' && *pattern == '?') {
6528 pattern++; /* '?', let it match */
6530 } else if (*pattern == '*') { /* '*' ... */
6531 psave = ++pattern; /* remember where we saw it */
6532 ssave = string; /* let it match 0 chars */
6533 } else if (ssave != NULL && *ssave != '\0') { /* if not at end */
6534 /* ...have seen a star */
6535 string = ++ssave; /* skip 1 char from string */
6536 pattern = psave; /* and back up pattern */
6537 } else return(0); /* otherwise just fail */
6541 #endif /* COMMENT */
6544 The following two functions are for expanding tilde in filenames
6545 Contributed by Howie Kaye, CUCCA, developed for CCMD package.
6548 /* W H O A M I -- Get user's username. */
6552 2) See if the $USER environment variable is set ($LOGNAME on AT&T)
6553 3) If $USER's uid is the same as ruid, realname is $USER
6554 4) Otherwise get logged in user's name
6555 5) If that name has the same uid as the real uid realname is loginname
6556 6) Otherwise, get a name for ruid from /etc/passwd
6566 static char realname[UIDBUFLEN+1]; /* user's name */
6567 static int ruid = -1; /* user's real uid */
6568 char loginname[UIDBUFLEN+1], envname[256]; /* temp storage */
6571 _PROTOTYP(extern char * getlogin, (void) );
6576 ruid = real_uid(); /* get our uid */
6578 /* how about $USER or $LOGNAME? */
6579 if ((c = getenv(NAMEENV)) != NULL) { /* check the env variable */
6580 ckstrncpy(envname, c, 255);
6581 if ((p = getpwnam(envname)) != NULL) {
6582 if (p->pw_uid == ruid) { /* get passwd entry for envname */
6583 ckstrncpy(realname, envname, UIDBUFLEN); /* uid's are same */
6589 /* can we use loginname() ? */
6591 if ((c = getlogin()) != NULL) { /* name from utmp file */
6592 ckstrncpy (loginname, c, UIDBUFLEN);
6593 if ((p = getpwnam(loginname)) != NULL) /* get passwd entry */
6594 if (p->pw_uid == ruid) /* for loginname */
6595 ckstrncpy(realname, envname, UIDBUFLEN); /* if uid's are same */
6598 /* Use first name we get for ruid */
6600 if ((p = getpwuid(ruid)) == NULL) { /* name for uid */
6601 realname[0] = '\0'; /* no user name */
6605 ckstrncpy(realname, p->pw_name, UIDBUFLEN);
6612 /* T I L D E _ E X P A N D -- expand ~user to the user's home directory. */
6615 tilde_expand(dirname) char *dirname; {
6622 struct passwd *user;
6623 static char olddir[BUFLEN+1];
6624 static char oldrealdir[BUFLEN+1];
6625 static char temp[BUFLEN+1];
6628 debug(F111,"tilde_expand",dirname,dirname[0]);
6630 if (dirname[0] != '~') /* Not a tilde...return param */
6632 if (!strcmp(olddir,dirname)) { /* Same as last time */
6633 return(oldrealdir); /* so return old answer. */
6635 j = (int)strlen(dirname);
6636 for (i = 0; i < j; i++) /* find username part of string */
6637 if (!ISDIRSEP(dirname[i]))
6638 temp[i] = dirname[i];
6640 temp[i] = '\0'; /* tie off with a NULL */
6641 if (i == 1) { /* if just a "~" */
6644 user = getpwnam(uidbuf); /* Get info on current user */
6648 char * p = whoami();
6655 user = getpwnam(&temp[1]); /* otherwise on the specified user */
6658 if (user != NULL) { /* valid user? */
6659 ckstrncpy(olddir, dirname, BUFLEN); /* remember the directory */
6660 ckstrncpy(oldrealdir,user->pw_dir, BUFLEN); /* and home directory */
6661 ckstrncat(oldrealdir,&dirname[i], BUFLEN);
6662 oldrealdir[BUFLEN] = '\0';
6664 } else { /* invalid? */
6665 ckstrncpy(olddir, dirname, BUFLEN); /* remember for next time */
6666 ckstrncpy(oldrealdir, dirname, BUFLEN);
6675 Functions for executing system commands.
6676 zsyscmd() executes the system command in the normal, default way for
6677 the system. In UNIX, it does what system() does. Thus, its results
6678 are always predictable.
6679 zshcmd() executes the command using the user's preferred shell.
6682 zsyscmd(s) char *s; {
6684 if (nopush) return(-1);
6685 if (!priv_chk()) return(system(s));
6689 /* This doesn't work... */
6693 #endif /* COMMENT */
6695 if (nopush) return(-1);
6696 if ((shpid = fork())) {
6697 if (shpid < (PID_T)0) return(-1); /* Parent */
6698 while (shpid != (PID_T) wait(&status))
6702 if (priv_can()) { /* Child: cancel any priv's */
6703 printf("?Privilege cancellation failure\n");
6706 restorsigs(); /* Restore ignored signals */
6708 execl("/usr/bin/sh","sh","-c",s,NULL);
6709 perror("/usr/bin/sh");
6712 execl("/bin/rc", "rc", "-c", s, NULL);
6715 execl("/bin/sh","sh","-c",s,NULL);
6720 return(0); /* Shut up ANSI compilers. */
6725 /* Z _ E X E C -- Overlay ourselves with another program */
6733 #endif /* ATT7300 */
6735 #endif /* NOZEXEC */
6738 z_exec(p,s,t) char * p, ** s; int t; { /* Overlay ourselves with "p s..." */
6740 printf("EXEC /REDIRECT NOT IMPLEMENTED IN THIS VERSION OF C-KERMIT\n");
6741 debug(F110,"z_exec NOT IMPLEMENTED",p,0);
6745 debug(F110,"z_exec command",p,0);
6746 debug(F110,"z_exec arg 0",s[0],0);
6747 debug(F110,"z_exec arg 1",s[1],0);
6748 debug(F101,"z_exec t","",t);
6754 /* dup2(ttyfd, 2); */
6758 restorsigs(); /* Restore ignored signals */
6760 if (x < 0) debug(F101,"z_exec errno","",errno);
6761 #endif /* NOZEXEC */
6765 Z S H C M D -- Execute a shell command (or program thru the shell).
6767 Original UNIX code by H. Fischer; copyright rights assigned to Columbia U.
6768 Adapted to use getpwuid to find login shell because many systems do not
6769 have SHELL in environment, and to use direct calling of shell rather
6770 than intermediate system() call. -- H. Fischer (1985); many changes since
6771 then. Call with s pointing to command to execute. Returns:
6772 -1 on failure to start the command (can't find, can't fork, can't run).
6773 1 if command ran and gave an exit status of 0.
6774 0 if command ran and gave a nonzero exit status.
6775 with pexitstatus containing the command's exit status.
6778 zshcmd(s) char *s; {
6784 if (nopush) return(-1);
6785 debug(F110,"zshcmd command",s,0);
6788 if ((pid = vfork()) == 0) { /* Make child quickly */
6789 char *shpath, *shname, *shptr; /* For finding desired shell */
6791 if (priv_can()) exit(1); /* Turn off privs. */
6792 if ((shpath = getenv("SHELL")) == NULL) shpath = "/com/sh";
6794 #else /* All Unix systems */
6795 if ((pid = fork()) == 0) { /* Make child */
6796 char *shpath, *shname, *shptr; /* For finding desired shell */
6798 #ifdef HPUX10 /* Default */
6799 char *defshell = "/usr/bin/sh";
6802 char *defshell = "/bin/rc";
6804 char *defshell = "/bin/sh";
6807 if (priv_can()) exit(1); /* Turn off privs. */
6809 /* Old way always used /etc/passwd shell */
6810 p = getpwuid(real_uid()); /* Get login data */
6811 if (p == (struct passwd *) NULL || !*(p->pw_shell))
6814 shpath = p->pw_shell;
6816 /* New way lets user override with SHELL variable, but does not rely on it. */
6817 /* This allows user to specify a different shell. */
6818 shpath = getenv("SHELL"); /* What shell? */
6819 debug(F110,"zshcmd SHELL",shpath,0);
6820 if (shpath == NULL) {
6821 p = getpwuid( real_uid() ); /* Get login data */
6822 if (p == (struct passwd *)NULL || !*(p->pw_shell))
6824 else shpath = p->pw_shell;
6825 debug(F110,"zshcmd shpath",shpath,0);
6827 #endif /* COMMENT */
6829 shptr = shname = shpath;
6830 while (*shptr != '\0')
6831 if (*shptr++ == DIRSEP)
6833 restorsigs(); /* Restore ignored signals */
6834 debug(F110,"zshcmd shname",shname,0);
6835 if (s == NULL || *s == '\0') { /* Interactive shell requested? */
6836 execl(shpath,shname,"-i",NULL); /* Yes, do that */
6837 } else { /* Otherwise, */
6838 execl(shpath,shname,"-c",s,NULL); /* exec the given command */
6839 } /* If execl() failed, */
6840 exit(BAD_EXIT); /* return bad return code. */
6842 } else { /* Parent */
6844 int wstat; /* ... must wait for child */
6846 int child; /* Child's exit status */
6847 #endif /* CK_CHILD */
6848 SIGTYP (*istat)(), (*qstat)();
6850 if (pid == (PID_T) -1) return(-1); /* fork() failed? */
6852 istat = signal(SIGINT,SIG_IGN); /* Let the fork handle keyboard */
6853 qstat = signal(SIGQUIT,SIG_IGN); /* interrupts itself... */
6856 while (((wstat = wait(&child)) != pid) && (wstat != -1))
6858 while (((wstat = wait((WAIT_T *)0)) != pid) && (wstat != -1))
6859 #endif /* CK_CHILD */
6860 ; /* Wait for fork */
6861 signal(SIGINT,istat); /* Restore interrupts */
6862 signal(SIGQUIT,qstat);
6864 pexitstat = (child & 0xff) ? child : child >> 8;
6865 debug(F101,"zshcmd exit status","",pexitstat);
6866 return(child == 0 ? 1 : 0); /* Return child's status */
6867 #endif /* CK_CHILD */
6873 /* I S W I L D -- Check if filespec is "wild" */
6877 0 if argument is empty or is the name of a single file;
6878 1 if it contains wildcard characters.
6879 Note: must match the algorithm used by match(), hence no [a-z], etc.
6882 iswild(filespec) char *filespec; {
6883 char c, *p, *f; int x;
6888 if (wildxpand) { /* Shell handles wildcarding */
6889 if ((x = nzxpand(filespec,0)) > 1)
6891 if (x == 0) return(0); /* File does not exist */
6892 p = malloc(MAXNAMLEN + 20);
6894 x = (strcmp(filespec,p) != 0);
6898 } else { /* We do it ourselves */
6899 while ((c = *filespec++) != '\0') {
6900 if (c == '\\' && quo == 0) {
6904 if (!quo && (c == '*' || c == '?'
6910 #endif /* CKREGEX */
6912 debug(F111,"iswild",f,1);
6917 debug(F111,"iswild",f,0);
6923 I S D I R -- Is a Directory.
6925 Tell if string pointer s is the name of an existing directory. Returns 1 if
6926 directory, 0 if not a directory.
6928 The following no longer applies:
6930 If the file is a symlink, we return 1 if
6931 it is a directory OR if it is a link to a directory and the "xrecursive" flag
6932 is NOT set. This is to allow parsing a link to a directory as if it were a
6933 directory (e.g. in the CD or IF DIRECTORY command) but still prevent
6934 recursive traversal from visiting the same directory twice.
6938 /* This turns out to be unsafe and gives little benefit anyway. */
6939 /* See notes 28 Sep 2003. Thus ISDIRCACHE is not defined. */
6941 static char prevpath[CKMAXPATH+4] = { '\0', '\0' };
6942 static int prevstat = -1;
6945 debug(F100,"CLEAR ISDIR CACHE","",0);
6949 #endif /* ISDIRCACHE */
6953 int x, needrlink = 0, islink = 0;
6954 struct stat statbuf;
6955 char fnam[CKMAXPATH+4];
6961 if (prevstat > -1) {
6962 if (s[0] == prevpath[0]) {
6963 if (!strcmp(s,prevpath)) {
6964 debug(F111,"isdir cache hit",s,prevstat);
6969 #endif /* ISDIRCACHE */
6974 The following over-clever bit has been commented out because it presumes
6975 to know when a symlink might be redundant, which it can't possibly know.
6976 Using plain old stat() gives Kermit the same results as ls and ls -R, which
6977 is just fine: no surprises.
6981 x = lstat(s,&statbuf);
6982 debug(F111,"isdir lstat",s,x);
6984 #endif /* USE_LSTAT */
6985 x = stat(s,&statbuf);
6986 debug(F111,"isdir stat",s,x);
6989 #endif /* USE_LSTAT */
6991 x = stat(s,&statbuf);
6992 debug(F111,"isdir stat",s,x);
6993 #endif /* COMMENT */
6995 debug(F101,"isdir errno","",errno);
7004 islink = S_ISLNK(statbuf.st_mode);
7005 debug(F101,"isdir S_ISLNK islink","",islink);
7008 islink = (_IFMT & statbuf.st_mode) == _IFLNK;
7009 debug(F101,"isdir _IFLNK islink","",islink);
7011 #endif /* S_ISLNK */
7012 #endif /* NOLINKBITS */
7014 if (readlink(s,fnam,CKMAXPATH) > -1)
7019 x = stat(s,&statbuf);
7021 debug(F101,"isdir errno","",errno);
7024 debug(F111,"isdir stat",s,x);
7025 #endif /* CKSYMLINK */
7026 debug(F101,"isdir islink","",islink);
7027 debug(F101,"isdir statbuf.st_mode","",statbuf.st_mode);
7028 x = islink ? 0 : (S_ISDIR (statbuf.st_mode) ? 1 : 0);
7031 ckstrncpy(prevpath,s,CKMAXPATH+1);
7032 #endif /* ISDIRCACHE */
7037 /* Some systems don't have mkdir(), e.g. Tandy Xenix 3.2.. */
7039 /* Z M K D I R -- Create directory(s) if necessary */
7042 A pointer to a file specification that might contain directory
7043 information. The filename is expected to be included.
7044 If the file specification does not include any directory separators,
7045 then it is assumed to be a plain file.
7046 If one or more directories are included in the file specification,
7047 this routine tries to create them if they don't already exist.
7049 0 or greater on success, i.e. the number of directories created.
7050 -1 on failure to create the directory
7053 zmkdir(path) char *path; {
7057 if (!path) path = "";
7058 if (!*path) return(-1);
7061 debug(F111,"zmkdir setroot",ckroot,ckrootset);
7062 if (ckrootset) if (!zinroot(path)) {
7063 debug(F110,"zmkdir setroot violation",path,0);
7069 debug(F111,"zmkdir",path,x);
7070 if (x < 1 || x > MAXPATH) /* Check length */
7072 if (!(tp = malloc(x+1))) /* Make a temporary copy */
7074 strcpy(tp,path); /* safe (prechecked) */
7076 if (*tp == '~') { /* Starts with tilde? */
7077 xp = tilde_expand(tp); /* Attempt to expand tilde */
7081 debug(F110,"zmkdir tilde_expand",xp,0);
7082 if (!(zp = malloc(strlen(xp) + 1))) { /* Make a place for it */
7087 free(tp); /* Free previous buffer */
7088 tp = zp; /* Point to new one */
7089 strcpy(tp,xp); /* Copy expanded name to new buffer */
7093 debug(F110,"zmkdir tp after tilde_expansion",tp,0);
7095 if (ISDIRSEP(*xp)) /* Don't create root directory! */
7098 /* Go thru filespec from left to right... */
7100 for (; *xp; xp++) { /* Create parts that don't exist */
7101 if (!ISDIRSEP(*xp)) /* Find next directory separator */
7103 c = *xp; /* Got one. */
7104 *xp = NUL; /* Make this the end of the string. */
7105 if (!isdir(tp)) { /* This directory exists already? */
7107 if (isguest) /* Not allowed for guests */
7110 /* Nor if MKDIR and/or CD are disabled */
7112 #endif /* CK_LOGIN */
7117 ) && (!ENABLED(en_mkd) || !ENABLED(en_cwd)))
7121 debug(F110,"zmkdir making",tp,0);
7122 x = /* No, try to create it */
7124 -1 /* Systems without mkdir() */
7126 mkdir(tp,0777) /* UNIX */
7127 #endif /* NOMKDIR */
7130 debug(F101,"zmkdir failed, errno","",errno);
7131 free(tp); /* Free temporary buffer. */
7133 return(-1); /* Return failure code. */
7137 *xp = c; /* Replace the separator. */
7139 free(tp); /* Free temporary buffer. */
7140 return(count); /* Return success code. */
7142 #endif /* CK_MKDIR */
7145 zrmdir(path) char *path; {
7149 #endif /* CK_LOGIN */
7151 if (!path) path = "";
7152 if (!*path) return(-1);
7155 debug(F111,"zrmdir setroot",ckroot,ckrootset);
7156 if (ckrootset) if (!zinroot(path)) {
7157 debug(F110,"zrmdir setroot violation",path,0);
7163 return(rmdir(path));
7166 #endif /* NOMKDIR */
7169 /* Z F S E E K -- Position input file pointer */
7172 Long int, 0-based, indicating desired position.
7182 zfseek(pos) long pos;
7183 #endif /* CK_ANSIC */
7185 zincnt = -1; /* Must empty the input buffer */
7186 debug(F101,"zfseek","",pos);
7187 return(fseek(fp[ZIFILE], pos, 0)?-1:0);
7189 #endif /* NORESEND */
7191 /* Z F N Q F P -- Convert filename to fully qualified absolute pathname */
7193 static struct zfnfp fnfp = { 0, NULL, NULL };
7196 zfnqfp(fname, buflen, buf) char * fname; int buflen; char * buf; {
7200 char zfntmp[MAXPATHLEN+4];
7202 char zfntmp[CKMAXPATH+4];
7203 #endif /* MAXPATHLEN */
7206 int i = 0, j = 0, k = 0, x = 0, y = 0;
7217 /* Initialize the data structure */
7219 fnfp.len = ckstrncpy(buf,fname,buflen);
7223 debug(F111,"zfnqfp fname",fname,len);
7226 if (*s == '~') { /* Starts with tilde? */
7228 xp = tilde_expand(s); /* Attempt to expand tilde */
7229 debug(F110,"zfnqfp xp",xp,0); /* (realpath() doesn't do this) */
7238 /* N.B.: The realpath() result buffer MUST be MAXPATHLEN bytes long */
7239 /* otherwise we write over memory. */
7241 if (!realpath(s,zfntmp)) {
7242 debug(F111,"zfnqfp realpath fails",s,errno);
7244 if (errno != ENOENT)
7247 /* If realpath() fails use the do-it-yourself method */
7250 #endif /* COMMENT */
7252 len = strlen(zfntmp);
7254 debug(F111,"zfnqfp result too long",ckitoa(buflen),len);
7257 ckstrncpy(buf,zfntmp,buflen);
7259 if (buf[len-1] != '/') {
7260 if ((itsadir = isdir(buf)) && len < (buflen - 1)) {
7267 debug(F110,"zfnqfp realpath path",fnfp.fpath,0);
7268 tmp = buf + fnfp.len - 1;
7270 while (tmp >= buf) {
7272 fnfp.fname = tmp + 1;
7273 debug(F110,"zfnqfp realpath name",fnfp.fname,0);
7281 #endif /* CKREALPATH */
7286 while (*s) { /* Remove leading "./" (0 or more) */
7287 debug(F110,"zfnqfp while *s",s,0);
7288 if (*s == '.' && *(s+1) == '/') {
7290 while (*s == '/') s++;
7294 if (!*s) return(NULL);
7295 if (*s == '/') { /* Pathname is absolute */
7296 ckstrncpy(buf,s,len);
7299 } else { /* Pathname is relative */
7301 if (p = zgtdir()) { /* So get current directory */
7302 debug(F110,"zfnqfp zgtdir",p,0);
7303 x = ckstrncpy(buf,p,len);
7305 debug(F110,"zfnqfp buf 1",buf,0);
7306 len -= x; /* How much room left in buffer */
7307 if ((y = (int)strlen(s)) > len) /* If enough room... */
7309 ckstrncpy(buf+x,s,len); /* ... append the filename */
7310 debug(F110,"zfnqfp buf 2",buf,0);
7316 /* Buf now holds full path but maybe containing some . or .. tricks */
7318 j = x + y; /* Length of what's in buf */
7320 debug(F101,"zfnqfp len","",len);
7322 /* Catch dangling "/." or "/.." */
7323 if ((j > 1 && buf[j-1] == '.' && buf[j-2] == '/') ||
7324 (j > 2 && buf[j-1] == '.' && buf[j-2] == '.' && buf[j-3] == '/')) {
7325 if (j < buflen - 2) {
7330 j = -1; /* j = position of rightmost "/" */
7331 i = 0; /* i = destination index */
7332 tmp[i] = NUL; /* destination is temporary buffer */
7334 for (x = 0; x < len; x++) { /* x = source index */
7335 if (buf[x] == '/') {
7336 for (k = 0; k < 4; k++) {
7341 if (!strncmp(sb,"/./",3)) { /* Eliminate "./" in "/./" */
7344 } else if (!strncmp(sb,"//",2)) { /* Change "//" to "/" */
7346 } else if (!strncmp(sb,"/../",4)) { /* ".." in path */
7347 for (k = i - 1; k >= 0; k--) { /* Back up one level */
7348 if (tmp[k] == '/') {
7358 if (i >= (buflen - 1)) {
7359 debug(F111,"zfnqfp overflow",tmp,i);
7362 tmp[i++] = buf[x]; /* Regular character, copy */
7364 if (buf[x] == '/') /* Remember rightmost "/" */
7367 ckstrncpy(buf,tmp,buflen-1); /* Copy the result back */
7369 buf[buflen-1] = NUL;
7370 if (!buf[0]) { /* If empty, say root */
7376 if ((itsadir = isdir(buf))) {
7377 if (buf[i-1] != '/' && i < (buflen - 1)) {
7382 if (!itsadir && (j > -1)) { /* Set pointer to basename */
7383 fnfp.fname = (char *)(buf + j);
7384 fnfp.fpath = (char *)buf;
7386 debug(F111,"zfnqfp path",fnfp.fpath,i);
7387 debug(F110,"zfnqfp name",fnfp.fname,0);
7393 /* Z C M P F N -- Compare two filenames */
7395 /* Returns 1 if the two names refer to the same existing file, 0 otherwise. */
7398 zcmpfn(s1,s2) char * s1, * s2; {
7399 char buf1[CKMAXPATH+1];
7400 char buf2[CKMAXPATH+1];
7403 char linkname[CKMAXPATH+1];
7405 #endif /* USE_LSTAT */
7410 if (!*s1 || !*s2) return(0);
7412 #ifdef CKSYMLINK /* We're doing symlinks? */
7413 #ifdef USE_LSTAT /* OK to use lstat()? */
7415 if (x > -1 && /* Now see if it's a symlink */
7417 S_ISLNK(buf.st_mode)
7420 ((_IFMT & buf.st_mode) == _IFLNK)
7422 #endif /* S_ISLNK */
7424 linkname[0] = '\0'; /* Get the name */
7425 x = readlink(s1,linkname,CKMAXPATH);
7426 if (x > -1 && x < CKMAXPATH) { /* It's a link */
7431 #endif /* USE_LSTAT */
7432 #endif /* CKSYMLINK */
7434 if (zfnqfp(s1,CKMAXPATH,buf1)) { /* Convert to full pathname */
7436 #ifdef CKSYMLINK /* Same deal for second name... */
7441 S_ISLNK(buf.st_mode)
7444 ((_IFMT & buf.st_mode) == _IFLNK)
7446 #endif /* S_ISLNK */
7449 x = readlink(s2,linkname,CKMAXPATH);
7450 if (x > -1 && x < CKMAXPATH) {
7455 #endif /* USE_LSTAT */
7456 #endif /* CKSYMLINK */
7457 if (zfnqfp(s2,CKMAXPATH,buf2)) {
7458 debug(F110,"zcmpfn s1",buf1,0);
7459 debug(F110,"zcmpfn s2",buf2,0);
7460 if (!strncmp(buf1,buf2,CKMAXPATH))
7464 debug(F101,"zcmpfn result","",rc);
7470 /* User-mode chroot() implementation */
7473 zsetroot(s) char * s; { /* Sets the root */
7474 char buf[CKMAXPATH+1];
7476 if (!*s) return(-1);
7477 debug(F110,"zsetroot",s,0);
7478 if (!isdir(s)) return(-2);
7479 if (!zfnqfp(s,CKMAXPATH,buf)) /* Get full, real path */
7481 if (access(buf,R_OK) < 0) { /* Check access */
7482 debug(F110,"zsetroot access denied",buf,0);
7486 if (ckrootset) { /* If root already set */
7487 if (!zinroot(s)) { /* make sure new root is in it */
7488 debug(F110,"zsetroot new root not in root",ckroot,0);
7492 if (zchdir(buf) < 1) return(-4); /* Change directory to new root */
7493 ckrootset = ckstrncpy(ckroot,buf,CKMAXPATH); /* Now set the new root */
7494 if (ckroot[ckrootset-1] != '/') {
7495 ckroot[ckrootset++] = '/';
7496 ckroot[ckrootset] = '\0';
7498 debug(F111,"zsetroot rootset",ckroot,ckrootset);
7499 ckrooterr = 0; /* Reset error flag */
7504 zgetroot() { /* Returns the root */
7507 return((char *)ckroot);
7511 zinroot(s) char * s; { /* Checks if file s is in the root */
7513 struct zfnfp * f = NULL;
7514 char buf[CKMAXPATH+2];
7516 debug(F111,"zinroot setroot",ckroot,ckrootset);
7517 ckrooterr = 0; /* Reset global error flag */
7518 if (!ckrootset) /* Root not set */
7519 return(1); /* so it's ok - no need to check */
7520 if (!(f = zfnqfp(s,CKMAXPATH,buf))) /* Get full and real pathname */
7521 return(0); /* Fail if we can't */
7522 n = f->len; /* Length of full pathname */
7523 debug(F111,"zinroot n",buf,n);
7524 if (n < (ckrootset - 1) || n > CKMAXPATH) { /* Bad length */
7525 ckrooterr = 1; /* Fail */
7528 if (isdir(buf) && buf[n-1] != '/') { /* If it's a directory name */
7529 buf[n++] = '/'; /* make sure it ends with '/' */
7532 x = strncmp(buf,ckroot,ckrootset); /* Compare, case-sensitive */
7533 debug(F111,"zinroot checked",buf,x);
7534 if (x == 0) /* OK */
7536 ckrooterr = 1; /* Not OK */
7543 The following code provides support for user login and logout
7544 including anonymous accounts. If this feature is to be supported
7545 outside of UNIX, it should be spread out among the ck?fio.c modules...
7547 #ifndef _PATH_BSHELL
7548 #define _PATH_BSHELL "/usr/bin/bash"
7549 #endif /* _PATH_BSHELL */
7550 #ifndef _PATH_FTPUSERS
7551 #define _PATH_FTPUSERS "/etc/ftpusers"
7552 #endif /* _PATH_FTPUSERS */
7555 * Helper function for sgetpwnam().
7558 sgetsave(s) char *s; {
7559 char *new = malloc((unsigned) strlen(s) + 1);
7561 printf("?Local resource failure: malloc\n");
7565 (void) strcpy(new, s); /* safe */
7570 * Save the result of getpwnam(). Used for USER command, since
7571 * the data returned must not be clobbered by any other command
7575 sgetpwnam(name) char *name; {
7576 static struct passwd save;
7577 register struct passwd *p;
7579 register struct spwd *sp;
7580 #endif /* CK_SHADOW */
7583 #ifdef HPUX10_TRUSTED
7584 struct pr_passwd *pr;
7585 #endif /* HPUX10_TRUSTED */
7588 sp = getspnam(name);
7589 debug(F111,"sgetpwnam","getspnam()",sp);
7592 #endif /* CK_SHADOW */
7594 #ifdef HPUX10_TRUSTED
7595 if ((pr = getprpwnam(name)) == NULL)
7597 #endif /* HPUX10_TRUSTED */
7600 debug(F111,"sgetpwnam","getpwnam()",p);
7605 free(save.pw_passwd);
7606 free(save.pw_gecos);
7608 free(save.pw_shell);
7611 save.pw_name = sgetsave(p->pw_name);
7613 save.pw_passwd = sgetsave(sp->sp_pwdp);
7614 #else /* CK_SHADOW */
7615 #ifdef HPUX10_TRUSTED
7616 if (pr->uflg.fg_encrypt && pr->ufld.fd_encrypt && *pr->ufld.fd_encrypt)
7617 save.pw_passwd = sgetsave(pr->ufld.fd_encrypt);
7619 save.pw_passwd = sgetsave("");
7620 #else /* HPUX10_TRUSTED */
7621 save.pw_passwd = sgetsave(p->pw_passwd);
7622 #endif /* HPUX10_TRUSTED */
7623 #endif /* CK_SHADOW */
7624 save.pw_gecos = sgetsave(p->pw_gecos);
7625 save.pw_dir = sgetsave(p->pw_dir);
7626 save.pw_shell = sgetsave(p->pw_shell);
7630 #define CKXLOGBSIZ 256
7632 struct passwd * pw = NULL;
7633 char * home = NULL; /* Home directory pointer for glob */
7640 int defumask = CMASK; /* Default umask value */
7642 /* Z V U S E R -- Verify user, Returns 1 if user OK, 0 otherwise. */
7644 /* Sets global passwd pointer pw if named account exists and is acceptable;
7645 * sets askpasswd if a PASS command is expected. If logged in previously,
7646 * need to reset state. If name is "ftp" or "anonymous", the name is not in
7647 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
7648 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
7649 * requesting login privileges. Disallow anyone who does not have a standard
7650 * shell as returned by getusershell(). Disallow anyone mentioned in the file
7651 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
7653 _PROTOTYP(static int checkuser, (char *) );
7655 char zvuname[64] = { NUL, NUL };
7656 char zvhome[CKMAXPATH+1] = { NUL, NUL };
7658 #define ZENVHOME CKMAXPATH+12
7659 #define ZENVLOGNAME 74
7660 static char zenvuser[ZENVUSER];
7661 static char zenvhome[ZENVHOME];
7662 static char zenvlogname[ZENVLOGNAME];
7665 static char pam_data[500];
7666 struct pam_conv pam_conv = {pam_cb, pam_data}; /* PAM structure */
7667 struct pam_handle * pamh = NULL; /* PAM reference handle */
7671 zvuser(name) char *name; {
7672 register char *cp = NULL;
7676 char *getusershell();
7677 #endif /* GETUSERSHELL */
7681 const char * reply = NULL;
7684 debug(F111,"user",name,logged_in);
7686 if (!name) name = "";
7689 debug(F101,"zvuser ckxsyslog","",ckxsyslog);
7692 debug(F100,"zvuser CKSYSLOG defined","",0);
7693 #endif /* CKSYSLOG */
7695 if (logged_in) /* Should not be called if logged in */
7699 if (ckxsyslog && ckxlogging) {
7701 "login: user %s",name
7704 #endif /* CKSYSLOG */
7706 guest = 0; /* Assume not guest */
7709 if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
7710 debug(F101,"zvuser anonymous ckxanon","",ckxanon);
7711 if (!ckxanon) { /* Anonymous login not allowed */
7713 if (ckxsyslog && ckxlogging) {
7715 "login: anonymous login not allowed: %s",
7716 clienthost ? clienthost : "(unknown host)"
7719 #endif /* CKSYSLOG */
7722 if (checkuser("ftp") || checkuser("anonymous")) {
7723 debug(F100,"zvuser anon forbidden by ftpusers file","",0);
7725 if (ckxsyslog && ckxlogging) {
7727 "login: anonymous login forbidden by ftpusers file: %s",
7728 clienthost ? clienthost : "(unknown host)"
7731 #endif /* CKSYSLOG */
7733 } else if ((pw = sgetpwnam("ftp")) != NULL) {
7734 debug(F100,"zvuser anon sgetpwnam(ftp) OK","",0);
7737 ckstrncpy(zvuname,"anonymous",64);
7740 debug(F100,"zvuser anon sgetpwnam(ftp) FAILED","",0);
7742 if (ckxsyslog && ckxlogging) {
7744 "login: anonymous getpwnam(ftp) failed: %s",
7745 clienthost ? clienthost : "(unknown host)"
7748 #endif /* CKSYSLOG */
7752 pw = sgetpwnam(name);
7755 Of course some UNIX platforms (like AIX) don't have getusershell().
7756 In that case we can't check if the user's account has been "turned off"
7757 or somesuch, e.g. by setting their shell to "/etc/nologin" or somesuch,
7758 which runs (usually just printing a message and exiting), but which is
7759 not listed in /etc/shells. For that matter, if getusershell() is not
7760 available, then probably neither is /etc/shells.
7762 debug(F100,"zvuser sgetpwnam ok","",0);
7763 shell = pw->pw_shell;
7764 if (!shell) shell = "";
7766 shell = _PATH_BSHELL;
7767 debug(F110,"zvuser shell",shell,0);
7769 while ((cp = getusershell()) != NULL) {
7770 debug(F110,"zvuser getusershell",cp,0);
7771 if (strcmp(cp, shell) == 0)
7774 debug(F100,"zvuser endusershell 1","",0);
7776 debug(F100,"zvuser endusershell 2","",0);
7777 #else /* GETUSERSHELL */
7778 cp = ""; /* Do not refuse if we cannot check */
7779 #endif /* GETUSERSHELL */
7780 x = checkuser(name);
7781 debug(F101,"zvuser checkuser","",x);
7783 debug(F100,"zvuser refused 1","",0);
7784 pw = (struct passwd *) NULL;
7786 if (ckxsyslog && ckxlogging) {
7788 "login: invalid shell %s for %s %s",shell, name,
7789 clienthost ? clienthost : "(unknown host)"
7792 #endif /* CKSYSLOG */
7795 debug(F100,"zvuser refused 2","",0);
7796 pw = (struct passwd *) NULL;
7798 if (ckxsyslog && ckxlogging) {
7800 "login: %s login forbidden by ftpusers file: %s",
7801 name, clienthost ? clienthost : "(unknown host)"
7804 #endif /* CKSYSLOG */
7809 /* Get PAM authentication details */
7810 debug(F110,"zvuser","calling pam_start",0);
7812 pam_start(PAM_SERVICE_TYPE,name,&pam_conv,&pamh))
7814 reply = pam_strerror(NULL, pam_status);
7815 debug(F110,"zvuser PAM failure",reply,0);
7816 printf("%s\n",reply);
7818 if (ckxsyslog && ckxlogging) {
7820 "login: %s refused by PAM \"%s\": %s",
7822 clienthost ? clienthost : "(unknown host)"
7825 #endif /* CKSYSLOG */
7830 ckstrncpy(zvuname,name,64);
7835 debug(F100,"zvuser sgetpwnam NULL","",0);
7837 if (ckxsyslog && ckxlogging) {
7839 "login: getpwnam(%s) failed: %s",name,
7840 clienthost ? clienthost : "(unknown host)"
7843 #endif /* CKSYSLOG */
7848 if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) {
7850 /* Why sprintf and then printf? */
7851 /* Also, what is kerb_ok? And is the test on it right? */
7852 char buf[CKXLOGBSIZ];
7853 sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s%s",
7854 kdata.pname, *kdata.pinst ? "." : "",
7855 kdata.pinst, kdata.prealm,
7856 (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not",
7857 name, kerb_ok ? "" : "; Password required.");
7860 printf("Kerberos user %s%s%s@%s is%s authorized as %s%s",
7861 kdata.pname, *kdata.pinst ? "." : "",
7862 kdata.pinst, kdata.prealm,
7863 (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not",
7864 name, kerb_ok ? "" : "; Password required.");
7865 #endif /* COMMENT */
7866 if (kerb_ok) return(1);
7869 #endif /* FTP_KERBEROS */
7872 /* Check if the given user is in the forbidden-user file */
7875 checkuser(name) char *name; {
7876 extern char * userfile;
7879 char line[CKXLOGBSIZ+1];
7884 debug(F111,"checkuser name",name,i);
7888 fd = fopen(userfile ? userfile : _PATH_FTPUSERS, "r");
7889 debug(F111,"checkuser userfile",userfile,fd);
7892 while (fgets(line, sizeof(line), fd)) {
7893 debug(F110,"checkuser line",line,0);
7896 if (strncmp(line, name, i) == 0) {
7897 debug(F110,"checkuser REFUSED",name,0);
7904 debug(F110,"checkuser OK",name,0);
7908 /* Z V L O G O U T -- Log out from Internet Kermit Service */
7913 /* This could be dangerous */
7914 if (setuid((UID_T)0) < 0) {
7915 debug(F100,"zvlogout setuid FAILED","",0);
7918 debug(F100,"zvlogout setuid OK","",0);
7919 #endif /* COMMENT */
7921 if (ckxsyslog >= SYSLG_LI && ckxlogging) {
7922 cksyslog(SYSLG_LI, 1, "logout",(char *) uidbuf, clienthost);
7924 #endif /* CKSYSLOG */
7926 debug(F110,"WTMP logout",cksysline,logged_in);
7928 logwtmp(cksysline, "", "");
7937 kpass(name, p) char *name, *p; {
7938 char instance[INST_SZ];
7939 char realm[REALM_SZ];
7943 unsigned long faddr;
7946 if (krb_get_lrealm(realm, 1) != KSUCCESS)
7949 ckstrncpy(tkt_file, TKT_ROOT, 20);
7950 ckstrncat(tkt_file, "_ftpdXXXXXX", 20);
7951 krb_set_tkt_string(mktemp(tkt_file));
7953 (VOID) ckstrncpy(instance, krb_get_phost(hostname), sizeof(instance));
7955 if ((hp = gethostbyname(instance)) == NULL)
7959 hp = ck_copyhostent(hp); /* safe copy that won't change */
7960 #endif /* HADDRLIST */
7961 bcopy((char *)hp->h_addr, (char *) &faddr, sizeof(faddr));
7963 if (krb_get_pw_in_tkt(name, "", realm, "krbtgt", realm, 1, p) ||
7964 krb_mk_req(&ticket, "rcmd", instance, realm, 33) ||
7965 krb_rd_req(&ticket, "rcmd", instance, faddr, &authdata, "") ||
7966 kuserok(&authdata, name)) {
7973 #endif /* FTP_KERBEROS */
7978 if (ckxsyslog && !ckxlogging) {
7980 openlog(inserver ? "iksd" : "ckermit", LOG_PID, LOG_DAEMON);
7982 openlog(inserver ? "iksd" : "ckermit", LOG_PID);
7983 #endif /* LOG_DAEMON */
7985 debug(F100,"zsyslog syslog opened","",0);
7987 #endif /* CKSYSLOG */
7990 /* Z V P A S S -- Verify password; returns 1 if OK, 0 otherwise */
7994 #endif /* AUTH_USER */
7996 #define AUTH_VALID 4
7997 #endif /* AUTH_VALID */
8000 zvpass(p) char *p; {
8001 char *xpasswd, *salt;
8005 const char * reply = NULL;
8008 if (logged_in || askpasswd == 0) {
8011 debug(F111,"zvpass",p ? (guest ? p : "xxxxxx") : "(null)",guest);
8014 if (guest && !*p) { /* Guests must specify a password */
8016 if (ckxsyslog && ckxlogging) {
8018 "login: anonymous guests must specify a password"
8021 #endif /* CKSYSLOG */
8025 #ifdef CK_AUTHENTICATION
8026 && ck_tn_auth_valid() != AUTH_VALID
8027 #endif /* CK_AUTHENTICATION */
8028 ) { /* "ftp" is only account allowed no password */
8030 debug(F110,"zvpass","calling pam_set_item(AUTHTOK)",0);
8031 if ((pam_status = pam_set_item(pamh,PAM_AUTHTOK,p)) != PAM_SUCCESS) {
8032 reply = pam_strerror(pamh, pam_status);
8033 debug(F110,"zvpass PAM failure",reply,0);
8034 /* if no password given treat as non-fatal error */
8035 /* pam will prompt for password in pam_authenticate() */
8037 printf("%s\n",reply);
8039 debug(F100,"zvpass denied","",0);
8045 debug(F110,"zvpass","calling pam_authenticate",0);
8048 if ((pam_status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
8049 reply = pam_strerror(pamh, pam_status);
8050 debug(F110,"zvpass PAM failure",reply,0);
8051 printf("%s\n",reply);
8053 debug(F100,"zvpass denied","",0);
8060 debug(F110,"zvpass","calling pam_acct_mgmt",0);
8061 if ((pam_status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
8062 reply = pam_strerror(pamh, pam_status);
8063 debug(F110,"zvpass PAM failure",reply,0);
8064 printf("%s\n",reply);
8066 debug(F100,"zvpass denied","",0);
8071 debug(F110,"zvpass","PAM validates OK",0);
8077 salt = pw->pw_passwd;
8079 #ifdef HPUX10_TRUSTED
8080 xpasswd = bigcrypt(p, salt);
8083 On 64-bit platforms this can give "cast to pointer from integer of
8084 different size" warning, but I'm not sure what the effect is at runtime,
8085 or what to do about it.
8087 xpasswd = (char *)crypt(p, salt);
8088 #endif /* HPUX10_TRUSTED */
8092 /* null pw_passwd ok if Kerberos password ok */
8094 ((*pw->pw_passwd != '\0' ||
8095 strcmp(xpasswd, pw->pw_passwd))
8096 && !kpass(pw->pw_name, p))
8099 /* check with tpasswd first if there */
8100 pw == NULL || *pw->pw_passwd == '\0' ||
8101 t_verifypw (pw->pw_name, p) == 0 ||
8102 (t_verifypw (pw->pw_name, p) < 0 &&
8103 strcmp (xpasswd, pw->pw_passwd))
8105 /* The strcmp does not catch null passwords! */
8106 (pw == NULL) || (*pw->pw_passwd == '\0') ||
8107 strcmp(xpasswd, pw->pw_passwd)
8109 #endif /* FTP_KERBEROS */
8111 debug(F100,"zvpass denied","",0);
8119 (VOID) setgid((GID_T)pw->pw_gid); /* Set group ID */
8121 #ifndef NOINITGROUPS
8122 (VOID) initgroups(pw->pw_name, pw->pw_gid);
8123 #endif /* NOINITGROUPS */
8129 /* Open wtmp before chroot */
8131 sprintf(cksysline,"iks_%04x", getpid()); /* safe */
8132 logwtmp(cksysline, pw->pw_name,
8133 clienthost ? clienthost : "(unknown host)"
8135 debug(F110,"WTMP login",cksysline,logged_in);
8139 For anonymous users, we chroot to user ftp's home directory unless
8140 started with --anonroot:xxx, in which case we chroot to xxx. We must
8141 immediately chdir() to the same directory we chroot() to or else the
8142 old current directory remains accessible as "." outside the new root.
8145 if (anonroot) /* Non-default anonymous root */
8148 makestr(&anonroot,dir);
8150 debug(F110,"zvpass anon chroot",dir,0);
8151 if (chroot(dir) < 0) {
8152 debug(F111,"zvpass anon chroot FAILED",dir,errno);
8156 if (chdir("/") < 0) {
8157 debug(F111,"zvpass anon chdir FAILED",dir,errno);
8160 debug(F110,"zvpass anon chroot/chdir OK",dir,0);
8161 } else if (chdir(dir) < 0) { /* Not guest */
8163 if (chdir("/") < 0) {
8164 debug(F110,"Non-guest chdir FAILED",dir,0);
8167 printf("?No directory! Logging in with home=/\n");
8169 debug(F110,"zvpass non-guest chdir FAILED",dir,0);
8170 goto bad; /* Be conservative at first */
8171 #endif /* COMMENT */
8173 debug(F110,"zvpass non-guest chdir OK",dir,0);
8174 if (setuid((UID_T)pw->pw_uid) < 0) {
8175 debug(F101,"zvpass setuid FAILED","",pw->pw_uid);
8178 debug(F101,"zvpass setuid OK","",pw->pw_uid);
8180 guestpass[0] = '\0';
8184 fncact = XYFX_R; /* FILE COLLISION = RENAME */
8185 debug(F110,"GUEST fncact=R",p,0);
8186 lset(guestpass,"anonymous:",10,32);
8187 ckstrncpy(&guestpass[10],p,GUESTPASS-10);
8189 printf("Anonymous login.\r\n");
8192 /* proctitle declared where? Obviously this code is never compiled. */
8193 sprintf(proctitle, "%s: anonymous/%.*s",
8194 clienthost ? clienthost : "(unk)",
8195 sizeof(proctitle) - sizeof(clienthost) -
8196 sizeof(": anonymous/"), p);
8197 setproctitle(proctitle);
8198 #endif /* SETPROCTITLE */
8201 if (ckxsyslog && ckxlogging) {
8203 "login: anonymous %s %s",
8204 clienthost ? clienthost : "(unknown host)",
8208 #endif /* CKSYSLOG */
8210 } else { /* Real user */
8213 ckstrncpy(guestpass,zvuname,GUESTPASS);
8215 printf("User %s logged in.\r\n", pw->pw_name);
8218 sprintf(proctitle, "%s: %s",
8219 clienthost ? clienthost : "(unk)",
8222 setproctitle(proctitle);
8223 #endif /* SETPROCTITLE */
8226 if (ckxsyslog && ckxlogging)
8227 syslog(LOG_INFO, "login: %s %s",
8229 clienthost ? clienthost : "(unknown host)"
8231 #endif /* CKSYSLOG */
8233 ckstrncpy(zvhome,home,CKMAXPATH); /* Set environment variables */
8236 ckmakmsg(zenvuser,ZENVUSER,"USER=",zvuname,NULL,NULL);
8237 putenv((char *)zenvuser);
8238 ckmakmsg(zenvlogname,ZENVLOGNAME,"LOGNAME=",zvuname,NULL,NULL);
8239 putenv((char *)zenvlogname);
8240 ckmakmsg(zenvhome,ZENVHOME,"HOME=",zvhome,NULL,NULL);
8241 putenv((char *)zenvhome);
8242 #endif /* NOPUTENV */
8243 /* homdir = (char *)zvhome; */
8244 ckstrncpy((char *)uidbuf,(char *)zvuname,64);
8245 (VOID) umask(defumask);
8250 extern char dbrec[];
8251 extern unsigned long myflags;
8252 extern unsigned int mydbslot;
8253 extern struct iksdbfld dbfld[];
8254 #ifdef CK_AUTHENTICATION
8255 extern unsigned long myamode, myatype;
8256 #endif /* CK_AUTHENTICATION */
8257 myflags |= DBF_LOGGED;
8260 debug(F101,"zvpass guest","",guest);
8261 debug(F111,"zvpass zvuname",zvuname,0);
8262 debug(F110,"zvpass guestpass",guestpass,0);
8263 debug(F110,"zvpass dir",dir,0);
8264 debug(F110,"zvpass home",home,0);
8265 debug(F110,"zvpass anonroot",anonroot,0);
8268 p2 = guest ? guestpass : zvuname;
8270 p2 = (char *)guestpass;
8271 myflags &= ~DBF_USER;
8273 p2 = (char *)zvuname;
8274 myflags |= DBF_USER;
8277 strncpy(&dbrec[DB_ULEN],ulongtohex((unsigned long)k,4),4);
8278 lset(&dbrec[dbfld[db_USER].off],p2,1024,32);
8279 strncpy(&dbrec[DB_FLAGS],ulongtohex(myflags,4),4);
8280 #ifdef CK_AUTHENTICATION
8281 myamode = ck_tn_auth_valid();
8282 strncpy(&dbrec[DB_AMODE],ulongtohex(myamode,4),4);
8283 myatype = ck_tn_authenticated();
8284 strncpy(&dbrec[DB_ATYPE],ulongtohex(myatype,4),4);
8285 #endif /* CK_AUTHENTICATION */
8291 if (!*p2) p2 = home;
8293 strncpy(&dbrec[DB_DLEN],
8294 ulongtohex((unsigned long)strlen(p2),4),
8297 lset(&dbrec[dbfld[db_DIR].off],p2,1024,32);
8303 bad: /* Common failure exit */
8308 #endif /* CK_LOGIN */
8310 /* Buggy Xenix 2.3.4 cc needs this line after the endif */