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 */
494 extern int gotemptypasswd;
496 #endif /* CK_LOGIN */
499 static char ckroot[CKMAXPATH+1] = { NUL, NUL };
500 static int ckrootset = 0;
504 _PROTOTYP( VOID ignorsigs, (void) );
505 _PROTOTYP( VOID restorsigs, (void) );
508 Change argument to "(const char *)" if this causes trouble.
509 Or... if it causes trouble, then maybe it was already declared
510 in a header file after all, so you can remove this prototype.
512 #ifndef NDGPWNAM /* If not defined No Declare getpwnam... */
513 #ifndef _POSIX_SOURCE
516 /* POSIX <pwd.h> already gave prototypes for these. */
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 *) );
533 _PROTOTYP( struct passwd * getpwnam, (const char *) );
535 _PROTOTYP( struct passwd * getpwnam, (char *) );
536 #endif /* DCGPWNAM */
546 _PROTOTYP( struct passwd * getpwuid, (PWID_T) );
551 _PROTOTYP( struct passwd * getpwent, (void) );
554 #endif /* _POSIX_SOURCE */
555 #endif /* NDGPWNAM */
557 #ifdef CK_SHADOW /* Shadow Passwords... */
559 #endif /* CK_SHADOW */
560 #ifdef CK_PAM /* PAM... */
561 #include <security/pam_appl.h>
562 #ifndef PAM_SERVICE_TYPE /* Defines which PAM service we are */
563 #define PAM_SERVICE_TYPE "kermit"
564 #endif /* PAM_SERVICE_TYPE */
569 #define PAM_CONST CONST
572 static char * pam_pw = NULL;
577 PAM_CONST struct pam_message **msg,
578 struct pam_response **resp,
582 pam_cb(num_msg, msg, resp, appdata_ptr)
584 PAM_CONST struct pam_message **msg;
585 struct pam_response **resp;
587 #endif /* CK_ANSIC */
591 debug(F111,"pam_cb","num_msg",num_msg);
593 for (i = 0; i < num_msg; i++) {
594 char message[PAM_MAX_MSG_SIZE];
596 /* Issue prompt and get response */
597 debug(F111,"pam_cb","Message",i);
598 debug(F111,"pam_cb",msg[i]->msg,msg[i]->msg_style);
599 if (msg[i]->msg_style == PAM_ERROR_MSG) {
600 debug(F111,"pam_cb","PAM ERROR",0);
601 fprintf(stdout,"%s\n", msg[i]->msg);
603 } else if (msg[i]->msg_style == PAM_TEXT_INFO) {
604 debug(F111,"pam_cb","PAM TEXT INFO",0);
605 fprintf(stdout,"%s\n", msg[i]->msg);
607 } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) {
608 debug(F111,"pam_cb","Reading response, no echo",0);
609 /* Ugly hack. We check to see if a password has been pushed */
610 /* into zvpasswd(). This would be true if the password was */
611 /* received by REMOTE LOGIN. */
613 ckstrncpy(message,pam_pw,PAM_MAX_MSG_SIZE);
615 readpass((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
616 } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON) {
617 debug(F111,"pam_cb","Reading response, with echo",0);
618 readtext((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
620 debug(F111,"pam_cb","unknown style",0);
624 /* Allocate space for this message's response structure */
625 resp[i] = (struct pam_response *) malloc(sizeof (struct pam_response));
628 debug(F110,"pam_cb","malloc failure",0);
629 for (j = 0; j < i; j++) {
636 /* Allocate a buffer for the response */
637 resp[i]->resp = (char *) malloc((int)strlen(message) + 1);
638 if (!resp[i]->resp) {
640 debug(F110,"pam_cb","malloc failure",0);
641 for (j = 0; j < i; j++) {
648 /* Return the results back to PAM */
649 strcpy(resp[i]->resp, message); /* safe (prechecked) */
650 resp[i]->resp_retcode = 0;
652 debug(F110,"pam_cb","Exiting",0);
657 /* Define macros for getting file type */
661 Olivetti X/OS 2.3 has S_ISREG and S_ISDIR defined
662 incorrectly, so we force their redefinition.
668 #ifdef UTSV /* Same deal for Amdahl UTSV */
673 #ifdef UNISYS52 /* And for UNISYS UTS V 5.2 */
676 #endif /* UNISYS52 */
678 #ifdef ICLSVR3 /* And for old ICL versions */
683 #ifdef ISDIRBUG /* Also allow this from command line */
690 #endif /* ISDIRBUG */
696 #define _IFMT 0170000
701 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
704 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
707 /* The following mainly for NeXTSTEP... */
710 #define S_IWUSR 0000200
714 #define S_IRGRP 0000040
718 #define S_IWGRP 0000020
722 #define S_IXGRP 0000010
726 #define S_IROTH 0000004
730 #define S_IWOTH 0000002
734 #define S_IXOTH 0000001
737 Define maximum length for a file name if not already defined.
738 NOTE: This applies to a path segment (directory or file name),
739 not the entire path string, which can be CKMAXPATH bytes long.
743 #define MAXNAMLEN _MAX_FNAME
746 #endif /* _MAX_FNAME */
750 #define MAXNAMLEN 255
753 #define MAXNAMLEN FILENAME_MAX
756 #define MAXNAMLEN NAME_MAX
758 #ifdef _POSIX_NAME_MAX
759 #define MAXNAMLEN _POSIX_NAME_MAX
762 #define MAXNAMLEN _D_NAME_MAX
765 #define MAXNAMLEN DIRSIZ
769 #endif /* _D_NAME_MAX */
770 #endif /* _POSIX_NAME_MAX */
771 #endif /* NAME_MAX */
772 #endif /* FILENAME_MAX */
774 #endif /* MAXNAMLEN */
778 /* As of 2001-11-03 this is handled in ckcdeb.h */
779 /* Longest pathname ... */
781 Beware: MAXPATHLEN is one of UNIX's dirty little secrets. Where is it
782 defined? Who knows... <param.h>, <mod.h>, <unistd.h>, <limits.h>, ...
783 There is not necessarily even a definition for it anywhere, or it might have
784 another name. If you get it wrong, bad things happen with getcwd() and/or
785 getwd(). If you allocate a buffer that is too short, getwd() might write
786 over memory and getcwd() will fail with ERANGE. The definitions of these
787 functions (e.g. in SVID or POSIX.1) do not tell you how to determine the
788 maximum path length in order to allocate a buffer that is the right size.
791 #include <sys/param.h> /* For MAXPATHLEN */
794 #include <sys/param.h> /* for MAXPATHLEN, needed for -DDIRENT */
795 #endif /* COHERENT */
802 #define MAXPATH MAXPATHLEN
805 #define MAXPATH PATH_MAX
807 #ifdef _POSIX_PATH_MAX
808 #define MAXPATH _POSIX_PATH_MAX
819 #endif /* _POSIX_PATH_MAX */
820 #endif /* PATH_MAX */
821 #endif /* MAXPATHLEN */
823 /* Maximum number of filenames for wildcard expansion */
826 /* Already defined in ckcdeb.h so the following is superfluous. */
827 /* Don't expect changing them to have any effect. */
832 #define MAXWLD 102400
835 #endif /* BIGBUFOK */
836 #endif /* CK_SMALL */
839 static int maxnames = MAXWLD;
841 /* Define the size of the string space for filename expansion. */
854 #define SSPACE 10000 /* Size of string-generating buffer */
855 #else /* Default static buffer size */
857 #define SSPACE 65000 /* Size of string-generating buffer */
859 #define SSPACE 2000 /* size of string-generating buffer */
860 #endif /* BIGBUFOK */
865 static char sspace[SSPACE]; /* Buffer for generating filenames */
866 #else /* is DYNAMIC */
868 #define SSPACE 500000
871 #endif /* BIGBUFOK */
872 char *sspace = (char *)0;
874 static int ssplen = SSPACE; /* Length of string space buffer */
877 /* fdopen() needs declaring because it's not declared in <stdio.h> */
878 _PROTOTYP( FILE * fdopen, (int, char *) );
879 #endif /* DCLFDOPEN */
882 /* popen() needs declaring because it's not declared in <stdio.h> */
883 _PROTOTYP( FILE * popen, (char *, char *) );
884 #endif /* DCLPOPEN */
888 /* More internal function prototypes */
890 * The path structure is used to represent the name to match.
891 * Each slash-separated segment of the name is kept in one
892 * such structure, and they are linked together, to make
893 * traversing the name easier.
896 char npart[MAXNAMLEN+4]; /* name part of path segment */
897 struct path *fwd; /* forward ptr */
900 _PROTOTYP( int shxpand, (char *, char *[], int ) );
902 _PROTOTYP( static int fgen, (char *, char *[], int ) );
903 _PROTOTYP( static VOID traverse, (struct path *, char *, char *) );
904 _PROTOTYP( static VOID addresult, (char *, int) );
906 /* Replaced by ckmatch() */
907 _PROTOTYP( static int match, (char *, char *) );
909 _PROTOTYP( char * whoami, (void) );
910 _PROTOTYP( UID_T real_uid, (void) );
911 _PROTOTYP( static struct path *splitpath, (char *p) );
912 _PROTOTYP( char * zdtstr, (time_t) );
913 _PROTOTYP( time_t zstrdt, (char *, int) );
915 /* Some systems define these symbols in include files, others don't... */
918 #define R_OK 4 /* For access */
931 #endif /* O_RDONLY */
933 /* syslog and wtmp items for Internet Kermit Service */
935 extern char * clienthost; /* From ckcmai.c. */
937 static char fullname[CKMAXPATH+1];
938 static char tmp2[CKMAXPATH+1];
940 extern int ckxlogging;
942 #ifdef CKXPRINTF /* Our printf macro conflicts with */
943 #undef printf /* use of "printf" in syslog.h */
944 #endif /* CKXPRINTF */
947 #include <sys/syslog.h>
951 #endif /* CKSYSLOG */
953 #define printf ckxprintf
954 #endif /* CKXPRINTF */
956 int ckxanon = 1; /* Anonymous login ok */
957 int ckxperms = 0040; /* Anonymous file permissions */
958 int ckxpriv = 1; /* Priv'd login ok */
961 #define XFERFILE "/var/log/iksd.log"
962 #endif /* XFERFILE */
964 /* wtmp logging for IKSD... */
966 #ifndef CKWTMP /* wtmp logging not selected */
967 int ckxwtmp = 0; /* Know this at runtime */
968 #else /* wtmp file details */
970 #ifdef UTMPBUG /* Unfortunately... */
972 Some versions of Linux have a <utmp.h> file that contains
973 "enum utlogin { local, telnet, rlogin, screen, ... };" This clobbers
974 any program that uses any of these words as variable names, function
975 names, macro names, etc. (Other versions of Linux have this declaration
976 within #if 0 ... #endif.) There is nothing we can do about this other
977 than to not include the stupid file. But we need stuff from it, so...
979 #include <features.h>
980 #include <sys/types.h>
981 #define UT_LINESIZE 32
982 #define UT_NAMESIZE 32
983 #define UT_HOSTSIZE 256
991 short int e_termination; /* Process termination status. */
992 short int e_exit; /* Process exit status. */
996 short int ut_type; /* Type of login */
997 pid_t ut_pid; /* Pid of login process */
998 char ut_line[UT_LINESIZE]; /* NUL-terminated devicename of tty */
999 char ut_id[4]; /* Inittab id */
1000 char ut_user[UT_NAMESIZE]; /* Username (not NUL terminated) */
1002 char ut_host[UT_HOSTSIZE]; /* Hostname for remote login */
1003 struct exit_status ut_exit; /* Exit status */
1004 long ut_session; /* Session ID, used for windowing */
1005 struct timeval ut_tv; /* Time entry was made */
1006 int32_t ut_addr_v6[4]; /* Internet address of remote host */
1007 char pad[20]; /* Reserved */
1010 #define ut_time ut_tv.tv_sec /* Why should Linux be like anything else? */
1011 #define ut_name ut_user /* ... */
1014 logwtmp __P ((__const char *__ut_line, __const char *__ut_name,
1015 __const char *__ut_host));
1017 #else /* Not UTMPBUG */
1019 #ifndef HAVEUTMPX /* Who has <utmpx.h> */
1034 #endif /* UNIXWARE */
1035 #endif /* HPUX100 */
1036 #endif /* CK_SCOV5 */
1038 #endif /* SOLARIS */
1039 #endif /* HAVEUTMPX */
1044 /* Because the time_t in the utmp struct is 64 bits but time() wants 32 */
1045 #define __V40_OBJ_COMPAT 1
1049 #undef __V40_OBJ_COMPAT
1051 #endif /* HAVEUTMPX */
1052 #endif /* UTMPBUG */
1056 #define WTMPFILE "/usr/adm/wtmp.1"
1059 #define WTMPFILE "/var/log/wtmp"
1061 #define WTMPFILE "/usr/adm/wtmp"
1064 #endif /* WTMPFILE */
1065 char * wtmpfile = NULL;
1067 static int wtmpfd = 0;
1068 static char cksysline[32] = { NUL, NUL };
1070 #ifndef HAVEUTHOST /* Does utmp include ut_host[]? */
1071 #ifdef HAVEUTMPX /* utmpx always does */
1074 #ifdef LINUX /* Linux does */
1077 #ifdef SUNOS4 /* SunOS does */
1080 #ifdef AIX41 /* AIX 4.1 and later do */
1085 #endif /* HAVEUTMPX */
1086 #endif /* HAVEUTHOST */
1089 PID_T _vfork() { /* To satisfy a library foulup */
1090 return(fork()); /* in Unixware 2.0.x */
1096 logwtmp(const char * line, const char * name, const char * host)
1098 logwtmp(line, name, host) char *line, *name, *host;
1099 #endif /* CK_ANSIC */
1102 struct utmpx ut; /* Needed for ut_host[] */
1105 #endif /* HAVEUTMPX */
1107 /* time_t time(); */
1113 makestr(&wtmpfile,WTMPFILE);
1115 if (!line) line = "";
1116 if (!name) name = "";
1117 if (!host) host = "";
1119 if (!wtmpfd && (wtmpfd = open(wtmpfile, O_WRONLY|O_APPEND, 0)) < 0) {
1121 debug(F110,"WTMP open failed",line,0);
1124 if (!fstat(wtmpfd, &buf)) {
1125 ckstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
1126 ckstrncpy(ut.ut_name, name, sizeof(ut.ut_name));
1129 ckstrncpy(ut.ut_host, host, sizeof(ut.ut_host));
1130 #endif /* HAVEUTHOST */
1132 time(&ut.ut_tv.tv_sec);
1135 /* In light of the following comment perhaps the previous line should */
1136 /* be "#ifndef COMMENT". */
1139 * On 64-bit platforms sizeof(time_t) and sizeof(ut.ut_time)
1140 * are not the same and attempt to use an address of
1141 * ut.ut_time as an argument to time() call may cause
1142 * "unaligned access" trap.
1151 #endif /* HAVEUTMPX */
1152 if (write(wtmpfd, (char *)&ut, sizeof(struct utmp)) !=
1153 sizeof(struct utmp)) {
1156 ftruncate(wtmpfd, buf.st_size); /* Error, undo any partial write */
1158 chsize(wtmpfd, buf.st_size); /* Error, undo any partial write */
1159 #endif /* COHERENT */
1160 #endif /* NOFTRUNCATE */
1161 debug(F110,"WTMP write error",line,0);
1163 debug(F110,"WTMP record OK",line,0);
1172 C K S Y S L O G -- C-Kermit system logging function,
1174 For use by other modules.
1175 This module can, but doesn't have to, use it.
1177 n = SYSLG_xx values defined in ckcdeb.h
1178 s1, s2, s3: strings.
1181 cksyslog(n, m, s1, s2, s3) int n, m; char * s1, * s2, * s3; {
1184 if (!ckxlogging) /* syslogging */
1186 if (!s1) s1 = ""; /* Fix null args */
1189 switch (n) { /* Translate Kermit level */
1190 case SYSLG_DB: /* to syslog level */
1194 level = m ? LOG_INFO : LOG_ERR;
1196 debug(F110,"cksyslog s1",s1,0);
1197 debug(F110,"cksyslog s2",s2,0);
1198 debug(F110,"cksyslog s3",s3,0);
1200 syslog(level, "%s: %s %s", s1, s2, s3); /* Write syslog record */
1201 debug(F101,"cksyslog errno","",errno);
1203 #endif /* CKSYSLOG */
1208 int maxnam = MAXNAMLEN; /* Available to the outside */
1209 int maxpath = MAXPATH;
1213 char startupdir[MAXPATH+1];
1216 int pexitstat = -2; /* Process exit status */
1218 FILE *fp[ZNFILS] = { /* File pointers */
1219 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
1222 /* Flags for each file indicating whether it was opened with popen() */
1223 int ispipe[ZNFILS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1225 /* Buffers and pointers used in buffered file input and output. */
1227 extern char *zinbuffer, *zoutbuffer;
1229 extern char zinbuffer[], zoutbuffer[];
1230 #endif /* DYNAMIC */
1231 extern char *zinptr, *zoutptr;
1232 extern int zincnt, zoutcnt;
1233 extern int wildxpand;
1235 static long iflen = -1L; /* Input file length */
1237 static PID_T pid = 0; /* pid of child fork */
1238 static int fcount = 0; /* Number of files in wild group */
1239 static int nxpand = 0; /* Copy of fcount */
1240 static char nambuf[CKMAXPATH+4]; /* Buffer for a pathname */
1243 #define ZMBUFLEN 200
1244 static char zmbuf[ZMBUFLEN]; /* For mail, remote print strings */
1245 #endif /* NOFRILLS */
1247 char **mtchs = NULL; /* Matches found for filename */
1248 char **mtchptr = NULL; /* Pointer to current match */
1250 /* Z K S E L F -- Kill Self: log out own job, if possible. */
1252 /* Note, should get current pid, but if your system doesn't have */
1253 /* getppid(), then just kill(0,9)... */
1258 /* Already declared in unistd.h for SVR3 and POSIX */
1260 extern PID_T getppid(void);
1264 extern PID_T getppid();
1265 #endif /* COHERENT */
1266 #endif /* PS2AIX10 */
1267 #endif /* CK_ANSIC */
1273 zkself() { /* For "bye", but no guarantee! */
1290 return(kill((PID_T)getpid(),1));
1293 exit(kill((PID_T)getppid(),1));
1296 exit(kill(getppid(),1));
1308 getfullname(name) char * name; {
1309 char *p = (char *)fullname;
1312 /* If necessary we could also chase down symlinks here... */
1314 /* This works but is incompatible with wuftpd */
1315 if (isguest && anonroot) {
1316 ckstrncpy(fullname,anonroot,CKMAXPATH);
1317 len = strlen(fullname);
1319 if (fullname[len-1] == '/')
1323 #endif /* COMMENT */
1324 zfnqfp(name, CKMAXPATH - len, p);
1326 if (*p < '!') *p = '_';
1331 /* D O I K L O G -- Open Kermit-specific ftp-like transfer log. */
1333 VOID /* Called in ckcmai.c */
1335 if (iklogopen) /* Already open? */
1337 if (xferlog) { /* Open iksd log if requested */
1338 if (!xferfile) /* If no pathname given */
1339 makestr(&xferfile,XFERFILE); /* use this default */
1341 xferlog = open(xferfile, O_WRONLY | O_APPEND | O_CREAT, 0660);
1342 debug(F101,"doiklog open","",xferlog);
1345 syslog(LOG_ERR, "xferlog open failure %s: %m", xferfile);
1346 #endif /* CKSYSLOG */
1347 debug(F101,"doiklog open errno","",errno);
1354 if (xferlog && ckxlogging)
1355 syslog(LOG_INFO, "xferlog: %s open ok", xferfile);
1356 #endif /* CKSYSLOG */
1360 /* Z O P E N I -- Open an existing file for input. */
1362 /* Returns 1 on success, 0 on failure */
1365 zopeni(n,name) int n; char *name; {
1368 debug(F111,"zopeni",name,n);
1369 if ((x = chkfn(n)) != 0) {
1370 debug(F111,"zopeni chkfn",ckitoa(n),x);
1373 zincnt = 0; /* Reset input buffer */
1374 if (n == ZSYSFN) { /* Input from a system function? */
1376 /*** Note, this function should not be called with ZSYSFN ***/
1377 /*** Always call zxcmd() directly, and give it the real file number ***/
1378 /*** you want to use. ***/
1379 return(zxcmd(n,name)); /* Try to fork the command */
1381 debug(F110,"zopeni called with ZSYSFN, failing!",name,0);
1382 *nambuf = '\0'; /* No filename. */
1383 return(0); /* fail. */
1384 #endif /* COMMENT */
1386 if (n == ZSTDIO) { /* Standard input? */
1388 fprintf(stderr,"Terminal input not allowed");
1389 debug(F110,"zopeni: attempts input from unredirected stdin","",0);
1397 debug(F111,"zopeni setroot",ckroot,ckrootset);
1398 if (ckrootset) if (!zinroot(name)) {
1399 debug(F110,"zopeni setroot violation",name,0);
1403 fp[n] = fopen(name,"r"); /* Real file, open it. */
1404 debug(F111,"zopeni fopen", name, fp[n]);
1406 printf("ZOPENI fp[%d]=%ld\n",n,fp[n]);
1412 || ((ckxsyslog >= SYSLG_FA) && ckxlogging)
1413 #endif /* CKSYSLOG */
1416 debug(F110,"zopeni fullname",fullname,0);
1418 if (fp[n] == NULL) {
1420 if (ckxsyslog >= SYSLG_FA && ckxlogging) {
1421 syslog(LOG_INFO, "file[%d] %s: open failed (%m)", n, fullname);
1424 #endif /* CKSYSLOG */
1429 if (ckxsyslog >= SYSLG_FA && ckxlogging)
1430 syslog(LOG_INFO, "file[%d] %s: open read ok", n, fullname);
1431 #endif /* CKSYSLOG */
1442 #endif /* O_NDELAY */
1445 /* Z O P E N O -- Open a new file for output. */
1447 /*ARGSUSED*/ /* zz not used */
1449 zopeno(n,name,zz,fcb)
1450 /* zopeno */ int n; char *name; struct zattr *zz; struct filinfo *fcb; {
1455 /* As of Version 5A, the attribute structure and the file information */
1456 /* structure are included in the arglist. */
1459 debug(F111,"zopeno",name,n);
1461 debug(F101,"zopeno fcb disp","",fcb->dsp);
1462 debug(F101,"zopeno fcb type","",fcb->typ);
1463 debug(F101,"zopeno fcb char","",fcb->cs);
1465 debug(F100,"zopeno fcb is NULL","",0);
1469 if (chkfn(n) != 0) /* Already open? */
1470 return(0); /* Nothing to do. */
1472 if ((n == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */
1473 fp[ZOFILE] = stdout;
1476 /* This seems right but it breaks client server ops */
1479 #endif /* COMMENT */
1482 debug(F101,"zopeno fp[n]=stdout","",fp[n]);
1485 zoutptr = zoutbuffer;
1489 /* A real file. Open it in desired mode (create or append). */
1492 debug(F111,"zopeno setroot",ckroot,ckrootset);
1493 if (ckrootset) if (!zinroot(name)) {
1494 debug(F110,"zopeno setroot violation",name,0);
1499 ckstrncpy(p,"w",8); /* Assume write/create mode */
1500 if (fcb) { /* If called with an FCB... */
1501 if (fcb->dsp == XYFZ_A) { /* Does it say Append? */
1502 ckstrncpy(p,"a",8); /* Yes. */
1503 debug(F100,"zopeno append","",0);
1510 || ((ckxsyslog >= SYSLG_FC) && ckxlogging)
1511 #endif /* CKSYSLOG */
1514 debug(F110,"zopeno fullname",fullname,0);
1516 debug(F110,"zopeno fopen arg",p,0);
1517 fp[n] = fopen(name,p); /* Try to open the file */
1521 printf("ZOPENO fp[%d]=%ld\n",n,fp[n]);
1524 if (fp[n] == NULL) { /* Failed */
1525 debug(F101,"zopeno failed errno","",errno);
1527 if (ckxsyslog >= SYSLG_FC && ckxlogging)
1528 syslog(LOG_INFO, "file[%d] %s: %s failed (%m)",
1531 append ? "append" : "create"
1533 #endif /* CKSYSLOG */
1534 #ifdef COMMENT /* Let upper levels print message. */
1535 perror("Can't open output file");
1536 #endif /* COMMENT */
1537 } else { /* Succeeded */
1538 extern int zofbuffer, zofblock, zobufsize;
1539 debug(F101, "zopeno zobufsize", "", zobufsize);
1540 if (n == ZDFILE || n == ZTFILE) { /* If debug or transaction log */
1541 setbuf(fp[n],NULL); /* make it unbuffered. */
1543 } else if (n == ZOFILE && !zofblock) { /* blocking or nonblocking */
1545 if ((flags = fcntl(fileno(fp[n]),F_GETFL,0)) > -1)
1546 fcntl(fileno(fp[n]),F_SETFL, flags |
1553 debug(F100,"zopeno ZOFILE nonblocking","",0);
1554 #endif /* DONDELAY */
1555 } else if (n == ZOFILE && !zofbuffer) { /* buffered or unbuffered */
1557 debug(F100,"zopeno ZOFILE unbuffered","",0);
1561 /* Enforce anonymous file-creation permission */
1563 if (n == ZWFILE || n == ZMFILE ||
1564 n == ZOFILE || n == ZDFILE ||
1565 n == ZTFILE || n == ZPFILE ||
1567 chmod(name,ckxperms);
1568 #endif /* CK_LOGIN */
1570 if (ckxsyslog >= SYSLG_FC && ckxlogging)
1571 syslog(LOG_INFO, "file[%d] %s: %s ok",
1574 append ? "append" : "create"
1576 #endif /* CKSYSLOG */
1577 debug(F100, "zopeno ok", "", 0);
1579 zoutcnt = 0; /* (PWP) reset output buffer */
1580 zoutptr = zoutbuffer;
1581 return((fp[n] != NULL) ? 1 : 0);
1584 /* Z C L O S E -- Close the given file. */
1586 /* Returns 0 if arg out of range, 1 if successful, -1 if close failed. */
1593 debug(F101,"zclose file number","",n);
1594 if (chkfn(n) < 1) return(0); /* Check range of n */
1595 if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */
1598 if (fp[ZSYSFN] || ispipe[n]) { /* If file is really pipe */
1600 x = zclosf(n); /* do it specially */
1604 debug(F101,"zclose zclosf","",x);
1605 debug(F101,"zclose zclosf fp[n]","",fp[n]);
1607 if ((fp[n] != stdout) && (fp[n] != stdin))
1611 if (n == ZCTERM || n == ZSTDIO) /* See zopeno() */
1612 if (fp[ZOFILE] == stdout)
1614 #endif /* COMMENT */
1616 iflen = -1L; /* Invalidate file length */
1617 if (x == EOF) { /* if we got a close error */
1618 debug(F101,"zclose fclose fails","",x);
1620 } else if (x2 < 0) { /* or error flushing last buffer */
1621 debug(F101,"zclose error flushing last buffer","",x2);
1622 return(-1); /* then return an error */
1624 /* Print log record compatible with wu-ftpd */
1625 if (xferlog && (n == ZIFILE || n == ZOFILE)) {
1627 extern char ttname[];
1628 if (!iklogopen) (VOID) doiklog(); /* Open log if necessary */
1629 debug(F101,"zclose iklogopen","",iklogopen);
1634 timenow = time(NULL);
1639 #endif /* CK_LOGIN */
1648 #endif /* CK_LOGIN */
1651 len = 24 + 12 + (int)strlen(s) + 16
1652 + (int)strlen(fullname) + 1 + 1 + 1 + 1
1653 + (int)strlen(p) + 6 + 2 + 12;
1655 if (!*fnam) fnam = "(pipe)";
1657 if (len > IKSDMSGLEN)
1658 sprintf(iksdmsg, /* SAFE */
1659 "%.24s [BUFFER WOULD OVERFLOW]\n",ctime(&timenow));
1661 sprintf(iksdmsg, /* SAFE */
1662 "%.24s %d %s %ld %s %c %s %c %c %s %s %d %s\n",
1663 ctime(&timenow), /* date/time */
1664 gtimer(), /* elapsed secs */
1666 ffc, /* byte count */
1667 fnam, /* full pathname of file */
1668 (binary ? 'b' : 'a'), /* binary or ascii */
1669 "_", /* options = none */
1670 n == ZIFILE ? 'o' : 'i', /* in/out */
1672 (isguest ? 'a' : 'r'), /* User type */
1675 #endif /* CK_LOGIN */
1676 p, /* Username or guest passwd */
1678 logged_in ? "iks" : "kermit", /* Record ID */
1681 #endif /* CK_LOGIN */
1682 0, /* User ID on client system unknown */
1685 debug(F110,"zclose iksdmsg",iksdmsg,0);
1686 write(xferlog, iksdmsg, (int)strlen(iksdmsg));
1689 debug(F101,"zclose returns","",1);
1694 /* Z C H I N -- Get a character from the input file. */
1696 /* Returns -1 if EOF, 0 otherwise with character returned in argument */
1699 zchin(n,c) int n; int *c; {
1703 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1709 /* (PWP) Just in case this gets called when it shouldn't. */
1711 a = zminchar(); /* Note: this catches Ctrl-Z */
1712 if (a < 0) /* (See zinfill()...) */
1716 if (a == EOF) return(-1);
1718 /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
1719 if (!binary && a == 0x1A && eofmethod == XYEOF_Z)
1721 #endif /* CK_CTRLZ */
1723 *c = (CHAR) a & 0377;
1727 /* Z S I N L -- Read a line from a file */
1730 Writes the line into the address provided by the caller.
1731 n is the Kermit "channel number".
1732 Writing terminates when newline is encountered, newline is not copied.
1733 Writing also terminates upon EOF or if length x is exhausted.
1734 Returns 0 on success, -1 on EOF or error.
1737 zsinl(n,s,x) int n, x; char *s; {
1738 int a, z = 0; /* z is return code. */
1742 extern CHAR feol; /* Line terminator */
1744 if (!s || chkfn(n) < 1) /* Make sure file is open, etc */
1747 s[0] = '\0'; /* Don't return junk */
1749 a = -1; /* Current character, none yet. */
1750 while (x--) { /* Up to given length */
1752 if (feol) /* Previous character */
1754 if (zchin(n,&a) < 0) { /* Read a character from the file */
1755 debug(F101,"zsinl zchin fail","",count);
1757 z = -1; /* EOF or other error */
1761 if (feol) { /* Single-character line terminator */
1764 } else { /* CRLF line terminator */
1765 if (a == '\015') /* CR, get next character */
1767 if (old == '\015') { /* Previous character was CR */
1768 if (a == '\012') { /* This one is LF, so we have a line */
1770 } else { /* Not LF, deposit CR */
1777 *s = a; /* Deposit character */
1781 *s = '\0'; /* Terminate the string */
1782 debug(F011,"zsinl",buf,len);
1786 /* Z X I N -- Read x bytes from a file */
1789 Reads x bytes (or less) from channel n and writes them
1790 to the address provided by the caller.
1791 Returns number of bytes read on success, 0 on EOF or error.
1794 zxin(n,s,x) int n, x; char *s; {
1796 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1799 if (a < 1) return(0);
1800 for (i = 0; i < a && i < x; i++)
1806 return(fread(s, sizeof (char), x, fp[n]));
1810 Z I N F I L L -- Buffered file input.
1812 (re)fill the file input buffer with data. All file input
1813 should go through this routine, usually by calling the zminchar()
1814 macro defined in ckcker.h. Returns:
1816 Value 0..255 on success, the character that was read.
1818 -2 on any kind of error other than end of file.
1819 -3 timeout when reading from pipe (Kermit packet mode only).
1823 extern int kactive, srvping;
1827 printf("ZINFILL fp[%d]=%ld\n",ZIFILE,fp[ZIFILE]);
1831 if (inserver && !local && fp[ZIFILE] == stdin) {
1834 if (a < 0) return(-2);
1835 for (i = 0; i < a && i < INBUFSIZE; i++) {
1836 zinbuffer[i] = coninc(0);
1839 /* set pointer to beginning, (== &zinbuffer[0]) */
1841 if (zincnt == 0) return(-1);
1842 zincnt--; /* One less char in buffer */
1843 return((int)(*zinptr++) & 0377); /* because we return the first */
1847 debug(F101,"zinfill kactive","",kactive);
1849 if (!(kactive && ispipe[ZIFILE])) {
1850 if (feof(fp[ZIFILE])) {
1851 debug(F100,"ZINFILL feof","",0);
1853 printf("ZINFILL EOF\n");
1858 clearerr(fp[ZIFILE]);
1861 /* Here we can call select() to get a timeout... */
1862 if (kactive && ispipe[ZIFILE]) {
1867 debug(F101,"zinfill calling ttwait","",secs);
1868 z = ttwait(fileno(fp[ZIFILE]),secs);
1869 debug(F101,"zinfill ttwait","",z);
1880 debug(F101,"ZINFILL INBUFSIZE","",INBUFSIZE);
1882 memset(zinbuffer, 0xFF, INBUFSIZE);
1884 for (i = 0; i < INBUFSIZE; i++) {
1885 zinbuffer[i] = 0xFF;
1886 #ifdef COMMENT /* Too much! */
1887 debug(F101,"ZINFILL zinbuffer[i]","",i);
1888 #endif /* COMMENT */
1890 #endif /* USE_MEMCPY */
1891 ckstrncpy(zinbuffer,"zinbuffer is a valid buffer",INBUFSIZE);
1892 debug(F111,"ZINFILL about to call fread",zinbuffer,zinbuffer);
1897 Note: The following read MUST be nonblocking when reading from a pipe
1898 and we want timeouts to work. See zxcmd().
1900 zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]);
1901 debug(F101,"ZINFILL fread","",zincnt); /* Just the size */
1903 printf("FREAD=%d\n",zincnt);
1906 /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
1907 if (zincnt > 0 && !binary && eofmethod == XYEOF_Z) {
1909 for (i = 0; i < zincnt; i++) {
1910 if (zinbuffer[i] == SUB) {
1911 zincnt = i; /* Stop at first Ctrl-Z */
1918 #endif /* CK_CTRLZ */
1920 if (zincnt == 0) { /* Got nothing? */
1921 if (ferror(fp[ZIFILE])) {
1922 debug(F100,"ZINFILL ferror","",0);
1923 debug(F101,"ZINFILL errno","",errno);
1925 printf("ZINFILL errno=%d\n",errno);
1928 return((errno == EWOULDBLOCK) ? -3 : -2);
1931 #endif /* EWOULDBLOCK */
1934 /* In case feof() didn't work just above -- sometimes it doesn't... */
1936 if (feof(fp[ZIFILE]) ) {
1937 debug(F100,"ZINFILL count 0 EOF return -1","",0);
1940 debug(F100,"ZINFILL count 0 not EOF return -2","",0);
1944 zinptr = zinbuffer; /* set pointer to beginning, (== &zinbuffer[0]) */
1945 zincnt--; /* One less char in buffer */
1946 return((int)(*zinptr++) & 0377); /* because we return the first */
1949 /* Z S O U T -- Write a string out to the given file, buffered. */
1952 zsout(n,s) int n; char *s; {
1955 if (rc < 1) return(-1); /* Keep this, prevents memory faults */
1956 if (!s) return(0); /* Null pointer, do nothing, succeed */
1957 if (!*s) return(0); /* empty string, ditto */
1961 This happens with client-side Kermit server when a REMOTE command
1962 was sent from the server to the client and the server is supposed to
1963 display the text, but of course there is no place to display it
1964 since it is in remote mode executing Kermit protocol.
1966 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1968 return(ttol(s,((int)strlen(s)) < 0) ? -1 : 0);
1971 #endif /* COMMENT */
1976 return(write(fileno(fp[n]),s,(int)strlen(s)));
1977 rc = fputs(s,fp[n]) == EOF ? -1 : 0;
1983 /* Z S O U T L -- Write string to file, with line terminator, buffered */
1986 zsoutl(n,s) int n; char *s; {
1991 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1995 return(0); /* See comments in zsout() */
1996 #endif /* COMMENT */
2000 if (n == ZSFILE) /* Session log is unbuffered */
2001 return(write(fileno(fp[n]),"\n",1));
2002 else if (fputs("\n",fp[n]) == EOF)
2004 if (n == ZDIFIL || n == ZWFILE) /* Flush connection log records */
2009 /* Z S O U T X -- Write x characters to file, unbuffered. */
2012 zsoutx(n,s,x) int n, x; char *s; {
2014 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2016 return(ttol(s,x)); /* See comments in zsout() */
2019 #endif /* COMMENT */
2024 if (chkfn(n) < 1) return(-1);
2025 return(write(fp[n]->_file,s,x));
2026 #endif /* COMMENT */
2027 return(write(fileno(fp[n]),s,x) == x ? x : -1);
2030 /* Z C H O U T -- Add a character to the given file. */
2032 /* Should return 0 or greater on success, -1 on failure (e.g. disk full) */
2036 zchout(register int n, char c)
2038 zchout(n,c) register int n; char c;
2039 #endif /* CK_ANSIC */
2041 /* if (chkfn(n) < 1) return(-1); */
2044 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2048 return(0); /* See comments in zsout() */
2049 #endif /* COMMENT */
2053 if (n == ZSFILE) /* Use unbuffered for session log */
2054 return(write(fileno(fp[n]),&c,1) == 1 ? 0 : -1);
2055 /* Buffered for everything else */
2056 if (putc(c,fp[n]) == EOF) /* If true, maybe there was an error */
2057 return(ferror(fp[n])?-1:0); /* Check to make sure */
2058 else /* Otherwise... */
2059 return(0); /* There was no error. */
2062 /* (PWP) buffered character output routine to speed up file IO */
2068 zoutptr = zoutbuffer; /* Reset buffer pointer in all cases */
2071 debug(F101,"zoutdump zoutcnt","",zoutcnt);
2073 if (zoutcnt == 0) { /* Nothing to output */
2075 } else if (zoutcnt < 0) { /* Unexpected negative argument */
2076 zoutcnt = 0; /* Reset output buffer count */
2077 return(-1); /* and fail. */
2081 if (inserver && !local && fp[ZOFILE] == stdout) {
2083 x = ttol(zoutbuffer,zoutcnt);
2085 x = 1; /* See comments in zsout() */
2086 #endif /* COMMENT */
2088 return(x > 0 ? 0 : -1);
2093 Frank Prindle suggested that replacing this fwrite() by an fflush()
2094 followed by a write() would improve the efficiency, especially when
2095 writing to stdout. Subsequent tests showed a 5-fold improvement.
2098 if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) ...
2099 #endif /* COMMENT */
2103 #endif /* CK_NONBLOCK */
2105 while (zoutcnt > 0) {
2106 if ((x = write(fileno(fp[ZOFILE]),zp,zoutcnt)) > -1) {
2108 if (deblog) /* Save a function call... */
2109 debug(F101,"zoutdump wrote","",x);
2111 zoutcnt -= x; /* Adjust output buffer count */
2112 zp += x; /* and pointer */
2116 debug(F101,"zoutdump write error","",errno);
2117 debug(F101,"zoutdump write returns","",x);
2120 zoutcnt = 0; /* Reset output buffer count */
2121 return(-1); /* write() failed */
2127 /* C H K F N -- Internal function to verify file number is ok */
2131 -1: File number n is out of range
2132 0: n is in range, but file is not open
2133 1: n in range and file is open
2137 /* if (n != ZDFILE) debug(F101,"chkfn","",n); */
2138 if (n < 0 || n >= ZNFILS) {
2139 if (n != ZDFILE) debug(F101,"chkfn out of range","",n);
2142 /* if (n != ZDFILE) debug(F101,"chkfn fp[n]","",fp[n]); */
2143 return((fp[n] == NULL) ? 0 : 1);
2147 /* Z G E T F S -- Return file size regardless of accessibility */
2149 Used for directory listings, etc.
2151 The size of the file in bytes, 0 or greater, if the size can be learned.
2152 -1 if the file size can not be obtained.
2153 Also (and this is a hack just for UNIX):
2154 If the argument is the name of a symbolic link,
2155 the global variable issymlink is set to 1,
2156 and the global buffer linkname[] gets the link value.
2157 And it sets zgfs_dir to 1 if it's a directory, otherwise 0.
2158 This lets us avoid numerous redundant calls to stat().
2162 time_t zgfs_mtime = 0;
2163 unsigned int zgfs_mode = 0;
2166 char linkname[CKMAXPATH+1];
2168 #define _IFLNK 0120000
2170 #endif /* CKSYMLINK */
2173 zgetfs(name) char *name; {
2175 char fnam[CKMAXPATH+4];
2181 if (!name) name = "";
2182 if (!*name) return(-1);
2186 if (x == 9 && !strcmp(name,"/dev/null"))
2193 s = tilde_expand(s);
2198 x = ckstrncpy(fnam,s,CKMAXPATH);
2200 debug(F111,"zgetfs fnam",s,x);
2201 if (x > 0 && s[x-1] == '/')
2204 zgfs_dir = 0; /* Assume it's not a directory */
2205 zgfs_link = 0; /* Assume it's not a symlink */
2206 zgfs_mtime = 0; /* No time yet */
2207 zgfs_mode = 0; /* No permission bits yet */
2209 #ifdef CKSYMLINK /* We're doing symlinks? */
2210 #ifdef USE_LSTAT /* OK to use lstat()? */
2212 debug(F101,"STAT","",1);
2213 if (x < 0) /* stat() failed */
2215 if ( /* Now see if it's a symlink */
2217 S_ISLNK(buf.st_mode)
2220 ((_IFMT & buf.st_mode) == _IFLNK)
2222 #endif /* S_ISLNK */
2224 zgfs_link = 1; /* It's a symlink */
2225 linkname[0] = '\0'; /* Get the name */
2226 x = readlink(s,linkname,CKMAXPATH);
2227 debug(F101,"zgetfs readlink",s,x);
2228 if (x > -1 && x < CKMAXPATH) { /* It's a link */
2230 size = buf.st_size; /* Remember size of link */
2231 x = stat(s,&buf); /* Now stat the linked-to file */
2232 debug(F101,"STAT","",2);
2233 if (x < 0) /* so we can see if it's a directory */
2236 ckstrncpy(linkname,"(lookup failed)",CKMAXPATH);
2239 #else /* !USE_LSTAT */
2240 x = stat(s,&buf); /* No lstat(), use stat() instead */
2241 debug(F101,"STAT","",3);
2244 #endif /* USE_LSTAT */
2246 /* Do we need to call readlink()? */
2250 lstat() does not work in SCO operating systems. From "man NS lstat":
2252 lstat obtains information about the file named by path. In the case of a
2253 symbolic link, lstat returns information about the link, and not the file
2254 named by the link. It is only used by the NFS automount daemon and should
2255 not be utilized by users.
2258 debug(F101,"zgetfs forced needrlink","",needrlink);
2261 needrlink = S_ISLNK(buf.st_mode);
2262 debug(F101,"zgetfs S_ISLNK needrlink","",needrlink);
2265 needrlink = (_IFMT & buf.st_mode) == _IFLNK;
2266 debug(F101,"zgetfs _IFLNK needrlink","",needrlink);
2269 debug(F101,"zgetfs default needrlink","",needrlink);
2271 #endif /* S_ISLNK */
2272 #endif /* NOLINKBITS */
2277 x = readlink(s,linkname,CKMAXPATH);
2279 debug(F111,"zgetfs readlink",s,x);
2281 debug(F101,"zgetfs readlink errno","",errno);
2283 debug(F110,"zgetfs readlink result",linkname,0);
2285 if (x > -1 && x < CKMAXPATH) {
2290 #else /* !CKSYMLINK */
2291 x = stat(s,&buf); /* Just stat the file */
2292 debug(F111,"zgetfs stat",s,x);
2293 if (x < 0) /* and get the size */
2295 #endif /* CKSYMLINK */
2297 zgfs_mtime = buf.st_mtime;
2298 zgfs_mode = buf.st_mode;
2299 zgfs_dir = (S_ISDIR(buf.st_mode)) ? 1 : 0; /* Set "is directory" flag */
2300 debug(F111,"zgetfs size",s,size);
2301 debug(F111,"zgetfs st_size",s,buf.st_size);
2302 return((size < 0L) ? buf.st_size : size); /* Return the size */
2306 /* Z C H K I -- Check if input file exists and is readable */
2310 >= 0 if the file can be read (returns the size).
2311 -1 if file doesn't exist or can't be accessed,
2312 -2 if file exists but is not readable (e.g. a directory file).
2313 -3 if file exists but protected against read access.
2315 For Berkeley Unix, a file must be of type "regular" to be readable.
2316 Directory files, special files, and symbolic links are not readable.
2319 zchki(name) char *name; {
2323 extern int zchkid, diractive, matchfifo;
2333 if (x == 9 && !strcmp(s,"/dev/null"))
2335 if (x == 8 && !strcmp(s,"/dev/tty"))
2341 s = tilde_expand(s);
2348 debug(F111,"zchki setroot",ckroot,ckrootset);
2349 if (ckrootset) if (!zinroot(name)) {
2350 debug(F110,"zchki setroot violation",name,0);
2356 debug(F101,"STAT","",5);
2358 debug(F111,"zchki stat fails",s,errno);
2361 if (S_ISDIR (buf.st_mode))
2364 if (!(itsadir && zchkid)) { /* Unless this... */
2365 if (!S_ISREG (buf.st_mode) /* Must be regular file */
2367 && (!matchfifo || !S_ISFIFO (buf.st_mode)) /* or FIFO */
2368 #endif /* S_ISFIFO */
2370 debug(F111,"zchki not regular file (or fifo)",s,matchfifo);
2374 debug(F111,"zchki stat ok:",s,x);
2376 if (diractive) { /* If listing don't check access */
2380 debug(F100,"zchki swapping ids for access()","",0);
2382 #endif /* SW_ACC_ID */
2383 if ((x = access(s,R_OK)) < 0)
2384 x = access(s,X_OK); /* For RUN-class commands */
2387 debug(F100,"zchki swapped ids restored","",0);
2388 #endif /* SW_ACC_ID */
2390 if (x < 0) { /* Is the file accessible? */
2391 debug(F111,"zchki access failed:",s,x); /* No */
2394 iflen = buf.st_size; /* Yes, remember size */
2395 ckstrncpy(nambuf,s,CKMAXPATH); /* and name globally. */
2396 debug(F111,"zchki access ok:",s,iflen);
2397 return((iflen > -1L) ? iflen : 0L);
2401 /* Z C H K O -- Check if output file can be created */
2404 Returns -1 if write permission for the file would be denied, 0 otherwise.
2406 NOTE: The design is flawed. There is no distinction among:
2407 . Can I overwrite an existing file?
2408 . Can I create a file (or directory) in an existing directory?
2409 . Can I create a file (or directory) and its parent(s)?
2412 zchko(name) char *name; {
2413 int i, x, itsadir = 0;
2416 extern int zchkod; /* Used by IF WRITEABLE */
2418 debug(F110,"zchko entry",name,0);
2420 if (!name) return(-1); /* Watch out for null pointer. */
2425 debug(F111,"zchko setroot",ckroot,ckrootset);
2426 if (ckrootset) if (!zinroot(name)) {
2427 debug(F110,"zchko setroot violation",name,0);
2433 x = (int)strlen(name); /* Get length of filename */
2434 debug(F111,"zchko len",name,x);
2435 debug(F111,"zchko zchkod",name,zchkod);
2439 Writing to null device is OK.
2441 if (x == 9 && !strcmp(name,"/dev/null"))
2443 if (x == 8 && !strcmp(name,"/dev/tty"))
2450 s = tilde_expand(s);
2459 zchkod is a global flag meaning we're checking not to see if the directory
2460 file is writeable, but if it's OK to create files IN the directory.
2462 if (!zchkod && isdir(name)) /* Directories are not writeable */
2465 s = malloc(x+3); /* Must copy because we can't */
2466 if (!s) { /* write into our argument. */
2467 fprintf(stderr,"zchko: Malloc error 46\n");
2470 ckstrncpy(s,name,x+3);
2472 for (i = x; i > 0; i--) { /* Strip filename from right. */
2473 if (ISDIRSEP(s[i-1])) {
2478 debug(F101,"zchko i","",i);
2479 debug(F101,"zchko itsadir","",itsadir);
2482 /* X/OPEN XPG3-compliant systems fail if argument ends with "/"... */
2483 if (i == 0) /* If no path, use current directory */
2485 else /* Otherwise, use given one. */
2490 The following does not work for "foo/bar" where the foo directory does
2491 not exist even though we could create it: access("foo/.") fails, but
2492 access("foo") works OK.
2494 /* So now we use "path/." if path given, or "." if no path given. */
2495 s[i++] = '.'; /* Append "." to path. */
2498 /* So NOW we strip path segments from the right as long as they don't */
2499 /* exist -- we only call access() for path segments that *do* exist.. */
2500 /* (But this isn't quite right either since now zchko(/foo/bar/baz/xxx) */
2501 /* succeeds when I have write access to foo and bar but baz doesn't exit.) */
2503 if (itsadir && i > 0) {
2505 while (s[0] && !isdir(s)) {
2506 for (i = (int)strlen(s); i > 0; i--) {
2507 if (ISDIRSEP(s[i-1])) {
2516 s[i++] = '.'; /* Append "." to path. */
2519 #endif /* COMMENT */
2520 #endif /* COMMENT */
2523 ckstrncpy(s,".",x+3);
2526 debug(F100,"zchko swapping ids for access()","",0);
2528 #endif /* SW_ACC_ID */
2530 x = access(s,W_OK); /* Check access of path. */
2534 debug(F100,"zchko swapped ids restored","",0);
2535 #endif /* SW_ACC_ID */
2538 debug(F111,"zchko access failed:",s,errno);
2540 debug(F111,"zchko access ok:",s,x);
2541 free(s); /* Free temporary storage */
2543 return((x < 0) ? -1 : 0); /* and return. */
2546 /* Z D E L E T -- Delete the named file. */
2548 /* Returns: -1 on error, 0 on success */
2551 zdelet(name) char *name; {
2556 #endif /* CK_LOGIN */
2559 debug(F111,"zdelet setroot",ckroot,ckrootset);
2560 if (ckrootset) if (!zinroot(name)) {
2561 debug(F110,"zdelet setroot violation",name,0);
2567 debug(F111,"zdelet",name,x);
2569 if (ckxsyslog >= SYSLG_FC && ckxlogging) {
2571 zfnqfp(name,CKMAXPATH,fullname);
2572 debug(F110,"zdelet fullname",fullname,0);
2574 syslog(LOG_INFO, "file[] %s: delete failed (%m)", fullname);
2576 syslog(LOG_INFO, "file[] %s: delete ok", fullname);
2578 #endif /* CKSYSLOG */
2582 /* Z R T O L -- Convert remote filename into local form */
2585 zrtol(name,name2) char *name, *name2; {
2586 nzrtol(name,name2,1,0,CKMAXPATH);
2590 nzrtol(name,name2,fncnv,fnrpath,max)
2591 char *name, *name2; int fncnv, fnrpath, max;
2594 int flag = 0, n = 0;
2595 char fullname[CKMAXPATH+1];
2599 if (!name) name = "";
2601 debug(F111,"nzrtol name",name,fncnv);
2606 s = tilde_expand(s);
2612 /* Handle the path -- we don't have to convert its format, since */
2613 /* the standard path format and our (UNIX) format are the same. */
2616 devnull = !strcmp(name,"/dev/null");
2618 if (!devnull && fnrpath == PATH_OFF) { /* RECEIVE PATHNAMES OFF */
2620 strncpy(fullname,p,CKMAXPATH);
2621 } else if (!devnull && fnrpath == PATH_ABS) { /* REC PATHNAMES ABSOLUTE */
2622 strncpy(fullname,name,CKMAXPATH);
2623 } else if (!devnull && isabsolute(name)) { /* RECEIVE PATHNAMES RELATIVE */
2624 ckmakmsg(fullname,CKMAXPATH,".",name,NULL,NULL);
2625 } else { /* Ditto */
2626 ckstrncpy(fullname,name,CKMAXPATH);
2628 fullname[CKMAXPATH] = NUL;
2629 debug(F110,"nzrtol fullname",fullname,0);
2633 The maximum length for any segment of a filename is MAXNAMLEN, defined
2634 above. On some platforms (at least QNX) if a segment exceeds this limit,
2635 the open fails with ENAMETOOLONG, so we must prevent it by truncating each
2636 overlong name segment to the maximum segment length before passing the
2637 name to open(). This must be done even when file names are literal, so as
2638 not to halt a file transfer unnecessarily.
2641 char buf[CKMAXPATH+1]; /* New temporary buffer on stack */
2642 char *p = fullname; /* Source and */
2643 char *s = buf; /* destination pointers */
2645 debug(F101,"nzrtol sizing MAXNAMLEN","",MAXNAMLEN);
2646 while (*p && n < CKMAXPATH) { /* Copy name to new buffer */
2647 if (++i > MAXNAMLEN) { /* If this segment too long */
2648 while (*p && *p != '/') /* skip past the rest... */
2650 i = 0; /* and reset counter. */
2651 } else if (*p == '/') { /* End of this segment. */
2652 i = 0; /* Reset counter. */
2654 *s++ = *p++; /* Copy this character. */
2658 ckstrncpy(fullname,buf,CKMAXPATH); /* Copy back to original buffer. */
2659 debug(F111,"nzrtol sizing",fullname,n);
2661 #endif /* NOTRUNCATE */
2663 if (!fncnv || devnull) { /* Not converting */
2664 ckstrncpy(name2,fullname,max); /* We're done. */
2667 name = fullname; /* Converting */
2670 for (; *name != '\0' && n < maxnam; name++) {
2671 if (*name > SP) flag = 1; /* Strip leading blanks and controls */
2672 if (flag == 0 && *name < '!')
2680 if (isupper(*name)) /* Check for mixed case */
2682 else if (islower(*name))
2688 *p-- = '\0'; /* Terminate */
2689 while (*p < '!' && p > name2) /* Strip trailing blanks & controls */
2692 if (*name2 == '\0') { /* Nothing left? */
2693 ckstrncpy(name2,"NONAME",max); /* do this... */
2694 } else if (acase == 1) { /* All uppercase? */
2695 p = name2; /* So convert all letters to lower */
2702 debug(F110,"nzrtol new name",name2,0);
2706 /* Z S T R I P -- Strip device & directory name from file specification */
2708 /* Strip pathname from filename "name", return pointer to result in name2 */
2710 static char work[CKMAXPATH+1];
2713 zstrip(name,name2) char *name, **name2; {
2717 debug(F110,"zstrip before",name,0);
2718 if (!name) { *name2 = ""; return; }
2721 /* Strip leading tilde */
2722 if (*name == '~') name++;
2723 debug(F110,"zstrip after tilde-stripping",name,0);
2725 for (cp = name; *cp; cp++) {
2726 if (ISDIRSEP(*cp)) {
2731 if (n++ >= CKMAXPATH)
2735 *pp = '\0'; /* Terminate the string */
2737 debug(F110,"zstrip after",*name2,0);
2740 /* Z L T O R -- Local TO Remote */
2743 zltor(name,name2) char *name, *name2; {
2744 nzltor(name,name2,1,0,CKMAXPATH);
2747 /* N Z L T O R -- New Local TO Remote */
2750 fncnv = 0 for no conversion, > 0 for regular conversion, < 0 for minimal.
2753 nzltor(name,name2,fncnv,fnspath,max)
2754 char *name, *name2; int fncnv, fnspath, max;
2759 #endif /* COMMENT */
2763 char fullname[CKMAXPATH+1];
2768 extern int fcharset, /* tcharset, */ language;
2770 _PROTOTYP ( CHAR (*sxo), (CHAR) ) = NULL; /* Translation functions */
2772 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR);
2774 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])();
2775 #endif /* CK_ANSIC */
2777 language = L_USASCII;
2779 /* Proper translation of filenames must be done elsewhere */
2780 n = tcharset ? tcharset : TC_USASCII;
2781 sxo = xls[n][fcharset];
2783 sxo = xls[TC_USASCII][fcharset];
2784 #endif /* COMMENT */
2785 #endif /* NOCSETS */
2787 debug(F110,"nzltor name",name,0);
2789 /* Handle pathname */
2792 if (fnspath == PATH_OFF) { /* PATHNAMES OFF */
2794 ckstrncpy(fullname,p,CKMAXPATH);
2795 } else { /* PATHNAMES RELATIVE or ABSOLUTE */
2798 if (!strncmp(p,"../",3))
2800 else if (!strncmp(p,"./",2))
2805 if (fnspath == PATH_ABS) { /* ABSOLUTE */
2806 zfnqfp(p,CKMAXPATH,fullname);
2807 } else { /* RELATIVE */
2808 ckstrncpy(fullname,p,CKMAXPATH);
2811 debug(F110,"nzltor fullname",fullname,0);
2813 if (!fncnv) { /* Not converting */
2814 ckstrncpy(name2,fullname,max); /* We're done. */
2817 #endif /* NOCSETS */
2820 name = fullname; /* Converting */
2824 int tilde = 0, bslash = 0;
2826 if ((namechars = getenv("NAMECHARS")) != NULL) {
2827 if (ckstrchr(namechars, '~' ) != NULL) tilde = '~';
2828 if (ckstrchr(namechars, '\\') != NULL) bslash = '\\';
2835 pp = work; /* Output buffer */
2836 for (cp = name, n = 0; *cp && n < max; cp++,n++) { /* Convert name chars */
2839 if (sxo) c = (*sxo)(c); /* Convert to ASCII */
2840 #endif /* NOCSETS */
2841 if (fncnv > 0 && islower(c)) /* Uppercase letters */
2842 *pp++ = toupper(c); /* Change tilde to hyphen */
2845 else if (fncnv > 0 && c == '#') /* Change number sign to 'X' */
2847 else if (c == '*' || c == '?') /* Change wildcard chars to 'X' */
2849 else if (c == ' ') /* Change space to underscore */
2851 else if (c < ' ') /* Change controls to 'X' */
2853 else if (fncnv > 0 && c == '.') { /* Change dot to underscore */
2854 dotp = pp; /* Remember where we last did this */
2862 *pp = NUL; /* Tie it off. */
2864 if (dotp) *dotp = '.'; /* Restore last dot (if any) */
2866 if (dotp > dirp) *dotp = '.'; /* Restore last dot in file name */
2867 #endif /* COMMENT */
2868 cp = name2; /* If nothing before dot, */
2869 if (*work == '.') *cp++ = 'X'; /* insert 'X' */
2870 ckstrncpy(cp,work,max);
2873 #endif /* NOCSETS */
2874 debug(F110,"nzltor name2",name2,0);
2878 /* Z C H D I R -- Change directory */
2881 dirnam = pointer to name of directory to change to,
2882 which may be "" or NULL to indicate user's home directory.
2888 zchdir(dirnam) char *dirnam; {
2891 _PROTOTYP (int slotdir,(char *,char *));
2894 extern struct mtab *mactab; /* Main macro table */
2895 extern int nmac; /* Number of macros */
2898 debug(F110,"zchdir",dirnam,0);
2899 if (!dirnam) dirnam = "";
2900 if (!*dirnam) /* If argument is null or empty, */
2901 dirnam = zhome(); /* use user's home directory. */
2903 debug(F110,"zchdir 2",dirnam,0);
2906 hd = tilde_expand(dirnam); /* Attempt to expand tilde */
2908 if (*hd == '\0') hd = dirnam; /* in directory name. */
2912 debug(F110,"zchdir 3",hd,0);
2915 debug(F111,"zchdir setroot",ckroot,ckrootset);
2916 if (ckrootset) if (!zinroot(hd)) {
2917 debug(F110,"zchdir setroot violation",hd,0);
2923 /* Just to save some space */
2924 return((chdir(hd) == 0) ? 1 : 0);
2926 if (chdir(hd) == 0) { /* Try to cd */
2929 if (inserver && ikdbopen)
2930 slotdir(isguest ? anonroot : "", zgtdir());
2931 #endif /* CK_LOGIN */
2935 if (nmac) { /* Any macros defined? */
2937 static int on_cd = 0;
2940 k = mlook(mactab,"on_cd",nmac); /* Look this up */
2941 if (k >= 0) { /* If found, */
2942 if (dodo(k,zgtdir(),0) > -1) /* set it up, */
2943 parser(1); /* and execute it */
2957 zchkpid(unsigned long xpid)
2959 zchkpid(xpid) unsigned long xpid;
2960 #endif /* CK_ANSIC */
2962 return((kill((PID_T)xpid,0) < 0) ? 0 : 1);
2966 /* Z H O M E -- Return pointer to user's home directory */
2968 static char * zhomdir = NULL;
2976 return((char *)ckroot);
2980 home = getenv("home");
2982 home = getenv("HOME");
2984 makestr(&zhomdir,home);
2985 return(home ? zhomdir : ".");
2988 /* Z G T D I R -- Returns a pointer to the current directory */
2991 The "preferred" interface for getting the current directory in modern UNIX
2992 is getcwd() [POSIX 1003.1 5.2.2]. However, on certain platforms (such as
2993 SunOS), it is implemented by forking a shell, feeding it the pwd command,
2994 and returning the result, which is not only inefficient but also can result
2995 in stray messages to the terminal. In such cases -- as well as when
2996 getcwd() is not available at all -- getwd() can be used instead by defining
2997 USE_GETWD. However, note that getwd() provides no buffer-length argument
2998 and therefore no safeguard against memory leaks.
3008 #endif /* USE_GETWD */
3011 #define CWDBL 80 /* Save every byte we can... */
3013 #define CWDBL CKMAXPATH
3015 static char cwdbuf[CWDBL+2];
3017 NOTE: The getcwd() prototypes are commented out on purpose. If you get
3018 compile-time warnings, search through your system's header files to see
3019 which one has the needed prototype, and #include it. Usually it is
3020 <unistd.h>. See the section for including <unistd.h> in ckcdeb.h and
3021 make any needed adjustments there (and report them).
3025 char * buf = cwdbuf;
3029 extern char *getwd();
3031 debug(F110,"zgtdir BSD4 getwd()",s,0);
3037 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3038 #endif /* DCLGETCWD */
3039 debug(F101,"zgtdir BSD44 CWDBL","",CWDBL);
3040 s = getcwd(buf,CWDBL);
3046 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3047 #endif /* DCLGETCWD */
3048 debug(F101,"zgtdir MINIX2 CWDBL","",CWDBL);
3049 s = getcwd(buf,CWDBL);
3055 /* This non-ANSI prototype can be fatal at runtime! (e.g. in SCO3.2v5.0.5). */
3056 /* Anyway it's already prototyped in some header file that we have included. */
3057 extern char *getcwd();
3060 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3061 #endif /* DCLGETCWD */
3062 #endif /* COMMENT */
3063 debug(F101,"zgtdir SVORPOSIX CWDBL","",CWDBL);
3064 s = getcwd(buf,CWDBL);
3071 extern char *getcwd();
3072 #endif /* DCLGETCWD */
3073 debug(F101,"zgtdir COHERENT _I386 CWDBL","",CWDBL);
3074 s = getcwd(buf,CWDBL);
3078 extern char *getwd();
3079 debug(F101,"zgtdir COHERENT CWDBL","",CWDBL);
3086 debug(F101,"zgtdir SUNOS CWDBL","",CWDBL);
3087 s = getcwd(buf,CWDBL);
3093 #endif /* COHERENT */
3094 #endif /* SYSVORPOSIX */
3097 #endif /* USE_GETWD */
3100 /* Z X C M D -- Run a system command so its output can be read like a file */
3104 zxcmd(filnum,comand) int filnum; char *comand; {
3107 extern int kactive; /* From ckcpro.w and ckcmai.c */
3110 debug(F100,"zxcmd fails: nopush","",0);
3113 debug(F111,"zxcmd",comand,filnum);
3114 if (chkfn(filnum) < 0) return(-1); /* Need a valid Kermit file number. */
3115 if (filnum == ZSTDIO || filnum == ZCTERM) /* But not one of these. */
3118 out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
3119 debug(F101,"zxcmd out",comand,out);
3121 /* Output to a command */
3123 if (out) { /* Need popen() to do this. */
3124 ckstrncpy(fullname,"(pipe)",CKMAXPATH);
3126 return(0); /* no popen(), fail. */
3128 /* Use popen() to run the command. */
3130 #ifdef _POSIX_SOURCE
3131 /* Strictly speaking, popen() is not available in POSIX.1 */
3133 #endif /* _POSIX_SOURCE */
3135 debug(F110,"zxcmd out",comand,0);
3138 debug(F100,"zxcmd priv_chk failed","",0);
3142 fp[filnum] = popen(comand,"w");
3143 debug(F111,"zxcmd popen",fp[filnum] ? "OK" : "Failed", errno);
3144 if (fp[filnum] == NULL)
3147 /* I wonder what this is all about... */
3148 close(pipes[0]); /* Don't need the input side */
3149 fp[filnum] = fdopen(pipes[1],"w"); /* Open output stream. */
3150 fp[ZSYSFN] = fp[filnum]; /* Remember. */
3151 #endif /* COMMENT */
3153 zoutcnt = 0; /* (PWP) reset input buffer */
3154 zoutptr = zoutbuffer;
3156 #endif /* NOPOPEN */
3159 /* Input from a command */
3162 /* SINIX-L 5.41 does not like fdopen() */
3165 if (pipe(pipes) != 0) {
3166 debug(F100,"zxcmd pipe failure","",0);
3167 return(0); /* can't make pipe, fail */
3170 /* Create a fork in which to run the named process */
3174 pid = vfork() /* child */
3176 pid = fork() /* child */
3180 /* We're in the fork. */
3182 char *shpath, *shname, *shptr; /* Find user's preferred shell */
3186 #ifdef HPUX10 /* Default shell */
3187 defshell = "/usr/bin/sh";
3190 defshell = "/bin/rc";
3192 defshell = "/bin/sh";
3196 if (priv_can()) exit(1); /* Turn off any privileges! */
3197 debug(F101,"zxcmd pid","",pid);
3198 close(pipes[0]); /* close input side of pipe */
3199 close(0); /* close stdin */
3200 if (open("/dev/null",0) < 0) return(0); /* replace input by null */
3203 dup2(pipes[1],1); /* BSD: replace stdout & stderr */
3204 dup2(pipes[1],2); /* by the pipe */
3206 close(1); /* AT&T: close stdout */
3207 if (dup(pipes[1]) != 1) /* Send stdout to the pipe */
3209 close(2); /* Send stderr to the pipe */
3210 if (dup(pipes[1]) != 2)
3212 #endif /* SVORPOSIX */
3217 close(pipes[1]); /* Don't need this any more. */
3220 if ((shpath = getenv("SERVERSHELL")) == NULL)
3223 shpath = getenv("SHELL"); /* What shell? */
3224 if (shpath == NULL) {
3225 p = getpwuid( real_uid() ); /* Get login data */
3226 debug(F111,"zxcmd shpath","getpwuid()",p);
3227 if (p == (struct passwd *)NULL || !*(p->pw_shell))
3229 else shpath = p->pw_shell;
3232 shptr = shname = shpath;
3233 while (*shptr != '\0')
3234 if (*shptr++ == '/')
3236 debug(F110,shpath,shname,0);
3237 restorsigs(); /* Restore ignored signals */
3238 execl(shpath,shname,"-c",comand,(char *)NULL); /* Execute the cmd */
3239 exit(0); /* just punt if it failed. */
3240 } else if (pid == (PID_T) -1) {
3241 debug(F100,"zxcmd fork failure","",0);
3244 debug(F101,"zxcmd pid","",pid);
3245 close(pipes[1]); /* Don't need the output side */
3246 ispipe[filnum] = 1; /* Remember it's a pipe */
3247 fp[filnum] = fdopen(pipes[0],"r"); /* Open a stream for input. */
3251 if (filnum == ZIFILE && kactive) { /* Make pipe reads nonblocking */
3253 if ((flags = fcntl(fileno(fp[filnum]),F_GETFL,0)) > -1) {
3254 debug(F101,"zxcmd fcntl 1 pipe flags","",flags);
3255 x = fcntl(fileno(fp[filnum]),F_SETFL, flags |
3262 debug(F101,"zxcmd fcntl 2 result","",x);
3266 #endif /* DONDELAY */
3268 fp[ZSYSFN] = fp[filnum]; /* Remember. */
3269 zincnt = 0; /* (PWP) reset input buffer */
3275 /* Z C L O S F - wait for the child fork to terminate and close the pipe. */
3277 /* Used internally by zclose - returns -1 on failure, 1 on success. */
3280 zclosf(filnum) int filnum; {
3284 debug(F101,"zclosf filnum","",filnum);
3285 out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
3286 debug(F101,"zclosf out","",out);
3290 /* In UNIX we use popen() only for output files */
3294 x = pclose(fp[filnum]);
3296 debug(F101,"zclosf pclose","",x);
3297 debug(F101,"zclosf pexitstat","",pexitstat);
3298 fp[filnum] = fp[ZSYSFN] = NULL;
3300 return((x != 0) ? -1 : 1);
3302 #endif /* NOPOPEN */
3303 debug(F101,"zclosf fp[filnum]","", fp[filnum]);
3304 debug(F101,"zclosf fp[ZSYSFN]","", fp[ZSYSFN]);
3306 if (pid != (PID_T) 0) {
3307 debug(F101,"zclosf killing pid","",pid);
3316 This is the original code (before 20 April 1997) and has proven totally
3317 portable. But it does not give us the process's return code.
3319 while ((wstat = wait((WAIT_T *)0)) != pid && wstat != -1) ;
3321 /* Here we try to get the return code. Let's hope this is portable too. */
3322 while ((wstat = wait(&statusp)) != pid && wstat != -1) ;
3323 pexitstat = (statusp & 0xff) ? statusp : statusp >> 8;
3324 debug(F101,"zclosf wait statusp","",statusp);
3325 debug(F101,"zclosf wait pexitstat","",pexitstat);
3326 #endif /* CK_CHILD */
3330 fp[filnum] = fp[ZSYSFN] = NULL;
3333 debug(F101,"zclosf fp[filnum]","",fp[filnum]);
3335 return(pexitstat == 0 ? 1 : -1);
3338 #endif /* CK_CHILD */
3344 zxcmd(filnum,comand) int filnum; char *comand; {
3348 zclosf(filnum) int filnum; {
3354 /* Z X P A N D -- Expand a wildcard string into an array of strings */
3356 As of C-Kermit 7.0, this API is obsolete, replaced by nzxpand(), and this
3357 function is only used internally. See nzxpand() below.
3359 Returns the number of files that match fnarg, with data structures set up
3360 so that first file (if any) will be returned by the next znext() call.
3362 Depends on external variable wildxpand: 0 means we expand wildcards
3363 internally, nonzero means we call the shell to do it.
3365 static int xdironly = 0;
3366 static int xfilonly = 0;
3367 static int xmatchdot = 0;
3368 static int xrecursive = 0;
3369 static int xnobackup = 0;
3370 static int xnolinks = 0;
3372 static char *freeptr = NULL, **resptr = NULL; /* Copies of caller's args */
3373 static int remlen; /* Remaining space in caller's array */
3374 static int numfnd = 0; /* Number of matches found */
3376 #define MINSPACE 1024
3379 initspace(resarry,len) char * resarry[]; int len; {
3381 if (len < MINSPACE) len = MINSPACE;
3382 if (!sspace) { /* Need to allocate string space? */
3383 while (len >= MINSPACE) {
3384 if ((sspace = malloc(len+2))) { /* Got it. */
3385 debug(F101,"fgen string space","",len);
3388 len = (len / 2) + (len / 4); /* Didn't, reduce by 3/4 */
3390 if (len <= MINSPACE) { /* Did we get it? */
3391 fprintf(stderr,"fgen can't malloc string space\n");
3396 #endif /* DYNAMIC */
3398 freeptr = sspace; /* This is where matches are copied. */
3399 resptr = resarry; /* Static copies of these so */
3400 remlen = len; /* recursive calls can alter them. */
3401 debug(F101,"initspace ssplen","",ssplen);
3406 Z S E T F I L -- Query or change the size of file list buffers.
3408 fc = 1: Change current string space to n, return new size.
3409 fc = 2: Return current string space size.
3410 fc = 3: Change current maxnames to n, return new maxnames.
3411 fc = 4: Return current maxnames.
3412 Returns < 0 on error.
3415 zsetfil(n, fc) int n, fc; {
3418 case 1: /* Stringspace */
3423 if (initspace(mtchs,n) < 0)
3425 case 2: /* Fall thru deliberately */
3427 case 3: /* Listsize */
3429 free((char *)mtchs);
3432 mtchs = (char **)malloc(n * sizeof(char *));
3436 case 4: /* Fall thru deliberately */
3439 #endif /* DYNAMIC */
3449 #endif /* NONZXPAND */
3451 zxpand(fnarg) char *fnarg; {
3452 extern int diractive;
3453 char fnbuf[CKMAXPATH+8], * fn, * p;
3455 #ifdef DTILDE /* Built with tilde-expansion? */
3461 if (!fnarg) { /* If no argument provided */
3462 nxpand = fcount = 0;
3463 return(0); /* Return zero files found */
3465 debug(F110,"zxpand entry",fnarg,0);
3466 debug(F101,"zxpand xdironly","",xdironly);
3467 debug(F101,"zxpand xfilonly","",xfilonly);
3469 if (!*fnarg) { /* If no argument provided */
3470 nxpand = fcount = 0;
3471 return(0); /* Return zero files found */
3475 debug(F111,"zxpand setroot",ckroot,ckrootset);
3476 if (ckrootset) if (!zinroot(fnarg)) {
3477 debug(F110,"zxpand setroot violation",fnarg,0);
3478 nxpand = fcount = 0;
3485 This would have been perfect, except it makes us return fully qualified
3486 pathnames for all files.
3488 zfnqfp(fnarg,CKMAXPATH,fnbuf);
3489 debug(F110,"zxpand zfnqfp",fnbuf,0);
3491 debug(F110,"zxpand zgtdir",s,0);
3493 while (*p && *s) /* Make it relative */
3496 fn = (*s) ? fnbuf : p;
3497 debug(F110,"zxpand fn 0",fn,0);
3503 debug(F110,"zxpand fn 0.5",fn,0);
3505 #ifdef DTILDE /* Built with tilde-expansion? */
3506 if (*fnarg == '~') { /* Starts with tilde? */
3507 tnam = tilde_expand(fnarg); /* Try to expand it. */
3508 ckstrncpy(fnbuf,tnam,CKMAXPATH);
3511 ckstrncpy(fnbuf,fnarg,CKMAXPATH);
3512 fn = fnbuf; /* Point to what we'll work with */
3513 #endif /* COMMENT */
3514 debug(F110,"zxpand fn 1",fn,0);
3516 if (!*fn) /* But make sure something is there */
3519 p = fn + (int)strlen(fn) - 1;
3520 if (*p == '/') { /* If last char = / it must be a dir */
3521 if (!xfilonly && !iswild(p)) haveonedir++;
3522 ckstrncat(fn, "*", CKMAXPATH+8); /* so append '*' */
3523 } else if (p > fn) { /* If ends in "/." */
3524 if (*(p-1) == '/' && *p == '.') /* change '.' to '*' */
3526 } else if (p == fn) { /* If it's '.' alone */
3527 if (*p == '.') /* change '.' to '*' */
3530 debug(F110,"zxpand fn 2",fn,0);
3531 x = isdir(fn); /* Is it a directory? */
3532 debug(F111,"zxpand isdir 1",fn,x);
3533 if (x) { /* If so, make it into a wildcard */
3534 if (!xfilonly && !iswild(p))
3536 if ((x = strlen(fn)) > 0) {
3537 if (!ISDIRSEP(fn[x-1]))
3543 debug(F111,"zxpand fn 3",fn,haveonedir);
3545 The following allows us to parse a single directory name without opening
3546 the directory and looking at its contents. The diractive flag is a horrible
3547 hack (especially since DIR /NORECURSIVE turns it off), but otherwise we'd
3548 have to change the API.
3550 if (!diractive && haveonedir) {
3552 fcount = (mtchs == NULL &&
3553 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
3558 mtchs = (char **)malloc(maxnames * sizeof(*mtchs));
3562 return(nxpand = fcount);
3564 #endif /* COMMENT */
3565 debug(F110,"zxpand haveonedir A1",fnarg,0);
3566 initspace(mtchs,ssplen);
3568 if (numfnd < 0) return(-1);
3569 mtchptr = mtchs; /* Save pointer for next. */
3570 debug(F110,"zxpand haveonedir A2",*mtchptr,0);
3571 return(nxpand = fcount);
3575 if (!nopush && wildxpand) /* Who is expanding wildcards? */
3576 fcount = (mtchs == NULL && /* Shell */
3577 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
3579 : shxpand(fn,mtchs,maxnames);
3582 fcount = (mtchs == NULL && /* Kermit */
3583 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
3585 : fgen(fn,mtchs,maxnames); /* Look up the file. */
3587 if (fcount == 0 && haveonedir) {
3589 debug(F110,"zxpand haveonedir B",fnarg,0);
3591 if (numfnd < 0) return(-1);
3593 mtchptr = mtchs; /* Save pointer for next. */
3599 debug(F111,"zxpand ok",mtchs[0],fcount);
3601 debug(F101,"zxpand fcount","",fcount);
3608 /* N Z X P A N D -- Expand a file list, with options. */
3611 s = pointer to filename or pattern.
3612 flags = option bits:
3614 flags & ZX_FILONLY Match regular files
3615 flags & ZX_DIRONLY Match directories
3616 flags & ZX_RECURSE Descend through directory tree
3617 flags & ZX_MATCHDOT Match "dot files"
3618 flags & ZX_NOBACKUP Don't match "backup files"
3619 flags & ZX_NOLINKS Don't follow symlinks.
3621 Returns the number of files that match s, with data structures set up
3622 so that first file (if any) will be returned by the next znext() call.
3625 nzxpand(s,flags) char * s; int flags; {
3629 debug(F111,"nzxpand",s,flags);
3630 x = flags & (ZX_DIRONLY|ZX_FILONLY);
3631 xdironly = (x == ZX_DIRONLY);
3632 xfilonly = (x == ZX_FILONLY);
3633 if (xdironly && xfilonly) {
3637 xmatchdot = (flags & ZX_MATCHDOT);
3638 debug(F111,"nzxpand xmatchdot 1",s,xmatchdot);
3639 /* If xmatchdot not set by caller but pattern implies it, set it anyway */
3640 if (!xmatchdot && ((p = ckstrchr(s,'.')))) {
3641 if (p == s && p[1] != '/') {
3643 debug(F111,"nzxpand xmatchdot 2",s,xmatchdot);
3645 xmatchdot = (*(p-1) == ',') || (*(p-1) == '{') || (*(p-1) == '/');
3646 debug(F111,"nzxpand xmatchdot 3",s,xmatchdot);
3649 xrecursive = (flags & ZX_RECURSE);
3650 xnobackup = (flags & ZX_NOBACKUP);
3651 xnolinks = (flags & ZX_NOLINKS);
3655 debug(F101,"nzxpand xdironly","",xdironly);
3656 debug(F101,"nzxpand xfilonly","",xfilonly);
3657 debug(F101,"nzxpand xmatchdot","",xmatchdot);
3658 debug(F101,"nzxpand xrecursive","",xrecursive);
3659 debug(F101,"nzxpand xnobackup","",xnobackup);
3660 debug(F101,"nzxpand xnolinks","",xnolinks);
3666 sh_sort(mtchs,NULL,x,0,0,1); /* Alphabetize the list */
3675 #endif /* NONZXPAND */
3678 /* Z X R E W I N D -- Rewinds the zxpand() list */
3682 /* if (!mtchs) return(-1); */
3687 #endif /* NOZXREWIND */
3689 /* Z N E X T -- Get name of next file from list created by zxpand(). */
3691 Returns >0 if there's another file, with its name copied into the arg string,
3692 or 0 if no more files in list.
3695 znext(fn) char *fn; {
3697 ckstrncpy(fn,*mtchptr++,CKMAXPATH);
3702 debug(F111,"znext",fn,fcount+1);
3705 debug(F111,"znext",fn,fcount); /* Return 0 if no filename to return */
3707 #endif /* COMMENT */
3710 /* Z C H K S P A -- Check if there is enough space to store the file */
3713 Call with file specification f, size n in bytes.
3714 Returns -1 on error, 0 if not enough space, 1 if enough space.
3719 zchkspa(char *f, long n)
3721 zchkspa(f,n) char *f; long n;
3722 #endif /* CK_ANSIC */
3724 /* In UNIX there is no good (and portable) way. */
3725 return(1); /* Always say OK. */
3728 #ifdef COMMENT /* (not used) */
3730 /* I S B A C K U P -- Tells if given file has a backup suffix */
3733 -1: Invalid argument
3734 0: File does not have a backup suffix
3735 >0: Backup suffix number
3738 isbackup(fn) char * fn; { /* Get backup suffix number */
3739 int i, j, k, x, state, flag;
3741 if (!fn) /* Watch out for null pointers. */
3743 if (!*fn) /* And empty names. */
3747 for (i = (int)strlen(fn) - 1; (!flag && (i > 0)); i--) {
3749 case 0: /* State 0 - final char */
3750 if (fn[i] == '~') /* Is tilde */
3751 state = 1; /* Switch to next state */
3752 else /* Otherwise */
3753 flag = 1; /* Quit - no backup suffix. */
3755 case 1: /* State 1 - digits */
3756 if (fn[i] == '~' && fn[i-1] == '.') { /* Have suffix */
3757 return(atoi(&fn[i+1]));
3758 } else if (fn[i] >= '0' && fn[i] <= '9') { /* In number part */
3759 continue; /* Keep going */
3760 } else { /* Something else */
3761 flag = 1; /* Not a backup suffix - quit. */
3768 #endif /* COMMENT */
3771 /* Z N E W N -- Make a new name for the given file */
3774 Given the name, fn, of a file that already exists, this function builds a
3775 new name of the form "<oldname>.~<n>~", where <oldname> is argument name
3776 (fn), and <n> is a version number, one higher than any existing version
3777 number for that file, up to 99999. This format is consistent with that used
3778 by GNU EMACS. If the constructed name is too long for the system's maximum,
3779 enough characters are truncated from the end of <fn> to allow the version
3780 number to fit. If no free version numbers exist between 1 and 99999, a
3781 version number of "xxxx" is used. Returns a pointer to the new name in
3785 #define ZNEWNBL 63 /* Name buffer length */
3786 #define ZNEWNMD 3 /* Max digits for version number */
3788 #define ZNEWNBL CKMAXPATH
3792 #define MAXBUDIGITS 5
3794 static char znewbuf[ZNEWNBL+12];
3797 znewn(fn,s) char *fn, **s; {
3798 char * buf; /* Pointer to buffer for new name */
3799 char * xp, * namepart = NULL; /* Pointer to filename part */
3800 struct zfnfp * fnfp; /* znfqfp() result struct pointer */
3801 int d = 0, t, fnlen, buflen;
3802 int n, i, k, flag, state;
3803 int max = MAXNAMLEN; /* Maximum name length */
3804 char * dname = NULL;
3807 *s = NULL; /* Initialize return value */
3808 if (!fn) fn = ""; /* Check filename argument */
3811 /* If incoming file already has a backup suffix, remove it. */
3812 /* Then we'll tack a new on later, which will be the highest for this file. */
3814 if (i <= max && i > 0 && fn[i-1] == '~') {
3817 debug(F111,"znewn suffix removal",fn,i);
3818 if ((dname = (char *)malloc(i+1))) {
3819 ckstrncpy(dname,fn,i+1);
3821 for (flag = state = 0; (!flag && (i > 0)); i--) {
3823 case 0: /* State 0 - final char */
3824 if (p[i] == '~') /* Is tilde */
3825 state = 1; /* Switch to next state */
3826 else /* Otherwise */
3827 flag = 1; /* Quit - no backup suffix. */
3829 case 1: /* State 1 - digits */
3830 if (p[i] == '~' && p[i-1] == '.') { /* Have suffix */
3831 p[i-1] = NUL; /* Trim it */
3833 debug(F111,"znewn suffix removal 2",fn,i);
3834 flag = 1; /* done */
3835 } else if (p[i] >= '0' && p[i] <= '9') { /* Number part */
3836 continue; /* Keep going */
3837 } else { /* Something else */
3838 flag = 1; /* Not a backup suffix - quit. */
3845 if ((fnlen = strlen(fn)) < 1) { /* Get length */
3846 if (dname) free(dname);
3849 debug(F111,"znewn",fn,fnlen);
3851 debug(F101,"znewn max 1","",max);
3852 if (max < 14) max = 14; /* Make max reasonable for any UNIX */
3853 if (max > ZNEWNBL) max = ZNEWNBL;
3854 debug(F101,"znewn max 2","",max);
3856 if ((fnfp = zfnqfp(fn, ZNEWNBL, buf))) { /* Get fully qualified name */
3857 namepart = fnfp->fname; /* Isolate the filename */
3858 k = strlen(fn); /* Length of name part */
3859 debug(F111,"znewn namepart",namepart,k);
3861 if (dname) free(dname);
3864 buflen = fnfp->len; /* Length of fully qualified name */
3865 debug(F111,"znewn len",buf,buflen);
3867 if (k + MAXBUDIGITS + 3 < max) { /* Backup name fits - no overflow */
3868 /* Make pattern for backup names */
3869 ckstrncpy(buf+buflen,".~*~",ZNEWNBL+12-buflen);
3870 n = nzxpand(buf,ZX_FILONLY); /* Expand the pattern */
3871 debug(F111,"znewn A matches",buf,n);
3872 while (n-- > 0) { /* Find any existing name.~n~ files */
3873 xp = *mtchptr++; /* Point at matching name */
3874 t = atoi(xp+buflen+2); /* Get number */
3875 if (t > d) d = t; /* Save d = highest version number */
3877 sprintf(buf+buflen,".~%d~",d+1); /* Yes, make "name.~<d+1>~" */
3878 debug(F110,"znewn A newname",buf,0);
3879 } else { /* Backup name would be too long */
3880 int xlen; /* So we have to eat back into it */
3882 char buf2[ZNEWNBL+12];
3885 debug(F101,"znewn B delta","",delta);
3887 for (i = MAXBUDIGITS; i > 0; i--) { /* In this case the format of */
3888 ckstrncpy(buf2,buf,ZNEWNBL+12); /* the backup name depends on */
3889 xlen = buflen - i - 3 + delta; /* how many digits are in the */
3890 ckstrncpy(buf2+xlen,".~*~",ZNEWNBL+12-xlen); /* backup number */
3891 n = nzxpand(buf2,ZX_FILONLY);
3892 debug(F111,"znewn B matches",buf2,n);
3896 while (n-- > 0) { /* Find any existing name.~n~ files */
3897 xp = *mtchptr++; /* Point at matching name */
3898 t = atoi(xp+xlen+2); /* Get number */
3899 if (t > d) d = t; /* Save d = highest version number */
3901 if (d > 0) /* If the odometer turned over... */
3902 if ((d % 10) == 9) /* back up one space. */
3904 sprintf(buf2+xlen,".~%d~",d+1); /* This just fits */
3905 ckstrncpy(buf,buf2,ZNEWNBL+12); /* (we could be more clever here...) */
3906 debug(F110,"znewn B new name",buf,0);
3908 *s = buf; /* Point to new name */
3909 ck_znewn = d+1; /* Also make it available globally */
3910 if (dname) free(dname);
3914 /* Z R E N A M E -- Rename a file */
3916 Call with old and new names.
3917 If new name is the name of a directory, the 'old' file is moved to
3919 Returns 0 on success, -1 on failure.
3922 zrename(old,new) char *old, *new; {
3928 debug(F110,"zrename old",old,0);
3929 debug(F110,"zrename new",new,0);
3930 if (!*old) return(-1);
3931 if (!*new) return(-1);
3935 if (inserver && isguest)
3937 #endif /* CK_LOGIN */
3941 debug(F111,"zrename setroot",ckroot,ckrootset);
3943 if (!zinroot(old)) {
3944 debug(F110,"zrename old: setroot violation",old,0);
3947 if (!zinroot(new)) {
3948 debug(F110,"zrename new: setroot violation",new,0);
3960 if (!(p = malloc(strlen(new) + strlen(old) + 2)))
3962 strcpy(p,new); /* (safe) Directory part */
3963 if (!ISDIRSEP(*(new+x-1))) /* Separator, if needed */
3964 strcat(p,"/"); /* (safe) */
3965 zstrip(old,&q); /* Strip path part from old name */
3966 strcat(p,q); /* cat to new directory (safe) */
3968 debug(F110,"zrename dir",s,0);
3971 else debug(F110,"zrename no dir",s,0);
3975 if (inserver && (!ENABLED(en_del))) {
3976 if (zchki(s) > -1) /* Destination file exists? */
3981 x = -1; /* Return code. */
3983 /* Atomic, preferred, uses a single system call, rename(), if available. */
3985 debug(F111,"zrename rename()",old,x);
3989 /* If rename() failed or not available try link()/unlink() */
3992 if (zchko(old) > -1) { /* Requires write access to orignal */
3994 debug(F111,"zrename link()",old,x);
3995 if (x > -1) { /* Make a link with the new name. */
3997 debug(F111,"zrename unlink()",old,x);
3999 /* If link/unlink failed copy and delete */
4002 debug(F111,"zrename zcopy()",old,x);
4005 debug(F111,"zrename zdelet()",old,x);
4010 fullname[0] = '\0'; /* Clear this out for next time. */
4013 if (ckxsyslog >= SYSLG_FC && ckxlogging) {
4014 zfnqfp(old,CKMAXPATH,fullname);
4016 zfnqfp(s,CKMAXPATH,tmp2);
4018 syslog(LOG_INFO,"file[] %s: renamed to %s ok", fullname, tmp2);
4020 syslog(LOG_INFO,"file[] %s: rename to %s failed (%m)",fullname,tmp2);
4022 #endif /* CKSYSLOG */
4028 /* Z C O P Y -- Copy a single file. */
4030 Call with source and destination names.
4031 If destination is a directory, the source file is
4032 copied to that directory with its original name.
4036 -2 = source file is not a regular file.
4037 -3 = source file not found.
4038 -4 = permission denied.
4039 -5 = source and destination are the same file.
4044 zcopy(source,destination) char *source, *destination; {
4045 char *src, *dst; /* Local pointers to filenames */
4046 int x, y, rc; /* Workers */
4047 int in = -1, out = -1; /* i/o file descriptors */
4048 struct stat srcbuf; /* Source file info buffer */
4049 int perms; /* Output file permissions */
4050 char buf[1024]; /* File copying buffer */
4052 if (!source) source = "";
4053 if (!destination) destination = "";
4055 debug(F110,"zcopy src arg",source,0);
4056 debug(F110,"zcopy dst arg",destination,0);
4058 if (!*source) return(-1);
4059 if (!*destination) return(-1);
4063 if (inserver && isguest)
4065 #endif /* CK_LOGIN */
4069 debug(F111,"zcopy setroot",ckroot,ckrootset);
4071 if (!zinroot(source)) {
4072 debug(F110,"zcopy source: setroot violation",source,0);
4075 if (!zinroot(destination)) {
4076 debug(F110,"zcopy destination: setroot violation",destination,0);
4085 if (stat(src,&srcbuf) == 0) { /* Get source file info */
4086 struct stat dstbuf; /* Destination file info buffer */
4087 debug(F101,"STAT","",6);
4088 if (stat(dst,&dstbuf) == 0) {
4089 debug(F101,"STAT","",7);
4090 if (srcbuf.st_dev == dstbuf.st_dev)
4091 if (srcbuf.st_ino == dstbuf.st_ino) {
4092 debug(F100,"zcopy files identical: stat()","",0);
4096 } else { /* stat() failed... */
4097 debug(F101,"STAT","",8);
4098 debug(F111,"source file not found",src,errno);
4101 fullname[0] = '\0'; /* Get full pathnames */
4102 if (zfnqfp(source,CKMAXPATH,fullname))
4104 debug(F110,"zcopy src",src,0);
4106 if (zfnqfp(destination,CKMAXPATH,tmp2))
4108 debug(F110,"zcopy dst 1",dst,0);
4109 if (!strcmp(src,dst)) { /* Src and dst are same file? */
4110 debug(F100,"zcopy files identical: strcmp()","",0); /* This... */
4111 return(-5); /* should not happen. */
4113 if (isdir(src)) { /* Source file is a directory? */
4114 debug(F110,"zcopy source is directory",src,0);
4115 return(-2); /* Fail */
4117 if (isdir(dst)) { /* Destination is a directory? */
4118 char *q = NULL; /* Yes, add filename to it. */
4120 if (x < 1) return(-1);
4121 if (!ISDIRSEP(*(dst+x-1))) { /* Add separator if needed */
4125 debug(F111,"zcopy dst 2",dst,x);
4126 zstrip(src,&q); /* Strip path part from old name */
4127 ckstrncpy(tmp2+x,q,CKMAXPATH-x); /* Concatenate it to new name */
4129 debug(F110,"zcopy dst 3",dst,0);
4132 if (inserver && (!ENABLED(en_del))) {
4133 if (zchki(dst) > -1) /* Destination file exists? */
4138 perms = umask(0); /* Get user's umask */
4139 umask(perms); /* Put it back! */
4140 perms ^= 0777; /* Flip the bits */
4141 perms &= 0666; /* Zero execute bits from umask */
4142 perms |= (srcbuf.st_mode & 0111); /* OR in source file's execute bits */
4143 rc = -1; /* Default return code */
4144 errno = 0; /* Reset errno */
4145 in = open(src, O_RDONLY, 0); /* Open source file */
4146 debug(F111,"zcopy open source",src,in);
4147 if (in > -1) { /* If open... */
4148 /* Open destination file */
4150 out = open(dst, O_WRONLY|O_CREAT|O_TRUNC, perms);
4152 out = open(dst, O_WRONLY|O_CREAT, perms);
4153 #endif /* O_TRUNC */
4154 debug(F111,"zcopy open dest",dst,out);
4155 if (out > -1) { /* If open... */
4156 while ((x = read(in,buf,1024)) > 0) { /* Copy in 1K blocks */
4157 y = write(out,buf,x);
4158 if (y < 0) { /* On write failure */
4160 rc = -6; /* Indicate i/o error */
4164 debug(F101,"zcopy final read","",x);
4165 debug(F101,"zcopy errno","",errno);
4166 rc = (x == 0) ? 0 : -6; /* In case of read failure */
4169 if (in > -1) close(in); /* Close files */
4170 if (out > -1) close(out);
4171 if (rc == -1) { /* Set return code */
4173 case ENOENT: rc = -3; break;
4174 case EACCES: rc = -4; break;
4180 if (rc > -1 && ckxsyslog >= SYSLG_FC && ckxlogging) {
4182 syslog(LOG_INFO,"file[] %s: copy to %s failed (%m)", fullname, tmp2);
4184 syslog(LOG_INFO,"file[] %s: copy to %s ok", fullname, tmp2);
4186 #endif /* CKSYSLOG */
4193 Fills in a Kermit file attribute structure for the file which is to be sent.
4194 Returns 0 on success with the structure filled in, or -1 on failure.
4195 If any string member is null, then it should be ignored.
4196 If any numeric member is -1, then it should be ignored.
4202 #endif /* CK_GPERMS */
4206 #define S_IRUSR 0400
4207 #endif /* S_IRUSR */
4209 #define S_IXUSR 0200
4210 #endif /* S_IWUSR */
4212 #define S_IXUSR 0100
4213 #endif /* S_IXUSR */
4220 #endif /* S_IXUSR */
4221 #endif /* S_IWUSR */
4222 #endif /* S_IRUSR */
4224 static char gperms[2];
4226 #endif /* CK_GPERMS */
4228 static char lperms[24];
4231 static char xlperms[24];
4233 /* Z S E T P E R M -- Set permissions of a file */
4236 zsetperm(f,code) char * f; int code; {
4242 #endif /* CK_SCO32V4 */
4244 if (inserver && guest) {
4245 debug(F110,"zsetperm guest",f,0);
4250 debug(F111,"zsetperm error",f,errno);
4253 debug(F111,"zsetperm ok",f,mask);
4257 /* Z G P E R M -- Get permissions of a file as an octal string */
4260 zgperm(f) char *f; {
4261 extern int diractive;
4262 int x; char *s = (char *)xlperms;
4264 debug(F110,"zgperm",f,0);
4265 if (!f) return("----------");
4266 if (!*f) return("----------");
4269 debug(F111,"zgperm setroot",ckroot,ckrootset);
4270 if (ckrootset) if (!zinroot(f)) {
4271 debug(F110,"zgperm setroot violation",f,0);
4272 return("----------");
4280 #endif /* USE_LSTAT */
4282 debug(F101,"STAT","",9);
4284 return("----------");
4285 sprintf(s,"%o",buf.st_mode);
4286 debug(F110,"zgperm",s,0);
4290 /* Like zgperm() but returns permissions in "ls -l" string format */
4292 static char xsperms[24];
4295 ziperm(f) char * f; {
4296 extern int diractive;
4297 int x; char *s = (char *)xsperms;
4299 unsigned int perms = 0;
4301 debug(F110,"ziperm",f,0);
4303 if (!f) return(NULL);
4304 if (!*f) return(NULL);
4306 if (diractive && zgfs_mode != 0) {
4307 perms = zgfs_mode; /* zgetfs() already got them */
4313 #endif /* USE_LSTAT */
4315 debug(F101,"STAT","",10);
4317 return("----------");
4318 perms = buf.st_mode;
4320 switch (perms & S_IFMT) {
4324 case S_IFCHR: /* Character special */
4327 case S_IFBLK: /* Block special */
4330 case S_IFREG: /* Regular */
4334 case S_IFLNK: /* Symbolic link */
4337 #endif /* S_IFLNK */
4339 case S_IFSOCK: /* Socket */
4342 #endif /* S_IFSOCK */
4346 case S_IFIFO: /* FIFO */
4349 #endif /* COHERENT */
4351 #endif /* S_IFIFO */
4353 case S_IFWHT: /* Whiteout */
4356 #endif /* S_IFWHT */
4357 default: /* Unknown */
4361 if (perms & S_IRUSR) /* Owner's permissions */
4365 if (perms & S_IWUSR)
4369 switch (perms & (S_IXUSR | S_ISUID)) {
4379 case S_IXUSR | S_ISUID:
4383 if (perms & S_IRGRP) /* Group permissions */
4387 if (perms & S_IWGRP)
4391 switch (perms & (S_IXGRP | S_ISGID)) {
4401 case S_IXGRP | S_ISGID:
4405 if (perms & S_IROTH) /* World permissions */
4409 if (perms & S_IWOTH)
4417 perms & (S_IXOTH | S_ISVTX)
4430 case S_IXOTH | S_ISVTX:
4436 debug(F110,"ziperm",xsperms,0);
4437 return((char *)xsperms);
4443 zgperm(f) char *f; {
4444 return("----------");
4447 ziperms(f) char *f; {
4448 return("----------");
4450 #endif /* CK_PERMS */
4453 zsattr(xx) struct zattr *xx; {
4457 k = iflen % 1024L; /* File length in K */
4458 if (k != 0L) k = 1L;
4459 xx->lengthk = (iflen / 1024L) + k;
4460 xx->type.len = 0; /* File type can't be filled in here */
4463 xx->date.val = zfcdat(nambuf); /* File creation date */
4464 xx->date.len = (int)strlen(xx->date.val);
4469 xx->creator.len = 0; /* File creator */
4470 xx->creator.val = "";
4471 xx->account.len = 0; /* File account */
4472 xx->account.val = "";
4473 xx->area.len = 0; /* File area */
4475 xx->password.len = 0; /* Area password */
4476 xx->password.val = "";
4477 xx->blksize = -1L; /* File blocksize */
4478 xx->xaccess.len = 0; /* File access */
4479 xx->xaccess.val = "";
4480 xx->encoding.len = 0; /* Transfer syntax */
4481 xx->encoding.val = 0;
4482 xx->disp.len = 0; /* Disposition upon arrival */
4484 xx->lprotect.len = 0; /* Local protection */
4485 xx->lprotect.val = "";
4486 xx->gprotect.len = 0; /* Generic protection */
4487 xx->gprotect.val = "";
4489 if (*nambuf) x = stat(nambuf,&buf);
4490 debug(F101,"STAT","",11);
4492 debug(F111,"zsattr buf.st_mode & 0777",nambuf,buf.st_mode & 0777);
4493 /* UNIX filemode as an octal string without filetype bits */
4494 sprintf(lperms,"%o",buf.st_mode & 0777);
4495 xx->lprotect.len = (int)strlen(lperms);
4496 xx->lprotect.val = (char *)lperms;
4499 /* Generic permissions only if we have stat.h symbols defined */
4500 if (buf.st_mode & S_IRUSR) x |= 1; /* Read */
4501 if (buf.st_mode & S_IWUSR) x |= (2+16); /* Write and Delete */
4502 if (buf.st_mode & S_IXUSR) x |= 4; /* Execute */
4503 gperms[0] = tochar(x);
4505 xx->gprotect.len = 1;
4506 xx->gprotect.val = (char *)gperms;
4507 #endif /* CK_GPERMS */
4509 debug(F111,"zsattr lperms",xx->lprotect.val,xx->lprotect.len);
4510 debug(F111,"zsattr gperms",xx->gprotect.val,xx->gprotect.len);
4511 xx->systemid.val = "U1"; /* U1 = UNIX */
4512 xx->systemid.len = 2; /* System ID */
4513 xx->recfm.len = 0; /* Record format */
4515 xx->sysparam.len = 0; /* System-dependent parameters */
4516 xx->sysparam.val = "";
4517 xx->length = iflen; /* Length */
4521 /* Z F C D A T -- Get file creation date */
4523 Call with pointer to filename.
4524 On success, returns pointer to modification date in yyyymmdd hh:mm:ss format.
4525 On failure, returns pointer to null string.
4527 static char datbuf[40];
4531 zdtstr(time_t timearg)
4533 zdtstr(timearg) time_t timearg;
4534 #endif /* CK_ANSIC */
4539 struct tm * time_stamp;
4540 struct tm * localtime();
4543 debug(F101,"zdtstr timearg","",timearg);
4546 time_stamp = localtime(&(timearg));
4548 debug(F100,"localtime returns null","",0);
4552 We assume that tm_year is ALWAYS years since 1900.
4553 Any platform where this is not the case will have problems
4556 yy = time_stamp->tm_year; /* Year - 1900 */
4557 debug(F101,"zdtstr tm_year","",time_stamp->tm_year);
4559 debug(F101,"zstrdt YEAR-2000 ALERT 1: localtime year","",yy);
4562 debug(F101,"zdatstr year","",yy);
4564 if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11)
4566 if (time_stamp->tm_mday < 0 || time_stamp->tm_mday > 31)
4568 if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 23)
4570 if (time_stamp->tm_min < 0 || time_stamp->tm_min > 59)
4572 ss = time_stamp->tm_sec; /* Seconds */
4573 if (ss < 0 || ss > 59) /* Some systems give a BIG number */
4577 /* For some reason, 2.1x BSD sprintf gets the last field wrong. */
4578 "%04d%02d%02d %02d:%02d:00",
4580 "%04d%02d%02d %02d:%02d:%02d",
4583 time_stamp->tm_mon + 1,
4584 time_stamp->tm_mday,
4585 time_stamp->tm_hour,
4591 yy = (int)strlen(datbuf);
4592 debug(F111,"zdatstr",datbuf,yy);
4593 if (yy > 17) datbuf[17] = '\0';
4595 #endif /* TIMESTAMP */
4599 zfcdat(name) char *name; {
4602 extern int diractive;
4614 debug(F111,"zfcdat setroot",ckroot,ckrootset);
4615 if (ckrootset) if (!zinroot(name)) {
4616 debug(F110,"zfcdat setroot violation",name,0);
4623 s = tilde_expand(s);
4631 debug(F111,"zfcdat",s,diractive);
4633 if (diractive && zgfs_mtime) {
4638 x = lstat(s,&buffer);
4639 debug(F101,"STAT","",12);
4640 debug(F101,"zfcdat lstat","",x);
4642 #endif /* USE_LSTAT */
4643 x = stat(s,&buffer);
4644 debug(F101,"STAT","",13);
4645 debug(F101,"zfcdat stat","",x);
4648 #endif /* USE_LSTAT */
4651 debug(F111,"zfcdat stat failed",s,errno);
4653 debug(F111,"zfcdat lstat failed",s,errno);
4654 #endif /* USE_LSTAT */
4657 debug(F101,"zfcdat buffer.st_mtime","",buffer.st_mtime);
4658 mtime = buffer.st_mtime;
4660 return(zdtstr(mtime));
4663 #endif /* TIMESTAMP */
4668 /* Z S T R D T -- Converts local date string to internal representation */
4670 In our case (UNIX) this is seconds since midnite 1 Jan 1970 UTC,
4671 suitable for comparison with UNIX file dates. As far as I know, there is
4672 no library or system call -- at least nothing reasonably portable -- to
4673 convert local time to UTC.
4676 zstrdt(date,len) char * date; int len; {
4679 SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime().
4680 ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in
4681 dependence on the XPG4 supplement presence. So always use
4682 what the system header file supplies in ODT 3.0...
4686 extern void ftime(); /* extern void ftime(struct timeb *) */
4687 #endif /* _SCO_DS */
4692 #endif /* M_XENIX */
4694 extern struct tm * localtime();
4696 /* And this should have been declared always through a header file */
4708 int i, n, isleapyear;
4709 /* J F M A M J J A S O N D */
4710 /* 31 28 31 30 31 30 31 31 30 31 30 31 */
4712 int monthdays [13] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 };
4714 struct tm *time_stamp;
4717 struct timeval tp[2];
4718 long xtimezone = 0L;
4722 time_t timep[2]; /* New access and modificaton time */
4725 long timezone; /* In case timezone not defined in .h file */
4734 #endif /* SYSUTIMEH */
4740 static struct timeb tbp;
4747 debug(F111,"zstrdt",date,len);
4752 || (date[11] != ':')
4753 || (date[14] != ':') ) {
4754 debug(F111,"Bad creation date ",date,len);
4757 debug(F111,"zstrdt date check 1",date,len);
4758 for(i = 0; i < 8; i++) {
4759 if (!isdigit(date[i])) {
4760 debug(F111,"Bad creation date ",date,len);
4764 debug(F111,"zstrdt date check 2",date,len);
4767 for (; i < 16; i += 3) {
4768 if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) {
4769 debug(F111,"Bad creation date ",date,len);
4773 debug(F111,"zstrdt date check 3",date,len);
4776 #ifdef COMMENT /* was BSD44 */
4778 man gettimeofday on BSDI 3.1 says:
4779 "The timezone field is no longer used; timezone information is stored out-
4780 side the kernel. See ctime(3) for more information." So this chunk of
4781 code is effectively a no-op, at least in BSDI 3.x.
4785 struct timezone tzp;
4786 x = gettimeofday(NULL, &tzp);
4787 debug(F101,"zstrdt BSD44 gettimeofday","",x);
4789 xtimezone = tzp.tz_minuteswest * 60L;
4792 debug(F101,"zstrdt BSD44 timezone","",xtimezone);
4796 debug(F100,"zstrdt BSD calling ftime","",0);
4798 debug(F100,"zstrdt BSD back from ftime","",0);
4799 timezone = tbp.timezone * 60L;
4800 debug(F101,"zstrdt BSD timezone","",timezone);
4803 tzset(); /* Set timezone */
4806 if ((tz = getenv("TZ")) == NULL)
4807 timezone = 0; /* UTC/GMT */
4809 timezone = atoi(&tz[3]); /* Set 'timezone'. */
4812 #endif /* SVORPOSIX */
4814 #endif /* COMMENT (was BSD44) */
4816 debug(F100,"zstrdt so far so good","",0);
4819 for (i = 0; i < 4; i++) /* Fix the year */
4823 debug(F111,"zstrdt year",s,n);
4825 debug(F100,"zstrdt fails - year","",n);
4829 /* Previous year's leap days. This won't work after year 2100. */
4831 isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
4832 days = (long) (n - 1970) * 365;
4833 days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
4837 for (i = 4; i < 16; i += 2) {
4842 case 4: /* MM: month */
4843 if ((n < 1 ) || ( n > 12)) {
4844 debug(F111,"zstrdt 4 bad date ",date,len);
4847 days += monthdays [n];
4848 if (isleapyear && n > 2)
4852 case 6: /* DD: day */
4853 if ((n < 1 ) || ( n > 31)) {
4854 debug(F111,"zstrdt 6 bad date ",date,len);
4857 tmx = (days + n - 1) * 24L * 60L * 60L;
4858 i++; /* Skip the space */
4861 case 9: /* hh: hour */
4862 if ((n < 0 ) || ( n > 23)) {
4863 debug(F111,"zstrdt 9 bad date ",date,len);
4866 tmx += n * 60L * 60L;
4867 i++; /* Skip the colon */
4870 case 12: /* mm: minute */
4871 if ((n < 0 ) || ( n > 59)) {
4872 debug(F111,"zstrdt 12 bad date ",date,len);
4875 #ifdef COMMENT /* (was BSD44) */ /* Correct for time zone */
4877 debug(F101,"zstrdt BSD44 tmx","",tmx);
4882 #ifndef CONVEX9 /* Don't yet know how to do this here */
4884 tmx += (long) timezone;
4888 extern time_t tzoffset;
4897 #endif /* CONVEX9 */
4899 #endif /* COMMENT (was BSD44) */
4901 i++; /* Skip the colon */
4904 case 15: /* ss: second */
4905 if ((n < 0 ) || ( n > 59)) {
4906 debug(F111,"zstrdt 15 bad date ",date,len);
4911 time_stamp = localtime(&tmx);
4912 debug(F101,"zstrdt tmx 1","",tmx);
4916 /* Why was this here? */
4917 time_stamp = localtime(&tmx);
4918 debug(F101,"zstrdt tmx 2","",tmx);
4919 #endif /* COMMENT */
4921 { /* New to 7.0 - Works in at at least BSDI 3.1 and FreeBSD 2.2.7 */
4923 zz = time_stamp->tm_gmtoff; /* Seconds away from Zero Meridian */
4924 debug(F101,"zstrdt BSD44 tm_gmtoff","",zz);
4926 debug(F101,"zstrdt BSD44 tmx 3 (GMT)","",tmx);
4930 Daylight Savings Time adjustment.
4931 Do this everywhere BUT in BSD44 because in BSD44,
4932 tm_gmtoff also includes the DST adjustment.
4934 if (time_stamp->tm_isdst) {
4936 debug(F101,"zstrdt tmx 3 (DST)","",tmx);
4939 n = time_stamp->tm_year;
4949 /* Z L O C A L T I M E -- GMT/UTC time string to local time string */
4952 Call with: "yyyymmdd hh:mm:ss" GMT/UTC date-time.
4953 Returns: "yyyymmdd hh:mm:ss" local date-time on success, NULL on failure.
4955 static char zltimbuf[64];
4958 zlocaltime(gmtstring) char * gmtstring; {
4961 SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime().
4962 ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in
4963 dependence on the XPG4 supplement presence. So always use
4964 what the system header file supplies in ODT 3.0...
4968 extern void ftime(); /* extern void ftime(struct timeb *) */
4969 #endif /* _SCO_DS */
4974 #endif /* M_XENIX */
4976 extern struct tm * localtime();
4978 /* And this should have been declared always through a header file */
4990 int i, n, x, isleapyear;
4991 /* J F M A M J J A S O N D */
4992 /* 31 28 31 30 31 30 31 31 30 31 30 31 */
4994 int monthdays [13] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 };
4996 struct tm *time_stamp;
4999 struct timeval tp[2];
5003 time_t timep[2]; /* New access and modificaton time */
5013 #endif /* SYSUTIMEH */
5018 static struct timeb tbp;
5021 char * date = gmtstring;
5025 debug(F111,"zlocaltime",date,len);
5030 || (date[11] != ':')
5031 || (date[14] != ':') ) {
5032 debug(F111,"Bad creation date ",date,len);
5035 debug(F111,"zlocaltime date check 1",date,len);
5036 for(i = 0; i < 8; i++) {
5037 if (!isdigit(date[i])) {
5038 debug(F111,"Bad creation date ",date,len);
5042 debug(F111,"zlocaltime date check 2",date,len);
5045 for (; i < 16; i += 3) {
5046 if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) {
5047 debug(F111,"Bad creation date ",date,len);
5051 debug(F111,"zlocaltime date check 3",date,len);
5053 debug(F100,"zlocaltime so far so good","",0);
5056 for (i = 0; i < 4; i++) /* Fix the year */
5060 debug(F111,"zlocaltime year",s,n);
5062 debug(F100,"zlocaltime fails - year","",n);
5066 /* Previous year's leap days. This won't work after year 2100. */
5068 isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
5069 days = (long) (n - 1970) * 365;
5070 days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
5074 for (i = 4; i < 16; i += 2) {
5079 case 4: /* MM: month */
5080 if ((n < 1 ) || ( n > 12)) {
5081 debug(F111,"zlocaltime 4 bad date ",date,len);
5084 days += monthdays [n];
5085 if (isleapyear && n > 2)
5089 case 6: /* DD: day */
5090 if ((n < 1 ) || ( n > 31)) {
5091 debug(F111,"zlocaltime 6 bad date ",date,len);
5094 tmx = (days + n - 1) * 24L * 60L * 60L;
5095 i++; /* Skip the space */
5098 case 9: /* hh: hour */
5099 if ((n < 0 ) || ( n > 23)) {
5100 debug(F111,"zlocaltime 9 bad date ",date,len);
5103 tmx += n * 60L * 60L;
5104 i++; /* Skip the colon */
5107 case 12: /* mm: minute */
5108 if ((n < 0 ) || ( n > 59)) {
5109 debug(F111,"zlocaltime 12 bad date ",date,len);
5113 i++; /* Skip the colon */
5116 case 15: /* ss: second */
5117 if ((n < 0 ) || ( n > 59)) {
5118 debug(F111,"zlocaltime 15 bad date ",date,len);
5125 At this point tmx is the time_t representation of the argument date-time
5126 string without any timezone or DST adjustments. Therefore it should be
5127 the same as the time_t representation of the GMT/UTC time. Now we should
5128 be able to feed it to localtime() and have it converted to a struct tm
5129 representing the local time equivalent of the given UTC time.
5131 time_stamp = localtime(&tmx);
5136 /* Now we simply reformat the struct tm to a string */
5138 x = time_stamp->tm_year;
5139 if (time_stamp->tm_year < 70 || time_stamp->tm_year > 8099)
5141 if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11)
5143 if (time_stamp->tm_mday < 1 || time_stamp->tm_mday > 31)
5145 if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 24)
5147 if (time_stamp->tm_min < 0 || time_stamp->tm_min > 60)
5149 if (time_stamp->tm_sec < 0 || time_stamp->tm_sec > 60)
5151 sprintf(zltimbuf,"%04d%02d%02d %02d:%02d:%02d",
5152 time_stamp->tm_year + 1900,
5153 time_stamp->tm_mon + 1,
5154 time_stamp->tm_mday,
5155 time_stamp->tm_hour,
5159 return((char *)zltimbuf);
5161 #endif /* ZLOCALTIME */
5162 #endif /* NOTIMESTAMP */
5164 /* Z S T I M E -- Set modification date/time+permissions for incoming file */
5167 f = pointer to name of existing file.
5168 yy = pointer to a Kermit file attribute structure in which yy->date.val
5169 is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00.
5170 yy->lprotect.val & yy->gprotect.val are permission/protection values.
5171 x = is a function code: 0 means to set the file's attributes as given.
5172 1 means compare the date in struct yy with the file creation date.
5174 -1 on any kind of error.
5175 0 if x is 0 and the attributes were set successfully.
5176 0 if x is 1 and date from attribute structure <= file creation date.
5177 1 if x is 1 and date from attribute structure > file creation date.
5181 char *f; struct zattr *yy; int x;
5183 int r = -1; /* Return code */
5186 #endif /* CK_PERMS */
5189 /* It is ifdef'd TIMESTAMP because it might not work on V7. bk@kullmar.se. */
5193 extern int utimes();
5200 /* At least, the declarations for int functions are not needed anyway */
5203 struct timeval tp[2];
5208 time_t timep[2]; /* New access and modificaton time */
5211 long timezone; /* In case not defined in .h file */
5220 #endif /* SYSUTIMEH */
5227 if (!*f) return(-1);
5228 if (!yy) return(-1);
5230 debug(F110,"zstime",f,0);
5231 debug(F111,"zstime date",yy->date.val,yy->date.len);
5234 debug(F111,"zstime setroot",ckroot,ckrootset);
5235 if (ckrootset) if (!zinroot(f)) {
5236 debug(F110,"zstime setroot violation",f,0);
5241 if (yy->date.len == 0) { /* No date in struct */
5242 if (yy->lprotect.len != 0) { /* So go do permissions */
5245 debug(F100,"zstime: nothing to do","",0);
5249 if ((tm = zstrdt(yy->date.val,yy->date.len)) < 0) {
5250 debug(F101,"zstime: zstrdt fails","",0);
5253 debug(F101,"zstime: tm","",tm);
5254 debug(F111,"zstime: A-pkt date ok ",yy->date.val,yy->date.len);
5256 if (stat(f,&sb)) { /* Get the time for the file */
5257 debug(F101,"STAT","",14);
5258 debug(F111,"zstime: Can't stat file:",f,errno);
5261 debug(F101,"STAT","",15);
5267 int i, x = 0, xx, flag = 0;
5272 debug(F111,"zstime lperms",yy->lprotect.val,yy->lprotect.len);
5273 debug(F111,"zstime gperms",yy->gprotect.val,yy->gprotect.len);
5274 debug(F110,"zstime system id",yy->systemid.val,0);
5275 sprintf(obuf,"%o",sb.st_mode);
5276 debug(F110,"zstime file perms before",obuf,0);
5281 debug(F101,"zstime isguest","",isguest);
5282 debug(F101,"zstime ckxperms","",ckxperms);
5285 /* Clear owner permissions */
5286 sb.st_mode &= (unsigned) 0177077; /* (16 bits) */
5288 /* Set permissions from ckxperms variable */
5289 sb.st_mode = ckxperms;
5290 #endif /* COMMENT */
5291 debug(F101,"zstime isguest sb.st_mode","",sb.st_mode);
5293 /* We already set them in zopeno() */
5295 #endif /* COMMENT */
5298 #endif /* CK_LOGIN */
5299 if ((yy->lprotect.len > 0 && /* Have local-format permissions */
5300 yy->systemid.len > 0 && /* from A-packet... */
5302 !strcmp(yy->systemid.val,"U1") /* AND you are same as me */
5306 ) || (yy->lprotect.len < 0) /* OR by inheritance from old file */
5309 s = yy->lprotect.val; /* UNIX filemode */
5310 xx = yy->lprotect.len;
5311 if (xx < 0) /* len < 0 means inheritance */
5313 for (i = 0; i < xx; i++) { /* Decode octal string */
5314 if (*s <= '7' && *s >= '0') {
5315 x = 8 * x + (int)(*s) - '0';
5323 sprintf(obuf,"%o",x);
5324 debug(F110,"zstime octal lperm",obuf,0);
5326 } else if (!flag && yy->gprotect.len > 0) {
5332 #endif /* CK_SCO32V4 */
5333 mask = umask(0); /* Get umask */
5334 debug(F101,"zstime mask 1","",mask);
5335 umask(mask); /* Put it back */
5336 mask ^= 0777; /* Flip the bits */
5337 debug(F101,"zstime mask 2","",mask);
5338 g = xunchar(*(yy->gprotect.val)); /* Decode generic protection */
5339 debug(F101,"zstime gprotect","",g);
5341 debug(F100,"zstime S_IRUSR","",0);
5342 if (g & 1) x |= S_IRUSR; /* Read permission */
5344 #endif /* S_IRUSR */
5346 debug(F100,"zstime S_IWUSR","",0);
5347 if (g & 2) x |= S_IWUSR; /* Write permission */
5348 if (g & 16) x |= S_IWUSR; /* Delete permission */
5350 #endif /* S_IWUSR */
5352 debug(F100,"zstime S_IXUSR","",0);
5353 if (g & 4) /* Has execute permission bit */
5355 else /* Doesn't have it */
5356 mask &= 0666; /* so also clear it out of mask */
5358 #endif /* S_IXUSR */
5359 debug(F101,"zstime mask x","",x);
5361 debug(F101,"zstime mask x|mask","",x);
5363 debug(F101,"zstime flag","",flag);
5366 debug(F101,"zstime S_IFMT x","",x);
5367 sb.st_mode = (sb.st_mode & S_IFMT) | x;
5371 debug(F101,"zstime _IFMT x","",x);
5372 sb.st_mode = (sb.st_mode & _IFMT) | x;
5378 sprintf(obuf,"%04o",sb.st_mode);
5379 debug(F111,"zstime file perms after",obuf,setperms);
5382 #endif /* CK_PERMS */
5384 debug(F101,"zstime: sb.st_atime","",sb.st_atime);
5387 tp[0].tv_sec = sb.st_atime; /* Access time first */
5388 tp[1].tv_sec = tm; /* Update time second */
5389 debug(F100,"zstime: BSD44 modtime","",0);
5392 tp.timep[0] = tm; /* Set modif. time to creation date */
5393 tp.timep[1] = sb.st_atime; /* Don't change the access time */
5394 debug(F100,"zstime: V7 modtime","",0);
5397 tp.modtime = tm; /* Set modif. time to creation date */
5398 tp.actime = sb.st_atime; /* Don't change the access time */
5399 debug(F100,"zstime: SYSUTIMEH modtime","",0);
5401 tp.mtime = tm; /* Set modif. time to creation date */
5402 tp.atime = sb.st_atime; /* Don't change the access time */
5403 debug(F100,"zstime: default modtime","",0);
5404 #endif /* SYSUTIMEH */
5408 switch (x) { /* Execute desired function */
5409 case 0: /* Set the creation date of the file */
5410 #ifdef CK_PERMS /* And permissions */
5412 NOTE: If we are inheriting permissions from a previous file, and the
5413 previous file was a directory, this would turn the new file into a directory
5414 too, but it's not, so we try to unset the right bit. Luckily, this code
5415 will probably never be executed since the upper level modules do not allow
5416 reception of a file that has the same name as a directory.
5418 NOTE 2: We change the permissions *before* we change the modification time,
5419 otherwise changing the permissions would set the mod time to the present
5424 debug(F101,"zstime setperms","",setperms);
5425 if (S_ISDIR(sb.st_mode)) {
5426 debug(F101,"zstime DIRECTORY bit on","",sb.st_mode);
5427 sb.st_mode ^= 0040000;
5428 debug(F101,"zstime DIRECTORY bit off","",sb.st_mode);
5431 x = chmod(f,sb.st_mode);
5432 debug(F101,"zstime chmod","",x);
5435 if (x < 0) return(-1);
5436 #endif /* CK_PERMS */
5438 if (!setdate) /* We don't have a date */
5439 return(0); /* so skip the following... */
5447 ) { /* Fix modification time */
5448 debug(F111,"zstime 0: can't set modtime for file",f,errno);
5451 /* Including the modtime here is not portable */
5452 debug(F110,"zstime 0: modtime set for file",f,0);
5457 case 1: /* Compare the dates */
5459 This was st_atime, which was wrong. We want the file-data modification
5462 debug(F111,"zstime 1: compare",f,sb.st_mtime);
5463 debug(F111,"zstime 1: compare","packet",tm);
5465 r = (sb.st_mtime < tm) ? 0 : 1;
5468 default: /* Error */
5471 #endif /* TIMESTAMP */
5475 /* Find initialization file. */
5480 /* nothing here for Unix. This function added for benefit of VMS Kermit. */
5483 #endif /* NOTUSED */
5486 /* Historical -- not used in Unix any more (2001-11-03) */
5489 zmail(p,f) char *p; char *f; { /* Send file f as mail to address p */
5491 Returns 0 on success
5492 2 if mail delivered but temp file can't be deleted
5493 -2 if mail can't be delivered
5494 -1 on file access error
5495 The UNIX version always returns 0 because it can't get a good return
5503 #endif /* CK_LOGIN */
5506 if (!*f) return(-1);
5509 debug(F111,"zmail setroot",ckroot,ckrootset);
5510 if (ckrootset) if (!zinroot(f)) {
5511 debug(F110,"zmail setroot violation",f,0);
5517 /* The idea is to use /usr/ucb/mail, rather than regular mail, so that */
5518 /* a subject line can be included with -s. Since we can't depend on the */
5519 /* user's path, we use the convention that /usr/ucb/Mail = /usr/ucb/mail */
5520 /* and even if Mail has been moved to somewhere else, this should still */
5521 /* find it... The search could be made more reliable by actually using */
5522 /* access() to see if /usr/ucb/Mail exists. */
5525 n = n + n + 15 + (int)strlen(p);
5531 sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
5533 sprintf(zmbuf,"Mail -s %c%s%c %s < %s", '"', f, '"', p, f);
5534 #endif /* DGUX540 */
5539 sprintf(zmbuf,"mail %s < %s", p, f);
5541 sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
5550 #endif /* NOFRILLS */
5555 zprint(p,f) char *p; char *f; { /* Print file f with options p */
5556 extern char * printername; /* From ckuus3.c */
5557 extern int printpipe;
5563 #endif /* CK_LOGIN */
5566 if (!*f) return(-1);
5569 debug(F111,"zprint setroot",ckroot,ckrootset);
5570 if (ckrootset) if (!zinroot(f)) {
5571 debug(F110,"zprint setroot violation",f,0);
5576 debug(F110,"zprint file",f,0);
5577 debug(F110,"zprint flags",p,0);
5578 debug(F110,"zprint printername",printername,0);
5579 debug(F101,"zprint printpipe","",printpipe);
5583 Note use of standard input redirection. In some systems, lp[r] runs
5584 setuid to lp (or ...?), so if user has sent a file into a directory
5585 that lp does not have read access to, it can't be printed unless it is
5586 fed to lp[r] as standard input.
5588 if (printpipe && printername) {
5589 n = 8 + (int)strlen(f) + (int)strlen(printername);
5592 sprintf(zmbuf,"cat %s | %s", f, printername);
5593 } else if (printername) {
5594 n = 8 + (int)strlen(f) + (int)strlen(printername);
5597 sprintf(zmbuf,"cat %s >> %s", f, printername);
5599 n = 4 + (int)strlen(PRINTCMD) + (int)strlen(p) + (int)strlen(f);
5602 sprintf(zmbuf,"%s %s < %s", PRINTCMD, p, f);
5604 debug(F110,"zprint command",zmbuf,0);
5606 #else /* Not UNIX */
5611 #endif /* NOFRILLS */
5613 /* Wildcard expansion functions... */
5615 static char scratch[MAXPATH+4]; /* Used by both methods */
5617 static int oldmtchs = 0; /* Let shell (ls) expand them. */
5619 static char *lscmd = "/bin/ls -d"; /* Command to use. */
5621 static char *lscmd = "echo"; /* Command to use. */
5622 #endif /* COMMENT */
5626 shxpand(pat,namlst,len) char *pat, *namlst[]; int len; {
5627 char *fgbuf = NULL; /* Buffer for forming ls command */
5628 char *p, *q; /* Workers */
5630 int i, x, retcode, itsadir;
5633 x = (int)strlen(pat) + (int)strlen(lscmd) + 3; /* Length of ls command */
5634 for (i = 0; i < oldmtchs; i++) { /* Free previous file list */
5635 if (namlst[i] ) { /* If memory is allocated */
5636 free(namlst[i]); /* Free the memory */
5637 namlst[i] = NULL ; /* Remember no memory is allocated */
5640 oldmtchs = 0 ; /* Remember there are no matches */
5641 fgbuf = malloc(x); /* Get buffer for command */
5642 if (!fgbuf) return(-1); /* Fail if cannot */
5643 ckmakmsg(fgbuf,x,lscmd," ",pat,NULL); /* Form the command */
5644 zxcmd(ZIFILE,fgbuf); /* Start the command */
5645 i = 0; /* File counter */
5646 p = scratch; /* Point to scratch area */
5647 retcode = -1; /* Assume failure */
5648 while ((x = zminchar()) != -1) { /* Read characters from command */
5650 if (c == ' ' || c == '\n') { /* Got newline or space? */
5651 *p = '\0'; /* Yes, terminate string */
5652 p = scratch; /* Point back to beginning */
5653 if (zchki(p) == -1) /* Does file exist? */
5654 continue; /* No, continue */
5655 itsadir = isdir(p); /* Yes, is it a directory? */
5656 if (xdironly && !itsadir) /* Want only dirs but this isn't */
5657 continue; /* so skip. */
5658 if (xfilonly && itsadir) /* It's a dir but want only files */
5659 continue; /* so skip. */
5660 x = (int)strlen(p); /* Keep - get length of name */
5661 q = malloc(x+1); /* Allocate space for it */
5662 if (!q) goto shxfin; /* Fail if space can't be obtained */
5663 strcpy(q,scratch); /* (safe) Copy name to space */
5664 namlst[i++] = q; /* Copy pointer to name into array */
5665 if (i >= len) goto shxfin; /* Fail if too many */
5666 } else { /* Regular character */
5667 *p++ = c; /* Copy it into scratch area */
5670 retcode = i; /* Return number of matching files */
5671 shxfin: /* Common exit point */
5672 free(fgbuf); /* Free command buffer */
5674 zclosf(ZIFILE); /* Delete the command fork. */
5675 oldmtchs = i; /* Remember how many files */
5681 Directory-reading functions for UNIX originally written for C-Kermit 4.0
5682 by Jeff Damens, CUCCA, 1984.
5684 static char * xpat = NULL; /* Global copy of fgen() pattern */
5685 static char * xpatlast = NULL; /* Rightmost segment of pattern*/
5686 static int xpatslash = 0; /* Slash count in pattern */
5687 static int xpatwild = 0; /* Original pattern is wild */
5688 static int xleafwild = 0; /* Last segment of pattern is wild */
5689 static int xpatabsolute = 0;
5696 /* S P L I T P A T H */
5699 Splits the slash-separated portions of the argument string into
5700 a list of path structures. Returns the head of the list. The
5701 structures are allocated by malloc, so they must be freed.
5702 Splitpath is used internally by the filename generator.
5708 A linked list of the slash-separated segments of the input.
5710 static struct path *
5711 splitpath(p) char *p; {
5712 struct path *head,*cur,*prv;
5715 debug(F111,"splitpath",p,xrecursive);
5718 if (!p) return(NULL);
5719 if (!*p) return(NULL);
5721 if (!strcmp(p,"**")) { /* Fix this */
5724 if (ISDIRSEP(*p)) p++; /* Skip leading slash if any */
5726 /* Make linked list of path segments from pattern */
5729 cur = (struct path *) malloc(sizeof (struct path));
5730 debug(F101,"splitpath malloc","",cur);
5732 debug(F100,"splitpath malloc failure","",0);
5734 return((struct path *)NULL);
5737 if (head == NULL) /* First, make list head */
5739 else /* Not first, link into chain */
5741 prv = cur; /* Link from previous to this one */
5744 /* treat backslash as "../" */
5745 if (bslash && *p == bslash) {
5746 strcpy(cur->npart, ".."); /* safe */
5749 for (i=0; i < MAXNAMLEN && *p && *p != '/' && *p != bslash; i++)
5750 cur -> npart[i] = *p++;
5751 cur -> npart[i] = '\0'; /* end this segment */
5753 while (*p && *p != '/' && *p != bslash)
5758 /* General case (UNIX) */
5759 for (i = 0; i < MAXNAMLEN && !ISDIRSEP(*p) && *p != '\0'; i++) {
5760 cur -> npart[i] = *p++;
5763 cur -> npart[i] = '\0'; /* End this path segment */
5765 while (!ISDIRSEP(*p) && *p != '\0') p++;
5772 makestr(&xpatlast,prv -> npart);
5773 debug(F110,"splitpath xpatlast",xpatlast,0);
5776 /* Show original path list */
5778 for (i = 0, cur = head; cur; i++) {
5779 debug(F111,"SPLITPATH",cur -> npart, i);
5787 /* F G E N -- Generate File List */
5790 File name generator. It is passed a string, possibly containing wildcards,
5791 and an array of character pointers. It finds all the matching filenames and
5792 stores pointers to them in the array. The returned strings are allocated
5793 from a static buffer local to this module (so the caller doesn't have to
5794 worry about deallocating them); this means that successive calls to fgen
5795 will wipe out the results of previous calls.
5798 A wildcard string, an array to write names to, the length of the array.
5801 The number of matches.
5802 The array is filled with filenames that matched the pattern.
5803 If there wasn't enough room in the array, -1 is returned.
5805 Originally by: Jeff Damens, CUCCA, 1984. Many changes since then.
5808 fgen(pat,resarry,len) char *pat,*resarry[]; int len; {
5815 int tilde = 0, bquote = 0;
5817 if ((namechars = getenv("NAMECHARS")) != NULL) {
5818 if (ckstrchr(namechars, '~' ) != NULL) tilde = '~';
5819 if (ckstrchr(namechars, '\\') != NULL) bslash = '\\';
5820 if (ckstrchr(namechars, '`' ) != NULL) bquote = '`';
5822 tilde = '~'; bslash = '\\'; bquote = '`';
5826 /* copy "`node_data", etc. anchors */
5827 if (bquote && *pat == bquote)
5828 while (*pat && *pat != '/' && *pat != bslash)
5830 else if (tilde && *pat == tilde)
5834 if (sptr == scratch) {
5835 strcpy(scratch,"./"); /* safe */
5838 if (!(head = splitpath(pat))) return(-1);
5840 #else /* not aegis */
5842 debug(F111,"fgen pat",pat,len);
5843 debug(F110,"fgen current directory",zgtdir(),0);
5844 debug(F101,"fgen stathack","",stathack);
5851 if (!(head = splitpath(pat))) /* Make the path segment list */
5857 if (strncmp(pat,"./",2) && strncmp(pat,"../",3)) {
5858 #endif /* COMMENT */
5859 if (!ISDIRSEP(*pat)) /* If name is not absolute */
5860 *sptr++ = '.'; /* put "./" in front. */
5864 #endif /* COMMENT */
5868 makestr(&xpat,pat); /* Save copy of original pattern */
5869 debug(F110,"fgen scratch",scratch,0);
5871 for (n = 0, s = xpat; *s; s++) /* How many slashes in the pattern */
5872 if (*s == DIRSEP) /* since these are fences for */
5873 n++; /* pattern matching */
5875 debug(F101,"fgen xpatslash","",xpatslash);
5877 numfnd = 0; /* None found yet */
5879 if (initspace(resarry,ssplen) < 0)
5882 xpatwild = iswild(xpat); /* Original pattern is wild? */
5883 xpatabsolute = isabsolute(xpat);
5884 xleafwild = iswild(xpatlast);
5886 debug(F111,"fgen xpat",xpat,xpatwild);
5887 debug(F111,"fgen xpatlast",xpatlast,xleafwild);
5888 debug(F101,"fgen xpatabsolute","",xpatabsolute);
5890 traverse(head,scratch,sptr); /* Go walk the directory tree. */
5891 while (head != NULL) { /* Done - free path segment list. */
5892 struct path *next = head -> fwd;
5896 debug(F101,"fgen","",numfnd);
5897 return(numfnd); /* Return the number of matches */
5900 /* Define LONGFN (long file names) automatically for BSD 2.9 and 4.2 */
5901 /* LONGFN can also be defined on the cc command line. */
5916 T R A V E R S E -- Traverse a directory tree.
5918 Walks the directory tree looking for matches to its arguments.
5919 The algorithm is, briefly:
5921 If the current pattern segment contains no wildcards, that
5922 segment is added to what we already have. If the name so far
5923 exists, we call ourselves recursively with the next segment
5924 in the pattern string; otherwise, we just return.
5926 If the current pattern segment contains wildcards, we open the name
5927 we've accumulated so far (assuming it is really a directory), then read
5928 each filename in it, and, if it matches the wildcard pattern segment, add
5929 that filename to what we have so far and call ourselves recursively on
5932 Finally, when no more pattern segments remain, we add what's accumulated
5933 so far to the result array and increment the number of matches.
5936 A pattern path list (as generated by splitpath), a string pointer that
5937 points to what we've traversed so far (this can be initialized to "/"
5938 to start the search at the root directory, or to "./" to start the
5939 search at the current directory), and a string pointer to the end of
5940 the string in the previous argument, plus the global "recursive",
5941 "xmatchdot", and "xdironly" flags.
5943 Returns: void, with:
5944 mtchs[] containing the array of filename string pointers, and:
5945 numfnd containing the number of filenames.
5947 Although it might be poor practice, the mtchs[] array is revealed to the
5948 outside in case it needs it; for example, to be sorted prior to use.
5949 (It is poor practice because not all platforms implement file lists the
5950 same way; some don't use an array at all.)
5952 Note that addresult() acts as a second-level filter; due to selection
5953 criteria outside of the pattern, it might decline to add files that
5954 this routine asks it to, e.g. because we are collecting only directory
5955 names but not the names of regular files.
5957 WARNING: In the course of C-Kermit 7.0 development, this routine became
5958 ridiculously complex, in order to meet approximately sixty specific
5959 requirements. DON'T EVEN THINK ABOUT MODIFYING THIS ROUTINE! Trust me;
5960 it is not possible to fix anything in it without breaking something else.
5961 This routine badly needs a total redesign and rewrite. Note: There may
5962 be some good applications for realpath() and/or scandir() and/or fts_blah()
5963 here, on platforms where they are available.
5966 traverse(pl,sofar,endcur) struct path *pl; char *sofar, *endcur; {
5968 /* Appropriate declarations for directory routines and structures */
5969 /* #define OPENDIR means to use opendir(), readdir(), closedir() */
5970 /* If OPENDIR not defined, we use open(), read(), close() */
5972 #ifdef DIRENT /* New way, <dirent.h> */
5974 DIR *fd, *opendir();
5975 struct dirent *dirbuf;
5976 struct dirent *readdir();
5978 #ifdef LONGFN /* Old way, <dir.h> with opendir() */
5980 DIR *fd, *opendir();
5981 struct direct *dirbuf;
5983 int fd; /* Old way, <dir.h> with open() */
5984 struct direct dir_entry;
5985 struct direct *dirbuf = &dir_entry;
5988 int mopts = 0; /* ckmatch() opts */
5989 int depth = 0; /* Directory tree depth */
5991 char nambuf[MAXNAMLEN+4]; /* Buffer for a filename */
5992 int itsadir = 0, segisdir = 0, itswild = 0, mresult, n, x /* , y */ ;
5993 struct stat statbuf; /* For file info. */
5995 debug(F101,"STAT","",16);
5996 if (pl == NULL) { /* End of path-segment list */
5997 *--endcur = '\0'; /* Terminate string, overwrite trailing slash */
5998 debug(F110,"traverse add: end of path segment",sofar,0);
5999 addresult(sofar,-1);
6003 /* This speeds up the search a lot and we still get good results */
6004 /* but it breaks the tagging of directory names done in addresult */
6005 if (xrecursive || xfilonly || xdironly || xpatslash) {
6006 itsadir = xisdir(sofar);
6007 debug(F101,"STAT","",17);
6009 itsadir = (strncmp(sofar,"./",2) == 0);
6011 itsadir = xisdir(sofar);
6012 debug(F101,"STAT","",18);
6014 debug(F111,"traverse entry sofar",sofar,itsadir);
6016 #ifdef CKSYMLINK /* We're doing symlinks? */
6017 #ifdef USE_LSTAT /* OK to use lstat()? */
6018 if (itsadir && xnolinks) { /* If not following symlinks */
6021 x = lstat(sofar,&buf);
6022 debug(F111,"traverse lstat 1",sofar,x);
6025 S_ISLNK(buf.st_mode)
6028 ((_IFMT & buf.st_mode) == _IFLNK)
6030 #endif /* S_ISLNK */
6034 #endif /* USE_LSTAT */
6035 #endif /* CKSYMLINK */
6037 if (!xmatchdot && xpatlast[0] == '.')
6039 if (!xmatchdot && xpat[0] == '.' && xpat[1] != '/' && xpat[1] != '.')
6042 /* ckmatch() options */
6044 if (xmatchdot) mopts |= 1; /* Match dot */
6045 if (!xrecursive) mopts |= 2; /* Dirsep is fence */
6047 debug(F111,"traverse entry xpat",xpat,xpatslash);
6048 debug(F111,"traverse entry xpatlast",xpatlast,xmatchdot);
6049 debug(F110,"traverse entry pl -> npart",pl -> npart,0);
6052 if (xrecursive > 0 && !itsadir) {
6053 char * s; /* Recursive descent and this is a regular file */
6054 *--endcur = '\0'; /* Terminate string, overwrite trailing slash */
6056 /* Find the nth slash from the right and match from there... */
6057 /* (n == the number of slashes in the original pattern - see fgen) */
6058 if (*sofar == '/') {
6059 debug(F110,"traverse xpatslash absolute",sofar,0);
6062 debug(F111,"traverse xpatslash relative",sofar,xpatslash);
6063 for (s = endcur - 1, n = 0; s >= sofar; s--) {
6065 if (++n >= xpatslash) {
6073 /* This speeds things up a bit. */
6074 /* If it causes trouble define NOSKIPMATCH and rebuild. */
6075 if (xpat[0] == '*' && !xpat[1])
6076 x = xmatchdot ? 1 : (s[0] != '.');
6078 #endif /* NOSKIPMATCH */
6079 x = ckmatch(xpat, s, 1, mopts); /* Match with original pattern */
6080 debug(F111,"traverse xpatslash ckmatch",s,x);
6082 debug(F110,"traverse add: recursive, match, && !isdir",sofar,0);
6083 addresult(sofar,itsadir);
6087 #endif /* RECURSIVE */
6089 debug(F111,"traverse sofar 2",sofar,0);
6091 segisdir = ((pl -> fwd) == NULL) ? 0 : 1;
6092 itswild = iswild(pl -> npart);
6094 debug(F111,"traverse segisdir",sofar,segisdir);
6095 debug(F111,"traverse itswild ",pl -> npart,itswild);
6098 if (xrecursive > 0) { /* If recursing and... */
6099 if (segisdir && itswild) /* this is a dir and npart is wild */
6100 goto blah; /* or... */
6101 else if (!xpatabsolute && !xpatwild) /* search object is nonwild */
6102 goto blah; /* then go recurse */
6104 #endif /* RECURSIVE */
6106 if (!itswild) { /* This path segment not wild? */
6108 strcpy(endcur,pl -> npart); /* (safe) Append next part. */
6109 endcur += (int)strlen(pl -> npart); /* Advance end pointer */
6112 strcpy() does not account for quoted metacharacters.
6113 We must remove the quotes before doing the stat().
6119 while ((c = *s++)) {
6130 #endif /* COMMENT */
6131 *endcur = '\0'; /* End new current string. */
6133 if (stat(sofar,&statbuf) == 0) { /* If this piece exists... */
6134 debug(F110,"traverse exists",sofar,0);
6135 *endcur++ = DIRSEP; /* add slash to end */
6136 *endcur = '\0'; /* and end the string again. */
6137 traverse(pl -> fwd, sofar, endcur);
6140 else debug(F110,"traverse not found", sofar, 0);
6145 *endcur = '\0'; /* End current string */
6146 debug(F111,"traverse sofar 3",sofar,0);
6151 /* Search is recursive or ... */
6152 /* path segment contains wildcards, have to open and search directory. */
6156 debug(F110,"traverse opening directory", sofar, 0);
6159 debug(F110,"traverse opendir()",sofar,0);
6160 if ((fd = opendir(sofar)) == NULL) { /* Can't open, fail. */
6161 debug(F101,"traverse opendir() failed","",errno);
6164 while ((dirbuf = readdir(fd)))
6165 #else /* !OPENDIR */
6166 debug(F110,"traverse directory open()",sofar,0);
6167 if ((fd = open(sofar,O_RDONLY)) < 0) {
6168 debug(F101,"traverse directory open() failed","",errno);
6171 while (read(fd, (char *)dirbuf, sizeof dir_entry))
6172 #endif /* OPENDIR */
6173 { /* Read each entry in this directory */
6178 /* On some platforms, the read[dir]() can return deleted files, */
6179 /* e.g. HP-UX 5.00. There is no point in grinding through this */
6180 /* routine when the file doesn't exist... */
6182 if ( /* There actually is an inode... */
6190 dirbuf->d_stat.st_ino != 0
6196 dirbuf->d_fileno != 0
6199 dirbuf->d_fileno != 0
6202 dirbuf->d_fileno != 0
6205 dirbuf->d_fileno != 0
6216 #endif /* __FreeBSD__ */
6217 #endif /* __386BSD__ */
6220 #endif /* SOLARIS */
6229 ckstrncpy(nambuf, /* Copy the name */
6233 if (nambuf[0] == '.') {
6234 if (!nambuf[1] || (nambuf[1] == '.' && !nambuf[2])) {
6235 debug(F110,"traverse skipping",nambuf,0);
6236 continue; /* skip "." and ".." */
6239 s = nambuf; /* Copy name to end of sofar */
6241 while ((*eos = *s)) {
6246 Now we check the file for (a) whether it is a directory, and (b) whether
6247 its name matches our pattern. If it is a directory, and if we have been
6248 told to build a recursive list, then we must descend regardless of whether
6249 it matches the pattern. If it is not a directory and it does not match
6250 our pattern, we skip it. Note: sofar is the full pathname, nambuf is
6253 /* Do this first to save pointless function calls */
6254 if (nambuf[0] == '.' && !xmatchdot) /* Dir name starts with '.' */
6257 if (xrecursive || xfilonly || xdironly || xpatslash) {
6258 itsadir = xisdir(sofar); /* See if it's a directory */
6259 debug(F101,"STAT","",19);
6264 itsadir = xisdir(sofar);
6265 debug(F101,"STAT","",20);
6270 if (itsadir && xnolinks) { /* If not following symlinks */
6273 x = lstat(sofar,&buf);
6274 debug(F111,"traverse lstat 2",sofar,x);
6277 S_ISLNK(buf.st_mode)
6280 ((_IFMT & buf.st_mode) == _IFLNK)
6282 #endif /* S_ISLNK */
6286 #endif /* USE_LSTAT */
6287 #endif /* CKSYMLINK */
6290 if (xrecursive > 0 && itsadir &&
6291 (xpatlast[0] == '*') && !xpatlast[1]
6294 "traverse add: recursive && isdir && segisdir or match",
6298 addresult(sofar,itsadir);
6299 if (numfnd < 0) return;
6301 #endif /* RECURSIVE */
6303 debug(F111,"traverse mresult xpat",xpat,xrecursive);
6304 debug(F111,"traverse mresult pl -> npart",
6306 ((pl -> fwd) ? 9999 : 0)
6308 debug(F111,"traverse mresult sofar segisdir",sofar,segisdir);
6309 debug(F111,"traverse mresult sofar itsadir",sofar,itsadir);
6310 debug(F101,"traverse mresult xmatchdot","",xmatchdot);
6312 Match the path so far with the pattern after stripping any leading "./"
6313 from either or both. The pattern chosen is the full original pattern if
6314 the match candidate (sofar) is not a directory, or else just the name part
6315 (pl->npart) if it is.
6318 char * s1; /* The pattern */
6319 char * s2 = sofar; /* The path so far */
6320 char * s3; /* Worker */
6321 int opts; /* Match options */
6323 s1 = itsadir ? pl->npart : xpat;
6326 /* I can't explain this but it unbreaks "cd blah/sub<Esc>" */
6327 if (itsadir && !xrecursive && xpatslash > 0 &&
6328 segisdir == 0 && itswild) {
6330 debug(F110,"traverse mresult s1 kludge",s1,0);
6332 #endif /* COMMENT */
6334 if (xrecursive && xpatslash == 0)
6336 while ((s1[0] == '.') && (s1[1] == '/')) /* Strip "./" */
6338 while ((s2[0] == '.') && (s2[1] == '/')) /* Ditto */
6340 opts = mopts; /* Match options */
6341 if (itsadir) /* Current segment is a directory */
6342 opts = mopts & 1; /* No fences */
6343 s3 = s2; /* Get segment depth */
6345 while (*s3) { if (*s3++ == '/') depth++; }
6347 /* This speeds things up a bit. */
6348 /* If it causes trouble define NOSKIPMATCH and rebuild. */
6349 if (depth == 0 && (s1[0] == '*') && !s1[1])
6350 mresult = xmatchdot ? 1 : (s2[0] != '.');
6352 #endif /* NOSKIPMATCH */
6353 mresult = ckmatch(s1,s2,1,opts); /* Match */
6357 debug(F111,"traverse mresult depth",sofar,depth);
6358 debug(F101,"traverse mresult xpatslash","",xpatslash);
6359 debug(F111,"traverse mresult nambuf",nambuf,mresult);
6360 debug(F111,"traverse mresult itswild",pl -> npart,itswild);
6361 debug(F111,"traverse mresult segisdir",pl -> npart,segisdir);
6364 if (mresult || /* If match succeeded */
6365 xrecursive || /* Or search is recursive */
6366 depth < xpatslash /* Or not deep enough to match... */
6368 if ( /* If it's not a directory... */
6370 The problem here is that segisdir is apparently not set appropriately.
6371 If I leave in the !segisdir test, then "dir /recursive blah" (where blah is
6372 a directory name) misses some regular files because sometimes segisdir
6373 is set and sometimes it's not. But if I comment it out, then
6374 "dir <star>/<star>.txt lists every file in * and does not even open up the
6375 subdirectories. However, "dir /rec <star>/<star>.txt" works right.
6378 mresult && (!itsadir && !segisdir)
6380 mresult && /* Matched */
6381 !itsadir && /* sofar is not a directory */
6382 ((!xrecursive && !segisdir) || xrecursive)
6383 #endif /* COMMENT */
6386 "traverse add: match && !itsadir",sofar,0);
6387 addresult(sofar,itsadir);
6388 if (numfnd < 0) return;
6389 } else if (itsadir && (xrecursive || mresult)) {
6390 struct path * xx = NULL;
6391 *eos++ = DIRSEP; /* Add directory separator */
6392 *eos = '\0'; /* to end of segment */
6394 /* Copy previous pattern segment to this new directory */
6396 if (xrecursive > 0 && !(pl -> fwd)) {
6397 xx = (struct path *) malloc(sizeof (struct path));
6401 strcpy(xx -> npart, pl -> npart); /* safe */
6404 #endif /* RECURSIVE */
6405 traverse(pl -> fwd, sofar, eos); /* Traverse new directory */
6411 #else /* !OPENDIR */
6413 #endif /* OPENDIR */
6418 * Adds a result string to the result array. Increments the number
6419 * of matches found, copies the found string into our string
6420 * buffer, and puts a pointer to the buffer into the caller's result
6421 * array. Our free buffer pointer is updated. If there is no
6422 * more room in the caller's array, the number of matches is set to -1.
6423 * Input: a result string.
6427 addresult(str,itsadir) char *str; int itsadir; {
6431 debug(F100,"addresult string space not init'd","",0);
6432 initspace(mtchs,ssplen);
6435 debug(F111,"addresult",str,itsadir);
6440 itsadir = xisdir(str);
6442 if ((xdironly && !itsadir) || (xfilonly && itsadir)) {
6443 debug(F111,"addresult skip",str,itsadir);
6446 while (str[0] == '.' && ISDIRSEP(str[1])) /* Strip all "./" from front */
6448 if (--remlen < 0) { /* Elements left in array of names */
6449 debug(F111,"addresult ARRAY FULL",str,numfnd);
6453 len = (int)strlen(str); /* Space this will use */
6454 debug(F111,"addresult len",str,len);
6459 if ((freeptr + len + itsadir + 1) > (sspace + ssplen)) {
6460 debug(F111,"addresult OUT OF SPACE",str,numfnd);
6463 "?String space %d exhausted - use SET FILE STRINGSPACE to increase\n",ssplen);
6465 printf("?String space %d exhausted\n",ssplen);
6466 #endif /* DYNAMIC */
6467 numfnd = -1; /* Do not record if not enough space */
6470 strcpy(freeptr,str); /* safe */
6472 /* Tag directory names by putting '/' at the end */
6474 if (itsadir && (freeptr[len-1] == '/')) {
6475 freeptr[len++] = DIRSEP;
6476 freeptr[len] = '\0';
6478 if (numfnd >= maxnames) {
6481 "?Too many files (%d max) - use SET FILE LISTSIZE to increase\n",maxnames);
6483 printf("?Too many files - %d max\n",maxnames);
6484 #endif /* DYNAMIC */
6489 *resptr++ = freeptr;
6490 freeptr += (len + 1);
6492 debug(F111,"addresult ADD",str,numfnd);
6497 * match(pattern,string):
6498 * pattern matcher. Takes a string and a pattern possibly containing
6499 * the wildcard characters '*' and '?'. Returns true if the pattern
6500 * matches the string, false otherwise.
6501 * Orignally by: Jeff Damens, CUCCA, 1984
6502 * No longer used as of C-Kermit 7.0, now we use ckmatch() instead (ckclib.c).
6504 * Input: a string and a wildcard pattern.
6505 * Returns: 1 if match, 0 if no match.
6508 match(pattern, string) char *pattern, *string; {
6509 char *psave = NULL, *ssave = NULL; /* Backup pointers for failure */
6510 int q = 0; /* Quote flag */
6512 if (*string == '.' && *pattern != '.' && !xmatchdot) {
6513 debug(F110,"match skip",string,0);
6517 for (; *pattern == *string; pattern++,string++) /* Skip first */
6518 if (*string == '\0') return(1); /* End of strings, succeed */
6520 if (*pattern == '\\' && q == 0) { /* Watch out for quoted */
6521 q = 1; /* metacharacters */
6522 pattern++; /* advance past quote */
6523 if (*pattern != *string) return(0);
6530 if (*string != '\0' && *pattern == '?') {
6531 pattern++; /* '?', let it match */
6533 } else if (*pattern == '*') { /* '*' ... */
6534 psave = ++pattern; /* remember where we saw it */
6535 ssave = string; /* let it match 0 chars */
6536 } else if (ssave != NULL && *ssave != '\0') { /* if not at end */
6537 /* ...have seen a star */
6538 string = ++ssave; /* skip 1 char from string */
6539 pattern = psave; /* and back up pattern */
6540 } else return(0); /* otherwise just fail */
6544 #endif /* COMMENT */
6547 The following two functions are for expanding tilde in filenames
6548 Contributed by Howie Kaye, CUCCA, developed for CCMD package.
6551 /* W H O A M I -- Get user's username. */
6555 2) See if the $USER environment variable is set ($LOGNAME on AT&T)
6556 3) If $USER's uid is the same as ruid, realname is $USER
6557 4) Otherwise get logged in user's name
6558 5) If that name has the same uid as the real uid realname is loginname
6559 6) Otherwise, get a name for ruid from /etc/passwd
6569 static char realname[UIDBUFLEN+1]; /* user's name */
6570 static int ruid = -1; /* user's real uid */
6571 char loginname[UIDBUFLEN+1], envname[256]; /* temp storage */
6574 _PROTOTYP(extern char * getlogin, (void) );
6579 ruid = real_uid(); /* get our uid */
6581 /* how about $USER or $LOGNAME? */
6582 if ((c = getenv(NAMEENV)) != NULL) { /* check the env variable */
6583 ckstrncpy(envname, c, 255);
6584 if ((p = getpwnam(envname)) != NULL) {
6585 if (p->pw_uid == ruid) { /* get passwd entry for envname */
6586 ckstrncpy(realname, envname, UIDBUFLEN); /* uid's are same */
6592 /* can we use loginname() ? */
6594 if ((c = getlogin()) != NULL) { /* name from utmp file */
6595 ckstrncpy (loginname, c, UIDBUFLEN);
6596 if ((p = getpwnam(loginname)) != NULL) /* get passwd entry */
6597 if (p->pw_uid == ruid) /* for loginname */
6598 ckstrncpy(realname, envname, UIDBUFLEN); /* if uid's are same */
6601 /* Use first name we get for ruid */
6603 if ((p = getpwuid(ruid)) == NULL) { /* name for uid */
6604 realname[0] = '\0'; /* no user name */
6608 ckstrncpy(realname, p->pw_name, UIDBUFLEN);
6615 /* T I L D E _ E X P A N D -- expand ~user to the user's home directory. */
6618 tilde_expand(dirname) char *dirname; {
6625 struct passwd *user;
6626 static char olddir[BUFLEN+1];
6627 static char oldrealdir[BUFLEN+1];
6628 static char temp[BUFLEN+1];
6631 debug(F111,"tilde_expand",dirname,dirname[0]);
6633 if (dirname[0] != '~') /* Not a tilde...return param */
6635 if (!strcmp(olddir,dirname)) { /* Same as last time */
6636 return(oldrealdir); /* so return old answer. */
6638 j = (int)strlen(dirname);
6639 for (i = 0; i < j; i++) /* find username part of string */
6640 if (!ISDIRSEP(dirname[i]))
6641 temp[i] = dirname[i];
6643 temp[i] = '\0'; /* tie off with a NULL */
6644 if (i == 1) { /* if just a "~" */
6647 user = getpwnam(uidbuf); /* Get info on current user */
6651 char * p = whoami();
6658 user = getpwnam(&temp[1]); /* otherwise on the specified user */
6661 if (user != NULL) { /* valid user? */
6662 ckstrncpy(olddir, dirname, BUFLEN); /* remember the directory */
6663 ckstrncpy(oldrealdir,user->pw_dir, BUFLEN); /* and home directory */
6664 ckstrncat(oldrealdir,&dirname[i], BUFLEN);
6665 oldrealdir[BUFLEN] = '\0';
6667 } else { /* invalid? */
6668 ckstrncpy(olddir, dirname, BUFLEN); /* remember for next time */
6669 ckstrncpy(oldrealdir, dirname, BUFLEN);
6678 Functions for executing system commands.
6679 zsyscmd() executes the system command in the normal, default way for
6680 the system. In UNIX, it does what system() does. Thus, its results
6681 are always predictable.
6682 zshcmd() executes the command using the user's preferred shell.
6685 zsyscmd(s) char *s; {
6687 if (nopush) return(-1);
6688 if (!priv_chk()) return(system(s));
6692 /* This doesn't work... */
6696 #endif /* COMMENT */
6698 if (nopush) return(-1);
6699 if ((shpid = fork())) {
6700 if (shpid < (PID_T)0) return(-1); /* Parent */
6701 while (shpid != (PID_T) wait(&status))
6705 if (priv_can()) { /* Child: cancel any priv's */
6706 printf("?Privilege cancellation failure\n");
6709 restorsigs(); /* Restore ignored signals */
6711 execl("/usr/bin/sh","sh","-c",s,NULL);
6712 perror("/usr/bin/sh");
6715 execl("/bin/rc", "rc", "-c", s, NULL);
6718 execl("/bin/sh","sh","-c",s,NULL);
6723 return(0); /* Shut up ANSI compilers. */
6728 /* Z _ E X E C -- Overlay ourselves with another program */
6736 #endif /* ATT7300 */
6738 #endif /* NOZEXEC */
6741 z_exec(p,s,t) char * p, ** s; int t; { /* Overlay ourselves with "p s..." */
6743 printf("EXEC /REDIRECT NOT IMPLEMENTED IN THIS VERSION OF C-KERMIT\n");
6744 debug(F110,"z_exec NOT IMPLEMENTED",p,0);
6748 debug(F110,"z_exec command",p,0);
6749 debug(F110,"z_exec arg 0",s[0],0);
6750 debug(F110,"z_exec arg 1",s[1],0);
6751 debug(F101,"z_exec t","",t);
6757 /* dup2(ttyfd, 2); */
6761 restorsigs(); /* Restore ignored signals */
6763 if (x < 0) debug(F101,"z_exec errno","",errno);
6764 #endif /* NOZEXEC */
6768 Z S H C M D -- Execute a shell command (or program thru the shell).
6770 Original UNIX code by H. Fischer; copyright rights assigned to Columbia U.
6771 Adapted to use getpwuid to find login shell because many systems do not
6772 have SHELL in environment, and to use direct calling of shell rather
6773 than intermediate system() call. -- H. Fischer (1985); many changes since
6774 then. Call with s pointing to command to execute. Returns:
6775 -1 on failure to start the command (can't find, can't fork, can't run).
6776 1 if command ran and gave an exit status of 0.
6777 0 if command ran and gave a nonzero exit status.
6778 with pexitstatus containing the command's exit status.
6781 zshcmd(s) char *s; {
6787 if (nopush) return(-1);
6788 debug(F110,"zshcmd command",s,0);
6791 if ((pid = vfork()) == 0) { /* Make child quickly */
6792 char *shpath, *shname, *shptr; /* For finding desired shell */
6794 if (priv_can()) exit(1); /* Turn off privs. */
6795 if ((shpath = getenv("SHELL")) == NULL) shpath = "/com/sh";
6797 #else /* All Unix systems */
6798 if ((pid = fork()) == 0) { /* Make child */
6799 char *shpath, *shname, *shptr; /* For finding desired shell */
6801 #ifdef HPUX10 /* Default */
6802 char *defshell = "/usr/bin/sh";
6805 char *defshell = "/bin/rc";
6807 char *defshell = "/bin/sh";
6810 if (priv_can()) exit(1); /* Turn off privs. */
6812 /* Old way always used /etc/passwd shell */
6813 p = getpwuid(real_uid()); /* Get login data */
6814 if (p == (struct passwd *) NULL || !*(p->pw_shell))
6817 shpath = p->pw_shell;
6819 /* New way lets user override with SHELL variable, but does not rely on it. */
6820 /* This allows user to specify a different shell. */
6821 shpath = getenv("SHELL"); /* What shell? */
6822 debug(F110,"zshcmd SHELL",shpath,0);
6823 if (shpath == NULL) {
6824 p = getpwuid( real_uid() ); /* Get login data */
6825 if (p == (struct passwd *)NULL || !*(p->pw_shell))
6827 else shpath = p->pw_shell;
6828 debug(F110,"zshcmd shpath",shpath,0);
6830 #endif /* COMMENT */
6832 shptr = shname = shpath;
6833 while (*shptr != '\0')
6834 if (*shptr++ == DIRSEP)
6836 restorsigs(); /* Restore ignored signals */
6837 debug(F110,"zshcmd shname",shname,0);
6838 if (s == NULL || *s == '\0') { /* Interactive shell requested? */
6839 execl(shpath,shname,"-i",NULL); /* Yes, do that */
6840 } else { /* Otherwise, */
6841 execl(shpath,shname,"-c",s,NULL); /* exec the given command */
6842 } /* If execl() failed, */
6843 exit(BAD_EXIT); /* return bad return code. */
6845 } else { /* Parent */
6847 int wstat; /* ... must wait for child */
6849 int child; /* Child's exit status */
6850 #endif /* CK_CHILD */
6851 SIGTYP (*istat)(), (*qstat)();
6853 if (pid == (PID_T) -1) return(-1); /* fork() failed? */
6855 istat = signal(SIGINT,SIG_IGN); /* Let the fork handle keyboard */
6856 qstat = signal(SIGQUIT,SIG_IGN); /* interrupts itself... */
6859 while (((wstat = wait(&child)) != pid) && (wstat != -1))
6861 while (((wstat = wait((WAIT_T *)0)) != pid) && (wstat != -1))
6862 #endif /* CK_CHILD */
6863 ; /* Wait for fork */
6864 signal(SIGINT,istat); /* Restore interrupts */
6865 signal(SIGQUIT,qstat);
6867 pexitstat = (child & 0xff) ? child : child >> 8;
6868 debug(F101,"zshcmd exit status","",pexitstat);
6869 return(child == 0 ? 1 : 0); /* Return child's status */
6870 #endif /* CK_CHILD */
6876 /* I S W I L D -- Check if filespec is "wild" */
6880 0 if argument is empty or is the name of a single file;
6881 1 if it contains wildcard characters.
6882 Note: must match the algorithm used by match(), hence no [a-z], etc.
6885 iswild(filespec) char *filespec; {
6886 char c, *p, *f; int x;
6891 if (wildxpand) { /* Shell handles wildcarding */
6892 if ((x = nzxpand(filespec,0)) > 1)
6894 if (x == 0) return(0); /* File does not exist */
6895 p = malloc(MAXNAMLEN + 20);
6897 x = (strcmp(filespec,p) != 0);
6901 } else { /* We do it ourselves */
6902 while ((c = *filespec++) != '\0') {
6903 if (c == '\\' && quo == 0) {
6907 if (!quo && (c == '*' || c == '?'
6913 #endif /* CKREGEX */
6915 debug(F111,"iswild",f,1);
6920 debug(F111,"iswild",f,0);
6926 I S D I R -- Is a Directory.
6928 Tell if string pointer s is the name of an existing directory. Returns 1 if
6929 directory, 0 if not a directory.
6931 The following no longer applies:
6933 If the file is a symlink, we return 1 if
6934 it is a directory OR if it is a link to a directory and the "xrecursive" flag
6935 is NOT set. This is to allow parsing a link to a directory as if it were a
6936 directory (e.g. in the CD or IF DIRECTORY command) but still prevent
6937 recursive traversal from visiting the same directory twice.
6941 /* This turns out to be unsafe and gives little benefit anyway. */
6942 /* See notes 28 Sep 2003. Thus ISDIRCACHE is not defined. */
6944 static char prevpath[CKMAXPATH+4] = { '\0', '\0' };
6945 static int prevstat = -1;
6948 debug(F100,"CLEAR ISDIR CACHE","",0);
6952 #endif /* ISDIRCACHE */
6956 int x, needrlink = 0, islink = 0;
6957 struct stat statbuf;
6958 char fnam[CKMAXPATH+4];
6964 if (prevstat > -1) {
6965 if (s[0] == prevpath[0]) {
6966 if (!strcmp(s,prevpath)) {
6967 debug(F111,"isdir cache hit",s,prevstat);
6972 #endif /* ISDIRCACHE */
6977 The following over-clever bit has been commented out because it presumes
6978 to know when a symlink might be redundant, which it can't possibly know.
6979 Using plain old stat() gives Kermit the same results as ls and ls -R, which
6980 is just fine: no surprises.
6984 x = lstat(s,&statbuf);
6985 debug(F111,"isdir lstat",s,x);
6987 #endif /* USE_LSTAT */
6988 x = stat(s,&statbuf);
6989 debug(F111,"isdir stat",s,x);
6992 #endif /* USE_LSTAT */
6994 x = stat(s,&statbuf);
6995 debug(F111,"isdir stat",s,x);
6996 #endif /* COMMENT */
6998 debug(F101,"isdir errno","",errno);
7007 islink = S_ISLNK(statbuf.st_mode);
7008 debug(F101,"isdir S_ISLNK islink","",islink);
7011 islink = (_IFMT & statbuf.st_mode) == _IFLNK;
7012 debug(F101,"isdir _IFLNK islink","",islink);
7014 #endif /* S_ISLNK */
7015 #endif /* NOLINKBITS */
7017 if (readlink(s,fnam,CKMAXPATH) > -1)
7022 x = stat(s,&statbuf);
7024 debug(F101,"isdir errno","",errno);
7027 debug(F111,"isdir stat",s,x);
7028 #endif /* CKSYMLINK */
7029 debug(F101,"isdir islink","",islink);
7030 debug(F101,"isdir statbuf.st_mode","",statbuf.st_mode);
7031 x = islink ? 0 : (S_ISDIR (statbuf.st_mode) ? 1 : 0);
7034 ckstrncpy(prevpath,s,CKMAXPATH+1);
7035 #endif /* ISDIRCACHE */
7040 /* Some systems don't have mkdir(), e.g. Tandy Xenix 3.2.. */
7042 /* Z M K D I R -- Create directory(s) if necessary */
7045 A pointer to a file specification that might contain directory
7046 information. The filename is expected to be included.
7047 If the file specification does not include any directory separators,
7048 then it is assumed to be a plain file.
7049 If one or more directories are included in the file specification,
7050 this routine tries to create them if they don't already exist.
7052 0 or greater on success, i.e. the number of directories created.
7053 -1 on failure to create the directory
7056 zmkdir(path) char *path; {
7060 if (!path) path = "";
7061 if (!*path) return(-1);
7064 debug(F111,"zmkdir setroot",ckroot,ckrootset);
7065 if (ckrootset) if (!zinroot(path)) {
7066 debug(F110,"zmkdir setroot violation",path,0);
7072 debug(F111,"zmkdir",path,x);
7073 if (x < 1 || x > MAXPATH) /* Check length */
7075 if (!(tp = malloc(x+1))) /* Make a temporary copy */
7077 strcpy(tp,path); /* safe (prechecked) */
7079 if (*tp == '~') { /* Starts with tilde? */
7080 xp = tilde_expand(tp); /* Attempt to expand tilde */
7084 debug(F110,"zmkdir tilde_expand",xp,0);
7085 if (!(zp = malloc(strlen(xp) + 1))) { /* Make a place for it */
7090 free(tp); /* Free previous buffer */
7091 tp = zp; /* Point to new one */
7092 strcpy(tp,xp); /* Copy expanded name to new buffer */
7096 debug(F110,"zmkdir tp after tilde_expansion",tp,0);
7098 if (ISDIRSEP(*xp)) /* Don't create root directory! */
7101 /* Go thru filespec from left to right... */
7103 for (; *xp; xp++) { /* Create parts that don't exist */
7104 if (!ISDIRSEP(*xp)) /* Find next directory separator */
7106 c = *xp; /* Got one. */
7107 *xp = NUL; /* Make this the end of the string. */
7108 if (!isdir(tp)) { /* This directory exists already? */
7110 if (isguest) /* Not allowed for guests */
7113 /* Nor if MKDIR and/or CD are disabled */
7115 #endif /* CK_LOGIN */
7120 ) && (!ENABLED(en_mkd) || !ENABLED(en_cwd)))
7124 debug(F110,"zmkdir making",tp,0);
7125 x = /* No, try to create it */
7127 -1 /* Systems without mkdir() */
7129 mkdir(tp,0777) /* UNIX */
7130 #endif /* NOMKDIR */
7133 debug(F101,"zmkdir failed, errno","",errno);
7134 free(tp); /* Free temporary buffer. */
7136 return(-1); /* Return failure code. */
7140 *xp = c; /* Replace the separator. */
7142 free(tp); /* Free temporary buffer. */
7143 return(count); /* Return success code. */
7145 #endif /* CK_MKDIR */
7148 zrmdir(path) char *path; {
7152 #endif /* CK_LOGIN */
7154 if (!path) path = "";
7155 if (!*path) return(-1);
7158 debug(F111,"zrmdir setroot",ckroot,ckrootset);
7159 if (ckrootset) if (!zinroot(path)) {
7160 debug(F110,"zrmdir setroot violation",path,0);
7166 return(rmdir(path));
7169 #endif /* NOMKDIR */
7172 /* Z F S E E K -- Position input file pointer */
7175 Long int, 0-based, indicating desired position.
7185 zfseek(pos) long pos;
7186 #endif /* CK_ANSIC */
7188 zincnt = -1; /* Must empty the input buffer */
7189 debug(F101,"zfseek","",pos);
7190 return(fseek(fp[ZIFILE], pos, 0)?-1:0);
7192 #endif /* NORESEND */
7194 /* Z F N Q F P -- Convert filename to fully qualified absolute pathname */
7196 static struct zfnfp fnfp = { 0, NULL, NULL };
7199 zfnqfp(fname, buflen, buf) char * fname; int buflen; char * buf; {
7203 char zfntmp[MAXPATHLEN+4];
7205 char zfntmp[CKMAXPATH+4];
7206 #endif /* MAXPATHLEN */
7209 int i = 0, j = 0, k = 0, x = 0, y = 0;
7220 /* Initialize the data structure */
7222 fnfp.len = ckstrncpy(buf,fname,buflen);
7226 debug(F111,"zfnqfp fname",fname,len);
7229 if (*s == '~') { /* Starts with tilde? */
7231 xp = tilde_expand(s); /* Attempt to expand tilde */
7232 debug(F110,"zfnqfp xp",xp,0); /* (realpath() doesn't do this) */
7241 /* N.B.: The realpath() result buffer MUST be MAXPATHLEN bytes long */
7242 /* otherwise we write over memory. */
7244 if (!realpath(s,zfntmp)) {
7245 debug(F111,"zfnqfp realpath fails",s,errno);
7247 if (errno != ENOENT)
7250 /* If realpath() fails use the do-it-yourself method */
7253 #endif /* COMMENT */
7255 len = strlen(zfntmp);
7257 debug(F111,"zfnqfp result too long",ckitoa(buflen),len);
7260 ckstrncpy(buf,zfntmp,buflen);
7262 if (buf[len-1] != '/') {
7263 if ((itsadir = isdir(buf)) && len < (buflen - 1)) {
7270 debug(F110,"zfnqfp realpath path",fnfp.fpath,0);
7271 tmp = buf + fnfp.len - 1;
7273 while (tmp >= buf) {
7275 fnfp.fname = tmp + 1;
7276 debug(F110,"zfnqfp realpath name",fnfp.fname,0);
7284 #endif /* CKREALPATH */
7289 while (*s) { /* Remove leading "./" (0 or more) */
7290 debug(F110,"zfnqfp while *s",s,0);
7291 if (*s == '.' && *(s+1) == '/') {
7293 while (*s == '/') s++;
7297 if (!*s) return(NULL);
7298 if (*s == '/') { /* Pathname is absolute */
7299 ckstrncpy(buf,s,len);
7302 } else { /* Pathname is relative */
7304 if (p = zgtdir()) { /* So get current directory */
7305 debug(F110,"zfnqfp zgtdir",p,0);
7306 x = ckstrncpy(buf,p,len);
7308 debug(F110,"zfnqfp buf 1",buf,0);
7309 len -= x; /* How much room left in buffer */
7310 if ((y = (int)strlen(s)) > len) /* If enough room... */
7312 ckstrncpy(buf+x,s,len); /* ... append the filename */
7313 debug(F110,"zfnqfp buf 2",buf,0);
7319 /* Buf now holds full path but maybe containing some . or .. tricks */
7321 j = x + y; /* Length of what's in buf */
7323 debug(F101,"zfnqfp len","",len);
7325 /* Catch dangling "/." or "/.." */
7326 if ((j > 1 && buf[j-1] == '.' && buf[j-2] == '/') ||
7327 (j > 2 && buf[j-1] == '.' && buf[j-2] == '.' && buf[j-3] == '/')) {
7328 if (j < buflen - 2) {
7333 j = -1; /* j = position of rightmost "/" */
7334 i = 0; /* i = destination index */
7335 tmp[i] = NUL; /* destination is temporary buffer */
7337 for (x = 0; x < len; x++) { /* x = source index */
7338 if (buf[x] == '/') {
7339 for (k = 0; k < 4; k++) {
7344 if (!strncmp(sb,"/./",3)) { /* Eliminate "./" in "/./" */
7347 } else if (!strncmp(sb,"//",2)) { /* Change "//" to "/" */
7349 } else if (!strncmp(sb,"/../",4)) { /* ".." in path */
7350 for (k = i - 1; k >= 0; k--) { /* Back up one level */
7351 if (tmp[k] == '/') {
7361 if (i >= (buflen - 1)) {
7362 debug(F111,"zfnqfp overflow",tmp,i);
7365 tmp[i++] = buf[x]; /* Regular character, copy */
7367 if (buf[x] == '/') /* Remember rightmost "/" */
7370 ckstrncpy(buf,tmp,buflen-1); /* Copy the result back */
7372 buf[buflen-1] = NUL;
7373 if (!buf[0]) { /* If empty, say root */
7379 if ((itsadir = isdir(buf))) {
7380 if (buf[i-1] != '/' && i < (buflen - 1)) {
7385 if (!itsadir && (j > -1)) { /* Set pointer to basename */
7386 fnfp.fname = (char *)(buf + j);
7387 fnfp.fpath = (char *)buf;
7389 debug(F111,"zfnqfp path",fnfp.fpath,i);
7390 debug(F110,"zfnqfp name",fnfp.fname,0);
7396 /* Z C M P F N -- Compare two filenames */
7398 /* Returns 1 if the two names refer to the same existing file, 0 otherwise. */
7401 zcmpfn(s1,s2) char * s1, * s2; {
7402 char buf1[CKMAXPATH+1];
7403 char buf2[CKMAXPATH+1];
7406 char linkname[CKMAXPATH+1];
7408 #endif /* USE_LSTAT */
7413 if (!*s1 || !*s2) return(0);
7415 #ifdef CKSYMLINK /* We're doing symlinks? */
7416 #ifdef USE_LSTAT /* OK to use lstat()? */
7418 if (x > -1 && /* Now see if it's a symlink */
7420 S_ISLNK(buf.st_mode)
7423 ((_IFMT & buf.st_mode) == _IFLNK)
7425 #endif /* S_ISLNK */
7427 linkname[0] = '\0'; /* Get the name */
7428 x = readlink(s1,linkname,CKMAXPATH);
7429 if (x > -1 && x < CKMAXPATH) { /* It's a link */
7434 #endif /* USE_LSTAT */
7435 #endif /* CKSYMLINK */
7437 if (zfnqfp(s1,CKMAXPATH,buf1)) { /* Convert to full pathname */
7439 #ifdef CKSYMLINK /* Same deal for second name... */
7444 S_ISLNK(buf.st_mode)
7447 ((_IFMT & buf.st_mode) == _IFLNK)
7449 #endif /* S_ISLNK */
7452 x = readlink(s2,linkname,CKMAXPATH);
7453 if (x > -1 && x < CKMAXPATH) {
7458 #endif /* USE_LSTAT */
7459 #endif /* CKSYMLINK */
7460 if (zfnqfp(s2,CKMAXPATH,buf2)) {
7461 debug(F110,"zcmpfn s1",buf1,0);
7462 debug(F110,"zcmpfn s2",buf2,0);
7463 if (!strncmp(buf1,buf2,CKMAXPATH))
7467 debug(F101,"zcmpfn result","",rc);
7473 /* User-mode chroot() implementation */
7476 zsetroot(s) char * s; { /* Sets the root */
7477 char buf[CKMAXPATH+1];
7479 if (!*s) return(-1);
7480 debug(F110,"zsetroot",s,0);
7481 if (!isdir(s)) return(-2);
7482 if (!zfnqfp(s,CKMAXPATH,buf)) /* Get full, real path */
7484 if (access(buf,R_OK) < 0) { /* Check access */
7485 debug(F110,"zsetroot access denied",buf,0);
7489 if (ckrootset) { /* If root already set */
7490 if (!zinroot(s)) { /* make sure new root is in it */
7491 debug(F110,"zsetroot new root not in root",ckroot,0);
7495 if (zchdir(buf) < 1) return(-4); /* Change directory to new root */
7496 ckrootset = ckstrncpy(ckroot,buf,CKMAXPATH); /* Now set the new root */
7497 if (ckroot[ckrootset-1] != '/') {
7498 ckroot[ckrootset++] = '/';
7499 ckroot[ckrootset] = '\0';
7501 debug(F111,"zsetroot rootset",ckroot,ckrootset);
7502 ckrooterr = 0; /* Reset error flag */
7507 zgetroot() { /* Returns the root */
7510 return((char *)ckroot);
7514 zinroot(s) char * s; { /* Checks if file s is in the root */
7516 struct zfnfp * f = NULL;
7517 char buf[CKMAXPATH+2];
7519 debug(F111,"zinroot setroot",ckroot,ckrootset);
7520 ckrooterr = 0; /* Reset global error flag */
7521 if (!ckrootset) /* Root not set */
7522 return(1); /* so it's ok - no need to check */
7523 if (!(f = zfnqfp(s,CKMAXPATH,buf))) /* Get full and real pathname */
7524 return(0); /* Fail if we can't */
7525 n = f->len; /* Length of full pathname */
7526 debug(F111,"zinroot n",buf,n);
7527 if (n < (ckrootset - 1) || n > CKMAXPATH) { /* Bad length */
7528 ckrooterr = 1; /* Fail */
7531 if (isdir(buf) && buf[n-1] != '/') { /* If it's a directory name */
7532 buf[n++] = '/'; /* make sure it ends with '/' */
7535 x = strncmp(buf,ckroot,ckrootset); /* Compare, case-sensitive */
7536 debug(F111,"zinroot checked",buf,x);
7537 if (x == 0) /* OK */
7539 ckrooterr = 1; /* Not OK */
7546 The following code provides support for user login and logout
7547 including anonymous accounts. If this feature is to be supported
7548 outside of UNIX, it should be spread out among the ck?fio.c modules...
7550 #ifndef _PATH_BSHELL
7551 #define _PATH_BSHELL "/usr/bin/bash"
7552 #endif /* _PATH_BSHELL */
7553 #ifndef _PATH_FTPUSERS
7554 #define _PATH_FTPUSERS "/etc/ftpusers"
7555 #endif /* _PATH_FTPUSERS */
7558 * Helper function for sgetpwnam().
7561 sgetsave(s) char *s; {
7562 char *new = malloc((unsigned) strlen(s) + 1);
7564 printf("?Local resource failure: malloc\n");
7568 (void) strcpy(new, s); /* safe */
7573 * Save the result of getpwnam(). Used for USER command, since
7574 * the data returned must not be clobbered by any other command
7578 sgetpwnam(name) char *name; {
7579 static struct passwd save;
7580 register struct passwd *p;
7582 register struct spwd *sp;
7583 #endif /* CK_SHADOW */
7586 #ifdef HPUX10_TRUSTED
7587 struct pr_passwd *pr;
7588 #endif /* HPUX10_TRUSTED */
7591 sp = getspnam(name);
7592 debug(F111,"sgetpwnam","getspnam()",sp);
7595 #endif /* CK_SHADOW */
7597 #ifdef HPUX10_TRUSTED
7598 if ((pr = getprpwnam(name)) == NULL)
7600 #endif /* HPUX10_TRUSTED */
7603 debug(F111,"sgetpwnam","getpwnam()",p);
7608 free(save.pw_passwd);
7609 free(save.pw_gecos);
7611 free(save.pw_shell);
7614 save.pw_name = sgetsave(p->pw_name);
7616 save.pw_passwd = sgetsave(sp->sp_pwdp);
7617 #else /* CK_SHADOW */
7618 #ifdef HPUX10_TRUSTED
7619 if (pr->uflg.fg_encrypt && pr->ufld.fd_encrypt && *pr->ufld.fd_encrypt)
7620 save.pw_passwd = sgetsave(pr->ufld.fd_encrypt);
7622 save.pw_passwd = sgetsave("");
7623 #else /* HPUX10_TRUSTED */
7624 save.pw_passwd = sgetsave(p->pw_passwd);
7625 #endif /* HPUX10_TRUSTED */
7626 #endif /* CK_SHADOW */
7627 save.pw_gecos = sgetsave(p->pw_gecos);
7628 save.pw_dir = sgetsave(p->pw_dir);
7629 save.pw_shell = sgetsave(p->pw_shell);
7633 #define CKXLOGBSIZ 256
7635 struct passwd * pw = NULL;
7636 char * home = NULL; /* Home directory pointer for glob */
7643 int defumask = CMASK; /* Default umask value */
7645 /* Z V U S E R -- Verify user, Returns 1 if user OK, 0 otherwise. */
7647 /* Sets global passwd pointer pw if named account exists and is acceptable;
7648 * sets askpasswd if a PASS command is expected. If logged in previously,
7649 * need to reset state. If name is "ftp" or "anonymous", the name is not in
7650 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
7651 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
7652 * requesting login privileges. Disallow anyone who does not have a standard
7653 * shell as returned by getusershell(). Disallow anyone mentioned in the file
7654 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
7656 _PROTOTYP(static int checkuser, (char *) );
7658 char zvuname[64] = { NUL, NUL };
7659 char zvhome[CKMAXPATH+1] = { NUL, NUL };
7661 #define ZENVHOME CKMAXPATH+12
7662 #define ZENVLOGNAME 74
7663 static char zenvuser[ZENVUSER];
7664 static char zenvhome[ZENVHOME];
7665 static char zenvlogname[ZENVLOGNAME];
7668 static char pam_data[500];
7669 struct pam_conv pam_conv = {pam_cb, pam_data}; /* PAM structure */
7670 struct pam_handle * pamh = NULL; /* PAM reference handle */
7674 zvuser(name) char *name; {
7675 register char *cp = NULL;
7679 char *getusershell();
7680 #endif /* GETUSERSHELL */
7684 const char * reply = NULL;
7687 debug(F111,"user",name,logged_in);
7689 if (!name) name = "";
7692 debug(F101,"zvuser ckxsyslog","",ckxsyslog);
7695 debug(F100,"zvuser CKSYSLOG defined","",0);
7696 #endif /* CKSYSLOG */
7698 if (logged_in) /* Should not be called if logged in */
7702 if (ckxsyslog && ckxlogging) {
7704 "login: user %s",name
7707 #endif /* CKSYSLOG */
7709 guest = 0; /* Assume not guest */
7712 if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
7713 debug(F101,"zvuser anonymous ckxanon","",ckxanon);
7714 if (!ckxanon) { /* Anonymous login not allowed */
7716 if (ckxsyslog && ckxlogging) {
7718 "login: anonymous login not allowed: %s",
7719 clienthost ? clienthost : "(unknown host)"
7722 #endif /* CKSYSLOG */
7725 if (checkuser("ftp") || checkuser("anonymous")) {
7726 debug(F100,"zvuser anon forbidden by ftpusers file","",0);
7728 if (ckxsyslog && ckxlogging) {
7730 "login: anonymous login forbidden by ftpusers file: %s",
7731 clienthost ? clienthost : "(unknown host)"
7734 #endif /* CKSYSLOG */
7736 } else if ((pw = sgetpwnam("ftp")) != NULL) {
7737 debug(F100,"zvuser anon sgetpwnam(ftp) OK","",0);
7740 ckstrncpy(zvuname,"anonymous",64);
7743 debug(F100,"zvuser anon sgetpwnam(ftp) FAILED","",0);
7745 if (ckxsyslog && ckxlogging) {
7747 "login: anonymous getpwnam(ftp) failed: %s",
7748 clienthost ? clienthost : "(unknown host)"
7751 #endif /* CKSYSLOG */
7755 pw = sgetpwnam(name);
7758 Of course some UNIX platforms (like AIX) don't have getusershell().
7759 In that case we can't check if the user's account has been "turned off"
7760 or somesuch, e.g. by setting their shell to "/etc/nologin" or somesuch,
7761 which runs (usually just printing a message and exiting), but which is
7762 not listed in /etc/shells. For that matter, if getusershell() is not
7763 available, then probably neither is /etc/shells.
7765 debug(F100,"zvuser sgetpwnam ok","",0);
7766 shell = pw->pw_shell;
7767 if (!shell) shell = "";
7769 shell = _PATH_BSHELL;
7770 debug(F110,"zvuser shell",shell,0);
7772 while ((cp = getusershell()) != NULL) {
7773 debug(F110,"zvuser getusershell",cp,0);
7774 if (strcmp(cp, shell) == 0)
7777 debug(F100,"zvuser endusershell 1","",0);
7779 debug(F100,"zvuser endusershell 2","",0);
7780 #else /* GETUSERSHELL */
7781 cp = ""; /* Do not refuse if we cannot check */
7782 #endif /* GETUSERSHELL */
7783 x = checkuser(name);
7784 debug(F101,"zvuser checkuser","",x);
7786 debug(F100,"zvuser refused 1","",0);
7787 pw = (struct passwd *) NULL;
7789 if (ckxsyslog && ckxlogging) {
7791 "login: invalid shell %s for %s %s",shell, name,
7792 clienthost ? clienthost : "(unknown host)"
7795 #endif /* CKSYSLOG */
7798 debug(F100,"zvuser refused 2","",0);
7799 pw = (struct passwd *) NULL;
7801 if (ckxsyslog && ckxlogging) {
7803 "login: %s login forbidden by ftpusers file: %s",
7804 name, clienthost ? clienthost : "(unknown host)"
7807 #endif /* CKSYSLOG */
7812 /* Get PAM authentication details */
7813 debug(F110,"zvuser","calling pam_start",0);
7815 pam_start(PAM_SERVICE_TYPE,name,&pam_conv,&pamh))
7817 reply = pam_strerror(NULL, pam_status);
7818 debug(F110,"zvuser PAM failure",reply,0);
7819 printf("%s\n",reply);
7821 if (ckxsyslog && ckxlogging) {
7823 "login: %s refused by PAM \"%s\": %s",
7825 clienthost ? clienthost : "(unknown host)"
7828 #endif /* CKSYSLOG */
7833 ckstrncpy(zvuname,name,64);
7838 debug(F100,"zvuser sgetpwnam NULL","",0);
7840 if (ckxsyslog && ckxlogging) {
7842 "login: getpwnam(%s) failed: %s",name,
7843 clienthost ? clienthost : "(unknown host)"
7846 #endif /* CKSYSLOG */
7851 if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) {
7853 /* Why sprintf and then printf? */
7854 /* Also, what is kerb_ok? And is the test on it right? */
7855 char buf[CKXLOGBSIZ];
7856 sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s%s",
7857 kdata.pname, *kdata.pinst ? "." : "",
7858 kdata.pinst, kdata.prealm,
7859 (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not",
7860 name, kerb_ok ? "" : "; Password required.");
7863 printf("Kerberos user %s%s%s@%s is%s authorized as %s%s",
7864 kdata.pname, *kdata.pinst ? "." : "",
7865 kdata.pinst, kdata.prealm,
7866 (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not",
7867 name, kerb_ok ? "" : "; Password required.");
7868 #endif /* COMMENT */
7869 if (kerb_ok) return(1);
7872 #endif /* FTP_KERBEROS */
7875 /* Check if the given user is in the forbidden-user file */
7878 checkuser(name) char *name; {
7879 extern char * userfile;
7882 char line[CKXLOGBSIZ+1];
7887 debug(F111,"checkuser name",name,i);
7891 fd = fopen(userfile ? userfile : _PATH_FTPUSERS, "r");
7892 debug(F111,"checkuser userfile",userfile,fd);
7895 while (fgets(line, sizeof(line), fd)) {
7896 debug(F110,"checkuser line",line,0);
7899 if (strncmp(line, name, i) == 0) {
7900 debug(F110,"checkuser REFUSED",name,0);
7907 debug(F110,"checkuser OK",name,0);
7911 /* Z V L O G O U T -- Log out from Internet Kermit Service */
7916 /* This could be dangerous */
7917 if (setuid((UID_T)0) < 0) {
7918 debug(F100,"zvlogout setuid FAILED","",0);
7921 debug(F100,"zvlogout setuid OK","",0);
7922 #endif /* COMMENT */
7924 if (ckxsyslog >= SYSLG_LI && ckxlogging) {
7925 cksyslog(SYSLG_LI, 1, "logout",(char *) uidbuf, clienthost);
7927 #endif /* CKSYSLOG */
7929 debug(F110,"WTMP logout",cksysline,logged_in);
7931 logwtmp(cksysline, "", "");
7940 kpass(name, p) char *name, *p; {
7941 char instance[INST_SZ];
7942 char realm[REALM_SZ];
7946 unsigned long faddr;
7949 if (krb_get_lrealm(realm, 1) != KSUCCESS)
7952 ckstrncpy(tkt_file, TKT_ROOT, 20);
7953 ckstrncat(tkt_file, "_ftpdXXXXXX", 20);
7954 krb_set_tkt_string(mktemp(tkt_file));
7956 (VOID) ckstrncpy(instance, krb_get_phost(hostname), sizeof(instance));
7958 if ((hp = gethostbyname(instance)) == NULL)
7962 hp = ck_copyhostent(hp); /* safe copy that won't change */
7963 #endif /* HADDRLIST */
7964 bcopy((char *)hp->h_addr, (char *) &faddr, sizeof(faddr));
7966 if (krb_get_pw_in_tkt(name, "", realm, "krbtgt", realm, 1, p) ||
7967 krb_mk_req(&ticket, "rcmd", instance, realm, 33) ||
7968 krb_rd_req(&ticket, "rcmd", instance, faddr, &authdata, "") ||
7969 kuserok(&authdata, name)) {
7976 #endif /* FTP_KERBEROS */
7981 if (ckxsyslog && !ckxlogging) {
7983 openlog(inserver ? "iksd" : "ckermit", LOG_PID, LOG_DAEMON);
7985 openlog(inserver ? "iksd" : "ckermit", LOG_PID);
7986 #endif /* LOG_DAEMON */
7988 debug(F100,"zsyslog syslog opened","",0);
7990 #endif /* CKSYSLOG */
7993 /* Z V P A S S -- Verify password; returns 1 if OK, 0 otherwise */
7997 #endif /* AUTH_USER */
7999 #define AUTH_VALID 4
8000 #endif /* AUTH_VALID */
8003 zvpass(p) char *p; {
8004 char *xpasswd, *salt;
8008 const char * reply = NULL;
8011 if (logged_in || askpasswd == 0) {
8014 debug(F111,"zvpass",p ? (guest ? p : "xxxxxx") : "(null)",guest);
8017 if (guest && !*p) { /* Guests must specify a password */
8019 if (ckxsyslog && ckxlogging) {
8021 "login: anonymous guests must specify a password"
8024 #endif /* CKSYSLOG */
8028 #ifdef CK_AUTHENTICATION
8029 && ck_tn_auth_valid() != AUTH_VALID
8030 #endif /* CK_AUTHENTICATION */
8031 ) { /* "ftp" is only account allowed no password */
8033 debug(F110,"zvpass","calling pam_set_item(AUTHTOK)",0);
8034 if ((pam_status = pam_set_item(pamh,PAM_AUTHTOK,p)) != PAM_SUCCESS) {
8035 reply = pam_strerror(pamh, pam_status);
8036 debug(F110,"zvpass PAM failure",reply,0);
8037 /* if no password given treat as non-fatal error */
8038 /* pam will prompt for password in pam_authenticate() */
8040 printf("%s\n",reply);
8042 debug(F100,"zvpass denied","",0);
8048 debug(F110,"zvpass","calling pam_authenticate",0);
8052 #endif /* CK_LOGIN */
8055 if ((pam_status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
8056 reply = pam_strerror(pamh, pam_status);
8057 debug(F110,"zvpass PAM failure",reply,0);
8058 printf("%s\n",reply);
8060 debug(F100,"zvpass denied","",0);
8067 debug(F110,"zvpass","calling pam_acct_mgmt",0);
8068 if ((pam_status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
8069 reply = pam_strerror(pamh, pam_status);
8070 debug(F110,"zvpass PAM failure",reply,0);
8071 printf("%s\n",reply);
8073 debug(F100,"zvpass denied","",0);
8078 debug(F110,"zvpass","PAM validates OK",0);
8084 salt = pw->pw_passwd;
8086 #ifdef HPUX10_TRUSTED
8087 xpasswd = bigcrypt(p, salt);
8090 On 64-bit platforms this can give "cast to pointer from integer of
8091 different size" warning, but I'm not sure what the effect is at runtime,
8092 or what to do about it.
8094 xpasswd = (char *)crypt(p, salt);
8095 #endif /* HPUX10_TRUSTED */
8099 /* null pw_passwd ok if Kerberos password ok */
8101 ((*pw->pw_passwd != '\0' ||
8102 strcmp(xpasswd, pw->pw_passwd))
8103 && !kpass(pw->pw_name, p))
8106 /* check with tpasswd first if there */
8107 pw == NULL || *pw->pw_passwd == '\0' ||
8108 t_verifypw (pw->pw_name, p) == 0 ||
8109 (t_verifypw (pw->pw_name, p) < 0 &&
8110 strcmp (xpasswd, pw->pw_passwd))
8112 /* The strcmp does not catch null passwords! */
8113 (pw == NULL) || (*pw->pw_passwd == '\0') ||
8114 strcmp(xpasswd, pw->pw_passwd)
8116 #endif /* FTP_KERBEROS */
8118 debug(F100,"zvpass denied","",0);
8126 (VOID) setgid((GID_T)pw->pw_gid); /* Set group ID */
8128 #ifndef NOINITGROUPS
8129 (VOID) initgroups(pw->pw_name, pw->pw_gid);
8130 #endif /* NOINITGROUPS */
8136 /* Open wtmp before chroot */
8138 sprintf(cksysline,"iks_%04x", getpid()); /* safe */
8139 logwtmp(cksysline, pw->pw_name,
8140 clienthost ? clienthost : "(unknown host)"
8142 debug(F110,"WTMP login",cksysline,logged_in);
8146 For anonymous users, we chroot to user ftp's home directory unless
8147 started with --anonroot:xxx, in which case we chroot to xxx. We must
8148 immediately chdir() to the same directory we chroot() to or else the
8149 old current directory remains accessible as "." outside the new root.
8152 if (anonroot) /* Non-default anonymous root */
8155 makestr(&anonroot,dir);
8157 debug(F110,"zvpass anon chroot",dir,0);
8158 if (chroot(dir) < 0) {
8159 debug(F111,"zvpass anon chroot FAILED",dir,errno);
8163 if (chdir("/") < 0) {
8164 debug(F111,"zvpass anon chdir FAILED",dir,errno);
8167 debug(F110,"zvpass anon chroot/chdir OK",dir,0);
8168 } else if (chdir(dir) < 0) { /* Not guest */
8170 if (chdir("/") < 0) {
8171 debug(F110,"Non-guest chdir FAILED",dir,0);
8174 printf("?No directory! Logging in with home=/\n");
8176 debug(F110,"zvpass non-guest chdir FAILED",dir,0);
8177 goto bad; /* Be conservative at first */
8178 #endif /* COMMENT */
8180 debug(F110,"zvpass non-guest chdir OK",dir,0);
8181 if (setuid((UID_T)pw->pw_uid) < 0) {
8182 debug(F101,"zvpass setuid FAILED","",pw->pw_uid);
8185 debug(F101,"zvpass setuid OK","",pw->pw_uid);
8187 guestpass[0] = '\0';
8191 fncact = XYFX_R; /* FILE COLLISION = RENAME */
8192 debug(F110,"GUEST fncact=R",p,0);
8193 lset(guestpass,"anonymous:",10,32);
8194 ckstrncpy(&guestpass[10],p,GUESTPASS-10);
8196 printf("Anonymous login.\r\n");
8199 /* proctitle declared where? Obviously this code is never compiled. */
8200 sprintf(proctitle, "%s: anonymous/%.*s",
8201 clienthost ? clienthost : "(unk)",
8202 sizeof(proctitle) - sizeof(clienthost) -
8203 sizeof(": anonymous/"), p);
8204 setproctitle(proctitle);
8205 #endif /* SETPROCTITLE */
8208 if (ckxsyslog && ckxlogging) {
8210 "login: anonymous %s %s",
8211 clienthost ? clienthost : "(unknown host)",
8215 #endif /* CKSYSLOG */
8217 } else { /* Real user */
8220 ckstrncpy(guestpass,zvuname,GUESTPASS);
8222 printf("User %s logged in.\r\n", pw->pw_name);
8225 sprintf(proctitle, "%s: %s",
8226 clienthost ? clienthost : "(unk)",
8229 setproctitle(proctitle);
8230 #endif /* SETPROCTITLE */
8233 if (ckxsyslog && ckxlogging)
8234 syslog(LOG_INFO, "login: %s %s",
8236 clienthost ? clienthost : "(unknown host)"
8238 #endif /* CKSYSLOG */
8240 ckstrncpy(zvhome,home,CKMAXPATH); /* Set environment variables */
8243 ckmakmsg(zenvuser,ZENVUSER,"USER=",zvuname,NULL,NULL);
8244 putenv((char *)zenvuser);
8245 ckmakmsg(zenvlogname,ZENVLOGNAME,"LOGNAME=",zvuname,NULL,NULL);
8246 putenv((char *)zenvlogname);
8247 ckmakmsg(zenvhome,ZENVHOME,"HOME=",zvhome,NULL,NULL);
8248 putenv((char *)zenvhome);
8249 #endif /* NOPUTENV */
8250 /* homdir = (char *)zvhome; */
8251 ckstrncpy((char *)uidbuf,(char *)zvuname,64);
8252 (VOID) umask(defumask);
8257 extern char dbrec[];
8258 extern unsigned long myflags;
8259 extern unsigned int mydbslot;
8260 extern struct iksdbfld dbfld[];
8261 #ifdef CK_AUTHENTICATION
8262 extern unsigned long myamode, myatype;
8263 #endif /* CK_AUTHENTICATION */
8264 myflags |= DBF_LOGGED;
8267 debug(F101,"zvpass guest","",guest);
8268 debug(F111,"zvpass zvuname",zvuname,0);
8269 debug(F110,"zvpass guestpass",guestpass,0);
8270 debug(F110,"zvpass dir",dir,0);
8271 debug(F110,"zvpass home",home,0);
8272 debug(F110,"zvpass anonroot",anonroot,0);
8275 p2 = guest ? guestpass : zvuname;
8277 p2 = (char *)guestpass;
8278 myflags &= ~DBF_USER;
8280 p2 = (char *)zvuname;
8281 myflags |= DBF_USER;
8284 strncpy(&dbrec[DB_ULEN],ulongtohex((unsigned long)k,4),4);
8285 lset(&dbrec[dbfld[db_USER].off],p2,1024,32);
8286 strncpy(&dbrec[DB_FLAGS],ulongtohex(myflags,4),4);
8287 #ifdef CK_AUTHENTICATION
8288 myamode = ck_tn_auth_valid();
8289 strncpy(&dbrec[DB_AMODE],ulongtohex(myamode,4),4);
8290 myatype = ck_tn_authenticated();
8291 strncpy(&dbrec[DB_ATYPE],ulongtohex(myatype,4),4);
8292 #endif /* CK_AUTHENTICATION */
8298 if (!*p2) p2 = home;
8300 strncpy(&dbrec[DB_DLEN],
8301 ulongtohex((unsigned long)strlen(p2),4),
8304 lset(&dbrec[dbfld[db_DIR].off],p2,1024,32);
8310 bad: /* Common failure exit */
8315 #endif /* CK_LOGIN */
8317 /* Buggy Xenix 2.3.4 cc needs this line after the endif */