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, 9.0.216, 20 Aug 2011";
9 char *ckzv = "Plan 9 File support, 9.0.216, 20 Aug 2011";
11 char *ckzv = "UNIX File support, 9.0.216, 20 Aug 2011";
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. Note: AcIS = Previous
19 of Columbia University Information Technology.
21 Copyright (C) 1985, 2011,
22 Trustees of Columbia University in the City of New York.
23 All rights reserved. See the C-Kermit COPYING.TXT file or the
24 copyright text in the ckcmai.c module for disclaimer and permissions.
28 NOTE TO CONTRIBUTORS: This file, and all the other C-Kermit files, must be
29 compatible with C preprocessors that support only #ifdef, #else, #endif,
30 #define, and #undef. Please do not use #if, logical operators, or other
31 preprocessor features in any of the portable C-Kermit modules. You can,
32 of course, use these constructions in platform-specific modules where you
33 know they are supported.
49 /* To avoid pulling in all of ckuusr.h so we copy the few needed prototypes */
51 struct mtab { /* Macro table, like keyword table */
52 char *kwd; /* But with pointers for vals */
53 char *mval; /* instead of ints. */
56 _PROTOTYP( int mlook, (struct mtab [], char *, int) );
57 _PROTOTYP( int dodo, (int, char *, int) );
58 _PROTOTYP( int parser, ( int ) );
61 /* This causes trouble in C-Kermit 8.0. I don't remember the original */
62 /* reason for this being here but it must have been needed at the time... */
67 #endif /* _NO_PROTO */
73 #include <errno.h> /* Error number symbols */
75 #ifndef ERRNO_INCLUDED
76 #include <errno.h> /* Error number symbols */
77 #endif /* ERRNO_INCLUDED */
78 #endif /* HPUXPRE65 */
92 #include <sys/types.h>
104 Directory Separator macros, to allow this module to work with both UNIX and
105 OS/2: Because of ambiguity with the command line editor escape \ character,
106 the directory separator is currently left as / for OS/2 too, because the
107 OS/2 kernel also accepts / as directory separator. But this is subject to
108 change in future versions to conform to the normal OS/2 style.
114 #define ISDIRSEP(c) ((c)=='/')
115 #endif /* ISDIRSEP */
122 #include <sys/ndir.h>
126 #else /* !NDIR, !XNDIR */
128 #include "/usr/lib/ndir.h"
129 #else /* !RTU, !NDIR, !XNDIR */
132 #include <sys/dirent.h>
143 #ifdef UNIX /* Pointer arg to wait() allowed */
144 #define CK_CHILD /* Assume this is safe in all UNIX */
147 extern int binary, recursive, stathack;
149 extern int eofmethod;
150 #endif /* CK_CTRLZ */
152 #include <pwd.h> /* Password file for shell name */
154 #include <t_pwd.h> /* SRP Password file */
157 #ifdef HPUX10_TRUSTED
158 #include <hpsecurity.h>
160 #endif /* HPUX10_TRUSTED */
163 /* Moved to ckcdeb.h */
173 #ifdef SYSUTIMEH /* <sys/utime.h> if requested, */
174 #include <sys/utime.h> /* for extra fields required by */
175 #else /* 88Open spec. */
176 #ifdef UTIMEH /* or <utime.h> if requested */
177 #include <utime.h> /* (SVR4, POSIX) */
180 /* Not sure why this is here. What it implies is that the code bracketed
181 by SYSUTIMEH is valid on all platforms on which we support time
182 functionality. But we know that is not true because the BSD44 and V7
183 platforms do not support sys/utime.h and the data structures which
184 are defined in them. Now this worked before because prior to today's
185 changes the UTIMEH definition for BSD44 and V7 did not take place
186 until after SYSUTIMEH was defined. It also would not have been a
187 problem if the ordering of all the time blocks was consistent. All but
188 one of the blocks were BSD44, V7, SYSUTIMEH, <OTHER>. That one case
189 is where this problem was triggered.
191 #define SYSUTIMEH /* Use this for both cases. */
195 #endif /* SYSUTIMEH */
204 #ifdef BSD44 /* BSD 4.4 */
206 #define TIMESTAMP /* Can do file dates */
207 #endif /* TIMESTAMP */
208 #include <sys/time.h>
209 #include <sys/timeb.h>
211 #else /* Not BSD44 */
213 #ifdef BSD4 /* BSD 4.3 and below */
214 #define TIMESTAMP /* Can do file dates */
215 #include <time.h> /* Need this */
216 #include <sys/timeb.h> /* Need this if really BSD */
218 #else /* Not BSD 4.3 and below */
220 #ifdef SVORPOSIX /* System V or POSIX */
223 #endif /* TIMESTAMP */
226 /* void tzset(); (the "void" type upsets some compilers) */
230 /* ConvexOS 9.0, supposedly POSIX, has extern char *timezone(int,int) */
232 extern long timezone;
237 #endif /* SVORPOSIX */
243 #endif /* COHERENT */
245 /* Is `y' a leap year? */
246 #define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
248 /* Number of leap years from 1970 to `y' (not including `y' itself). */
249 #define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
251 #endif /* NOTIMESTAMP */
254 #include <stat.h> /* File status */
256 #include <sys/stat.h>
261 /* Macro to alleviate isdir() calls internal to this module */
263 static struct stat STATBUF;
264 #define xisdir(a) ((stat(a,&STATBUF)==-1)?0:(S_ISDIR(STATBUF.st_mode)?1:0))
266 extern char uidbuf[];
268 extern char * xferfile;
270 static time_t timenow;
272 #define IKSDMSGLEN CKMAXPATH+512
274 static char iksdmsg[IKSDMSGLEN];
278 extern int server, en_mkd, en_cwd, en_del;
281 Functions (n is one of the predefined file numbers from ckcker.h):
283 zopeni(n,name) -- Opens an existing file for input.
284 zopeno(n,name,attr,fcb) -- Opens a new file for output.
285 zclose(n) -- Closes a file.
286 zchin(n,&c) -- Gets the next character from an input file.
287 zsinl(n,&s,x) -- Read a line from file n, max len x, into address s.
288 zsout(n,s) -- Write a null-terminated string to output file, buffered.
289 zsoutl(n,s) -- Like zsout, but appends a line terminator.
290 zsoutx(n,s,x) -- Write x characters to output file, unbuffered.
291 zchout(n,c) -- Add a character to an output file, unbuffered.
292 zchki(name) -- Check if named file exists and is readable, return size.
293 zchko(name) -- Check if named file can be created.
294 zchkspa(name,n) -- Check if n bytes available to create new file, name.
295 znewn(name,s) -- Make a new unique file name based on the given name.
296 zdelet(name) -- Delete the named file.
297 zxpand(string) -- Expands the given wildcard string into a list of files.
298 znext(string) -- Returns the next file from the list in "string".
299 zxrewind() -- Rewind zxpand list.
300 zxcmd(n,cmd) -- Execute the command in a lower fork on file number n.
301 zclosf() -- Close input file associated with zxcmd()'s lower fork.
302 zrtol(n1,n2) -- Convert remote filename into local form.
303 zltor(n1,n2) -- Convert local filename into remote form.
304 zchdir(dirnam) -- Change working directory.
305 zhome() -- Return pointer to home directory name string.
306 zkself() -- Kill self, log out own job.
307 zsattr(struct zattr *) -- Return attributes for file which is being sent.
308 zstime(f, struct zattr *, x) - Set file creation date from attribute packet.
309 zrename(old, new) -- Rename a file.
310 zcopy(source,destination) -- Copy a file.
311 zmkdir(path) -- Create the directory path if possible
312 zfnqfp(fname,len,fullpath) - Determine full path for file name.
313 zgetfs(name) -- return file size regardless of accessibility
314 zchkpid(pid) -- tell if PID is valid and active
317 /* Kermit-specific includes */
319 Definitions here supersede those from system include files.
320 ckcdeb.h is included above.
322 #include "ckcker.h" /* Kermit definitions */
323 #include "ckucmd.h" /* For keyword tables */
324 #include "ckuver.h" /* Version herald */
326 char *ckzsys = HERALD;
329 File access checking ... There are two calls to access() in this module.
330 If this program is installed setuid or setgid on a Berkeley-based UNIX
331 system that does NOT incorporate the saved-original-effective-uid/gid
332 feature, then, when we have swapped the effective and original uid/gid,
333 access() fails because it uses what it thinks are the REAL ids, but we have
334 swapped them. This occurs on systems where ANYBSD is defined, NOSETREU
335 is NOT defined, and SAVEDUID is NOT defined. So, in theory, we should take
336 care of this situation like so:
346 But we can't test such a general scheme everywhere, so let's only do this
347 when we know we have to...
349 #ifdef NEXT /* NeXTSTEP 1.0-3.0 */
353 /* Support for tilde-expansion in file and directory names */
356 #define NAMEENV "LOGNAME"
359 #define NAMEENV "USER"
362 #define NAMEENV "LOGNAME"
367 /* Berkeley Unix Version 4.x */
368 /* 4.1bsd support from Charles E Brooks, EDN-VAX */
373 #endif /* MAXNAMLEN */
376 /* Definitions of some system commands */
378 char *DELCMD = "rm -f "; /* For file deletion */
379 char *CPYCMD = "cp "; /* For file copy */
380 char *RENCMD = "mv "; /* For file rename */
381 char *PWDCMD = "pwd "; /* For saying where I am */
385 char *DIRCMD = "/usr/bin/ls -l "; /* For directory listing */
386 char *DIRCM2 = "/usr/bin/ls -l "; /* For directory listing, no args */
388 char *DIRCMD = "/bin/ls -l "; /* For directory listing */
389 char *DIRCM2 = "/bin/ls -l "; /* For directory listing, no args */
392 char *DIRCMD = "ls -l "; /* For directory listing */
393 char *DIRCM2 = "ls -l "; /* For directory listing, no args */
396 char *TYPCMD = "cat "; /* For typing a file */
399 char *MAILCMD = "mailx"; /* For sending mail */
402 char *MAILCMD = "mailx";
406 char *MAILCMD = CK_MAILCMD; /* CFLAGS override */
408 char *MAILCMD = "Mail"; /* Default */
409 #endif /* CK_MAILCMD */
417 #ifdef ANYBSD /* BSD uses lpr to spool */
418 #ifdef DGUX540 /* And DG/UX */
419 char * PRINTCMD = "lp";
421 char * PRINTCMD = "lpr";
423 #else /* Sys V uses lp */
424 #ifdef TRS16 /* except for Tandy-16/6000... */
425 char * PRINTCMD = "lpr";
427 char * PRINTCMD = "lp";
434 #ifdef FT18 /* Fortune For:Pro 1.8 */
439 char *SPACMD = "pwd ; df ."; /* Space in current directory */
442 char *SPACMD = "pwd ; du ; df .";
444 char *SPACMD = "df ";
448 char *SPACM2 = "df "; /* For space in specified directory */
455 char *WHOCMD = "finger ";
457 char *WHOCMD = "who ";
460 /* More system-dependent includes, which depend on symbols defined */
461 /* in the Kermit-specific includes. Oh what a tangled web we weave... */
463 #ifdef COHERENT /* <sys/file.h> */
465 #endif /* COHERENT */
480 #include <sys/file.h>
483 #ifndef is68k /* Whether to include <fcntl.h> */
484 #ifndef BSD41 /* All but a couple UNIXes have it. */
488 #endif /* COHERENT */
497 #include <sys/fcntl.h>
499 #endif /* COHERENT */
501 extern int inserver; /* I am IKSD */
502 int guest = 0; /* Anonymous user */
506 extern char * anonroot;
510 #define GUESTPASS 256
511 static char guestpass[GUESTPASS] = { NUL, NUL }; /* Anonymous "password" */
512 static int logged_in = 0; /* Set when user is logged in */
513 static int askpasswd = 0; /* Have OK user, must ask for passwd */
515 extern int gotemptypasswd;
517 #endif /* CK_LOGIN */
520 static char ckroot[CKMAXPATH+1] = { NUL, NUL };
521 static int ckrootset = 0;
525 _PROTOTYP( VOID ignorsigs, (void) );
526 _PROTOTYP( VOID restorsigs, (void) );
528 _PROTOTYP( int ttwait, (int, int) ); /* ckutio.c */
532 Change argument to "(const char *)" if this causes trouble.
533 Or... if it causes trouble, then maybe it was already declared
534 in a header file after all, so you can remove this prototype.
536 #ifndef NDGPWNAM /* If not defined No Declare getpwnam... */
537 #ifndef _POSIX_SOURCE
540 /* POSIX <pwd.h> already gave prototypes for these. */
542 _PROTOTYP( struct passwd * getpwnam, (const char *) );
545 _PROTOTYP( struct passwd * getpwnam, (const char *) );
548 _PROTOTYP( struct passwd * getpwnam, (const char *) );
551 _PROTOTYP( struct passwd * getpwnam, (const char *) );
554 _PROTOTYP( struct passwd * getpwnam, (const char *) );
557 _PROTOTYP( struct passwd * getpwnam, (const char *) );
559 _PROTOTYP( struct passwd * getpwnam, (char *) );
560 #endif /* DCGPWNAM */
570 _PROTOTYP( struct passwd * getpwuid, (PWID_T) );
575 _PROTOTYP( struct passwd * getpwent, (void) );
578 #endif /* _POSIX_SOURCE */
579 #endif /* NDGPWNAM */
581 #ifdef CK_SHADOW /* Shadow Passwords... */
583 #endif /* CK_SHADOW */
584 #ifdef CK_PAM /* PAM... */
586 #include <pam/pam_appl.h>
588 #include <security/pam_appl.h>
590 #ifndef PAM_SERVICE_TYPE /* Defines which PAM service we are */
591 #define PAM_SERVICE_TYPE "kermit"
592 #endif /* PAM_SERVICE_TYPE */
597 #define PAM_CONST CONST
600 static char * pam_pw = NULL;
605 PAM_CONST struct pam_message **msg,
606 struct pam_response **resp,
610 pam_cb(num_msg, msg, resp, appdata_ptr)
612 PAM_CONST struct pam_message **msg;
613 struct pam_response **resp;
615 #endif /* CK_ANSIC */
619 debug(F111,"pam_cb","num_msg",num_msg);
621 for (i = 0; i < num_msg; i++) {
622 char message[PAM_MAX_MSG_SIZE];
624 /* Issue prompt and get response */
625 debug(F111,"pam_cb","Message",i);
626 debug(F111,"pam_cb",msg[i]->msg,msg[i]->msg_style);
627 if (msg[i]->msg_style == PAM_ERROR_MSG) {
628 debug(F111,"pam_cb","PAM ERROR",0);
629 fprintf(stdout,"%s\n", msg[i]->msg);
631 } else if (msg[i]->msg_style == PAM_TEXT_INFO) {
632 debug(F111,"pam_cb","PAM TEXT INFO",0);
633 fprintf(stdout,"%s\n", msg[i]->msg);
635 } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) {
636 debug(F111,"pam_cb","Reading response, no echo",0);
637 /* Ugly hack. We check to see if a password has been pushed */
638 /* into zvpasswd(). This would be true if the password was */
639 /* received by REMOTE LOGIN. */
641 ckstrncpy(message,pam_pw,PAM_MAX_MSG_SIZE);
643 readpass((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
644 } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON) {
645 debug(F111,"pam_cb","Reading response, with echo",0);
646 readtext((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
648 debug(F111,"pam_cb","unknown style",0);
652 /* Allocate space for this message's response structure */
653 resp[i] = (struct pam_response *) malloc(sizeof (struct pam_response));
656 debug(F110,"pam_cb","malloc failure",0);
657 for (j = 0; j < i; j++) {
664 /* Allocate a buffer for the response */
665 resp[i]->resp = (char *) malloc((int)strlen(message) + 1);
666 if (!resp[i]->resp) {
668 debug(F110,"pam_cb","malloc failure",0);
669 for (j = 0; j < i; j++) {
676 /* Return the results back to PAM */
677 strcpy(resp[i]->resp, message); /* safe (prechecked) */
678 resp[i]->resp_retcode = 0;
680 debug(F110,"pam_cb","Exiting",0);
685 /* Define macros for getting file type */
689 Olivetti X/OS 2.3 has S_ISREG and S_ISDIR defined
690 incorrectly, so we force their redefinition.
696 #ifdef UTSV /* Same deal for Amdahl UTSV */
701 #ifdef UNISYS52 /* And for UNISYS UTS V 5.2 */
704 #endif /* UNISYS52 */
706 #ifdef ICLSVR3 /* And for old ICL versions */
711 #ifdef ISDIRBUG /* Also allow this from command line */
718 #endif /* ISDIRBUG */
724 #define _IFMT 0170000
729 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
732 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
735 /* The following mainly for NeXTSTEP... */
738 #define S_IWUSR 0000200
742 #define S_IRGRP 0000040
746 #define S_IWGRP 0000020
750 #define S_IXGRP 0000010
754 #define S_IROTH 0000004
758 #define S_IWOTH 0000002
762 #define S_IXOTH 0000001
765 Define maximum length for a file name if not already defined.
766 NOTE: This applies to a path segment (directory or file name),
767 not the entire path string, which can be CKMAXPATH bytes long.
771 #define MAXNAMLEN _MAX_FNAME
774 #endif /* _MAX_FNAME */
778 #define MAXNAMLEN 255
781 #define MAXNAMLEN FILENAME_MAX
784 #define MAXNAMLEN NAME_MAX
786 #ifdef _POSIX_NAME_MAX
787 #define MAXNAMLEN _POSIX_NAME_MAX
790 #define MAXNAMLEN _D_NAME_MAX
793 #define MAXNAMLEN DIRSIZ
797 #endif /* _D_NAME_MAX */
798 #endif /* _POSIX_NAME_MAX */
799 #endif /* NAME_MAX */
800 #endif /* FILENAME_MAX */
802 #endif /* MAXNAMLEN */
806 /* As of 2001-11-03 this is handled in ckcdeb.h */
807 /* Longest pathname ... */
809 Beware: MAXPATHLEN is one of UNIX's dirty little secrets. Where is it
810 defined? Who knows... <param.h>, <mod.h>, <unistd.h>, <limits.h>, ...
811 There is not necessarily even a definition for it anywhere, or it might have
812 another name. If you get it wrong, bad things happen with getcwd() and/or
813 getwd(). If you allocate a buffer that is too short, getwd() might write
814 over memory and getcwd() will fail with ERANGE. The definitions of these
815 functions (e.g. in SVID or POSIX.1) do not tell you how to determine the
816 maximum path length in order to allocate a buffer that is the right size.
819 #include <sys/param.h> /* For MAXPATHLEN */
822 #include <sys/param.h> /* for MAXPATHLEN, needed for -DDIRENT */
823 #endif /* COHERENT */
830 #define MAXPATH MAXPATHLEN
833 #define MAXPATH PATH_MAX
835 #ifdef _POSIX_PATH_MAX
836 #define MAXPATH _POSIX_PATH_MAX
847 #endif /* _POSIX_PATH_MAX */
848 #endif /* PATH_MAX */
849 #endif /* MAXPATHLEN */
851 /* Maximum number of filenames for wildcard expansion */
854 /* Already defined in ckcdeb.h so the following is superfluous. */
855 /* Don't expect changing them to have any effect. */
860 #define MAXWLD 102400
863 #endif /* BIGBUFOK */
864 #endif /* CK_SMALL */
867 static int maxnames = MAXWLD;
869 /* Define the size of the string space for filename expansion. */
882 #define SSPACE 10000 /* Size of string-generating buffer */
883 #else /* Default static buffer size */
885 #define SSPACE 65000 /* Size of string-generating buffer */
887 #define SSPACE 2000 /* size of string-generating buffer */
888 #endif /* BIGBUFOK */
893 static char sspace[SSPACE]; /* Buffer for generating filenames */
894 #else /* is DYNAMIC */
896 #define SSPACE 2000000000 /* Two billion bytes */
899 #define SSPACE 10000000 /* Ten million */
901 #define SSPACE 10000 /* Ten thousand */
902 #endif /* BIGBUFOK */
903 #endif /* CK_64BIT */
904 char *sspace = (char *)0;
906 static int ssplen = SSPACE; /* Length of string space buffer */
909 /* fdopen() needs declaring because it's not declared in <stdio.h> */
910 _PROTOTYP( FILE * fdopen, (int, char *) );
911 #endif /* DCLFDOPEN */
914 /* popen() needs declaring because it's not declared in <stdio.h> */
915 _PROTOTYP( FILE * popen, (char *, char *) );
916 #endif /* DCLPOPEN */
920 /* More internal function prototypes */
922 * The path structure is used to represent the name to match.
923 * Each slash-separated segment of the name is kept in one
924 * such structure, and they are linked together, to make
925 * traversing the name easier.
928 char npart[MAXNAMLEN+4]; /* name part of path segment */
929 struct path *fwd; /* forward ptr */
932 _PROTOTYP( int shxpand, (char *, char *[], int ) );
934 _PROTOTYP( static int fgen, (char *, char *[], int ) );
935 _PROTOTYP( static VOID traverse, (struct path *, char *, char *) );
936 _PROTOTYP( static VOID addresult, (char *, int) );
938 /* Replaced by ckmatch() */
939 _PROTOTYP( static int match, (char *, char *) );
941 _PROTOTYP( char * whoami, (void) );
942 _PROTOTYP( UID_T real_uid, (void) );
943 _PROTOTYP( static struct path *splitpath, (char *p) );
944 _PROTOTYP( char * zdtstr, (time_t) );
945 _PROTOTYP( time_t zstrdt, (char *, int) );
947 /* Some systems define these symbols in include files, others don't... */
950 #define R_OK 4 /* For access */
963 #endif /* O_RDONLY */
965 /* syslog and wtmp items for Internet Kermit Service */
967 extern char * clienthost; /* From ckcmai.c. */
969 static char fullname[CKMAXPATH+1];
970 static char tmp2[CKMAXPATH+1];
972 extern int ckxlogging;
974 #ifdef CKXPRINTF /* Our printf macro conflicts with */
975 #undef printf /* use of "printf" in syslog.h */
976 #endif /* CKXPRINTF */
979 #include <sys/syslog.h>
983 #endif /* CKSYSLOG */
985 #define printf ckxprintf
986 #endif /* CKXPRINTF */
988 int ckxanon = 1; /* Anonymous login ok */
989 int ckxperms = 0040; /* Anonymous file permissions */
990 int ckxpriv = 1; /* Priv'd login ok */
993 #define XFERFILE "/var/log/iksd.log"
994 #endif /* XFERFILE */
996 /* wtmp logging for IKSD... */
998 #ifndef CKWTMP /* wtmp logging not selected */
999 int ckxwtmp = 0; /* Know this at runtime */
1000 #else /* wtmp file details */
1002 #ifdef UTMPBUG /* Unfortunately... */
1004 Some versions of Linux have a <utmp.h> file that contains
1005 "enum utlogin { local, telnet, rlogin, screen, ... };" This clobbers
1006 any program that uses any of these words as variable names, function
1007 names, macro names, etc. (Other versions of Linux have this declaration
1008 within #if 0 ... #endif.) There is nothing we can do about this other
1009 than to not include the stupid file. But we need stuff from it, so...
1011 #include <features.h>
1012 #include <sys/types.h>
1013 #define UT_LINESIZE 32
1014 #define UT_NAMESIZE 32
1015 #define UT_HOSTSIZE 256
1022 struct exit_status {
1023 short int e_termination; /* Process termination status. */
1024 short int e_exit; /* Process exit status. */
1028 short int ut_type; /* Type of login */
1029 pid_t ut_pid; /* Pid of login process */
1030 char ut_line[UT_LINESIZE]; /* NUL-terminated devicename of tty */
1031 char ut_id[4]; /* Inittab id */
1032 char ut_user[UT_NAMESIZE]; /* Username (not NUL terminated) */
1034 char ut_host[UT_HOSTSIZE]; /* Hostname for remote login */
1035 struct exit_status ut_exit; /* Exit status */
1036 long ut_session; /* Session ID, used for windowing */
1037 struct timeval ut_tv; /* Time entry was made */
1038 int32_t ut_addr_v6[4]; /* Internet address of remote host */
1039 char pad[20]; /* Reserved */
1042 #define ut_time ut_tv.tv_sec /* Why should Linux be like anything else? */
1043 #define ut_name ut_user /* ... */
1046 logwtmp __P ((__const char *__ut_line, __const char *__ut_name,
1047 __const char *__ut_host));
1049 #else /* Not UTMPBUG */
1051 #ifndef HAVEUTMPX /* Who has <utmpx.h> */
1066 #endif /* UNIXWARE */
1067 #endif /* HPUX100 */
1068 #endif /* CK_SCOV5 */
1070 #endif /* SOLARIS */
1071 #endif /* HAVEUTMPX */
1077 /* Because the time_t in the utmp struct is 64 bits but time() wants 32 */
1078 #define __V40_OBJ_COMPAT 1
1082 #undef __V40_OBJ_COMPAT
1084 #endif /* HAVEUTMPX */
1085 #endif /* UTMPBUG */
1088 #define UTMPSTRUCT utmpx
1090 #define UTMPSTRUCT utmp
1091 #endif /* HAVEUTMPX */
1095 #define WTMPFILE "/usr/adm/wtmp.1"
1098 #define WTMPFILE "/var/log/wtmp"
1100 #define WTMPFILE "/usr/adm/wtmp"
1103 #endif /* WTMPFILE */
1104 char * wtmpfile = NULL;
1106 static int wtmpfd = 0;
1107 static char cksysline[32] = { NUL, NUL };
1109 #ifndef HAVEUTHOST /* Does utmp include ut_host[]? */
1110 #ifdef HAVEUTMPX /* utmpx always does */
1113 #ifdef LINUX /* Linux does */
1116 #ifdef SUNOS4 /* SunOS does */
1119 #ifdef AIX41 /* AIX 4.1 and later do */
1124 #endif /* HAVEUTMPX */
1125 #endif /* HAVEUTHOST */
1128 PID_T _vfork() { /* To satisfy a library foulup */
1129 return(fork()); /* in Unixware 2.0.x */
1135 logwtmp(const char * line, const char * name, const char * host)
1137 logwtmp(line, name, host) char *line, *name, *host;
1138 #endif /* CK_ANSIC */
1140 struct UTMPSTRUCT ut;
1142 /* time_t time(); */
1148 makestr(&wtmpfile,WTMPFILE);
1150 if (!line) line = "";
1151 if (!name) name = "";
1152 if (!host) host = "";
1154 if (!wtmpfd && (wtmpfd = open(wtmpfile, O_WRONLY|O_APPEND, 0)) < 0) {
1156 debug(F110,"WTMP open failed",line,0);
1159 if (!fstat(wtmpfd, &buf)) {
1160 ckstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
1162 ckstrncpy(ut.ut_user, name, sizeof(ut.ut_user));
1164 ckstrncpy(ut.ut_name, name, sizeof(ut.ut_name));
1165 #endif /* FREEBSD9 */
1168 ckstrncpy(ut.ut_host, host, sizeof(ut.ut_host));
1169 #endif /* HAVEUTHOST */
1171 time(&ut.ut_tv.tv_sec);
1174 /* In light of the following comment perhaps the previous line should */
1175 /* be "#ifndef COMMENT". */
1178 * On 64-bit platforms sizeof(time_t) and sizeof(ut.ut_time)
1179 * are not the same and attempt to use an address of
1180 * ut.ut_time as an argument to time() call may cause
1181 * "unaligned access" trap.
1190 /* Now (Jan 2006) we can do this for any 64-bit build */
1197 #endif /* CK_64BIT */
1199 #endif /* HAVEUTMPX */
1200 if (write(wtmpfd, (char *)&ut, sizeof(struct UTMPSTRUCT)) !=
1201 sizeof(struct UTMPSTRUCT)) {
1204 ftruncate(wtmpfd, buf.st_size); /* Error, undo any partial write */
1206 chsize(wtmpfd, buf.st_size); /* Error, undo any partial write */
1207 #endif /* COHERENT */
1208 #endif /* NOFTRUNCATE */
1209 debug(F110,"WTMP write error",line,0);
1211 debug(F110,"WTMP record OK",line,0);
1220 C K S Y S L O G -- C-Kermit system logging function,
1222 For use by other modules.
1223 This module can, but doesn't have to, use it.
1225 n = SYSLG_xx values defined in ckcdeb.h
1226 s1, s2, s3: strings.
1229 cksyslog(n, m, s1, s2, s3) int n, m; char * s1, * s2, * s3; {
1232 if (!ckxlogging) /* syslogging */
1234 if (!s1) s1 = ""; /* Fix null args */
1237 switch (n) { /* Translate Kermit level */
1238 case SYSLG_DB: /* to syslog level */
1242 level = m ? LOG_INFO : LOG_ERR;
1244 debug(F110,"cksyslog s1",s1,0);
1245 debug(F110,"cksyslog s2",s2,0);
1246 debug(F110,"cksyslog s3",s3,0);
1248 syslog(level, "%s: %s %s", s1, s2, s3); /* Write syslog record */
1249 debug(F101,"cksyslog errno","",errno);
1251 #endif /* CKSYSLOG */
1256 int maxnam = MAXNAMLEN; /* Available to the outside */
1257 int maxpath = MAXPATH;
1261 char startupdir[MAXPATH+1];
1264 int pexitstat = -2; /* Process exit status */
1266 FILE *fp[ZNFILS] = { /* File pointers */
1267 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
1270 /* Flags for each file indicating whether it was opened with popen() */
1271 int ispipe[ZNFILS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1273 /* Buffers and pointers used in buffered file input and output. */
1275 extern char *zinbuffer, *zoutbuffer;
1277 extern char zinbuffer[], zoutbuffer[];
1278 #endif /* DYNAMIC */
1279 extern char *zinptr, *zoutptr;
1280 extern int zincnt, zoutcnt;
1281 extern int wildxpand, wildena; /* Wildcard handling */
1283 static CK_OFF_T iflen = (CK_OFF_T)-1; /* Input file length */
1285 static PID_T pid = 0; /* pid of child fork */
1286 static int fcount = 0; /* Number of files in wild group */
1287 static int nxpand = 0; /* Copy of fcount */
1288 static char nambuf[CKMAXPATH+4]; /* Buffer for a pathname */
1291 #define ZMBUFLEN 200
1292 static char zmbuf[ZMBUFLEN]; /* For mail, remote print strings */
1293 #endif /* NOFRILLS */
1295 char **mtchs = NULL; /* Matches found for filename */
1296 char **mtchptr = NULL; /* Pointer to current match */
1298 /* Z K S E L F -- Kill Self: log out own job, if possible. */
1300 /* Note, should get current pid, but if your system doesn't have */
1301 /* getppid(), then just kill(0,9)... */
1306 /* Already declared in unistd.h for SVR3 and POSIX */
1308 extern PID_T getppid(void);
1312 extern PID_T getppid();
1313 #endif /* COHERENT */
1314 #endif /* PS2AIX10 */
1315 #endif /* CK_ANSIC */
1321 zkself() { /* For "bye", but no guarantee! */
1338 return(kill((PID_T)getpid(),1));
1341 exit(kill((PID_T)getppid(),1));
1344 exit(kill(getppid(),1));
1356 getfullname(name) char * name; {
1357 char *p = (char *)fullname;
1360 /* If necessary we could also chase down symlinks here... */
1362 /* This works but is incompatible with wuftpd */
1363 if (isguest && anonroot) {
1364 ckstrncpy(fullname,anonroot,CKMAXPATH);
1365 len = strlen(fullname);
1367 if (fullname[len-1] == '/')
1371 #endif /* COMMENT */
1372 zfnqfp(name, CKMAXPATH - len, p);
1374 if (*p < '!') *p = '_';
1379 /* D O I K L O G -- Open Kermit-specific ftp-like transfer log. */
1381 VOID /* Called in ckcmai.c */
1383 if (iklogopen) /* Already open? */
1385 if (xferlog) { /* Open iksd log if requested */
1386 if (!xferfile) /* If no pathname given */
1387 makestr(&xferfile,XFERFILE); /* use this default */
1389 xferlog = open(xferfile, O_WRONLY | O_APPEND | O_CREAT, 0660);
1390 debug(F101,"doiklog open","",xferlog);
1393 syslog(LOG_ERR, "xferlog open failure %s: %m", xferfile);
1394 #endif /* CKSYSLOG */
1395 debug(F101,"doiklog open errno","",errno);
1402 if (xferlog && ckxlogging)
1403 syslog(LOG_INFO, "xferlog: %s open ok", xferfile);
1404 #endif /* CKSYSLOG */
1408 /* Z O P E N I -- Open an existing file for input. */
1410 /* Returns 1 on success, 0 on failure */
1413 zopeni(n,name) int n; char *name; {
1416 debug(F111,"zopeni",name,n);
1417 if ((x = chkfn(n)) != 0) {
1418 debug(F111,"zopeni chkfn",ckitoa(n),x);
1421 zincnt = 0; /* Reset input buffer */
1422 if (n == ZSYSFN) { /* Input from a system function? */
1424 /*** Note, this function should not be called with ZSYSFN ***/
1425 /*** Always call zxcmd() directly, and give it the real file number ***/
1426 /*** you want to use. ***/
1427 return(zxcmd(n,name)); /* Try to fork the command */
1429 debug(F110,"zopeni called with ZSYSFN, failing!",name,0);
1430 *nambuf = '\0'; /* No filename. */
1431 return(0); /* fail. */
1432 #endif /* COMMENT */
1434 if (n == ZSTDIO) { /* Standard input? */
1436 fprintf(stderr,"Terminal input not allowed");
1437 debug(F110,"zopeni: attempts input from unredirected stdin","",0);
1445 debug(F111,"zopeni setroot",ckroot,ckrootset);
1446 if (ckrootset) if (!zinroot(name)) {
1447 debug(F110,"zopeni setroot violation",name,0);
1451 fp[n] = fopen(name,"r"); /* Real file, open it. */
1452 /* debug(F111,"zopeni fopen", name, fp[n]); */
1454 /* printf("ZOPENI fp[%d]=%ld\n",n,fp[n]); */
1460 || ((ckxsyslog >= SYSLG_FA) && ckxlogging)
1461 #endif /* CKSYSLOG */
1464 debug(F110,"zopeni fullname",fullname,0);
1466 if (fp[n] == NULL) {
1468 if (ckxsyslog >= SYSLG_FA && ckxlogging) {
1469 syslog(LOG_INFO, "file[%d] %s: open failed (%m)", n, fullname);
1472 #endif /* CKSYSLOG */
1477 if (ckxsyslog >= SYSLG_FA && ckxlogging)
1478 syslog(LOG_INFO, "file[%d] %s: open read ok", n, fullname);
1479 #endif /* CKSYSLOG */
1490 #endif /* O_NDELAY */
1493 /* Z O P E N O -- Open a new file for output. */
1495 /*ARGSUSED*/ /* zz not used */
1497 zopeno(n,name,zz,fcb)
1498 /* zopeno */ int n; char *name; struct zattr *zz; struct filinfo *fcb; {
1502 int istty = 0, filefd = 0;
1504 /* As of Version 5A, the attribute structure and the file information */
1505 /* structure are included in the arglist. */
1508 debug(F111,"zopeno",name,n);
1510 debug(F101,"zopeno fcb disp","",fcb->dsp);
1511 debug(F101,"zopeno fcb type","",fcb->typ);
1512 debug(F101,"zopeno fcb char","",fcb->cs);
1514 debug(F100,"zopeno fcb is NULL","",0);
1518 if (chkfn(n) != 0) /* Already open? */
1519 return(0); /* Nothing to do. */
1521 if ((n == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */
1522 fp[ZOFILE] = stdout;
1525 /* This seems right but it breaks client server ops */
1528 #endif /* COMMENT */
1532 debug(F101,"zopeno fp[n]=stdout","",fp[n]);
1534 #endif /* COMMENT */
1536 zoutptr = zoutbuffer;
1540 /* A real file. Open it in desired mode (create or append). */
1543 debug(F111,"zopeno setroot",ckroot,ckrootset);
1544 if (ckrootset) if (!zinroot(name)) {
1545 debug(F110,"zopeno setroot violation",name,0);
1550 ckstrncpy(p,"w",8); /* Assume write/create mode */
1551 if (fcb) { /* If called with an FCB... */
1552 if (fcb->dsp == XYFZ_A) { /* Does it say Append? */
1553 ckstrncpy(p,"a",8); /* Yes. */
1554 debug(F100,"zopeno append","",0);
1560 || ((ckxsyslog >= SYSLG_FC) && ckxlogging)
1561 #endif /* CKSYSLOG */
1564 debug(F110,"zopeno fullname",fullname,0);
1567 /* Allow tty devices to opened as output files 2009/10/20 */
1569 debug(F110,"zopeno attempting to open",name,0);
1578 #endif /* FNDELAY */
1579 #endif /* O_NDELAY */
1580 #endif /* O_NONBLOCK */
1581 debug(F111,"zopeno open mode",name,mode);
1582 fd = open(name,O_WRONLY,mode);
1583 debug(F111,"zopeno open",name,fd);
1591 debug(F111,"zopeno istty",name,istty);
1592 debug(F110,"zopeno fopen arg",p,0);
1594 fp[n] = fdopen(filefd,p);
1596 fp[n] = fopen(name,p); /* Try to open the file */
1600 printf("ZOPENO fp[%d]=%ld\n",n,fp[n]);
1603 if (fp[n] == NULL) { /* Failed */
1604 debug(F101,"zopeno failed errno","",errno);
1605 if (istty) close(filefd);
1607 if (ckxsyslog >= SYSLG_FC && ckxlogging)
1608 syslog(LOG_INFO, "file[%d] %s: %s failed (%m)",
1611 append ? "append" : "create"
1613 #endif /* CKSYSLOG */
1614 #ifdef COMMENT /* Let upper levels print message. */
1615 perror("Can't open output file");
1616 #endif /* COMMENT */
1617 } else { /* Succeeded */
1618 extern int zofbuffer, zofblock, zobufsize;
1619 debug(F101, "zopeno zobufsize", "", zobufsize);
1620 if (n == ZDFILE || n == ZTFILE) { /* If debug or transaction log */
1621 setbuf(fp[n],NULL); /* make it unbuffered. */
1623 } else if (n == ZOFILE && !zofblock) { /* blocking or nonblocking */
1625 if ((flags = fcntl(fileno(fp[n]),F_GETFL,0)) > -1)
1626 fcntl(fileno(fp[n]),F_SETFL, flags |
1633 debug(F100,"zopeno ZOFILE nonblocking","",0);
1634 #endif /* DONDELAY */
1635 } else if (n == ZOFILE && !zofbuffer) { /* buffered or unbuffered */
1637 debug(F100,"zopeno ZOFILE unbuffered","",0);
1641 /* Enforce anonymous file-creation permission */
1643 if (n == ZWFILE || n == ZMFILE ||
1644 n == ZOFILE || n == ZDFILE ||
1645 n == ZTFILE || n == ZPFILE ||
1647 chmod(name,ckxperms);
1648 #endif /* CK_LOGIN */
1650 if (ckxsyslog >= SYSLG_FC && ckxlogging)
1651 syslog(LOG_INFO, "file[%d] %s: %s ok",
1654 append ? "append" : "create"
1656 #endif /* CKSYSLOG */
1657 debug(F100, "zopeno ok", "", 0);
1659 zoutcnt = 0; /* (PWP) reset output buffer */
1660 zoutptr = zoutbuffer;
1661 return((fp[n] != NULL) ? 1 : 0);
1664 /* Z C L O S E -- Close the given file. */
1666 /* Returns 0 if arg out of range, 1 if successful, -1 if close failed. */
1671 extern CK_OFF_T ffc;
1673 debug(F101,"zclose file number","",n);
1674 if (chkfn(n) < 1) return(0); /* Check range of n */
1675 if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */
1678 if (fp[ZSYSFN] || ispipe[n]) { /* If file is really pipe */
1680 x = zclosf(n); /* do it specially */
1684 debug(F101,"zclose zclosf","",x);
1685 /* debug(F101,"zclose zclosf fp[n]","",fp[n]); */
1687 if ((fp[n] != stdout) && (fp[n] != stdin))
1691 if (n == ZCTERM || n == ZSTDIO) /* See zopeno() */
1692 if (fp[ZOFILE] == stdout)
1694 #endif /* COMMENT */
1696 iflen = -1L; /* Invalidate file length */
1697 if (x == EOF) { /* if we got a close error */
1698 debug(F101,"zclose fclose fails","",x);
1700 } else if (x2 < 0) { /* or error flushing last buffer */
1701 debug(F101,"zclose error flushing last buffer","",x2);
1702 return(-1); /* then return an error */
1704 /* Print log record compatible with wu-ftpd */
1705 if (xferlog && (n == ZIFILE || n == ZOFILE)) {
1707 extern char ttname[];
1708 if (!iklogopen) (VOID) doiklog(); /* Open log if necessary */
1709 debug(F101,"zclose iklogopen","",iklogopen);
1714 timenow = time(NULL);
1719 #endif /* CK_LOGIN */
1728 #endif /* CK_LOGIN */
1731 len = 24 + 12 + (int)strlen(s) + 16
1732 + (int)strlen(fullname) + 1 + 1 + 1 + 1
1733 + (int)strlen(p) + 6 + 2 + 12;
1735 if (!*fnam) fnam = "(pipe)";
1737 if (len > IKSDMSGLEN)
1738 sprintf(iksdmsg, /* SAFE */
1739 "%.24s [BUFFER WOULD OVERFLOW]\n",ctime(&timenow));
1741 sprintf(iksdmsg, /* SAFE */
1742 "%.24s %d %s %s %s %c %s %c %c %s %s %d %s\n",
1743 ctime(&timenow), /* date/time */
1744 gtimer(), /* elapsed secs */
1746 ckfstoa(ffc), /* byte count */
1747 fnam, /* full pathname of file */
1748 (binary ? 'b' : 'a'), /* binary or ascii */
1749 "_", /* options = none */
1750 n == ZIFILE ? 'o' : 'i', /* in/out */
1752 (isguest ? 'a' : 'r'), /* User type */
1755 #endif /* CK_LOGIN */
1756 p, /* Username or guest passwd */
1758 logged_in ? "iks" : "kermit", /* Record ID */
1761 #endif /* CK_LOGIN */
1762 0, /* User ID on client system unknown */
1765 debug(F110,"zclose iksdmsg",iksdmsg,0);
1766 write(xferlog, iksdmsg, (int)strlen(iksdmsg));
1769 debug(F101,"zclose returns","",1);
1774 /* Z C H I N -- Get a character from the input file. */
1776 /* Returns -1 if EOF, 0 otherwise with character returned in argument */
1779 zchin(n,c) int n; int *c; {
1783 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1789 /* (PWP) Just in case this gets called when it shouldn't. */
1791 a = zminchar(); /* Note: this catches Ctrl-Z */
1792 if (a < 0) /* (See zinfill()...) */
1796 if (a == EOF) return(-1);
1798 /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
1799 if (!binary && a == 0x1A && eofmethod == XYEOF_Z)
1801 #endif /* CK_CTRLZ */
1803 *c = (CHAR) a & 0377;
1807 /* Z S I N L -- Read a line from a file */
1810 Writes the line into the address provided by the caller.
1811 n is the Kermit "channel number".
1812 Writing terminates when newline is encountered, newline is not copied.
1813 Writing also terminates upon EOF or if length x is exhausted.
1814 Returns 0 on success, -1 on EOF or error.
1817 zsinl(n,s,x) int n, x; char *s; {
1818 int a, z = 0; /* z is return code. */
1822 extern CHAR feol; /* Line terminator */
1824 if (!s || chkfn(n) < 1) /* Make sure file is open, etc */
1827 s[0] = '\0'; /* Don't return junk */
1829 a = -1; /* Current character, none yet. */
1830 while (x--) { /* Up to given length */
1832 if (feol) /* Previous character */
1834 if (zchin(n,&a) < 0) { /* Read a character from the file */
1835 debug(F101,"zsinl zchin fail","",count);
1837 z = -1; /* EOF or other error */
1841 if (feol) { /* Single-character line terminator */
1844 } else { /* CRLF line terminator */
1845 if (a == '\015') /* CR, get next character */
1847 if (old == '\015') { /* Previous character was CR */
1848 if (a == '\012') { /* This one is LF, so we have a line */
1850 } else { /* Not LF, deposit CR */
1857 *s = a; /* Deposit character */
1861 *s = '\0'; /* Terminate the string */
1862 debug(F011,"zsinl",buf,len);
1866 /* Z X I N -- Read x bytes from a file */
1869 Reads x bytes (or less) from channel n and writes them
1870 to the address provided by the caller.
1871 Returns number of bytes read on success, 0 on EOF or error.
1874 zxin(n,s,x) int n, x; char *s; {
1876 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1879 if (a < 1) return(0);
1880 for (i = 0; i < a && i < x; i++)
1886 return(fread(s, sizeof (char), x, fp[n]));
1890 Z I N F I L L -- Buffered file input.
1892 (re)fill the file input buffer with data. All file input
1893 should go through this routine, usually by calling the zminchar()
1894 macro defined in ckcker.h. Returns:
1896 Value 0..255 on success, the character that was read.
1898 -2 on any kind of error other than end of file.
1899 -3 timeout when reading from pipe (Kermit packet mode only).
1903 extern int kactive, srvping;
1907 printf("ZINFILL fp[%d]=%ld\n",ZIFILE,fp[ZIFILE]);
1911 if (inserver && !local && fp[ZIFILE] == stdin) {
1914 if (a < 0) return(-2);
1915 for (i = 0; i < a && i < INBUFSIZE; i++) {
1916 zinbuffer[i] = coninc(0);
1919 /* set pointer to beginning, (== &zinbuffer[0]) */
1921 if (zincnt == 0) return(-1);
1922 zincnt--; /* One less char in buffer */
1923 return((int)(*zinptr++) & 0377); /* because we return the first */
1927 debug(F101,"zinfill kactive","",kactive);
1929 if (!(kactive && ispipe[ZIFILE])) {
1930 if (feof(fp[ZIFILE])) {
1931 debug(F100,"ZINFILL feof","",0);
1933 printf("ZINFILL EOF\n");
1938 clearerr(fp[ZIFILE]);
1941 /* Here we can call select() to get a timeout... */
1942 if (kactive && ispipe[ZIFILE]) {
1947 debug(F101,"zinfill calling ttwait","",secs);
1948 z = ttwait(fileno(fp[ZIFILE]),secs);
1949 debug(F101,"zinfill ttwait","",z);
1960 debug(F101,"ZINFILL INBUFSIZE","",INBUFSIZE);
1962 memset(zinbuffer, 0xFF, INBUFSIZE);
1964 for (i = 0; i < INBUFSIZE; i++) {
1965 zinbuffer[i] = 0xFF;
1966 #ifdef COMMENT /* Too much! */
1967 debug(F101,"ZINFILL zinbuffer[i]","",i);
1968 #endif /* COMMENT */
1970 #endif /* USE_MEMCPY */
1971 ckstrncpy(zinbuffer,"zinbuffer is a valid buffer",INBUFSIZE);
1972 /* debug(F111,"ZINFILL about to call fread",zinbuffer,zinbuffer); */
1977 Note: The following read MUST be nonblocking when reading from a pipe
1978 and we want timeouts to work. See zxcmd().
1980 zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]);
1981 debug(F101,"ZINFILL fread","",zincnt); /* Just the size */
1983 printf("FREAD=%d\n",zincnt);
1986 /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
1987 if (zincnt > 0 && !binary && eofmethod == XYEOF_Z) {
1989 for (i = 0; i < zincnt; i++) {
1990 if (zinbuffer[i] == SUB) {
1991 zincnt = i; /* Stop at first Ctrl-Z */
1998 #endif /* CK_CTRLZ */
2000 if (zincnt == 0) { /* Got nothing? */
2001 if (ferror(fp[ZIFILE])) {
2002 debug(F100,"ZINFILL ferror","",0);
2003 debug(F101,"ZINFILL errno","",errno);
2005 printf("ZINFILL errno=%d\n",errno);
2008 return((errno == EWOULDBLOCK) ? -3 : -2);
2011 #endif /* EWOULDBLOCK */
2014 /* In case feof() didn't work just above -- sometimes it doesn't... */
2016 if (feof(fp[ZIFILE]) ) {
2017 debug(F100,"ZINFILL count 0 EOF return -1","",0);
2020 debug(F100,"ZINFILL count 0 not EOF return -2","",0);
2024 zinptr = zinbuffer; /* set pointer to beginning, (== &zinbuffer[0]) */
2025 zincnt--; /* One less char in buffer */
2026 return((int)(*zinptr++) & 0377); /* because we return the first */
2029 /* Z S O U T -- Write a string out to the given file, buffered. */
2031 /* Returns 0 on success, -1 on failure */
2034 zsout(n,s) int n; char *s; {
2037 if (rc < 1) return(-1); /* Keep this, prevents memory faults */
2038 if (!s) return(0); /* Null pointer, do nothing, succeed */
2039 if (!*s) return(0); /* empty string, ditto */
2043 This happens with client-side Kermit server when a REMOTE command
2044 was sent from the server to the client and the server is supposed to
2045 display the text, but of course there is no place to display it
2046 since it is in remote mode executing Kermit protocol.
2048 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2050 return(ttol(s,((int)strlen(s)) < 0) ? -1 : 0);
2053 #endif /* COMMENT */
2060 rc = write(fileno(fp[n]),s,k);
2061 return((rc == k) ? 0 : -1);
2063 rc = fputs(s,fp[n]) == EOF ? -1 : 0;
2069 /* Z S O U T L -- Write string to file, with line terminator, buffered */
2071 /* Returns 0 on success, -1 on failure */
2074 zsoutl(n,s) int n; char *s; {
2079 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2083 return(0); /* See comments in zsout() */
2084 #endif /* COMMENT */
2088 if (n == ZSFILE) /* Session log is unbuffered */
2089 return(write(fileno(fp[n]),"\n",1) == 1 ? 0 : -1);
2090 else if (fputs("\n",fp[n]) == EOF)
2092 if (n == ZDIFIL || n == ZWFILE) /* Flush connection log records */
2097 /* Z S O U T X -- Write x characters to file, unbuffered. */
2099 /* Returns number of characters written on success, -1 on failure */
2102 zsoutx(n,s,x) int n, x; char *s; {
2108 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2110 return(ttol(s,x)); /* See comments in zsout() */
2113 #endif /* COMMENT */
2117 if ((k = (int)strlen(s)) > x) x = k; /* Nothing else would make sense */
2119 if (chkfn(n) < 1) return(-1);
2120 return(write(fp[n]->_file,s,x));
2121 #endif /* COMMENT */
2122 return(write(fileno(fp[n]),s,x) == x ? x : -1);
2125 /* Z C H O U T -- Add a character to the given file. */
2127 /* Should return 0 or greater on success, -1 on failure (e.g. disk full) */
2131 zchout(register int n, char c)
2133 zchout(n,c) register int n; char c;
2134 #endif /* CK_ANSIC */
2136 /* if (chkfn(n) < 1) return(-1); */
2139 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2143 return(0); /* See comments in zsout() */
2144 #endif /* COMMENT */
2148 if (n == ZSFILE) /* Use unbuffered for session log */
2149 return(write(fileno(fp[n]),&c,1) == 1 ? 0 : -1);
2150 /* Buffered for everything else */
2151 if (putc(c,fp[n]) == EOF) /* If true, maybe there was an error */
2152 return(ferror(fp[n])?-1:0); /* Check to make sure */
2153 else /* Otherwise... */
2154 return(0); /* There was no error. */
2157 /* (PWP) buffered character output routine to speed up file IO */
2163 zoutptr = zoutbuffer; /* Reset buffer pointer in all cases */
2166 debug(F101,"zoutdump zoutcnt","",zoutcnt);
2168 if (zoutcnt == 0) { /* Nothing to output */
2170 } else if (zoutcnt < 0) { /* Unexpected negative argument */
2171 zoutcnt = 0; /* Reset output buffer count */
2172 return(-1); /* and fail. */
2176 if (inserver && !local && fp[ZOFILE] == stdout) {
2178 x = ttol(zoutbuffer,zoutcnt);
2180 x = 1; /* See comments in zsout() */
2181 #endif /* COMMENT */
2183 return(x > 0 ? 0 : -1);
2188 Frank Prindle suggested that replacing this fwrite() by an fflush()
2189 followed by a write() would improve the efficiency, especially when
2190 writing to stdout. Subsequent tests showed a 5-fold improvement.
2193 if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) ...
2194 #endif /* COMMENT */
2198 #endif /* CK_NONBLOCK */
2200 while (zoutcnt > 0) {
2201 if ((x = write(fileno(fp[ZOFILE]),zp,zoutcnt)) > -1) {
2203 if (deblog) /* Save a function call... */
2204 debug(F101,"zoutdump wrote","",x);
2206 zoutcnt -= x; /* Adjust output buffer count */
2207 zp += x; /* and pointer */
2211 debug(F101,"zoutdump write error","",errno);
2212 debug(F101,"zoutdump write returns","",x);
2215 zoutcnt = 0; /* Reset output buffer count */
2216 return(-1); /* write() failed */
2222 /* C H K F N -- Internal function to verify file number is ok */
2226 -1: File number n is out of range
2227 0: n is in range, but file is not open
2228 1: n in range and file is open
2232 /* if (n != ZDFILE) debug(F101,"chkfn","",n); */
2233 if (n < 0 || n >= ZNFILS) {
2234 if (n != ZDFILE) debug(F101,"chkfn out of range","",n);
2237 /* if (n != ZDFILE) debug(F101,"chkfn fp[n]","",fp[n]); */
2238 return((fp[n] == NULL) ? 0 : 1);
2242 /* Z G E T F S -- Return file size regardless of accessibility */
2244 Used for directory listings, etc.
2246 The size of the file in bytes, 0 or greater, if the size can be learned.
2247 -1 if the file size can not be obtained.
2248 Also (and this is a hack just for UNIX):
2249 If the argument is the name of a symbolic link,
2250 the global variable issymlink is set to 1,
2251 and the global buffer linkname[] gets the link value.
2252 And it sets zgfs_dir to 1 if it's a directory, otherwise 0.
2253 This lets us avoid numerous redundant calls to stat().
2257 time_t zgfs_mtime = 0;
2258 unsigned int zgfs_mode = 0;
2261 char linkname[CKMAXPATH+1];
2263 #define _IFLNK 0120000
2265 #endif /* CKSYMLINK */
2268 zgetfs(name) char *name; {
2270 char fnam[CKMAXPATH+4];
2271 CK_OFF_T size = (CK_OFF_T)-1;
2276 if (!name) name = "";
2277 if (!*name) return(-1);
2281 if (x == 9 && !strcmp(name,"/dev/null"))
2288 s = tilde_expand(s);
2293 x = ckstrncpy(fnam,s,CKMAXPATH);
2295 debug(F111,"zgetfs fnam",s,x);
2296 if (x > 0 && s[x-1] == '/')
2299 zgfs_dir = 0; /* Assume it's not a directory */
2300 zgfs_link = 0; /* Assume it's not a symlink */
2301 zgfs_mtime = 0; /* No time yet */
2302 zgfs_mode = 0; /* No permission bits yet */
2304 #ifdef CKSYMLINK /* We're doing symlinks? */
2305 #ifdef USE_LSTAT /* OK to use lstat()? */
2307 debug(F101,"STAT","",1);
2308 if (x < 0) /* stat() failed */
2310 if ( /* Now see if it's a symlink */
2312 S_ISLNK(buf.st_mode)
2315 ((_IFMT & buf.st_mode) == _IFLNK)
2317 #endif /* S_ISLNK */
2319 zgfs_link = 1; /* It's a symlink */
2320 linkname[0] = '\0'; /* Get the name */
2321 x = readlink(s,linkname,CKMAXPATH);
2322 debug(F101,"zgetfs readlink",s,x);
2323 if (x > -1 && x < CKMAXPATH) { /* It's a link */
2325 size = buf.st_size; /* Remember size of link */
2326 x = stat(s,&buf); /* Now stat the linked-to file */
2327 debug(F101,"STAT","",2);
2328 if (x < 0) /* so we can see if it's a directory */
2331 ckstrncpy(linkname,"(lookup failed)",CKMAXPATH);
2334 #else /* !USE_LSTAT */
2335 x = stat(s,&buf); /* No lstat(), use stat() instead */
2336 debug(F101,"STAT","",3);
2339 #endif /* USE_LSTAT */
2341 /* Do we need to call readlink()? */
2345 lstat() does not work in SCO operating systems. From "man NS lstat":
2347 lstat obtains information about the file named by path. In the case of a
2348 symbolic link, lstat returns information about the link, and not the file
2349 named by the link. It is only used by the NFS automount daemon and should
2350 not be utilized by users.
2353 debug(F101,"zgetfs forced needrlink","",needrlink);
2356 needrlink = S_ISLNK(buf.st_mode);
2357 debug(F101,"zgetfs S_ISLNK needrlink","",needrlink);
2360 needrlink = (_IFMT & buf.st_mode) == _IFLNK;
2361 debug(F101,"zgetfs _IFLNK needrlink","",needrlink);
2364 debug(F101,"zgetfs default needrlink","",needrlink);
2366 #endif /* S_ISLNK */
2367 #endif /* NOLINKBITS */
2372 x = readlink(s,linkname,CKMAXPATH);
2374 debug(F111,"zgetfs readlink",s,x);
2376 debug(F101,"zgetfs readlink errno","",errno);
2378 debug(F110,"zgetfs readlink result",linkname,0);
2380 if (x > -1 && x < CKMAXPATH) {
2385 #else /* !CKSYMLINK */
2386 x = stat(s,&buf); /* Just stat the file */
2387 debug(F111,"zgetfs stat",s,x);
2388 if (x < 0) /* and get the size */
2390 #endif /* CKSYMLINK */
2392 zgfs_mtime = buf.st_mtime;
2393 zgfs_mode = buf.st_mode;
2394 zgfs_dir = (S_ISDIR(buf.st_mode)) ? 1 : 0; /* Set "is directory" flag */
2395 debug(F111,"zgetfs size",s,size);
2396 debug(F111,"zgetfs st_size",s,buf.st_size);
2397 return((size < 0L) ? buf.st_size : size); /* Return the size */
2401 /* Z C H K I -- Check if input file exists and is readable */
2405 >= 0 if the file can be read (returns the size).
2406 -1 if file doesn't exist or can't be accessed,
2407 -2 if file exists but is not readable (e.g. a directory file).
2408 -3 if file exists but protected against read access.
2410 For Berkeley Unix, a file must be of type "regular" to be readable.
2411 Directory files, special files, and symbolic links are not readable.
2414 zchki(name) char *name; {
2418 extern int zchkid, diractive, matchfifo;
2428 if (x == 9 && !strcmp(s,"/dev/null"))
2430 if (x == 8 && !strcmp(s,"/dev/tty"))
2436 s = tilde_expand(s);
2443 debug(F111,"zchki setroot",ckroot,ckrootset);
2444 if (ckrootset) if (!zinroot(name)) {
2445 debug(F110,"zchki setroot violation",name,0);
2451 debug(F101,"STAT","",5);
2453 debug(F111,"zchki stat fails",s,errno);
2456 if (S_ISDIR (buf.st_mode))
2459 if (!(itsadir && zchkid)) { /* Unless this... */
2460 if (!S_ISREG (buf.st_mode) /* Must be regular file */
2462 && (!matchfifo || !S_ISFIFO (buf.st_mode)) /* or FIFO */
2463 #endif /* S_ISFIFO */
2465 debug(F111,"zchki not regular file (or fifo)",s,matchfifo);
2469 debug(F111,"zchki stat ok:",s,x);
2471 if (diractive) { /* If listing don't check access */
2475 debug(F100,"zchki swapping ids for access()","",0);
2477 #endif /* SW_ACC_ID */
2478 if ((x = access(s,R_OK)) < 0)
2479 x = access(s,X_OK); /* For RUN-class commands */
2482 debug(F100,"zchki swapped ids restored","",0);
2483 #endif /* SW_ACC_ID */
2485 if (x < 0) { /* Is the file accessible? */
2486 debug(F111,"zchki access failed:",s,x); /* No */
2489 iflen = buf.st_size; /* Yes, remember size */
2490 ckstrncpy(nambuf,s,CKMAXPATH); /* and name globally. */
2491 debug(F111,"zchki access ok:",s,iflen);
2492 return((iflen > -1L) ? iflen : 0L);
2496 /* Z C H K O -- Check if output file can be created */
2499 Returns -1 if write permission for the file would be denied, 0 otherwise.
2501 NOTE: The design is flawed. There is no distinction among:
2502 . Can I overwrite an existing file?
2503 . Can I create a file (or directory) in an existing directory?
2504 . Can I create a file (or directory) and its parent(s)?
2507 zchko(name) char *name; {
2508 int i, x, itsadir = 0;
2511 extern int zchkod; /* Used by IF WRITEABLE */
2513 debug(F110,"zchko entry",name,0);
2515 if (!name) return(-1); /* Watch out for null pointer. */
2520 debug(F111,"zchko setroot",ckroot,ckrootset);
2521 if (ckrootset) if (!zinroot(name)) {
2522 debug(F110,"zchko setroot violation",name,0);
2528 x = (int)strlen(name); /* Get length of filename */
2529 debug(F111,"zchko len",name,x);
2530 debug(F111,"zchko zchkod",name,zchkod);
2534 Writing to null device is OK.
2536 if (x == 9 && !strcmp(name,"/dev/null"))
2538 if (x == 8 && !strcmp(name,"/dev/tty"))
2545 s = tilde_expand(s);
2554 zchkod is a global flag meaning we're checking not to see if the directory
2555 file is writeable, but if it's OK to create files IN the directory.
2557 if (!zchkod && isdir(name)) { /* Directories are not writeable */
2558 debug(F111,"zchko isdir",name,1);
2561 s = malloc(x+3); /* Must copy because we can't */
2562 if (!s) { /* write into our argument. */
2563 fprintf(stderr,"zchko: Malloc error 46\n");
2566 ckstrncpy(s,name,x+3);
2570 /* Allow tty devices to opened as output files */
2571 int fd, istty = 0, mode = 0;
2572 debug(F110,"zchko attempting to open",name,0);
2573 /* Don't block on lack of Carrier or other modem signals */
2582 #endif /* FNDELAY */
2583 #endif /* O_NDELAY */
2584 #endif /* O_NONBLOCK */
2585 debug(F111,"zchko open mode",name,mode);
2586 fd = open(name,O_WRONLY,mode); /* Must attempt to open it */
2587 debug(F111,"zchko open",name,fd);
2588 if (fd > -1) { /* to get a file descriptor */
2589 if (isatty(fd)) /* for isatty() */
2591 debug(F111,"zchko isatty",name,istty);
2597 debug(F101,"zchko open errno","",errno);
2603 for (i = x; i > 0; i--) { /* Strip filename from right. */
2604 if (ISDIRSEP(s[i-1])) {
2609 debug(F101,"zchko i","",i);
2610 debug(F101,"zchko itsadir","",itsadir);
2613 /* X/OPEN XPG3-compliant systems fail if argument ends with "/"... */
2614 if (i == 0) /* If no path, use current directory */
2616 else /* Otherwise, use given one. */
2621 The following does not work for "foo/bar" where the foo directory does
2622 not exist even though we could create it: access("foo/.") fails, but
2623 access("foo") works OK.
2625 /* So now we use "path/." if path given, or "." if no path given. */
2626 s[i++] = '.'; /* Append "." to path. */
2629 /* So NOW we strip path segments from the right as long as they don't */
2630 /* exist -- we only call access() for path segments that *do* exist.. */
2631 /* (But this isn't quite right either since now zchko(/foo/bar/baz/xxx) */
2632 /* succeeds when I have write access to foo and bar but baz doesn't exit.) */
2634 if (itsadir && i > 0) {
2636 while (s[0] && !isdir(s)) {
2637 for (i = (int)strlen(s); i > 0; i--) {
2638 if (ISDIRSEP(s[i-1])) {
2647 s[i++] = '.'; /* Append "." to path. */
2650 #endif /* COMMENT */
2651 #endif /* COMMENT */
2654 ckstrncpy(s,".",x+3);
2659 debug(F100,"zchko swapping ids for access()","",0);
2661 #endif /* SW_ACC_ID */
2663 x = access(s,W_OK); /* Check access of path. */
2667 debug(F100,"zchko swapped ids restored","",0);
2668 #endif /* SW_ACC_ID */
2671 debug(F111,"zchko access failed:",s,errno);
2673 debug(F111,"zchko access ok:",s,x);
2674 if (s) free(s); /* Free temporary storage */
2676 return((x < 0) ? -1 : 0); /* and return. */
2679 /* Z D E L E T -- Delete the named file. */
2681 /* Returns: -1 on error, 0 on success */
2684 zdelet(name) char *name; {
2689 #endif /* CK_LOGIN */
2692 debug(F111,"zdelet setroot",ckroot,ckrootset);
2693 if (ckrootset) if (!zinroot(name)) {
2694 debug(F110,"zdelet setroot violation",name,0);
2700 debug(F111,"zdelet",name,x);
2702 if (ckxsyslog >= SYSLG_FC && ckxlogging) {
2704 zfnqfp(name,CKMAXPATH,fullname);
2705 debug(F110,"zdelet fullname",fullname,0);
2707 syslog(LOG_INFO, "file[] %s: delete failed (%m)", fullname);
2709 syslog(LOG_INFO, "file[] %s: delete ok", fullname);
2711 #endif /* CKSYSLOG */
2715 /* Z R T O L -- Convert remote filename into local form */
2718 zrtol(name,name2) char *name, *name2; {
2719 nzrtol(name,name2,1,0,CKMAXPATH);
2723 nzrtol(name,name2,fncnv,fnrpath,max)
2724 char *name, *name2; int fncnv, fnrpath, max;
2727 int flag = 0, n = 0;
2728 char fullname[CKMAXPATH+1];
2732 if (!name) name = "";
2734 debug(F111,"nzrtol name",name,fncnv);
2739 s = tilde_expand(s);
2745 /* Handle the path -- we don't have to convert its format, since */
2746 /* the standard path format and our (UNIX) format are the same. */
2749 devnull = !strcmp(name,"/dev/null");
2751 if (!devnull && fnrpath == PATH_OFF) { /* RECEIVE PATHNAMES OFF */
2753 strncpy(fullname,p,CKMAXPATH);
2754 } else if (!devnull && fnrpath == PATH_ABS) { /* REC PATHNAMES ABSOLUTE */
2755 strncpy(fullname,name,CKMAXPATH);
2756 } else if (!devnull && isabsolute(name)) { /* RECEIVE PATHNAMES RELATIVE */
2757 ckmakmsg(fullname,CKMAXPATH,".",name,NULL,NULL);
2758 } else { /* Ditto */
2759 ckstrncpy(fullname,name,CKMAXPATH);
2761 fullname[CKMAXPATH] = NUL;
2762 debug(F110,"nzrtol fullname",fullname,0);
2766 The maximum length for any segment of a filename is MAXNAMLEN, defined
2767 above. On some platforms (at least QNX) if a segment exceeds this limit,
2768 the open fails with ENAMETOOLONG, so we must prevent it by truncating each
2769 overlong name segment to the maximum segment length before passing the
2770 name to open(). This must be done even when file names are literal, so as
2771 not to halt a file transfer unnecessarily.
2774 char buf[CKMAXPATH+1]; /* New temporary buffer on stack */
2775 char *p = fullname; /* Source and */
2776 char *s = buf; /* destination pointers */
2778 debug(F101,"nzrtol sizing MAXNAMLEN","",MAXNAMLEN);
2779 while (*p && n < CKMAXPATH) { /* Copy name to new buffer */
2780 if (++i > MAXNAMLEN) { /* If this segment too long */
2781 while (*p && *p != '/') /* skip past the rest... */
2783 i = 0; /* and reset counter. */
2784 } else if (*p == '/') { /* End of this segment. */
2785 i = 0; /* Reset counter. */
2787 *s++ = *p++; /* Copy this character. */
2791 ckstrncpy(fullname,buf,CKMAXPATH); /* Copy back to original buffer. */
2792 debug(F111,"nzrtol sizing",fullname,n);
2794 #endif /* NOTRUNCATE */
2796 if (!fncnv || devnull) { /* Not converting */
2797 ckstrncpy(name2,fullname,max); /* We're done. */
2800 name = fullname; /* Converting */
2803 for (; *name != '\0' && n < maxnam; name++) {
2804 if (*name > SP) flag = 1; /* Strip leading blanks and controls */
2805 if (flag == 0 && *name < '!')
2813 if (isupper(*name)) /* Check for mixed case */
2815 else if (islower(*name))
2821 *p-- = '\0'; /* Terminate */
2822 while (*p < '!' && p > name2) /* Strip trailing blanks & controls */
2825 if (*name2 == '\0') { /* Nothing left? */
2826 ckstrncpy(name2,"NONAME",max); /* do this... */
2827 } else if (acase == 1) { /* All uppercase? */
2828 p = name2; /* So convert all letters to lower */
2835 debug(F110,"nzrtol new name",name2,0);
2839 /* Z S T R I P -- Strip device & directory name from file specification */
2841 /* Strip pathname from filename "name", return pointer to result in name2 */
2843 static char work[CKMAXPATH+1];
2846 zstrip(name,name2) char *name, **name2; {
2850 debug(F110,"zstrip before",name,0);
2851 if (!name) { *name2 = ""; return; }
2854 /* Strip leading tilde */
2855 if (*name == '~') name++;
2856 debug(F110,"zstrip after tilde-stripping",name,0);
2858 for (cp = name; *cp; cp++) {
2859 if (ISDIRSEP(*cp)) {
2864 if (n++ >= CKMAXPATH)
2868 *pp = '\0'; /* Terminate the string */
2870 debug(F110,"zstrip after",*name2,0);
2873 /* Z L T O R -- Local TO Remote */
2876 zltor(name,name2) char *name, *name2; {
2877 nzltor(name,name2,1,0,CKMAXPATH);
2880 /* N Z L T O R -- New Local TO Remote */
2883 fncnv = 0 for no conversion, > 0 for regular conversion, < 0 for minimal.
2886 nzltor(name,name2,fncnv,fnspath,max)
2887 char *name, *name2; int fncnv, fnspath, max;
2892 #endif /* COMMENT */
2896 char fullname[CKMAXPATH+1];
2901 extern int fcharset, /* tcharset, */ language;
2903 _PROTOTYP ( CHAR (*sxo), (CHAR) ) = NULL; /* Translation functions */
2905 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR);
2907 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])();
2908 #endif /* CK_ANSIC */
2910 language = L_USASCII;
2912 /* Proper translation of filenames must be done elsewhere */
2913 n = tcharset ? tcharset : TC_USASCII;
2914 sxo = xls[n][fcharset];
2916 sxo = xls[TC_USASCII][fcharset];
2917 #endif /* COMMENT */
2918 #endif /* NOCSETS */
2920 debug(F110,"nzltor name",name,0);
2922 /* Handle pathname */
2925 if (fnspath == PATH_OFF) { /* PATHNAMES OFF */
2927 ckstrncpy(fullname,p,CKMAXPATH);
2928 } else { /* PATHNAMES RELATIVE or ABSOLUTE */
2931 if (!strncmp(p,"../",3))
2933 else if (!strncmp(p,"./",2))
2938 if (fnspath == PATH_ABS) { /* ABSOLUTE */
2939 zfnqfp(p,CKMAXPATH,fullname);
2940 } else { /* RELATIVE */
2941 ckstrncpy(fullname,p,CKMAXPATH);
2944 debug(F110,"nzltor fullname",fullname,0);
2946 if (!fncnv) { /* Not converting */
2947 ckstrncpy(name2,fullname,max); /* We're done. */
2950 #endif /* NOCSETS */
2953 name = fullname; /* Converting */
2957 int tilde = 0, bslash = 0;
2959 if ((namechars = getenv("NAMECHARS")) != NULL) {
2960 if (ckstrchr(namechars, '~' ) != NULL) tilde = '~';
2961 if (ckstrchr(namechars, '\\') != NULL) bslash = '\\';
2968 pp = work; /* Output buffer */
2969 for (cp = name, n = 0; *cp && n < max; cp++,n++) { /* Convert name chars */
2972 if (sxo) c = (*sxo)(c); /* Convert to ASCII */
2973 #endif /* NOCSETS */
2974 if (fncnv > 0 && islower(c)) /* Uppercase letters */
2975 *pp++ = toupper(c); /* Change tilde to hyphen */
2978 else if (fncnv > 0 && c == '#') /* Change number sign to 'X' */
2980 else if (c == '*' || c == '?') /* Change wildcard chars to 'X' */
2982 else if (c == ' ') /* Change space to underscore */
2984 else if (c < ' ') /* Change controls to 'X' */
2986 else if (fncnv > 0 && c == '.') { /* Change dot to underscore */
2987 dotp = pp; /* Remember where we last did this */
2995 *pp = NUL; /* Tie it off. */
2997 if (dotp) *dotp = '.'; /* Restore last dot (if any) */
2999 if (dotp > dirp) *dotp = '.'; /* Restore last dot in file name */
3000 #endif /* COMMENT */
3001 cp = name2; /* If nothing before dot, */
3002 if (*work == '.') *cp++ = 'X'; /* insert 'X' */
3003 ckstrncpy(cp,work,max);
3006 #endif /* NOCSETS */
3007 debug(F110,"nzltor name2",name2,0);
3011 /* Z C H D I R -- Change directory */
3014 dirnam = pointer to name of directory to change to,
3015 which may be "" or NULL to indicate user's home directory.
3021 zchdir(dirnam) char *dirnam; {
3024 _PROTOTYP (int slotdir,(char *,char *));
3027 extern struct mtab *mactab; /* Main macro table */
3028 extern int nmac; /* Number of macros */
3031 debug(F110,"zchdir",dirnam,0);
3032 if (!dirnam) dirnam = "";
3033 if (!*dirnam) /* If argument is null or empty, */
3034 dirnam = zhome(); /* use user's home directory. */
3036 debug(F110,"zchdir 2",dirnam,0);
3039 hd = tilde_expand(dirnam); /* Attempt to expand tilde */
3041 if (*hd == '\0') hd = dirnam; /* in directory name. */
3045 debug(F110,"zchdir 3",hd,0);
3048 debug(F111,"zchdir setroot",ckroot,ckrootset);
3049 if (ckrootset) if (!zinroot(hd)) {
3050 debug(F110,"zchdir setroot violation",hd,0);
3056 /* Just to save some space */
3057 return((chdir(hd) == 0) ? 1 : 0);
3059 if (chdir(hd) == 0) { /* Try to cd */
3062 if (inserver && ikdbopen)
3063 slotdir(isguest ? anonroot : "", zgtdir());
3064 #endif /* CK_LOGIN */
3068 if (nmac) { /* Any macros defined? */
3070 static int on_cd = 0;
3073 k = mlook(mactab,"on_cd",nmac); /* Look this up */
3074 if (k >= 0) { /* If found, */
3075 if (dodo(k,zgtdir(),0) > -1) /* set it up, */
3076 parser(1); /* and execute it */
3090 zchkpid(unsigned long xpid)
3092 zchkpid(xpid) unsigned long xpid;
3093 #endif /* CK_ANSIC */
3095 return((kill((PID_T)xpid,0) < 0) ? 0 : 1);
3099 /* Z H O M E -- Return pointer to user's home directory */
3101 static char * zhomdir = NULL;
3109 return((char *)ckroot);
3113 home = getenv("home");
3115 home = getenv("HOME");
3117 makestr(&zhomdir,home);
3118 return(home ? zhomdir : ".");
3121 /* Z G T D I R -- Returns a pointer to the current directory */
3124 The "preferred" interface for getting the current directory in modern UNIX
3125 is getcwd() [POSIX 1003.1 5.2.2]. However, on certain platforms (such as
3126 SunOS), it is implemented by forking a shell, feeding it the pwd command,
3127 and returning the result, which is not only inefficient but also can result
3128 in stray messages to the terminal. In such cases -- as well as when
3129 getcwd() is not available at all -- getwd() can be used instead by defining
3130 USE_GETWD. However, note that getwd() provides no buffer-length argument
3131 and therefore no safeguard against memory leaks.
3141 #endif /* USE_GETWD */
3144 #define CWDBL 80 /* Save every byte we can... */
3146 #define CWDBL CKMAXPATH
3148 static char cwdbuf[CWDBL+2];
3150 NOTE: The getcwd() prototypes are commented out on purpose. If you get
3151 compile-time warnings, search through your system's header files to see
3152 which one has the needed prototype, and #include it. Usually it is
3153 <unistd.h>. See the section for including <unistd.h> in ckcdeb.h and
3154 make any needed adjustments there (and report them).
3158 char * buf = cwdbuf;
3162 extern char *getwd();
3164 debug(F110,"zgtdir BSD4 getwd()",s,0);
3170 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3171 #endif /* DCLGETCWD */
3172 debug(F101,"zgtdir BSD44 CWDBL","",CWDBL);
3173 s = getcwd(buf,CWDBL);
3179 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3180 #endif /* DCLGETCWD */
3181 debug(F101,"zgtdir MINIX2 CWDBL","",CWDBL);
3182 s = getcwd(buf,CWDBL);
3188 /* This non-ANSI prototype can be fatal at runtime! (e.g. in SCO3.2v5.0.5). */
3189 /* Anyway it's already prototyped in some header file that we have included. */
3190 extern char *getcwd();
3193 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3194 #endif /* DCLGETCWD */
3195 #endif /* COMMENT */
3196 debug(F101,"zgtdir SVORPOSIX CWDBL","",CWDBL);
3197 s = getcwd(buf,CWDBL);
3204 extern char *getcwd();
3205 #endif /* DCLGETCWD */
3206 debug(F101,"zgtdir COHERENT _I386 CWDBL","",CWDBL);
3207 s = getcwd(buf,CWDBL);
3211 extern char *getwd();
3212 debug(F101,"zgtdir COHERENT CWDBL","",CWDBL);
3219 debug(F101,"zgtdir SUNOS CWDBL","",CWDBL);
3220 s = getcwd(buf,CWDBL);
3226 #endif /* COHERENT */
3227 #endif /* SYSVORPOSIX */
3230 #endif /* USE_GETWD */
3233 /* Z X C M D -- Run a system command so its output can be read like a file */
3237 zxcmd(filnum,comand) int filnum; char *comand; {
3240 extern int kactive; /* From ckcpro.w and ckcmai.c */
3243 debug(F100,"zxcmd fails: nopush","",0);
3246 debug(F111,"zxcmd",comand,filnum);
3247 if (chkfn(filnum) < 0) return(-1); /* Need a valid Kermit file number. */
3248 if (filnum == ZSTDIO || filnum == ZCTERM) /* But not one of these. */
3251 out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
3252 debug(F101,"zxcmd out",comand,out);
3254 /* Output to a command */
3256 if (out) { /* Need popen() to do this. */
3257 ckstrncpy(fullname,"(pipe)",CKMAXPATH);
3259 return(0); /* no popen(), fail. */
3261 /* Use popen() to run the command. */
3263 #ifdef _POSIX_SOURCE
3264 /* Strictly speaking, popen() is not available in POSIX.1 */
3266 #endif /* _POSIX_SOURCE */
3268 debug(F110,"zxcmd out",comand,0);
3271 debug(F100,"zxcmd priv_chk failed","",0);
3275 fp[filnum] = popen(comand,"w");
3276 debug(F111,"zxcmd popen",fp[filnum] ? "OK" : "Failed", errno);
3277 if (fp[filnum] == NULL)
3280 /* I wonder what this is all about... */
3281 close(pipes[0]); /* Don't need the input side */
3282 fp[filnum] = fdopen(pipes[1],"w"); /* Open output stream. */
3283 fp[ZSYSFN] = fp[filnum]; /* Remember. */
3284 #endif /* COMMENT */
3286 zoutcnt = 0; /* (PWP) reset input buffer */
3287 zoutptr = zoutbuffer;
3289 #endif /* NOPOPEN */
3292 /* Input from a command */
3295 /* SINIX-L 5.41 does not like fdopen() */
3298 if (pipe(pipes) != 0) {
3299 debug(F100,"zxcmd pipe failure","",0);
3300 return(0); /* can't make pipe, fail */
3303 /* Create a fork in which to run the named process */
3307 pid = vfork() /* child */
3309 pid = fork() /* child */
3313 /* We're in the fork. */
3315 char *shpath, *shname, *shptr; /* Find user's preferred shell */
3319 #ifdef HPUX10 /* Default shell */
3320 defshell = "/usr/bin/sh";
3323 defshell = "/bin/rc";
3325 defshell = "/bin/sh";
3329 if (priv_can()) exit(1); /* Turn off any privileges! */
3330 debug(F101,"zxcmd pid","",pid);
3331 close(pipes[0]); /* close input side of pipe */
3332 close(0); /* close stdin */
3333 if (open("/dev/null",0) < 0) return(0); /* replace input by null */
3336 dup2(pipes[1],1); /* BSD: replace stdout & stderr */
3337 dup2(pipes[1],2); /* by the pipe */
3339 close(1); /* AT&T: close stdout */
3340 if (dup(pipes[1]) != 1) /* Send stdout to the pipe */
3342 close(2); /* Send stderr to the pipe */
3343 if (dup(pipes[1]) != 2)
3345 #endif /* SVORPOSIX */
3350 close(pipes[1]); /* Don't need this any more. */
3353 if ((shpath = getenv("SERVERSHELL")) == NULL)
3356 shpath = getenv("SHELL"); /* What shell? */
3357 if (shpath == NULL) {
3358 p = getpwuid( real_uid() ); /* Get login data */
3359 /* debug(F111,"zxcmd shpath","getpwuid()",p); */
3360 if (p == (struct passwd *)NULL || !*(p->pw_shell))
3362 else shpath = p->pw_shell;
3365 shptr = shname = shpath;
3366 while (*shptr != '\0')
3367 if (*shptr++ == '/')
3369 debug(F110,shpath,shname,0);
3370 restorsigs(); /* Restore ignored signals */
3371 execl(shpath,shname,"-c",comand,(char *)NULL); /* Execute the cmd */
3372 exit(0); /* just punt if it failed. */
3373 } else if (pid == (PID_T) -1) {
3374 debug(F100,"zxcmd fork failure","",0);
3377 debug(F101,"zxcmd pid","",pid);
3378 close(pipes[1]); /* Don't need the output side */
3379 ispipe[filnum] = 1; /* Remember it's a pipe */
3380 fp[filnum] = fdopen(pipes[0],"r"); /* Open a stream for input. */
3384 if (filnum == ZIFILE && kactive) { /* Make pipe reads nonblocking */
3386 if ((flags = fcntl(fileno(fp[filnum]),F_GETFL,0)) > -1) {
3387 debug(F101,"zxcmd fcntl 1 pipe flags","",flags);
3388 x = fcntl(fileno(fp[filnum]),F_SETFL, flags |
3395 debug(F101,"zxcmd fcntl 2 result","",x);
3399 #endif /* DONDELAY */
3401 fp[ZSYSFN] = fp[filnum]; /* Remember. */
3402 zincnt = 0; /* (PWP) reset input buffer */
3408 /* Z C L O S F - wait for the child fork to terminate and close the pipe. */
3410 /* Used internally by zclose - returns -1 on failure, 1 on success. */
3413 zclosf(filnum) int filnum; {
3417 debug(F101,"zclosf filnum","",filnum);
3418 out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
3419 debug(F101,"zclosf out","",out);
3423 /* In UNIX we use popen() only for output files */
3427 x = pclose(fp[filnum]);
3429 debug(F101,"zclosf pclose","",x);
3430 debug(F101,"zclosf pexitstat","",pexitstat);
3431 fp[filnum] = fp[ZSYSFN] = NULL;
3433 return((x != 0) ? -1 : 1);
3435 #endif /* NOPOPEN */
3436 /* debug(F101,"zclosf fp[filnum]","", fp[filnum]); */
3437 /* debug(F101,"zclosf fp[ZSYSFN]","", fp[ZSYSFN]); */
3439 if (pid != (PID_T) 0) {
3440 debug(F101,"zclosf killing pid","",pid);
3449 This is the original code (before 20 April 1997) and has proven totally
3450 portable. But it does not give us the process's return code.
3452 while ((wstat = wait((WAIT_T *)0)) != pid && wstat != -1) ;
3454 /* Here we try to get the return code. Let's hope this is portable too. */
3455 while ((wstat = wait(&statusp)) != pid && wstat != -1) ;
3456 pexitstat = (statusp & 0xff) ? statusp : statusp >> 8;
3457 debug(F101,"zclosf wait statusp","",statusp);
3458 debug(F101,"zclosf wait pexitstat","",pexitstat);
3459 #endif /* CK_CHILD */
3463 fp[filnum] = fp[ZSYSFN] = NULL;
3466 /* debug(F101,"zclosf fp[filnum]","",fp[filnum]); */
3468 return(pexitstat == 0 ? 1 : -1);
3471 #endif /* CK_CHILD */
3477 zxcmd(filnum,comand) int filnum; char *comand; {
3481 zclosf(filnum) int filnum; {
3487 /* Z X P A N D -- Expand a wildcard string into an array of strings */
3489 As of C-Kermit 7.0, this API is obsolete, replaced by nzxpand(), and this
3490 function is only used internally. See nzxpand() below.
3492 Returns the number of files that match fnarg, with data structures set up
3493 so that first file (if any) will be returned by the next znext() call.
3495 Depends on external variable wildxpand: 0 means we expand wildcards
3496 internally, nonzero means we call the shell to do it.
3498 AND in C-Kermit 8.0.212 and later, on extern wildena: 1 means wildcards
3499 are enabled, 0 means disabled, the characters are taken literally.
3501 static int xdironly = 0;
3502 static int xfilonly = 0;
3503 static int xmatchdot = 0;
3504 static int xrecursive = 0;
3505 static int xnobackup = 0;
3506 static int xnolinks = 0;
3508 static char *freeptr = NULL, **resptr = NULL; /* Copies of caller's args */
3509 static int remlen; /* Remaining space in caller's array */
3510 static int numfnd = 0; /* Number of matches found */
3512 #define MINSPACE 1024
3515 initspace(resarry,len) char * resarry[]; int len; {
3517 if (len < MINSPACE) len = MINSPACE;
3518 if (!sspace) { /* Need to allocate string space? */
3519 while (len >= MINSPACE) {
3520 if ((sspace = malloc(len+2))) { /* Got it. */
3521 debug(F101,"fgen string space","",len);
3524 len = (len / 2) + (len / 4); /* Didn't, reduce by 3/4 */
3526 if (len <= MINSPACE) { /* Did we get it? */
3527 fprintf(stderr,"fgen can't malloc string space\n");
3532 #endif /* DYNAMIC */
3534 freeptr = sspace; /* This is where matches are copied. */
3535 resptr = resarry; /* Static copies of these so */
3536 remlen = len; /* recursive calls can alter them. */
3537 debug(F101,"initspace ssplen","",ssplen);
3542 Z S E T F I L -- Query or change the size of file list buffers.
3544 fc = 1: Change current string space to n, return new size.
3545 fc = 2: Return current string space size.
3546 fc = 3: Change current maxnames to n, return new maxnames.
3547 fc = 4: Return current maxnames.
3548 Returns < 0 on error.
3551 zsetfil(n, fc) int n, fc; {
3554 case 1: /* Stringspace */
3559 if (initspace(mtchs,n) < 0)
3561 case 2: /* Fall thru deliberately */
3563 case 3: /* Listsize */
3565 free((char *)mtchs);
3568 mtchs = (char **)malloc(n * sizeof(char *));
3572 case 4: /* Fall thru deliberately */
3575 #endif /* DYNAMIC */
3585 #endif /* NONZXPAND */
3587 zxpand(fnarg) char *fnarg; {
3588 extern int diractive;
3589 char fnbuf[CKMAXPATH+8], * fn, * p;
3591 #ifdef DTILDE /* Built with tilde-expansion? */
3597 if (!fnarg) { /* If no argument provided */
3598 nxpand = fcount = 0;
3599 return(0); /* Return zero files found */
3601 debug(F110,"zxpand entry",fnarg,0);
3602 debug(F101,"zxpand xdironly","",xdironly);
3603 debug(F101,"zxpand xfilonly","",xfilonly);
3605 if (!*fnarg) { /* If no argument provided */
3606 nxpand = fcount = 0;
3607 return(0); /* Return zero files found */
3611 debug(F111,"zxpand setroot",ckroot,ckrootset);
3612 if (ckrootset) if (!zinroot(fnarg)) {
3613 debug(F110,"zxpand setroot violation",fnarg,0);
3614 nxpand = fcount = 0;
3621 This would have been perfect, except it makes us return fully qualified
3622 pathnames for all files.
3624 zfnqfp(fnarg,CKMAXPATH,fnbuf);
3625 debug(F110,"zxpand zfnqfp",fnbuf,0);
3627 debug(F110,"zxpand zgtdir",s,0);
3629 while (*p && *s) /* Make it relative */
3632 fn = (*s) ? fnbuf : p;
3633 debug(F110,"zxpand fn 0",fn,0);
3639 debug(F110,"zxpand fn 0.5",fn,0);
3641 #ifdef DTILDE /* Built with tilde-expansion? */
3642 if (*fnarg == '~') { /* Starts with tilde? */
3643 tnam = tilde_expand(fnarg); /* Try to expand it. */
3644 ckstrncpy(fnbuf,tnam,CKMAXPATH);
3647 ckstrncpy(fnbuf,fnarg,CKMAXPATH);
3648 fn = fnbuf; /* Point to what we'll work with */
3649 #endif /* COMMENT */
3650 debug(F110,"zxpand fn 1",fn,0);
3652 if (!*fn) /* But make sure something is there */
3655 p = fn + (int)strlen(fn) - 1;
3656 if (*p == '/') { /* If last char = / it must be a dir */
3657 if (!xfilonly && !iswild(p)) haveonedir++;
3658 ckstrncat(fn, "*", CKMAXPATH+8); /* so append '*' */
3659 } else if (p > fn) { /* If ends in "/." */
3660 if (*(p-1) == '/' && *p == '.') /* change '.' to '*' */
3662 } else if (p == fn) { /* If it's '.' alone */
3663 if (*p == '.') /* change '.' to '*' */
3666 debug(F110,"zxpand fn 2",fn,0);
3667 x = isdir(fn); /* Is it a directory? */
3668 debug(F111,"zxpand isdir 1",fn,x);
3669 if (x) { /* If so, make it into a wildcard */
3670 if (!xfilonly && !iswild(p))
3672 if ((x = strlen(fn)) > 0) {
3673 if (!ISDIRSEP(fn[x-1]))
3679 debug(F111,"zxpand fn 3 haveonedir",fn,haveonedir);
3681 The following allows us to parse a single directory name without opening
3682 the directory and looking at its contents. The diractive flag is a horrible
3683 hack (especially since DIR /NORECURSIVE turns it off), but otherwise we'd
3684 have to change the API.
3686 debug(F111,"zxpand fn 3 diractive",fn,diractive);
3687 if (!diractive && haveonedir) {
3690 mtchs = (char **)malloc(maxnames * sizeof(*mtchs));
3692 return(nxpand = fcount);
3695 debug(F110,"zxpand haveonedir A1",fnarg,0);
3696 initspace(mtchs,ssplen);
3698 if (numfnd < 0) return(-1);
3699 mtchptr = mtchs; /* Save pointer for next. */
3700 debug(F111,"zxpand haveonedir A2",*mtchptr,numfnd);
3701 return(nxpand = fcount);
3705 if (!nopush && wildxpand) /* Who is expanding wildcards? */
3706 fcount = (mtchs == NULL && /* Shell */
3707 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
3709 : shxpand(fn,mtchs,maxnames);
3712 fcount = (mtchs == NULL && /* Kermit */
3713 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
3715 : fgen(fn,mtchs,maxnames); /* Look up the file. */
3717 if (fcount == 0 && haveonedir) {
3719 debug(F110,"zxpand haveonedir B",fnarg,0);
3721 if (numfnd < 0) return(-1);
3723 mtchptr = mtchs; /* Save pointer for next. */
3729 debug(F111,"zxpand ok",mtchs[0],fcount);
3731 debug(F101,"zxpand fcount","",fcount);
3738 /* N Z X P A N D -- Expand a file list, with options. */
3741 s = pointer to filename or pattern.
3742 flags = option bits:
3744 flags & ZX_FILONLY Match regular files
3745 flags & ZX_DIRONLY Match directories
3746 flags & ZX_RECURSE Descend through directory tree
3747 flags & ZX_MATCHDOT Match "dot files"
3748 flags & ZX_NOBACKUP Don't match "backup files"
3749 flags & ZX_NOLINKS Don't follow symlinks.
3751 Returns the number of files that match s, with data structures set up
3752 so that first file (if any) will be returned by the next znext() call.
3755 nzxpand(s,flags) char * s; int flags; {
3759 debug(F111,"nzxpand",s,flags);
3760 x = flags & (ZX_DIRONLY|ZX_FILONLY);
3761 xdironly = (x == ZX_DIRONLY);
3762 xfilonly = (x == ZX_FILONLY);
3763 if (xdironly && xfilonly) {
3767 xmatchdot = (flags & ZX_MATCHDOT);
3768 debug(F111,"nzxpand xmatchdot 1",s,xmatchdot);
3769 /* If xmatchdot not set by caller but pattern implies it, set it anyway */
3770 if (!xmatchdot && ((p = ckstrchr(s,'.')))) {
3771 if (p == s && p[1] != '/') {
3773 debug(F111,"nzxpand xmatchdot 2",s,xmatchdot);
3775 xmatchdot = (*(p-1) == ',') || (*(p-1) == '{') || (*(p-1) == '/');
3776 debug(F111,"nzxpand xmatchdot 3",s,xmatchdot);
3779 xrecursive = (flags & ZX_RECURSE);
3780 xnobackup = (flags & ZX_NOBACKUP);
3781 xnolinks = (flags & ZX_NOLINKS);
3785 debug(F101,"nzxpand xdironly","",xdironly);
3786 debug(F101,"nzxpand xfilonly","",xfilonly);
3787 debug(F101,"nzxpand xmatchdot","",xmatchdot);
3788 debug(F101,"nzxpand xrecursive","",xrecursive);
3789 debug(F101,"nzxpand xnobackup","",xnobackup);
3790 debug(F101,"nzxpand xnolinks","",xnolinks);
3796 sh_sort(mtchs,NULL,x,0,0,1); /* Alphabetize the list */
3805 #endif /* NONZXPAND */
3808 /* Z X R E W I N D -- Rewinds the zxpand() list */
3812 /* if (!mtchs) return(-1); */
3817 #endif /* NOZXREWIND */
3819 /* Z N E X T -- Get name of next file from list created by zxpand(). */
3821 Returns >0 if there's another file, with its name copied into the arg string,
3822 or 0 if no more files in list.
3825 znext(fn) char *fn; {
3827 ckstrncpy(fn,*mtchptr++,CKMAXPATH);
3832 debug(F111,"znext",fn,fcount+1);
3835 debug(F111,"znext",fn,fcount); /* Return 0 if no filename to return */
3837 #endif /* COMMENT */
3840 /* Z C H K S P A -- Check if there is enough space to store the file */
3843 Call with file specification f, size n in bytes.
3844 Returns -1 on error, 0 if not enough space, 1 if enough space.
3849 zchkspa(char *f, CK_OFF_T n)
3851 zchkspa(f,n) char *f; CK_OFF_T n;
3852 #endif /* CK_ANSIC */
3854 /* In UNIX there is no good (and portable) way. */
3855 return(1); /* Always say OK. */
3858 #ifdef COMMENT /* (not used) */
3860 /* I S B A C K U P -- Tells if given file has a backup suffix */
3863 -1: Invalid argument
3864 0: File does not have a backup suffix
3865 >0: Backup suffix number
3868 isbackup(fn) char * fn; { /* Get backup suffix number */
3869 int i, j, k, x, state, flag;
3871 if (!fn) /* Watch out for null pointers. */
3873 if (!*fn) /* And empty names. */
3877 for (i = (int)strlen(fn) - 1; (!flag && (i > 0)); i--) {
3879 case 0: /* State 0 - final char */
3880 if (fn[i] == '~') /* Is tilde */
3881 state = 1; /* Switch to next state */
3882 else /* Otherwise */
3883 flag = 1; /* Quit - no backup suffix. */
3885 case 1: /* State 1 - digits */
3886 if (fn[i] == '~' && fn[i-1] == '.') { /* Have suffix */
3887 return(atoi(&fn[i+1]));
3888 } else if (fn[i] >= '0' && fn[i] <= '9') { /* In number part */
3889 continue; /* Keep going */
3890 } else { /* Something else */
3891 flag = 1; /* Not a backup suffix - quit. */
3898 #endif /* COMMENT */
3901 /* Z N E W N -- Make a new name for the given file */
3904 Given the name, fn, of a file that already exists, this function builds a
3905 new name of the form "<oldname>.~<n>~", where <oldname> is argument name
3906 (fn), and <n> is a version number, one higher than any existing version
3907 number for that file, up to 99999. This format is consistent with that used
3908 by GNU EMACS. If the constructed name is too long for the system's maximum,
3909 enough characters are truncated from the end of <fn> to allow the version
3910 number to fit. If no free version numbers exist between 1 and 99999, a
3911 version number of "xxxx" is used. Returns a pointer to the new name in
3915 #define ZNEWNBL 63 /* Name buffer length */
3916 #define ZNEWNMD 3 /* Max digits for version number */
3918 #define ZNEWNBL CKMAXPATH
3922 #define MAXBUDIGITS 5
3924 static char znewbuf[ZNEWNBL+12];
3927 znewn(fn,s) char *fn, **s; {
3928 char * buf; /* Pointer to buffer for new name */
3929 char * xp, * namepart = NULL; /* Pointer to filename part */
3930 struct zfnfp * fnfp; /* znfqfp() result struct pointer */
3931 int d = 0, t, fnlen, buflen;
3932 int n, i, k, flag, state;
3933 int max = MAXNAMLEN; /* Maximum name length */
3934 char * dname = NULL;
3937 *s = NULL; /* Initialize return value */
3938 if (!fn) fn = ""; /* Check filename argument */
3941 /* If incoming file already has a backup suffix, remove it. */
3942 /* Then we'll tack a new on later, which will be the highest for this file. */
3944 if (i <= max && i > 0 && fn[i-1] == '~') {
3947 debug(F111,"znewn suffix removal",fn,i);
3948 if ((dname = (char *)malloc(i+1))) {
3949 ckstrncpy(dname,fn,i+1);
3951 for (flag = state = 0; (!flag && (i > 0)); i--) {
3953 case 0: /* State 0 - final char */
3954 if (p[i] == '~') /* Is tilde */
3955 state = 1; /* Switch to next state */
3956 else /* Otherwise */
3957 flag = 1; /* Quit - no backup suffix. */
3959 case 1: /* State 1 - digits */
3960 if (p[i] == '~' && p[i-1] == '.') { /* Have suffix */
3961 p[i-1] = NUL; /* Trim it */
3963 debug(F111,"znewn suffix removal 2",fn,i);
3964 flag = 1; /* done */
3965 } else if (p[i] >= '0' && p[i] <= '9') { /* Number part */
3966 continue; /* Keep going */
3967 } else { /* Something else */
3968 flag = 1; /* Not a backup suffix - quit. */
3975 if ((fnlen = strlen(fn)) < 1) { /* Get length */
3976 if (dname) free(dname);
3979 debug(F111,"znewn",fn,fnlen);
3981 debug(F101,"znewn max 1","",max);
3982 if (max < 14) max = 14; /* Make max reasonable for any UNIX */
3983 if (max > ZNEWNBL) max = ZNEWNBL;
3984 debug(F101,"znewn max 2","",max);
3986 if ((fnfp = zfnqfp(fn, ZNEWNBL, buf))) { /* Get fully qualified name */
3987 namepart = fnfp->fname; /* Isolate the filename */
3988 k = strlen(fn); /* Length of name part */
3989 debug(F111,"znewn namepart",namepart,k);
3991 if (dname) free(dname);
3994 buflen = fnfp->len; /* Length of fully qualified name */
3995 debug(F111,"znewn len",buf,buflen);
3997 if (k + MAXBUDIGITS + 3 < max) { /* Backup name fits - no overflow */
3998 /* Make pattern for backup names */
3999 ckstrncpy(buf+buflen,".~*~",ZNEWNBL+12-buflen);
4000 n = nzxpand(buf,ZX_FILONLY); /* Expand the pattern */
4001 debug(F111,"znewn A matches",buf,n);
4002 while (n-- > 0) { /* Find any existing name.~n~ files */
4003 xp = *mtchptr++; /* Point at matching name */
4004 t = atoi(xp+buflen+2); /* Get number */
4005 if (t > d) d = t; /* Save d = highest version number */
4007 sprintf(buf+buflen,".~%d~",d+1); /* Yes, make "name.~<d+1>~" */
4008 debug(F110,"znewn A newname",buf,0);
4009 } else { /* Backup name would be too long */
4010 int xlen; /* So we have to eat back into it */
4012 char buf2[ZNEWNBL+12];
4015 debug(F101,"znewn B delta","",delta);
4017 for (i = MAXBUDIGITS; i > 0; i--) { /* In this case the format of */
4018 ckstrncpy(buf2,buf,ZNEWNBL+12); /* the backup name depends on */
4019 xlen = buflen - i - 3 + delta; /* how many digits are in the */
4020 ckstrncpy(buf2+xlen,".~*~",ZNEWNBL+12-xlen); /* backup number */
4021 n = nzxpand(buf2,ZX_FILONLY);
4022 debug(F111,"znewn B matches",buf2,n);
4026 while (n-- > 0) { /* Find any existing name.~n~ files */
4027 xp = *mtchptr++; /* Point at matching name */
4028 t = atoi(xp+xlen+2); /* Get number */
4029 if (t > d) d = t; /* Save d = highest version number */
4031 if (d > 0) /* If the odometer turned over... */
4032 if ((d % 10) == 9) /* back up one space. */
4034 sprintf(buf2+xlen,".~%d~",d+1); /* This just fits */
4035 ckstrncpy(buf,buf2,ZNEWNBL+12); /* (we could be more clever here...) */
4036 debug(F110,"znewn B new name",buf,0);
4038 *s = buf; /* Point to new name */
4039 ck_znewn = d+1; /* Also make it available globally */
4040 if (dname) free(dname);
4044 /* Z R E N A M E -- Rename a file */
4046 Call with old and new names.
4047 If new name is the name of a directory, the 'old' file is moved to
4049 Returns 0 on success, -1 on failure.
4052 zrename(old,new) char *old, *new; {
4058 debug(F110,"zrename old",old,0);
4059 debug(F110,"zrename new",new,0);
4060 if (!*old) return(-1);
4061 if (!*new) return(-1);
4065 if (inserver && isguest)
4067 #endif /* CK_LOGIN */
4071 debug(F111,"zrename setroot",ckroot,ckrootset);
4073 if (!zinroot(old)) {
4074 debug(F110,"zrename old: setroot violation",old,0);
4077 if (!zinroot(new)) {
4078 debug(F110,"zrename new: setroot violation",new,0);
4090 if (!(p = malloc(strlen(new) + strlen(old) + 2)))
4092 strcpy(p,new); /* (safe) Directory part */
4093 if (!ISDIRSEP(*(new+x-1))) /* Separator, if needed */
4094 strcat(p,"/"); /* (safe) */
4095 zstrip(old,&q); /* Strip path part from old name */
4096 strcat(p,q); /* cat to new directory (safe) */
4098 debug(F110,"zrename dir",s,0);
4101 else debug(F110,"zrename no dir",s,0);
4105 if (inserver && (!ENABLED(en_del))) {
4106 if (zchki(s) > -1) /* Destination file exists? */
4111 x = -1; /* Return code. */
4113 /* Atomic, preferred, uses a single system call, rename(), if available. */
4115 debug(F111,"zrename rename()",old,x);
4119 /* If rename() failed or not available try link()/unlink() */
4122 if (zchko(old) > -1) { /* Requires write access to orignal */
4124 debug(F111,"zrename link()",old,x);
4125 if (x > -1) { /* Make a link with the new name. */
4127 debug(F111,"zrename unlink()",old,x);
4129 /* If link/unlink failed copy and delete */
4132 debug(F111,"zrename zcopy()",old,x);
4135 debug(F111,"zrename zdelet()",old,x);
4140 fullname[0] = '\0'; /* Clear this out for next time. */
4143 if (ckxsyslog >= SYSLG_FC && ckxlogging) {
4144 zfnqfp(old,CKMAXPATH,fullname);
4146 zfnqfp(s,CKMAXPATH,tmp2);
4148 syslog(LOG_INFO,"file[] %s: renamed to %s ok", fullname, tmp2);
4150 syslog(LOG_INFO,"file[] %s: rename to %s failed (%m)",fullname,tmp2);
4152 #endif /* CKSYSLOG */
4158 /* Z C O P Y -- Copy a single file. */
4160 Call with source and destination names.
4161 If destination is a directory, the source file is
4162 copied to that directory with its original name.
4166 -2 = source file is not a regular file.
4167 -3 = source file not found.
4168 -4 = permission denied.
4169 -5 = source and destination are the same file.
4174 zcopy(source,destination) char *source, *destination; {
4175 char *src, *dst; /* Local pointers to filenames */
4176 int x, y, rc; /* Workers */
4177 int in = -1, out = -1; /* i/o file descriptors */
4178 struct stat srcbuf; /* Source file info buffer */
4179 int perms; /* Output file permissions */
4180 char buf[1024]; /* File copying buffer */
4182 if (!source) source = "";
4183 if (!destination) destination = "";
4185 debug(F110,"zcopy src arg",source,0);
4186 debug(F110,"zcopy dst arg",destination,0);
4188 if (!*source) return(-1);
4189 if (!*destination) return(-1);
4193 if (inserver && isguest)
4195 #endif /* CK_LOGIN */
4199 debug(F111,"zcopy setroot",ckroot,ckrootset);
4201 if (!zinroot(source)) {
4202 debug(F110,"zcopy source: setroot violation",source,0);
4205 if (!zinroot(destination)) {
4206 debug(F110,"zcopy destination: setroot violation",destination,0);
4215 if (stat(src,&srcbuf) == 0) { /* Get source file info */
4216 struct stat dstbuf; /* Destination file info buffer */
4217 debug(F101,"STAT","",6);
4218 if (stat(dst,&dstbuf) == 0) {
4219 debug(F101,"STAT","",7);
4220 if (srcbuf.st_dev == dstbuf.st_dev)
4221 if (srcbuf.st_ino == dstbuf.st_ino) {
4222 debug(F100,"zcopy files identical: stat()","",0);
4226 } else { /* stat() failed... */
4227 debug(F101,"STAT","",8);
4228 debug(F111,"source file not found",src,errno);
4231 fullname[0] = '\0'; /* Get full pathnames */
4232 if (zfnqfp(source,CKMAXPATH,fullname))
4234 debug(F110,"zcopy src",src,0);
4236 if (zfnqfp(destination,CKMAXPATH,tmp2))
4238 debug(F110,"zcopy dst 1",dst,0);
4239 if (!strcmp(src,dst)) { /* Src and dst are same file? */
4240 debug(F100,"zcopy files identical: strcmp()","",0); /* This... */
4241 return(-5); /* should not happen. */
4243 if (isdir(src)) { /* Source file is a directory? */
4244 debug(F110,"zcopy source is directory",src,0);
4245 return(-2); /* Fail */
4247 if (isdir(dst)) { /* Destination is a directory? */
4248 char *q = NULL; /* Yes, add filename to it. */
4250 if (x < 1) return(-1);
4251 if (!ISDIRSEP(*(dst+x-1))) { /* Add separator if needed */
4255 debug(F111,"zcopy dst 2",dst,x);
4256 zstrip(src,&q); /* Strip path part from old name */
4257 ckstrncpy(tmp2+x,q,CKMAXPATH-x); /* Concatenate it to new name */
4259 debug(F110,"zcopy dst 3",dst,0);
4262 if (inserver && (!ENABLED(en_del))) {
4263 if (zchki(dst) > -1) /* Destination file exists? */
4268 perms = umask(0); /* Get user's umask */
4269 umask(perms); /* Put it back! */
4270 perms ^= 0777; /* Flip the bits */
4271 perms &= 0666; /* Zero execute bits from umask */
4272 perms |= (srcbuf.st_mode & 0111); /* OR in source file's execute bits */
4273 rc = -1; /* Default return code */
4274 errno = 0; /* Reset errno */
4275 in = open(src, O_RDONLY, 0); /* Open source file */
4276 debug(F111,"zcopy open source",src,in);
4277 if (in > -1) { /* If open... */
4278 /* Open destination file */
4280 out = open(dst, O_WRONLY|O_CREAT|O_TRUNC, perms);
4282 out = open(dst, O_WRONLY|O_CREAT, perms);
4283 #endif /* O_TRUNC */
4284 debug(F111,"zcopy open dest",dst,out);
4285 if (out > -1) { /* If open... */
4286 while ((x = read(in,buf,1024)) > 0) { /* Copy in 1K blocks */
4287 y = write(out,buf,x);
4288 if (y < 0) { /* On write failure */
4290 rc = -6; /* Indicate i/o error */
4294 debug(F101,"zcopy final read","",x);
4295 debug(F101,"zcopy errno","",errno);
4296 rc = (x == 0) ? 0 : -6; /* In case of read failure */
4299 if (in > -1) close(in); /* Close files */
4300 if (out > -1) close(out);
4301 if (rc == -1) { /* Set return code */
4303 case ENOENT: rc = -3; break;
4304 case EACCES: rc = -4; break;
4310 if (rc > -1 && ckxsyslog >= SYSLG_FC && ckxlogging) {
4312 syslog(LOG_INFO,"file[] %s: copy to %s failed (%m)", fullname, tmp2);
4314 syslog(LOG_INFO,"file[] %s: copy to %s ok", fullname, tmp2);
4316 #endif /* CKSYSLOG */
4323 Fills in a Kermit file attribute structure for the file which is to be sent.
4324 Returns 0 on success with the structure filled in, or -1 on failure.
4325 If any string member is null, then it should be ignored.
4326 If any numeric member is -1, then it should be ignored.
4332 #endif /* CK_GPERMS */
4336 #define S_IRUSR 0400
4337 #endif /* S_IRUSR */
4339 #define S_IXUSR 0200
4340 #endif /* S_IWUSR */
4342 #define S_IXUSR 0100
4343 #endif /* S_IXUSR */
4350 #endif /* S_IXUSR */
4351 #endif /* S_IWUSR */
4352 #endif /* S_IRUSR */
4354 static char gperms[2];
4356 #endif /* CK_GPERMS */
4358 static char lperms[24];
4361 static char xlperms[24];
4363 /* Z S E T P E R M -- Set permissions of a file */
4366 zsetperm(f,code) char * f; int code; {
4372 #endif /* CK_SCO32V4 */
4374 if (inserver && guest) {
4375 debug(F110,"zsetperm guest",f,0);
4380 debug(F111,"zsetperm error",f,errno);
4383 debug(F111,"zsetperm ok",f,mask);
4387 /* Z G P E R M -- Get permissions of a file as an octal string */
4390 zgperm(f) char *f; {
4391 extern int diractive;
4392 int x; char *s = (char *)xlperms;
4394 debug(F110,"zgperm",f,0);
4395 if (!f) return("----------");
4396 if (!*f) return("----------");
4399 debug(F111,"zgperm setroot",ckroot,ckrootset);
4400 if (ckrootset) if (!zinroot(f)) {
4401 debug(F110,"zgperm setroot violation",f,0);
4402 return("----------");
4410 #endif /* USE_LSTAT */
4412 debug(F101,"STAT","",9);
4414 return("----------");
4415 sprintf(s,"%o",buf.st_mode);
4416 debug(F110,"zgperm",s,0);
4420 /* Like zgperm() but returns permissions in "ls -l" string format */
4422 static char xsperms[24];
4425 ziperm(f) char * f; {
4426 extern int diractive;
4427 int x; char *s = (char *)xsperms;
4429 unsigned int perms = 0;
4431 debug(F110,"ziperm",f,0);
4433 if (!f) return(NULL);
4434 if (!*f) return(NULL);
4436 if (diractive && zgfs_mode != 0) {
4437 perms = zgfs_mode; /* zgetfs() already got them */
4443 #endif /* USE_LSTAT */
4445 debug(F101,"STAT","",10);
4447 return("----------");
4448 perms = buf.st_mode;
4450 switch (perms & S_IFMT) {
4454 case S_IFCHR: /* Character special */
4457 case S_IFBLK: /* Block special */
4460 case S_IFREG: /* Regular */
4464 case S_IFLNK: /* Symbolic link */
4467 #endif /* S_IFLNK */
4469 case S_IFSOCK: /* Socket */
4472 #endif /* S_IFSOCK */
4476 case S_IFIFO: /* FIFO */
4479 #endif /* COHERENT */
4481 #endif /* S_IFIFO */
4483 case S_IFWHT: /* Whiteout */
4486 #endif /* S_IFWHT */
4487 default: /* Unknown */
4491 if (perms & S_IRUSR) /* Owner's permissions */
4495 if (perms & S_IWUSR)
4499 switch (perms & (S_IXUSR | S_ISUID)) {
4509 case S_IXUSR | S_ISUID:
4513 if (perms & S_IRGRP) /* Group permissions */
4517 if (perms & S_IWGRP)
4521 switch (perms & (S_IXGRP | S_ISGID)) {
4531 case S_IXGRP | S_ISGID:
4535 if (perms & S_IROTH) /* World permissions */
4539 if (perms & S_IWOTH)
4547 perms & (S_IXOTH | S_ISVTX)
4560 case S_IXOTH | S_ISVTX:
4566 debug(F110,"ziperm",xsperms,0);
4567 return((char *)xsperms);
4573 zgperm(f) char *f; {
4574 return("----------");
4577 ziperms(f) char *f; {
4578 return("----------");
4580 #endif /* CK_PERMS */
4583 zsattr(xx) struct zattr *xx; {
4587 k = iflen % 1024; /* File length in K */
4589 xx->lengthk = (iflen / 1024) + k;
4590 xx->type.len = 0; /* File type can't be filled in here */
4593 xx->date.val = zfcdat(nambuf); /* File creation date */
4594 xx->date.len = (int)strlen(xx->date.val);
4599 xx->creator.len = 0; /* File creator */
4600 xx->creator.val = "";
4601 xx->account.len = 0; /* File account */
4602 xx->account.val = "";
4603 xx->area.len = 0; /* File area */
4605 xx->password.len = 0; /* Area password */
4606 xx->password.val = "";
4607 xx->blksize = -1L; /* File blocksize */
4608 xx->xaccess.len = 0; /* File access */
4609 xx->xaccess.val = "";
4610 xx->encoding.len = 0; /* Transfer syntax */
4611 xx->encoding.val = 0;
4612 xx->disp.len = 0; /* Disposition upon arrival */
4614 xx->lprotect.len = 0; /* Local protection */
4615 xx->lprotect.val = "";
4616 xx->gprotect.len = 0; /* Generic protection */
4617 xx->gprotect.val = "";
4619 if (*nambuf) x = stat(nambuf,&buf);
4620 debug(F101,"STAT","",11);
4622 debug(F111,"zsattr buf.st_mode & 0777",nambuf,buf.st_mode & 0777);
4623 /* UNIX filemode as an octal string without filetype bits */
4624 sprintf(lperms,"%o",buf.st_mode & 0777);
4625 xx->lprotect.len = (int)strlen(lperms);
4626 xx->lprotect.val = (char *)lperms;
4629 /* Generic permissions only if we have stat.h symbols defined */
4630 if (buf.st_mode & S_IRUSR) x |= 1; /* Read */
4631 if (buf.st_mode & S_IWUSR) x |= (2+16); /* Write and Delete */
4632 if (buf.st_mode & S_IXUSR) x |= 4; /* Execute */
4633 gperms[0] = tochar(x);
4635 xx->gprotect.len = 1;
4636 xx->gprotect.val = (char *)gperms;
4637 #endif /* CK_GPERMS */
4639 debug(F111,"zsattr lperms",xx->lprotect.val,xx->lprotect.len);
4640 debug(F111,"zsattr gperms",xx->gprotect.val,xx->gprotect.len);
4641 xx->systemid.val = "U1"; /* U1 = UNIX */
4642 xx->systemid.len = 2; /* System ID */
4643 xx->recfm.len = 0; /* Record format */
4645 xx->sysparam.len = 0; /* System-dependent parameters */
4646 xx->sysparam.val = "";
4647 xx->length = iflen; /* Length */
4651 /* Z F C D A T -- Get file creation date */
4653 Call with pointer to filename.
4654 On success, returns pointer to modification date in yyyymmdd hh:mm:ss format.
4655 On failure, returns pointer to null string.
4657 static char datbuf[40];
4661 zdtstr(time_t timearg)
4663 zdtstr(timearg) time_t timearg;
4664 #endif /* CK_ANSIC */
4669 struct tm * time_stamp;
4670 struct tm * localtime();
4673 debug(F101,"zdtstr timearg","",timearg);
4676 time_stamp = localtime(&(timearg));
4678 debug(F100,"localtime returns null","",0);
4682 We assume that tm_year is ALWAYS years since 1900.
4683 Any platform where this is not the case will have problems
4686 yy = time_stamp->tm_year; /* Year - 1900 */
4687 debug(F101,"zdtstr tm_year","",time_stamp->tm_year);
4689 debug(F101,"zstrdt YEAR-2000 ALERT 1: localtime year","",yy);
4692 debug(F101,"zdatstr year","",yy);
4694 if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11)
4696 if (time_stamp->tm_mday < 0 || time_stamp->tm_mday > 31)
4698 if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 23)
4700 if (time_stamp->tm_min < 0 || time_stamp->tm_min > 59)
4702 ss = time_stamp->tm_sec; /* Seconds */
4703 if (ss < 0 || ss > 59) /* Some systems give a BIG number */
4707 /* For some reason, 2.1x BSD sprintf gets the last field wrong. */
4708 "%04d%02d%02d %02d:%02d:00",
4710 "%04d%02d%02d %02d:%02d:%02d",
4713 time_stamp->tm_mon + 1,
4714 time_stamp->tm_mday,
4715 time_stamp->tm_hour,
4721 yy = (int)strlen(datbuf);
4722 debug(F111,"zdatstr",datbuf,yy);
4723 if (yy > 17) datbuf[17] = '\0';
4725 #endif /* TIMESTAMP */
4729 zfcdat(name) char *name; {
4732 extern int diractive;
4744 debug(F111,"zfcdat setroot",ckroot,ckrootset);
4745 if (ckrootset) if (!zinroot(name)) {
4746 debug(F110,"zfcdat setroot violation",name,0);
4753 s = tilde_expand(s);
4761 debug(F111,"zfcdat",s,diractive);
4763 if (diractive && zgfs_mtime) {
4768 x = lstat(s,&buffer);
4769 debug(F101,"STAT","",12);
4770 debug(F101,"zfcdat lstat","",x);
4772 #endif /* USE_LSTAT */
4773 x = stat(s,&buffer);
4774 debug(F101,"STAT","",13);
4775 debug(F101,"zfcdat stat","",x);
4778 #endif /* USE_LSTAT */
4781 debug(F111,"zfcdat stat failed",s,errno);
4783 debug(F111,"zfcdat lstat failed",s,errno);
4784 #endif /* USE_LSTAT */
4787 debug(F101,"zfcdat buffer.st_mtime","",buffer.st_mtime);
4788 mtime = buffer.st_mtime;
4790 return(zdtstr(mtime));
4793 #endif /* TIMESTAMP */
4798 /* Z S T R D T -- Converts local date string to internal representation */
4800 In our case (UNIX) this is seconds since midnite 1 Jan 1970 UTC,
4801 suitable for comparison with UNIX file dates. As far as I know, there is
4802 no library or system call -- at least nothing reasonably portable -- to
4803 convert local time to UTC.
4806 zstrdt(date,len) char * date; int len; {
4809 SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime().
4810 ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in
4811 dependence on the XPG4 supplement presence. So always use
4812 what the system header file supplies in ODT 3.0...
4816 extern void ftime(); /* extern void ftime(struct timeb *) */
4817 #endif /* _SCO_DS */
4822 #endif /* M_XENIX */
4824 extern struct tm * localtime();
4826 /* And this should have been declared always through a header file */
4838 int i, n, isleapyear;
4839 /* J F M A M J J A S O N D */
4840 /* 31 28 31 30 31 30 31 31 30 31 30 31 */
4842 int monthdays [13] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 };
4844 struct tm *time_stamp;
4847 struct timeval tp[2];
4848 long xtimezone = 0L;
4852 time_t timep[2]; /* New access and modificaton time */
4855 long timezone; /* In case timezone not defined in .h file */
4864 #endif /* SYSUTIMEH */
4870 static struct timeb tbp;
4877 debug(F111,"zstrdt",date,len);
4882 || (date[11] != ':')
4883 || (date[14] != ':') ) {
4884 debug(F111,"Bad creation date ",date,len);
4887 debug(F111,"zstrdt date check 1",date,len);
4888 for(i = 0; i < 8; i++) {
4889 if (!isdigit(date[i])) {
4890 debug(F111,"Bad creation date ",date,len);
4894 debug(F111,"zstrdt date check 2",date,len);
4897 for (; i < 16; i += 3) {
4898 if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) {
4899 debug(F111,"Bad creation date ",date,len);
4903 debug(F111,"zstrdt date check 3",date,len);
4906 #ifdef COMMENT /* was BSD44 */
4908 man gettimeofday on BSDI 3.1 says:
4909 "The timezone field is no longer used; timezone information is stored out-
4910 side the kernel. See ctime(3) for more information." So this chunk of
4911 code is effectively a no-op, at least in BSDI 3.x.
4915 struct timezone tzp;
4916 x = gettimeofday(NULL, &tzp);
4917 debug(F101,"zstrdt BSD44 gettimeofday","",x);
4919 xtimezone = tzp.tz_minuteswest * 60L;
4922 debug(F101,"zstrdt BSD44 timezone","",xtimezone);
4926 debug(F100,"zstrdt BSD calling ftime","",0);
4928 debug(F100,"zstrdt BSD back from ftime","",0);
4929 timezone = tbp.timezone * 60L;
4930 debug(F101,"zstrdt BSD timezone","",timezone);
4933 tzset(); /* Set timezone */
4936 if ((tz = getenv("TZ")) == NULL)
4937 timezone = 0; /* UTC/GMT */
4939 timezone = atoi(&tz[3]); /* Set 'timezone'. */
4942 #endif /* SVORPOSIX */
4944 #endif /* COMMENT (was BSD44) */
4946 debug(F100,"zstrdt so far so good","",0);
4949 for (i = 0; i < 4; i++) /* Fix the year */
4953 debug(F111,"zstrdt year",s,n);
4955 debug(F100,"zstrdt fails - year","",n);
4959 /* Previous year's leap days. This won't work after year 2100. */
4961 isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
4962 days = (long) (n - 1970) * 365;
4963 days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
4967 for (i = 4; i < 16; i += 2) {
4972 case 4: /* MM: month */
4973 if ((n < 1 ) || ( n > 12)) {
4974 debug(F111,"zstrdt 4 bad date ",date,len);
4977 days += monthdays [n];
4978 if (isleapyear && n > 2)
4982 case 6: /* DD: day */
4983 if ((n < 1 ) || ( n > 31)) {
4984 debug(F111,"zstrdt 6 bad date ",date,len);
4987 tmx = (days + n - 1) * 24L * 60L * 60L;
4988 i++; /* Skip the space */
4991 case 9: /* hh: hour */
4992 if ((n < 0 ) || ( n > 23)) {
4993 debug(F111,"zstrdt 9 bad date ",date,len);
4996 tmx += n * 60L * 60L;
4997 i++; /* Skip the colon */
5000 case 12: /* mm: minute */
5001 if ((n < 0 ) || ( n > 59)) {
5002 debug(F111,"zstrdt 12 bad date ",date,len);
5005 #ifdef COMMENT /* (was BSD44) */ /* Correct for time zone */
5007 debug(F101,"zstrdt BSD44 tmx","",tmx);
5012 #ifndef CONVEX9 /* Don't yet know how to do this here */
5014 tmx += (long) timezone;
5018 extern time_t tzoffset;
5025 #endif /* NOTIMEZONE */
5029 #endif /* CONVEX9 */
5031 #endif /* COMMENT (was BSD44) */
5033 i++; /* Skip the colon */
5036 case 15: /* ss: second */
5037 if ((n < 0 ) || ( n > 59)) {
5038 debug(F111,"zstrdt 15 bad date ",date,len);
5043 time_stamp = localtime(&tmx);
5044 debug(F101,"zstrdt tmx 1","",tmx);
5048 /* Why was this here? */
5049 time_stamp = localtime(&tmx);
5050 debug(F101,"zstrdt tmx 2","",tmx);
5051 #endif /* COMMENT */
5053 { /* New to 7.0 - Works in at at least BSDI 3.1 and FreeBSD 2.2.7 */
5055 zz = time_stamp->tm_gmtoff; /* Seconds away from Zero Meridian */
5056 debug(F101,"zstrdt BSD44 tm_gmtoff","",zz);
5058 debug(F101,"zstrdt BSD44 tmx 3 (GMT)","",tmx);
5062 Daylight Savings Time adjustment.
5063 Do this everywhere BUT in BSD44 because in BSD44,
5064 tm_gmtoff also includes the DST adjustment.
5066 if (time_stamp->tm_isdst) {
5068 debug(F101,"zstrdt tmx 3 (DST)","",tmx);
5071 n = time_stamp->tm_year;
5081 /* Z L O C A L T I M E -- GMT/UTC time string to local time string */
5084 Call with: "yyyymmdd hh:mm:ss" GMT/UTC date-time.
5085 Returns: "yyyymmdd hh:mm:ss" local date-time on success, NULL on failure.
5087 static char zltimbuf[64];
5090 zlocaltime(gmtstring) char * gmtstring; {
5093 SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime().
5094 ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in
5095 dependence on the XPG4 supplement presence. So always use
5096 what the system header file supplies in ODT 3.0...
5100 extern void ftime(); /* extern void ftime(struct timeb *) */
5101 #endif /* _SCO_DS */
5106 #endif /* M_XENIX */
5108 extern struct tm * localtime();
5110 /* And this should have been declared always through a header file */
5122 int i, n, x, isleapyear;
5123 /* J F M A M J J A S O N D */
5124 /* 31 28 31 30 31 30 31 31 30 31 30 31 */
5126 int monthdays [13] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 };
5128 struct tm *time_stamp;
5131 struct timeval tp[2];
5135 time_t timep[2]; /* New access and modificaton time */
5145 #endif /* SYSUTIMEH */
5150 static struct timeb tbp;
5153 char * date = gmtstring;
5157 debug(F111,"zlocaltime",date,len);
5162 || (date[11] != ':')
5163 || (date[14] != ':') ) {
5164 debug(F111,"Bad creation date ",date,len);
5167 debug(F111,"zlocaltime date check 1",date,len);
5168 for(i = 0; i < 8; i++) {
5169 if (!isdigit(date[i])) {
5170 debug(F111,"Bad creation date ",date,len);
5174 debug(F111,"zlocaltime date check 2",date,len);
5177 for (; i < 16; i += 3) {
5178 if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) {
5179 debug(F111,"Bad creation date ",date,len);
5183 debug(F111,"zlocaltime date check 3",date,len);
5185 debug(F100,"zlocaltime so far so good","",0);
5188 for (i = 0; i < 4; i++) /* Fix the year */
5192 debug(F111,"zlocaltime year",s,n);
5194 debug(F100,"zlocaltime fails - year","",n);
5198 /* Previous year's leap days. This won't work after year 2100. */
5200 isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
5201 days = (long) (n - 1970) * 365;
5202 days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
5206 for (i = 4; i < 16; i += 2) {
5211 case 4: /* MM: month */
5212 if ((n < 1 ) || ( n > 12)) {
5213 debug(F111,"zlocaltime 4 bad date ",date,len);
5216 days += monthdays [n];
5217 if (isleapyear && n > 2)
5221 case 6: /* DD: day */
5222 if ((n < 1 ) || ( n > 31)) {
5223 debug(F111,"zlocaltime 6 bad date ",date,len);
5226 tmx = (days + n - 1) * 24L * 60L * 60L;
5227 i++; /* Skip the space */
5230 case 9: /* hh: hour */
5231 if ((n < 0 ) || ( n > 23)) {
5232 debug(F111,"zlocaltime 9 bad date ",date,len);
5235 tmx += n * 60L * 60L;
5236 i++; /* Skip the colon */
5239 case 12: /* mm: minute */
5240 if ((n < 0 ) || ( n > 59)) {
5241 debug(F111,"zlocaltime 12 bad date ",date,len);
5245 i++; /* Skip the colon */
5248 case 15: /* ss: second */
5249 if ((n < 0 ) || ( n > 59)) {
5250 debug(F111,"zlocaltime 15 bad date ",date,len);
5257 At this point tmx is the time_t representation of the argument date-time
5258 string without any timezone or DST adjustments. Therefore it should be
5259 the same as the time_t representation of the GMT/UTC time. Now we should
5260 be able to feed it to localtime() and have it converted to a struct tm
5261 representing the local time equivalent of the given UTC time.
5263 time_stamp = localtime(&tmx);
5268 /* Now we simply reformat the struct tm to a string */
5270 x = time_stamp->tm_year;
5271 if (time_stamp->tm_year < 70 || time_stamp->tm_year > 8099)
5273 if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11)
5275 if (time_stamp->tm_mday < 1 || time_stamp->tm_mday > 31)
5277 if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 24)
5279 if (time_stamp->tm_min < 0 || time_stamp->tm_min > 60)
5281 if (time_stamp->tm_sec < 0 || time_stamp->tm_sec > 60)
5283 sprintf(zltimbuf,"%04d%02d%02d %02d:%02d:%02d",
5284 time_stamp->tm_year + 1900,
5285 time_stamp->tm_mon + 1,
5286 time_stamp->tm_mday,
5287 time_stamp->tm_hour,
5291 return((char *)zltimbuf);
5293 #endif /* ZLOCALTIME */
5294 #endif /* NOTIMESTAMP */
5296 /* Z S T I M E -- Set modification date/time+permissions for incoming file */
5299 f = pointer to name of existing file.
5300 yy = pointer to a Kermit file attribute structure in which yy->date.val
5301 is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00.
5302 yy->lprotect.val & yy->gprotect.val are permission/protection values.
5303 x = is a function code: 0 means to set the file's attributes as given.
5304 1 means compare the date in struct yy with the file creation date.
5306 -1 on any kind of error.
5307 0 if x is 0 and the attributes were set successfully.
5308 0 if x is 1 and date from attribute structure <= file creation date.
5309 1 if x is 1 and date from attribute structure > file creation date.
5313 char *f; struct zattr *yy; int x;
5315 int r = -1; /* Return code */
5318 #endif /* CK_PERMS */
5321 /* It is ifdef'd TIMESTAMP because it might not work on V7. bk@kullmar.se. */
5325 extern int utimes();
5332 /* At least, the declarations for int functions are not needed anyway */
5335 struct timeval tp[2];
5340 time_t timep[2]; /* New access and modificaton time */
5343 long timezone; /* In case not defined in .h file */
5352 #endif /* SYSUTIMEH */
5359 if (!*f) return(-1);
5360 if (!yy) return(-1);
5363 debug(F111,"zstime setroot",ckroot,ckrootset);
5364 if (ckrootset) if (!zinroot(f)) {
5365 debug(F110,"zstime setroot violation",f,0);
5370 if (yy->date.len == 0) { /* No date in struct */
5371 if (yy->lprotect.len != 0) { /* So go do permissions */
5374 debug(F100,"zstime: nothing to do","",0);
5378 if ((tm = zstrdt(yy->date.val,yy->date.len)) < 0) {
5379 debug(F101,"zstime: zstrdt fails","",0);
5382 debug(F101,"zstime: tm","",tm);
5383 debug(F111,"zstime: A-pkt date ok ",yy->date.val,yy->date.len);
5385 if (stat(f,&sb)) { /* Get the time for the file */
5386 debug(F101,"STAT","",14);
5387 debug(F111,"zstime: Can't stat file:",f,errno);
5390 debug(F101,"STAT","",15);
5396 int i, x = 0, xx, flag = 0;
5401 debug(F111,"zstime lperms",yy->lprotect.val,yy->lprotect.len);
5402 debug(F111,"zstime gperms",yy->gprotect.val,yy->gprotect.len);
5403 debug(F110,"zstime system id",yy->systemid.val,0);
5404 sprintf(obuf,"%o",sb.st_mode);
5405 debug(F110,"zstime file perms before",obuf,0);
5410 debug(F101,"zstime isguest","",isguest);
5411 debug(F101,"zstime ckxperms","",ckxperms);
5414 /* Clear owner permissions */
5415 sb.st_mode &= (unsigned) 0177077; /* (16 bits) */
5417 /* Set permissions from ckxperms variable */
5418 sb.st_mode = ckxperms;
5419 #endif /* COMMENT */
5420 debug(F101,"zstime isguest sb.st_mode","",sb.st_mode);
5422 /* We already set them in zopeno() */
5424 #endif /* COMMENT */
5427 #endif /* CK_LOGIN */
5428 if ((yy->lprotect.len > 0 && /* Have local-format permissions */
5429 yy->systemid.len > 0 && /* from A-packet... */
5431 !strcmp(yy->systemid.val,"U1") /* AND you are same as me */
5435 ) || (yy->lprotect.len < 0) /* OR by inheritance from old file */
5438 s = yy->lprotect.val; /* UNIX filemode */
5439 xx = yy->lprotect.len;
5440 if (xx < 0) /* len < 0 means inheritance */
5442 for (i = 0; i < xx; i++) { /* Decode octal string */
5443 if (*s <= '7' && *s >= '0') {
5444 x = 8 * x + (int)(*s) - '0';
5452 sprintf(obuf,"%o",x);
5453 debug(F110,"zstime octal lperm",obuf,0);
5455 } else if (!flag && yy->gprotect.len > 0) {
5461 #endif /* CK_SCO32V4 */
5462 mask = umask(0); /* Get umask */
5463 debug(F101,"zstime mask 1","",mask);
5464 umask(mask); /* Put it back */
5465 mask ^= 0777; /* Flip the bits */
5466 debug(F101,"zstime mask 2","",mask);
5467 g = xunchar(*(yy->gprotect.val)); /* Decode generic protection */
5468 debug(F101,"zstime gprotect","",g);
5470 debug(F100,"zstime S_IRUSR","",0);
5471 if (g & 1) x |= S_IRUSR; /* Read permission */
5473 #endif /* S_IRUSR */
5475 debug(F100,"zstime S_IWUSR","",0);
5476 if (g & 2) x |= S_IWUSR; /* Write permission */
5477 if (g & 16) x |= S_IWUSR; /* Delete permission */
5479 #endif /* S_IWUSR */
5481 debug(F100,"zstime S_IXUSR","",0);
5482 if (g & 4) /* Has execute permission bit */
5484 else /* Doesn't have it */
5485 mask &= 0666; /* so also clear it out of mask */
5487 #endif /* S_IXUSR */
5488 debug(F101,"zstime mask x","",x);
5490 debug(F101,"zstime mask x|mask","",x);
5492 debug(F101,"zstime flag","",flag);
5495 debug(F101,"zstime S_IFMT x","",x);
5496 sb.st_mode = (sb.st_mode & S_IFMT) | x;
5500 debug(F101,"zstime _IFMT x","",x);
5501 sb.st_mode = (sb.st_mode & _IFMT) | x;
5507 sprintf(obuf,"%04o",sb.st_mode);
5508 debug(F111,"zstime file perms after",obuf,setperms);
5511 #endif /* CK_PERMS */
5513 debug(F101,"zstime: sb.st_atime","",sb.st_atime);
5516 tp[0].tv_sec = sb.st_atime; /* Access time first */
5517 tp[1].tv_sec = tm; /* Update time second */
5518 debug(F100,"zstime: BSD44 modtime","",0);
5521 tp.timep[0] = tm; /* Set modif. time to creation date */
5522 tp.timep[1] = sb.st_atime; /* Don't change the access time */
5523 debug(F100,"zstime: V7 modtime","",0);
5526 tp.modtime = tm; /* Set modif. time to creation date */
5527 tp.actime = sb.st_atime; /* Don't change the access time */
5528 debug(F100,"zstime: SYSUTIMEH modtime","",0);
5530 tp.mtime = tm; /* Set modif. time to creation date */
5531 tp.atime = sb.st_atime; /* Don't change the access time */
5532 debug(F100,"zstime: default modtime","",0);
5533 #endif /* SYSUTIMEH */
5537 switch (x) { /* Execute desired function */
5538 case 0: /* Set the creation date of the file */
5539 #ifdef CK_PERMS /* And permissions */
5541 NOTE: If we are inheriting permissions from a previous file, and the
5542 previous file was a directory, this would turn the new file into a directory
5543 too, but it's not, so we try to unset the right bit. Luckily, this code
5544 will probably never be executed since the upper level modules do not allow
5545 reception of a file that has the same name as a directory.
5547 NOTE 2: We change the permissions *before* we change the modification time,
5548 otherwise changing the permissions would set the mod time to the present
5553 debug(F101,"zstime setperms","",setperms);
5554 if (S_ISDIR(sb.st_mode)) {
5555 debug(F101,"zstime DIRECTORY bit on","",sb.st_mode);
5556 sb.st_mode ^= 0040000;
5557 debug(F101,"zstime DIRECTORY bit off","",sb.st_mode);
5560 x = chmod(f,sb.st_mode);
5561 debug(F101,"zstime chmod","",x);
5564 if (x < 0) return(-1);
5565 #endif /* CK_PERMS */
5567 if (!setdate) /* We don't have a date */
5568 return(0); /* so skip the following... */
5576 ) { /* Fix modification time */
5577 debug(F111,"zstime 0: can't set modtime for file",f,errno);
5580 /* Including the modtime here is not portable */
5581 debug(F110,"zstime 0: modtime set for file",f,0);
5586 case 1: /* Compare the dates */
5588 This was st_atime, which was wrong. We want the file-data modification
5591 debug(F111,"zstime 1: compare",f,sb.st_mtime);
5592 debug(F111,"zstime 1: compare","packet",tm);
5594 r = (sb.st_mtime < tm) ? 0 : 1;
5597 default: /* Error */
5600 #endif /* TIMESTAMP */
5604 /* Find initialization file. */
5609 /* nothing here for Unix. This function added for benefit of VMS Kermit. */
5612 #endif /* NOTUSED */
5615 /* Historical -- not used in Unix any more (2001-11-03) */
5618 zmail(p,f) char *p; char *f; { /* Send file f as mail to address p */
5620 Returns 0 on success
5621 2 if mail delivered but temp file can't be deleted
5622 -2 if mail can't be delivered
5623 -1 on file access error
5624 The UNIX version always returns 0 because it can't get a good return
5632 #endif /* CK_LOGIN */
5635 if (!*f) return(-1);
5638 debug(F111,"zmail setroot",ckroot,ckrootset);
5639 if (ckrootset) if (!zinroot(f)) {
5640 debug(F110,"zmail setroot violation",f,0);
5646 /* The idea is to use /usr/ucb/mail, rather than regular mail, so that */
5647 /* a subject line can be included with -s. Since we can't depend on the */
5648 /* user's path, we use the convention that /usr/ucb/Mail = /usr/ucb/mail */
5649 /* and even if Mail has been moved to somewhere else, this should still */
5650 /* find it... The search could be made more reliable by actually using */
5651 /* access() to see if /usr/ucb/Mail exists. */
5654 n = n + n + 15 + (int)strlen(p);
5660 sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
5662 sprintf(zmbuf,"Mail -s %c%s%c %s < %s", '"', f, '"', p, f);
5663 #endif /* DGUX540 */
5668 sprintf(zmbuf,"mail %s < %s", p, f);
5670 sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
5679 #endif /* NOFRILLS */
5684 zprint(p,f) char *p; char *f; { /* Print file f with options p */
5685 extern char * printername; /* From ckuus3.c */
5686 extern int printpipe;
5692 #endif /* CK_LOGIN */
5695 if (!*f) return(-1);
5698 debug(F111,"zprint setroot",ckroot,ckrootset);
5699 if (ckrootset) if (!zinroot(f)) {
5700 debug(F110,"zprint setroot violation",f,0);
5705 debug(F110,"zprint file",f,0);
5706 debug(F110,"zprint flags",p,0);
5707 debug(F110,"zprint printername",printername,0);
5708 debug(F101,"zprint printpipe","",printpipe);
5712 Note use of standard input redirection. In some systems, lp[r] runs
5713 setuid to lp (or ...?), so if user has sent a file into a directory
5714 that lp does not have read access to, it can't be printed unless it is
5715 fed to lp[r] as standard input.
5717 if (printpipe && printername) {
5718 n = 8 + (int)strlen(f) + (int)strlen(printername);
5721 sprintf(zmbuf,"cat %s | %s", f, printername);
5722 } else if (printername) {
5723 n = 8 + (int)strlen(f) + (int)strlen(printername);
5726 sprintf(zmbuf,"cat %s >> %s", f, printername);
5728 n = 4 + (int)strlen(PRINTCMD) + (int)strlen(p) + (int)strlen(f);
5731 sprintf(zmbuf,"%s %s < %s", PRINTCMD, p, f);
5733 debug(F110,"zprint command",zmbuf,0);
5735 #else /* Not UNIX */
5740 #endif /* NOFRILLS */
5742 /* Wildcard expansion functions... */
5744 static char scratch[MAXPATH+4]; /* Used by both methods */
5746 static int oldmtchs = 0; /* Let shell (ls) expand them. */
5748 static char *lscmd = "/bin/ls -d"; /* Command to use. */
5750 static char *lscmd = "echo"; /* Command to use. */
5751 #endif /* COMMENT */
5755 shxpand(pat,namlst,len) char *pat, *namlst[]; int len; {
5756 char *fgbuf = NULL; /* Buffer for forming ls command */
5757 char *p, *q; /* Workers */
5759 int i, x, retcode, itsadir;
5762 x = (int)strlen(pat) + (int)strlen(lscmd) + 3; /* Length of ls command */
5763 for (i = 0; i < oldmtchs; i++) { /* Free previous file list */
5764 if (namlst[i] ) { /* If memory is allocated */
5765 free(namlst[i]); /* Free the memory */
5766 namlst[i] = NULL ; /* Remember no memory is allocated */
5769 oldmtchs = 0 ; /* Remember there are no matches */
5770 fgbuf = malloc(x); /* Get buffer for command */
5771 if (!fgbuf) return(-1); /* Fail if cannot */
5772 ckmakmsg(fgbuf,x,lscmd," ",pat,NULL); /* Form the command */
5773 zxcmd(ZIFILE,fgbuf); /* Start the command */
5774 i = 0; /* File counter */
5775 p = scratch; /* Point to scratch area */
5776 retcode = -1; /* Assume failure */
5777 while ((x = zminchar()) != -1) { /* Read characters from command */
5779 if (c == ' ' || c == '\n') { /* Got newline or space? */
5780 *p = '\0'; /* Yes, terminate string */
5781 p = scratch; /* Point back to beginning */
5782 if (zchki(p) == -1) /* Does file exist? */
5783 continue; /* No, continue */
5784 itsadir = isdir(p); /* Yes, is it a directory? */
5785 if (xdironly && !itsadir) /* Want only dirs but this isn't */
5786 continue; /* so skip. */
5787 if (xfilonly && itsadir) /* It's a dir but want only files */
5788 continue; /* so skip. */
5789 x = (int)strlen(p); /* Keep - get length of name */
5790 q = malloc(x+1); /* Allocate space for it */
5791 if (!q) goto shxfin; /* Fail if space can't be obtained */
5792 strcpy(q,scratch); /* (safe) Copy name to space */
5793 namlst[i++] = q; /* Copy pointer to name into array */
5794 if (i >= len) goto shxfin; /* Fail if too many */
5795 } else { /* Regular character */
5796 *p++ = c; /* Copy it into scratch area */
5799 retcode = i; /* Return number of matching files */
5800 shxfin: /* Common exit point */
5801 free(fgbuf); /* Free command buffer */
5803 zclosf(ZIFILE); /* Delete the command fork. */
5804 oldmtchs = i; /* Remember how many files */
5810 Directory-reading functions for UNIX originally written for C-Kermit 4.0
5811 by Jeff Damens, CUCCA, 1984.
5813 static char * xpat = NULL; /* Global copy of fgen() pattern */
5814 static char * xpatlast = NULL; /* Rightmost segment of pattern*/
5815 static int xpatslash = 0; /* Slash count in pattern */
5816 static int xpatwild = 0; /* Original pattern is wild */
5817 static int xleafwild = 0; /* Last segment of pattern is wild */
5818 static int xpatabsolute = 0;
5825 /* S P L I T P A T H */
5828 Splits the slash-separated portions of the argument string into
5829 a list of path structures. Returns the head of the list. The
5830 structures are allocated by malloc, so they must be freed.
5831 Splitpath is used internally by the filename generator.
5837 A linked list of the slash-separated segments of the input.
5839 static struct path *
5840 splitpath(p) char *p; {
5841 struct path *head,*cur,*prv;
5844 debug(F111,"splitpath",p,xrecursive);
5847 if (!p) return(NULL);
5848 if (!*p) return(NULL);
5850 if (!strcmp(p,"**")) { /* Fix this */
5853 if (ISDIRSEP(*p)) p++; /* Skip leading slash if any */
5855 /* Make linked list of path segments from pattern */
5858 cur = (struct path *) malloc(sizeof (struct path));
5859 /* debug(F101,"splitpath malloc","",cur); */
5861 debug(F100,"splitpath malloc failure","",0);
5863 return((struct path *)NULL);
5866 if (head == NULL) /* First, make list head */
5868 else /* Not first, link into chain */
5870 prv = cur; /* Link from previous to this one */
5873 /* treat backslash as "../" */
5874 if (bslash && *p == bslash) {
5875 strcpy(cur->npart, ".."); /* safe */
5878 for (i=0; i < MAXNAMLEN && *p && *p != '/' && *p != bslash; i++)
5879 cur -> npart[i] = *p++;
5880 cur -> npart[i] = '\0'; /* end this segment */
5882 while (*p && *p != '/' && *p != bslash)
5887 /* General case (UNIX) */
5888 for (i = 0; i < MAXNAMLEN && !ISDIRSEP(*p) && *p != '\0'; i++) {
5889 cur -> npart[i] = *p++;
5892 cur -> npart[i] = '\0'; /* End this path segment */
5894 while (!ISDIRSEP(*p) && *p != '\0') p++;
5901 makestr(&xpatlast,prv -> npart);
5902 debug(F110,"splitpath xpatlast",xpatlast,0);
5905 /* Show original path list */
5907 for (i = 0, cur = head; cur; i++) {
5908 debug(F111,"SPLITPATH",cur -> npart, i);
5916 /* F G E N -- Generate File List */
5919 File name generator. It is passed a string, possibly containing wildcards,
5920 and an array of character pointers. It finds all the matching filenames and
5921 stores pointers to them in the array. The returned strings are allocated
5922 from a static buffer local to this module (so the caller doesn't have to
5923 worry about deallocating them); this means that successive calls to fgen
5924 will wipe out the results of previous calls.
5927 A wildcard string, an array to write names to, the length of the array.
5930 The number of matches.
5931 The array is filled with filenames that matched the pattern.
5932 If there wasn't enough room in the array, -1 is returned.
5934 Originally by: Jeff Damens, CUCCA, 1984. Many changes since then.
5937 fgen(pat,resarry,len) char *pat,*resarry[]; int len; {
5944 int tilde = 0, bquote = 0;
5946 if ((namechars = getenv("NAMECHARS")) != NULL) {
5947 if (ckstrchr(namechars, '~' ) != NULL) tilde = '~';
5948 if (ckstrchr(namechars, '\\') != NULL) bslash = '\\';
5949 if (ckstrchr(namechars, '`' ) != NULL) bquote = '`';
5951 tilde = '~'; bslash = '\\'; bquote = '`';
5955 /* copy "`node_data", etc. anchors */
5956 if (bquote && *pat == bquote)
5957 while (*pat && *pat != '/' && *pat != bslash)
5959 else if (tilde && *pat == tilde)
5963 if (sptr == scratch) {
5964 strcpy(scratch,"./"); /* safe */
5967 if (!(head = splitpath(pat))) return(-1);
5969 #else /* not aegis */
5971 debug(F111,"fgen pat",pat,len);
5972 debug(F110,"fgen current directory",zgtdir(),0);
5973 debug(F101,"fgen stathack","",stathack);
5980 if (!(head = splitpath(pat))) /* Make the path segment list */
5986 if (strncmp(pat,"./",2) && strncmp(pat,"../",3)) {
5987 #endif /* COMMENT */
5988 if (!ISDIRSEP(*pat)) /* If name is not absolute */
5989 *sptr++ = '.'; /* put "./" in front. */
5993 #endif /* COMMENT */
5997 makestr(&xpat,pat); /* Save copy of original pattern */
5998 debug(F110,"fgen scratch",scratch,0);
6000 for (n = 0, s = xpat; *s; s++) /* How many slashes in the pattern */
6001 if (*s == DIRSEP) /* since these are fences for */
6002 n++; /* pattern matching */
6004 debug(F101,"fgen xpatslash","",xpatslash);
6006 numfnd = 0; /* None found yet */
6008 if (initspace(resarry,ssplen) < 0)
6011 xpatwild = iswild(xpat); /* Original pattern is wild? */
6012 xpatabsolute = isabsolute(xpat);
6013 xleafwild = iswild(xpatlast);
6015 debug(F111,"fgen xpat",xpat,xpatwild);
6016 debug(F111,"fgen xpatlast",xpatlast,xleafwild);
6017 debug(F101,"fgen xpatabsolute","",xpatabsolute);
6019 traverse(head,scratch,sptr); /* Go walk the directory tree. */
6020 while (head != NULL) { /* Done - free path segment list. */
6021 struct path *next = head -> fwd;
6025 debug(F101,"fgen","",numfnd);
6026 return(numfnd); /* Return the number of matches */
6029 /* Define LONGFN (long file names) automatically for BSD 2.9 and 4.2 */
6030 /* LONGFN can also be defined on the cc command line. */
6045 T R A V E R S E -- Traverse a directory tree.
6047 Walks the directory tree looking for matches to its arguments.
6048 The algorithm is, briefly:
6050 If the current pattern segment contains no wildcards, that
6051 segment is added to what we already have. If the name so far
6052 exists, we call ourselves recursively with the next segment
6053 in the pattern string; otherwise, we just return.
6055 If the current pattern segment contains wildcards, we open the name
6056 we've accumulated so far (assuming it is really a directory), then read
6057 each filename in it, and, if it matches the wildcard pattern segment, add
6058 that filename to what we have so far and call ourselves recursively on
6061 Finally, when no more pattern segments remain, we add what's accumulated
6062 so far to the result array and increment the number of matches.
6065 A pattern path list (as generated by splitpath), a string pointer that
6066 points to what we've traversed so far (this can be initialized to "/"
6067 to start the search at the root directory, or to "./" to start the
6068 search at the current directory), and a string pointer to the end of
6069 the string in the previous argument, plus the global "recursive",
6070 "xmatchdot", and "xdironly" flags.
6072 Returns: void, with:
6073 mtchs[] containing the array of filename string pointers, and:
6074 numfnd containing the number of filenames.
6076 Although it might be poor practice, the mtchs[] array is revealed to the
6077 outside in case it needs it; for example, to be sorted prior to use.
6078 (It is poor practice because not all platforms implement file lists the
6079 same way; some don't use an array at all.)
6081 Note that addresult() acts as a second-level filter; due to selection
6082 criteria outside of the pattern, it might decline to add files that
6083 this routine asks it to, e.g. because we are collecting only directory
6084 names but not the names of regular files.
6086 WARNING: In the course of C-Kermit 7.0 development, this routine became
6087 ridiculously complex, in order to meet approximately sixty specific
6088 requirements. DON'T EVEN THINK ABOUT MODIFYING THIS ROUTINE! Trust me;
6089 it is not possible to fix anything in it without breaking something else.
6090 This routine badly needs a total redesign and rewrite. Note: There may
6091 be some good applications for realpath() and/or scandir() and/or fts_blah()
6092 here, on platforms where they are available.
6095 traverse(pl,sofar,endcur) struct path *pl; char *sofar, *endcur; {
6097 /* Appropriate declarations for directory routines and structures */
6098 /* #define OPENDIR means to use opendir(), readdir(), closedir() */
6099 /* If OPENDIR not defined, we use open(), read(), close() */
6101 #ifdef DIRENT /* New way, <dirent.h> */
6103 DIR *fd, *opendir();
6104 struct dirent *dirbuf;
6105 struct dirent *readdir();
6107 #ifdef LONGFN /* Old way, <dir.h> with opendir() */
6109 DIR *fd, *opendir();
6110 struct direct *dirbuf;
6112 int fd; /* Old way, <dir.h> with open() */
6113 struct direct dir_entry;
6114 struct direct *dirbuf = &dir_entry;
6117 int mopts = 0; /* ckmatch() opts */
6118 int depth = 0; /* Directory tree depth */
6120 char nambuf[MAXNAMLEN+4]; /* Buffer for a filename */
6121 int itsadir = 0, segisdir = 0, itswild = 0, mresult, n, x /* , y */ ;
6122 struct stat statbuf; /* For file info. */
6124 debug(F101,"STAT","",16);
6125 if (pl == NULL) { /* End of path-segment list */
6126 *--endcur = '\0'; /* Terminate string, overwrite trailing slash */
6127 debug(F110,"traverse add: end of path segment",sofar,0);
6128 addresult(sofar,-1);
6132 /* This speeds up the search a lot and we still get good results */
6133 /* but it breaks the tagging of directory names done in addresult */
6134 if (xrecursive || xfilonly || xdironly || xpatslash) {
6135 itsadir = xisdir(sofar);
6136 debug(F101,"STAT","",17);
6138 itsadir = (strncmp(sofar,"./",2) == 0);
6140 itsadir = xisdir(sofar);
6141 debug(F101,"STAT","",18);
6143 debug(F111,"traverse entry sofar",sofar,itsadir);
6145 #ifdef CKSYMLINK /* We're doing symlinks? */
6146 #ifdef USE_LSTAT /* OK to use lstat()? */
6147 if (itsadir && xnolinks) { /* If not following symlinks */
6150 x = lstat(sofar,&buf);
6151 debug(F111,"traverse lstat 1",sofar,x);
6154 S_ISLNK(buf.st_mode)
6157 ((_IFMT & buf.st_mode) == _IFLNK)
6159 #endif /* S_ISLNK */
6163 #endif /* USE_LSTAT */
6164 #endif /* CKSYMLINK */
6166 if (!xmatchdot && xpatlast[0] == '.')
6168 if (!xmatchdot && xpat[0] == '.' && xpat[1] != '/' && xpat[1] != '.')
6171 /* ckmatch() options */
6173 if (xmatchdot) mopts |= 1; /* Match dot */
6174 if (!xrecursive) mopts |= 2; /* Dirsep is fence */
6176 debug(F111,"traverse entry xpat",xpat,xpatslash);
6177 debug(F111,"traverse entry xpatlast",xpatlast,xmatchdot);
6178 debug(F110,"traverse entry pl -> npart",pl -> npart,0);
6181 if (xrecursive > 0 && !itsadir) {
6182 char * s; /* Recursive descent and this is a regular file */
6183 *--endcur = '\0'; /* Terminate string, overwrite trailing slash */
6185 /* Find the nth slash from the right and match from there... */
6186 /* (n == the number of slashes in the original pattern - see fgen) */
6187 if (*sofar == '/') {
6188 debug(F110,"traverse xpatslash absolute",sofar,0);
6191 debug(F111,"traverse xpatslash relative",sofar,xpatslash);
6192 for (s = endcur - 1, n = 0; s >= sofar; s--) {
6194 if (++n >= xpatslash) {
6202 /* This speeds things up a bit. */
6203 /* If it causes trouble define NOSKIPMATCH and rebuild. */
6204 if (xpat[0] == '*' && !xpat[1])
6205 x = xmatchdot ? 1 : (s[0] != '.');
6207 #endif /* NOSKIPMATCH */
6208 x = ckmatch(xpat, s, 1, mopts); /* Match with original pattern */
6209 debug(F111,"traverse xpatslash ckmatch",s,x);
6211 debug(F110,"traverse add: recursive, match, && !isdir",sofar,0);
6212 addresult(sofar,itsadir);
6216 #endif /* RECURSIVE */
6218 debug(F111,"traverse sofar 2",sofar,0);
6220 segisdir = ((pl -> fwd) == NULL) ? 0 : 1;
6221 itswild = wildena ? (iswild(pl -> npart)) : 0; /* 15 Jun 2005 */
6223 debug(F111,"traverse segisdir",sofar,segisdir);
6224 debug(F111,"traverse itswild ",pl -> npart,itswild);
6227 if (xrecursive > 0) { /* If recursing and... */
6228 if (segisdir && itswild) /* this is a dir and npart is wild */
6229 goto blah; /* or... */
6230 else if (!xpatabsolute && !xpatwild) /* search object is nonwild */
6231 goto blah; /* then go recurse */
6233 #endif /* RECURSIVE */
6235 if (!itswild) { /* This path segment not wild? */
6237 strcpy(endcur,pl -> npart); /* (safe) Append next part. */
6238 endcur += (int)strlen(pl -> npart); /* Advance end pointer */
6241 strcpy() does not account for quoted metacharacters.
6242 We must remove the quotes before doing the stat().
6248 while ((c = *s++)) {
6259 #endif /* COMMENT */
6260 *endcur = '\0'; /* End new current string. */
6262 if (stat(sofar,&statbuf) == 0) { /* If this piece exists... */
6263 debug(F110,"traverse exists",sofar,0);
6264 *endcur++ = DIRSEP; /* add slash to end */
6265 *endcur = '\0'; /* and end the string again. */
6266 traverse(pl -> fwd, sofar, endcur);
6269 else debug(F110,"traverse not found", sofar, 0);
6274 *endcur = '\0'; /* End current string */
6275 debug(F111,"traverse sofar 3",sofar,0);
6280 /* Search is recursive or ... */
6281 /* path segment contains wildcards, have to open and search directory. */
6285 debug(F110,"traverse opening directory", sofar, 0);
6288 debug(F110,"traverse opendir()",sofar,0);
6289 if ((fd = opendir(sofar)) == NULL) { /* Can't open, fail. */
6290 debug(F101,"traverse opendir() failed","",errno);
6293 while ((dirbuf = readdir(fd)))
6294 #else /* !OPENDIR */
6295 debug(F110,"traverse directory open()",sofar,0);
6296 if ((fd = open(sofar,O_RDONLY)) < 0) {
6297 debug(F101,"traverse directory open() failed","",errno);
6300 while (read(fd, (char *)dirbuf, sizeof dir_entry) > 0)
6301 #endif /* OPENDIR */
6302 { /* Read each entry in this directory */
6307 /* On some platforms, the read[dir]() can return deleted files, */
6308 /* e.g. HP-UX 5.00. There is no point in grinding through this */
6309 /* routine when the file doesn't exist... */
6311 if ( /* There actually is an inode... */
6319 dirbuf->d_stat.st_ino != 0
6325 dirbuf->d_fileno != 0
6328 dirbuf->d_fileno != 0
6331 dirbuf->d_fileno != 0
6334 dirbuf->d_fileno != 0
6345 #endif /* __FreeBSD__ */
6346 #endif /* __386BSD__ */
6349 #endif /* SOLARIS */
6358 ckstrncpy(nambuf, /* Copy the name */
6362 if (nambuf[0] == '.') {
6363 if (!nambuf[1] || (nambuf[1] == '.' && !nambuf[2])) {
6364 debug(F110,"traverse skipping",nambuf,0);
6365 continue; /* skip "." and ".." */
6368 s = nambuf; /* Copy name to end of sofar */
6370 while ((*eos = *s)) {
6375 Now we check the file for (a) whether it is a directory, and (b) whether
6376 its name matches our pattern. If it is a directory, and if we have been
6377 told to build a recursive list, then we must descend regardless of whether
6378 it matches the pattern. If it is not a directory and it does not match
6379 our pattern, we skip it. Note: sofar is the full pathname, nambuf is
6382 /* Do this first to save pointless function calls */
6383 if (nambuf[0] == '.' && !xmatchdot) /* Dir name starts with '.' */
6386 if (xrecursive || xfilonly || xdironly || xpatslash) {
6387 itsadir = xisdir(sofar); /* See if it's a directory */
6388 debug(F101,"STAT","",19);
6393 itsadir = xisdir(sofar);
6394 debug(F101,"STAT","",20);
6399 if (itsadir && xnolinks) { /* If not following symlinks */
6402 x = lstat(sofar,&buf);
6403 debug(F111,"traverse lstat 2",sofar,x);
6406 S_ISLNK(buf.st_mode)
6409 ((_IFMT & buf.st_mode) == _IFLNK)
6411 #endif /* S_ISLNK */
6415 #endif /* USE_LSTAT */
6416 #endif /* CKSYMLINK */
6419 if (xrecursive > 0 && itsadir &&
6420 (xpatlast[0] == '*') && !xpatlast[1]
6423 "traverse add: recursive && isdir && segisdir or match",
6427 addresult(sofar,itsadir);
6428 if (numfnd < 0) return;
6430 #endif /* RECURSIVE */
6432 debug(F111,"traverse mresult xpat",xpat,xrecursive);
6433 debug(F111,"traverse mresult pl -> npart",
6435 ((pl -> fwd) ? 9999 : 0)
6437 debug(F111,"traverse mresult sofar segisdir",sofar,segisdir);
6438 debug(F111,"traverse mresult sofar itsadir",sofar,itsadir);
6439 debug(F101,"traverse mresult xmatchdot","",xmatchdot);
6441 Match the path so far with the pattern after stripping any leading "./"
6442 from either or both. The pattern chosen is the full original pattern if
6443 the match candidate (sofar) is not a directory, or else just the name part
6444 (pl->npart) if it is.
6447 char * s1; /* The pattern */
6448 char * s2 = sofar; /* The path so far */
6449 char * s3; /* Worker */
6450 int opts; /* Match options */
6452 s1 = itsadir ? pl->npart : xpat;
6455 /* I can't explain this but it unbreaks "cd blah/sub<Esc>" */
6456 if (itsadir && !xrecursive && xpatslash > 0 &&
6457 segisdir == 0 && itswild) {
6459 debug(F110,"traverse mresult s1 kludge",s1,0);
6461 #endif /* COMMENT */
6463 if (xrecursive && xpatslash == 0)
6465 while ((s1[0] == '.') && (s1[1] == '/')) /* Strip "./" */
6467 while ((s2[0] == '.') && (s2[1] == '/')) /* Ditto */
6469 opts = mopts; /* Match options */
6470 if (itsadir) /* Current segment is a directory */
6471 opts = mopts & 1; /* No fences */
6472 s3 = s2; /* Get segment depth */
6474 while (*s3) { if (*s3++ == '/') depth++; }
6476 /* This speeds things up a bit. */
6477 /* If it causes trouble define NOSKIPMATCH and rebuild. */
6478 if (depth == 0 && (s1[0] == '*') && !s1[1])
6479 mresult = xmatchdot ? 1 : (s2[0] != '.');
6481 #endif /* NOSKIPMATCH */
6482 mresult = ckmatch(s1,s2,1,opts); /* Match */
6486 debug(F111,"traverse mresult depth",sofar,depth);
6487 debug(F101,"traverse mresult xpatslash","",xpatslash);
6488 debug(F111,"traverse mresult nambuf",nambuf,mresult);
6489 debug(F111,"traverse mresult itswild",pl -> npart,itswild);
6490 debug(F111,"traverse mresult segisdir",pl -> npart,segisdir);
6493 if (mresult || /* If match succeeded */
6494 xrecursive || /* Or search is recursive */
6495 depth < xpatslash /* Or not deep enough to match... */
6497 if ( /* If it's not a directory... */
6499 The problem here is that segisdir is apparently not set appropriately.
6500 If I leave in the !segisdir test, then "dir /recursive blah" (where blah is
6501 a directory name) misses some regular files because sometimes segisdir
6502 is set and sometimes it's not. But if I comment it out, then
6503 "dir <star>/<star>.txt lists every file in * and does not even open up the
6504 subdirectories. However, "dir /rec <star>/<star>.txt" works right.
6507 mresult && (!itsadir && !segisdir)
6509 mresult && /* Matched */
6510 !itsadir && /* sofar is not a directory */
6511 ((!xrecursive && !segisdir) || xrecursive)
6512 #endif /* COMMENT */
6515 "traverse add: match && !itsadir",sofar,0);
6516 addresult(sofar,itsadir);
6517 if (numfnd < 0) return;
6518 } else if (itsadir && (xrecursive || mresult)) {
6519 struct path * xx = NULL;
6520 *eos++ = DIRSEP; /* Add directory separator */
6521 *eos = '\0'; /* to end of segment */
6523 /* Copy previous pattern segment to this new directory */
6525 if (xrecursive > 0 && !(pl -> fwd)) {
6526 xx = (struct path *) malloc(sizeof (struct path));
6530 strcpy(xx -> npart, pl -> npart); /* safe */
6533 #endif /* RECURSIVE */
6534 traverse(pl -> fwd, sofar, eos); /* Traverse new directory */
6540 #else /* !OPENDIR */
6542 #endif /* OPENDIR */
6547 * Adds a result string to the result array. Increments the number
6548 * of matches found, copies the found string into our string
6549 * buffer, and puts a pointer to the buffer into the caller's result
6550 * array. Our free buffer pointer is updated. If there is no
6551 * more room in the caller's array, the number of matches is set to -1.
6552 * Input: a result string.
6556 addresult(str,itsadir) char *str; int itsadir; {
6560 debug(F100,"addresult string space not init'd","",0);
6561 initspace(mtchs,ssplen);
6564 debug(F111,"addresult",str,itsadir);
6569 itsadir = xisdir(str);
6571 if ((xdironly && !itsadir) || (xfilonly && itsadir)) {
6572 debug(F111,"addresult skip",str,itsadir);
6575 while (str[0] == '.' && ISDIRSEP(str[1])) /* Strip all "./" from front */
6577 if (--remlen < 0) { /* Elements left in array of names */
6578 debug(F111,"addresult ARRAY FULL",str,numfnd);
6582 len = (int)strlen(str); /* Space this will use */
6583 debug(F111,"addresult len",str,len);
6588 if ((freeptr + len + itsadir + 1) > (sspace + ssplen)) {
6589 debug(F111,"addresult OUT OF SPACE",str,numfnd);
6592 "?String space %d exhausted - use SET FILE STRINGSPACE to increase\n",ssplen);
6594 printf("?String space %d exhausted\n",ssplen);
6595 #endif /* DYNAMIC */
6596 numfnd = -1; /* Do not record if not enough space */
6599 strcpy(freeptr,str); /* safe */
6601 /* Tag directory names by putting '/' at the end */
6603 if (itsadir && (freeptr[len-1] == '/')) {
6604 freeptr[len++] = DIRSEP;
6605 freeptr[len] = '\0';
6607 if (numfnd >= maxnames) {
6610 "?Too many files (%d max) - use SET FILE LISTSIZE to increase\n",maxnames);
6612 printf("?Too many files - %d max\n",maxnames);
6613 #endif /* DYNAMIC */
6618 *resptr++ = freeptr;
6619 freeptr += (len + 1);
6621 debug(F111,"addresult ADD",str,numfnd);
6626 * match(pattern,string):
6627 * pattern matcher. Takes a string and a pattern possibly containing
6628 * the wildcard characters '*' and '?'. Returns true if the pattern
6629 * matches the string, false otherwise.
6630 * Orignally by: Jeff Damens, CUCCA, 1984
6631 * No longer used as of C-Kermit 7.0, now we use ckmatch() instead (ckclib.c).
6633 * Input: a string and a wildcard pattern.
6634 * Returns: 1 if match, 0 if no match.
6637 match(pattern, string) char *pattern, *string; {
6638 char *psave = NULL, *ssave = NULL; /* Backup pointers for failure */
6639 int q = 0; /* Quote flag */
6641 if (*string == '.' && *pattern != '.' && !xmatchdot) {
6642 debug(F110,"match skip",string,0);
6646 for (; *pattern == *string; pattern++,string++) /* Skip first */
6647 if (*string == '\0') return(1); /* End of strings, succeed */
6649 if (*pattern == '\\' && q == 0) { /* Watch out for quoted */
6650 q = 1; /* metacharacters */
6651 pattern++; /* advance past quote */
6652 if (*pattern != *string) return(0);
6659 if (*string != '\0' && *pattern == '?') {
6660 pattern++; /* '?', let it match */
6662 } else if (*pattern == '*') { /* '*' ... */
6663 psave = ++pattern; /* remember where we saw it */
6664 ssave = string; /* let it match 0 chars */
6665 } else if (ssave != NULL && *ssave != '\0') { /* if not at end */
6666 /* ...have seen a star */
6667 string = ++ssave; /* skip 1 char from string */
6668 pattern = psave; /* and back up pattern */
6669 } else return(0); /* otherwise just fail */
6673 #endif /* COMMENT */
6676 The following two functions are for expanding tilde in filenames
6677 Contributed by Howie Kaye, CUCCA, developed for CCMD package.
6680 /* W H O A M I -- Get user's username. */
6684 2) See if the $USER environment variable is set ($LOGNAME on AT&T)
6685 3) If $USER's uid is the same as ruid, realname is $USER
6686 4) Otherwise get logged in user's name
6687 5) If that name has the same uid as the real uid realname is loginname
6688 6) Otherwise, get a name for ruid from /etc/passwd
6698 static char realname[UIDBUFLEN+1]; /* user's name */
6699 static int ruid = -1; /* user's real uid */
6700 char loginname[UIDBUFLEN+1], envname[256]; /* temp storage */
6703 _PROTOTYP(extern char * getlogin, (void) );
6705 debug(F111,"whoami ruid A",realname,ruid);
6710 ruid = real_uid(); /* get our uid */
6711 debug(F101,"whoami ruid B","",ruid);
6712 if (ruid < 0) ruid = getuid();
6713 debug(F101,"whoami ruid C","",ruid);
6715 /* how about $USER or $LOGNAME? */
6716 if ((c = getenv(NAMEENV)) != NULL) { /* check the env variable */
6717 ckstrncpy(envname, c, 255);
6718 debug(F110,"whoami envname",envname,0);
6719 if ((p = getpwnam(envname)) != NULL) {
6720 if (p->pw_uid == ruid) { /* get passwd entry for envname */
6721 ckstrncpy(realname, envname, UIDBUFLEN); /* uid's are same */
6722 debug(F110,"whoami realname",realname,0);
6728 /* can we use loginname() ? */
6730 if ((c = getlogin()) != NULL) { /* name from utmp file */
6731 ckstrncpy (loginname, c, UIDBUFLEN);
6732 debug(F110,"whoami loginname",loginname,0);
6733 if ((p = getpwnam(loginname)) != NULL) /* get passwd entry */
6734 if (p->pw_uid == ruid) /* for loginname */
6735 ckstrncpy(realname, envname, UIDBUFLEN); /* if uid's are same */
6738 /* Use first name we get for ruid */
6740 if ((p = getpwuid(ruid)) == NULL) { /* name for uid */
6741 debug(F101,"whoami no username for ruid","",ruid);
6742 realname[0] = '\0'; /* no user name */
6746 ckstrncpy(realname, p->pw_name, UIDBUFLEN);
6747 debug(F110,"whoami realname from getpwuid",realname,0);
6754 /* T I L D E _ E X P A N D -- expand ~user to the user's home directory. */
6757 tilde_expand(dirname) char *dirname; {
6764 struct passwd *user;
6765 static char olddir[BUFLEN+1];
6766 static char oldrealdir[BUFLEN+1];
6767 static char temp[BUFLEN+1];
6770 debug(F111,"tilde_expand",dirname,dirname[0]);
6772 if (dirname[0] != '~') { /* Not a tilde...return param */
6773 debug(F000,"tilde_expand NOT TILDE","",dirname[0]);
6776 if (!strcmp(olddir,dirname)) { /* Same as last time */
6777 debug(F110,"tilde_expand same as previous",oldrealdir,0);
6778 return(oldrealdir); /* so return old answer. */
6780 debug(F110,"tilde_expand working...","",0);
6781 j = (int)strlen(dirname);
6782 for (i = 0; i < j; i++) /* find username part of string */
6783 if (!ISDIRSEP(dirname[i]))
6784 temp[i] = dirname[i];
6786 temp[i] = '\0'; /* tie off with a NULL */
6787 debug(F111,"tilde_expand first part",temp,i);
6788 if (i == 1) { /* if just a "~" */
6791 user = getpwnam(uidbuf); /* Get info on current user */
6795 char * p = whoami();
6796 debug(F110,"tilde_expand p",p,0);
6799 debug(F110,"tilde_expand getpwpam ~",user,0);
6805 debug(F110,"tilde_expand ~user",&temp[1],0);
6806 user = getpwnam(&temp[1]); /* otherwise on the specified user */
6807 debug(F110,"tilde_expand getpwpam user",user,0);
6811 if (user != NULL) { /* valid user? */
6812 ckstrncpy(olddir, dirname, BUFLEN); /* remember the directory */
6813 ckstrncpy(oldrealdir,user->pw_dir, BUFLEN); /* and home directory */
6814 ckstrncat(oldrealdir,&dirname[i], BUFLEN);
6815 oldrealdir[BUFLEN] = '\0';
6817 } else { /* invalid? */
6818 ckstrncpy(olddir, dirname, BUFLEN); /* remember for next time */
6819 ckstrncpy(oldrealdir, dirname, BUFLEN);
6828 Functions for executing system commands.
6829 zsyscmd() executes the system command in the normal, default way for
6830 the system. In UNIX, it does what system() does. Thus, its results
6831 are always predictable.
6832 zshcmd() executes the command using the user's preferred shell.
6835 zsyscmd(s) char *s; {
6837 if (nopush) return(-1);
6838 if (!priv_chk()) return(system(s));
6842 /* This doesn't work... */
6846 #endif /* COMMENT */
6848 if (nopush) return(-1);
6849 if ((shpid = fork())) {
6850 if (shpid < (PID_T)0) return(-1); /* Parent */
6851 while (shpid != (PID_T) wait(&status))
6855 if (priv_can()) { /* Child: cancel any priv's */
6856 printf("?Privilege cancellation failure\n");
6859 restorsigs(); /* Restore ignored signals */
6861 execl("/usr/bin/sh","sh","-c",s,NULL);
6862 perror("/usr/bin/sh");
6865 execl("/bin/rc", "rc", "-c", s, NULL);
6868 execl("/bin/sh","sh","-c",s,NULL);
6873 return(0); /* Shut up ANSI compilers. */
6878 /* Z _ E X E C -- Overlay ourselves with another program */
6886 #endif /* ATT7300 */
6888 #endif /* NOZEXEC */
6891 z_exec(p,s,t) char * p, ** s; int t; { /* Overlay ourselves with "p s..." */
6893 printf("EXEC /REDIRECT NOT IMPLEMENTED IN THIS VERSION OF C-KERMIT\n");
6894 debug(F110,"z_exec NOT IMPLEMENTED",p,0);
6898 debug(F110,"z_exec command",p,0);
6899 debug(F110,"z_exec arg 0",s[0],0);
6900 debug(F110,"z_exec arg 1",s[1],0);
6901 debug(F101,"z_exec t","",t);
6907 /* dup2(ttyfd, 2); */
6911 restorsigs(); /* Restore ignored signals */
6913 if (x < 0) debug(F101,"z_exec errno","",errno);
6914 #endif /* NOZEXEC */
6918 Z S H C M D -- Execute a shell command (or program thru the shell).
6920 Original UNIX code by H. Fischer; copyright rights assigned to Columbia U.
6921 Adapted to use getpwuid to find login shell because many systems do not
6922 have SHELL in environment, and to use direct calling of shell rather
6923 than intermediate system() call. -- H. Fischer (1985); many changes since
6924 then. Call with s pointing to command to execute. Returns:
6925 -1 on failure to start the command (can't find, can't fork, can't run).
6926 1 if command ran and gave an exit status of 0.
6927 0 if command ran and gave a nonzero exit status.
6928 with pexitstatus containing the command's exit status.
6931 zshcmd(s) char *s; {
6937 if (nopush) return(-1);
6939 while (*s == ' ') s++;
6941 debug(F110,"zshcmd command",s,0);
6944 if ((pid = vfork()) == 0) { /* Make child quickly */
6945 char *shpath, *shname, *shptr; /* For finding desired shell */
6947 if (priv_can()) exit(1); /* Turn off privs. */
6948 if ((shpath = getenv("SHELL")) == NULL) shpath = "/com/sh";
6950 #else /* All Unix systems */
6951 if ((pid = fork()) == 0) { /* Make child */
6952 char *shpath, *shname, *shptr; /* For finding desired shell */
6954 #ifdef HPUX10 /* Default */
6955 char *defshell = "/usr/bin/sh";
6958 char *defshell = "/bin/rc";
6960 char *defshell = "/bin/sh";
6963 if (priv_can()) exit(1); /* Turn off privs. */
6965 /* Old way always used /etc/passwd shell */
6966 p = getpwuid(real_uid()); /* Get login data */
6967 if (p == (struct passwd *) NULL || !*(p->pw_shell))
6970 shpath = p->pw_shell;
6972 /* New way lets user override with SHELL variable, but does not rely on it. */
6973 /* This allows user to specify a different shell. */
6974 shpath = getenv("SHELL"); /* What shell? */
6975 debug(F110,"zshcmd SHELL",shpath,0);
6980 } else if (!*shpath) {
6984 debug(F100,"zshcmd SHELL not defined","",0);
6985 p = getpwuid( real_uid() ); /* Get login data */
6986 if (p == (struct passwd *)NULL || !*(p->pw_shell)) {
6989 shpath = p->pw_shell;
6991 debug(F110,"zshcmd shpath from getpwuid",shpath,0);
6994 #endif /* COMMENT */
6996 shptr = shname = shpath;
6997 while (*shptr != '\0')
6998 if (*shptr++ == DIRSEP)
7000 restorsigs(); /* Restore ignored signals */
7001 debug(F110,"zshcmd execl shpath",shpath,0);
7002 debug(F110,"zshcmd execl shname",shname,0);
7003 if (s == NULL || *s == '\0') { /* Interactive shell requested? */
7004 debug(F100,"zshcmd execl interactive","",0);
7005 execl(shpath,shname,"-i",NULL); /* Yes, do that */
7006 } else { /* Otherwise, */
7007 debug(F110,"zshcmd execl command",s,0);
7008 execl(shpath,shname,"-c",s,NULL); /* exec the given command */
7009 } /* If execl() failed, */
7010 debug(F101,"zshcmd errno","",errno);
7011 perror(shpath); /* print reason and */
7012 exit(BAD_EXIT); /* return bad return code. */
7014 } else { /* Parent */
7016 int wstat; /* ... must wait for child */
7018 int child; /* Child's exit status */
7019 #endif /* CK_CHILD */
7020 SIGTYP (*istat)(), (*qstat)();
7022 if (pid == (PID_T) -1) return(-1); /* fork() failed? */
7024 istat = signal(SIGINT,SIG_IGN); /* Let the fork handle keyboard */
7025 qstat = signal(SIGQUIT,SIG_IGN); /* interrupts itself... */
7027 debug(F110,"zshcmd parent waiting for child",s,0);
7029 while (((wstat = wait(&child)) != pid) && (wstat != -1))
7031 while (((wstat = wait((WAIT_T *)0)) != pid) && (wstat != -1))
7032 #endif /* CK_CHILD */
7033 ; /* Wait for fork */
7034 signal(SIGINT,istat); /* Restore interrupts */
7035 signal(SIGQUIT,qstat);
7037 pexitstat = (child & 0xff) ? child : child >> 8;
7038 debug(F101,"zshcmd exit status","",pexitstat);
7039 return(child == 0 ? 1 : 0); /* Return child's status */
7040 #endif /* CK_CHILD */
7046 /* I S W I L D -- Check if filespec is "wild" */
7050 0 wildcards disabled or argument is empty or is the name of a single file;
7051 1 if it contains wildcard characters.
7052 Note: must match the algorithm used by match(), hence no [a-z], etc.
7055 iswild(filespec) char *filespec; {
7056 char c, *p, *f; int x;
7058 if (!filespec) /* Safety */
7060 if (!wildena) /* Wildcards disabled - 12 Jun 2005 */
7063 if (wildxpand) { /* Shell handles wildcarding */
7064 if ((x = nzxpand(filespec,0)) > 1)
7066 if (x == 0) return(0); /* File does not exist */
7067 p = malloc(MAXNAMLEN + 20);
7069 x = (strcmp(filespec,p) != 0);
7073 } else { /* We do it ourselves */
7074 while ((c = *filespec++) != '\0') {
7075 if (c == '\\' && quo == 0) {
7079 if (!quo && (c == '*' || c == '?'
7085 #endif /* CKREGEX */
7087 debug(F111,"iswild",f,1);
7092 debug(F111,"iswild",f,0);
7098 I S D I R -- Is a Directory.
7100 Tell if string pointer s is the name of an existing directory. Returns 1 if
7101 directory, 0 if not a directory.
7103 The following no longer applies:
7105 If the file is a symlink, we return 1 if
7106 it is a directory OR if it is a link to a directory and the "xrecursive" flag
7107 is NOT set. This is to allow parsing a link to a directory as if it were a
7108 directory (e.g. in the CD or IF DIRECTORY command) but still prevent
7109 recursive traversal from visiting the same directory twice.
7113 /* This turns out to be unsafe and gives little benefit anyway. */
7114 /* See notes 28 Sep 2003. Thus ISDIRCACHE is not defined. */
7116 static char prevpath[CKMAXPATH+4] = { '\0', '\0' };
7117 static int prevstat = -1;
7120 debug(F100,"CLEAR ISDIR CACHE","",0);
7124 #endif /* ISDIRCACHE */
7127 isalink(s) char *s; {
7132 char filbuf[CKMAXPATH+4];
7133 if (readlink(s,filbuf,CKMAXPATH) > -1)
7135 debug(F110,"isalink readlink",s,r);
7137 #endif /* CKSYMLINK */
7142 int x, needrlink = 0, islink = 0;
7143 struct stat statbuf;
7144 char fnam[CKMAXPATH+4];
7147 debug(F110,"isdir entry",s,0);
7148 #ifdef DTILDE /* 2005-08-13 */
7149 if (*s == '~') { /* Starts with tilde? */
7150 s = tilde_expand(s); /* Attempt to expand tilde */
7152 debug(F110,"isdir tilde_expand",s,0);
7158 if (prevstat > -1) {
7159 if (s[0] == prevpath[0]) {
7160 if (!strcmp(s,prevpath)) {
7161 debug(F111,"isdir cache hit",s,prevstat);
7166 #endif /* ISDIRCACHE */
7171 The following over-clever bit has been commented out because it presumes
7172 to know when a symlink might be redundant, which it can't possibly know.
7173 Using plain old stat() gives Kermit the same results as ls and ls -R, which
7174 is just fine: no surprises.
7178 x = lstat(s,&statbuf);
7179 debug(F111,"isdir lstat",s,x);
7181 #endif /* USE_LSTAT */
7182 x = stat(s,&statbuf);
7183 debug(F111,"isdir stat",s,x);
7186 #endif /* USE_LSTAT */
7188 x = stat(s,&statbuf);
7189 debug(F111,"isdir stat",s,x);
7190 #endif /* COMMENT */
7192 debug(F101,"isdir errno","",errno);
7201 islink = S_ISLNK(statbuf.st_mode);
7202 debug(F101,"isdir S_ISLNK islink","",islink);
7205 islink = (_IFMT & statbuf.st_mode) == _IFLNK;
7206 debug(F101,"isdir _IFLNK islink","",islink);
7208 #endif /* S_ISLNK */
7209 #endif /* NOLINKBITS */
7211 if (readlink(s,fnam,CKMAXPATH) > -1)
7216 x = stat(s,&statbuf);
7218 debug(F101,"isdir errno","",errno);
7221 debug(F111,"isdir stat",s,x);
7222 #endif /* CKSYMLINK */
7223 debug(F101,"isdir islink","",islink);
7224 debug(F101,"isdir statbuf.st_mode","",statbuf.st_mode);
7225 x = islink ? 0 : (S_ISDIR (statbuf.st_mode) ? 1 : 0);
7228 ckstrncpy(prevpath,s,CKMAXPATH+1);
7229 #endif /* ISDIRCACHE */
7234 /* Some systems don't have mkdir(), e.g. Tandy Xenix 3.2.. */
7236 /* Z M K D I R -- Create directory(s) if necessary */
7239 A pointer to a file specification that might contain directory
7240 information. The filename is expected to be included.
7241 If the file specification does not include any directory separators,
7242 then it is assumed to be a plain file.
7243 If one or more directories are included in the file specification,
7244 this routine tries to create them if they don't already exist.
7246 0 or greater on success, i.e. the number of directories created.
7247 -1 on failure to create the directory
7250 zmkdir(path) char *path; {
7254 if (!path) path = "";
7255 if (!*path) return(-1);
7258 debug(F111,"zmkdir setroot",ckroot,ckrootset);
7259 if (ckrootset) if (!zinroot(path)) {
7260 debug(F110,"zmkdir setroot violation",path,0);
7266 debug(F111,"zmkdir",path,x);
7267 if (x < 1 || x > MAXPATH) /* Check length */
7269 if (!(tp = malloc(x+1))) /* Make a temporary copy */
7271 strcpy(tp,path); /* safe (prechecked) */
7273 if (*tp == '~') { /* Starts with tilde? */
7274 xp = tilde_expand(tp); /* Attempt to expand tilde */
7278 debug(F110,"zmkdir tilde_expand",xp,0);
7279 if (!(zp = malloc(strlen(xp) + 1))) { /* Make a place for it */
7284 free(tp); /* Free previous buffer */
7285 tp = zp; /* Point to new one */
7286 strcpy(tp,xp); /* Copy expanded name to new buffer */
7290 debug(F110,"zmkdir tp after tilde_expansion",tp,0);
7292 if (ISDIRSEP(*xp)) /* Don't create root directory! */
7295 /* Go thru filespec from left to right... */
7297 for (; *xp; xp++) { /* Create parts that don't exist */
7298 if (!ISDIRSEP(*xp)) /* Find next directory separator */
7300 c = *xp; /* Got one. */
7301 *xp = NUL; /* Make this the end of the string. */
7302 if (!isdir(tp)) { /* This directory exists already? */
7304 if (isguest) /* Not allowed for guests */
7307 /* Nor if MKDIR and/or CD are disabled */
7309 #endif /* CK_LOGIN */
7314 ) && (!ENABLED(en_mkd) || !ENABLED(en_cwd)))
7318 debug(F110,"zmkdir making",tp,0);
7319 x = /* No, try to create it */
7321 -1 /* Systems without mkdir() */
7323 mkdir(tp,0777) /* UNIX */
7324 #endif /* NOMKDIR */
7327 debug(F101,"zmkdir failed, errno","",errno);
7328 free(tp); /* Free temporary buffer. */
7330 return(-1); /* Return failure code. */
7334 *xp = c; /* Replace the separator. */
7336 free(tp); /* Free temporary buffer. */
7337 return(count); /* Return success code. */
7339 #endif /* CK_MKDIR */
7342 zrmdir(path) char *path; {
7346 #endif /* CK_LOGIN */
7348 if (!path) path = "";
7349 if (!*path) return(-1);
7352 debug(F111,"zrmdir setroot",ckroot,ckrootset);
7353 if (ckrootset) if (!zinroot(path)) {
7354 debug(F110,"zrmdir setroot violation",path,0);
7360 return(rmdir(path));
7363 #endif /* NOMKDIR */
7366 /* Z F S E E K -- Position input file pointer */
7369 CK_OFF_T (32 or 64 bits), 0-based, indicating desired position.
7377 zfseek(CK_OFF_T pos)
7379 zfseek(pos) CK_OFF_T pos;
7380 #endif /* CK_ANSIC */
7382 zincnt = -1; /* Must empty the input buffer */
7383 debug(F101,"zfseek","",pos);
7384 return(CKFSEEK(fp[ZIFILE], pos, 0)?-1:0);
7386 #endif /* NORESEND */
7388 /* Z F N Q F P -- Convert filename to fully qualified absolute pathname */
7391 Given a possibly unqualified or relative file specification fn, zfnqfp()
7392 returns the fully qualified filespec for the same file, returning a struct
7393 that contains the length (len) of the result, a pointer (fpath) to the
7394 whole result, and a pointer (fname) to where the filename starts.
7396 static struct zfnfp fnfp = { 0, NULL, NULL };
7399 zfnqfp(fname, buflen, buf) char * fname; int buflen; char * buf; {
7403 char zfntmp[MAXPATHLEN+4];
7405 char zfntmp[CKMAXPATH+4];
7406 #endif /* MAXPATHLEN */
7409 int i = 0, j = 0, k = 0, x = 0, y = 0;
7420 /* Initialize the data structure */
7422 fnfp.len = ckstrncpy(buf,fname,buflen);
7426 debug(F111,"zfnqfp fname",fname,len);
7429 if (*s == '~') { /* Starts with tilde? */
7431 xp = tilde_expand(s); /* Attempt to expand tilde */
7432 debug(F110,"zfnqfp xp",xp,0); /* (realpath() doesn't do this) */
7441 /* N.B.: The realpath() result buffer MUST be MAXPATHLEN bytes long */
7442 /* otherwise we write over memory. */
7444 if (!realpath(s,zfntmp)) {
7445 debug(F111,"zfnqfp realpath fails",s,errno);
7447 if (errno != ENOENT)
7450 /* If realpath() fails use the do-it-yourself method */
7453 #endif /* COMMENT */
7455 len = strlen(zfntmp);
7457 debug(F111,"zfnqfp result too long",ckitoa(buflen),len);
7460 ckstrncpy(buf,zfntmp,buflen);
7462 if (buf[len-1] != '/') {
7463 if ((itsadir = isdir(buf)) && len < (buflen - 1)) {
7470 debug(F110,"zfnqfp realpath path",fnfp.fpath,0);
7471 tmp = buf + fnfp.len - 1;
7473 while (tmp >= buf) {
7475 fnfp.fname = tmp + 1;
7476 debug(F110,"zfnqfp realpath name",fnfp.fname,0);
7484 #endif /* CKREALPATH */
7489 while (*s) { /* Remove leading "./" (0 or more) */
7490 debug(F110,"zfnqfp while *s",s,0);
7491 if (*s == '.' && *(s+1) == '/') {
7493 while (*s == '/') s++;
7497 if (!*s) return(NULL);
7498 if (*s == '/') { /* Pathname is absolute */
7499 ckstrncpy(buf,s,len);
7502 } else { /* Pathname is relative */
7504 if (p = zgtdir()) { /* So get current directory */
7505 debug(F110,"zfnqfp zgtdir",p,0);
7506 x = ckstrncpy(buf,p,len);
7508 debug(F110,"zfnqfp buf 1",buf,0);
7509 len -= x; /* How much room left in buffer */
7510 if ((y = (int)strlen(s)) > len) /* If enough room... */
7512 ckstrncpy(buf+x,s,len); /* ... append the filename */
7513 debug(F110,"zfnqfp buf 2",buf,0);
7519 /* Buf now holds full path but maybe containing some . or .. tricks */
7521 j = x + y; /* Length of what's in buf */
7523 debug(F101,"zfnqfp len","",len);
7525 /* Catch dangling "/." or "/.." */
7526 if ((j > 1 && buf[j-1] == '.' && buf[j-2] == '/') ||
7527 (j > 2 && buf[j-1] == '.' && buf[j-2] == '.' && buf[j-3] == '/')) {
7528 if (j < buflen - 2) {
7533 j = -1; /* j = position of rightmost "/" */
7534 i = 0; /* i = destination index */
7535 tmp[i] = NUL; /* destination is temporary buffer */
7537 for (x = 0; x < len; x++) { /* x = source index */
7538 if (buf[x] == '/') {
7539 for (k = 0; k < 4; k++) {
7544 if (!strncmp(sb,"/./",3)) { /* Eliminate "./" in "/./" */
7547 } else if (!strncmp(sb,"//",2)) { /* Change "//" to "/" */
7549 } else if (!strncmp(sb,"/../",4)) { /* ".." in path */
7550 for (k = i - 1; k >= 0; k--) { /* Back up one level */
7551 if (tmp[k] == '/') {
7561 if (i >= (buflen - 1)) {
7562 debug(F111,"zfnqfp overflow",tmp,i);
7565 tmp[i++] = buf[x]; /* Regular character, copy */
7567 if (buf[x] == '/') /* Remember rightmost "/" */
7570 ckstrncpy(buf,tmp,buflen-1); /* Copy the result back */
7572 buf[buflen-1] = NUL;
7573 if (!buf[0]) { /* If empty, say root */
7579 if ((itsadir = isdir(buf))) {
7580 if (buf[i-1] != '/' && i < (buflen - 1)) {
7585 if (!itsadir && (j > -1)) { /* Set pointer to basename */
7586 fnfp.fname = (char *)(buf + j);
7587 fnfp.fpath = (char *)buf;
7589 debug(F111,"zfnqfp path",fnfp.fpath,i);
7590 debug(F110,"zfnqfp name",fnfp.fname,0);
7596 /* Z C M P F N -- Compare two filenames */
7598 /* Returns 1 if the two names refer to the same existing file, 0 otherwise. */
7601 zcmpfn(s1,s2) char * s1, * s2; {
7602 char buf1[CKMAXPATH+1];
7603 char buf2[CKMAXPATH+1];
7606 char linkname[CKMAXPATH+1];
7608 #endif /* USE_LSTAT */
7613 if (!*s1 || !*s2) return(0);
7615 #ifdef CKSYMLINK /* We're doing symlinks? */
7616 #ifdef USE_LSTAT /* OK to use lstat()? */
7618 if (x > -1 && /* Now see if it's a symlink */
7620 S_ISLNK(buf.st_mode)
7623 ((_IFMT & buf.st_mode) == _IFLNK)
7625 #endif /* S_ISLNK */
7627 linkname[0] = '\0'; /* Get the name */
7628 x = readlink(s1,linkname,CKMAXPATH);
7629 if (x > -1 && x < CKMAXPATH) { /* It's a link */
7634 #endif /* USE_LSTAT */
7635 #endif /* CKSYMLINK */
7637 if (zfnqfp(s1,CKMAXPATH,buf1)) { /* Convert to full pathname */
7639 #ifdef CKSYMLINK /* Same deal for second name... */
7644 S_ISLNK(buf.st_mode)
7647 ((_IFMT & buf.st_mode) == _IFLNK)
7649 #endif /* S_ISLNK */
7652 x = readlink(s2,linkname,CKMAXPATH);
7653 if (x > -1 && x < CKMAXPATH) {
7658 #endif /* USE_LSTAT */
7659 #endif /* CKSYMLINK */
7660 if (zfnqfp(s2,CKMAXPATH,buf2)) {
7661 debug(F110,"zcmpfn s1",buf1,0);
7662 debug(F110,"zcmpfn s2",buf2,0);
7663 if (!strncmp(buf1,buf2,CKMAXPATH))
7667 debug(F101,"zcmpfn result","",rc);
7673 /* User-mode chroot() implementation */
7676 zsetroot(s) char * s; { /* Sets the root */
7677 char buf[CKMAXPATH+1];
7679 if (!*s) return(-1);
7680 debug(F110,"zsetroot",s,0);
7681 if (!isdir(s)) return(-2);
7682 if (!zfnqfp(s,CKMAXPATH,buf)) /* Get full, real path */
7684 if (access(buf,R_OK) < 0) { /* Check access */
7685 debug(F110,"zsetroot access denied",buf,0);
7689 if (ckrootset) { /* If root already set */
7690 if (!zinroot(s)) { /* make sure new root is in it */
7691 debug(F110,"zsetroot new root not in root",ckroot,0);
7695 if (zchdir(buf) < 1) return(-4); /* Change directory to new root */
7696 ckrootset = ckstrncpy(ckroot,buf,CKMAXPATH); /* Now set the new root */
7697 if (ckroot[ckrootset-1] != '/') {
7698 ckroot[ckrootset++] = '/';
7699 ckroot[ckrootset] = '\0';
7701 debug(F111,"zsetroot rootset",ckroot,ckrootset);
7702 ckrooterr = 0; /* Reset error flag */
7707 zgetroot() { /* Returns the root */
7710 return((char *)ckroot);
7714 zinroot(s) char * s; { /* Checks if file s is in the root */
7716 struct zfnfp * f = NULL;
7717 char buf[CKMAXPATH+2];
7719 debug(F111,"zinroot setroot",ckroot,ckrootset);
7720 ckrooterr = 0; /* Reset global error flag */
7721 if (!ckrootset) /* Root not set */
7722 return(1); /* so it's ok - no need to check */
7723 if (!(f = zfnqfp(s,CKMAXPATH,buf))) /* Get full and real pathname */
7724 return(0); /* Fail if we can't */
7725 n = f->len; /* Length of full pathname */
7726 debug(F111,"zinroot n",buf,n);
7727 if (n < (ckrootset - 1) || n > CKMAXPATH) { /* Bad length */
7728 ckrooterr = 1; /* Fail */
7731 if (isdir(buf) && buf[n-1] != '/') { /* If it's a directory name */
7732 buf[n++] = '/'; /* make sure it ends with '/' */
7735 x = strncmp(buf,ckroot,ckrootset); /* Compare, case-sensitive */
7736 debug(F111,"zinroot checked",buf,x);
7737 if (x == 0) /* OK */
7739 ckrooterr = 1; /* Not OK */
7746 The following code provides support for user login and logout
7747 including anonymous accounts. If this feature is to be supported
7748 outside of UNIX, it should be spread out among the ck?fio.c modules...
7750 #ifndef _PATH_BSHELL
7751 #define _PATH_BSHELL "/usr/bin/bash"
7752 #endif /* _PATH_BSHELL */
7753 #ifndef _PATH_FTPUSERS
7754 #define _PATH_FTPUSERS "/etc/ftpusers"
7755 #endif /* _PATH_FTPUSERS */
7758 * Helper function for sgetpwnam().
7761 sgetsave(s) char *s; {
7762 char *new = malloc((unsigned) strlen(s) + 1);
7764 printf("?Local resource failure: malloc\n");
7768 (void) strcpy(new, s); /* safe */
7773 * Save the result of getpwnam(). Used for USER command, since
7774 * the data returned must not be clobbered by any other command
7778 sgetpwnam(name) char *name; {
7779 static struct passwd save;
7780 register struct passwd *p;
7782 register struct spwd *sp;
7783 #endif /* CK_SHADOW */
7786 #ifdef HPUX10_TRUSTED
7787 struct pr_passwd *pr;
7788 #endif /* HPUX10_TRUSTED */
7791 sp = getspnam(name);
7793 debug(F110,"sgetpwnam","getspnam() fails",0);
7796 #endif /* CK_SHADOW */
7798 #ifdef HPUX10_TRUSTED
7799 if ((pr = getprpwnam(name)) == NULL)
7801 #endif /* HPUX10_TRUSTED */
7804 /* debug(F111,"sgetpwnam","getpwnam()",p); */
7809 free(save.pw_passwd);
7810 free(save.pw_gecos);
7812 free(save.pw_shell);
7815 save.pw_name = sgetsave(p->pw_name);
7817 save.pw_passwd = sgetsave(sp->sp_pwdp);
7818 #else /* CK_SHADOW */
7819 #ifdef HPUX10_TRUSTED
7820 if (pr->uflg.fg_encrypt && pr->ufld.fd_encrypt && *pr->ufld.fd_encrypt)
7821 save.pw_passwd = sgetsave(pr->ufld.fd_encrypt);
7823 save.pw_passwd = sgetsave("");
7824 #else /* HPUX10_TRUSTED */
7825 save.pw_passwd = sgetsave(p->pw_passwd);
7826 #endif /* HPUX10_TRUSTED */
7827 #endif /* CK_SHADOW */
7828 save.pw_gecos = sgetsave(p->pw_gecos);
7829 save.pw_dir = sgetsave(p->pw_dir);
7830 save.pw_shell = sgetsave(p->pw_shell);
7834 #define CKXLOGBSIZ 256
7836 struct passwd * pw = NULL;
7837 char * home = NULL; /* Home directory pointer for glob */
7844 int defumask = CMASK; /* Default umask value */
7846 /* Z V U S E R -- Verify user, Returns 1 if user OK, 0 otherwise. */
7848 /* Sets global passwd pointer pw if named account exists and is acceptable;
7849 * sets askpasswd if a PASS command is expected. If logged in previously,
7850 * need to reset state. If name is "ftp" or "anonymous", the name is not in
7851 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
7852 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
7853 * requesting login privileges. Disallow anyone who does not have a standard
7854 * shell as returned by getusershell(). Disallow anyone mentioned in the file
7855 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
7857 _PROTOTYP(static int checkuser, (char *) );
7859 char zvuname[64] = { NUL, NUL };
7860 char zvhome[CKMAXPATH+1] = { NUL, NUL };
7862 #define ZENVHOME CKMAXPATH+12
7863 #define ZENVLOGNAME 74
7864 static char zenvuser[ZENVUSER];
7865 static char zenvhome[ZENVHOME];
7866 static char zenvlogname[ZENVLOGNAME];
7869 static char pam_data[500];
7870 struct pam_conv pam_conv = {pam_cb, pam_data}; /* PAM structure */
7871 struct pam_handle * pamh = NULL; /* PAM reference handle */
7875 zvuser(name) char *name; {
7876 register char *cp = NULL;
7880 _PROTOTYP(char * getusershell, (void) );
7881 #endif /* GETUSERSHELL */
7882 #ifndef NODCLENDUSERSHELL
7883 _PROTOTYP(VOID endusershell, (void) );
7884 #endif /* NODCLENDUSERSHELL */
7888 const char * reply = NULL;
7891 debug(F111,"user",name,logged_in);
7893 if (!name) name = "";
7896 debug(F101,"zvuser ckxsyslog","",ckxsyslog);
7899 debug(F100,"zvuser CKSYSLOG defined","",0);
7900 #endif /* CKSYSLOG */
7902 if (logged_in) /* Should not be called if logged in */
7906 if (ckxsyslog && ckxlogging) {
7908 "login: user %s",name
7911 #endif /* CKSYSLOG */
7913 guest = 0; /* Assume not guest */
7916 if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
7917 debug(F101,"zvuser anonymous ckxanon","",ckxanon);
7918 if (!ckxanon) { /* Anonymous login not allowed */
7920 if (ckxsyslog && ckxlogging) {
7922 "login: anonymous login not allowed: %s",
7923 clienthost ? clienthost : "(unknown host)"
7926 #endif /* CKSYSLOG */
7929 if (checkuser("ftp") || checkuser("anonymous")) {
7930 debug(F100,"zvuser anon forbidden by ftpusers file","",0);
7932 if (ckxsyslog && ckxlogging) {
7934 "login: anonymous login forbidden by ftpusers file: %s",
7935 clienthost ? clienthost : "(unknown host)"
7938 #endif /* CKSYSLOG */
7940 } else if ((pw = sgetpwnam("ftp")) != NULL) {
7941 debug(F100,"zvuser anon sgetpwnam(ftp) OK","",0);
7944 ckstrncpy(zvuname,"anonymous",64);
7947 debug(F100,"zvuser anon sgetpwnam(ftp) FAILED","",0);
7949 if (ckxsyslog && ckxlogging) {
7951 "login: anonymous getpwnam(ftp) failed: %s",
7952 clienthost ? clienthost : "(unknown host)"
7955 #endif /* CKSYSLOG */
7959 pw = sgetpwnam(name);
7962 Of course some UNIX platforms (like AIX) don't have getusershell().
7963 In that case we can't check if the user's account has been "turned off"
7964 or somesuch, e.g. by setting their shell to "/etc/nologin" or somesuch,
7965 which runs (usually just printing a message and exiting), but which is
7966 not listed in /etc/shells. For that matter, if getusershell() is not
7967 available, then probably neither is /etc/shells.
7969 debug(F100,"zvuser sgetpwnam ok","",0);
7970 shell = pw->pw_shell;
7971 if (!shell) shell = "";
7973 shell = _PATH_BSHELL;
7974 debug(F110,"zvuser shell",shell,0);
7976 while ((cp = getusershell()) != NULL) {
7977 debug(F110,"zvuser getusershell",cp,0);
7978 if ((int)strcmp(cp, shell) == 0)
7981 debug(F100,"zvuser endusershell 1","",0);
7982 #ifndef NODCLENDUSERSHELL
7983 (VOID) endusershell();
7986 #endif /* NODCLENDUSERSHELL */
7987 debug(F100,"zvuser endusershell 2","",0);
7988 #else /* GETUSERSHELL */
7989 cp = ""; /* Do not refuse if we cannot check */
7990 #endif /* GETUSERSHELL */
7991 x = checkuser(name);
7992 debug(F101,"zvuser checkuser","",x);
7994 debug(F100,"zvuser refused 1","",0);
7995 pw = (struct passwd *) NULL;
7997 if (ckxsyslog && ckxlogging) {
7999 "login: invalid shell %s for %s %s",shell, name,
8000 clienthost ? clienthost : "(unknown host)"
8003 #endif /* CKSYSLOG */
8006 debug(F100,"zvuser refused 2","",0);
8007 pw = (struct passwd *) NULL;
8009 if (ckxsyslog && ckxlogging) {
8011 "login: %s login forbidden by ftpusers file: %s",
8012 name, clienthost ? clienthost : "(unknown host)"
8015 #endif /* CKSYSLOG */
8020 /* Get PAM authentication details */
8021 debug(F110,"zvuser","calling pam_start",0);
8023 pam_start(PAM_SERVICE_TYPE,name,&pam_conv,&pamh))
8025 reply = pam_strerror(NULL, pam_status);
8026 debug(F110,"zvuser PAM failure",reply,0);
8027 printf("%s\n",reply);
8029 if (ckxsyslog && ckxlogging) {
8031 "login: %s refused by PAM \"%s\": %s",
8033 clienthost ? clienthost : "(unknown host)"
8036 #endif /* CKSYSLOG */
8041 ckstrncpy(zvuname,name,64);
8046 debug(F100,"zvuser sgetpwnam NULL","",0);
8048 if (ckxsyslog && ckxlogging) {
8050 "login: getpwnam(%s) failed: %s",name,
8051 clienthost ? clienthost : "(unknown host)"
8054 #endif /* CKSYSLOG */
8059 if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) {
8061 /* Why sprintf and then printf? */
8062 /* Also, what is kerb_ok? And is the test on it right? */
8063 char buf[CKXLOGBSIZ];
8064 sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s%s",
8065 kdata.pname, *kdata.pinst ? "." : "",
8066 kdata.pinst, kdata.prealm,
8067 (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not",
8068 name, kerb_ok ? "" : "; Password required.");
8071 printf("Kerberos user %s%s%s@%s is%s authorized as %s%s",
8072 kdata.pname, *kdata.pinst ? "." : "",
8073 kdata.pinst, kdata.prealm,
8074 (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not",
8075 name, kerb_ok ? "" : "; Password required.");
8076 #endif /* COMMENT */
8077 if (kerb_ok) return(1);
8080 #endif /* FTP_KERBEROS */
8083 /* Check if the given user is in the forbidden-user file */
8086 checkuser(name) char *name; {
8087 extern char * userfile;
8090 char line[CKXLOGBSIZ+1];
8095 debug(F111,"checkuser name",name,i);
8099 fd = fopen(userfile ? userfile : _PATH_FTPUSERS, "r");
8100 /* debug(F111,"checkuser userfile",userfile,fd); */
8103 while (fgets(line, sizeof(line), fd)) {
8104 debug(F110,"checkuser line",line,0);
8107 if (strncmp(line, name, i) == 0) {
8108 debug(F110,"checkuser REFUSED",name,0);
8115 debug(F110,"checkuser OK",name,0);
8119 /* Z V L O G O U T -- Log out from Internet Kermit Service */
8124 /* This could be dangerous */
8125 if (setuid((UID_T)0) < 0) {
8126 debug(F100,"zvlogout setuid FAILED","",0);
8129 debug(F100,"zvlogout setuid OK","",0);
8130 #endif /* COMMENT */
8132 if (ckxsyslog >= SYSLG_LI && ckxlogging) {
8133 cksyslog(SYSLG_LI, 1, "logout",(char *) uidbuf, clienthost);
8135 #endif /* CKSYSLOG */
8137 debug(F110,"WTMP logout",cksysline,logged_in);
8139 logwtmp(cksysline, "", "");
8148 kpass(name, p) char *name, *p; {
8149 char instance[INST_SZ];
8150 char realm[REALM_SZ];
8154 unsigned long faddr;
8157 if (krb_get_lrealm(realm, 1) != KSUCCESS)
8160 ckstrncpy(tkt_file, TKT_ROOT, 20);
8161 ckstrncat(tkt_file, "_ftpdXXXXXX", 20);
8162 krb_set_tkt_string(mktemp(tkt_file));
8164 (VOID) ckstrncpy(instance, krb_get_phost(hostname), sizeof(instance));
8166 if ((hp = gethostbyname(instance)) == NULL)
8170 hp = ck_copyhostent(hp); /* safe copy that won't change */
8171 #endif /* HADDRLIST */
8172 bcopy((char *)hp->h_addr, (char *) &faddr, sizeof(faddr));
8174 if (krb_get_pw_in_tkt(name, "", realm, "krbtgt", realm, 1, p) ||
8175 krb_mk_req(&ticket, "rcmd", instance, realm, 33) ||
8176 krb_rd_req(&ticket, "rcmd", instance, faddr, &authdata, "") ||
8177 kuserok(&authdata, name)) {
8184 #endif /* FTP_KERBEROS */
8189 if (ckxsyslog && !ckxlogging) {
8191 openlog(inserver ? "iksd" : "ckermit", LOG_PID, LOG_DAEMON);
8193 openlog(inserver ? "iksd" : "ckermit", LOG_PID);
8194 #endif /* LOG_DAEMON */
8196 debug(F100,"zsyslog syslog opened","",0);
8198 #endif /* CKSYSLOG */
8201 /* Z V P A S S -- Verify password; returns 1 if OK, 0 otherwise */
8205 #endif /* AUTH_USER */
8207 #define AUTH_VALID 4
8208 #endif /* AUTH_VALID */
8210 #ifdef __FreeBSD__ /* 299 This was necessary in */
8211 #ifndef NODCLINITGROUPS /* FreeBSD 4.4, don't know */
8212 #define NODCLINITGROUPS /* about other versions... */
8213 #endif /* NODCLINITGROUPS */
8214 #endif /* __FreeBSD__ */
8217 zvpass(p) char *p; {
8218 #ifndef NODCLINITGROUPS
8219 _PROTOTYP(int initgroups, (const char *, gid_t) );
8220 #endif /* NODCLINITGROUPS */
8222 char *xpasswd, *salt;
8226 const char * reply = NULL;
8229 if (logged_in || askpasswd == 0) {
8232 debug(F111,"zvpass",p ? (guest ? p : "xxxxxx") : "(null)",guest);
8235 if (guest && !*p) { /* Guests must specify a password */
8237 if (ckxsyslog && ckxlogging) {
8239 "login: anonymous guests must specify a password"
8242 #endif /* CKSYSLOG */
8246 #ifdef CK_AUTHENTICATION
8247 && ck_tn_auth_valid() != AUTH_VALID
8248 #endif /* CK_AUTHENTICATION */
8249 ) { /* "ftp" is only account allowed no password */
8251 debug(F110,"zvpass","calling pam_set_item(AUTHTOK)",0);
8252 if ((pam_status = pam_set_item(pamh,PAM_AUTHTOK,p)) != PAM_SUCCESS) {
8253 reply = pam_strerror(pamh, pam_status);
8254 debug(F110,"zvpass PAM failure",reply,0);
8255 /* if no password given treat as non-fatal error */
8256 /* pam will prompt for password in pam_authenticate() */
8258 printf("%s\n",reply);
8260 debug(F100,"zvpass denied","",0);
8266 debug(F110,"zvpass","calling pam_authenticate",0);
8272 Make IKSD authentication (using PAM) ask for a password when an
8273 invalid username has been given, to avoid disclosing which account
8274 names are valid. See #417247 (Debian).
8279 #endif /* CK_LOGIN */
8282 #endif /* COMMENT */
8283 if ((pam_status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
8284 reply = pam_strerror(pamh, pam_status);
8285 debug(F110,"zvpass PAM failure",reply,0);
8286 printf("%s\n",reply);
8288 debug(F100,"zvpass denied","",0);
8295 debug(F110,"zvpass","calling pam_acct_mgmt",0);
8296 if ((pam_status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
8297 reply = pam_strerror(pamh, pam_status);
8298 debug(F110,"zvpass PAM failure",reply,0);
8299 printf("%s\n",reply);
8301 debug(F100,"zvpass denied","",0);
8306 debug(F110,"zvpass","PAM validates OK",0);
8312 salt = pw->pw_passwd;
8314 #ifdef HPUX10_TRUSTED
8315 xpasswd = bigcrypt(p, salt);
8318 On 64-bit platforms this can give "cast to pointer from integer of
8319 different size" warning, but I'm not sure what the effect is at runtime,
8320 or what to do about it.
8322 xpasswd = (char *)crypt(p, salt);
8323 #endif /* HPUX10_TRUSTED */
8327 /* null pw_passwd ok if Kerberos password ok */
8329 ((*pw->pw_passwd != '\0' ||
8330 strcmp(xpasswd, pw->pw_passwd))
8331 && !kpass(pw->pw_name, p))
8334 /* check with tpasswd first if there */
8335 pw == NULL || *pw->pw_passwd == '\0' ||
8336 t_verifypw (pw->pw_name, p) == 0 ||
8337 (t_verifypw (pw->pw_name, p) < 0 &&
8338 strcmp (xpasswd, pw->pw_passwd))
8340 /* The strcmp does not catch null passwords! */
8341 (pw == NULL) || (*pw->pw_passwd == '\0') ||
8342 strcmp(xpasswd, pw->pw_passwd)
8344 #endif /* FTP_KERBEROS */
8346 debug(F100,"zvpass denied","",0);
8354 (VOID) setgid((GID_T)pw->pw_gid); /* Set group ID */
8356 #ifndef NOINITGROUPS
8357 (VOID) initgroups(pw->pw_name, pw->pw_gid);
8358 #endif /* NOINITGROUPS */
8364 /* Open wtmp before chroot */
8366 sprintf(cksysline,"iks_%04x", getpid()); /* safe */
8367 logwtmp(cksysline, pw->pw_name,
8368 clienthost ? clienthost : "(unknown host)"
8370 debug(F110,"WTMP login",cksysline,logged_in);
8374 For anonymous users, we chroot to user ftp's home directory unless
8375 started with --anonroot:xxx, in which case we chroot to xxx. We must
8376 immediately chdir() to the same directory we chroot() to or else the
8377 old current directory remains accessible as "." outside the new root.
8380 if (anonroot) /* Non-default anonymous root */
8383 makestr(&anonroot,dir);
8385 debug(F110,"zvpass anon chroot",dir,0);
8386 if (chroot(dir) < 0) {
8387 debug(F111,"zvpass anon chroot FAILED",dir,errno);
8391 if (chdir("/") < 0) {
8392 debug(F111,"zvpass anon chdir FAILED",dir,errno);
8395 debug(F110,"zvpass anon chroot/chdir OK",dir,0);
8396 } else if (chdir(dir) < 0) { /* Not guest */
8398 if (chdir("/") < 0) {
8399 debug(F110,"Non-guest chdir FAILED",dir,0);
8402 printf("?No directory! Logging in with home=/\n");
8404 debug(F110,"zvpass non-guest chdir FAILED",dir,0);
8405 goto bad; /* Be conservative at first */
8406 #endif /* COMMENT */
8408 debug(F110,"zvpass non-guest chdir OK",dir,0);
8409 if (setuid((UID_T)pw->pw_uid) < 0) {
8410 debug(F101,"zvpass setuid FAILED","",pw->pw_uid);
8413 debug(F101,"zvpass setuid OK","",pw->pw_uid);
8415 guestpass[0] = '\0';
8419 fncact = XYFX_R; /* FILE COLLISION = RENAME */
8420 debug(F110,"GUEST fncact=R",p,0);
8421 lset(guestpass,"anonymous:",10,32);
8422 ckstrncpy(&guestpass[10],p,GUESTPASS-10);
8424 printf("Anonymous login.\r\n");
8427 /* proctitle declared where? Obviously this code is never compiled. */
8428 sprintf(proctitle, "%s: anonymous/%.*s",
8429 clienthost ? clienthost : "(unk)",
8430 sizeof(proctitle) - sizeof(clienthost) -
8431 sizeof(": anonymous/"), p);
8432 setproctitle(proctitle);
8433 #endif /* SETPROCTITLE */
8436 if (ckxsyslog && ckxlogging) {
8438 "login: anonymous %s %s",
8439 clienthost ? clienthost : "(unknown host)",
8443 #endif /* CKSYSLOG */
8445 } else { /* Real user */
8448 ckstrncpy(guestpass,zvuname,GUESTPASS);
8450 printf("User %s logged in.\r\n", pw->pw_name);
8453 sprintf(proctitle, "%s: %s",
8454 clienthost ? clienthost : "(unk)",
8457 setproctitle(proctitle);
8458 #endif /* SETPROCTITLE */
8461 if (ckxsyslog && ckxlogging)
8462 syslog(LOG_INFO, "login: %s %s",
8464 clienthost ? clienthost : "(unknown host)"
8466 #endif /* CKSYSLOG */
8468 ckstrncpy(zvhome,home,CKMAXPATH); /* Set environment variables */
8471 ckmakmsg(zenvuser,ZENVUSER,"USER=",zvuname,NULL,NULL);
8472 putenv((char *)zenvuser);
8473 ckmakmsg(zenvlogname,ZENVLOGNAME,"LOGNAME=",zvuname,NULL,NULL);
8474 putenv((char *)zenvlogname);
8475 ckmakmsg(zenvhome,ZENVHOME,"HOME=",zvhome,NULL,NULL);
8476 putenv((char *)zenvhome);
8477 #endif /* NOPUTENV */
8478 /* homdir = (char *)zvhome; */
8479 ckstrncpy((char *)uidbuf,(char *)zvuname,64);
8480 (VOID) umask(defumask);
8485 extern char dbrec[];
8486 extern unsigned long myflags;
8487 extern unsigned int mydbslot;
8488 extern struct iksdbfld dbfld[];
8489 #ifdef CK_AUTHENTICATION
8490 extern unsigned long myamode, myatype;
8491 #endif /* CK_AUTHENTICATION */
8492 myflags |= DBF_LOGGED;
8495 debug(F101,"zvpass guest","",guest);
8496 debug(F111,"zvpass zvuname",zvuname,0);
8497 debug(F110,"zvpass guestpass",guestpass,0);
8498 debug(F110,"zvpass dir",dir,0);
8499 debug(F110,"zvpass home",home,0);
8500 debug(F110,"zvpass anonroot",anonroot,0);
8503 p2 = guest ? guestpass : zvuname;
8505 p2 = (char *)guestpass;
8506 myflags &= ~DBF_USER;
8508 p2 = (char *)zvuname;
8509 myflags |= DBF_USER;
8512 strncpy(&dbrec[DB_ULEN],ulongtohex((unsigned long)k,4),4);
8513 lset(&dbrec[dbfld[db_USER].off],p2,1024,32);
8514 strncpy(&dbrec[DB_FLAGS],ulongtohex(myflags,4),4);
8515 #ifdef CK_AUTHENTICATION
8516 myamode = ck_tn_auth_valid();
8517 strncpy(&dbrec[DB_AMODE],ulongtohex(myamode,4),4);
8518 myatype = ck_tn_authenticated();
8519 strncpy(&dbrec[DB_ATYPE],ulongtohex(myatype,4),4);
8520 #endif /* CK_AUTHENTICATION */
8526 if (!*p2) p2 = home;
8528 strncpy(&dbrec[DB_DLEN],
8529 ulongtohex((unsigned long)strlen(p2),4),
8532 lset(&dbrec[dbfld[db_DIR].off],p2,1024,32);
8538 bad: /* Common failure exit */
8543 #endif /* CK_LOGIN */
8545 /* Buggy Xenix 2.3.4 cc needs this line after the endif */