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.215, 13 Jun 2011";
9 char *ckzv = "Plan 9 File support, 9.0.215, 13 Jun 2011";
11 char *ckzv = "UNIX File support, 9.0.215, 13 Jun 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 */
85 #include <sys/types.h>
97 Directory Separator macros, to allow this module to work with both UNIX and
98 OS/2: Because of ambiguity with the command line editor escape \ character,
99 the directory separator is currently left as / for OS/2 too, because the
100 OS/2 kernel also accepts / as directory separator. But this is subject to
101 change in future versions to conform to the normal OS/2 style.
107 #define ISDIRSEP(c) ((c)=='/')
108 #endif /* ISDIRSEP */
115 #include <sys/ndir.h>
119 #else /* !NDIR, !XNDIR */
121 #include "/usr/lib/ndir.h"
122 #else /* !RTU, !NDIR, !XNDIR */
125 #include <sys/dirent.h>
136 #ifdef UNIX /* Pointer arg to wait() allowed */
137 #define CK_CHILD /* Assume this is safe in all UNIX */
140 extern int binary, recursive, stathack;
142 extern int eofmethod;
143 #endif /* CK_CTRLZ */
145 #include <pwd.h> /* Password file for shell name */
147 #include <t_pwd.h> /* SRP Password file */
150 #ifdef HPUX10_TRUSTED
151 #include <hpsecurity.h>
153 #endif /* HPUX10_TRUSTED */
156 /* Moved to ckcdeb.h */
166 #ifdef SYSUTIMEH /* <sys/utime.h> if requested, */
167 #include <sys/utime.h> /* for extra fields required by */
168 #else /* 88Open spec. */
169 #ifdef UTIMEH /* or <utime.h> if requested */
170 #include <utime.h> /* (SVR4, POSIX) */
173 /* Not sure why this is here. What it implies is that the code bracketed
174 by SYSUTIMEH is valid on all platforms on which we support time
175 functionality. But we know that is not true because the BSD44 and V7
176 platforms do not support sys/utime.h and the data structures which
177 are defined in them. Now this worked before because prior to today's
178 changes the UTIMEH definition for BSD44 and V7 did not take place
179 until after SYSUTIMEH was defined. It also would not have been a
180 problem if the ordering of all the time blocks was consistent. All but
181 one of the blocks were BSD44, V7, SYSUTIMEH, <OTHER>. That one case
182 is where this problem was triggered.
184 #define SYSUTIMEH /* Use this for both cases. */
188 #endif /* SYSUTIMEH */
197 #ifdef BSD44 /* BSD 4.4 */
199 #define TIMESTAMP /* Can do file dates */
200 #endif /* TIMESTAMP */
201 #include <sys/time.h>
202 #include <sys/timeb.h>
204 #else /* Not BSD44 */
206 #ifdef BSD4 /* BSD 4.3 and below */
207 #define TIMESTAMP /* Can do file dates */
208 #include <time.h> /* Need this */
209 #include <sys/timeb.h> /* Need this if really BSD */
211 #else /* Not BSD 4.3 and below */
213 #ifdef SVORPOSIX /* System V or POSIX */
216 #endif /* TIMESTAMP */
219 /* void tzset(); (the "void" type upsets some compilers) */
223 /* ConvexOS 9.0, supposedly POSIX, has extern char *timezone(int,int) */
225 extern long timezone;
230 #endif /* SVORPOSIX */
236 #endif /* COHERENT */
238 /* Is `y' a leap year? */
239 #define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
241 /* Number of leap years from 1970 to `y' (not including `y' itself). */
242 #define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
244 #endif /* NOTIMESTAMP */
247 #include <stat.h> /* File status */
249 #include <sys/stat.h>
254 /* Macro to alleviate isdir() calls internal to this module */
256 static struct stat STATBUF;
257 #define xisdir(a) ((stat(a,&STATBUF)==-1)?0:(S_ISDIR(STATBUF.st_mode)?1:0))
259 extern char uidbuf[];
261 extern char * xferfile;
263 static time_t timenow;
265 #define IKSDMSGLEN CKMAXPATH+512
267 static char iksdmsg[IKSDMSGLEN];
271 extern int server, en_mkd, en_cwd, en_del;
274 Functions (n is one of the predefined file numbers from ckcker.h):
276 zopeni(n,name) -- Opens an existing file for input.
277 zopeno(n,name,attr,fcb) -- Opens a new file for output.
278 zclose(n) -- Closes a file.
279 zchin(n,&c) -- Gets the next character from an input file.
280 zsinl(n,&s,x) -- Read a line from file n, max len x, into address s.
281 zsout(n,s) -- Write a null-terminated string to output file, buffered.
282 zsoutl(n,s) -- Like zsout, but appends a line terminator.
283 zsoutx(n,s,x) -- Write x characters to output file, unbuffered.
284 zchout(n,c) -- Add a character to an output file, unbuffered.
285 zchki(name) -- Check if named file exists and is readable, return size.
286 zchko(name) -- Check if named file can be created.
287 zchkspa(name,n) -- Check if n bytes available to create new file, name.
288 znewn(name,s) -- Make a new unique file name based on the given name.
289 zdelet(name) -- Delete the named file.
290 zxpand(string) -- Expands the given wildcard string into a list of files.
291 znext(string) -- Returns the next file from the list in "string".
292 zxrewind() -- Rewind zxpand list.
293 zxcmd(n,cmd) -- Execute the command in a lower fork on file number n.
294 zclosf() -- Close input file associated with zxcmd()'s lower fork.
295 zrtol(n1,n2) -- Convert remote filename into local form.
296 zltor(n1,n2) -- Convert local filename into remote form.
297 zchdir(dirnam) -- Change working directory.
298 zhome() -- Return pointer to home directory name string.
299 zkself() -- Kill self, log out own job.
300 zsattr(struct zattr *) -- Return attributes for file which is being sent.
301 zstime(f, struct zattr *, x) - Set file creation date from attribute packet.
302 zrename(old, new) -- Rename a file.
303 zcopy(source,destination) -- Copy a file.
304 zmkdir(path) -- Create the directory path if possible
305 zfnqfp(fname,len,fullpath) - Determine full path for file name.
306 zgetfs(name) -- return file size regardless of accessibility
307 zchkpid(pid) -- tell if PID is valid and active
310 /* Kermit-specific includes */
312 Definitions here supersede those from system include files.
313 ckcdeb.h is included above.
315 #include "ckcker.h" /* Kermit definitions */
316 #include "ckucmd.h" /* For keyword tables */
317 #include "ckuver.h" /* Version herald */
319 char *ckzsys = HERALD;
322 File access checking ... There are two calls to access() in this module.
323 If this program is installed setuid or setgid on a Berkeley-based UNIX
324 system that does NOT incorporate the saved-original-effective-uid/gid
325 feature, then, when we have swapped the effective and original uid/gid,
326 access() fails because it uses what it thinks are the REAL ids, but we have
327 swapped them. This occurs on systems where ANYBSD is defined, NOSETREU
328 is NOT defined, and SAVEDUID is NOT defined. So, in theory, we should take
329 care of this situation like so:
339 But we can't test such a general scheme everywhere, so let's only do this
340 when we know we have to...
342 #ifdef NEXT /* NeXTSTEP 1.0-3.0 */
346 /* Support for tilde-expansion in file and directory names */
349 #define NAMEENV "LOGNAME"
352 #define NAMEENV "USER"
355 #define NAMEENV "LOGNAME"
360 /* Berkeley Unix Version 4.x */
361 /* 4.1bsd support from Charles E Brooks, EDN-VAX */
366 #endif /* MAXNAMLEN */
369 /* Definitions of some system commands */
371 char *DELCMD = "rm -f "; /* For file deletion */
372 char *CPYCMD = "cp "; /* For file copy */
373 char *RENCMD = "mv "; /* For file rename */
374 char *PWDCMD = "pwd "; /* For saying where I am */
378 char *DIRCMD = "/usr/bin/ls -l "; /* For directory listing */
379 char *DIRCM2 = "/usr/bin/ls -l "; /* For directory listing, no args */
381 char *DIRCMD = "/bin/ls -l "; /* For directory listing */
382 char *DIRCM2 = "/bin/ls -l "; /* For directory listing, no args */
385 char *DIRCMD = "ls -l "; /* For directory listing */
386 char *DIRCM2 = "ls -l "; /* For directory listing, no args */
389 char *TYPCMD = "cat "; /* For typing a file */
392 char *MAILCMD = "mailx"; /* For sending mail */
395 char *MAILCMD = "mailx";
399 char *MAILCMD = CK_MAILCMD; /* CFLAGS override */
401 char *MAILCMD = "Mail"; /* Default */
402 #endif /* CK_MAILCMD */
410 #ifdef ANYBSD /* BSD uses lpr to spool */
411 #ifdef DGUX540 /* And DG/UX */
412 char * PRINTCMD = "lp";
414 char * PRINTCMD = "lpr";
416 #else /* Sys V uses lp */
417 #ifdef TRS16 /* except for Tandy-16/6000... */
418 char * PRINTCMD = "lpr";
420 char * PRINTCMD = "lp";
427 #ifdef FT18 /* Fortune For:Pro 1.8 */
432 char *SPACMD = "pwd ; df ."; /* Space in current directory */
435 char *SPACMD = "pwd ; du ; df .";
437 char *SPACMD = "df ";
441 char *SPACM2 = "df "; /* For space in specified directory */
448 char *WHOCMD = "finger ";
450 char *WHOCMD = "who ";
453 /* More system-dependent includes, which depend on symbols defined */
454 /* in the Kermit-specific includes. Oh what a tangled web we weave... */
456 #ifdef COHERENT /* <sys/file.h> */
458 #endif /* COHERENT */
473 #include <sys/file.h>
476 #ifndef is68k /* Whether to include <fcntl.h> */
477 #ifndef BSD41 /* All but a couple UNIXes have it. */
481 #endif /* COHERENT */
490 #include <sys/fcntl.h>
492 #endif /* COHERENT */
494 extern int inserver; /* I am IKSD */
495 int guest = 0; /* Anonymous user */
499 extern char * anonroot;
503 #define GUESTPASS 256
504 static char guestpass[GUESTPASS] = { NUL, NUL }; /* Anonymous "password" */
505 static int logged_in = 0; /* Set when user is logged in */
506 static int askpasswd = 0; /* Have OK user, must ask for passwd */
508 extern int gotemptypasswd;
510 #endif /* CK_LOGIN */
513 static char ckroot[CKMAXPATH+1] = { NUL, NUL };
514 static int ckrootset = 0;
518 _PROTOTYP( VOID ignorsigs, (void) );
519 _PROTOTYP( VOID restorsigs, (void) );
521 _PROTOTYP( int ttwait, (int, int) ); /* ckutio.c */
525 Change argument to "(const char *)" if this causes trouble.
526 Or... if it causes trouble, then maybe it was already declared
527 in a header file after all, so you can remove this prototype.
529 #ifndef NDGPWNAM /* If not defined No Declare getpwnam... */
530 #ifndef _POSIX_SOURCE
533 /* POSIX <pwd.h> already gave prototypes for these. */
535 _PROTOTYP( struct passwd * getpwnam, (const char *) );
538 _PROTOTYP( struct passwd * getpwnam, (const char *) );
541 _PROTOTYP( struct passwd * getpwnam, (const char *) );
544 _PROTOTYP( struct passwd * getpwnam, (const char *) );
547 _PROTOTYP( struct passwd * getpwnam, (const char *) );
550 _PROTOTYP( struct passwd * getpwnam, (const char *) );
552 _PROTOTYP( struct passwd * getpwnam, (char *) );
553 #endif /* DCGPWNAM */
563 _PROTOTYP( struct passwd * getpwuid, (PWID_T) );
568 _PROTOTYP( struct passwd * getpwent, (void) );
571 #endif /* _POSIX_SOURCE */
572 #endif /* NDGPWNAM */
574 #ifdef CK_SHADOW /* Shadow Passwords... */
576 #endif /* CK_SHADOW */
577 #ifdef CK_PAM /* PAM... */
579 #include <pam/pam_appl.h>
581 #include <security/pam_appl.h>
583 #ifndef PAM_SERVICE_TYPE /* Defines which PAM service we are */
584 #define PAM_SERVICE_TYPE "kermit"
585 #endif /* PAM_SERVICE_TYPE */
590 #define PAM_CONST CONST
593 static char * pam_pw = NULL;
598 PAM_CONST struct pam_message **msg,
599 struct pam_response **resp,
603 pam_cb(num_msg, msg, resp, appdata_ptr)
605 PAM_CONST struct pam_message **msg;
606 struct pam_response **resp;
608 #endif /* CK_ANSIC */
612 debug(F111,"pam_cb","num_msg",num_msg);
614 for (i = 0; i < num_msg; i++) {
615 char message[PAM_MAX_MSG_SIZE];
617 /* Issue prompt and get response */
618 debug(F111,"pam_cb","Message",i);
619 debug(F111,"pam_cb",msg[i]->msg,msg[i]->msg_style);
620 if (msg[i]->msg_style == PAM_ERROR_MSG) {
621 debug(F111,"pam_cb","PAM ERROR",0);
622 fprintf(stdout,"%s\n", msg[i]->msg);
624 } else if (msg[i]->msg_style == PAM_TEXT_INFO) {
625 debug(F111,"pam_cb","PAM TEXT INFO",0);
626 fprintf(stdout,"%s\n", msg[i]->msg);
628 } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) {
629 debug(F111,"pam_cb","Reading response, no echo",0);
630 /* Ugly hack. We check to see if a password has been pushed */
631 /* into zvpasswd(). This would be true if the password was */
632 /* received by REMOTE LOGIN. */
634 ckstrncpy(message,pam_pw,PAM_MAX_MSG_SIZE);
636 readpass((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
637 } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON) {
638 debug(F111,"pam_cb","Reading response, with echo",0);
639 readtext((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
641 debug(F111,"pam_cb","unknown style",0);
645 /* Allocate space for this message's response structure */
646 resp[i] = (struct pam_response *) malloc(sizeof (struct pam_response));
649 debug(F110,"pam_cb","malloc failure",0);
650 for (j = 0; j < i; j++) {
657 /* Allocate a buffer for the response */
658 resp[i]->resp = (char *) malloc((int)strlen(message) + 1);
659 if (!resp[i]->resp) {
661 debug(F110,"pam_cb","malloc failure",0);
662 for (j = 0; j < i; j++) {
669 /* Return the results back to PAM */
670 strcpy(resp[i]->resp, message); /* safe (prechecked) */
671 resp[i]->resp_retcode = 0;
673 debug(F110,"pam_cb","Exiting",0);
678 /* Define macros for getting file type */
682 Olivetti X/OS 2.3 has S_ISREG and S_ISDIR defined
683 incorrectly, so we force their redefinition.
689 #ifdef UTSV /* Same deal for Amdahl UTSV */
694 #ifdef UNISYS52 /* And for UNISYS UTS V 5.2 */
697 #endif /* UNISYS52 */
699 #ifdef ICLSVR3 /* And for old ICL versions */
704 #ifdef ISDIRBUG /* Also allow this from command line */
711 #endif /* ISDIRBUG */
717 #define _IFMT 0170000
722 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
725 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
728 /* The following mainly for NeXTSTEP... */
731 #define S_IWUSR 0000200
735 #define S_IRGRP 0000040
739 #define S_IWGRP 0000020
743 #define S_IXGRP 0000010
747 #define S_IROTH 0000004
751 #define S_IWOTH 0000002
755 #define S_IXOTH 0000001
758 Define maximum length for a file name if not already defined.
759 NOTE: This applies to a path segment (directory or file name),
760 not the entire path string, which can be CKMAXPATH bytes long.
764 #define MAXNAMLEN _MAX_FNAME
767 #endif /* _MAX_FNAME */
771 #define MAXNAMLEN 255
774 #define MAXNAMLEN FILENAME_MAX
777 #define MAXNAMLEN NAME_MAX
779 #ifdef _POSIX_NAME_MAX
780 #define MAXNAMLEN _POSIX_NAME_MAX
783 #define MAXNAMLEN _D_NAME_MAX
786 #define MAXNAMLEN DIRSIZ
790 #endif /* _D_NAME_MAX */
791 #endif /* _POSIX_NAME_MAX */
792 #endif /* NAME_MAX */
793 #endif /* FILENAME_MAX */
795 #endif /* MAXNAMLEN */
799 /* As of 2001-11-03 this is handled in ckcdeb.h */
800 /* Longest pathname ... */
802 Beware: MAXPATHLEN is one of UNIX's dirty little secrets. Where is it
803 defined? Who knows... <param.h>, <mod.h>, <unistd.h>, <limits.h>, ...
804 There is not necessarily even a definition for it anywhere, or it might have
805 another name. If you get it wrong, bad things happen with getcwd() and/or
806 getwd(). If you allocate a buffer that is too short, getwd() might write
807 over memory and getcwd() will fail with ERANGE. The definitions of these
808 functions (e.g. in SVID or POSIX.1) do not tell you how to determine the
809 maximum path length in order to allocate a buffer that is the right size.
812 #include <sys/param.h> /* For MAXPATHLEN */
815 #include <sys/param.h> /* for MAXPATHLEN, needed for -DDIRENT */
816 #endif /* COHERENT */
823 #define MAXPATH MAXPATHLEN
826 #define MAXPATH PATH_MAX
828 #ifdef _POSIX_PATH_MAX
829 #define MAXPATH _POSIX_PATH_MAX
840 #endif /* _POSIX_PATH_MAX */
841 #endif /* PATH_MAX */
842 #endif /* MAXPATHLEN */
844 /* Maximum number of filenames for wildcard expansion */
847 /* Already defined in ckcdeb.h so the following is superfluous. */
848 /* Don't expect changing them to have any effect. */
853 #define MAXWLD 102400
856 #endif /* BIGBUFOK */
857 #endif /* CK_SMALL */
860 static int maxnames = MAXWLD;
862 /* Define the size of the string space for filename expansion. */
875 #define SSPACE 10000 /* Size of string-generating buffer */
876 #else /* Default static buffer size */
878 #define SSPACE 65000 /* Size of string-generating buffer */
880 #define SSPACE 2000 /* size of string-generating buffer */
881 #endif /* BIGBUFOK */
886 static char sspace[SSPACE]; /* Buffer for generating filenames */
887 #else /* is DYNAMIC */
889 #define SSPACE 2000000000 /* Two billion bytes */
892 #define SSPACE 10000000 /* Ten million */
894 #define SSPACE 10000 /* Ten thousand */
895 #endif /* BIGBUFOK */
896 #endif /* CK_64BIT */
897 char *sspace = (char *)0;
899 static int ssplen = SSPACE; /* Length of string space buffer */
902 /* fdopen() needs declaring because it's not declared in <stdio.h> */
903 _PROTOTYP( FILE * fdopen, (int, char *) );
904 #endif /* DCLFDOPEN */
907 /* popen() needs declaring because it's not declared in <stdio.h> */
908 _PROTOTYP( FILE * popen, (char *, char *) );
909 #endif /* DCLPOPEN */
913 /* More internal function prototypes */
915 * The path structure is used to represent the name to match.
916 * Each slash-separated segment of the name is kept in one
917 * such structure, and they are linked together, to make
918 * traversing the name easier.
921 char npart[MAXNAMLEN+4]; /* name part of path segment */
922 struct path *fwd; /* forward ptr */
925 _PROTOTYP( int shxpand, (char *, char *[], int ) );
927 _PROTOTYP( static int fgen, (char *, char *[], int ) );
928 _PROTOTYP( static VOID traverse, (struct path *, char *, char *) );
929 _PROTOTYP( static VOID addresult, (char *, int) );
931 /* Replaced by ckmatch() */
932 _PROTOTYP( static int match, (char *, char *) );
934 _PROTOTYP( char * whoami, (void) );
935 _PROTOTYP( UID_T real_uid, (void) );
936 _PROTOTYP( static struct path *splitpath, (char *p) );
937 _PROTOTYP( char * zdtstr, (time_t) );
938 _PROTOTYP( time_t zstrdt, (char *, int) );
940 /* Some systems define these symbols in include files, others don't... */
943 #define R_OK 4 /* For access */
956 #endif /* O_RDONLY */
958 /* syslog and wtmp items for Internet Kermit Service */
960 extern char * clienthost; /* From ckcmai.c. */
962 static char fullname[CKMAXPATH+1];
963 static char tmp2[CKMAXPATH+1];
965 extern int ckxlogging;
967 #ifdef CKXPRINTF /* Our printf macro conflicts with */
968 #undef printf /* use of "printf" in syslog.h */
969 #endif /* CKXPRINTF */
972 #include <sys/syslog.h>
976 #endif /* CKSYSLOG */
978 #define printf ckxprintf
979 #endif /* CKXPRINTF */
981 int ckxanon = 1; /* Anonymous login ok */
982 int ckxperms = 0040; /* Anonymous file permissions */
983 int ckxpriv = 1; /* Priv'd login ok */
986 #define XFERFILE "/var/log/iksd.log"
987 #endif /* XFERFILE */
989 /* wtmp logging for IKSD... */
991 #ifndef CKWTMP /* wtmp logging not selected */
992 int ckxwtmp = 0; /* Know this at runtime */
993 #else /* wtmp file details */
995 #ifdef UTMPBUG /* Unfortunately... */
997 Some versions of Linux have a <utmp.h> file that contains
998 "enum utlogin { local, telnet, rlogin, screen, ... };" This clobbers
999 any program that uses any of these words as variable names, function
1000 names, macro names, etc. (Other versions of Linux have this declaration
1001 within #if 0 ... #endif.) There is nothing we can do about this other
1002 than to not include the stupid file. But we need stuff from it, so...
1004 #include <features.h>
1005 #include <sys/types.h>
1006 #define UT_LINESIZE 32
1007 #define UT_NAMESIZE 32
1008 #define UT_HOSTSIZE 256
1015 struct exit_status {
1016 short int e_termination; /* Process termination status. */
1017 short int e_exit; /* Process exit status. */
1021 short int ut_type; /* Type of login */
1022 pid_t ut_pid; /* Pid of login process */
1023 char ut_line[UT_LINESIZE]; /* NUL-terminated devicename of tty */
1024 char ut_id[4]; /* Inittab id */
1025 char ut_user[UT_NAMESIZE]; /* Username (not NUL terminated) */
1027 char ut_host[UT_HOSTSIZE]; /* Hostname for remote login */
1028 struct exit_status ut_exit; /* Exit status */
1029 long ut_session; /* Session ID, used for windowing */
1030 struct timeval ut_tv; /* Time entry was made */
1031 int32_t ut_addr_v6[4]; /* Internet address of remote host */
1032 char pad[20]; /* Reserved */
1035 #define ut_time ut_tv.tv_sec /* Why should Linux be like anything else? */
1036 #define ut_name ut_user /* ... */
1039 logwtmp __P ((__const char *__ut_line, __const char *__ut_name,
1040 __const char *__ut_host));
1042 #else /* Not UTMPBUG */
1044 #ifndef HAVEUTMPX /* Who has <utmpx.h> */
1059 #endif /* UNIXWARE */
1060 #endif /* HPUX100 */
1061 #endif /* CK_SCOV5 */
1063 #endif /* SOLARIS */
1064 #endif /* HAVEUTMPX */
1069 /* Because the time_t in the utmp struct is 64 bits but time() wants 32 */
1070 #define __V40_OBJ_COMPAT 1
1074 #undef __V40_OBJ_COMPAT
1076 #endif /* HAVEUTMPX */
1077 #endif /* UTMPBUG */
1080 #define UTMPSTRUCT utmpx
1082 #define UTMPSTRUCT utmp
1083 #endif /* HAVEUTMPX */
1087 #define WTMPFILE "/usr/adm/wtmp.1"
1090 #define WTMPFILE "/var/log/wtmp"
1092 #define WTMPFILE "/usr/adm/wtmp"
1095 #endif /* WTMPFILE */
1096 char * wtmpfile = NULL;
1098 static int wtmpfd = 0;
1099 static char cksysline[32] = { NUL, NUL };
1101 #ifndef HAVEUTHOST /* Does utmp include ut_host[]? */
1102 #ifdef HAVEUTMPX /* utmpx always does */
1105 #ifdef LINUX /* Linux does */
1108 #ifdef SUNOS4 /* SunOS does */
1111 #ifdef AIX41 /* AIX 4.1 and later do */
1116 #endif /* HAVEUTMPX */
1117 #endif /* HAVEUTHOST */
1120 PID_T _vfork() { /* To satisfy a library foulup */
1121 return(fork()); /* in Unixware 2.0.x */
1127 logwtmp(const char * line, const char * name, const char * host)
1129 logwtmp(line, name, host) char *line, *name, *host;
1130 #endif /* CK_ANSIC */
1132 struct UTMPSTRUCT ut;
1134 /* time_t time(); */
1140 makestr(&wtmpfile,WTMPFILE);
1142 if (!line) line = "";
1143 if (!name) name = "";
1144 if (!host) host = "";
1146 if (!wtmpfd && (wtmpfd = open(wtmpfile, O_WRONLY|O_APPEND, 0)) < 0) {
1148 debug(F110,"WTMP open failed",line,0);
1151 if (!fstat(wtmpfd, &buf)) {
1152 ckstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
1153 ckstrncpy(ut.ut_name, name, sizeof(ut.ut_name));
1156 ckstrncpy(ut.ut_host, host, sizeof(ut.ut_host));
1157 #endif /* HAVEUTHOST */
1159 time(&ut.ut_tv.tv_sec);
1162 /* In light of the following comment perhaps the previous line should */
1163 /* be "#ifndef COMMENT". */
1166 * On 64-bit platforms sizeof(time_t) and sizeof(ut.ut_time)
1167 * are not the same and attempt to use an address of
1168 * ut.ut_time as an argument to time() call may cause
1169 * "unaligned access" trap.
1178 /* Now (Jan 2006) we can do this for any 64-bit build */
1185 #endif /* CK_64BIT */
1187 #endif /* HAVEUTMPX */
1188 if (write(wtmpfd, (char *)&ut, sizeof(struct UTMPSTRUCT)) !=
1189 sizeof(struct UTMPSTRUCT)) {
1192 ftruncate(wtmpfd, buf.st_size); /* Error, undo any partial write */
1194 chsize(wtmpfd, buf.st_size); /* Error, undo any partial write */
1195 #endif /* COHERENT */
1196 #endif /* NOFTRUNCATE */
1197 debug(F110,"WTMP write error",line,0);
1199 debug(F110,"WTMP record OK",line,0);
1208 C K S Y S L O G -- C-Kermit system logging function,
1210 For use by other modules.
1211 This module can, but doesn't have to, use it.
1213 n = SYSLG_xx values defined in ckcdeb.h
1214 s1, s2, s3: strings.
1217 cksyslog(n, m, s1, s2, s3) int n, m; char * s1, * s2, * s3; {
1220 if (!ckxlogging) /* syslogging */
1222 if (!s1) s1 = ""; /* Fix null args */
1225 switch (n) { /* Translate Kermit level */
1226 case SYSLG_DB: /* to syslog level */
1230 level = m ? LOG_INFO : LOG_ERR;
1232 debug(F110,"cksyslog s1",s1,0);
1233 debug(F110,"cksyslog s2",s2,0);
1234 debug(F110,"cksyslog s3",s3,0);
1236 syslog(level, "%s: %s %s", s1, s2, s3); /* Write syslog record */
1237 debug(F101,"cksyslog errno","",errno);
1239 #endif /* CKSYSLOG */
1244 int maxnam = MAXNAMLEN; /* Available to the outside */
1245 int maxpath = MAXPATH;
1249 char startupdir[MAXPATH+1];
1252 int pexitstat = -2; /* Process exit status */
1254 FILE *fp[ZNFILS] = { /* File pointers */
1255 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
1258 /* Flags for each file indicating whether it was opened with popen() */
1259 int ispipe[ZNFILS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1261 /* Buffers and pointers used in buffered file input and output. */
1263 extern char *zinbuffer, *zoutbuffer;
1265 extern char zinbuffer[], zoutbuffer[];
1266 #endif /* DYNAMIC */
1267 extern char *zinptr, *zoutptr;
1268 extern int zincnt, zoutcnt;
1269 extern int wildxpand, wildena; /* Wildcard handling */
1271 static CK_OFF_T iflen = (CK_OFF_T)-1; /* Input file length */
1273 static PID_T pid = 0; /* pid of child fork */
1274 static int fcount = 0; /* Number of files in wild group */
1275 static int nxpand = 0; /* Copy of fcount */
1276 static char nambuf[CKMAXPATH+4]; /* Buffer for a pathname */
1279 #define ZMBUFLEN 200
1280 static char zmbuf[ZMBUFLEN]; /* For mail, remote print strings */
1281 #endif /* NOFRILLS */
1283 char **mtchs = NULL; /* Matches found for filename */
1284 char **mtchptr = NULL; /* Pointer to current match */
1286 /* Z K S E L F -- Kill Self: log out own job, if possible. */
1288 /* Note, should get current pid, but if your system doesn't have */
1289 /* getppid(), then just kill(0,9)... */
1294 /* Already declared in unistd.h for SVR3 and POSIX */
1296 extern PID_T getppid(void);
1300 extern PID_T getppid();
1301 #endif /* COHERENT */
1302 #endif /* PS2AIX10 */
1303 #endif /* CK_ANSIC */
1309 zkself() { /* For "bye", but no guarantee! */
1326 return(kill((PID_T)getpid(),1));
1329 exit(kill((PID_T)getppid(),1));
1332 exit(kill(getppid(),1));
1344 getfullname(name) char * name; {
1345 char *p = (char *)fullname;
1348 /* If necessary we could also chase down symlinks here... */
1350 /* This works but is incompatible with wuftpd */
1351 if (isguest && anonroot) {
1352 ckstrncpy(fullname,anonroot,CKMAXPATH);
1353 len = strlen(fullname);
1355 if (fullname[len-1] == '/')
1359 #endif /* COMMENT */
1360 zfnqfp(name, CKMAXPATH - len, p);
1362 if (*p < '!') *p = '_';
1367 /* D O I K L O G -- Open Kermit-specific ftp-like transfer log. */
1369 VOID /* Called in ckcmai.c */
1371 if (iklogopen) /* Already open? */
1373 if (xferlog) { /* Open iksd log if requested */
1374 if (!xferfile) /* If no pathname given */
1375 makestr(&xferfile,XFERFILE); /* use this default */
1377 xferlog = open(xferfile, O_WRONLY | O_APPEND | O_CREAT, 0660);
1378 debug(F101,"doiklog open","",xferlog);
1381 syslog(LOG_ERR, "xferlog open failure %s: %m", xferfile);
1382 #endif /* CKSYSLOG */
1383 debug(F101,"doiklog open errno","",errno);
1390 if (xferlog && ckxlogging)
1391 syslog(LOG_INFO, "xferlog: %s open ok", xferfile);
1392 #endif /* CKSYSLOG */
1396 /* Z O P E N I -- Open an existing file for input. */
1398 /* Returns 1 on success, 0 on failure */
1401 zopeni(n,name) int n; char *name; {
1404 debug(F111,"zopeni",name,n);
1405 if ((x = chkfn(n)) != 0) {
1406 debug(F111,"zopeni chkfn",ckitoa(n),x);
1409 zincnt = 0; /* Reset input buffer */
1410 if (n == ZSYSFN) { /* Input from a system function? */
1412 /*** Note, this function should not be called with ZSYSFN ***/
1413 /*** Always call zxcmd() directly, and give it the real file number ***/
1414 /*** you want to use. ***/
1415 return(zxcmd(n,name)); /* Try to fork the command */
1417 debug(F110,"zopeni called with ZSYSFN, failing!",name,0);
1418 *nambuf = '\0'; /* No filename. */
1419 return(0); /* fail. */
1420 #endif /* COMMENT */
1422 if (n == ZSTDIO) { /* Standard input? */
1424 fprintf(stderr,"Terminal input not allowed");
1425 debug(F110,"zopeni: attempts input from unredirected stdin","",0);
1433 debug(F111,"zopeni setroot",ckroot,ckrootset);
1434 if (ckrootset) if (!zinroot(name)) {
1435 debug(F110,"zopeni setroot violation",name,0);
1439 fp[n] = fopen(name,"r"); /* Real file, open it. */
1440 /* debug(F111,"zopeni fopen", name, fp[n]); */
1442 /* printf("ZOPENI fp[%d]=%ld\n",n,fp[n]); */
1448 || ((ckxsyslog >= SYSLG_FA) && ckxlogging)
1449 #endif /* CKSYSLOG */
1452 debug(F110,"zopeni fullname",fullname,0);
1454 if (fp[n] == NULL) {
1456 if (ckxsyslog >= SYSLG_FA && ckxlogging) {
1457 syslog(LOG_INFO, "file[%d] %s: open failed (%m)", n, fullname);
1460 #endif /* CKSYSLOG */
1465 if (ckxsyslog >= SYSLG_FA && ckxlogging)
1466 syslog(LOG_INFO, "file[%d] %s: open read ok", n, fullname);
1467 #endif /* CKSYSLOG */
1478 #endif /* O_NDELAY */
1481 /* Z O P E N O -- Open a new file for output. */
1483 /*ARGSUSED*/ /* zz not used */
1485 zopeno(n,name,zz,fcb)
1486 /* zopeno */ int n; char *name; struct zattr *zz; struct filinfo *fcb; {
1490 int istty = 0, filefd = 0;
1492 /* As of Version 5A, the attribute structure and the file information */
1493 /* structure are included in the arglist. */
1496 debug(F111,"zopeno",name,n);
1498 debug(F101,"zopeno fcb disp","",fcb->dsp);
1499 debug(F101,"zopeno fcb type","",fcb->typ);
1500 debug(F101,"zopeno fcb char","",fcb->cs);
1502 debug(F100,"zopeno fcb is NULL","",0);
1506 if (chkfn(n) != 0) /* Already open? */
1507 return(0); /* Nothing to do. */
1509 if ((n == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */
1510 fp[ZOFILE] = stdout;
1513 /* This seems right but it breaks client server ops */
1516 #endif /* COMMENT */
1520 debug(F101,"zopeno fp[n]=stdout","",fp[n]);
1522 #endif /* COMMENT */
1524 zoutptr = zoutbuffer;
1528 /* A real file. Open it in desired mode (create or append). */
1531 debug(F111,"zopeno setroot",ckroot,ckrootset);
1532 if (ckrootset) if (!zinroot(name)) {
1533 debug(F110,"zopeno setroot violation",name,0);
1538 ckstrncpy(p,"w",8); /* Assume write/create mode */
1539 if (fcb) { /* If called with an FCB... */
1540 if (fcb->dsp == XYFZ_A) { /* Does it say Append? */
1541 ckstrncpy(p,"a",8); /* Yes. */
1542 debug(F100,"zopeno append","",0);
1548 || ((ckxsyslog >= SYSLG_FC) && ckxlogging)
1549 #endif /* CKSYSLOG */
1552 debug(F110,"zopeno fullname",fullname,0);
1555 /* Allow tty devices to opened as output files 2009/10/20 */
1557 debug(F110,"zopeno attempting to open",name,0);
1566 #endif /* FNDELAY */
1567 #endif /* O_NDELAY */
1568 #endif /* O_NONBLOCK */
1569 debug(F111,"zopeno open mode",name,mode);
1570 fd = open(name,O_WRONLY,mode);
1571 debug(F111,"zopeno open",name,fd);
1579 debug(F111,"zopeno istty",name,istty);
1580 debug(F110,"zopeno fopen arg",p,0);
1582 fp[n] = fdopen(filefd,p);
1584 fp[n] = fopen(name,p); /* Try to open the file */
1588 printf("ZOPENO fp[%d]=%ld\n",n,fp[n]);
1591 if (fp[n] == NULL) { /* Failed */
1592 debug(F101,"zopeno failed errno","",errno);
1593 if (istty) close(filefd);
1595 if (ckxsyslog >= SYSLG_FC && ckxlogging)
1596 syslog(LOG_INFO, "file[%d] %s: %s failed (%m)",
1599 append ? "append" : "create"
1601 #endif /* CKSYSLOG */
1602 #ifdef COMMENT /* Let upper levels print message. */
1603 perror("Can't open output file");
1604 #endif /* COMMENT */
1605 } else { /* Succeeded */
1606 extern int zofbuffer, zofblock, zobufsize;
1607 debug(F101, "zopeno zobufsize", "", zobufsize);
1608 if (n == ZDFILE || n == ZTFILE) { /* If debug or transaction log */
1609 setbuf(fp[n],NULL); /* make it unbuffered. */
1611 } else if (n == ZOFILE && !zofblock) { /* blocking or nonblocking */
1613 if ((flags = fcntl(fileno(fp[n]),F_GETFL,0)) > -1)
1614 fcntl(fileno(fp[n]),F_SETFL, flags |
1621 debug(F100,"zopeno ZOFILE nonblocking","",0);
1622 #endif /* DONDELAY */
1623 } else if (n == ZOFILE && !zofbuffer) { /* buffered or unbuffered */
1625 debug(F100,"zopeno ZOFILE unbuffered","",0);
1629 /* Enforce anonymous file-creation permission */
1631 if (n == ZWFILE || n == ZMFILE ||
1632 n == ZOFILE || n == ZDFILE ||
1633 n == ZTFILE || n == ZPFILE ||
1635 chmod(name,ckxperms);
1636 #endif /* CK_LOGIN */
1638 if (ckxsyslog >= SYSLG_FC && ckxlogging)
1639 syslog(LOG_INFO, "file[%d] %s: %s ok",
1642 append ? "append" : "create"
1644 #endif /* CKSYSLOG */
1645 debug(F100, "zopeno ok", "", 0);
1647 zoutcnt = 0; /* (PWP) reset output buffer */
1648 zoutptr = zoutbuffer;
1649 return((fp[n] != NULL) ? 1 : 0);
1652 /* Z C L O S E -- Close the given file. */
1654 /* Returns 0 if arg out of range, 1 if successful, -1 if close failed. */
1659 extern CK_OFF_T ffc;
1661 debug(F101,"zclose file number","",n);
1662 if (chkfn(n) < 1) return(0); /* Check range of n */
1663 if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */
1666 if (fp[ZSYSFN] || ispipe[n]) { /* If file is really pipe */
1668 x = zclosf(n); /* do it specially */
1672 debug(F101,"zclose zclosf","",x);
1673 /* debug(F101,"zclose zclosf fp[n]","",fp[n]); */
1675 if ((fp[n] != stdout) && (fp[n] != stdin))
1679 if (n == ZCTERM || n == ZSTDIO) /* See zopeno() */
1680 if (fp[ZOFILE] == stdout)
1682 #endif /* COMMENT */
1684 iflen = -1L; /* Invalidate file length */
1685 if (x == EOF) { /* if we got a close error */
1686 debug(F101,"zclose fclose fails","",x);
1688 } else if (x2 < 0) { /* or error flushing last buffer */
1689 debug(F101,"zclose error flushing last buffer","",x2);
1690 return(-1); /* then return an error */
1692 /* Print log record compatible with wu-ftpd */
1693 if (xferlog && (n == ZIFILE || n == ZOFILE)) {
1695 extern char ttname[];
1696 if (!iklogopen) (VOID) doiklog(); /* Open log if necessary */
1697 debug(F101,"zclose iklogopen","",iklogopen);
1702 timenow = time(NULL);
1707 #endif /* CK_LOGIN */
1716 #endif /* CK_LOGIN */
1719 len = 24 + 12 + (int)strlen(s) + 16
1720 + (int)strlen(fullname) + 1 + 1 + 1 + 1
1721 + (int)strlen(p) + 6 + 2 + 12;
1723 if (!*fnam) fnam = "(pipe)";
1725 if (len > IKSDMSGLEN)
1726 sprintf(iksdmsg, /* SAFE */
1727 "%.24s [BUFFER WOULD OVERFLOW]\n",ctime(&timenow));
1729 sprintf(iksdmsg, /* SAFE */
1730 "%.24s %d %s %s %s %c %s %c %c %s %s %d %s\n",
1731 ctime(&timenow), /* date/time */
1732 gtimer(), /* elapsed secs */
1734 ckfstoa(ffc), /* byte count */
1735 fnam, /* full pathname of file */
1736 (binary ? 'b' : 'a'), /* binary or ascii */
1737 "_", /* options = none */
1738 n == ZIFILE ? 'o' : 'i', /* in/out */
1740 (isguest ? 'a' : 'r'), /* User type */
1743 #endif /* CK_LOGIN */
1744 p, /* Username or guest passwd */
1746 logged_in ? "iks" : "kermit", /* Record ID */
1749 #endif /* CK_LOGIN */
1750 0, /* User ID on client system unknown */
1753 debug(F110,"zclose iksdmsg",iksdmsg,0);
1754 write(xferlog, iksdmsg, (int)strlen(iksdmsg));
1757 debug(F101,"zclose returns","",1);
1762 /* Z C H I N -- Get a character from the input file. */
1764 /* Returns -1 if EOF, 0 otherwise with character returned in argument */
1767 zchin(n,c) int n; int *c; {
1771 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1777 /* (PWP) Just in case this gets called when it shouldn't. */
1779 a = zminchar(); /* Note: this catches Ctrl-Z */
1780 if (a < 0) /* (See zinfill()...) */
1784 if (a == EOF) return(-1);
1786 /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
1787 if (!binary && a == 0x1A && eofmethod == XYEOF_Z)
1789 #endif /* CK_CTRLZ */
1791 *c = (CHAR) a & 0377;
1795 /* Z S I N L -- Read a line from a file */
1798 Writes the line into the address provided by the caller.
1799 n is the Kermit "channel number".
1800 Writing terminates when newline is encountered, newline is not copied.
1801 Writing also terminates upon EOF or if length x is exhausted.
1802 Returns 0 on success, -1 on EOF or error.
1805 zsinl(n,s,x) int n, x; char *s; {
1806 int a, z = 0; /* z is return code. */
1810 extern CHAR feol; /* Line terminator */
1812 if (!s || chkfn(n) < 1) /* Make sure file is open, etc */
1815 s[0] = '\0'; /* Don't return junk */
1817 a = -1; /* Current character, none yet. */
1818 while (x--) { /* Up to given length */
1820 if (feol) /* Previous character */
1822 if (zchin(n,&a) < 0) { /* Read a character from the file */
1823 debug(F101,"zsinl zchin fail","",count);
1825 z = -1; /* EOF or other error */
1829 if (feol) { /* Single-character line terminator */
1832 } else { /* CRLF line terminator */
1833 if (a == '\015') /* CR, get next character */
1835 if (old == '\015') { /* Previous character was CR */
1836 if (a == '\012') { /* This one is LF, so we have a line */
1838 } else { /* Not LF, deposit CR */
1845 *s = a; /* Deposit character */
1849 *s = '\0'; /* Terminate the string */
1850 debug(F011,"zsinl",buf,len);
1854 /* Z X I N -- Read x bytes from a file */
1857 Reads x bytes (or less) from channel n and writes them
1858 to the address provided by the caller.
1859 Returns number of bytes read on success, 0 on EOF or error.
1862 zxin(n,s,x) int n, x; char *s; {
1864 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1867 if (a < 1) return(0);
1868 for (i = 0; i < a && i < x; i++)
1874 return(fread(s, sizeof (char), x, fp[n]));
1878 Z I N F I L L -- Buffered file input.
1880 (re)fill the file input buffer with data. All file input
1881 should go through this routine, usually by calling the zminchar()
1882 macro defined in ckcker.h. Returns:
1884 Value 0..255 on success, the character that was read.
1886 -2 on any kind of error other than end of file.
1887 -3 timeout when reading from pipe (Kermit packet mode only).
1891 extern int kactive, srvping;
1895 printf("ZINFILL fp[%d]=%ld\n",ZIFILE,fp[ZIFILE]);
1899 if (inserver && !local && fp[ZIFILE] == stdin) {
1902 if (a < 0) return(-2);
1903 for (i = 0; i < a && i < INBUFSIZE; i++) {
1904 zinbuffer[i] = coninc(0);
1907 /* set pointer to beginning, (== &zinbuffer[0]) */
1909 if (zincnt == 0) return(-1);
1910 zincnt--; /* One less char in buffer */
1911 return((int)(*zinptr++) & 0377); /* because we return the first */
1915 debug(F101,"zinfill kactive","",kactive);
1917 if (!(kactive && ispipe[ZIFILE])) {
1918 if (feof(fp[ZIFILE])) {
1919 debug(F100,"ZINFILL feof","",0);
1921 printf("ZINFILL EOF\n");
1926 clearerr(fp[ZIFILE]);
1929 /* Here we can call select() to get a timeout... */
1930 if (kactive && ispipe[ZIFILE]) {
1935 debug(F101,"zinfill calling ttwait","",secs);
1936 z = ttwait(fileno(fp[ZIFILE]),secs);
1937 debug(F101,"zinfill ttwait","",z);
1948 debug(F101,"ZINFILL INBUFSIZE","",INBUFSIZE);
1950 memset(zinbuffer, 0xFF, INBUFSIZE);
1952 for (i = 0; i < INBUFSIZE; i++) {
1953 zinbuffer[i] = 0xFF;
1954 #ifdef COMMENT /* Too much! */
1955 debug(F101,"ZINFILL zinbuffer[i]","",i);
1956 #endif /* COMMENT */
1958 #endif /* USE_MEMCPY */
1959 ckstrncpy(zinbuffer,"zinbuffer is a valid buffer",INBUFSIZE);
1960 /* debug(F111,"ZINFILL about to call fread",zinbuffer,zinbuffer); */
1965 Note: The following read MUST be nonblocking when reading from a pipe
1966 and we want timeouts to work. See zxcmd().
1968 zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]);
1969 debug(F101,"ZINFILL fread","",zincnt); /* Just the size */
1971 printf("FREAD=%d\n",zincnt);
1974 /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
1975 if (zincnt > 0 && !binary && eofmethod == XYEOF_Z) {
1977 for (i = 0; i < zincnt; i++) {
1978 if (zinbuffer[i] == SUB) {
1979 zincnt = i; /* Stop at first Ctrl-Z */
1986 #endif /* CK_CTRLZ */
1988 if (zincnt == 0) { /* Got nothing? */
1989 if (ferror(fp[ZIFILE])) {
1990 debug(F100,"ZINFILL ferror","",0);
1991 debug(F101,"ZINFILL errno","",errno);
1993 printf("ZINFILL errno=%d\n",errno);
1996 return((errno == EWOULDBLOCK) ? -3 : -2);
1999 #endif /* EWOULDBLOCK */
2002 /* In case feof() didn't work just above -- sometimes it doesn't... */
2004 if (feof(fp[ZIFILE]) ) {
2005 debug(F100,"ZINFILL count 0 EOF return -1","",0);
2008 debug(F100,"ZINFILL count 0 not EOF return -2","",0);
2012 zinptr = zinbuffer; /* set pointer to beginning, (== &zinbuffer[0]) */
2013 zincnt--; /* One less char in buffer */
2014 return((int)(*zinptr++) & 0377); /* because we return the first */
2017 /* Z S O U T -- Write a string out to the given file, buffered. */
2019 /* Returns 0 on success, -1 on failure */
2022 zsout(n,s) int n; char *s; {
2025 if (rc < 1) return(-1); /* Keep this, prevents memory faults */
2026 if (!s) return(0); /* Null pointer, do nothing, succeed */
2027 if (!*s) return(0); /* empty string, ditto */
2031 This happens with client-side Kermit server when a REMOTE command
2032 was sent from the server to the client and the server is supposed to
2033 display the text, but of course there is no place to display it
2034 since it is in remote mode executing Kermit protocol.
2036 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2038 return(ttol(s,((int)strlen(s)) < 0) ? -1 : 0);
2041 #endif /* COMMENT */
2048 rc = write(fileno(fp[n]),s,k);
2049 return((rc == k) ? 0 : -1);
2051 rc = fputs(s,fp[n]) == EOF ? -1 : 0;
2057 /* Z S O U T L -- Write string to file, with line terminator, buffered */
2059 /* Returns 0 on success, -1 on failure */
2062 zsoutl(n,s) int n; char *s; {
2067 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2071 return(0); /* See comments in zsout() */
2072 #endif /* COMMENT */
2076 if (n == ZSFILE) /* Session log is unbuffered */
2077 return(write(fileno(fp[n]),"\n",1) == 1 ? 0 : -1);
2078 else if (fputs("\n",fp[n]) == EOF)
2080 if (n == ZDIFIL || n == ZWFILE) /* Flush connection log records */
2085 /* Z S O U T X -- Write x characters to file, unbuffered. */
2087 /* Returns number of characters written on success, -1 on failure */
2090 zsoutx(n,s,x) int n, x; char *s; {
2096 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2098 return(ttol(s,x)); /* See comments in zsout() */
2101 #endif /* COMMENT */
2105 if ((k = (int)strlen(s)) > x) x = k; /* Nothing else would make sense */
2107 if (chkfn(n) < 1) return(-1);
2108 return(write(fp[n]->_file,s,x));
2109 #endif /* COMMENT */
2110 return(write(fileno(fp[n]),s,x) == x ? x : -1);
2113 /* Z C H O U T -- Add a character to the given file. */
2115 /* Should return 0 or greater on success, -1 on failure (e.g. disk full) */
2119 zchout(register int n, char c)
2121 zchout(n,c) register int n; char c;
2122 #endif /* CK_ANSIC */
2124 /* if (chkfn(n) < 1) return(-1); */
2127 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2131 return(0); /* See comments in zsout() */
2132 #endif /* COMMENT */
2136 if (n == ZSFILE) /* Use unbuffered for session log */
2137 return(write(fileno(fp[n]),&c,1) == 1 ? 0 : -1);
2138 /* Buffered for everything else */
2139 if (putc(c,fp[n]) == EOF) /* If true, maybe there was an error */
2140 return(ferror(fp[n])?-1:0); /* Check to make sure */
2141 else /* Otherwise... */
2142 return(0); /* There was no error. */
2145 /* (PWP) buffered character output routine to speed up file IO */
2151 zoutptr = zoutbuffer; /* Reset buffer pointer in all cases */
2154 debug(F101,"zoutdump zoutcnt","",zoutcnt);
2156 if (zoutcnt == 0) { /* Nothing to output */
2158 } else if (zoutcnt < 0) { /* Unexpected negative argument */
2159 zoutcnt = 0; /* Reset output buffer count */
2160 return(-1); /* and fail. */
2164 if (inserver && !local && fp[ZOFILE] == stdout) {
2166 x = ttol(zoutbuffer,zoutcnt);
2168 x = 1; /* See comments in zsout() */
2169 #endif /* COMMENT */
2171 return(x > 0 ? 0 : -1);
2176 Frank Prindle suggested that replacing this fwrite() by an fflush()
2177 followed by a write() would improve the efficiency, especially when
2178 writing to stdout. Subsequent tests showed a 5-fold improvement.
2181 if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) ...
2182 #endif /* COMMENT */
2186 #endif /* CK_NONBLOCK */
2188 while (zoutcnt > 0) {
2189 if ((x = write(fileno(fp[ZOFILE]),zp,zoutcnt)) > -1) {
2191 if (deblog) /* Save a function call... */
2192 debug(F101,"zoutdump wrote","",x);
2194 zoutcnt -= x; /* Adjust output buffer count */
2195 zp += x; /* and pointer */
2199 debug(F101,"zoutdump write error","",errno);
2200 debug(F101,"zoutdump write returns","",x);
2203 zoutcnt = 0; /* Reset output buffer count */
2204 return(-1); /* write() failed */
2210 /* C H K F N -- Internal function to verify file number is ok */
2214 -1: File number n is out of range
2215 0: n is in range, but file is not open
2216 1: n in range and file is open
2220 /* if (n != ZDFILE) debug(F101,"chkfn","",n); */
2221 if (n < 0 || n >= ZNFILS) {
2222 if (n != ZDFILE) debug(F101,"chkfn out of range","",n);
2225 /* if (n != ZDFILE) debug(F101,"chkfn fp[n]","",fp[n]); */
2226 return((fp[n] == NULL) ? 0 : 1);
2230 /* Z G E T F S -- Return file size regardless of accessibility */
2232 Used for directory listings, etc.
2234 The size of the file in bytes, 0 or greater, if the size can be learned.
2235 -1 if the file size can not be obtained.
2236 Also (and this is a hack just for UNIX):
2237 If the argument is the name of a symbolic link,
2238 the global variable issymlink is set to 1,
2239 and the global buffer linkname[] gets the link value.
2240 And it sets zgfs_dir to 1 if it's a directory, otherwise 0.
2241 This lets us avoid numerous redundant calls to stat().
2245 time_t zgfs_mtime = 0;
2246 unsigned int zgfs_mode = 0;
2249 char linkname[CKMAXPATH+1];
2251 #define _IFLNK 0120000
2253 #endif /* CKSYMLINK */
2256 zgetfs(name) char *name; {
2258 char fnam[CKMAXPATH+4];
2259 CK_OFF_T size = (CK_OFF_T)-1;
2264 if (!name) name = "";
2265 if (!*name) return(-1);
2269 if (x == 9 && !strcmp(name,"/dev/null"))
2276 s = tilde_expand(s);
2281 x = ckstrncpy(fnam,s,CKMAXPATH);
2283 debug(F111,"zgetfs fnam",s,x);
2284 if (x > 0 && s[x-1] == '/')
2287 zgfs_dir = 0; /* Assume it's not a directory */
2288 zgfs_link = 0; /* Assume it's not a symlink */
2289 zgfs_mtime = 0; /* No time yet */
2290 zgfs_mode = 0; /* No permission bits yet */
2292 #ifdef CKSYMLINK /* We're doing symlinks? */
2293 #ifdef USE_LSTAT /* OK to use lstat()? */
2295 debug(F101,"STAT","",1);
2296 if (x < 0) /* stat() failed */
2298 if ( /* Now see if it's a symlink */
2300 S_ISLNK(buf.st_mode)
2303 ((_IFMT & buf.st_mode) == _IFLNK)
2305 #endif /* S_ISLNK */
2307 zgfs_link = 1; /* It's a symlink */
2308 linkname[0] = '\0'; /* Get the name */
2309 x = readlink(s,linkname,CKMAXPATH);
2310 debug(F101,"zgetfs readlink",s,x);
2311 if (x > -1 && x < CKMAXPATH) { /* It's a link */
2313 size = buf.st_size; /* Remember size of link */
2314 x = stat(s,&buf); /* Now stat the linked-to file */
2315 debug(F101,"STAT","",2);
2316 if (x < 0) /* so we can see if it's a directory */
2319 ckstrncpy(linkname,"(lookup failed)",CKMAXPATH);
2322 #else /* !USE_LSTAT */
2323 x = stat(s,&buf); /* No lstat(), use stat() instead */
2324 debug(F101,"STAT","",3);
2327 #endif /* USE_LSTAT */
2329 /* Do we need to call readlink()? */
2333 lstat() does not work in SCO operating systems. From "man NS lstat":
2335 lstat obtains information about the file named by path. In the case of a
2336 symbolic link, lstat returns information about the link, and not the file
2337 named by the link. It is only used by the NFS automount daemon and should
2338 not be utilized by users.
2341 debug(F101,"zgetfs forced needrlink","",needrlink);
2344 needrlink = S_ISLNK(buf.st_mode);
2345 debug(F101,"zgetfs S_ISLNK needrlink","",needrlink);
2348 needrlink = (_IFMT & buf.st_mode) == _IFLNK;
2349 debug(F101,"zgetfs _IFLNK needrlink","",needrlink);
2352 debug(F101,"zgetfs default needrlink","",needrlink);
2354 #endif /* S_ISLNK */
2355 #endif /* NOLINKBITS */
2360 x = readlink(s,linkname,CKMAXPATH);
2362 debug(F111,"zgetfs readlink",s,x);
2364 debug(F101,"zgetfs readlink errno","",errno);
2366 debug(F110,"zgetfs readlink result",linkname,0);
2368 if (x > -1 && x < CKMAXPATH) {
2373 #else /* !CKSYMLINK */
2374 x = stat(s,&buf); /* Just stat the file */
2375 debug(F111,"zgetfs stat",s,x);
2376 if (x < 0) /* and get the size */
2378 #endif /* CKSYMLINK */
2380 zgfs_mtime = buf.st_mtime;
2381 zgfs_mode = buf.st_mode;
2382 zgfs_dir = (S_ISDIR(buf.st_mode)) ? 1 : 0; /* Set "is directory" flag */
2383 debug(F111,"zgetfs size",s,size);
2384 debug(F111,"zgetfs st_size",s,buf.st_size);
2385 return((size < 0L) ? buf.st_size : size); /* Return the size */
2389 /* Z C H K I -- Check if input file exists and is readable */
2393 >= 0 if the file can be read (returns the size).
2394 -1 if file doesn't exist or can't be accessed,
2395 -2 if file exists but is not readable (e.g. a directory file).
2396 -3 if file exists but protected against read access.
2398 For Berkeley Unix, a file must be of type "regular" to be readable.
2399 Directory files, special files, and symbolic links are not readable.
2402 zchki(name) char *name; {
2406 extern int zchkid, diractive, matchfifo;
2416 if (x == 9 && !strcmp(s,"/dev/null"))
2418 if (x == 8 && !strcmp(s,"/dev/tty"))
2424 s = tilde_expand(s);
2431 debug(F111,"zchki setroot",ckroot,ckrootset);
2432 if (ckrootset) if (!zinroot(name)) {
2433 debug(F110,"zchki setroot violation",name,0);
2439 debug(F101,"STAT","",5);
2441 debug(F111,"zchki stat fails",s,errno);
2444 if (S_ISDIR (buf.st_mode))
2447 if (!(itsadir && zchkid)) { /* Unless this... */
2448 if (!S_ISREG (buf.st_mode) /* Must be regular file */
2450 && (!matchfifo || !S_ISFIFO (buf.st_mode)) /* or FIFO */
2451 #endif /* S_ISFIFO */
2453 debug(F111,"zchki not regular file (or fifo)",s,matchfifo);
2457 debug(F111,"zchki stat ok:",s,x);
2459 if (diractive) { /* If listing don't check access */
2463 debug(F100,"zchki swapping ids for access()","",0);
2465 #endif /* SW_ACC_ID */
2466 if ((x = access(s,R_OK)) < 0)
2467 x = access(s,X_OK); /* For RUN-class commands */
2470 debug(F100,"zchki swapped ids restored","",0);
2471 #endif /* SW_ACC_ID */
2473 if (x < 0) { /* Is the file accessible? */
2474 debug(F111,"zchki access failed:",s,x); /* No */
2477 iflen = buf.st_size; /* Yes, remember size */
2478 ckstrncpy(nambuf,s,CKMAXPATH); /* and name globally. */
2479 debug(F111,"zchki access ok:",s,iflen);
2480 return((iflen > -1L) ? iflen : 0L);
2484 /* Z C H K O -- Check if output file can be created */
2487 Returns -1 if write permission for the file would be denied, 0 otherwise.
2489 NOTE: The design is flawed. There is no distinction among:
2490 . Can I overwrite an existing file?
2491 . Can I create a file (or directory) in an existing directory?
2492 . Can I create a file (or directory) and its parent(s)?
2495 zchko(name) char *name; {
2496 int i, x, itsadir = 0;
2499 extern int zchkod; /* Used by IF WRITEABLE */
2501 debug(F110,"zchko entry",name,0);
2503 if (!name) return(-1); /* Watch out for null pointer. */
2508 debug(F111,"zchko setroot",ckroot,ckrootset);
2509 if (ckrootset) if (!zinroot(name)) {
2510 debug(F110,"zchko setroot violation",name,0);
2516 x = (int)strlen(name); /* Get length of filename */
2517 debug(F111,"zchko len",name,x);
2518 debug(F111,"zchko zchkod",name,zchkod);
2522 Writing to null device is OK.
2524 if (x == 9 && !strcmp(name,"/dev/null"))
2526 if (x == 8 && !strcmp(name,"/dev/tty"))
2533 s = tilde_expand(s);
2542 zchkod is a global flag meaning we're checking not to see if the directory
2543 file is writeable, but if it's OK to create files IN the directory.
2545 if (!zchkod && isdir(name)) { /* Directories are not writeable */
2546 debug(F111,"zchko isdir",name,1);
2549 s = malloc(x+3); /* Must copy because we can't */
2550 if (!s) { /* write into our argument. */
2551 fprintf(stderr,"zchko: Malloc error 46\n");
2554 ckstrncpy(s,name,x+3);
2558 /* Allow tty devices to opened as output files */
2559 int fd, istty = 0, mode = 0;
2560 debug(F110,"zchko attempting to open",name,0);
2561 /* Don't block on lack of Carrier or other modem signals */
2570 #endif /* FNDELAY */
2571 #endif /* O_NDELAY */
2572 #endif /* O_NONBLOCK */
2573 debug(F111,"zchko open mode",name,mode);
2574 fd = open(name,O_WRONLY,mode); /* Must attempt to open it */
2575 debug(F111,"zchko open",name,fd);
2576 if (fd > -1) { /* to get a file descriptor */
2577 if (isatty(fd)) /* for isatty() */
2579 debug(F111,"zchko isatty",name,istty);
2585 debug(F101,"zchko open errno","",errno);
2591 for (i = x; i > 0; i--) { /* Strip filename from right. */
2592 if (ISDIRSEP(s[i-1])) {
2597 debug(F101,"zchko i","",i);
2598 debug(F101,"zchko itsadir","",itsadir);
2601 /* X/OPEN XPG3-compliant systems fail if argument ends with "/"... */
2602 if (i == 0) /* If no path, use current directory */
2604 else /* Otherwise, use given one. */
2609 The following does not work for "foo/bar" where the foo directory does
2610 not exist even though we could create it: access("foo/.") fails, but
2611 access("foo") works OK.
2613 /* So now we use "path/." if path given, or "." if no path given. */
2614 s[i++] = '.'; /* Append "." to path. */
2617 /* So NOW we strip path segments from the right as long as they don't */
2618 /* exist -- we only call access() for path segments that *do* exist.. */
2619 /* (But this isn't quite right either since now zchko(/foo/bar/baz/xxx) */
2620 /* succeeds when I have write access to foo and bar but baz doesn't exit.) */
2622 if (itsadir && i > 0) {
2624 while (s[0] && !isdir(s)) {
2625 for (i = (int)strlen(s); i > 0; i--) {
2626 if (ISDIRSEP(s[i-1])) {
2635 s[i++] = '.'; /* Append "." to path. */
2638 #endif /* COMMENT */
2639 #endif /* COMMENT */
2642 ckstrncpy(s,".",x+3);
2647 debug(F100,"zchko swapping ids for access()","",0);
2649 #endif /* SW_ACC_ID */
2651 x = access(s,W_OK); /* Check access of path. */
2655 debug(F100,"zchko swapped ids restored","",0);
2656 #endif /* SW_ACC_ID */
2659 debug(F111,"zchko access failed:",s,errno);
2661 debug(F111,"zchko access ok:",s,x);
2662 if (s) free(s); /* Free temporary storage */
2664 return((x < 0) ? -1 : 0); /* and return. */
2667 /* Z D E L E T -- Delete the named file. */
2669 /* Returns: -1 on error, 0 on success */
2672 zdelet(name) char *name; {
2677 #endif /* CK_LOGIN */
2680 debug(F111,"zdelet setroot",ckroot,ckrootset);
2681 if (ckrootset) if (!zinroot(name)) {
2682 debug(F110,"zdelet setroot violation",name,0);
2688 debug(F111,"zdelet",name,x);
2690 if (ckxsyslog >= SYSLG_FC && ckxlogging) {
2692 zfnqfp(name,CKMAXPATH,fullname);
2693 debug(F110,"zdelet fullname",fullname,0);
2695 syslog(LOG_INFO, "file[] %s: delete failed (%m)", fullname);
2697 syslog(LOG_INFO, "file[] %s: delete ok", fullname);
2699 #endif /* CKSYSLOG */
2703 /* Z R T O L -- Convert remote filename into local form */
2706 zrtol(name,name2) char *name, *name2; {
2707 nzrtol(name,name2,1,0,CKMAXPATH);
2711 nzrtol(name,name2,fncnv,fnrpath,max)
2712 char *name, *name2; int fncnv, fnrpath, max;
2715 int flag = 0, n = 0;
2716 char fullname[CKMAXPATH+1];
2720 if (!name) name = "";
2722 debug(F111,"nzrtol name",name,fncnv);
2727 s = tilde_expand(s);
2733 /* Handle the path -- we don't have to convert its format, since */
2734 /* the standard path format and our (UNIX) format are the same. */
2737 devnull = !strcmp(name,"/dev/null");
2739 if (!devnull && fnrpath == PATH_OFF) { /* RECEIVE PATHNAMES OFF */
2741 strncpy(fullname,p,CKMAXPATH);
2742 } else if (!devnull && fnrpath == PATH_ABS) { /* REC PATHNAMES ABSOLUTE */
2743 strncpy(fullname,name,CKMAXPATH);
2744 } else if (!devnull && isabsolute(name)) { /* RECEIVE PATHNAMES RELATIVE */
2745 ckmakmsg(fullname,CKMAXPATH,".",name,NULL,NULL);
2746 } else { /* Ditto */
2747 ckstrncpy(fullname,name,CKMAXPATH);
2749 fullname[CKMAXPATH] = NUL;
2750 debug(F110,"nzrtol fullname",fullname,0);
2754 The maximum length for any segment of a filename is MAXNAMLEN, defined
2755 above. On some platforms (at least QNX) if a segment exceeds this limit,
2756 the open fails with ENAMETOOLONG, so we must prevent it by truncating each
2757 overlong name segment to the maximum segment length before passing the
2758 name to open(). This must be done even when file names are literal, so as
2759 not to halt a file transfer unnecessarily.
2762 char buf[CKMAXPATH+1]; /* New temporary buffer on stack */
2763 char *p = fullname; /* Source and */
2764 char *s = buf; /* destination pointers */
2766 debug(F101,"nzrtol sizing MAXNAMLEN","",MAXNAMLEN);
2767 while (*p && n < CKMAXPATH) { /* Copy name to new buffer */
2768 if (++i > MAXNAMLEN) { /* If this segment too long */
2769 while (*p && *p != '/') /* skip past the rest... */
2771 i = 0; /* and reset counter. */
2772 } else if (*p == '/') { /* End of this segment. */
2773 i = 0; /* Reset counter. */
2775 *s++ = *p++; /* Copy this character. */
2779 ckstrncpy(fullname,buf,CKMAXPATH); /* Copy back to original buffer. */
2780 debug(F111,"nzrtol sizing",fullname,n);
2782 #endif /* NOTRUNCATE */
2784 if (!fncnv || devnull) { /* Not converting */
2785 ckstrncpy(name2,fullname,max); /* We're done. */
2788 name = fullname; /* Converting */
2791 for (; *name != '\0' && n < maxnam; name++) {
2792 if (*name > SP) flag = 1; /* Strip leading blanks and controls */
2793 if (flag == 0 && *name < '!')
2801 if (isupper(*name)) /* Check for mixed case */
2803 else if (islower(*name))
2809 *p-- = '\0'; /* Terminate */
2810 while (*p < '!' && p > name2) /* Strip trailing blanks & controls */
2813 if (*name2 == '\0') { /* Nothing left? */
2814 ckstrncpy(name2,"NONAME",max); /* do this... */
2815 } else if (acase == 1) { /* All uppercase? */
2816 p = name2; /* So convert all letters to lower */
2823 debug(F110,"nzrtol new name",name2,0);
2827 /* Z S T R I P -- Strip device & directory name from file specification */
2829 /* Strip pathname from filename "name", return pointer to result in name2 */
2831 static char work[CKMAXPATH+1];
2834 zstrip(name,name2) char *name, **name2; {
2838 debug(F110,"zstrip before",name,0);
2839 if (!name) { *name2 = ""; return; }
2842 /* Strip leading tilde */
2843 if (*name == '~') name++;
2844 debug(F110,"zstrip after tilde-stripping",name,0);
2846 for (cp = name; *cp; cp++) {
2847 if (ISDIRSEP(*cp)) {
2852 if (n++ >= CKMAXPATH)
2856 *pp = '\0'; /* Terminate the string */
2858 debug(F110,"zstrip after",*name2,0);
2861 /* Z L T O R -- Local TO Remote */
2864 zltor(name,name2) char *name, *name2; {
2865 nzltor(name,name2,1,0,CKMAXPATH);
2868 /* N Z L T O R -- New Local TO Remote */
2871 fncnv = 0 for no conversion, > 0 for regular conversion, < 0 for minimal.
2874 nzltor(name,name2,fncnv,fnspath,max)
2875 char *name, *name2; int fncnv, fnspath, max;
2880 #endif /* COMMENT */
2884 char fullname[CKMAXPATH+1];
2889 extern int fcharset, /* tcharset, */ language;
2891 _PROTOTYP ( CHAR (*sxo), (CHAR) ) = NULL; /* Translation functions */
2893 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR);
2895 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])();
2896 #endif /* CK_ANSIC */
2898 language = L_USASCII;
2900 /* Proper translation of filenames must be done elsewhere */
2901 n = tcharset ? tcharset : TC_USASCII;
2902 sxo = xls[n][fcharset];
2904 sxo = xls[TC_USASCII][fcharset];
2905 #endif /* COMMENT */
2906 #endif /* NOCSETS */
2908 debug(F110,"nzltor name",name,0);
2910 /* Handle pathname */
2913 if (fnspath == PATH_OFF) { /* PATHNAMES OFF */
2915 ckstrncpy(fullname,p,CKMAXPATH);
2916 } else { /* PATHNAMES RELATIVE or ABSOLUTE */
2919 if (!strncmp(p,"../",3))
2921 else if (!strncmp(p,"./",2))
2926 if (fnspath == PATH_ABS) { /* ABSOLUTE */
2927 zfnqfp(p,CKMAXPATH,fullname);
2928 } else { /* RELATIVE */
2929 ckstrncpy(fullname,p,CKMAXPATH);
2932 debug(F110,"nzltor fullname",fullname,0);
2934 if (!fncnv) { /* Not converting */
2935 ckstrncpy(name2,fullname,max); /* We're done. */
2938 #endif /* NOCSETS */
2941 name = fullname; /* Converting */
2945 int tilde = 0, bslash = 0;
2947 if ((namechars = getenv("NAMECHARS")) != NULL) {
2948 if (ckstrchr(namechars, '~' ) != NULL) tilde = '~';
2949 if (ckstrchr(namechars, '\\') != NULL) bslash = '\\';
2956 pp = work; /* Output buffer */
2957 for (cp = name, n = 0; *cp && n < max; cp++,n++) { /* Convert name chars */
2960 if (sxo) c = (*sxo)(c); /* Convert to ASCII */
2961 #endif /* NOCSETS */
2962 if (fncnv > 0 && islower(c)) /* Uppercase letters */
2963 *pp++ = toupper(c); /* Change tilde to hyphen */
2966 else if (fncnv > 0 && c == '#') /* Change number sign to 'X' */
2968 else if (c == '*' || c == '?') /* Change wildcard chars to 'X' */
2970 else if (c == ' ') /* Change space to underscore */
2972 else if (c < ' ') /* Change controls to 'X' */
2974 else if (fncnv > 0 && c == '.') { /* Change dot to underscore */
2975 dotp = pp; /* Remember where we last did this */
2983 *pp = NUL; /* Tie it off. */
2985 if (dotp) *dotp = '.'; /* Restore last dot (if any) */
2987 if (dotp > dirp) *dotp = '.'; /* Restore last dot in file name */
2988 #endif /* COMMENT */
2989 cp = name2; /* If nothing before dot, */
2990 if (*work == '.') *cp++ = 'X'; /* insert 'X' */
2991 ckstrncpy(cp,work,max);
2994 #endif /* NOCSETS */
2995 debug(F110,"nzltor name2",name2,0);
2999 /* Z C H D I R -- Change directory */
3002 dirnam = pointer to name of directory to change to,
3003 which may be "" or NULL to indicate user's home directory.
3009 zchdir(dirnam) char *dirnam; {
3012 _PROTOTYP (int slotdir,(char *,char *));
3015 extern struct mtab *mactab; /* Main macro table */
3016 extern int nmac; /* Number of macros */
3019 debug(F110,"zchdir",dirnam,0);
3020 if (!dirnam) dirnam = "";
3021 if (!*dirnam) /* If argument is null or empty, */
3022 dirnam = zhome(); /* use user's home directory. */
3024 debug(F110,"zchdir 2",dirnam,0);
3027 hd = tilde_expand(dirnam); /* Attempt to expand tilde */
3029 if (*hd == '\0') hd = dirnam; /* in directory name. */
3033 debug(F110,"zchdir 3",hd,0);
3036 debug(F111,"zchdir setroot",ckroot,ckrootset);
3037 if (ckrootset) if (!zinroot(hd)) {
3038 debug(F110,"zchdir setroot violation",hd,0);
3044 /* Just to save some space */
3045 return((chdir(hd) == 0) ? 1 : 0);
3047 if (chdir(hd) == 0) { /* Try to cd */
3050 if (inserver && ikdbopen)
3051 slotdir(isguest ? anonroot : "", zgtdir());
3052 #endif /* CK_LOGIN */
3056 if (nmac) { /* Any macros defined? */
3058 static int on_cd = 0;
3061 k = mlook(mactab,"on_cd",nmac); /* Look this up */
3062 if (k >= 0) { /* If found, */
3063 if (dodo(k,zgtdir(),0) > -1) /* set it up, */
3064 parser(1); /* and execute it */
3078 zchkpid(unsigned long xpid)
3080 zchkpid(xpid) unsigned long xpid;
3081 #endif /* CK_ANSIC */
3083 return((kill((PID_T)xpid,0) < 0) ? 0 : 1);
3087 /* Z H O M E -- Return pointer to user's home directory */
3089 static char * zhomdir = NULL;
3097 return((char *)ckroot);
3101 home = getenv("home");
3103 home = getenv("HOME");
3105 makestr(&zhomdir,home);
3106 return(home ? zhomdir : ".");
3109 /* Z G T D I R -- Returns a pointer to the current directory */
3112 The "preferred" interface for getting the current directory in modern UNIX
3113 is getcwd() [POSIX 1003.1 5.2.2]. However, on certain platforms (such as
3114 SunOS), it is implemented by forking a shell, feeding it the pwd command,
3115 and returning the result, which is not only inefficient but also can result
3116 in stray messages to the terminal. In such cases -- as well as when
3117 getcwd() is not available at all -- getwd() can be used instead by defining
3118 USE_GETWD. However, note that getwd() provides no buffer-length argument
3119 and therefore no safeguard against memory leaks.
3129 #endif /* USE_GETWD */
3132 #define CWDBL 80 /* Save every byte we can... */
3134 #define CWDBL CKMAXPATH
3136 static char cwdbuf[CWDBL+2];
3138 NOTE: The getcwd() prototypes are commented out on purpose. If you get
3139 compile-time warnings, search through your system's header files to see
3140 which one has the needed prototype, and #include it. Usually it is
3141 <unistd.h>. See the section for including <unistd.h> in ckcdeb.h and
3142 make any needed adjustments there (and report them).
3146 char * buf = cwdbuf;
3150 extern char *getwd();
3152 debug(F110,"zgtdir BSD4 getwd()",s,0);
3158 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3159 #endif /* DCLGETCWD */
3160 debug(F101,"zgtdir BSD44 CWDBL","",CWDBL);
3161 s = getcwd(buf,CWDBL);
3167 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3168 #endif /* DCLGETCWD */
3169 debug(F101,"zgtdir MINIX2 CWDBL","",CWDBL);
3170 s = getcwd(buf,CWDBL);
3176 /* This non-ANSI prototype can be fatal at runtime! (e.g. in SCO3.2v5.0.5). */
3177 /* Anyway it's already prototyped in some header file that we have included. */
3178 extern char *getcwd();
3181 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3182 #endif /* DCLGETCWD */
3183 #endif /* COMMENT */
3184 debug(F101,"zgtdir SVORPOSIX CWDBL","",CWDBL);
3185 s = getcwd(buf,CWDBL);
3192 extern char *getcwd();
3193 #endif /* DCLGETCWD */
3194 debug(F101,"zgtdir COHERENT _I386 CWDBL","",CWDBL);
3195 s = getcwd(buf,CWDBL);
3199 extern char *getwd();
3200 debug(F101,"zgtdir COHERENT CWDBL","",CWDBL);
3207 debug(F101,"zgtdir SUNOS CWDBL","",CWDBL);
3208 s = getcwd(buf,CWDBL);
3214 #endif /* COHERENT */
3215 #endif /* SYSVORPOSIX */
3218 #endif /* USE_GETWD */
3221 /* Z X C M D -- Run a system command so its output can be read like a file */
3225 zxcmd(filnum,comand) int filnum; char *comand; {
3228 extern int kactive; /* From ckcpro.w and ckcmai.c */
3231 debug(F100,"zxcmd fails: nopush","",0);
3234 debug(F111,"zxcmd",comand,filnum);
3235 if (chkfn(filnum) < 0) return(-1); /* Need a valid Kermit file number. */
3236 if (filnum == ZSTDIO || filnum == ZCTERM) /* But not one of these. */
3239 out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
3240 debug(F101,"zxcmd out",comand,out);
3242 /* Output to a command */
3244 if (out) { /* Need popen() to do this. */
3245 ckstrncpy(fullname,"(pipe)",CKMAXPATH);
3247 return(0); /* no popen(), fail. */
3249 /* Use popen() to run the command. */
3251 #ifdef _POSIX_SOURCE
3252 /* Strictly speaking, popen() is not available in POSIX.1 */
3254 #endif /* _POSIX_SOURCE */
3256 debug(F110,"zxcmd out",comand,0);
3259 debug(F100,"zxcmd priv_chk failed","",0);
3263 fp[filnum] = popen(comand,"w");
3264 debug(F111,"zxcmd popen",fp[filnum] ? "OK" : "Failed", errno);
3265 if (fp[filnum] == NULL)
3268 /* I wonder what this is all about... */
3269 close(pipes[0]); /* Don't need the input side */
3270 fp[filnum] = fdopen(pipes[1],"w"); /* Open output stream. */
3271 fp[ZSYSFN] = fp[filnum]; /* Remember. */
3272 #endif /* COMMENT */
3274 zoutcnt = 0; /* (PWP) reset input buffer */
3275 zoutptr = zoutbuffer;
3277 #endif /* NOPOPEN */
3280 /* Input from a command */
3283 /* SINIX-L 5.41 does not like fdopen() */
3286 if (pipe(pipes) != 0) {
3287 debug(F100,"zxcmd pipe failure","",0);
3288 return(0); /* can't make pipe, fail */
3291 /* Create a fork in which to run the named process */
3295 pid = vfork() /* child */
3297 pid = fork() /* child */
3301 /* We're in the fork. */
3303 char *shpath, *shname, *shptr; /* Find user's preferred shell */
3307 #ifdef HPUX10 /* Default shell */
3308 defshell = "/usr/bin/sh";
3311 defshell = "/bin/rc";
3313 defshell = "/bin/sh";
3317 if (priv_can()) exit(1); /* Turn off any privileges! */
3318 debug(F101,"zxcmd pid","",pid);
3319 close(pipes[0]); /* close input side of pipe */
3320 close(0); /* close stdin */
3321 if (open("/dev/null",0) < 0) return(0); /* replace input by null */
3324 dup2(pipes[1],1); /* BSD: replace stdout & stderr */
3325 dup2(pipes[1],2); /* by the pipe */
3327 close(1); /* AT&T: close stdout */
3328 if (dup(pipes[1]) != 1) /* Send stdout to the pipe */
3330 close(2); /* Send stderr to the pipe */
3331 if (dup(pipes[1]) != 2)
3333 #endif /* SVORPOSIX */
3338 close(pipes[1]); /* Don't need this any more. */
3341 if ((shpath = getenv("SERVERSHELL")) == NULL)
3344 shpath = getenv("SHELL"); /* What shell? */
3345 if (shpath == NULL) {
3346 p = getpwuid( real_uid() ); /* Get login data */
3347 /* debug(F111,"zxcmd shpath","getpwuid()",p); */
3348 if (p == (struct passwd *)NULL || !*(p->pw_shell))
3350 else shpath = p->pw_shell;
3353 shptr = shname = shpath;
3354 while (*shptr != '\0')
3355 if (*shptr++ == '/')
3357 debug(F110,shpath,shname,0);
3358 restorsigs(); /* Restore ignored signals */
3359 execl(shpath,shname,"-c",comand,(char *)NULL); /* Execute the cmd */
3360 exit(0); /* just punt if it failed. */
3361 } else if (pid == (PID_T) -1) {
3362 debug(F100,"zxcmd fork failure","",0);
3365 debug(F101,"zxcmd pid","",pid);
3366 close(pipes[1]); /* Don't need the output side */
3367 ispipe[filnum] = 1; /* Remember it's a pipe */
3368 fp[filnum] = fdopen(pipes[0],"r"); /* Open a stream for input. */
3372 if (filnum == ZIFILE && kactive) { /* Make pipe reads nonblocking */
3374 if ((flags = fcntl(fileno(fp[filnum]),F_GETFL,0)) > -1) {
3375 debug(F101,"zxcmd fcntl 1 pipe flags","",flags);
3376 x = fcntl(fileno(fp[filnum]),F_SETFL, flags |
3383 debug(F101,"zxcmd fcntl 2 result","",x);
3387 #endif /* DONDELAY */
3389 fp[ZSYSFN] = fp[filnum]; /* Remember. */
3390 zincnt = 0; /* (PWP) reset input buffer */
3396 /* Z C L O S F - wait for the child fork to terminate and close the pipe. */
3398 /* Used internally by zclose - returns -1 on failure, 1 on success. */
3401 zclosf(filnum) int filnum; {
3405 debug(F101,"zclosf filnum","",filnum);
3406 out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
3407 debug(F101,"zclosf out","",out);
3411 /* In UNIX we use popen() only for output files */
3415 x = pclose(fp[filnum]);
3417 debug(F101,"zclosf pclose","",x);
3418 debug(F101,"zclosf pexitstat","",pexitstat);
3419 fp[filnum] = fp[ZSYSFN] = NULL;
3421 return((x != 0) ? -1 : 1);
3423 #endif /* NOPOPEN */
3424 /* debug(F101,"zclosf fp[filnum]","", fp[filnum]); */
3425 /* debug(F101,"zclosf fp[ZSYSFN]","", fp[ZSYSFN]); */
3427 if (pid != (PID_T) 0) {
3428 debug(F101,"zclosf killing pid","",pid);
3437 This is the original code (before 20 April 1997) and has proven totally
3438 portable. But it does not give us the process's return code.
3440 while ((wstat = wait((WAIT_T *)0)) != pid && wstat != -1) ;
3442 /* Here we try to get the return code. Let's hope this is portable too. */
3443 while ((wstat = wait(&statusp)) != pid && wstat != -1) ;
3444 pexitstat = (statusp & 0xff) ? statusp : statusp >> 8;
3445 debug(F101,"zclosf wait statusp","",statusp);
3446 debug(F101,"zclosf wait pexitstat","",pexitstat);
3447 #endif /* CK_CHILD */
3451 fp[filnum] = fp[ZSYSFN] = NULL;
3454 /* debug(F101,"zclosf fp[filnum]","",fp[filnum]); */
3456 return(pexitstat == 0 ? 1 : -1);
3459 #endif /* CK_CHILD */
3465 zxcmd(filnum,comand) int filnum; char *comand; {
3469 zclosf(filnum) int filnum; {
3475 /* Z X P A N D -- Expand a wildcard string into an array of strings */
3477 As of C-Kermit 7.0, this API is obsolete, replaced by nzxpand(), and this
3478 function is only used internally. See nzxpand() below.
3480 Returns the number of files that match fnarg, with data structures set up
3481 so that first file (if any) will be returned by the next znext() call.
3483 Depends on external variable wildxpand: 0 means we expand wildcards
3484 internally, nonzero means we call the shell to do it.
3486 AND in C-Kermit 8.0.212 and later, on extern wildena: 1 means wildcards
3487 are enabled, 0 means disabled, the characters are taken literally.
3489 static int xdironly = 0;
3490 static int xfilonly = 0;
3491 static int xmatchdot = 0;
3492 static int xrecursive = 0;
3493 static int xnobackup = 0;
3494 static int xnolinks = 0;
3496 static char *freeptr = NULL, **resptr = NULL; /* Copies of caller's args */
3497 static int remlen; /* Remaining space in caller's array */
3498 static int numfnd = 0; /* Number of matches found */
3500 #define MINSPACE 1024
3503 initspace(resarry,len) char * resarry[]; int len; {
3505 if (len < MINSPACE) len = MINSPACE;
3506 if (!sspace) { /* Need to allocate string space? */
3507 while (len >= MINSPACE) {
3508 if ((sspace = malloc(len+2))) { /* Got it. */
3509 debug(F101,"fgen string space","",len);
3512 len = (len / 2) + (len / 4); /* Didn't, reduce by 3/4 */
3514 if (len <= MINSPACE) { /* Did we get it? */
3515 fprintf(stderr,"fgen can't malloc string space\n");
3520 #endif /* DYNAMIC */
3522 freeptr = sspace; /* This is where matches are copied. */
3523 resptr = resarry; /* Static copies of these so */
3524 remlen = len; /* recursive calls can alter them. */
3525 debug(F101,"initspace ssplen","",ssplen);
3530 Z S E T F I L -- Query or change the size of file list buffers.
3532 fc = 1: Change current string space to n, return new size.
3533 fc = 2: Return current string space size.
3534 fc = 3: Change current maxnames to n, return new maxnames.
3535 fc = 4: Return current maxnames.
3536 Returns < 0 on error.
3539 zsetfil(n, fc) int n, fc; {
3542 case 1: /* Stringspace */
3547 if (initspace(mtchs,n) < 0)
3549 case 2: /* Fall thru deliberately */
3551 case 3: /* Listsize */
3553 free((char *)mtchs);
3556 mtchs = (char **)malloc(n * sizeof(char *));
3560 case 4: /* Fall thru deliberately */
3563 #endif /* DYNAMIC */
3573 #endif /* NONZXPAND */
3575 zxpand(fnarg) char *fnarg; {
3576 extern int diractive;
3577 char fnbuf[CKMAXPATH+8], * fn, * p;
3579 #ifdef DTILDE /* Built with tilde-expansion? */
3585 if (!fnarg) { /* If no argument provided */
3586 nxpand = fcount = 0;
3587 return(0); /* Return zero files found */
3589 debug(F110,"zxpand entry",fnarg,0);
3590 debug(F101,"zxpand xdironly","",xdironly);
3591 debug(F101,"zxpand xfilonly","",xfilonly);
3593 if (!*fnarg) { /* If no argument provided */
3594 nxpand = fcount = 0;
3595 return(0); /* Return zero files found */
3599 debug(F111,"zxpand setroot",ckroot,ckrootset);
3600 if (ckrootset) if (!zinroot(fnarg)) {
3601 debug(F110,"zxpand setroot violation",fnarg,0);
3602 nxpand = fcount = 0;
3609 This would have been perfect, except it makes us return fully qualified
3610 pathnames for all files.
3612 zfnqfp(fnarg,CKMAXPATH,fnbuf);
3613 debug(F110,"zxpand zfnqfp",fnbuf,0);
3615 debug(F110,"zxpand zgtdir",s,0);
3617 while (*p && *s) /* Make it relative */
3620 fn = (*s) ? fnbuf : p;
3621 debug(F110,"zxpand fn 0",fn,0);
3627 debug(F110,"zxpand fn 0.5",fn,0);
3629 #ifdef DTILDE /* Built with tilde-expansion? */
3630 if (*fnarg == '~') { /* Starts with tilde? */
3631 tnam = tilde_expand(fnarg); /* Try to expand it. */
3632 ckstrncpy(fnbuf,tnam,CKMAXPATH);
3635 ckstrncpy(fnbuf,fnarg,CKMAXPATH);
3636 fn = fnbuf; /* Point to what we'll work with */
3637 #endif /* COMMENT */
3638 debug(F110,"zxpand fn 1",fn,0);
3640 if (!*fn) /* But make sure something is there */
3643 p = fn + (int)strlen(fn) - 1;
3644 if (*p == '/') { /* If last char = / it must be a dir */
3645 if (!xfilonly && !iswild(p)) haveonedir++;
3646 ckstrncat(fn, "*", CKMAXPATH+8); /* so append '*' */
3647 } else if (p > fn) { /* If ends in "/." */
3648 if (*(p-1) == '/' && *p == '.') /* change '.' to '*' */
3650 } else if (p == fn) { /* If it's '.' alone */
3651 if (*p == '.') /* change '.' to '*' */
3654 debug(F110,"zxpand fn 2",fn,0);
3655 x = isdir(fn); /* Is it a directory? */
3656 debug(F111,"zxpand isdir 1",fn,x);
3657 if (x) { /* If so, make it into a wildcard */
3658 if (!xfilonly && !iswild(p))
3660 if ((x = strlen(fn)) > 0) {
3661 if (!ISDIRSEP(fn[x-1]))
3667 debug(F111,"zxpand fn 3 haveonedir",fn,haveonedir);
3669 The following allows us to parse a single directory name without opening
3670 the directory and looking at its contents. The diractive flag is a horrible
3671 hack (especially since DIR /NORECURSIVE turns it off), but otherwise we'd
3672 have to change the API.
3674 debug(F111,"zxpand fn 3 diractive",fn,diractive);
3675 if (!diractive && haveonedir) {
3678 mtchs = (char **)malloc(maxnames * sizeof(*mtchs));
3680 return(nxpand = fcount);
3683 debug(F110,"zxpand haveonedir A1",fnarg,0);
3684 initspace(mtchs,ssplen);
3686 if (numfnd < 0) return(-1);
3687 mtchptr = mtchs; /* Save pointer for next. */
3688 debug(F111,"zxpand haveonedir A2",*mtchptr,numfnd);
3689 return(nxpand = fcount);
3693 if (!nopush && wildxpand) /* Who is expanding wildcards? */
3694 fcount = (mtchs == NULL && /* Shell */
3695 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
3697 : shxpand(fn,mtchs,maxnames);
3700 fcount = (mtchs == NULL && /* Kermit */
3701 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
3703 : fgen(fn,mtchs,maxnames); /* Look up the file. */
3705 if (fcount == 0 && haveonedir) {
3707 debug(F110,"zxpand haveonedir B",fnarg,0);
3709 if (numfnd < 0) return(-1);
3711 mtchptr = mtchs; /* Save pointer for next. */
3717 debug(F111,"zxpand ok",mtchs[0],fcount);
3719 debug(F101,"zxpand fcount","",fcount);
3726 /* N Z X P A N D -- Expand a file list, with options. */
3729 s = pointer to filename or pattern.
3730 flags = option bits:
3732 flags & ZX_FILONLY Match regular files
3733 flags & ZX_DIRONLY Match directories
3734 flags & ZX_RECURSE Descend through directory tree
3735 flags & ZX_MATCHDOT Match "dot files"
3736 flags & ZX_NOBACKUP Don't match "backup files"
3737 flags & ZX_NOLINKS Don't follow symlinks.
3739 Returns the number of files that match s, with data structures set up
3740 so that first file (if any) will be returned by the next znext() call.
3743 nzxpand(s,flags) char * s; int flags; {
3747 debug(F111,"nzxpand",s,flags);
3748 x = flags & (ZX_DIRONLY|ZX_FILONLY);
3749 xdironly = (x == ZX_DIRONLY);
3750 xfilonly = (x == ZX_FILONLY);
3751 if (xdironly && xfilonly) {
3755 xmatchdot = (flags & ZX_MATCHDOT);
3756 debug(F111,"nzxpand xmatchdot 1",s,xmatchdot);
3757 /* If xmatchdot not set by caller but pattern implies it, set it anyway */
3758 if (!xmatchdot && ((p = ckstrchr(s,'.')))) {
3759 if (p == s && p[1] != '/') {
3761 debug(F111,"nzxpand xmatchdot 2",s,xmatchdot);
3763 xmatchdot = (*(p-1) == ',') || (*(p-1) == '{') || (*(p-1) == '/');
3764 debug(F111,"nzxpand xmatchdot 3",s,xmatchdot);
3767 xrecursive = (flags & ZX_RECURSE);
3768 xnobackup = (flags & ZX_NOBACKUP);
3769 xnolinks = (flags & ZX_NOLINKS);
3773 debug(F101,"nzxpand xdironly","",xdironly);
3774 debug(F101,"nzxpand xfilonly","",xfilonly);
3775 debug(F101,"nzxpand xmatchdot","",xmatchdot);
3776 debug(F101,"nzxpand xrecursive","",xrecursive);
3777 debug(F101,"nzxpand xnobackup","",xnobackup);
3778 debug(F101,"nzxpand xnolinks","",xnolinks);
3784 sh_sort(mtchs,NULL,x,0,0,1); /* Alphabetize the list */
3793 #endif /* NONZXPAND */
3796 /* Z X R E W I N D -- Rewinds the zxpand() list */
3800 /* if (!mtchs) return(-1); */
3805 #endif /* NOZXREWIND */
3807 /* Z N E X T -- Get name of next file from list created by zxpand(). */
3809 Returns >0 if there's another file, with its name copied into the arg string,
3810 or 0 if no more files in list.
3813 znext(fn) char *fn; {
3815 ckstrncpy(fn,*mtchptr++,CKMAXPATH);
3820 debug(F111,"znext",fn,fcount+1);
3823 debug(F111,"znext",fn,fcount); /* Return 0 if no filename to return */
3825 #endif /* COMMENT */
3828 /* Z C H K S P A -- Check if there is enough space to store the file */
3831 Call with file specification f, size n in bytes.
3832 Returns -1 on error, 0 if not enough space, 1 if enough space.
3837 zchkspa(char *f, CK_OFF_T n)
3839 zchkspa(f,n) char *f; CK_OFF_T n;
3840 #endif /* CK_ANSIC */
3842 /* In UNIX there is no good (and portable) way. */
3843 return(1); /* Always say OK. */
3846 #ifdef COMMENT /* (not used) */
3848 /* I S B A C K U P -- Tells if given file has a backup suffix */
3851 -1: Invalid argument
3852 0: File does not have a backup suffix
3853 >0: Backup suffix number
3856 isbackup(fn) char * fn; { /* Get backup suffix number */
3857 int i, j, k, x, state, flag;
3859 if (!fn) /* Watch out for null pointers. */
3861 if (!*fn) /* And empty names. */
3865 for (i = (int)strlen(fn) - 1; (!flag && (i > 0)); i--) {
3867 case 0: /* State 0 - final char */
3868 if (fn[i] == '~') /* Is tilde */
3869 state = 1; /* Switch to next state */
3870 else /* Otherwise */
3871 flag = 1; /* Quit - no backup suffix. */
3873 case 1: /* State 1 - digits */
3874 if (fn[i] == '~' && fn[i-1] == '.') { /* Have suffix */
3875 return(atoi(&fn[i+1]));
3876 } else if (fn[i] >= '0' && fn[i] <= '9') { /* In number part */
3877 continue; /* Keep going */
3878 } else { /* Something else */
3879 flag = 1; /* Not a backup suffix - quit. */
3886 #endif /* COMMENT */
3889 /* Z N E W N -- Make a new name for the given file */
3892 Given the name, fn, of a file that already exists, this function builds a
3893 new name of the form "<oldname>.~<n>~", where <oldname> is argument name
3894 (fn), and <n> is a version number, one higher than any existing version
3895 number for that file, up to 99999. This format is consistent with that used
3896 by GNU EMACS. If the constructed name is too long for the system's maximum,
3897 enough characters are truncated from the end of <fn> to allow the version
3898 number to fit. If no free version numbers exist between 1 and 99999, a
3899 version number of "xxxx" is used. Returns a pointer to the new name in
3903 #define ZNEWNBL 63 /* Name buffer length */
3904 #define ZNEWNMD 3 /* Max digits for version number */
3906 #define ZNEWNBL CKMAXPATH
3910 #define MAXBUDIGITS 5
3912 static char znewbuf[ZNEWNBL+12];
3915 znewn(fn,s) char *fn, **s; {
3916 char * buf; /* Pointer to buffer for new name */
3917 char * xp, * namepart = NULL; /* Pointer to filename part */
3918 struct zfnfp * fnfp; /* znfqfp() result struct pointer */
3919 int d = 0, t, fnlen, buflen;
3920 int n, i, k, flag, state;
3921 int max = MAXNAMLEN; /* Maximum name length */
3922 char * dname = NULL;
3925 *s = NULL; /* Initialize return value */
3926 if (!fn) fn = ""; /* Check filename argument */
3929 /* If incoming file already has a backup suffix, remove it. */
3930 /* Then we'll tack a new on later, which will be the highest for this file. */
3932 if (i <= max && i > 0 && fn[i-1] == '~') {
3935 debug(F111,"znewn suffix removal",fn,i);
3936 if ((dname = (char *)malloc(i+1))) {
3937 ckstrncpy(dname,fn,i+1);
3939 for (flag = state = 0; (!flag && (i > 0)); i--) {
3941 case 0: /* State 0 - final char */
3942 if (p[i] == '~') /* Is tilde */
3943 state = 1; /* Switch to next state */
3944 else /* Otherwise */
3945 flag = 1; /* Quit - no backup suffix. */
3947 case 1: /* State 1 - digits */
3948 if (p[i] == '~' && p[i-1] == '.') { /* Have suffix */
3949 p[i-1] = NUL; /* Trim it */
3951 debug(F111,"znewn suffix removal 2",fn,i);
3952 flag = 1; /* done */
3953 } else if (p[i] >= '0' && p[i] <= '9') { /* Number part */
3954 continue; /* Keep going */
3955 } else { /* Something else */
3956 flag = 1; /* Not a backup suffix - quit. */
3963 if ((fnlen = strlen(fn)) < 1) { /* Get length */
3964 if (dname) free(dname);
3967 debug(F111,"znewn",fn,fnlen);
3969 debug(F101,"znewn max 1","",max);
3970 if (max < 14) max = 14; /* Make max reasonable for any UNIX */
3971 if (max > ZNEWNBL) max = ZNEWNBL;
3972 debug(F101,"znewn max 2","",max);
3974 if ((fnfp = zfnqfp(fn, ZNEWNBL, buf))) { /* Get fully qualified name */
3975 namepart = fnfp->fname; /* Isolate the filename */
3976 k = strlen(fn); /* Length of name part */
3977 debug(F111,"znewn namepart",namepart,k);
3979 if (dname) free(dname);
3982 buflen = fnfp->len; /* Length of fully qualified name */
3983 debug(F111,"znewn len",buf,buflen);
3985 if (k + MAXBUDIGITS + 3 < max) { /* Backup name fits - no overflow */
3986 /* Make pattern for backup names */
3987 ckstrncpy(buf+buflen,".~*~",ZNEWNBL+12-buflen);
3988 n = nzxpand(buf,ZX_FILONLY); /* Expand the pattern */
3989 debug(F111,"znewn A matches",buf,n);
3990 while (n-- > 0) { /* Find any existing name.~n~ files */
3991 xp = *mtchptr++; /* Point at matching name */
3992 t = atoi(xp+buflen+2); /* Get number */
3993 if (t > d) d = t; /* Save d = highest version number */
3995 sprintf(buf+buflen,".~%d~",d+1); /* Yes, make "name.~<d+1>~" */
3996 debug(F110,"znewn A newname",buf,0);
3997 } else { /* Backup name would be too long */
3998 int xlen; /* So we have to eat back into it */
4000 char buf2[ZNEWNBL+12];
4003 debug(F101,"znewn B delta","",delta);
4005 for (i = MAXBUDIGITS; i > 0; i--) { /* In this case the format of */
4006 ckstrncpy(buf2,buf,ZNEWNBL+12); /* the backup name depends on */
4007 xlen = buflen - i - 3 + delta; /* how many digits are in the */
4008 ckstrncpy(buf2+xlen,".~*~",ZNEWNBL+12-xlen); /* backup number */
4009 n = nzxpand(buf2,ZX_FILONLY);
4010 debug(F111,"znewn B matches",buf2,n);
4014 while (n-- > 0) { /* Find any existing name.~n~ files */
4015 xp = *mtchptr++; /* Point at matching name */
4016 t = atoi(xp+xlen+2); /* Get number */
4017 if (t > d) d = t; /* Save d = highest version number */
4019 if (d > 0) /* If the odometer turned over... */
4020 if ((d % 10) == 9) /* back up one space. */
4022 sprintf(buf2+xlen,".~%d~",d+1); /* This just fits */
4023 ckstrncpy(buf,buf2,ZNEWNBL+12); /* (we could be more clever here...) */
4024 debug(F110,"znewn B new name",buf,0);
4026 *s = buf; /* Point to new name */
4027 ck_znewn = d+1; /* Also make it available globally */
4028 if (dname) free(dname);
4032 /* Z R E N A M E -- Rename a file */
4034 Call with old and new names.
4035 If new name is the name of a directory, the 'old' file is moved to
4037 Returns 0 on success, -1 on failure.
4040 zrename(old,new) char *old, *new; {
4046 debug(F110,"zrename old",old,0);
4047 debug(F110,"zrename new",new,0);
4048 if (!*old) return(-1);
4049 if (!*new) return(-1);
4053 if (inserver && isguest)
4055 #endif /* CK_LOGIN */
4059 debug(F111,"zrename setroot",ckroot,ckrootset);
4061 if (!zinroot(old)) {
4062 debug(F110,"zrename old: setroot violation",old,0);
4065 if (!zinroot(new)) {
4066 debug(F110,"zrename new: setroot violation",new,0);
4078 if (!(p = malloc(strlen(new) + strlen(old) + 2)))
4080 strcpy(p,new); /* (safe) Directory part */
4081 if (!ISDIRSEP(*(new+x-1))) /* Separator, if needed */
4082 strcat(p,"/"); /* (safe) */
4083 zstrip(old,&q); /* Strip path part from old name */
4084 strcat(p,q); /* cat to new directory (safe) */
4086 debug(F110,"zrename dir",s,0);
4089 else debug(F110,"zrename no dir",s,0);
4093 if (inserver && (!ENABLED(en_del))) {
4094 if (zchki(s) > -1) /* Destination file exists? */
4099 x = -1; /* Return code. */
4101 /* Atomic, preferred, uses a single system call, rename(), if available. */
4103 debug(F111,"zrename rename()",old,x);
4107 /* If rename() failed or not available try link()/unlink() */
4110 if (zchko(old) > -1) { /* Requires write access to orignal */
4112 debug(F111,"zrename link()",old,x);
4113 if (x > -1) { /* Make a link with the new name. */
4115 debug(F111,"zrename unlink()",old,x);
4117 /* If link/unlink failed copy and delete */
4120 debug(F111,"zrename zcopy()",old,x);
4123 debug(F111,"zrename zdelet()",old,x);
4128 fullname[0] = '\0'; /* Clear this out for next time. */
4131 if (ckxsyslog >= SYSLG_FC && ckxlogging) {
4132 zfnqfp(old,CKMAXPATH,fullname);
4134 zfnqfp(s,CKMAXPATH,tmp2);
4136 syslog(LOG_INFO,"file[] %s: renamed to %s ok", fullname, tmp2);
4138 syslog(LOG_INFO,"file[] %s: rename to %s failed (%m)",fullname,tmp2);
4140 #endif /* CKSYSLOG */
4146 /* Z C O P Y -- Copy a single file. */
4148 Call with source and destination names.
4149 If destination is a directory, the source file is
4150 copied to that directory with its original name.
4154 -2 = source file is not a regular file.
4155 -3 = source file not found.
4156 -4 = permission denied.
4157 -5 = source and destination are the same file.
4162 zcopy(source,destination) char *source, *destination; {
4163 char *src, *dst; /* Local pointers to filenames */
4164 int x, y, rc; /* Workers */
4165 int in = -1, out = -1; /* i/o file descriptors */
4166 struct stat srcbuf; /* Source file info buffer */
4167 int perms; /* Output file permissions */
4168 char buf[1024]; /* File copying buffer */
4170 if (!source) source = "";
4171 if (!destination) destination = "";
4173 debug(F110,"zcopy src arg",source,0);
4174 debug(F110,"zcopy dst arg",destination,0);
4176 if (!*source) return(-1);
4177 if (!*destination) return(-1);
4181 if (inserver && isguest)
4183 #endif /* CK_LOGIN */
4187 debug(F111,"zcopy setroot",ckroot,ckrootset);
4189 if (!zinroot(source)) {
4190 debug(F110,"zcopy source: setroot violation",source,0);
4193 if (!zinroot(destination)) {
4194 debug(F110,"zcopy destination: setroot violation",destination,0);
4203 if (stat(src,&srcbuf) == 0) { /* Get source file info */
4204 struct stat dstbuf; /* Destination file info buffer */
4205 debug(F101,"STAT","",6);
4206 if (stat(dst,&dstbuf) == 0) {
4207 debug(F101,"STAT","",7);
4208 if (srcbuf.st_dev == dstbuf.st_dev)
4209 if (srcbuf.st_ino == dstbuf.st_ino) {
4210 debug(F100,"zcopy files identical: stat()","",0);
4214 } else { /* stat() failed... */
4215 debug(F101,"STAT","",8);
4216 debug(F111,"source file not found",src,errno);
4219 fullname[0] = '\0'; /* Get full pathnames */
4220 if (zfnqfp(source,CKMAXPATH,fullname))
4222 debug(F110,"zcopy src",src,0);
4224 if (zfnqfp(destination,CKMAXPATH,tmp2))
4226 debug(F110,"zcopy dst 1",dst,0);
4227 if (!strcmp(src,dst)) { /* Src and dst are same file? */
4228 debug(F100,"zcopy files identical: strcmp()","",0); /* This... */
4229 return(-5); /* should not happen. */
4231 if (isdir(src)) { /* Source file is a directory? */
4232 debug(F110,"zcopy source is directory",src,0);
4233 return(-2); /* Fail */
4235 if (isdir(dst)) { /* Destination is a directory? */
4236 char *q = NULL; /* Yes, add filename to it. */
4238 if (x < 1) return(-1);
4239 if (!ISDIRSEP(*(dst+x-1))) { /* Add separator if needed */
4243 debug(F111,"zcopy dst 2",dst,x);
4244 zstrip(src,&q); /* Strip path part from old name */
4245 ckstrncpy(tmp2+x,q,CKMAXPATH-x); /* Concatenate it to new name */
4247 debug(F110,"zcopy dst 3",dst,0);
4250 if (inserver && (!ENABLED(en_del))) {
4251 if (zchki(dst) > -1) /* Destination file exists? */
4256 perms = umask(0); /* Get user's umask */
4257 umask(perms); /* Put it back! */
4258 perms ^= 0777; /* Flip the bits */
4259 perms &= 0666; /* Zero execute bits from umask */
4260 perms |= (srcbuf.st_mode & 0111); /* OR in source file's execute bits */
4261 rc = -1; /* Default return code */
4262 errno = 0; /* Reset errno */
4263 in = open(src, O_RDONLY, 0); /* Open source file */
4264 debug(F111,"zcopy open source",src,in);
4265 if (in > -1) { /* If open... */
4266 /* Open destination file */
4268 out = open(dst, O_WRONLY|O_CREAT|O_TRUNC, perms);
4270 out = open(dst, O_WRONLY|O_CREAT, perms);
4271 #endif /* O_TRUNC */
4272 debug(F111,"zcopy open dest",dst,out);
4273 if (out > -1) { /* If open... */
4274 while ((x = read(in,buf,1024)) > 0) { /* Copy in 1K blocks */
4275 y = write(out,buf,x);
4276 if (y < 0) { /* On write failure */
4278 rc = -6; /* Indicate i/o error */
4282 debug(F101,"zcopy final read","",x);
4283 debug(F101,"zcopy errno","",errno);
4284 rc = (x == 0) ? 0 : -6; /* In case of read failure */
4287 if (in > -1) close(in); /* Close files */
4288 if (out > -1) close(out);
4289 if (rc == -1) { /* Set return code */
4291 case ENOENT: rc = -3; break;
4292 case EACCES: rc = -4; break;
4298 if (rc > -1 && ckxsyslog >= SYSLG_FC && ckxlogging) {
4300 syslog(LOG_INFO,"file[] %s: copy to %s failed (%m)", fullname, tmp2);
4302 syslog(LOG_INFO,"file[] %s: copy to %s ok", fullname, tmp2);
4304 #endif /* CKSYSLOG */
4311 Fills in a Kermit file attribute structure for the file which is to be sent.
4312 Returns 0 on success with the structure filled in, or -1 on failure.
4313 If any string member is null, then it should be ignored.
4314 If any numeric member is -1, then it should be ignored.
4320 #endif /* CK_GPERMS */
4324 #define S_IRUSR 0400
4325 #endif /* S_IRUSR */
4327 #define S_IXUSR 0200
4328 #endif /* S_IWUSR */
4330 #define S_IXUSR 0100
4331 #endif /* S_IXUSR */
4338 #endif /* S_IXUSR */
4339 #endif /* S_IWUSR */
4340 #endif /* S_IRUSR */
4342 static char gperms[2];
4344 #endif /* CK_GPERMS */
4346 static char lperms[24];
4349 static char xlperms[24];
4351 /* Z S E T P E R M -- Set permissions of a file */
4354 zsetperm(f,code) char * f; int code; {
4360 #endif /* CK_SCO32V4 */
4362 if (inserver && guest) {
4363 debug(F110,"zsetperm guest",f,0);
4368 debug(F111,"zsetperm error",f,errno);
4371 debug(F111,"zsetperm ok",f,mask);
4375 /* Z G P E R M -- Get permissions of a file as an octal string */
4378 zgperm(f) char *f; {
4379 extern int diractive;
4380 int x; char *s = (char *)xlperms;
4382 debug(F110,"zgperm",f,0);
4383 if (!f) return("----------");
4384 if (!*f) return("----------");
4387 debug(F111,"zgperm setroot",ckroot,ckrootset);
4388 if (ckrootset) if (!zinroot(f)) {
4389 debug(F110,"zgperm setroot violation",f,0);
4390 return("----------");
4398 #endif /* USE_LSTAT */
4400 debug(F101,"STAT","",9);
4402 return("----------");
4403 sprintf(s,"%o",buf.st_mode);
4404 debug(F110,"zgperm",s,0);
4408 /* Like zgperm() but returns permissions in "ls -l" string format */
4410 static char xsperms[24];
4413 ziperm(f) char * f; {
4414 extern int diractive;
4415 int x; char *s = (char *)xsperms;
4417 unsigned int perms = 0;
4419 debug(F110,"ziperm",f,0);
4421 if (!f) return(NULL);
4422 if (!*f) return(NULL);
4424 if (diractive && zgfs_mode != 0) {
4425 perms = zgfs_mode; /* zgetfs() already got them */
4431 #endif /* USE_LSTAT */
4433 debug(F101,"STAT","",10);
4435 return("----------");
4436 perms = buf.st_mode;
4438 switch (perms & S_IFMT) {
4442 case S_IFCHR: /* Character special */
4445 case S_IFBLK: /* Block special */
4448 case S_IFREG: /* Regular */
4452 case S_IFLNK: /* Symbolic link */
4455 #endif /* S_IFLNK */
4457 case S_IFSOCK: /* Socket */
4460 #endif /* S_IFSOCK */
4464 case S_IFIFO: /* FIFO */
4467 #endif /* COHERENT */
4469 #endif /* S_IFIFO */
4471 case S_IFWHT: /* Whiteout */
4474 #endif /* S_IFWHT */
4475 default: /* Unknown */
4479 if (perms & S_IRUSR) /* Owner's permissions */
4483 if (perms & S_IWUSR)
4487 switch (perms & (S_IXUSR | S_ISUID)) {
4497 case S_IXUSR | S_ISUID:
4501 if (perms & S_IRGRP) /* Group permissions */
4505 if (perms & S_IWGRP)
4509 switch (perms & (S_IXGRP | S_ISGID)) {
4519 case S_IXGRP | S_ISGID:
4523 if (perms & S_IROTH) /* World permissions */
4527 if (perms & S_IWOTH)
4535 perms & (S_IXOTH | S_ISVTX)
4548 case S_IXOTH | S_ISVTX:
4554 debug(F110,"ziperm",xsperms,0);
4555 return((char *)xsperms);
4561 zgperm(f) char *f; {
4562 return("----------");
4565 ziperms(f) char *f; {
4566 return("----------");
4568 #endif /* CK_PERMS */
4571 zsattr(xx) struct zattr *xx; {
4575 k = iflen % 1024; /* File length in K */
4577 xx->lengthk = (iflen / 1024) + k;
4578 xx->type.len = 0; /* File type can't be filled in here */
4581 xx->date.val = zfcdat(nambuf); /* File creation date */
4582 xx->date.len = (int)strlen(xx->date.val);
4587 xx->creator.len = 0; /* File creator */
4588 xx->creator.val = "";
4589 xx->account.len = 0; /* File account */
4590 xx->account.val = "";
4591 xx->area.len = 0; /* File area */
4593 xx->password.len = 0; /* Area password */
4594 xx->password.val = "";
4595 xx->blksize = -1L; /* File blocksize */
4596 xx->xaccess.len = 0; /* File access */
4597 xx->xaccess.val = "";
4598 xx->encoding.len = 0; /* Transfer syntax */
4599 xx->encoding.val = 0;
4600 xx->disp.len = 0; /* Disposition upon arrival */
4602 xx->lprotect.len = 0; /* Local protection */
4603 xx->lprotect.val = "";
4604 xx->gprotect.len = 0; /* Generic protection */
4605 xx->gprotect.val = "";
4607 if (*nambuf) x = stat(nambuf,&buf);
4608 debug(F101,"STAT","",11);
4610 debug(F111,"zsattr buf.st_mode & 0777",nambuf,buf.st_mode & 0777);
4611 /* UNIX filemode as an octal string without filetype bits */
4612 sprintf(lperms,"%o",buf.st_mode & 0777);
4613 xx->lprotect.len = (int)strlen(lperms);
4614 xx->lprotect.val = (char *)lperms;
4617 /* Generic permissions only if we have stat.h symbols defined */
4618 if (buf.st_mode & S_IRUSR) x |= 1; /* Read */
4619 if (buf.st_mode & S_IWUSR) x |= (2+16); /* Write and Delete */
4620 if (buf.st_mode & S_IXUSR) x |= 4; /* Execute */
4621 gperms[0] = tochar(x);
4623 xx->gprotect.len = 1;
4624 xx->gprotect.val = (char *)gperms;
4625 #endif /* CK_GPERMS */
4627 debug(F111,"zsattr lperms",xx->lprotect.val,xx->lprotect.len);
4628 debug(F111,"zsattr gperms",xx->gprotect.val,xx->gprotect.len);
4629 xx->systemid.val = "U1"; /* U1 = UNIX */
4630 xx->systemid.len = 2; /* System ID */
4631 xx->recfm.len = 0; /* Record format */
4633 xx->sysparam.len = 0; /* System-dependent parameters */
4634 xx->sysparam.val = "";
4635 xx->length = iflen; /* Length */
4639 /* Z F C D A T -- Get file creation date */
4641 Call with pointer to filename.
4642 On success, returns pointer to modification date in yyyymmdd hh:mm:ss format.
4643 On failure, returns pointer to null string.
4645 static char datbuf[40];
4649 zdtstr(time_t timearg)
4651 zdtstr(timearg) time_t timearg;
4652 #endif /* CK_ANSIC */
4657 struct tm * time_stamp;
4658 struct tm * localtime();
4661 debug(F101,"zdtstr timearg","",timearg);
4664 time_stamp = localtime(&(timearg));
4666 debug(F100,"localtime returns null","",0);
4670 We assume that tm_year is ALWAYS years since 1900.
4671 Any platform where this is not the case will have problems
4674 yy = time_stamp->tm_year; /* Year - 1900 */
4675 debug(F101,"zdtstr tm_year","",time_stamp->tm_year);
4677 debug(F101,"zstrdt YEAR-2000 ALERT 1: localtime year","",yy);
4680 debug(F101,"zdatstr year","",yy);
4682 if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11)
4684 if (time_stamp->tm_mday < 0 || time_stamp->tm_mday > 31)
4686 if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 23)
4688 if (time_stamp->tm_min < 0 || time_stamp->tm_min > 59)
4690 ss = time_stamp->tm_sec; /* Seconds */
4691 if (ss < 0 || ss > 59) /* Some systems give a BIG number */
4695 /* For some reason, 2.1x BSD sprintf gets the last field wrong. */
4696 "%04d%02d%02d %02d:%02d:00",
4698 "%04d%02d%02d %02d:%02d:%02d",
4701 time_stamp->tm_mon + 1,
4702 time_stamp->tm_mday,
4703 time_stamp->tm_hour,
4709 yy = (int)strlen(datbuf);
4710 debug(F111,"zdatstr",datbuf,yy);
4711 if (yy > 17) datbuf[17] = '\0';
4713 #endif /* TIMESTAMP */
4717 zfcdat(name) char *name; {
4720 extern int diractive;
4732 debug(F111,"zfcdat setroot",ckroot,ckrootset);
4733 if (ckrootset) if (!zinroot(name)) {
4734 debug(F110,"zfcdat setroot violation",name,0);
4741 s = tilde_expand(s);
4749 debug(F111,"zfcdat",s,diractive);
4751 if (diractive && zgfs_mtime) {
4756 x = lstat(s,&buffer);
4757 debug(F101,"STAT","",12);
4758 debug(F101,"zfcdat lstat","",x);
4760 #endif /* USE_LSTAT */
4761 x = stat(s,&buffer);
4762 debug(F101,"STAT","",13);
4763 debug(F101,"zfcdat stat","",x);
4766 #endif /* USE_LSTAT */
4769 debug(F111,"zfcdat stat failed",s,errno);
4771 debug(F111,"zfcdat lstat failed",s,errno);
4772 #endif /* USE_LSTAT */
4775 debug(F101,"zfcdat buffer.st_mtime","",buffer.st_mtime);
4776 mtime = buffer.st_mtime;
4778 return(zdtstr(mtime));
4781 #endif /* TIMESTAMP */
4786 /* Z S T R D T -- Converts local date string to internal representation */
4788 In our case (UNIX) this is seconds since midnite 1 Jan 1970 UTC,
4789 suitable for comparison with UNIX file dates. As far as I know, there is
4790 no library or system call -- at least nothing reasonably portable -- to
4791 convert local time to UTC.
4794 zstrdt(date,len) char * date; int len; {
4797 SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime().
4798 ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in
4799 dependence on the XPG4 supplement presence. So always use
4800 what the system header file supplies in ODT 3.0...
4804 extern void ftime(); /* extern void ftime(struct timeb *) */
4805 #endif /* _SCO_DS */
4810 #endif /* M_XENIX */
4812 extern struct tm * localtime();
4814 /* And this should have been declared always through a header file */
4826 int i, n, isleapyear;
4827 /* J F M A M J J A S O N D */
4828 /* 31 28 31 30 31 30 31 31 30 31 30 31 */
4830 int monthdays [13] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 };
4832 struct tm *time_stamp;
4835 struct timeval tp[2];
4836 long xtimezone = 0L;
4840 time_t timep[2]; /* New access and modificaton time */
4843 long timezone; /* In case timezone not defined in .h file */
4852 #endif /* SYSUTIMEH */
4858 static struct timeb tbp;
4865 debug(F111,"zstrdt",date,len);
4870 || (date[11] != ':')
4871 || (date[14] != ':') ) {
4872 debug(F111,"Bad creation date ",date,len);
4875 debug(F111,"zstrdt date check 1",date,len);
4876 for(i = 0; i < 8; i++) {
4877 if (!isdigit(date[i])) {
4878 debug(F111,"Bad creation date ",date,len);
4882 debug(F111,"zstrdt date check 2",date,len);
4885 for (; i < 16; i += 3) {
4886 if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) {
4887 debug(F111,"Bad creation date ",date,len);
4891 debug(F111,"zstrdt date check 3",date,len);
4894 #ifdef COMMENT /* was BSD44 */
4896 man gettimeofday on BSDI 3.1 says:
4897 "The timezone field is no longer used; timezone information is stored out-
4898 side the kernel. See ctime(3) for more information." So this chunk of
4899 code is effectively a no-op, at least in BSDI 3.x.
4903 struct timezone tzp;
4904 x = gettimeofday(NULL, &tzp);
4905 debug(F101,"zstrdt BSD44 gettimeofday","",x);
4907 xtimezone = tzp.tz_minuteswest * 60L;
4910 debug(F101,"zstrdt BSD44 timezone","",xtimezone);
4914 debug(F100,"zstrdt BSD calling ftime","",0);
4916 debug(F100,"zstrdt BSD back from ftime","",0);
4917 timezone = tbp.timezone * 60L;
4918 debug(F101,"zstrdt BSD timezone","",timezone);
4921 tzset(); /* Set timezone */
4924 if ((tz = getenv("TZ")) == NULL)
4925 timezone = 0; /* UTC/GMT */
4927 timezone = atoi(&tz[3]); /* Set 'timezone'. */
4930 #endif /* SVORPOSIX */
4932 #endif /* COMMENT (was BSD44) */
4934 debug(F100,"zstrdt so far so good","",0);
4937 for (i = 0; i < 4; i++) /* Fix the year */
4941 debug(F111,"zstrdt year",s,n);
4943 debug(F100,"zstrdt fails - year","",n);
4947 /* Previous year's leap days. This won't work after year 2100. */
4949 isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
4950 days = (long) (n - 1970) * 365;
4951 days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
4955 for (i = 4; i < 16; i += 2) {
4960 case 4: /* MM: month */
4961 if ((n < 1 ) || ( n > 12)) {
4962 debug(F111,"zstrdt 4 bad date ",date,len);
4965 days += monthdays [n];
4966 if (isleapyear && n > 2)
4970 case 6: /* DD: day */
4971 if ((n < 1 ) || ( n > 31)) {
4972 debug(F111,"zstrdt 6 bad date ",date,len);
4975 tmx = (days + n - 1) * 24L * 60L * 60L;
4976 i++; /* Skip the space */
4979 case 9: /* hh: hour */
4980 if ((n < 0 ) || ( n > 23)) {
4981 debug(F111,"zstrdt 9 bad date ",date,len);
4984 tmx += n * 60L * 60L;
4985 i++; /* Skip the colon */
4988 case 12: /* mm: minute */
4989 if ((n < 0 ) || ( n > 59)) {
4990 debug(F111,"zstrdt 12 bad date ",date,len);
4993 #ifdef COMMENT /* (was BSD44) */ /* Correct for time zone */
4995 debug(F101,"zstrdt BSD44 tmx","",tmx);
5000 #ifndef CONVEX9 /* Don't yet know how to do this here */
5002 tmx += (long) timezone;
5006 extern time_t tzoffset;
5013 #endif /* NOTIMEZONE */
5017 #endif /* CONVEX9 */
5019 #endif /* COMMENT (was BSD44) */
5021 i++; /* Skip the colon */
5024 case 15: /* ss: second */
5025 if ((n < 0 ) || ( n > 59)) {
5026 debug(F111,"zstrdt 15 bad date ",date,len);
5031 time_stamp = localtime(&tmx);
5032 debug(F101,"zstrdt tmx 1","",tmx);
5036 /* Why was this here? */
5037 time_stamp = localtime(&tmx);
5038 debug(F101,"zstrdt tmx 2","",tmx);
5039 #endif /* COMMENT */
5041 { /* New to 7.0 - Works in at at least BSDI 3.1 and FreeBSD 2.2.7 */
5043 zz = time_stamp->tm_gmtoff; /* Seconds away from Zero Meridian */
5044 debug(F101,"zstrdt BSD44 tm_gmtoff","",zz);
5046 debug(F101,"zstrdt BSD44 tmx 3 (GMT)","",tmx);
5050 Daylight Savings Time adjustment.
5051 Do this everywhere BUT in BSD44 because in BSD44,
5052 tm_gmtoff also includes the DST adjustment.
5054 if (time_stamp->tm_isdst) {
5056 debug(F101,"zstrdt tmx 3 (DST)","",tmx);
5059 n = time_stamp->tm_year;
5069 /* Z L O C A L T I M E -- GMT/UTC time string to local time string */
5072 Call with: "yyyymmdd hh:mm:ss" GMT/UTC date-time.
5073 Returns: "yyyymmdd hh:mm:ss" local date-time on success, NULL on failure.
5075 static char zltimbuf[64];
5078 zlocaltime(gmtstring) char * gmtstring; {
5081 SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime().
5082 ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in
5083 dependence on the XPG4 supplement presence. So always use
5084 what the system header file supplies in ODT 3.0...
5088 extern void ftime(); /* extern void ftime(struct timeb *) */
5089 #endif /* _SCO_DS */
5094 #endif /* M_XENIX */
5096 extern struct tm * localtime();
5098 /* And this should have been declared always through a header file */
5110 int i, n, x, isleapyear;
5111 /* J F M A M J J A S O N D */
5112 /* 31 28 31 30 31 30 31 31 30 31 30 31 */
5114 int monthdays [13] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 };
5116 struct tm *time_stamp;
5119 struct timeval tp[2];
5123 time_t timep[2]; /* New access and modificaton time */
5133 #endif /* SYSUTIMEH */
5138 static struct timeb tbp;
5141 char * date = gmtstring;
5145 debug(F111,"zlocaltime",date,len);
5150 || (date[11] != ':')
5151 || (date[14] != ':') ) {
5152 debug(F111,"Bad creation date ",date,len);
5155 debug(F111,"zlocaltime date check 1",date,len);
5156 for(i = 0; i < 8; i++) {
5157 if (!isdigit(date[i])) {
5158 debug(F111,"Bad creation date ",date,len);
5162 debug(F111,"zlocaltime date check 2",date,len);
5165 for (; i < 16; i += 3) {
5166 if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) {
5167 debug(F111,"Bad creation date ",date,len);
5171 debug(F111,"zlocaltime date check 3",date,len);
5173 debug(F100,"zlocaltime so far so good","",0);
5176 for (i = 0; i < 4; i++) /* Fix the year */
5180 debug(F111,"zlocaltime year",s,n);
5182 debug(F100,"zlocaltime fails - year","",n);
5186 /* Previous year's leap days. This won't work after year 2100. */
5188 isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
5189 days = (long) (n - 1970) * 365;
5190 days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
5194 for (i = 4; i < 16; i += 2) {
5199 case 4: /* MM: month */
5200 if ((n < 1 ) || ( n > 12)) {
5201 debug(F111,"zlocaltime 4 bad date ",date,len);
5204 days += monthdays [n];
5205 if (isleapyear && n > 2)
5209 case 6: /* DD: day */
5210 if ((n < 1 ) || ( n > 31)) {
5211 debug(F111,"zlocaltime 6 bad date ",date,len);
5214 tmx = (days + n - 1) * 24L * 60L * 60L;
5215 i++; /* Skip the space */
5218 case 9: /* hh: hour */
5219 if ((n < 0 ) || ( n > 23)) {
5220 debug(F111,"zlocaltime 9 bad date ",date,len);
5223 tmx += n * 60L * 60L;
5224 i++; /* Skip the colon */
5227 case 12: /* mm: minute */
5228 if ((n < 0 ) || ( n > 59)) {
5229 debug(F111,"zlocaltime 12 bad date ",date,len);
5233 i++; /* Skip the colon */
5236 case 15: /* ss: second */
5237 if ((n < 0 ) || ( n > 59)) {
5238 debug(F111,"zlocaltime 15 bad date ",date,len);
5245 At this point tmx is the time_t representation of the argument date-time
5246 string without any timezone or DST adjustments. Therefore it should be
5247 the same as the time_t representation of the GMT/UTC time. Now we should
5248 be able to feed it to localtime() and have it converted to a struct tm
5249 representing the local time equivalent of the given UTC time.
5251 time_stamp = localtime(&tmx);
5256 /* Now we simply reformat the struct tm to a string */
5258 x = time_stamp->tm_year;
5259 if (time_stamp->tm_year < 70 || time_stamp->tm_year > 8099)
5261 if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11)
5263 if (time_stamp->tm_mday < 1 || time_stamp->tm_mday > 31)
5265 if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 24)
5267 if (time_stamp->tm_min < 0 || time_stamp->tm_min > 60)
5269 if (time_stamp->tm_sec < 0 || time_stamp->tm_sec > 60)
5271 sprintf(zltimbuf,"%04d%02d%02d %02d:%02d:%02d",
5272 time_stamp->tm_year + 1900,
5273 time_stamp->tm_mon + 1,
5274 time_stamp->tm_mday,
5275 time_stamp->tm_hour,
5279 return((char *)zltimbuf);
5281 #endif /* ZLOCALTIME */
5282 #endif /* NOTIMESTAMP */
5284 /* Z S T I M E -- Set modification date/time+permissions for incoming file */
5287 f = pointer to name of existing file.
5288 yy = pointer to a Kermit file attribute structure in which yy->date.val
5289 is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00.
5290 yy->lprotect.val & yy->gprotect.val are permission/protection values.
5291 x = is a function code: 0 means to set the file's attributes as given.
5292 1 means compare the date in struct yy with the file creation date.
5294 -1 on any kind of error.
5295 0 if x is 0 and the attributes were set successfully.
5296 0 if x is 1 and date from attribute structure <= file creation date.
5297 1 if x is 1 and date from attribute structure > file creation date.
5301 char *f; struct zattr *yy; int x;
5303 int r = -1; /* Return code */
5306 #endif /* CK_PERMS */
5309 /* It is ifdef'd TIMESTAMP because it might not work on V7. bk@kullmar.se. */
5313 extern int utimes();
5320 /* At least, the declarations for int functions are not needed anyway */
5323 struct timeval tp[2];
5328 time_t timep[2]; /* New access and modificaton time */
5331 long timezone; /* In case not defined in .h file */
5340 #endif /* SYSUTIMEH */
5347 if (!*f) return(-1);
5348 if (!yy) return(-1);
5351 debug(F111,"zstime setroot",ckroot,ckrootset);
5352 if (ckrootset) if (!zinroot(f)) {
5353 debug(F110,"zstime setroot violation",f,0);
5358 if (yy->date.len == 0) { /* No date in struct */
5359 if (yy->lprotect.len != 0) { /* So go do permissions */
5362 debug(F100,"zstime: nothing to do","",0);
5366 if ((tm = zstrdt(yy->date.val,yy->date.len)) < 0) {
5367 debug(F101,"zstime: zstrdt fails","",0);
5370 debug(F101,"zstime: tm","",tm);
5371 debug(F111,"zstime: A-pkt date ok ",yy->date.val,yy->date.len);
5373 if (stat(f,&sb)) { /* Get the time for the file */
5374 debug(F101,"STAT","",14);
5375 debug(F111,"zstime: Can't stat file:",f,errno);
5378 debug(F101,"STAT","",15);
5384 int i, x = 0, xx, flag = 0;
5389 debug(F111,"zstime lperms",yy->lprotect.val,yy->lprotect.len);
5390 debug(F111,"zstime gperms",yy->gprotect.val,yy->gprotect.len);
5391 debug(F110,"zstime system id",yy->systemid.val,0);
5392 sprintf(obuf,"%o",sb.st_mode);
5393 debug(F110,"zstime file perms before",obuf,0);
5398 debug(F101,"zstime isguest","",isguest);
5399 debug(F101,"zstime ckxperms","",ckxperms);
5402 /* Clear owner permissions */
5403 sb.st_mode &= (unsigned) 0177077; /* (16 bits) */
5405 /* Set permissions from ckxperms variable */
5406 sb.st_mode = ckxperms;
5407 #endif /* COMMENT */
5408 debug(F101,"zstime isguest sb.st_mode","",sb.st_mode);
5410 /* We already set them in zopeno() */
5412 #endif /* COMMENT */
5415 #endif /* CK_LOGIN */
5416 if ((yy->lprotect.len > 0 && /* Have local-format permissions */
5417 yy->systemid.len > 0 && /* from A-packet... */
5419 !strcmp(yy->systemid.val,"U1") /* AND you are same as me */
5423 ) || (yy->lprotect.len < 0) /* OR by inheritance from old file */
5426 s = yy->lprotect.val; /* UNIX filemode */
5427 xx = yy->lprotect.len;
5428 if (xx < 0) /* len < 0 means inheritance */
5430 for (i = 0; i < xx; i++) { /* Decode octal string */
5431 if (*s <= '7' && *s >= '0') {
5432 x = 8 * x + (int)(*s) - '0';
5440 sprintf(obuf,"%o",x);
5441 debug(F110,"zstime octal lperm",obuf,0);
5443 } else if (!flag && yy->gprotect.len > 0) {
5449 #endif /* CK_SCO32V4 */
5450 mask = umask(0); /* Get umask */
5451 debug(F101,"zstime mask 1","",mask);
5452 umask(mask); /* Put it back */
5453 mask ^= 0777; /* Flip the bits */
5454 debug(F101,"zstime mask 2","",mask);
5455 g = xunchar(*(yy->gprotect.val)); /* Decode generic protection */
5456 debug(F101,"zstime gprotect","",g);
5458 debug(F100,"zstime S_IRUSR","",0);
5459 if (g & 1) x |= S_IRUSR; /* Read permission */
5461 #endif /* S_IRUSR */
5463 debug(F100,"zstime S_IWUSR","",0);
5464 if (g & 2) x |= S_IWUSR; /* Write permission */
5465 if (g & 16) x |= S_IWUSR; /* Delete permission */
5467 #endif /* S_IWUSR */
5469 debug(F100,"zstime S_IXUSR","",0);
5470 if (g & 4) /* Has execute permission bit */
5472 else /* Doesn't have it */
5473 mask &= 0666; /* so also clear it out of mask */
5475 #endif /* S_IXUSR */
5476 debug(F101,"zstime mask x","",x);
5478 debug(F101,"zstime mask x|mask","",x);
5480 debug(F101,"zstime flag","",flag);
5483 debug(F101,"zstime S_IFMT x","",x);
5484 sb.st_mode = (sb.st_mode & S_IFMT) | x;
5488 debug(F101,"zstime _IFMT x","",x);
5489 sb.st_mode = (sb.st_mode & _IFMT) | x;
5495 sprintf(obuf,"%04o",sb.st_mode);
5496 debug(F111,"zstime file perms after",obuf,setperms);
5499 #endif /* CK_PERMS */
5501 debug(F101,"zstime: sb.st_atime","",sb.st_atime);
5504 tp[0].tv_sec = sb.st_atime; /* Access time first */
5505 tp[1].tv_sec = tm; /* Update time second */
5506 debug(F100,"zstime: BSD44 modtime","",0);
5509 tp.timep[0] = tm; /* Set modif. time to creation date */
5510 tp.timep[1] = sb.st_atime; /* Don't change the access time */
5511 debug(F100,"zstime: V7 modtime","",0);
5514 tp.modtime = tm; /* Set modif. time to creation date */
5515 tp.actime = sb.st_atime; /* Don't change the access time */
5516 debug(F100,"zstime: SYSUTIMEH modtime","",0);
5518 tp.mtime = tm; /* Set modif. time to creation date */
5519 tp.atime = sb.st_atime; /* Don't change the access time */
5520 debug(F100,"zstime: default modtime","",0);
5521 #endif /* SYSUTIMEH */
5525 switch (x) { /* Execute desired function */
5526 case 0: /* Set the creation date of the file */
5527 #ifdef CK_PERMS /* And permissions */
5529 NOTE: If we are inheriting permissions from a previous file, and the
5530 previous file was a directory, this would turn the new file into a directory
5531 too, but it's not, so we try to unset the right bit. Luckily, this code
5532 will probably never be executed since the upper level modules do not allow
5533 reception of a file that has the same name as a directory.
5535 NOTE 2: We change the permissions *before* we change the modification time,
5536 otherwise changing the permissions would set the mod time to the present
5541 debug(F101,"zstime setperms","",setperms);
5542 if (S_ISDIR(sb.st_mode)) {
5543 debug(F101,"zstime DIRECTORY bit on","",sb.st_mode);
5544 sb.st_mode ^= 0040000;
5545 debug(F101,"zstime DIRECTORY bit off","",sb.st_mode);
5548 x = chmod(f,sb.st_mode);
5549 debug(F101,"zstime chmod","",x);
5552 if (x < 0) return(-1);
5553 #endif /* CK_PERMS */
5555 if (!setdate) /* We don't have a date */
5556 return(0); /* so skip the following... */
5564 ) { /* Fix modification time */
5565 debug(F111,"zstime 0: can't set modtime for file",f,errno);
5568 /* Including the modtime here is not portable */
5569 debug(F110,"zstime 0: modtime set for file",f,0);
5574 case 1: /* Compare the dates */
5576 This was st_atime, which was wrong. We want the file-data modification
5579 debug(F111,"zstime 1: compare",f,sb.st_mtime);
5580 debug(F111,"zstime 1: compare","packet",tm);
5582 r = (sb.st_mtime < tm) ? 0 : 1;
5585 default: /* Error */
5588 #endif /* TIMESTAMP */
5592 /* Find initialization file. */
5597 /* nothing here for Unix. This function added for benefit of VMS Kermit. */
5600 #endif /* NOTUSED */
5603 /* Historical -- not used in Unix any more (2001-11-03) */
5606 zmail(p,f) char *p; char *f; { /* Send file f as mail to address p */
5608 Returns 0 on success
5609 2 if mail delivered but temp file can't be deleted
5610 -2 if mail can't be delivered
5611 -1 on file access error
5612 The UNIX version always returns 0 because it can't get a good return
5620 #endif /* CK_LOGIN */
5623 if (!*f) return(-1);
5626 debug(F111,"zmail setroot",ckroot,ckrootset);
5627 if (ckrootset) if (!zinroot(f)) {
5628 debug(F110,"zmail setroot violation",f,0);
5634 /* The idea is to use /usr/ucb/mail, rather than regular mail, so that */
5635 /* a subject line can be included with -s. Since we can't depend on the */
5636 /* user's path, we use the convention that /usr/ucb/Mail = /usr/ucb/mail */
5637 /* and even if Mail has been moved to somewhere else, this should still */
5638 /* find it... The search could be made more reliable by actually using */
5639 /* access() to see if /usr/ucb/Mail exists. */
5642 n = n + n + 15 + (int)strlen(p);
5648 sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
5650 sprintf(zmbuf,"Mail -s %c%s%c %s < %s", '"', f, '"', p, f);
5651 #endif /* DGUX540 */
5656 sprintf(zmbuf,"mail %s < %s", p, f);
5658 sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
5667 #endif /* NOFRILLS */
5672 zprint(p,f) char *p; char *f; { /* Print file f with options p */
5673 extern char * printername; /* From ckuus3.c */
5674 extern int printpipe;
5680 #endif /* CK_LOGIN */
5683 if (!*f) return(-1);
5686 debug(F111,"zprint setroot",ckroot,ckrootset);
5687 if (ckrootset) if (!zinroot(f)) {
5688 debug(F110,"zprint setroot violation",f,0);
5693 debug(F110,"zprint file",f,0);
5694 debug(F110,"zprint flags",p,0);
5695 debug(F110,"zprint printername",printername,0);
5696 debug(F101,"zprint printpipe","",printpipe);
5700 Note use of standard input redirection. In some systems, lp[r] runs
5701 setuid to lp (or ...?), so if user has sent a file into a directory
5702 that lp does not have read access to, it can't be printed unless it is
5703 fed to lp[r] as standard input.
5705 if (printpipe && printername) {
5706 n = 8 + (int)strlen(f) + (int)strlen(printername);
5709 sprintf(zmbuf,"cat %s | %s", f, printername);
5710 } else if (printername) {
5711 n = 8 + (int)strlen(f) + (int)strlen(printername);
5714 sprintf(zmbuf,"cat %s >> %s", f, printername);
5716 n = 4 + (int)strlen(PRINTCMD) + (int)strlen(p) + (int)strlen(f);
5719 sprintf(zmbuf,"%s %s < %s", PRINTCMD, p, f);
5721 debug(F110,"zprint command",zmbuf,0);
5723 #else /* Not UNIX */
5728 #endif /* NOFRILLS */
5730 /* Wildcard expansion functions... */
5732 static char scratch[MAXPATH+4]; /* Used by both methods */
5734 static int oldmtchs = 0; /* Let shell (ls) expand them. */
5736 static char *lscmd = "/bin/ls -d"; /* Command to use. */
5738 static char *lscmd = "echo"; /* Command to use. */
5739 #endif /* COMMENT */
5743 shxpand(pat,namlst,len) char *pat, *namlst[]; int len; {
5744 char *fgbuf = NULL; /* Buffer for forming ls command */
5745 char *p, *q; /* Workers */
5747 int i, x, retcode, itsadir;
5750 x = (int)strlen(pat) + (int)strlen(lscmd) + 3; /* Length of ls command */
5751 for (i = 0; i < oldmtchs; i++) { /* Free previous file list */
5752 if (namlst[i] ) { /* If memory is allocated */
5753 free(namlst[i]); /* Free the memory */
5754 namlst[i] = NULL ; /* Remember no memory is allocated */
5757 oldmtchs = 0 ; /* Remember there are no matches */
5758 fgbuf = malloc(x); /* Get buffer for command */
5759 if (!fgbuf) return(-1); /* Fail if cannot */
5760 ckmakmsg(fgbuf,x,lscmd," ",pat,NULL); /* Form the command */
5761 zxcmd(ZIFILE,fgbuf); /* Start the command */
5762 i = 0; /* File counter */
5763 p = scratch; /* Point to scratch area */
5764 retcode = -1; /* Assume failure */
5765 while ((x = zminchar()) != -1) { /* Read characters from command */
5767 if (c == ' ' || c == '\n') { /* Got newline or space? */
5768 *p = '\0'; /* Yes, terminate string */
5769 p = scratch; /* Point back to beginning */
5770 if (zchki(p) == -1) /* Does file exist? */
5771 continue; /* No, continue */
5772 itsadir = isdir(p); /* Yes, is it a directory? */
5773 if (xdironly && !itsadir) /* Want only dirs but this isn't */
5774 continue; /* so skip. */
5775 if (xfilonly && itsadir) /* It's a dir but want only files */
5776 continue; /* so skip. */
5777 x = (int)strlen(p); /* Keep - get length of name */
5778 q = malloc(x+1); /* Allocate space for it */
5779 if (!q) goto shxfin; /* Fail if space can't be obtained */
5780 strcpy(q,scratch); /* (safe) Copy name to space */
5781 namlst[i++] = q; /* Copy pointer to name into array */
5782 if (i >= len) goto shxfin; /* Fail if too many */
5783 } else { /* Regular character */
5784 *p++ = c; /* Copy it into scratch area */
5787 retcode = i; /* Return number of matching files */
5788 shxfin: /* Common exit point */
5789 free(fgbuf); /* Free command buffer */
5791 zclosf(ZIFILE); /* Delete the command fork. */
5792 oldmtchs = i; /* Remember how many files */
5798 Directory-reading functions for UNIX originally written for C-Kermit 4.0
5799 by Jeff Damens, CUCCA, 1984.
5801 static char * xpat = NULL; /* Global copy of fgen() pattern */
5802 static char * xpatlast = NULL; /* Rightmost segment of pattern*/
5803 static int xpatslash = 0; /* Slash count in pattern */
5804 static int xpatwild = 0; /* Original pattern is wild */
5805 static int xleafwild = 0; /* Last segment of pattern is wild */
5806 static int xpatabsolute = 0;
5813 /* S P L I T P A T H */
5816 Splits the slash-separated portions of the argument string into
5817 a list of path structures. Returns the head of the list. The
5818 structures are allocated by malloc, so they must be freed.
5819 Splitpath is used internally by the filename generator.
5825 A linked list of the slash-separated segments of the input.
5827 static struct path *
5828 splitpath(p) char *p; {
5829 struct path *head,*cur,*prv;
5832 debug(F111,"splitpath",p,xrecursive);
5835 if (!p) return(NULL);
5836 if (!*p) return(NULL);
5838 if (!strcmp(p,"**")) { /* Fix this */
5841 if (ISDIRSEP(*p)) p++; /* Skip leading slash if any */
5843 /* Make linked list of path segments from pattern */
5846 cur = (struct path *) malloc(sizeof (struct path));
5847 /* debug(F101,"splitpath malloc","",cur); */
5849 debug(F100,"splitpath malloc failure","",0);
5851 return((struct path *)NULL);
5854 if (head == NULL) /* First, make list head */
5856 else /* Not first, link into chain */
5858 prv = cur; /* Link from previous to this one */
5861 /* treat backslash as "../" */
5862 if (bslash && *p == bslash) {
5863 strcpy(cur->npart, ".."); /* safe */
5866 for (i=0; i < MAXNAMLEN && *p && *p != '/' && *p != bslash; i++)
5867 cur -> npart[i] = *p++;
5868 cur -> npart[i] = '\0'; /* end this segment */
5870 while (*p && *p != '/' && *p != bslash)
5875 /* General case (UNIX) */
5876 for (i = 0; i < MAXNAMLEN && !ISDIRSEP(*p) && *p != '\0'; i++) {
5877 cur -> npart[i] = *p++;
5880 cur -> npart[i] = '\0'; /* End this path segment */
5882 while (!ISDIRSEP(*p) && *p != '\0') p++;
5889 makestr(&xpatlast,prv -> npart);
5890 debug(F110,"splitpath xpatlast",xpatlast,0);
5893 /* Show original path list */
5895 for (i = 0, cur = head; cur; i++) {
5896 debug(F111,"SPLITPATH",cur -> npart, i);
5904 /* F G E N -- Generate File List */
5907 File name generator. It is passed a string, possibly containing wildcards,
5908 and an array of character pointers. It finds all the matching filenames and
5909 stores pointers to them in the array. The returned strings are allocated
5910 from a static buffer local to this module (so the caller doesn't have to
5911 worry about deallocating them); this means that successive calls to fgen
5912 will wipe out the results of previous calls.
5915 A wildcard string, an array to write names to, the length of the array.
5918 The number of matches.
5919 The array is filled with filenames that matched the pattern.
5920 If there wasn't enough room in the array, -1 is returned.
5922 Originally by: Jeff Damens, CUCCA, 1984. Many changes since then.
5925 fgen(pat,resarry,len) char *pat,*resarry[]; int len; {
5932 int tilde = 0, bquote = 0;
5934 if ((namechars = getenv("NAMECHARS")) != NULL) {
5935 if (ckstrchr(namechars, '~' ) != NULL) tilde = '~';
5936 if (ckstrchr(namechars, '\\') != NULL) bslash = '\\';
5937 if (ckstrchr(namechars, '`' ) != NULL) bquote = '`';
5939 tilde = '~'; bslash = '\\'; bquote = '`';
5943 /* copy "`node_data", etc. anchors */
5944 if (bquote && *pat == bquote)
5945 while (*pat && *pat != '/' && *pat != bslash)
5947 else if (tilde && *pat == tilde)
5951 if (sptr == scratch) {
5952 strcpy(scratch,"./"); /* safe */
5955 if (!(head = splitpath(pat))) return(-1);
5957 #else /* not aegis */
5959 debug(F111,"fgen pat",pat,len);
5960 debug(F110,"fgen current directory",zgtdir(),0);
5961 debug(F101,"fgen stathack","",stathack);
5968 if (!(head = splitpath(pat))) /* Make the path segment list */
5974 if (strncmp(pat,"./",2) && strncmp(pat,"../",3)) {
5975 #endif /* COMMENT */
5976 if (!ISDIRSEP(*pat)) /* If name is not absolute */
5977 *sptr++ = '.'; /* put "./" in front. */
5981 #endif /* COMMENT */
5985 makestr(&xpat,pat); /* Save copy of original pattern */
5986 debug(F110,"fgen scratch",scratch,0);
5988 for (n = 0, s = xpat; *s; s++) /* How many slashes in the pattern */
5989 if (*s == DIRSEP) /* since these are fences for */
5990 n++; /* pattern matching */
5992 debug(F101,"fgen xpatslash","",xpatslash);
5994 numfnd = 0; /* None found yet */
5996 if (initspace(resarry,ssplen) < 0)
5999 xpatwild = iswild(xpat); /* Original pattern is wild? */
6000 xpatabsolute = isabsolute(xpat);
6001 xleafwild = iswild(xpatlast);
6003 debug(F111,"fgen xpat",xpat,xpatwild);
6004 debug(F111,"fgen xpatlast",xpatlast,xleafwild);
6005 debug(F101,"fgen xpatabsolute","",xpatabsolute);
6007 traverse(head,scratch,sptr); /* Go walk the directory tree. */
6008 while (head != NULL) { /* Done - free path segment list. */
6009 struct path *next = head -> fwd;
6013 debug(F101,"fgen","",numfnd);
6014 return(numfnd); /* Return the number of matches */
6017 /* Define LONGFN (long file names) automatically for BSD 2.9 and 4.2 */
6018 /* LONGFN can also be defined on the cc command line. */
6033 T R A V E R S E -- Traverse a directory tree.
6035 Walks the directory tree looking for matches to its arguments.
6036 The algorithm is, briefly:
6038 If the current pattern segment contains no wildcards, that
6039 segment is added to what we already have. If the name so far
6040 exists, we call ourselves recursively with the next segment
6041 in the pattern string; otherwise, we just return.
6043 If the current pattern segment contains wildcards, we open the name
6044 we've accumulated so far (assuming it is really a directory), then read
6045 each filename in it, and, if it matches the wildcard pattern segment, add
6046 that filename to what we have so far and call ourselves recursively on
6049 Finally, when no more pattern segments remain, we add what's accumulated
6050 so far to the result array and increment the number of matches.
6053 A pattern path list (as generated by splitpath), a string pointer that
6054 points to what we've traversed so far (this can be initialized to "/"
6055 to start the search at the root directory, or to "./" to start the
6056 search at the current directory), and a string pointer to the end of
6057 the string in the previous argument, plus the global "recursive",
6058 "xmatchdot", and "xdironly" flags.
6060 Returns: void, with:
6061 mtchs[] containing the array of filename string pointers, and:
6062 numfnd containing the number of filenames.
6064 Although it might be poor practice, the mtchs[] array is revealed to the
6065 outside in case it needs it; for example, to be sorted prior to use.
6066 (It is poor practice because not all platforms implement file lists the
6067 same way; some don't use an array at all.)
6069 Note that addresult() acts as a second-level filter; due to selection
6070 criteria outside of the pattern, it might decline to add files that
6071 this routine asks it to, e.g. because we are collecting only directory
6072 names but not the names of regular files.
6074 WARNING: In the course of C-Kermit 7.0 development, this routine became
6075 ridiculously complex, in order to meet approximately sixty specific
6076 requirements. DON'T EVEN THINK ABOUT MODIFYING THIS ROUTINE! Trust me;
6077 it is not possible to fix anything in it without breaking something else.
6078 This routine badly needs a total redesign and rewrite. Note: There may
6079 be some good applications for realpath() and/or scandir() and/or fts_blah()
6080 here, on platforms where they are available.
6083 traverse(pl,sofar,endcur) struct path *pl; char *sofar, *endcur; {
6085 /* Appropriate declarations for directory routines and structures */
6086 /* #define OPENDIR means to use opendir(), readdir(), closedir() */
6087 /* If OPENDIR not defined, we use open(), read(), close() */
6089 #ifdef DIRENT /* New way, <dirent.h> */
6091 DIR *fd, *opendir();
6092 struct dirent *dirbuf;
6093 struct dirent *readdir();
6095 #ifdef LONGFN /* Old way, <dir.h> with opendir() */
6097 DIR *fd, *opendir();
6098 struct direct *dirbuf;
6100 int fd; /* Old way, <dir.h> with open() */
6101 struct direct dir_entry;
6102 struct direct *dirbuf = &dir_entry;
6105 int mopts = 0; /* ckmatch() opts */
6106 int depth = 0; /* Directory tree depth */
6108 char nambuf[MAXNAMLEN+4]; /* Buffer for a filename */
6109 int itsadir = 0, segisdir = 0, itswild = 0, mresult, n, x /* , y */ ;
6110 struct stat statbuf; /* For file info. */
6112 debug(F101,"STAT","",16);
6113 if (pl == NULL) { /* End of path-segment list */
6114 *--endcur = '\0'; /* Terminate string, overwrite trailing slash */
6115 debug(F110,"traverse add: end of path segment",sofar,0);
6116 addresult(sofar,-1);
6120 /* This speeds up the search a lot and we still get good results */
6121 /* but it breaks the tagging of directory names done in addresult */
6122 if (xrecursive || xfilonly || xdironly || xpatslash) {
6123 itsadir = xisdir(sofar);
6124 debug(F101,"STAT","",17);
6126 itsadir = (strncmp(sofar,"./",2) == 0);
6128 itsadir = xisdir(sofar);
6129 debug(F101,"STAT","",18);
6131 debug(F111,"traverse entry sofar",sofar,itsadir);
6133 #ifdef CKSYMLINK /* We're doing symlinks? */
6134 #ifdef USE_LSTAT /* OK to use lstat()? */
6135 if (itsadir && xnolinks) { /* If not following symlinks */
6138 x = lstat(sofar,&buf);
6139 debug(F111,"traverse lstat 1",sofar,x);
6142 S_ISLNK(buf.st_mode)
6145 ((_IFMT & buf.st_mode) == _IFLNK)
6147 #endif /* S_ISLNK */
6151 #endif /* USE_LSTAT */
6152 #endif /* CKSYMLINK */
6154 if (!xmatchdot && xpatlast[0] == '.')
6156 if (!xmatchdot && xpat[0] == '.' && xpat[1] != '/' && xpat[1] != '.')
6159 /* ckmatch() options */
6161 if (xmatchdot) mopts |= 1; /* Match dot */
6162 if (!xrecursive) mopts |= 2; /* Dirsep is fence */
6164 debug(F111,"traverse entry xpat",xpat,xpatslash);
6165 debug(F111,"traverse entry xpatlast",xpatlast,xmatchdot);
6166 debug(F110,"traverse entry pl -> npart",pl -> npart,0);
6169 if (xrecursive > 0 && !itsadir) {
6170 char * s; /* Recursive descent and this is a regular file */
6171 *--endcur = '\0'; /* Terminate string, overwrite trailing slash */
6173 /* Find the nth slash from the right and match from there... */
6174 /* (n == the number of slashes in the original pattern - see fgen) */
6175 if (*sofar == '/') {
6176 debug(F110,"traverse xpatslash absolute",sofar,0);
6179 debug(F111,"traverse xpatslash relative",sofar,xpatslash);
6180 for (s = endcur - 1, n = 0; s >= sofar; s--) {
6182 if (++n >= xpatslash) {
6190 /* This speeds things up a bit. */
6191 /* If it causes trouble define NOSKIPMATCH and rebuild. */
6192 if (xpat[0] == '*' && !xpat[1])
6193 x = xmatchdot ? 1 : (s[0] != '.');
6195 #endif /* NOSKIPMATCH */
6196 x = ckmatch(xpat, s, 1, mopts); /* Match with original pattern */
6197 debug(F111,"traverse xpatslash ckmatch",s,x);
6199 debug(F110,"traverse add: recursive, match, && !isdir",sofar,0);
6200 addresult(sofar,itsadir);
6204 #endif /* RECURSIVE */
6206 debug(F111,"traverse sofar 2",sofar,0);
6208 segisdir = ((pl -> fwd) == NULL) ? 0 : 1;
6209 itswild = wildena ? (iswild(pl -> npart)) : 0; /* 15 Jun 2005 */
6211 debug(F111,"traverse segisdir",sofar,segisdir);
6212 debug(F111,"traverse itswild ",pl -> npart,itswild);
6215 if (xrecursive > 0) { /* If recursing and... */
6216 if (segisdir && itswild) /* this is a dir and npart is wild */
6217 goto blah; /* or... */
6218 else if (!xpatabsolute && !xpatwild) /* search object is nonwild */
6219 goto blah; /* then go recurse */
6221 #endif /* RECURSIVE */
6223 if (!itswild) { /* This path segment not wild? */
6225 strcpy(endcur,pl -> npart); /* (safe) Append next part. */
6226 endcur += (int)strlen(pl -> npart); /* Advance end pointer */
6229 strcpy() does not account for quoted metacharacters.
6230 We must remove the quotes before doing the stat().
6236 while ((c = *s++)) {
6247 #endif /* COMMENT */
6248 *endcur = '\0'; /* End new current string. */
6250 if (stat(sofar,&statbuf) == 0) { /* If this piece exists... */
6251 debug(F110,"traverse exists",sofar,0);
6252 *endcur++ = DIRSEP; /* add slash to end */
6253 *endcur = '\0'; /* and end the string again. */
6254 traverse(pl -> fwd, sofar, endcur);
6257 else debug(F110,"traverse not found", sofar, 0);
6262 *endcur = '\0'; /* End current string */
6263 debug(F111,"traverse sofar 3",sofar,0);
6268 /* Search is recursive or ... */
6269 /* path segment contains wildcards, have to open and search directory. */
6273 debug(F110,"traverse opening directory", sofar, 0);
6276 debug(F110,"traverse opendir()",sofar,0);
6277 if ((fd = opendir(sofar)) == NULL) { /* Can't open, fail. */
6278 debug(F101,"traverse opendir() failed","",errno);
6281 while ((dirbuf = readdir(fd)))
6282 #else /* !OPENDIR */
6283 debug(F110,"traverse directory open()",sofar,0);
6284 if ((fd = open(sofar,O_RDONLY)) < 0) {
6285 debug(F101,"traverse directory open() failed","",errno);
6288 while (read(fd, (char *)dirbuf, sizeof dir_entry) > 0)
6289 #endif /* OPENDIR */
6290 { /* Read each entry in this directory */
6295 /* On some platforms, the read[dir]() can return deleted files, */
6296 /* e.g. HP-UX 5.00. There is no point in grinding through this */
6297 /* routine when the file doesn't exist... */
6299 if ( /* There actually is an inode... */
6307 dirbuf->d_stat.st_ino != 0
6313 dirbuf->d_fileno != 0
6316 dirbuf->d_fileno != 0
6319 dirbuf->d_fileno != 0
6322 dirbuf->d_fileno != 0
6333 #endif /* __FreeBSD__ */
6334 #endif /* __386BSD__ */
6337 #endif /* SOLARIS */
6346 ckstrncpy(nambuf, /* Copy the name */
6350 if (nambuf[0] == '.') {
6351 if (!nambuf[1] || (nambuf[1] == '.' && !nambuf[2])) {
6352 debug(F110,"traverse skipping",nambuf,0);
6353 continue; /* skip "." and ".." */
6356 s = nambuf; /* Copy name to end of sofar */
6358 while ((*eos = *s)) {
6363 Now we check the file for (a) whether it is a directory, and (b) whether
6364 its name matches our pattern. If it is a directory, and if we have been
6365 told to build a recursive list, then we must descend regardless of whether
6366 it matches the pattern. If it is not a directory and it does not match
6367 our pattern, we skip it. Note: sofar is the full pathname, nambuf is
6370 /* Do this first to save pointless function calls */
6371 if (nambuf[0] == '.' && !xmatchdot) /* Dir name starts with '.' */
6374 if (xrecursive || xfilonly || xdironly || xpatslash) {
6375 itsadir = xisdir(sofar); /* See if it's a directory */
6376 debug(F101,"STAT","",19);
6381 itsadir = xisdir(sofar);
6382 debug(F101,"STAT","",20);
6387 if (itsadir && xnolinks) { /* If not following symlinks */
6390 x = lstat(sofar,&buf);
6391 debug(F111,"traverse lstat 2",sofar,x);
6394 S_ISLNK(buf.st_mode)
6397 ((_IFMT & buf.st_mode) == _IFLNK)
6399 #endif /* S_ISLNK */
6403 #endif /* USE_LSTAT */
6404 #endif /* CKSYMLINK */
6407 if (xrecursive > 0 && itsadir &&
6408 (xpatlast[0] == '*') && !xpatlast[1]
6411 "traverse add: recursive && isdir && segisdir or match",
6415 addresult(sofar,itsadir);
6416 if (numfnd < 0) return;
6418 #endif /* RECURSIVE */
6420 debug(F111,"traverse mresult xpat",xpat,xrecursive);
6421 debug(F111,"traverse mresult pl -> npart",
6423 ((pl -> fwd) ? 9999 : 0)
6425 debug(F111,"traverse mresult sofar segisdir",sofar,segisdir);
6426 debug(F111,"traverse mresult sofar itsadir",sofar,itsadir);
6427 debug(F101,"traverse mresult xmatchdot","",xmatchdot);
6429 Match the path so far with the pattern after stripping any leading "./"
6430 from either or both. The pattern chosen is the full original pattern if
6431 the match candidate (sofar) is not a directory, or else just the name part
6432 (pl->npart) if it is.
6435 char * s1; /* The pattern */
6436 char * s2 = sofar; /* The path so far */
6437 char * s3; /* Worker */
6438 int opts; /* Match options */
6440 s1 = itsadir ? pl->npart : xpat;
6443 /* I can't explain this but it unbreaks "cd blah/sub<Esc>" */
6444 if (itsadir && !xrecursive && xpatslash > 0 &&
6445 segisdir == 0 && itswild) {
6447 debug(F110,"traverse mresult s1 kludge",s1,0);
6449 #endif /* COMMENT */
6451 if (xrecursive && xpatslash == 0)
6453 while ((s1[0] == '.') && (s1[1] == '/')) /* Strip "./" */
6455 while ((s2[0] == '.') && (s2[1] == '/')) /* Ditto */
6457 opts = mopts; /* Match options */
6458 if (itsadir) /* Current segment is a directory */
6459 opts = mopts & 1; /* No fences */
6460 s3 = s2; /* Get segment depth */
6462 while (*s3) { if (*s3++ == '/') depth++; }
6464 /* This speeds things up a bit. */
6465 /* If it causes trouble define NOSKIPMATCH and rebuild. */
6466 if (depth == 0 && (s1[0] == '*') && !s1[1])
6467 mresult = xmatchdot ? 1 : (s2[0] != '.');
6469 #endif /* NOSKIPMATCH */
6470 mresult = ckmatch(s1,s2,1,opts); /* Match */
6474 debug(F111,"traverse mresult depth",sofar,depth);
6475 debug(F101,"traverse mresult xpatslash","",xpatslash);
6476 debug(F111,"traverse mresult nambuf",nambuf,mresult);
6477 debug(F111,"traverse mresult itswild",pl -> npart,itswild);
6478 debug(F111,"traverse mresult segisdir",pl -> npart,segisdir);
6481 if (mresult || /* If match succeeded */
6482 xrecursive || /* Or search is recursive */
6483 depth < xpatslash /* Or not deep enough to match... */
6485 if ( /* If it's not a directory... */
6487 The problem here is that segisdir is apparently not set appropriately.
6488 If I leave in the !segisdir test, then "dir /recursive blah" (where blah is
6489 a directory name) misses some regular files because sometimes segisdir
6490 is set and sometimes it's not. But if I comment it out, then
6491 "dir <star>/<star>.txt lists every file in * and does not even open up the
6492 subdirectories. However, "dir /rec <star>/<star>.txt" works right.
6495 mresult && (!itsadir && !segisdir)
6497 mresult && /* Matched */
6498 !itsadir && /* sofar is not a directory */
6499 ((!xrecursive && !segisdir) || xrecursive)
6500 #endif /* COMMENT */
6503 "traverse add: match && !itsadir",sofar,0);
6504 addresult(sofar,itsadir);
6505 if (numfnd < 0) return;
6506 } else if (itsadir && (xrecursive || mresult)) {
6507 struct path * xx = NULL;
6508 *eos++ = DIRSEP; /* Add directory separator */
6509 *eos = '\0'; /* to end of segment */
6511 /* Copy previous pattern segment to this new directory */
6513 if (xrecursive > 0 && !(pl -> fwd)) {
6514 xx = (struct path *) malloc(sizeof (struct path));
6518 strcpy(xx -> npart, pl -> npart); /* safe */
6521 #endif /* RECURSIVE */
6522 traverse(pl -> fwd, sofar, eos); /* Traverse new directory */
6528 #else /* !OPENDIR */
6530 #endif /* OPENDIR */
6535 * Adds a result string to the result array. Increments the number
6536 * of matches found, copies the found string into our string
6537 * buffer, and puts a pointer to the buffer into the caller's result
6538 * array. Our free buffer pointer is updated. If there is no
6539 * more room in the caller's array, the number of matches is set to -1.
6540 * Input: a result string.
6544 addresult(str,itsadir) char *str; int itsadir; {
6548 debug(F100,"addresult string space not init'd","",0);
6549 initspace(mtchs,ssplen);
6552 debug(F111,"addresult",str,itsadir);
6557 itsadir = xisdir(str);
6559 if ((xdironly && !itsadir) || (xfilonly && itsadir)) {
6560 debug(F111,"addresult skip",str,itsadir);
6563 while (str[0] == '.' && ISDIRSEP(str[1])) /* Strip all "./" from front */
6565 if (--remlen < 0) { /* Elements left in array of names */
6566 debug(F111,"addresult ARRAY FULL",str,numfnd);
6570 len = (int)strlen(str); /* Space this will use */
6571 debug(F111,"addresult len",str,len);
6576 if ((freeptr + len + itsadir + 1) > (sspace + ssplen)) {
6577 debug(F111,"addresult OUT OF SPACE",str,numfnd);
6580 "?String space %d exhausted - use SET FILE STRINGSPACE to increase\n",ssplen);
6582 printf("?String space %d exhausted\n",ssplen);
6583 #endif /* DYNAMIC */
6584 numfnd = -1; /* Do not record if not enough space */
6587 strcpy(freeptr,str); /* safe */
6589 /* Tag directory names by putting '/' at the end */
6591 if (itsadir && (freeptr[len-1] == '/')) {
6592 freeptr[len++] = DIRSEP;
6593 freeptr[len] = '\0';
6595 if (numfnd >= maxnames) {
6598 "?Too many files (%d max) - use SET FILE LISTSIZE to increase\n",maxnames);
6600 printf("?Too many files - %d max\n",maxnames);
6601 #endif /* DYNAMIC */
6606 *resptr++ = freeptr;
6607 freeptr += (len + 1);
6609 debug(F111,"addresult ADD",str,numfnd);
6614 * match(pattern,string):
6615 * pattern matcher. Takes a string and a pattern possibly containing
6616 * the wildcard characters '*' and '?'. Returns true if the pattern
6617 * matches the string, false otherwise.
6618 * Orignally by: Jeff Damens, CUCCA, 1984
6619 * No longer used as of C-Kermit 7.0, now we use ckmatch() instead (ckclib.c).
6621 * Input: a string and a wildcard pattern.
6622 * Returns: 1 if match, 0 if no match.
6625 match(pattern, string) char *pattern, *string; {
6626 char *psave = NULL, *ssave = NULL; /* Backup pointers for failure */
6627 int q = 0; /* Quote flag */
6629 if (*string == '.' && *pattern != '.' && !xmatchdot) {
6630 debug(F110,"match skip",string,0);
6634 for (; *pattern == *string; pattern++,string++) /* Skip first */
6635 if (*string == '\0') return(1); /* End of strings, succeed */
6637 if (*pattern == '\\' && q == 0) { /* Watch out for quoted */
6638 q = 1; /* metacharacters */
6639 pattern++; /* advance past quote */
6640 if (*pattern != *string) return(0);
6647 if (*string != '\0' && *pattern == '?') {
6648 pattern++; /* '?', let it match */
6650 } else if (*pattern == '*') { /* '*' ... */
6651 psave = ++pattern; /* remember where we saw it */
6652 ssave = string; /* let it match 0 chars */
6653 } else if (ssave != NULL && *ssave != '\0') { /* if not at end */
6654 /* ...have seen a star */
6655 string = ++ssave; /* skip 1 char from string */
6656 pattern = psave; /* and back up pattern */
6657 } else return(0); /* otherwise just fail */
6661 #endif /* COMMENT */
6664 The following two functions are for expanding tilde in filenames
6665 Contributed by Howie Kaye, CUCCA, developed for CCMD package.
6668 /* W H O A M I -- Get user's username. */
6672 2) See if the $USER environment variable is set ($LOGNAME on AT&T)
6673 3) If $USER's uid is the same as ruid, realname is $USER
6674 4) Otherwise get logged in user's name
6675 5) If that name has the same uid as the real uid realname is loginname
6676 6) Otherwise, get a name for ruid from /etc/passwd
6686 static char realname[UIDBUFLEN+1]; /* user's name */
6687 static int ruid = -1; /* user's real uid */
6688 char loginname[UIDBUFLEN+1], envname[256]; /* temp storage */
6691 _PROTOTYP(extern char * getlogin, (void) );
6693 debug(F111,"whoami ruid A",realname,ruid);
6698 ruid = real_uid(); /* get our uid */
6699 debug(F101,"whoami ruid B","",ruid);
6700 if (ruid < 0) ruid = getuid();
6701 debug(F101,"whoami ruid C","",ruid);
6703 /* how about $USER or $LOGNAME? */
6704 if ((c = getenv(NAMEENV)) != NULL) { /* check the env variable */
6705 ckstrncpy(envname, c, 255);
6706 debug(F110,"whoami envname",envname,0);
6707 if ((p = getpwnam(envname)) != NULL) {
6708 if (p->pw_uid == ruid) { /* get passwd entry for envname */
6709 ckstrncpy(realname, envname, UIDBUFLEN); /* uid's are same */
6710 debug(F110,"whoami realname",realname,0);
6716 /* can we use loginname() ? */
6718 if ((c = getlogin()) != NULL) { /* name from utmp file */
6719 ckstrncpy (loginname, c, UIDBUFLEN);
6720 debug(F110,"whoami loginname",loginname,0);
6721 if ((p = getpwnam(loginname)) != NULL) /* get passwd entry */
6722 if (p->pw_uid == ruid) /* for loginname */
6723 ckstrncpy(realname, envname, UIDBUFLEN); /* if uid's are same */
6726 /* Use first name we get for ruid */
6728 if ((p = getpwuid(ruid)) == NULL) { /* name for uid */
6729 debug(F101,"whoami no username for ruid","",ruid);
6730 realname[0] = '\0'; /* no user name */
6734 ckstrncpy(realname, p->pw_name, UIDBUFLEN);
6735 debug(F110,"whoami realname from getpwuid",realname,0);
6742 /* T I L D E _ E X P A N D -- expand ~user to the user's home directory. */
6745 tilde_expand(dirname) char *dirname; {
6752 struct passwd *user;
6753 static char olddir[BUFLEN+1];
6754 static char oldrealdir[BUFLEN+1];
6755 static char temp[BUFLEN+1];
6758 debug(F111,"tilde_expand",dirname,dirname[0]);
6760 if (dirname[0] != '~') { /* Not a tilde...return param */
6761 debug(F000,"tilde_expand NOT TILDE","",dirname[0]);
6764 if (!strcmp(olddir,dirname)) { /* Same as last time */
6765 debug(F110,"tilde_expand same as previous",oldrealdir,0);
6766 return(oldrealdir); /* so return old answer. */
6768 debug(F110,"tilde_expand working...","",0);
6769 j = (int)strlen(dirname);
6770 for (i = 0; i < j; i++) /* find username part of string */
6771 if (!ISDIRSEP(dirname[i]))
6772 temp[i] = dirname[i];
6774 temp[i] = '\0'; /* tie off with a NULL */
6775 debug(F111,"tilde_expand first part",temp,i);
6776 if (i == 1) { /* if just a "~" */
6779 user = getpwnam(uidbuf); /* Get info on current user */
6783 char * p = whoami();
6784 debug(F110,"tilde_expand p",p,0);
6787 debug(F110,"tilde_expand getpwpam ~",user,0);
6793 debug(F110,"tilde_expand ~user",&temp[1],0);
6794 user = getpwnam(&temp[1]); /* otherwise on the specified user */
6795 debug(F110,"tilde_expand getpwpam user",user,0);
6799 if (user != NULL) { /* valid user? */
6800 ckstrncpy(olddir, dirname, BUFLEN); /* remember the directory */
6801 ckstrncpy(oldrealdir,user->pw_dir, BUFLEN); /* and home directory */
6802 ckstrncat(oldrealdir,&dirname[i], BUFLEN);
6803 oldrealdir[BUFLEN] = '\0';
6805 } else { /* invalid? */
6806 ckstrncpy(olddir, dirname, BUFLEN); /* remember for next time */
6807 ckstrncpy(oldrealdir, dirname, BUFLEN);
6816 Functions for executing system commands.
6817 zsyscmd() executes the system command in the normal, default way for
6818 the system. In UNIX, it does what system() does. Thus, its results
6819 are always predictable.
6820 zshcmd() executes the command using the user's preferred shell.
6823 zsyscmd(s) char *s; {
6825 if (nopush) return(-1);
6826 if (!priv_chk()) return(system(s));
6830 /* This doesn't work... */
6834 #endif /* COMMENT */
6836 if (nopush) return(-1);
6837 if ((shpid = fork())) {
6838 if (shpid < (PID_T)0) return(-1); /* Parent */
6839 while (shpid != (PID_T) wait(&status))
6843 if (priv_can()) { /* Child: cancel any priv's */
6844 printf("?Privilege cancellation failure\n");
6847 restorsigs(); /* Restore ignored signals */
6849 execl("/usr/bin/sh","sh","-c",s,NULL);
6850 perror("/usr/bin/sh");
6853 execl("/bin/rc", "rc", "-c", s, NULL);
6856 execl("/bin/sh","sh","-c",s,NULL);
6861 return(0); /* Shut up ANSI compilers. */
6866 /* Z _ E X E C -- Overlay ourselves with another program */
6874 #endif /* ATT7300 */
6876 #endif /* NOZEXEC */
6879 z_exec(p,s,t) char * p, ** s; int t; { /* Overlay ourselves with "p s..." */
6881 printf("EXEC /REDIRECT NOT IMPLEMENTED IN THIS VERSION OF C-KERMIT\n");
6882 debug(F110,"z_exec NOT IMPLEMENTED",p,0);
6886 debug(F110,"z_exec command",p,0);
6887 debug(F110,"z_exec arg 0",s[0],0);
6888 debug(F110,"z_exec arg 1",s[1],0);
6889 debug(F101,"z_exec t","",t);
6895 /* dup2(ttyfd, 2); */
6899 restorsigs(); /* Restore ignored signals */
6901 if (x < 0) debug(F101,"z_exec errno","",errno);
6902 #endif /* NOZEXEC */
6906 Z S H C M D -- Execute a shell command (or program thru the shell).
6908 Original UNIX code by H. Fischer; copyright rights assigned to Columbia U.
6909 Adapted to use getpwuid to find login shell because many systems do not
6910 have SHELL in environment, and to use direct calling of shell rather
6911 than intermediate system() call. -- H. Fischer (1985); many changes since
6912 then. Call with s pointing to command to execute. Returns:
6913 -1 on failure to start the command (can't find, can't fork, can't run).
6914 1 if command ran and gave an exit status of 0.
6915 0 if command ran and gave a nonzero exit status.
6916 with pexitstatus containing the command's exit status.
6919 zshcmd(s) char *s; {
6925 if (nopush) return(-1);
6927 while (*s == ' ') s++;
6929 debug(F110,"zshcmd command",s,0);
6932 if ((pid = vfork()) == 0) { /* Make child quickly */
6933 char *shpath, *shname, *shptr; /* For finding desired shell */
6935 if (priv_can()) exit(1); /* Turn off privs. */
6936 if ((shpath = getenv("SHELL")) == NULL) shpath = "/com/sh";
6938 #else /* All Unix systems */
6939 if ((pid = fork()) == 0) { /* Make child */
6940 char *shpath, *shname, *shptr; /* For finding desired shell */
6942 #ifdef HPUX10 /* Default */
6943 char *defshell = "/usr/bin/sh";
6946 char *defshell = "/bin/rc";
6948 char *defshell = "/bin/sh";
6951 if (priv_can()) exit(1); /* Turn off privs. */
6953 /* Old way always used /etc/passwd shell */
6954 p = getpwuid(real_uid()); /* Get login data */
6955 if (p == (struct passwd *) NULL || !*(p->pw_shell))
6958 shpath = p->pw_shell;
6960 /* New way lets user override with SHELL variable, but does not rely on it. */
6961 /* This allows user to specify a different shell. */
6962 shpath = getenv("SHELL"); /* What shell? */
6963 debug(F110,"zshcmd SHELL",shpath,0);
6968 } else if (!*shpath) {
6972 debug(F100,"zshcmd SHELL not defined","",0);
6973 p = getpwuid( real_uid() ); /* Get login data */
6974 if (p == (struct passwd *)NULL || !*(p->pw_shell)) {
6977 shpath = p->pw_shell;
6979 debug(F110,"zshcmd shpath from getpwuid",shpath,0);
6982 #endif /* COMMENT */
6984 shptr = shname = shpath;
6985 while (*shptr != '\0')
6986 if (*shptr++ == DIRSEP)
6988 restorsigs(); /* Restore ignored signals */
6989 debug(F110,"zshcmd execl shpath",shpath,0);
6990 debug(F110,"zshcmd execl shname",shname,0);
6991 if (s == NULL || *s == '\0') { /* Interactive shell requested? */
6992 debug(F100,"zshcmd execl interactive","",0);
6993 execl(shpath,shname,"-i",NULL); /* Yes, do that */
6994 } else { /* Otherwise, */
6995 debug(F110,"zshcmd execl command",s,0);
6996 execl(shpath,shname,"-c",s,NULL); /* exec the given command */
6997 } /* If execl() failed, */
6998 debug(F101,"zshcmd errno","",errno);
6999 perror(shpath); /* print reason and */
7000 exit(BAD_EXIT); /* return bad return code. */
7002 } else { /* Parent */
7004 int wstat; /* ... must wait for child */
7006 int child; /* Child's exit status */
7007 #endif /* CK_CHILD */
7008 SIGTYP (*istat)(), (*qstat)();
7010 if (pid == (PID_T) -1) return(-1); /* fork() failed? */
7012 istat = signal(SIGINT,SIG_IGN); /* Let the fork handle keyboard */
7013 qstat = signal(SIGQUIT,SIG_IGN); /* interrupts itself... */
7015 debug(F110,"zshcmd parent waiting for child",s,0);
7017 while (((wstat = wait(&child)) != pid) && (wstat != -1))
7019 while (((wstat = wait((WAIT_T *)0)) != pid) && (wstat != -1))
7020 #endif /* CK_CHILD */
7021 ; /* Wait for fork */
7022 signal(SIGINT,istat); /* Restore interrupts */
7023 signal(SIGQUIT,qstat);
7025 pexitstat = (child & 0xff) ? child : child >> 8;
7026 debug(F101,"zshcmd exit status","",pexitstat);
7027 return(child == 0 ? 1 : 0); /* Return child's status */
7028 #endif /* CK_CHILD */
7034 /* I S W I L D -- Check if filespec is "wild" */
7038 0 wildcards disabled or argument is empty or is the name of a single file;
7039 1 if it contains wildcard characters.
7040 Note: must match the algorithm used by match(), hence no [a-z], etc.
7043 iswild(filespec) char *filespec; {
7044 char c, *p, *f; int x;
7046 if (!filespec) /* Safety */
7048 if (!wildena) /* Wildcards disabled - 12 Jun 2005 */
7051 if (wildxpand) { /* Shell handles wildcarding */
7052 if ((x = nzxpand(filespec,0)) > 1)
7054 if (x == 0) return(0); /* File does not exist */
7055 p = malloc(MAXNAMLEN + 20);
7057 x = (strcmp(filespec,p) != 0);
7061 } else { /* We do it ourselves */
7062 while ((c = *filespec++) != '\0') {
7063 if (c == '\\' && quo == 0) {
7067 if (!quo && (c == '*' || c == '?'
7073 #endif /* CKREGEX */
7075 debug(F111,"iswild",f,1);
7080 debug(F111,"iswild",f,0);
7086 I S D I R -- Is a Directory.
7088 Tell if string pointer s is the name of an existing directory. Returns 1 if
7089 directory, 0 if not a directory.
7091 The following no longer applies:
7093 If the file is a symlink, we return 1 if
7094 it is a directory OR if it is a link to a directory and the "xrecursive" flag
7095 is NOT set. This is to allow parsing a link to a directory as if it were a
7096 directory (e.g. in the CD or IF DIRECTORY command) but still prevent
7097 recursive traversal from visiting the same directory twice.
7101 /* This turns out to be unsafe and gives little benefit anyway. */
7102 /* See notes 28 Sep 2003. Thus ISDIRCACHE is not defined. */
7104 static char prevpath[CKMAXPATH+4] = { '\0', '\0' };
7105 static int prevstat = -1;
7108 debug(F100,"CLEAR ISDIR CACHE","",0);
7112 #endif /* ISDIRCACHE */
7115 isalink(s) char *s; {
7120 char filbuf[CKMAXPATH+4];
7121 if (readlink(s,filbuf,CKMAXPATH) > -1)
7123 debug(F110,"isalink readlink",s,r);
7125 #endif /* CKSYMLINK */
7130 int x, needrlink = 0, islink = 0;
7131 struct stat statbuf;
7132 char fnam[CKMAXPATH+4];
7135 debug(F110,"isdir entry",s,0);
7136 #ifdef DTILDE /* 2005-08-13 */
7137 if (*s == '~') { /* Starts with tilde? */
7138 s = tilde_expand(s); /* Attempt to expand tilde */
7140 debug(F110,"isdir tilde_expand",s,0);
7146 if (prevstat > -1) {
7147 if (s[0] == prevpath[0]) {
7148 if (!strcmp(s,prevpath)) {
7149 debug(F111,"isdir cache hit",s,prevstat);
7154 #endif /* ISDIRCACHE */
7159 The following over-clever bit has been commented out because it presumes
7160 to know when a symlink might be redundant, which it can't possibly know.
7161 Using plain old stat() gives Kermit the same results as ls and ls -R, which
7162 is just fine: no surprises.
7166 x = lstat(s,&statbuf);
7167 debug(F111,"isdir lstat",s,x);
7169 #endif /* USE_LSTAT */
7170 x = stat(s,&statbuf);
7171 debug(F111,"isdir stat",s,x);
7174 #endif /* USE_LSTAT */
7176 x = stat(s,&statbuf);
7177 debug(F111,"isdir stat",s,x);
7178 #endif /* COMMENT */
7180 debug(F101,"isdir errno","",errno);
7189 islink = S_ISLNK(statbuf.st_mode);
7190 debug(F101,"isdir S_ISLNK islink","",islink);
7193 islink = (_IFMT & statbuf.st_mode) == _IFLNK;
7194 debug(F101,"isdir _IFLNK islink","",islink);
7196 #endif /* S_ISLNK */
7197 #endif /* NOLINKBITS */
7199 if (readlink(s,fnam,CKMAXPATH) > -1)
7204 x = stat(s,&statbuf);
7206 debug(F101,"isdir errno","",errno);
7209 debug(F111,"isdir stat",s,x);
7210 #endif /* CKSYMLINK */
7211 debug(F101,"isdir islink","",islink);
7212 debug(F101,"isdir statbuf.st_mode","",statbuf.st_mode);
7213 x = islink ? 0 : (S_ISDIR (statbuf.st_mode) ? 1 : 0);
7216 ckstrncpy(prevpath,s,CKMAXPATH+1);
7217 #endif /* ISDIRCACHE */
7222 /* Some systems don't have mkdir(), e.g. Tandy Xenix 3.2.. */
7224 /* Z M K D I R -- Create directory(s) if necessary */
7227 A pointer to a file specification that might contain directory
7228 information. The filename is expected to be included.
7229 If the file specification does not include any directory separators,
7230 then it is assumed to be a plain file.
7231 If one or more directories are included in the file specification,
7232 this routine tries to create them if they don't already exist.
7234 0 or greater on success, i.e. the number of directories created.
7235 -1 on failure to create the directory
7238 zmkdir(path) char *path; {
7242 if (!path) path = "";
7243 if (!*path) return(-1);
7246 debug(F111,"zmkdir setroot",ckroot,ckrootset);
7247 if (ckrootset) if (!zinroot(path)) {
7248 debug(F110,"zmkdir setroot violation",path,0);
7254 debug(F111,"zmkdir",path,x);
7255 if (x < 1 || x > MAXPATH) /* Check length */
7257 if (!(tp = malloc(x+1))) /* Make a temporary copy */
7259 strcpy(tp,path); /* safe (prechecked) */
7261 if (*tp == '~') { /* Starts with tilde? */
7262 xp = tilde_expand(tp); /* Attempt to expand tilde */
7266 debug(F110,"zmkdir tilde_expand",xp,0);
7267 if (!(zp = malloc(strlen(xp) + 1))) { /* Make a place for it */
7272 free(tp); /* Free previous buffer */
7273 tp = zp; /* Point to new one */
7274 strcpy(tp,xp); /* Copy expanded name to new buffer */
7278 debug(F110,"zmkdir tp after tilde_expansion",tp,0);
7280 if (ISDIRSEP(*xp)) /* Don't create root directory! */
7283 /* Go thru filespec from left to right... */
7285 for (; *xp; xp++) { /* Create parts that don't exist */
7286 if (!ISDIRSEP(*xp)) /* Find next directory separator */
7288 c = *xp; /* Got one. */
7289 *xp = NUL; /* Make this the end of the string. */
7290 if (!isdir(tp)) { /* This directory exists already? */
7292 if (isguest) /* Not allowed for guests */
7295 /* Nor if MKDIR and/or CD are disabled */
7297 #endif /* CK_LOGIN */
7302 ) && (!ENABLED(en_mkd) || !ENABLED(en_cwd)))
7306 debug(F110,"zmkdir making",tp,0);
7307 x = /* No, try to create it */
7309 -1 /* Systems without mkdir() */
7311 mkdir(tp,0777) /* UNIX */
7312 #endif /* NOMKDIR */
7315 debug(F101,"zmkdir failed, errno","",errno);
7316 free(tp); /* Free temporary buffer. */
7318 return(-1); /* Return failure code. */
7322 *xp = c; /* Replace the separator. */
7324 free(tp); /* Free temporary buffer. */
7325 return(count); /* Return success code. */
7327 #endif /* CK_MKDIR */
7330 zrmdir(path) char *path; {
7334 #endif /* CK_LOGIN */
7336 if (!path) path = "";
7337 if (!*path) return(-1);
7340 debug(F111,"zrmdir setroot",ckroot,ckrootset);
7341 if (ckrootset) if (!zinroot(path)) {
7342 debug(F110,"zrmdir setroot violation",path,0);
7348 return(rmdir(path));
7351 #endif /* NOMKDIR */
7354 /* Z F S E E K -- Position input file pointer */
7357 CK_OFF_T (32 or 64 bits), 0-based, indicating desired position.
7365 zfseek(CK_OFF_T pos)
7367 zfseek(pos) CK_OFF_T pos;
7368 #endif /* CK_ANSIC */
7370 zincnt = -1; /* Must empty the input buffer */
7371 debug(F101,"zfseek","",pos);
7372 return(CKFSEEK(fp[ZIFILE], pos, 0)?-1:0);
7374 #endif /* NORESEND */
7376 /* Z F N Q F P -- Convert filename to fully qualified absolute pathname */
7379 Given a possibly unqualified or relative file specification fn, zfnqfp()
7380 returns the fully qualified filespec for the same file, returning a struct
7381 that contains the length (len) of the result, a pointer (fpath) to the
7382 whole result, and a pointer (fname) to where the filename starts.
7384 static struct zfnfp fnfp = { 0, NULL, NULL };
7387 zfnqfp(fname, buflen, buf) char * fname; int buflen; char * buf; {
7391 char zfntmp[MAXPATHLEN+4];
7393 char zfntmp[CKMAXPATH+4];
7394 #endif /* MAXPATHLEN */
7397 int i = 0, j = 0, k = 0, x = 0, y = 0;
7408 /* Initialize the data structure */
7410 fnfp.len = ckstrncpy(buf,fname,buflen);
7414 debug(F111,"zfnqfp fname",fname,len);
7417 if (*s == '~') { /* Starts with tilde? */
7419 xp = tilde_expand(s); /* Attempt to expand tilde */
7420 debug(F110,"zfnqfp xp",xp,0); /* (realpath() doesn't do this) */
7429 /* N.B.: The realpath() result buffer MUST be MAXPATHLEN bytes long */
7430 /* otherwise we write over memory. */
7432 if (!realpath(s,zfntmp)) {
7433 debug(F111,"zfnqfp realpath fails",s,errno);
7435 if (errno != ENOENT)
7438 /* If realpath() fails use the do-it-yourself method */
7441 #endif /* COMMENT */
7443 len = strlen(zfntmp);
7445 debug(F111,"zfnqfp result too long",ckitoa(buflen),len);
7448 ckstrncpy(buf,zfntmp,buflen);
7450 if (buf[len-1] != '/') {
7451 if ((itsadir = isdir(buf)) && len < (buflen - 1)) {
7458 debug(F110,"zfnqfp realpath path",fnfp.fpath,0);
7459 tmp = buf + fnfp.len - 1;
7461 while (tmp >= buf) {
7463 fnfp.fname = tmp + 1;
7464 debug(F110,"zfnqfp realpath name",fnfp.fname,0);
7472 #endif /* CKREALPATH */
7477 while (*s) { /* Remove leading "./" (0 or more) */
7478 debug(F110,"zfnqfp while *s",s,0);
7479 if (*s == '.' && *(s+1) == '/') {
7481 while (*s == '/') s++;
7485 if (!*s) return(NULL);
7486 if (*s == '/') { /* Pathname is absolute */
7487 ckstrncpy(buf,s,len);
7490 } else { /* Pathname is relative */
7492 if (p = zgtdir()) { /* So get current directory */
7493 debug(F110,"zfnqfp zgtdir",p,0);
7494 x = ckstrncpy(buf,p,len);
7496 debug(F110,"zfnqfp buf 1",buf,0);
7497 len -= x; /* How much room left in buffer */
7498 if ((y = (int)strlen(s)) > len) /* If enough room... */
7500 ckstrncpy(buf+x,s,len); /* ... append the filename */
7501 debug(F110,"zfnqfp buf 2",buf,0);
7507 /* Buf now holds full path but maybe containing some . or .. tricks */
7509 j = x + y; /* Length of what's in buf */
7511 debug(F101,"zfnqfp len","",len);
7513 /* Catch dangling "/." or "/.." */
7514 if ((j > 1 && buf[j-1] == '.' && buf[j-2] == '/') ||
7515 (j > 2 && buf[j-1] == '.' && buf[j-2] == '.' && buf[j-3] == '/')) {
7516 if (j < buflen - 2) {
7521 j = -1; /* j = position of rightmost "/" */
7522 i = 0; /* i = destination index */
7523 tmp[i] = NUL; /* destination is temporary buffer */
7525 for (x = 0; x < len; x++) { /* x = source index */
7526 if (buf[x] == '/') {
7527 for (k = 0; k < 4; k++) {
7532 if (!strncmp(sb,"/./",3)) { /* Eliminate "./" in "/./" */
7535 } else if (!strncmp(sb,"//",2)) { /* Change "//" to "/" */
7537 } else if (!strncmp(sb,"/../",4)) { /* ".." in path */
7538 for (k = i - 1; k >= 0; k--) { /* Back up one level */
7539 if (tmp[k] == '/') {
7549 if (i >= (buflen - 1)) {
7550 debug(F111,"zfnqfp overflow",tmp,i);
7553 tmp[i++] = buf[x]; /* Regular character, copy */
7555 if (buf[x] == '/') /* Remember rightmost "/" */
7558 ckstrncpy(buf,tmp,buflen-1); /* Copy the result back */
7560 buf[buflen-1] = NUL;
7561 if (!buf[0]) { /* If empty, say root */
7567 if ((itsadir = isdir(buf))) {
7568 if (buf[i-1] != '/' && i < (buflen - 1)) {
7573 if (!itsadir && (j > -1)) { /* Set pointer to basename */
7574 fnfp.fname = (char *)(buf + j);
7575 fnfp.fpath = (char *)buf;
7577 debug(F111,"zfnqfp path",fnfp.fpath,i);
7578 debug(F110,"zfnqfp name",fnfp.fname,0);
7584 /* Z C M P F N -- Compare two filenames */
7586 /* Returns 1 if the two names refer to the same existing file, 0 otherwise. */
7589 zcmpfn(s1,s2) char * s1, * s2; {
7590 char buf1[CKMAXPATH+1];
7591 char buf2[CKMAXPATH+1];
7594 char linkname[CKMAXPATH+1];
7596 #endif /* USE_LSTAT */
7601 if (!*s1 || !*s2) return(0);
7603 #ifdef CKSYMLINK /* We're doing symlinks? */
7604 #ifdef USE_LSTAT /* OK to use lstat()? */
7606 if (x > -1 && /* Now see if it's a symlink */
7608 S_ISLNK(buf.st_mode)
7611 ((_IFMT & buf.st_mode) == _IFLNK)
7613 #endif /* S_ISLNK */
7615 linkname[0] = '\0'; /* Get the name */
7616 x = readlink(s1,linkname,CKMAXPATH);
7617 if (x > -1 && x < CKMAXPATH) { /* It's a link */
7622 #endif /* USE_LSTAT */
7623 #endif /* CKSYMLINK */
7625 if (zfnqfp(s1,CKMAXPATH,buf1)) { /* Convert to full pathname */
7627 #ifdef CKSYMLINK /* Same deal for second name... */
7632 S_ISLNK(buf.st_mode)
7635 ((_IFMT & buf.st_mode) == _IFLNK)
7637 #endif /* S_ISLNK */
7640 x = readlink(s2,linkname,CKMAXPATH);
7641 if (x > -1 && x < CKMAXPATH) {
7646 #endif /* USE_LSTAT */
7647 #endif /* CKSYMLINK */
7648 if (zfnqfp(s2,CKMAXPATH,buf2)) {
7649 debug(F110,"zcmpfn s1",buf1,0);
7650 debug(F110,"zcmpfn s2",buf2,0);
7651 if (!strncmp(buf1,buf2,CKMAXPATH))
7655 debug(F101,"zcmpfn result","",rc);
7661 /* User-mode chroot() implementation */
7664 zsetroot(s) char * s; { /* Sets the root */
7665 char buf[CKMAXPATH+1];
7667 if (!*s) return(-1);
7668 debug(F110,"zsetroot",s,0);
7669 if (!isdir(s)) return(-2);
7670 if (!zfnqfp(s,CKMAXPATH,buf)) /* Get full, real path */
7672 if (access(buf,R_OK) < 0) { /* Check access */
7673 debug(F110,"zsetroot access denied",buf,0);
7677 if (ckrootset) { /* If root already set */
7678 if (!zinroot(s)) { /* make sure new root is in it */
7679 debug(F110,"zsetroot new root not in root",ckroot,0);
7683 if (zchdir(buf) < 1) return(-4); /* Change directory to new root */
7684 ckrootset = ckstrncpy(ckroot,buf,CKMAXPATH); /* Now set the new root */
7685 if (ckroot[ckrootset-1] != '/') {
7686 ckroot[ckrootset++] = '/';
7687 ckroot[ckrootset] = '\0';
7689 debug(F111,"zsetroot rootset",ckroot,ckrootset);
7690 ckrooterr = 0; /* Reset error flag */
7695 zgetroot() { /* Returns the root */
7698 return((char *)ckroot);
7702 zinroot(s) char * s; { /* Checks if file s is in the root */
7704 struct zfnfp * f = NULL;
7705 char buf[CKMAXPATH+2];
7707 debug(F111,"zinroot setroot",ckroot,ckrootset);
7708 ckrooterr = 0; /* Reset global error flag */
7709 if (!ckrootset) /* Root not set */
7710 return(1); /* so it's ok - no need to check */
7711 if (!(f = zfnqfp(s,CKMAXPATH,buf))) /* Get full and real pathname */
7712 return(0); /* Fail if we can't */
7713 n = f->len; /* Length of full pathname */
7714 debug(F111,"zinroot n",buf,n);
7715 if (n < (ckrootset - 1) || n > CKMAXPATH) { /* Bad length */
7716 ckrooterr = 1; /* Fail */
7719 if (isdir(buf) && buf[n-1] != '/') { /* If it's a directory name */
7720 buf[n++] = '/'; /* make sure it ends with '/' */
7723 x = strncmp(buf,ckroot,ckrootset); /* Compare, case-sensitive */
7724 debug(F111,"zinroot checked",buf,x);
7725 if (x == 0) /* OK */
7727 ckrooterr = 1; /* Not OK */
7734 The following code provides support for user login and logout
7735 including anonymous accounts. If this feature is to be supported
7736 outside of UNIX, it should be spread out among the ck?fio.c modules...
7738 #ifndef _PATH_BSHELL
7739 #define _PATH_BSHELL "/usr/bin/bash"
7740 #endif /* _PATH_BSHELL */
7741 #ifndef _PATH_FTPUSERS
7742 #define _PATH_FTPUSERS "/etc/ftpusers"
7743 #endif /* _PATH_FTPUSERS */
7746 * Helper function for sgetpwnam().
7749 sgetsave(s) char *s; {
7750 char *new = malloc((unsigned) strlen(s) + 1);
7752 printf("?Local resource failure: malloc\n");
7756 (void) strcpy(new, s); /* safe */
7761 * Save the result of getpwnam(). Used for USER command, since
7762 * the data returned must not be clobbered by any other command
7766 sgetpwnam(name) char *name; {
7767 static struct passwd save;
7768 register struct passwd *p;
7770 register struct spwd *sp;
7771 #endif /* CK_SHADOW */
7774 #ifdef HPUX10_TRUSTED
7775 struct pr_passwd *pr;
7776 #endif /* HPUX10_TRUSTED */
7779 sp = getspnam(name);
7781 debug(F110,"sgetpwnam","getspnam() fails",0);
7784 #endif /* CK_SHADOW */
7786 #ifdef HPUX10_TRUSTED
7787 if ((pr = getprpwnam(name)) == NULL)
7789 #endif /* HPUX10_TRUSTED */
7792 /* debug(F111,"sgetpwnam","getpwnam()",p); */
7797 free(save.pw_passwd);
7798 free(save.pw_gecos);
7800 free(save.pw_shell);
7803 save.pw_name = sgetsave(p->pw_name);
7805 save.pw_passwd = sgetsave(sp->sp_pwdp);
7806 #else /* CK_SHADOW */
7807 #ifdef HPUX10_TRUSTED
7808 if (pr->uflg.fg_encrypt && pr->ufld.fd_encrypt && *pr->ufld.fd_encrypt)
7809 save.pw_passwd = sgetsave(pr->ufld.fd_encrypt);
7811 save.pw_passwd = sgetsave("");
7812 #else /* HPUX10_TRUSTED */
7813 save.pw_passwd = sgetsave(p->pw_passwd);
7814 #endif /* HPUX10_TRUSTED */
7815 #endif /* CK_SHADOW */
7816 save.pw_gecos = sgetsave(p->pw_gecos);
7817 save.pw_dir = sgetsave(p->pw_dir);
7818 save.pw_shell = sgetsave(p->pw_shell);
7822 #define CKXLOGBSIZ 256
7824 struct passwd * pw = NULL;
7825 char * home = NULL; /* Home directory pointer for glob */
7832 int defumask = CMASK; /* Default umask value */
7834 /* Z V U S E R -- Verify user, Returns 1 if user OK, 0 otherwise. */
7836 /* Sets global passwd pointer pw if named account exists and is acceptable;
7837 * sets askpasswd if a PASS command is expected. If logged in previously,
7838 * need to reset state. If name is "ftp" or "anonymous", the name is not in
7839 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
7840 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
7841 * requesting login privileges. Disallow anyone who does not have a standard
7842 * shell as returned by getusershell(). Disallow anyone mentioned in the file
7843 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
7845 _PROTOTYP(static int checkuser, (char *) );
7847 char zvuname[64] = { NUL, NUL };
7848 char zvhome[CKMAXPATH+1] = { NUL, NUL };
7850 #define ZENVHOME CKMAXPATH+12
7851 #define ZENVLOGNAME 74
7852 static char zenvuser[ZENVUSER];
7853 static char zenvhome[ZENVHOME];
7854 static char zenvlogname[ZENVLOGNAME];
7857 static char pam_data[500];
7858 struct pam_conv pam_conv = {pam_cb, pam_data}; /* PAM structure */
7859 struct pam_handle * pamh = NULL; /* PAM reference handle */
7863 zvuser(name) char *name; {
7864 register char *cp = NULL;
7868 _PROTOTYP(char * getusershell, (void) );
7869 #endif /* GETUSERSHELL */
7870 #ifndef NODCLENDUSERSHELL
7871 _PROTOTYP(VOID endusershell, (void) );
7872 #endif /* NODCLENDUSERSHELL */
7876 const char * reply = NULL;
7879 debug(F111,"user",name,logged_in);
7881 if (!name) name = "";
7884 debug(F101,"zvuser ckxsyslog","",ckxsyslog);
7887 debug(F100,"zvuser CKSYSLOG defined","",0);
7888 #endif /* CKSYSLOG */
7890 if (logged_in) /* Should not be called if logged in */
7894 if (ckxsyslog && ckxlogging) {
7896 "login: user %s",name
7899 #endif /* CKSYSLOG */
7901 guest = 0; /* Assume not guest */
7904 if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
7905 debug(F101,"zvuser anonymous ckxanon","",ckxanon);
7906 if (!ckxanon) { /* Anonymous login not allowed */
7908 if (ckxsyslog && ckxlogging) {
7910 "login: anonymous login not allowed: %s",
7911 clienthost ? clienthost : "(unknown host)"
7914 #endif /* CKSYSLOG */
7917 if (checkuser("ftp") || checkuser("anonymous")) {
7918 debug(F100,"zvuser anon forbidden by ftpusers file","",0);
7920 if (ckxsyslog && ckxlogging) {
7922 "login: anonymous login forbidden by ftpusers file: %s",
7923 clienthost ? clienthost : "(unknown host)"
7926 #endif /* CKSYSLOG */
7928 } else if ((pw = sgetpwnam("ftp")) != NULL) {
7929 debug(F100,"zvuser anon sgetpwnam(ftp) OK","",0);
7932 ckstrncpy(zvuname,"anonymous",64);
7935 debug(F100,"zvuser anon sgetpwnam(ftp) FAILED","",0);
7937 if (ckxsyslog && ckxlogging) {
7939 "login: anonymous getpwnam(ftp) failed: %s",
7940 clienthost ? clienthost : "(unknown host)"
7943 #endif /* CKSYSLOG */
7947 pw = sgetpwnam(name);
7950 Of course some UNIX platforms (like AIX) don't have getusershell().
7951 In that case we can't check if the user's account has been "turned off"
7952 or somesuch, e.g. by setting their shell to "/etc/nologin" or somesuch,
7953 which runs (usually just printing a message and exiting), but which is
7954 not listed in /etc/shells. For that matter, if getusershell() is not
7955 available, then probably neither is /etc/shells.
7957 debug(F100,"zvuser sgetpwnam ok","",0);
7958 shell = pw->pw_shell;
7959 if (!shell) shell = "";
7961 shell = _PATH_BSHELL;
7962 debug(F110,"zvuser shell",shell,0);
7964 while ((cp = getusershell()) != NULL) {
7965 debug(F110,"zvuser getusershell",cp,0);
7966 if ((int)strcmp(cp, shell) == 0)
7969 debug(F100,"zvuser endusershell 1","",0);
7970 #ifndef NODCLENDUSERSHELL
7971 (VOID) endusershell();
7974 #endif /* NODCLENDUSERSHELL */
7975 debug(F100,"zvuser endusershell 2","",0);
7976 #else /* GETUSERSHELL */
7977 cp = ""; /* Do not refuse if we cannot check */
7978 #endif /* GETUSERSHELL */
7979 x = checkuser(name);
7980 debug(F101,"zvuser checkuser","",x);
7982 debug(F100,"zvuser refused 1","",0);
7983 pw = (struct passwd *) NULL;
7985 if (ckxsyslog && ckxlogging) {
7987 "login: invalid shell %s for %s %s",shell, name,
7988 clienthost ? clienthost : "(unknown host)"
7991 #endif /* CKSYSLOG */
7994 debug(F100,"zvuser refused 2","",0);
7995 pw = (struct passwd *) NULL;
7997 if (ckxsyslog && ckxlogging) {
7999 "login: %s login forbidden by ftpusers file: %s",
8000 name, clienthost ? clienthost : "(unknown host)"
8003 #endif /* CKSYSLOG */
8008 /* Get PAM authentication details */
8009 debug(F110,"zvuser","calling pam_start",0);
8011 pam_start(PAM_SERVICE_TYPE,name,&pam_conv,&pamh))
8013 reply = pam_strerror(NULL, pam_status);
8014 debug(F110,"zvuser PAM failure",reply,0);
8015 printf("%s\n",reply);
8017 if (ckxsyslog && ckxlogging) {
8019 "login: %s refused by PAM \"%s\": %s",
8021 clienthost ? clienthost : "(unknown host)"
8024 #endif /* CKSYSLOG */
8029 ckstrncpy(zvuname,name,64);
8034 debug(F100,"zvuser sgetpwnam NULL","",0);
8036 if (ckxsyslog && ckxlogging) {
8038 "login: getpwnam(%s) failed: %s",name,
8039 clienthost ? clienthost : "(unknown host)"
8042 #endif /* CKSYSLOG */
8047 if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) {
8049 /* Why sprintf and then printf? */
8050 /* Also, what is kerb_ok? And is the test on it right? */
8051 char buf[CKXLOGBSIZ];
8052 sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s%s",
8053 kdata.pname, *kdata.pinst ? "." : "",
8054 kdata.pinst, kdata.prealm,
8055 (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not",
8056 name, kerb_ok ? "" : "; Password required.");
8059 printf("Kerberos user %s%s%s@%s is%s authorized as %s%s",
8060 kdata.pname, *kdata.pinst ? "." : "",
8061 kdata.pinst, kdata.prealm,
8062 (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not",
8063 name, kerb_ok ? "" : "; Password required.");
8064 #endif /* COMMENT */
8065 if (kerb_ok) return(1);
8068 #endif /* FTP_KERBEROS */
8071 /* Check if the given user is in the forbidden-user file */
8074 checkuser(name) char *name; {
8075 extern char * userfile;
8078 char line[CKXLOGBSIZ+1];
8083 debug(F111,"checkuser name",name,i);
8087 fd = fopen(userfile ? userfile : _PATH_FTPUSERS, "r");
8088 /* debug(F111,"checkuser userfile",userfile,fd); */
8091 while (fgets(line, sizeof(line), fd)) {
8092 debug(F110,"checkuser line",line,0);
8095 if (strncmp(line, name, i) == 0) {
8096 debug(F110,"checkuser REFUSED",name,0);
8103 debug(F110,"checkuser OK",name,0);
8107 /* Z V L O G O U T -- Log out from Internet Kermit Service */
8112 /* This could be dangerous */
8113 if (setuid((UID_T)0) < 0) {
8114 debug(F100,"zvlogout setuid FAILED","",0);
8117 debug(F100,"zvlogout setuid OK","",0);
8118 #endif /* COMMENT */
8120 if (ckxsyslog >= SYSLG_LI && ckxlogging) {
8121 cksyslog(SYSLG_LI, 1, "logout",(char *) uidbuf, clienthost);
8123 #endif /* CKSYSLOG */
8125 debug(F110,"WTMP logout",cksysline,logged_in);
8127 logwtmp(cksysline, "", "");
8136 kpass(name, p) char *name, *p; {
8137 char instance[INST_SZ];
8138 char realm[REALM_SZ];
8142 unsigned long faddr;
8145 if (krb_get_lrealm(realm, 1) != KSUCCESS)
8148 ckstrncpy(tkt_file, TKT_ROOT, 20);
8149 ckstrncat(tkt_file, "_ftpdXXXXXX", 20);
8150 krb_set_tkt_string(mktemp(tkt_file));
8152 (VOID) ckstrncpy(instance, krb_get_phost(hostname), sizeof(instance));
8154 if ((hp = gethostbyname(instance)) == NULL)
8158 hp = ck_copyhostent(hp); /* safe copy that won't change */
8159 #endif /* HADDRLIST */
8160 bcopy((char *)hp->h_addr, (char *) &faddr, sizeof(faddr));
8162 if (krb_get_pw_in_tkt(name, "", realm, "krbtgt", realm, 1, p) ||
8163 krb_mk_req(&ticket, "rcmd", instance, realm, 33) ||
8164 krb_rd_req(&ticket, "rcmd", instance, faddr, &authdata, "") ||
8165 kuserok(&authdata, name)) {
8172 #endif /* FTP_KERBEROS */
8177 if (ckxsyslog && !ckxlogging) {
8179 openlog(inserver ? "iksd" : "ckermit", LOG_PID, LOG_DAEMON);
8181 openlog(inserver ? "iksd" : "ckermit", LOG_PID);
8182 #endif /* LOG_DAEMON */
8184 debug(F100,"zsyslog syslog opened","",0);
8186 #endif /* CKSYSLOG */
8189 /* Z V P A S S -- Verify password; returns 1 if OK, 0 otherwise */
8193 #endif /* AUTH_USER */
8195 #define AUTH_VALID 4
8196 #endif /* AUTH_VALID */
8198 #ifdef __FreeBSD__ /* 299 This was necessary in */
8199 #ifndef NODCLINITGROUPS /* FreeBSD 4.4, don't know */
8200 #define NODCLINITGROUPS /* about other versions... */
8201 #endif /* NODCLINITGROUPS */
8202 #endif /* __FreeBSD__ */
8205 zvpass(p) char *p; {
8206 #ifndef NODCLINITGROUPS
8207 _PROTOTYP(int initgroups, (const char *, gid_t) );
8208 #endif /* NODCLINITGROUPS */
8210 char *xpasswd, *salt;
8214 const char * reply = NULL;
8217 if (logged_in || askpasswd == 0) {
8220 debug(F111,"zvpass",p ? (guest ? p : "xxxxxx") : "(null)",guest);
8223 if (guest && !*p) { /* Guests must specify a password */
8225 if (ckxsyslog && ckxlogging) {
8227 "login: anonymous guests must specify a password"
8230 #endif /* CKSYSLOG */
8234 #ifdef CK_AUTHENTICATION
8235 && ck_tn_auth_valid() != AUTH_VALID
8236 #endif /* CK_AUTHENTICATION */
8237 ) { /* "ftp" is only account allowed no password */
8239 debug(F110,"zvpass","calling pam_set_item(AUTHTOK)",0);
8240 if ((pam_status = pam_set_item(pamh,PAM_AUTHTOK,p)) != PAM_SUCCESS) {
8241 reply = pam_strerror(pamh, pam_status);
8242 debug(F110,"zvpass PAM failure",reply,0);
8243 /* if no password given treat as non-fatal error */
8244 /* pam will prompt for password in pam_authenticate() */
8246 printf("%s\n",reply);
8248 debug(F100,"zvpass denied","",0);
8254 debug(F110,"zvpass","calling pam_authenticate",0);
8260 Make IKSD authentication (using PAM) ask for a password when an
8261 invalid username has been given, to avoid disclosing which account
8262 names are valid. See #417247 (Debian).
8267 #endif /* CK_LOGIN */
8270 #endif /* COMMENT */
8271 if ((pam_status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
8272 reply = pam_strerror(pamh, pam_status);
8273 debug(F110,"zvpass PAM failure",reply,0);
8274 printf("%s\n",reply);
8276 debug(F100,"zvpass denied","",0);
8283 debug(F110,"zvpass","calling pam_acct_mgmt",0);
8284 if ((pam_status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
8285 reply = pam_strerror(pamh, pam_status);
8286 debug(F110,"zvpass PAM failure",reply,0);
8287 printf("%s\n",reply);
8289 debug(F100,"zvpass denied","",0);
8294 debug(F110,"zvpass","PAM validates OK",0);
8300 salt = pw->pw_passwd;
8302 #ifdef HPUX10_TRUSTED
8303 xpasswd = bigcrypt(p, salt);
8306 On 64-bit platforms this can give "cast to pointer from integer of
8307 different size" warning, but I'm not sure what the effect is at runtime,
8308 or what to do about it.
8310 xpasswd = (char *)crypt(p, salt);
8311 #endif /* HPUX10_TRUSTED */
8315 /* null pw_passwd ok if Kerberos password ok */
8317 ((*pw->pw_passwd != '\0' ||
8318 strcmp(xpasswd, pw->pw_passwd))
8319 && !kpass(pw->pw_name, p))
8322 /* check with tpasswd first if there */
8323 pw == NULL || *pw->pw_passwd == '\0' ||
8324 t_verifypw (pw->pw_name, p) == 0 ||
8325 (t_verifypw (pw->pw_name, p) < 0 &&
8326 strcmp (xpasswd, pw->pw_passwd))
8328 /* The strcmp does not catch null passwords! */
8329 (pw == NULL) || (*pw->pw_passwd == '\0') ||
8330 strcmp(xpasswd, pw->pw_passwd)
8332 #endif /* FTP_KERBEROS */
8334 debug(F100,"zvpass denied","",0);
8342 (VOID) setgid((GID_T)pw->pw_gid); /* Set group ID */
8344 #ifndef NOINITGROUPS
8345 (VOID) initgroups(pw->pw_name, pw->pw_gid);
8346 #endif /* NOINITGROUPS */
8352 /* Open wtmp before chroot */
8354 sprintf(cksysline,"iks_%04x", getpid()); /* safe */
8355 logwtmp(cksysline, pw->pw_name,
8356 clienthost ? clienthost : "(unknown host)"
8358 debug(F110,"WTMP login",cksysline,logged_in);
8362 For anonymous users, we chroot to user ftp's home directory unless
8363 started with --anonroot:xxx, in which case we chroot to xxx. We must
8364 immediately chdir() to the same directory we chroot() to or else the
8365 old current directory remains accessible as "." outside the new root.
8368 if (anonroot) /* Non-default anonymous root */
8371 makestr(&anonroot,dir);
8373 debug(F110,"zvpass anon chroot",dir,0);
8374 if (chroot(dir) < 0) {
8375 debug(F111,"zvpass anon chroot FAILED",dir,errno);
8379 if (chdir("/") < 0) {
8380 debug(F111,"zvpass anon chdir FAILED",dir,errno);
8383 debug(F110,"zvpass anon chroot/chdir OK",dir,0);
8384 } else if (chdir(dir) < 0) { /* Not guest */
8386 if (chdir("/") < 0) {
8387 debug(F110,"Non-guest chdir FAILED",dir,0);
8390 printf("?No directory! Logging in with home=/\n");
8392 debug(F110,"zvpass non-guest chdir FAILED",dir,0);
8393 goto bad; /* Be conservative at first */
8394 #endif /* COMMENT */
8396 debug(F110,"zvpass non-guest chdir OK",dir,0);
8397 if (setuid((UID_T)pw->pw_uid) < 0) {
8398 debug(F101,"zvpass setuid FAILED","",pw->pw_uid);
8401 debug(F101,"zvpass setuid OK","",pw->pw_uid);
8403 guestpass[0] = '\0';
8407 fncact = XYFX_R; /* FILE COLLISION = RENAME */
8408 debug(F110,"GUEST fncact=R",p,0);
8409 lset(guestpass,"anonymous:",10,32);
8410 ckstrncpy(&guestpass[10],p,GUESTPASS-10);
8412 printf("Anonymous login.\r\n");
8415 /* proctitle declared where? Obviously this code is never compiled. */
8416 sprintf(proctitle, "%s: anonymous/%.*s",
8417 clienthost ? clienthost : "(unk)",
8418 sizeof(proctitle) - sizeof(clienthost) -
8419 sizeof(": anonymous/"), p);
8420 setproctitle(proctitle);
8421 #endif /* SETPROCTITLE */
8424 if (ckxsyslog && ckxlogging) {
8426 "login: anonymous %s %s",
8427 clienthost ? clienthost : "(unknown host)",
8431 #endif /* CKSYSLOG */
8433 } else { /* Real user */
8436 ckstrncpy(guestpass,zvuname,GUESTPASS);
8438 printf("User %s logged in.\r\n", pw->pw_name);
8441 sprintf(proctitle, "%s: %s",
8442 clienthost ? clienthost : "(unk)",
8445 setproctitle(proctitle);
8446 #endif /* SETPROCTITLE */
8449 if (ckxsyslog && ckxlogging)
8450 syslog(LOG_INFO, "login: %s %s",
8452 clienthost ? clienthost : "(unknown host)"
8454 #endif /* CKSYSLOG */
8456 ckstrncpy(zvhome,home,CKMAXPATH); /* Set environment variables */
8459 ckmakmsg(zenvuser,ZENVUSER,"USER=",zvuname,NULL,NULL);
8460 putenv((char *)zenvuser);
8461 ckmakmsg(zenvlogname,ZENVLOGNAME,"LOGNAME=",zvuname,NULL,NULL);
8462 putenv((char *)zenvlogname);
8463 ckmakmsg(zenvhome,ZENVHOME,"HOME=",zvhome,NULL,NULL);
8464 putenv((char *)zenvhome);
8465 #endif /* NOPUTENV */
8466 /* homdir = (char *)zvhome; */
8467 ckstrncpy((char *)uidbuf,(char *)zvuname,64);
8468 (VOID) umask(defumask);
8473 extern char dbrec[];
8474 extern unsigned long myflags;
8475 extern unsigned int mydbslot;
8476 extern struct iksdbfld dbfld[];
8477 #ifdef CK_AUTHENTICATION
8478 extern unsigned long myamode, myatype;
8479 #endif /* CK_AUTHENTICATION */
8480 myflags |= DBF_LOGGED;
8483 debug(F101,"zvpass guest","",guest);
8484 debug(F111,"zvpass zvuname",zvuname,0);
8485 debug(F110,"zvpass guestpass",guestpass,0);
8486 debug(F110,"zvpass dir",dir,0);
8487 debug(F110,"zvpass home",home,0);
8488 debug(F110,"zvpass anonroot",anonroot,0);
8491 p2 = guest ? guestpass : zvuname;
8493 p2 = (char *)guestpass;
8494 myflags &= ~DBF_USER;
8496 p2 = (char *)zvuname;
8497 myflags |= DBF_USER;
8500 strncpy(&dbrec[DB_ULEN],ulongtohex((unsigned long)k,4),4);
8501 lset(&dbrec[dbfld[db_USER].off],p2,1024,32);
8502 strncpy(&dbrec[DB_FLAGS],ulongtohex(myflags,4),4);
8503 #ifdef CK_AUTHENTICATION
8504 myamode = ck_tn_auth_valid();
8505 strncpy(&dbrec[DB_AMODE],ulongtohex(myamode,4),4);
8506 myatype = ck_tn_authenticated();
8507 strncpy(&dbrec[DB_ATYPE],ulongtohex(myatype,4),4);
8508 #endif /* CK_AUTHENTICATION */
8514 if (!*p2) p2 = home;
8516 strncpy(&dbrec[DB_DLEN],
8517 ulongtohex((unsigned long)strlen(p2),4),
8520 lset(&dbrec[dbfld[db_DIR].off],p2,1024,32);
8526 bad: /* Common failure exit */
8531 #endif /* CK_LOGIN */
8533 /* Buggy Xenix 2.3.4 cc needs this line after the endif */