apply 010_makefile-destdir-support
[ckermit.git] / ckufio.c
1 /* C K U F I O  --  Kermit file system support for UNIX, Aegis, and Plan 9 */
2
3 #define CK_NONBLOCK                     /* See zoutdump() */
4
5 #ifdef aegis
6 char *ckzv = "Aegis File support, 8.0.200, 4 Mar 2004";
7 #else
8 #ifdef Plan9
9 char *ckzv = "Plan 9 File support, 8.0.200, 4 Mar 2004";
10 #else
11 char *ckzv = "UNIX File support, 8.0.200, 4 Mar 2004";
12 #endif /* Plan9 */
13 #endif /* aegis */
14 /*
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.
19
20   Copyright (C) 1985, 2004,
21     Trustees of Columbia University in the City of New York.
22     All rights reserved.  See the C-Kermit COPYING.TXT file or the
23     copyright text in the ckcmai.c module for disclaimer and permissions.
24 */
25
26 /*
27   NOTE TO CONTRIBUTORS: This file, and all the other C-Kermit files, must be
28   compatible with C preprocessors that support only #ifdef, #else, #endif,
29   #define, and #undef.  Please do not use #if, logical operators, or other
30   preprocessor features in any of the portable C-Kermit modules.  You can,
31   of course, use these constructions in platform-specific modules where you
32   know they are supported.
33 */
34 /* Include Files */
35
36 #ifdef MINIX2
37 #define _MINIX
38 #endif /* MINIX2 */
39
40 #include "ckcsym.h"
41 #include "ckcdeb.h"
42 #include "ckcasc.h"
43
44 #ifndef NOCSETS
45 #include "ckcxla.h"
46 #endif /* NOCSETS */
47
48 #ifdef COMMENT
49 /* This causes trouble in C-Kermit 8.0.  I don't remember the original */
50 /* reason for this being here but it must have been needed at the time... */
51 #ifdef OSF13
52 #ifdef CK_ANSIC
53 #ifdef _NO_PROTO
54 #undef _NO_PROTO
55 #endif /* _NO_PROTO */
56 #endif /* CK_ANSIC */
57 #endif /* OSF13 */
58 #endif /* COMMENT */
59
60 #include <errno.h>
61 #include <signal.h>
62
63 #ifdef MINIX2
64 #undef MINIX
65 #undef CKSYSLOG
66 #include <limits.h>
67 #include <time.h>
68 #define NOFILEH
69 #endif /* MINIX2 */
70
71 #ifdef MINIX
72 #include <limits.h>
73 #include <sys/types.h>
74 #include <time.h>
75 #else
76 #ifdef POSIX
77 #include <limits.h>
78 #else
79 #ifdef SVR3
80 #include <limits.h>
81 #endif /* SVR3 */
82 #endif /* POSIX */
83 #endif /* MINIX */
84 /*
85   Directory Separator macros, to allow this module to work with both UNIX and
86   OS/2: Because of ambiguity with the command line editor escape \ character,
87   the directory separator is currently left as / for OS/2 too, because the
88   OS/2 kernel also accepts / as directory separator.  But this is subject to
89   change in future versions to conform to the normal OS/2 style.
90 */
91 #ifndef DIRSEP
92 #define DIRSEP       '/'
93 #endif /* DIRSEP */
94 #ifndef ISDIRSEP
95 #define ISDIRSEP(c)  ((c)=='/')
96 #endif /* ISDIRSEP */
97
98 #ifdef SDIRENT
99 #define DIRENT
100 #endif /* SDIRENT */
101
102 #ifdef XNDIR
103 #include <sys/ndir.h>
104 #else /* !XNDIR */
105 #ifdef NDIR
106 #include <ndir.h>
107 #else /* !NDIR, !XNDIR */
108 #ifdef RTU
109 #include "/usr/lib/ndir.h"
110 #else /* !RTU, !NDIR, !XNDIR */
111 #ifdef DIRENT
112 #ifdef SDIRENT
113 #include <sys/dirent.h>
114 #else
115 #include <dirent.h>
116 #endif /* SDIRENT */
117 #else
118 #include <sys/dir.h>
119 #endif /* DIRENT */
120 #endif /* RTU */
121 #endif /* NDIR */
122 #endif /* XNDIR */
123
124 #ifdef UNIX                             /* Pointer arg to wait() allowed */
125 #define CK_CHILD                        /* Assume this is safe in all UNIX */
126 #endif /* UNIX */
127
128 extern int binary, recursive, stathack;
129 #ifdef CK_CTRLZ
130 extern int eofmethod;
131 #endif /* CK_CTRLZ */
132
133 #include <pwd.h>                        /* Password file for shell name */
134 #ifdef CK_SRP
135 #include <t_pwd.h>                      /* SRP Password file */
136 #endif /* CK_SRP */
137
138 #ifdef HPUX10_TRUSTED
139 #include <hpsecurity.h>
140 #include <prot.h>
141 #endif /* HPUX10_TRUSTED */
142
143 #ifdef COMMENT
144 /* Moved to ckcdeb.h */
145 #ifdef POSIX
146 #define UTIMEH
147 #else
148 #ifdef HPUX9
149 #define UTIMEH
150 #endif /* HPUX9 */
151 #endif /* POSIX */
152 #endif /* COMMENT */
153
154 #ifdef SYSUTIMEH                        /* <sys/utime.h> if requested,  */
155 #include <sys/utime.h>                  /* for extra fields required by */
156 #else                                   /* 88Open spec. */
157 #ifdef UTIMEH                           /* or <utime.h> if requested */
158 #include <utime.h>                      /* (SVR4, POSIX) */
159 #ifndef BSD44
160 #ifndef V7
161 /* Not sure why this is here.  What it implies is that the code bracketed
162    by SYSUTIMEH is valid on all platforms on which we support time 
163    functionality.  But we know that is not true because the BSD44 and V7
164    platforms do not support sys/utime.h and the data structures which
165    are defined in them.  Now this worked before because prior to today's
166    changes the UTIMEH definition for BSD44 and V7 did not take place
167    until after SYSUTIMEH was defined.  It also would not have been a 
168    problem if the ordering of all the time blocks was consistent.  All but
169    one of the blocks were BSD44, V7, SYSUTIMEH, <OTHER>.  That one case
170    is where this problem was triggered.
171 */
172 #define SYSUTIMEH                       /* Use this for both cases. */
173 #endif /* V7 */
174 #endif /* BSD44 */
175 #endif /* UTIMEH */
176 #endif /* SYSUTIMEH */
177
178 #ifndef NOTIMESTAMP
179 #ifdef POSIX
180 #ifndef AS400
181 #define TIMESTAMP
182 #endif /* AS400 */
183 #endif /* POSIX */
184
185 #ifdef BSD44                            /* BSD 4.4 */
186 #ifndef TIMESTAMP
187 #define TIMESTAMP                       /* Can do file dates */
188 #endif /* TIMESTAMP */
189 #include <sys/time.h>
190 #include <sys/timeb.h>
191
192 #else  /* Not BSD44 */
193
194 #ifdef BSD4                             /* BSD 4.3 and below */
195 #define TIMESTAMP                       /* Can do file dates */
196 #include <time.h>                       /* Need this */
197 #include <sys/timeb.h>                  /* Need this if really BSD */
198
199 #else  /* Not BSD 4.3 and below */
200
201 #ifdef SVORPOSIX                        /* System V or POSIX */
202 #ifndef TIMESTAMP
203 #define TIMESTAMP
204 #endif /* TIMESTAMP */
205 #include <time.h>
206
207 /* void tzset(); (the "void" type upsets some compilers) */
208 #ifndef IRIX60
209 #ifndef ultrix
210 #ifndef CONVEX9
211 /* ConvexOS 9.0, supposedly POSIX, has extern char *timezone(int,int) */
212 #ifndef Plan9
213 extern long timezone;
214 #endif /* Plan9 */
215 #endif /* CONVEX9 */
216 #endif /* ultrix */
217 #endif /* IRIX60 */
218 #endif /* SVORPOSIX */
219 #endif /* BSD4 */
220 #endif /* BSD44 */
221
222 #ifdef COHERENT
223 #include <time.h>
224 #endif /* COHERENT */
225
226 /* Is `y' a leap year? */
227 #define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
228
229 /* Number of leap years from 1970 to `y' (not including `y' itself). */
230 #define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
231
232 #endif /* NOTIMESTAMP */
233
234 #ifdef CIE
235 #include <stat.h>                       /* File status */
236 #else
237 #include <sys/stat.h>
238 #endif /* CIE */
239
240 /* Macro to alleviate isdir() calls internal to this module */
241
242 static struct stat STATBUF;
243 #define xisdir(a) ((stat(a,&STATBUF)==-1)?0:(S_ISDIR(STATBUF.st_mode)?1:0))
244
245 extern char uidbuf[];
246 extern int xferlog;
247 extern char * xferfile;
248 int iklogopen = 0;
249 static time_t timenow;
250
251 #define IKSDMSGLEN CKMAXPATH+512
252
253 static char iksdmsg[IKSDMSGLEN];
254
255 extern int local;
256
257 extern int server, en_mkd, en_cwd, en_del;
258
259 /*
260   Functions (n is one of the predefined file numbers from ckcker.h):
261
262    zopeni(n,name)   -- Opens an existing file for input.
263    zopeno(n,name,attr,fcb) -- Opens a new file for output.
264    zclose(n)        -- Closes a file.
265    zchin(n,&c)      -- Gets the next character from an input file.
266    zsinl(n,&s,x)    -- Read a line from file n, max len x, into address s.
267    zsout(n,s)       -- Write a null-terminated string to output file, buffered.
268    zsoutl(n,s)      -- Like zsout, but appends a line terminator.
269    zsoutx(n,s,x)    -- Write x characters to output file, unbuffered.
270    zchout(n,c)      -- Add a character to an output file, unbuffered.
271    zchki(name)      -- Check if named file exists and is readable, return size.
272    zchko(name)      -- Check if named file can be created.
273    zchkspa(name,n)  -- Check if n bytes available to create new file, name.
274    znewn(name,s)    -- Make a new unique file name based on the given name.
275    zdelet(name)     -- Delete the named file.
276    zxpand(string)   -- Expands the given wildcard string into a list of files.
277    znext(string)    -- Returns the next file from the list in "string".
278    zxrewind()       -- Rewind zxpand list.
279    zxcmd(n,cmd)     -- Execute the command in a lower fork on file number n.
280    zclosf()         -- Close input file associated with zxcmd()'s lower fork.
281    zrtol(n1,n2)     -- Convert remote filename into local form.
282    zltor(n1,n2)     -- Convert local filename into remote form.
283    zchdir(dirnam)   -- Change working directory.
284    zhome()          -- Return pointer to home directory name string.
285    zkself()         -- Kill self, log out own job.
286    zsattr(struct zattr *) -- Return attributes for file which is being sent.
287    zstime(f, struct zattr *, x) - Set file creation date from attribute packet.
288    zrename(old, new) -- Rename a file.
289    zcopy(source,destination) -- Copy a file.
290    zmkdir(path)       -- Create the directory path if possible
291    zfnqfp(fname,len,fullpath) - Determine full path for file name.
292    zgetfs(name)     -- return file size regardless of accessibility
293    zchkpid(pid)     -- tell if PID is valid and active
294 */
295
296 /* Kermit-specific includes */
297 /*
298   Definitions here supersede those from system include files.
299   ckcdeb.h is included above.
300 */
301 #include "ckcker.h"                     /* Kermit definitions */
302 #include "ckucmd.h"                     /* For keyword tables */
303 #include "ckuver.h"                     /* Version herald */
304
305 char *ckzsys = HERALD;
306
307 /*
308   File access checking ...  There are two calls to access() in this module.
309   If this program is installed setuid or setgid on a Berkeley-based UNIX
310   system that does NOT incorporate the saved-original-effective-uid/gid
311   feature, then, when we have swapped the effective and original uid/gid,
312   access() fails because it uses what it thinks are the REAL ids, but we have
313   swapped them.  This occurs on systems where ANYBSD is defined, NOSETREU
314   is NOT defined, and SAVEDUID is NOT defined.  So, in theory, we should take
315   care of this situation like so:
316
317     ifdef ANYBSD
318     ifndef NOSETREU
319     ifndef SAVEDUID
320     define SW_ACC_ID
321     endif
322     endif
323     endif
324
325   But we can't test such a general scheme everywhere, so let's only do this
326   when we know we have to...
327 */
328 #ifdef NEXT                             /* NeXTSTEP 1.0-3.0 */
329 #define SW_ACC_ID
330 #endif /* NEXT */
331
332 /* Support for tilde-expansion in file and directory names */
333
334 #ifdef POSIX
335 #define NAMEENV "LOGNAME"
336 #else
337 #ifdef BSD4
338 #define NAMEENV "USER"
339 #else
340 #ifdef ATTSV
341 #define NAMEENV "LOGNAME"
342 #endif /* ATTSV */
343 #endif /* BSD4 */
344 #endif /* POSIX */
345
346 /* Berkeley Unix Version 4.x */
347 /* 4.1bsd support from Charles E Brooks, EDN-VAX */
348
349 #ifdef BSD4
350 #ifdef MAXNAMLEN
351 #define BSD42
352 #endif /* MAXNAMLEN */
353 #endif /* BSD4 */
354
355 /* Definitions of some system commands */
356
357 char *DELCMD = "rm -f ";                /* For file deletion */
358 char *CPYCMD = "cp ";                   /* For file copy */
359 char *RENCMD = "mv ";                   /* For file rename */
360 char *PWDCMD = "pwd ";                  /* For saying where I am */
361
362 #ifdef COMMENT
363 #ifdef HPUX10
364 char *DIRCMD = "/usr/bin/ls -l ";       /* For directory listing */
365 char *DIRCM2 = "/usr/bin/ls -l ";       /* For directory listing, no args */
366 #else
367 char *DIRCMD = "/bin/ls -l ";           /* For directory listing */
368 char *DIRCM2 = "/bin/ls -l ";           /* For directory listing, no args */
369 #endif /* HPUX10 */
370 #else
371 char *DIRCMD = "ls -l ";                /* For directory listing */
372 char *DIRCM2 = "ls -l ";                /* For directory listing, no args */
373 #endif /* COMMENT */
374
375 char *TYPCMD = "cat ";                  /* For typing a file */
376
377 #ifdef HPUX
378 char *MAILCMD = "mailx";                /* For sending mail */
379 #else
380 #ifdef DGUX540
381 char *MAILCMD = "mailx";
382 #else
383 #ifdef UNIX
384 #ifdef CK_MAILCMD
385 char *MAILCMD = CK_MAILCMD;             /* CFLAGS override */
386 #else
387 char *MAILCMD = "Mail";                 /* Default */
388 #endif /* CK_MAILCMD */
389 #else
390 char *MAILCMD = "";
391 #endif /* UNIX */
392 #endif /* HPUX */
393 #endif /* DGUX40 */
394
395 #ifdef UNIX
396 #ifdef ANYBSD                           /* BSD uses lpr to spool */
397 #ifdef DGUX540                          /* And DG/UX */
398 char * PRINTCMD = "lp";
399 #else
400 char * PRINTCMD = "lpr";
401 #endif /* DGUX540 */
402 #else                                   /* Sys V uses lp */
403 #ifdef TRS16                            /* except for Tandy-16/6000... */
404 char * PRINTCMD = "lpr";
405 #else
406 char * PRINTCMD = "lp";
407 #endif /* TRS16 */
408 #endif /* ANYBSD */
409 #else  /* Not UNIX */
410 #define PRINTCMD ""
411 #endif /* UNIX */
412
413 #ifdef FT18                             /* Fortune For:Pro 1.8 */
414 #undef BSD4
415 #endif /* FT18 */
416
417 #ifdef BSD4
418 char *SPACMD = "pwd ; df .";            /* Space in current directory */
419 #else
420 #ifdef FT18
421 char *SPACMD = "pwd ; du ; df .";
422 #else
423 char *SPACMD = "df ";
424 #endif /* FT18 */
425 #endif /* BSD4 */
426
427 char *SPACM2 = "df ";                   /* For space in specified directory */
428
429 #ifdef FT18
430 #define BSD4
431 #endif /* FT18 */
432
433 #ifdef BSD4
434 char *WHOCMD = "finger ";
435 #else
436 char *WHOCMD = "who ";
437 #endif /* BSD4 */
438
439 /* More system-dependent includes, which depend on symbols defined */
440 /* in the Kermit-specific includes.  Oh what a tangled web we weave... */
441
442 #ifdef COHERENT                         /* <sys/file.h> */
443 #define NOFILEH
444 #endif /* COHERENT */
445
446 #ifdef MINIX
447 #define NOFILEH
448 #endif /* MINIX */
449
450 #ifdef aegis
451 #define NOFILEH
452 #endif /* aegis */
453
454 #ifdef unos
455 #define NOFILEH
456 #endif /* unos */
457
458 #ifndef NOFILEH
459 #include <sys/file.h>
460 #endif /* NOFILEH */
461
462 #ifndef is68k                           /* Whether to include <fcntl.h> */
463 #ifndef BSD41                           /* All but a couple UNIXes have it. */
464 #ifndef FT18
465 #ifndef COHERENT
466 #include <fcntl.h>
467 #endif /* COHERENT */
468 #endif /* FT18  */
469 #endif /* BSD41 */
470 #endif /* is68k */
471
472 #ifdef COHERENT
473 #ifdef _I386
474 #include <fcntl.h>
475 #else
476 #include <sys/fcntl.h>
477 #endif /* _I386 */
478 #endif /* COHERENT */
479
480 extern int inserver;                    /* I am IKSD */
481 int guest = 0;                          /* Anonymous user */
482
483 #ifdef IKSD
484 extern int isguest;
485 extern char * anonroot;
486 #endif /* IKSD */
487
488 #ifdef CK_LOGIN
489 #define GUESTPASS 256
490 static char guestpass[GUESTPASS] = { NUL, NUL }; /* Anonymous "password" */
491 static int logged_in = 0;               /* Set when user is logged in */
492 static int askpasswd = 0;               /* Have OK user, must ask for passwd */
493 #endif /* CK_LOGIN */
494
495 #ifdef CKROOT
496 static char ckroot[CKMAXPATH+1] = { NUL, NUL };
497 static int ckrootset = 0;
498 int ckrooterr = 0;
499 #endif /* CKROOT */
500
501 _PROTOTYP( VOID ignorsigs, (void) );
502 _PROTOTYP( VOID restorsigs, (void) );
503
504 /*
505   Change argument to "(const char *)" if this causes trouble.
506   Or... if it causes trouble, then maybe it was already declared
507   in a header file after all, so you can remove this prototype.
508 */
509 #ifndef NDGPWNAM /* If not defined No Declare getpwnam... */
510 #ifndef _POSIX_SOURCE
511 #ifndef NEXT
512 #ifndef SVR4
513 /* POSIX <pwd.h> already gave prototypes for these. */
514 #ifdef IRIX40
515 _PROTOTYP( struct passwd * getpwnam, (const char *) );
516 #else
517 #ifdef IRIX51
518 _PROTOTYP( struct passwd * getpwnam, (const char *) );
519 #else
520 #ifdef M_UNIX
521 _PROTOTYP( struct passwd * getpwnam, (const char *) );
522 #else
523 #ifdef HPUX9
524 _PROTOTYP( struct passwd * getpwnam, (const char *) );
525 #else
526 #ifdef HPUX10
527 _PROTOTYP( struct passwd * getpwnam, (const char *) );
528 #else
529 #ifdef DCGPWNAM
530 _PROTOTYP( struct passwd * getpwnam, (const char *) );
531 #else
532 _PROTOTYP( struct passwd * getpwnam, (char *) );
533 #endif /* DCGPWNAM */
534 #endif /* HPUX10 */
535 #endif /* HPUX9 */
536 #endif /* M_UNIX */
537 #endif /* IRIX51 */
538 #endif /* IRIX40 */
539 #ifndef SUNOS4
540 #ifndef HPUX9
541 #ifndef HPUX10
542 #ifndef _SCO_DS
543 _PROTOTYP( struct passwd * getpwuid, (PWID_T) );
544 #endif /* _SCO_DS */
545 #endif /* HPUX10 */
546 #endif /* HPUX9 */
547 #endif /* SUNOS4 */
548 _PROTOTYP( struct passwd * getpwent, (void) );
549 #endif /* SVR4 */
550 #endif /* NEXT */
551 #endif /* _POSIX_SOURCE */
552 #endif /* NDGPWNAM */
553
554 #ifdef CK_SHADOW                        /* Shadow Passwords... */
555 #include <shadow.h>
556 #endif /* CK_SHADOW */
557 #ifdef CK_PAM                           /* PAM... */
558 #include <security/pam_appl.h>
559 #ifndef PAM_SERVICE_TYPE                /* Defines which PAM service we are */
560 #define PAM_SERVICE_TYPE "kermit"
561 #endif /* PAM_SERVICE_TYPE */
562
563 #ifdef SOLARIS
564 #define PAM_CONST 
565 #else /* SOLARIS */
566 #define PAM_CONST CONST
567 #endif 
568
569 static char * pam_pw = NULL;
570
571 int
572 #ifdef CK_ANSIC
573 pam_cb(int num_msg,
574        PAM_CONST struct pam_message **msg,
575        struct pam_response **resp,
576        void *appdata_ptr
577        )
578 #else /* CK_ANSIC */
579 pam_cb(num_msg, msg, resp, appdata_ptr)
580     int num_msg;
581     PAM_CONST struct pam_message **msg;
582     struct pam_response **resp;
583     void *appdata_ptr;
584 #endif /* CK_ANSIC */
585 {
586     int i;
587
588     debug(F111,"pam_cb","num_msg",num_msg);
589
590     for (i = 0; i < num_msg; i++) {
591         char message[PAM_MAX_MSG_SIZE];
592
593         /* Issue prompt and get response */
594         debug(F111,"pam_cb","Message",i);
595         debug(F111,"pam_cb",msg[i]->msg,msg[i]->msg_style);
596         if (msg[i]->msg_style == PAM_ERROR_MSG) {
597             debug(F111,"pam_cb","PAM ERROR",0);
598             fprintf(stdout,"%s\n", msg[i]->msg);
599             return(0);
600         } else if (msg[i]->msg_style == PAM_TEXT_INFO) {
601             debug(F111,"pam_cb","PAM TEXT INFO",0);
602             fprintf(stdout,"%s\n", msg[i]->msg);
603             return(0);
604         } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) {
605             debug(F111,"pam_cb","Reading response, no echo",0);
606             /* Ugly hack.  We check to see if a password has been pushed */
607             /* into zvpasswd().  This would be true if the password was  */
608             /* received by REMOTE LOGIN.                                 */
609             if (pam_pw) {
610                 ckstrncpy(message,pam_pw,PAM_MAX_MSG_SIZE);
611             } else
612                 readpass((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
613         } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON) {
614             debug(F111,"pam_cb","Reading response, with echo",0);
615             readtext((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
616         } else {
617             debug(F111,"pam_cb","unknown style",0);
618             return(0);
619         }
620
621         /* Allocate space for this message's response structure */
622         resp[i] = (struct pam_response *) malloc(sizeof (struct pam_response));
623         if (!resp[i]) {
624             int j;
625             debug(F110,"pam_cb","malloc failure",0);
626             for (j = 0; j < i; j++) {
627                 free(resp[j]->resp);
628                 free(resp[j]);
629             }
630             return(0);
631         }
632
633         /* Allocate a buffer for the response */
634         resp[i]->resp = (char *) malloc((int)strlen(message) + 1);
635         if (!resp[i]->resp) {
636             int j;
637             debug(F110,"pam_cb","malloc failure",0);
638             for (j = 0; j < i; j++) {
639                 free(resp[j]->resp);
640                 free(resp[j]);
641             }
642             free(resp[i]);
643             return(0);
644         }
645         /* Return the results back to PAM */
646         strcpy(resp[i]->resp, message); /* safe (prechecked) */
647         resp[i]->resp_retcode = 0;
648     }
649     debug(F110,"pam_cb","Exiting",0);
650     return(0);
651 }
652 #endif /* CK_PAM */
653
654 /* Define macros for getting file type */
655
656 #ifdef OXOS
657 /*
658   Olivetti X/OS 2.3 has S_ISREG and S_ISDIR defined
659   incorrectly, so we force their redefinition.
660 */
661 #undef S_ISREG
662 #undef S_ISDIR
663 #endif /* OXOS */
664
665 #ifdef UTSV                             /* Same deal for Amdahl UTSV */
666 #undef S_ISREG
667 #undef S_ISDIR
668 #endif /* UTSV */
669
670 #ifdef UNISYS52                         /* And for UNISYS UTS V 5.2 */
671 #undef S_ISREG
672 #undef S_ISDIR
673 #endif /* UNISYS52 */
674
675 #ifdef ICLSVR3                          /* And for old ICL versions */
676 #undef S_ISREG
677 #undef S_ISDIR
678 #endif /* ICLSVR3 */
679
680 #ifdef ISDIRBUG                         /* Also allow this from command line */
681 #ifdef S_ISREG
682 #undef S_ISREG
683 #endif /* S_ISREG */
684 #ifdef S_ISDIR
685 #undef S_ISDIR
686 #endif /*  S_ISDIR */
687 #endif /* ISDIRBUG */
688
689 #ifndef _IFMT
690 #ifdef S_IFMT
691 #define _IFMT S_IFMT
692 #else
693 #define _IFMT 0170000
694 #endif /* S_IFMT */
695 #endif /* _IFMT */
696
697 #ifndef S_ISREG
698 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
699 #endif /* S_ISREG */
700 #ifndef S_ISDIR
701 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
702 #endif /* S_ISDIR */
703
704 /* The following mainly for NeXTSTEP... */
705
706 #ifndef S_IWUSR
707 #define S_IWUSR 0000200
708 #endif /* S_IWUSR */
709
710 #ifndef S_IRGRP
711 #define S_IRGRP 0000040
712 #endif /* S_IRGRP */
713
714 #ifndef S_IWGRP
715 #define S_IWGRP 0000020
716 #endif /* S_IWGRP */
717
718 #ifndef S_IXGRP
719 #define S_IXGRP 0000010
720 #endif /* S_IXGRP */
721
722 #ifndef S_IROTH
723 #define S_IROTH 0000004
724 #endif /* S_IROTH */
725
726 #ifndef S_IWOTH
727 #define S_IWOTH 0000002
728 #endif /* S_IWOTH */
729
730 #ifndef S_IXOTH
731 #define S_IXOTH 0000001
732 #endif /* S_IXOTH */
733 /*
734   Define maximum length for a file name if not already defined.
735   NOTE: This applies to a path segment (directory or file name),
736   not the entire path string, which can be CKMAXPATH bytes long.
737 */
738 #ifdef QNX
739 #ifdef _MAX_FNAME
740 #define MAXNAMLEN _MAX_FNAME
741 #else
742 #define MAXNAMLEN 48
743 #endif /* _MAX_FNAME */
744 #else
745 #ifndef MAXNAMLEN
746 #ifdef sun
747 #define MAXNAMLEN 255
748 #else
749 #ifdef FILENAME_MAX
750 #define MAXNAMLEN FILENAME_MAX
751 #else
752 #ifdef NAME_MAX
753 #define MAXNAMLEN NAME_MAX
754 #else
755 #ifdef _POSIX_NAME_MAX
756 #define MAXNAMLEN _POSIX_NAME_MAX
757 #else
758 #ifdef _D_NAME_MAX
759 #define MAXNAMLEN _D_NAME_MAX
760 #else
761 #ifdef DIRSIZ
762 #define MAXNAMLEN DIRSIZ
763 #else
764 #define MAXNAMLEN 14
765 #endif /* DIRSIZ */
766 #endif /* _D_NAME_MAX */
767 #endif /* _POSIX_NAME_MAX */
768 #endif /* NAME_MAX */
769 #endif /* FILENAME_MAX */
770 #endif /* sun */
771 #endif /* MAXNAMLEN */
772 #endif /* QNX */
773
774 #ifdef COMMENT
775 /* As of 2001-11-03 this is handled in ckcdeb.h */
776 /* Longest pathname ... */
777 /*
778   Beware: MAXPATHLEN is one of UNIX's dirty little secrets.  Where is it
779   defined?  Who knows...  <param.h>, <mod.h>, <unistd.h>, <limits.h>, ...
780   There is not necessarily even a definition for it anywhere, or it might have
781   another name.  If you get it wrong, bad things happen with getcwd() and/or
782   getwd().  If you allocate a buffer that is too short, getwd() might write
783   over memory and getcwd() will fail with ERANGE.  The definitions of these
784   functions (e.g. in SVID or POSIX.1) do not tell you how to determine the
785   maximum path length in order to allocate a buffer that is the right size.
786 */
787 #ifdef BSD44
788 #include <sys/param.h>                  /* For MAXPATHLEN */
789 #endif /* BSD44 */
790 #ifdef COHERENT
791 #include <sys/param.h>  /* for MAXPATHLEN, needed for -DDIRENT */
792 #endif /* COHERENT */
793 #endif /* COMMENT */
794
795 #ifdef MAXPATHLEN
796 #ifdef MAXPATH
797 #undef MAXPATH
798 #endif /* MAXPATH */
799 #define MAXPATH MAXPATHLEN
800 #else
801 #ifdef PATH_MAX
802 #define MAXPATH PATH_MAX
803 #else
804 #ifdef _POSIX_PATH_MAX
805 #define MAXPATH _POSIX_PATH_MAX
806 #else
807 #ifdef BSD42
808 #define MAXPATH 1024
809 #else
810 #ifdef SVR4
811 #define MAXPATH 1024
812 #else
813 #define MAXPATH 255
814 #endif /* SVR4 */
815 #endif /* BSD42 */
816 #endif /* _POSIX_PATH_MAX */
817 #endif /* PATH_MAX */
818 #endif /* MAXPATHLEN */
819
820 /* Maximum number of filenames for wildcard expansion */
821
822 #ifndef MAXWLD
823 /* Already defined in ckcdeb.h so the following is superfluous. */
824 /* Don't expect changing them to have any effect. */
825 #ifdef CK_SMALL
826 #define MAXWLD 50
827 #else
828 #ifdef BIGBUFOK
829 #define MAXWLD 102400
830 #else
831 #define MAXWLD 8192
832 #endif /* BIGBUFOK */
833 #endif /* CK_SMALL */
834 #endif /* MAXWLD */
835
836 static int maxnames = MAXWLD;
837
838 /* Define the size of the string space for filename expansion. */
839
840 #ifndef DYNAMIC
841 #ifdef PROVX1
842 #define SSPACE 500
843 #else
844 #ifdef BSD29
845 #define SSPACE 500
846 #else
847 #ifdef pdp11
848 #define SSPACE 500
849 #else
850 #ifdef aegis
851 #define SSPACE 10000                    /* Size of string-generating buffer */
852 #else                                   /* Default static buffer size */
853 #ifdef BIGBUFOK
854 #define SSPACE 65000                    /* Size of string-generating buffer */
855 #else
856 #define SSPACE 2000                     /* size of string-generating buffer */
857 #endif /* BIGBUFOK */
858 #endif /* aegis */
859 #endif /* pdp11 */
860 #endif /* BSD29 */
861 #endif /* PROVX1 */
862 static char sspace[SSPACE];             /* Buffer for generating filenames */
863 #else /* is DYNAMIC */
864 #ifdef BIGBUFOK
865 #define SSPACE 500000
866 #else
867 #define SSPACE 10000
868 #endif /* BIGBUFOK */
869 char *sspace = (char *)0;
870 #endif /* DYNAMIC */
871 static int ssplen = SSPACE;             /* Length of string space buffer */
872
873 #ifdef DCLFDOPEN
874 /* fdopen() needs declaring because it's not declared in <stdio.h> */
875 _PROTOTYP( FILE * fdopen, (int, char *) );
876 #endif /* DCLFDOPEN */
877
878 #ifdef DCLPOPEN
879 /* popen() needs declaring because it's not declared in <stdio.h> */
880 _PROTOTYP( FILE * popen, (char *, char *) );
881 #endif /* DCLPOPEN */
882
883 extern int nopush;
884
885 /* More internal function prototypes */
886 /*
887  * The path structure is used to represent the name to match.
888  * Each slash-separated segment of the name is kept in one
889  * such structure, and they are linked together, to make
890  * traversing the name easier.
891  */
892 struct path {
893     char npart[MAXNAMLEN+4];            /* name part of path segment */
894     struct path *fwd;                   /* forward ptr */
895 };
896 #ifndef NOPUSH
897 _PROTOTYP( int shxpand, (char *, char *[], int ) );
898 #endif /* NOPUSH */
899 _PROTOTYP( static int fgen, (char *, char *[], int ) );
900 _PROTOTYP( static VOID traverse, (struct path *, char *, char *) );
901 _PROTOTYP( static VOID addresult, (char *, int) );
902 #ifdef COMMENT
903 /* Replaced by ckmatch() */
904 _PROTOTYP( static int match, (char *, char *) );
905 #endif /* COMMENT */
906 _PROTOTYP( char * whoami, (void) );
907 _PROTOTYP( UID_T real_uid, (void) );
908 _PROTOTYP( static struct path *splitpath, (char *p) );
909 _PROTOTYP( char * zdtstr, (time_t) );
910 _PROTOTYP( time_t zstrdt, (char *, int) );
911
912 /* Some systems define these symbols in include files, others don't... */
913
914 #ifndef R_OK
915 #define R_OK 4                          /* For access */
916 #endif /* R_OK */
917
918 #ifndef W_OK
919 #define W_OK 2
920 #endif /* W_OK */
921
922 #ifndef X_OK
923 #define X_OK 1
924 #endif /* X_OK */
925
926 #ifndef O_RDONLY
927 #define O_RDONLY 000
928 #endif /* O_RDONLY */
929
930 /* syslog and wtmp items for Internet Kermit Service */
931
932 extern char * clienthost;               /* From ckcmai.c. */
933
934 static char fullname[CKMAXPATH+1];
935 static char tmp2[CKMAXPATH+1];
936
937 extern int ckxlogging;
938
939 #ifdef CKXPRINTF                        /* Our printf macro conflicts with */
940 #undef printf                           /* use of "printf" in syslog.h */
941 #endif /* CKXPRINTF */
942 #ifdef CKSYSLOG
943 #ifdef RTAIX
944 #include <sys/syslog.h>
945 #else  /* RTAIX */
946 #include <syslog.h>
947 #endif /* RTAIX */
948 #endif /* CKSYSLOG */
949 #ifdef CKXPRINTF
950 #define printf ckxprintf
951 #endif /* CKXPRINTF */
952
953 int ckxanon = 1;                        /* Anonymous login ok */
954 int ckxperms = 0040;                    /* Anonymous file permissions */
955 int ckxpriv = 1;                        /* Priv'd login ok */
956
957 #ifndef XFERFILE
958 #define XFERFILE "/var/log/iksd.log"
959 #endif /* XFERFILE */
960
961 /* wtmp logging for IKSD... */
962
963 #ifndef CKWTMP                          /* wtmp logging not selected */
964 int ckxwtmp = 0;                        /* Know this at runtime */
965 #else                                   /* wtmp file details */
966 int ckxwtmp = 1;
967 #ifdef UTMPBUG                          /* Unfortunately... */
968 /*
969   Some versions of Linux have a <utmp.h> file that contains
970   "enum utlogin { local, telnet, rlogin, screen, ... };"  This clobbers
971   any program that uses any of these words as variable names, function
972   names, macro names, etc.  (Other versions of Linux have this declaration
973   within #if 0 ... #endif.)  There is nothing we can do about this other
974   than to not include the stupid file.  But we need stuff from it, so...
975 */
976 #include <features.h>
977 #include <sys/types.h>
978 #define UT_LINESIZE     32
979 #define UT_NAMESIZE     32
980 #define UT_HOSTSIZE     256
981
982 struct timeval {
983   time_t tv_sec;
984   time_t tv_usec;
985 };
986
987 struct exit_status {
988   short int e_termination;      /* Process termination status.  */
989   short int e_exit;             /* Process exit status.  */
990 };
991
992 struct utmp {
993   short int ut_type;                    /* Type of login */
994   pid_t ut_pid;                         /* Pid of login process */
995   char ut_line[UT_LINESIZE];            /* NUL-terminated devicename of tty */
996   char ut_id[4];                        /* Inittab id */
997   char ut_user[UT_NAMESIZE];            /* Username (not NUL terminated) */
998
999   char ut_host[UT_HOSTSIZE];            /* Hostname for remote login */
1000   struct exit_status ut_exit;           /* Exit status */
1001   long ut_session;                      /* Session ID, used for windowing */
1002   struct timeval ut_tv;                 /* Time entry was made */
1003   int32_t ut_addr_v6[4];                /* Internet address of remote host */
1004   char pad[20];                         /* Reserved */
1005 };
1006
1007 #define ut_time ut_tv.tv_sec    /* Why should Linux be like anything else? */
1008 #define ut_name ut_user         /* ... */
1009
1010 extern void
1011 logwtmp __P ((__const char *__ut_line, __const char *__ut_name,
1012                           __const char *__ut_host));
1013
1014 #else  /* Not UTMPBUG */
1015
1016 #ifndef HAVEUTMPX                       /* Who has <utmpx.h> */
1017 #ifdef SOLARIS
1018 #define HAVEUTMPX
1019 #else
1020 #ifdef IRIX60
1021 #define HAVEUTMPX
1022 #else
1023 #ifdef CK_SCOV5
1024 #define HAVEUTMPX
1025 #else
1026 #ifdef HPUX100
1027 #define HAVEUTMPX
1028 #else
1029 #ifdef UNIXWARE
1030 #define HAVEUTMPX
1031 #endif /* UNIXWARE */
1032 #endif /* HPUX100 */
1033 #endif /* CK_SCOV5 */
1034 #endif /* IRIX60 */
1035 #endif /* SOLARIS */
1036 #endif /* HAVEUTMPX */
1037 #ifdef HAVEUTMPX
1038 #include <utmpx.h>
1039 #else
1040 #ifdef OSF50
1041 /* Because the time_t in the utmp struct is 64 bits but time() wants 32 */
1042 #define __V40_OBJ_COMPAT 1
1043 #endif /* OSF50 */
1044 #include <utmp.h>
1045 #ifdef OSF50
1046 #undef __V40_OBJ_COMPAT
1047 #endif /* OSF50 */
1048 #endif /* HAVEUTMPX */
1049 #endif /* UTMPBUG */
1050
1051 #ifndef WTMPFILE
1052 #ifdef QNX
1053 #define WTMPFILE "/usr/adm/wtmp.1"
1054 #else
1055 #ifdef LINUX
1056 #define WTMPFILE "/var/log/wtmp"
1057 #else
1058 #define WTMPFILE "/usr/adm/wtmp"
1059 #endif /* QNX */
1060 #endif /* LINUX */
1061 #endif /* WTMPFILE */
1062 char * wtmpfile = NULL;
1063
1064 static int wtmpfd = 0;
1065 static char cksysline[32] = { NUL, NUL };
1066
1067 #ifndef HAVEUTHOST                      /* Does utmp include ut_host[]? */
1068 #ifdef HAVEUTMPX                        /* utmpx always does */
1069 #define HAVEUTHOST
1070 #else
1071 #ifdef LINUX                            /* Linux does */
1072 #define HAVEUTHOST
1073 #else
1074 #ifdef SUNOS4                           /* SunOS does */
1075 #define HAVEUTHOST
1076 #else
1077 #ifdef AIX41                            /* AIX 4.1 and later do */
1078 #define HAVEUTHOST
1079 #endif /* AIX41 */
1080 #endif /* SUNOS4 */
1081 #endif /* LINUX */
1082 #endif /* HAVEUTMPX */
1083 #endif /* HAVEUTHOST */
1084
1085 #ifdef UW200
1086 PID_T _vfork() {                        /* To satisfy a library foulup */
1087     return(fork());                     /* in Unixware 2.0.x */
1088 }
1089 #endif /* UW200 */
1090
1091 VOID
1092 #ifdef CK_ANSIC
1093 logwtmp(const char * line, const char * name, const char * host)
1094 #else
1095 logwtmp(line, name, host) char *line, *name, *host;
1096 #endif /* CK_ANSIC */
1097 /* logwtmp */ {
1098 #ifdef HAVEUTMPX
1099     struct utmpx ut;                    /* Needed for ut_host[] */
1100 #else
1101     struct utmp ut;
1102 #endif /* HAVEUTMPX */
1103     struct stat buf;
1104     /* time_t time(); */
1105
1106     if (!ckxwtmp)
1107       return;
1108
1109     if (!wtmpfile)
1110       makestr(&wtmpfile,WTMPFILE);
1111
1112     if (!line) line = "";
1113     if (!name) name = "";
1114     if (!host) host = "";
1115
1116     if (!wtmpfd && (wtmpfd = open(wtmpfile, O_WRONLY|O_APPEND, 0)) < 0) {
1117         ckxwtmp = 0;
1118         debug(F110,"WTMP open failed",line,0);
1119         return;
1120     }
1121     if (!fstat(wtmpfd, &buf)) {
1122         ckstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
1123         ckstrncpy(ut.ut_name, name, sizeof(ut.ut_name));
1124 #ifdef HAVEUTHOST
1125         /* Not portable */
1126         ckstrncpy(ut.ut_host, host, sizeof(ut.ut_host));
1127 #endif /* HAVEUTHOST */
1128 #ifdef HAVEUTMPX
1129         time(&ut.ut_tv.tv_sec);
1130 #else
1131 #ifdef LINUX
1132 /* In light of the following comment perhaps the previous line should */
1133 /* be "#ifndef COMMENT". */
1134         {
1135             /*
1136              * On 64-bit platforms sizeof(time_t) and sizeof(ut.ut_time)
1137              * are not the same and attempt to use an address of
1138              * ut.ut_time as an argument to time() call may cause
1139              * "unaligned access" trap.
1140              */
1141             time_t zz;
1142             time(&zz);
1143             ut.ut_time = zz;
1144         }
1145 #else
1146         time(&ut.ut_time);
1147 #endif /* LINUX */
1148 #endif /* HAVEUTMPX */
1149         if (write(wtmpfd, (char *)&ut, sizeof(struct utmp)) !=
1150             sizeof(struct utmp)) {
1151 #ifndef NOFTRUNCATE
1152 #ifndef COHERENT
1153             ftruncate(wtmpfd, buf.st_size); /* Error, undo any partial write */
1154 #else
1155             chsize(wtmpfd, buf.st_size); /* Error, undo any partial write */
1156 #endif /* COHERENT */
1157 #endif /* NOFTRUNCATE */
1158             debug(F110,"WTMP write error",line,0);
1159         } else {
1160             debug(F110,"WTMP record OK",line,0);
1161             return;
1162         }
1163     }
1164 }
1165 #endif /* CKWTMP */
1166
1167 #ifdef CKSYSLOG
1168 /*
1169   C K S Y S L O G  --  C-Kermit system logging function,
1170
1171   For use by other modules.
1172   This module can, but doesn't have to, use it.
1173   Call with:
1174     n = SYSLG_xx values defined in ckcdeb.h
1175     s1, s2, s3: strings.
1176 */
1177 VOID
1178 cksyslog(n, m, s1, s2, s3) int n, m; char * s1, * s2, * s3; {
1179     int level;
1180
1181     if (!ckxlogging)                    /* syslogging */
1182       return;
1183     if (!s1) s1 = "";                   /* Fix null args */
1184     if (!s2) s2 = "";
1185     if (!s3) s3 = "";
1186     switch (n) {                        /* Translate Kermit level */
1187       case SYSLG_DB:                    /* to syslog level */
1188         level = LOG_DEBUG;
1189         break;
1190       default:
1191         level = m ? LOG_INFO : LOG_ERR;
1192     }
1193     debug(F110,"cksyslog s1",s1,0);
1194     debug(F110,"cksyslog s2",s2,0);
1195     debug(F110,"cksyslog s3",s3,0);
1196     errno = 0;
1197     syslog(level, "%s: %s %s", s1, s2, s3); /* Write syslog record */
1198     debug(F101,"cksyslog errno","",errno);
1199 }
1200 #endif /* CKSYSLOG */
1201
1202
1203 /* Declarations */
1204
1205 int maxnam = MAXNAMLEN;                 /* Available to the outside */
1206 int maxpath = MAXPATH;
1207 int ck_znewn = -1;
1208
1209 #ifdef UNIX
1210 char startupdir[MAXPATH+1];
1211 #endif /* UNIX */
1212
1213 int pexitstat = -2;                     /* Process exit status */
1214
1215 FILE *fp[ZNFILS] = {                    /* File pointers */
1216    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
1217 };
1218
1219 /* Flags for each file indicating whether it was opened with popen() */
1220 int ispipe[ZNFILS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1221
1222 /* Buffers and pointers used in buffered file input and output. */
1223 #ifdef DYNAMIC
1224 extern char *zinbuffer, *zoutbuffer;
1225 #else
1226 extern char zinbuffer[], zoutbuffer[];
1227 #endif /* DYNAMIC */
1228 extern char *zinptr, *zoutptr;
1229 extern int zincnt, zoutcnt;
1230 extern int wildxpand;
1231
1232 static long iflen = -1L;                /* Input file length */
1233
1234 static PID_T pid = 0;                   /* pid of child fork */
1235 static int fcount = 0;                  /* Number of files in wild group */
1236 static int nxpand = 0;                  /* Copy of fcount */
1237 static char nambuf[CKMAXPATH+4];        /* Buffer for a pathname */
1238
1239 #ifndef NOFRILLS
1240 #define ZMBUFLEN 200
1241 static char zmbuf[ZMBUFLEN];            /* For mail, remote print strings */
1242 #endif /* NOFRILLS */
1243
1244 char **mtchs = NULL;                    /* Matches found for filename */
1245 char **mtchptr = NULL;                  /* Pointer to current match */
1246
1247 /*  Z K S E L F  --  Kill Self: log out own job, if possible.  */
1248
1249 /* Note, should get current pid, but if your system doesn't have */
1250 /* getppid(), then just kill(0,9)...  */
1251
1252 #ifndef SVR3
1253 #ifndef POSIX
1254 #ifndef OSFPC
1255 /* Already declared in unistd.h for SVR3 and POSIX */
1256 #ifdef CK_ANSIC
1257 extern PID_T getppid(void);
1258 #else
1259 #ifndef PS2AIX10
1260 #ifndef COHERENT
1261 extern PID_T getppid();
1262 #endif /* COHERENT */
1263 #endif /* PS2AIX10 */
1264 #endif /* CK_ANSIC */
1265 #endif /* OSFPC */
1266 #endif /* POSIX */
1267 #endif /* SVR3 */
1268
1269 int
1270 zkself() {                              /* For "bye", but no guarantee! */
1271 #ifdef PROVX1
1272     return(kill(0,9));
1273 #else
1274 #ifdef V7
1275     return(kill(0,9));
1276 #else
1277 #ifdef TOWER1
1278     return(kill(0,9));
1279 #else
1280 #ifdef FT18
1281     return(kill(0,9));
1282 #else
1283 #ifdef aegis
1284     return(kill(0,9));
1285 #else
1286 #ifdef COHERENT
1287     return(kill((PID_T)getpid(),1));
1288 #else
1289 #ifdef PID_T
1290     exit(kill((PID_T)getppid(),1));
1291     return(0);
1292 #else
1293     exit(kill(getppid(),1));
1294     return(0);
1295 #endif
1296 #endif
1297 #endif
1298 #endif
1299 #endif
1300 #endif
1301 #endif
1302 }
1303
1304 static VOID
1305 getfullname(name) char * name; {
1306     char *p = (char *)fullname;
1307     int len = 0;
1308     fullname[0] = '\0';
1309     /* If necessary we could also chase down symlinks here... */
1310 #ifdef COMMENT
1311     /* This works but is incompatible with wuftpd */
1312     if (isguest && anonroot) {
1313         ckstrncpy(fullname,anonroot,CKMAXPATH);
1314         len = strlen(fullname);
1315         if (len > 0)
1316           if (fullname[len-1] == '/')
1317             len--;
1318     }
1319     p += len;
1320 #endif /* COMMENT */
1321     zfnqfp(name, CKMAXPATH - len, p);
1322     while (*p) {
1323         if (*p < '!') *p = '_';
1324         p++;
1325     }
1326 }
1327
1328 /*  D O I K L O G  --  Open Kermit-specific ftp-like transfer log. */
1329
1330 VOID                                    /* Called in ckcmai.c */
1331 doiklog() {
1332     if (iklogopen)                      /* Already open? */
1333       return;
1334     if (xferlog) {                      /* Open iksd log if requested */
1335         if (!xferfile)                  /* If no pathname given */
1336           makestr(&xferfile,XFERFILE);  /* use this default */
1337         if (*xferfile) {
1338             xferlog = open(xferfile, O_WRONLY | O_APPEND | O_CREAT, 0660);
1339             debug(F101,"doiklog open","",xferlog);
1340             if (xferlog < 0) {
1341 #ifdef CKSYSLOG
1342                 syslog(LOG_ERR, "xferlog open failure %s: %m", xferfile);
1343 #endif /* CKSYSLOG */
1344                 debug(F101,"doiklog open errno","",errno);
1345                 xferlog = 0;
1346             } else
1347               iklogopen = 1;
1348         } else
1349           xferlog = 0;
1350 #ifdef CKSYSLOG
1351         if (xferlog && ckxlogging)
1352           syslog(LOG_INFO, "xferlog: %s open ok", xferfile);
1353 #endif /* CKSYSLOG */
1354     }
1355 }
1356
1357 /*  Z O P E N I  --  Open an existing file for input. */
1358
1359 /* Returns 1 on success, 0 on failure */
1360
1361 int
1362 zopeni(n,name) int n; char *name; {
1363     int x;
1364
1365     debug(F111,"zopeni",name,n);
1366     if ((x = chkfn(n)) != 0) {
1367         debug(F111,"zopeni chkfn",ckitoa(n),x);
1368         return(0);
1369     }
1370     zincnt = 0;                         /* Reset input buffer */
1371     if (n == ZSYSFN) {                  /* Input from a system function? */
1372 #ifdef COMMENT
1373 /*** Note, this function should not be called with ZSYSFN ***/
1374 /*** Always call zxcmd() directly, and give it the real file number ***/
1375 /*** you want to use.  ***/
1376         return(zxcmd(n,name));          /* Try to fork the command */
1377 #else
1378         debug(F110,"zopeni called with ZSYSFN, failing!",name,0);
1379         *nambuf = '\0';                 /* No filename. */
1380         return(0);                      /* fail. */
1381 #endif /* COMMENT */
1382     }
1383     if (n == ZSTDIO) {                  /* Standard input? */
1384         if (is_a_tty(0)) {
1385             fprintf(stderr,"Terminal input not allowed");
1386             debug(F110,"zopeni: attempts input from unredirected stdin","",0);
1387             return(0);
1388         }
1389         fp[ZIFILE] = stdin;
1390         ispipe[ZIFILE] = 0;
1391         return(1);
1392     }
1393 #ifdef CKROOT
1394     debug(F111,"zopeni setroot",ckroot,ckrootset);
1395     if (ckrootset) if (!zinroot(name)) {
1396         debug(F110,"zopeni setroot violation",name,0);
1397         return(0);
1398     }
1399 #endif /* CKROOT */
1400     fp[n] = fopen(name,"r");            /* Real file, open it. */
1401     debug(F111,"zopeni fopen", name, fp[n]);
1402 #ifdef ZDEBUG
1403     printf("ZOPENI fp[%d]=%ld\n",n,fp[n]);
1404 #endif /* ZDEBUG */
1405     ispipe[n] = 0;
1406
1407     if (xferlog
1408 #ifdef CKSYSLOG
1409         || ((ckxsyslog >= SYSLG_FA) && ckxlogging)
1410 #endif /* CKSYSLOG */
1411         ) {
1412         getfullname(name);
1413         debug(F110,"zopeni fullname",fullname,0);
1414     }
1415     if (fp[n] == NULL) {
1416 #ifdef CKSYSLOG
1417         if (ckxsyslog >= SYSLG_FA && ckxlogging) {
1418             syslog(LOG_INFO, "file[%d] %s: open failed (%m)", n, fullname);
1419             perror(fullname);
1420         } else
1421 #endif /* CKSYSLOG */
1422           perror(name);
1423         return(0);
1424     } else {
1425 #ifdef CKSYSLOG
1426         if (ckxsyslog >= SYSLG_FA && ckxlogging)
1427           syslog(LOG_INFO, "file[%d] %s: open read ok", n, fullname);
1428 #endif /* CKSYSLOG */
1429         clearerr(fp[n]);
1430         return(1);
1431     }
1432 }
1433
1434 #ifdef QNX
1435 #define DONDELAY
1436 #else
1437 #ifdef O_NDELAY
1438 #define DONDELAY
1439 #endif /* O_NDELAY */
1440 #endif /* QNX */
1441
1442 /*  Z O P E N O  --  Open a new file for output.  */
1443
1444 /*ARGSUSED*/    /* zz not used */
1445 int
1446 zopeno(n,name,zz,fcb)
1447 /* zopeno */  int n; char *name; struct zattr *zz; struct filinfo *fcb; {
1448
1449     char p[8];
1450     int append = 0;
1451
1452 /* As of Version 5A, the attribute structure and the file information */
1453 /* structure are included in the arglist. */
1454
1455 #ifdef DEBUG
1456     debug(F111,"zopeno",name,n);
1457     if (fcb) {
1458         debug(F101,"zopeno fcb disp","",fcb->dsp);
1459         debug(F101,"zopeno fcb type","",fcb->typ);
1460         debug(F101,"zopeno fcb char","",fcb->cs);
1461     } else {
1462         debug(F100,"zopeno fcb is NULL","",0);
1463     }
1464 #endif /* DEBUG */
1465
1466     if (chkfn(n) != 0)                  /* Already open? */
1467       return(0);                        /* Nothing to do. */
1468
1469     if ((n == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */
1470         fp[ZOFILE] = stdout;
1471         ispipe[ZOFILE] = 0;
1472 #ifdef COMMENT
1473         /* This seems right but it breaks client server ops */
1474         fp[n] = stdout;
1475         ispipe[n] = 0;
1476 #endif /* COMMENT */
1477 #ifdef DEBUG
1478         if (n != ZDFILE)
1479           debug(F101,"zopeno fp[n]=stdout","",fp[n]);
1480 #endif /* DEBUG */
1481         zoutcnt = 0;
1482         zoutptr = zoutbuffer;
1483         return(1);
1484     }
1485
1486 /* A real file.  Open it in desired mode (create or append). */
1487
1488 #ifdef CKROOT
1489     debug(F111,"zopeno setroot",ckroot,ckrootset);
1490     if (ckrootset) if (!zinroot(name)) {
1491         debug(F110,"zopeno setroot violation",name,0);
1492         return(0);
1493     }
1494 #endif /* CKROOT */
1495
1496     ckstrncpy(p,"w",8);                 /* Assume write/create mode */
1497     if (fcb) {                          /* If called with an FCB... */
1498         if (fcb->dsp == XYFZ_A) {       /* Does it say Append? */
1499             ckstrncpy(p,"a",8);         /* Yes. */
1500             debug(F100,"zopeno append","",0);
1501             append = 1;
1502         }
1503     }
1504
1505     if (xferlog
1506 #ifdef CKSYSLOG
1507         || ((ckxsyslog >= SYSLG_FC) && ckxlogging)
1508 #endif /* CKSYSLOG */
1509         ) {
1510         getfullname(name);
1511         debug(F110,"zopeno fullname",fullname,0);
1512     }
1513     debug(F110,"zopeno fopen arg",p,0);
1514     fp[n] = fopen(name,p);              /* Try to open the file */
1515     ispipe[ZIFILE] = 0;
1516
1517 #ifdef ZDEBUG
1518     printf("ZOPENO fp[%d]=%ld\n",n,fp[n]);
1519 #endif /* ZDEBUG */
1520
1521     if (fp[n] == NULL) {                /* Failed */
1522         debug(F101,"zopeno failed errno","",errno);
1523 #ifdef CKSYSLOG
1524         if (ckxsyslog >= SYSLG_FC && ckxlogging)
1525           syslog(LOG_INFO, "file[%d] %s: %s failed (%m)",
1526                  n,
1527                  fullname,
1528                  append ? "append" : "create"
1529                  );
1530 #endif /* CKSYSLOG */
1531 #ifdef COMMENT                          /* Let upper levels print message. */
1532         perror("Can't open output file");
1533 #endif /* COMMENT */
1534     } else {                            /* Succeeded */
1535         extern int zofbuffer, zofblock, zobufsize;
1536         debug(F101, "zopeno zobufsize", "", zobufsize);
1537         if (n == ZDFILE || n == ZTFILE) { /* If debug or transaction log */
1538             setbuf(fp[n],NULL);           /* make it unbuffered. */
1539 #ifdef DONDELAY
1540         } else if (n == ZOFILE && !zofblock) { /* blocking or nonblocking */
1541             int flags;
1542             if ((flags = fcntl(fileno(fp[n]),F_GETFL,0)) > -1)
1543               fcntl(fileno(fp[n]),F_SETFL, flags |
1544 #ifdef QNX
1545                     O_NONBLOCK
1546 #else
1547                     O_NDELAY
1548 #endif /* QNX */
1549                     );
1550             debug(F100,"zopeno ZOFILE nonblocking","",0);
1551 #endif /* DONDELAY */
1552         } else if (n == ZOFILE && !zofbuffer) { /* buffered or unbuffered */
1553             setbuf(fp[n],NULL);
1554             debug(F100,"zopeno ZOFILE unbuffered","",0);
1555         }
1556
1557 #ifdef CK_LOGIN
1558         /* Enforce anonymous file-creation permission */
1559         if (isguest)
1560           if (n == ZWFILE || n == ZMFILE ||
1561               n == ZOFILE || n == ZDFILE ||
1562               n == ZTFILE || n == ZPFILE ||
1563               n == ZSFILE)
1564             chmod(name,ckxperms);
1565 #endif /* CK_LOGIN */
1566 #ifdef CKSYSLOG
1567         if (ckxsyslog >= SYSLG_FC && ckxlogging)
1568           syslog(LOG_INFO, "file[%d] %s: %s ok",
1569                  n,
1570                  fullname,
1571                  append ? "append" : "create"
1572                  );
1573 #endif /* CKSYSLOG */
1574         debug(F100, "zopeno ok", "", 0);
1575     }
1576     zoutcnt = 0;                        /* (PWP) reset output buffer */
1577     zoutptr = zoutbuffer;
1578     return((fp[n] != NULL) ? 1 : 0);
1579 }
1580
1581 /*  Z C L O S E  --  Close the given file.  */
1582
1583 /*  Returns 0 if arg out of range, 1 if successful, -1 if close failed.  */
1584
1585 int
1586 zclose(n) int n; {
1587     int x = 0, x2 = 0;
1588     extern long ffc;
1589
1590     debug(F101,"zclose file number","",n);
1591     if (chkfn(n) < 1) return(0);        /* Check range of n */
1592     if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */
1593       x2 = zoutdump();
1594
1595     if (fp[ZSYSFN] || ispipe[n]) {      /* If file is really pipe */
1596 #ifndef NOPUSH
1597         x = zclosf(n);                  /* do it specially */
1598 #else
1599         x = EOF;
1600 #endif /* NOPUSH */
1601         debug(F101,"zclose zclosf","",x);
1602         debug(F101,"zclose zclosf fp[n]","",fp[n]);
1603     } else {
1604         if ((fp[n] != stdout) && (fp[n] != stdin))
1605           x = fclose(fp[n]);
1606         fp[n] = NULL;
1607 #ifdef COMMENT
1608         if (n == ZCTERM || n == ZSTDIO) /* See zopeno() */
1609           if (fp[ZOFILE] == stdout)
1610             fp[ZOFILE] = NULL;
1611 #endif /* COMMENT */
1612     }
1613     iflen = -1L;                        /* Invalidate file length */
1614     if (x == EOF) {                     /* if we got a close error */
1615         debug(F101,"zclose fclose fails","",x);
1616         return(-1);
1617     } else if (x2 < 0) {                /* or error flushing last buffer */
1618         debug(F101,"zclose error flushing last buffer","",x2);
1619         return(-1);                     /* then return an error */
1620     } else {
1621         /* Print log record compatible with wu-ftpd */
1622         if (xferlog && (n == ZIFILE || n == ZOFILE)) {
1623             char * s, *p;
1624             extern char ttname[];
1625             if (!iklogopen) (VOID) doiklog(); /* Open log if necessary */
1626             debug(F101,"zclose iklogopen","",iklogopen);
1627             if (iklogopen) {
1628                 int len;
1629                 char * fnam;
1630
1631                 timenow = time(NULL);
1632 #ifdef CK_LOGIN
1633                 if (logged_in)
1634                   s = clienthost;
1635                 else
1636 #endif /* CK_LOGIN */
1637                   s = (char *)ttname;
1638                 if (!s) s = "";
1639                 if (!*s) s = "*";
1640 #ifdef CK_LOGIN
1641                 if (logged_in) {
1642                     p = guestpass;
1643                     if (!*p) p = "*";
1644                 } else
1645 #endif /* CK_LOGIN */
1646                   p = whoami();
1647
1648                 len = 24 + 12 + (int)strlen(s) + 16
1649                   + (int)strlen(fullname) + 1 + 1 + 1 + 1
1650                     + (int)strlen(p) + 6 + 2 + 12;
1651                 fnam = fullname;
1652                 if (!*fnam) fnam = "(pipe)";
1653
1654                 if (len > IKSDMSGLEN)
1655                   sprintf(iksdmsg,      /* SAFE */
1656                         "%.24s [BUFFER WOULD OVERFLOW]\n",ctime(&timenow));
1657                 else
1658                   sprintf(iksdmsg,      /* SAFE */
1659                         "%.24s %d %s %ld %s %c %s %c %c %s %s %d %s\n",
1660                         ctime(&timenow),        /* date/time */
1661                         gtimer(),               /* elapsed secs */
1662                         s,                      /* peer name */
1663                         ffc,                    /* byte count */
1664                         fnam,                   /* full pathname of file */
1665                         (binary ? 'b' : 'a'),   /* binary or ascii */
1666                         "_",                    /* options = none */
1667                         n == ZIFILE ? 'o' : 'i', /* in/out */
1668 #ifdef CK_LOGIN
1669                         (isguest ? 'a' : 'r'),  /* User type */
1670 #else
1671                         'r',
1672 #endif /* CK_LOGIN */
1673                         p,                      /* Username or guest passwd */
1674 #ifdef CK_LOGIN
1675                         logged_in ? "iks" : "kermit", /* Record ID */
1676 #else
1677                         "kermit",
1678 #endif /* CK_LOGIN */
1679                         0,              /* User ID on client system unknown */
1680                         "*"             /* Ditto */
1681                         );
1682                 debug(F110,"zclose iksdmsg",iksdmsg,0);
1683                 write(xferlog, iksdmsg, (int)strlen(iksdmsg));
1684             }
1685         }
1686         debug(F101,"zclose returns","",1);
1687         return(1);
1688     }
1689 }
1690
1691 /*  Z C H I N  --  Get a character from the input file.  */
1692
1693 /*  Returns -1 if EOF, 0 otherwise with character returned in argument  */
1694
1695 int
1696 zchin(n,c) int n; int *c; {
1697     int a;
1698
1699 #ifdef IKSD
1700     if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1701         a = coninc(0);
1702         if (*c < 0)
1703           return(-1);
1704     } else
1705 #endif /* IKSD */
1706     /* (PWP) Just in case this gets called when it shouldn't. */
1707     if (n == ZIFILE) {
1708         a = zminchar();                 /* Note: this catches Ctrl-Z */
1709         if (a < 0)                      /* (See zinfill()...) */
1710           return(-1);
1711     } else {
1712         a = getc(fp[n]);
1713         if (a == EOF) return(-1);
1714 #ifdef CK_CTRLZ
1715         /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
1716         if (!binary && a == 0x1A && eofmethod == XYEOF_Z)
1717           return(-1);
1718 #endif /* CK_CTRLZ */
1719     }
1720     *c = (CHAR) a & 0377;
1721     return(0);
1722 }
1723
1724 /*  Z S I N L  --  Read a line from a file  */
1725
1726 /*
1727   Writes the line into the address provided by the caller.
1728   n is the Kermit "channel number".
1729   Writing terminates when newline is encountered, newline is not copied.
1730   Writing also terminates upon EOF or if length x is exhausted.
1731   Returns 0 on success, -1 on EOF or error.
1732 */
1733 int
1734 zsinl(n,s,x) int n, x; char *s; {
1735     int a, z = 0;                       /* z is return code. */
1736     int count = 0;
1737     int len = 0;
1738     char *buf;
1739     extern CHAR feol;                   /* Line terminator */
1740
1741     if (!s || chkfn(n) < 1)             /* Make sure file is open, etc */
1742       return(-1);
1743     buf = s;
1744     s[0] = '\0';                        /* Don't return junk */
1745
1746     a = -1;                             /* Current character, none yet. */
1747     while (x--) {                       /* Up to given length */
1748         int old = 0;
1749         if (feol)                       /* Previous character */
1750           old = a;
1751         if (zchin(n,&a) < 0) {          /* Read a character from the file */
1752             debug(F101,"zsinl zchin fail","",count);
1753             if (count == 0)
1754               z = -1;                   /* EOF or other error */
1755             break;
1756         } else
1757           count++;
1758         if (feol) {                     /* Single-character line terminator */
1759             if (a == feol)
1760               break;
1761         } else {                        /* CRLF line terminator */
1762             if (a == '\015')            /* CR, get next character */
1763               continue;
1764             if (old == '\015') {        /* Previous character was CR */
1765                 if (a == '\012') {      /* This one is LF, so we have a line */
1766                     break;
1767                 } else {                /* Not LF, deposit CR */
1768                     *s++ = '\015';
1769                     x--;
1770                     len++;
1771                 }
1772             }
1773         }
1774         *s = a;                         /* Deposit character */
1775         s++;
1776         len++;
1777     }
1778     *s = '\0';                          /* Terminate the string */
1779     debug(F011,"zsinl",buf,len);
1780     return(z);
1781 }
1782
1783 /*  Z X I N  --  Read x bytes from a file  */
1784
1785 /*
1786   Reads x bytes (or less) from channel n and writes them
1787   to the address provided by the caller.
1788   Returns number of bytes read on success, 0 on EOF or error.
1789 */
1790 int
1791 zxin(n,s,x) int n, x; char *s; {
1792 #ifdef IKSD
1793     if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1794         int a, i;
1795         a = ttchk();
1796         if (a < 1) return(0);
1797         for (i = 0; i < a && i < x; i++)
1798           s[i] = coninc(0);
1799         return(i);
1800     }
1801 #endif /* IKSD */
1802
1803     return(fread(s, sizeof (char), x, fp[n]));
1804 }
1805
1806 /*
1807   Z I N F I L L  --  Buffered file input.
1808
1809   (re)fill the file input buffer with data.  All file input
1810   should go through this routine, usually by calling the zminchar()
1811   macro defined in ckcker.h.  Returns:
1812
1813   Value 0..255 on success, the character that was read.
1814   -1 on end of file.
1815   -2 on any kind of error other than end of file.
1816   -3 timeout when reading from pipe (Kermit packet mode only).
1817 */
1818 int
1819 zinfill() {
1820     extern int kactive, srvping;
1821     errno = 0;
1822
1823 #ifdef ZDEBUG
1824     printf("ZINFILL fp[%d]=%ld\n",ZIFILE,fp[ZIFILE]);
1825 #endif /* ZDEBUG */
1826
1827 #ifdef IKSD
1828     if (inserver && !local && fp[ZIFILE] == stdin) {
1829         int a, i;
1830         a = ttchk();
1831         if (a < 0) return(-2);
1832         for (i = 0; i < a && i < INBUFSIZE; i++) {
1833             zinbuffer[i] = coninc(0);
1834         }
1835         zincnt = i;
1836         /* set pointer to beginning, (== &zinbuffer[0]) */
1837         zinptr = zinbuffer;
1838         if (zincnt == 0) return(-1);
1839         zincnt--;                       /* One less char in buffer */
1840         return((int)(*zinptr++) & 0377); /* because we return the first */
1841     }
1842 #endif /* IKSD */
1843
1844     debug(F101,"zinfill kactive","",kactive);
1845
1846     if (!(kactive && ispipe[ZIFILE])) {
1847         if (feof(fp[ZIFILE])) {
1848             debug(F100,"ZINFILL feof","",0);
1849 #ifdef ZDEBUG
1850             printf("ZINFILL EOF\n");
1851 #endif /* ZDEBUG */
1852             return(-1);
1853         }
1854     }
1855     clearerr(fp[ZIFILE]);
1856
1857 #ifdef SELECT
1858     /* Here we can call select() to get a timeout... */
1859     if (kactive && ispipe[ZIFILE]) {
1860         int secs, z = 0;
1861 #ifndef NOXFER
1862         if (srvping) {
1863             secs = 1;
1864             debug(F101,"zinfill calling ttwait","",secs);
1865             z = ttwait(fileno(fp[ZIFILE]),secs);
1866             debug(F101,"zinfill ttwait","",z);
1867         }
1868 #endif /* NOXFER */
1869         if (z == 0)
1870           return(-3);
1871     }
1872 #endif /* SELECT */
1873
1874 #ifdef DEBUG
1875     if (deblog) {
1876         int i;
1877         debug(F101,"ZINFILL INBUFSIZE","",INBUFSIZE);
1878 #ifdef USE_MEMCPY
1879         memset(zinbuffer, 0xFF, INBUFSIZE);
1880 #else
1881         for (i = 0; i < INBUFSIZE; i++) {
1882             zinbuffer[i] = 0xFF;
1883 #ifdef COMMENT                          /* Too much! */
1884             debug(F101,"ZINFILL zinbuffer[i]","",i);
1885 #endif /* COMMENT */
1886         }
1887 #endif /* USE_MEMCPY */
1888         ckstrncpy(zinbuffer,"zinbuffer is a valid buffer",INBUFSIZE);
1889         debug(F111,"ZINFILL about to call fread",zinbuffer,zinbuffer);
1890     }
1891 #endif /* DEBUG */
1892
1893 /*
1894   Note: The following read MUST be nonblocking when reading from a pipe
1895   and we want timeouts to work.  See zxcmd().
1896 */
1897     zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]);
1898     debug(F101,"ZINFILL fread","",zincnt); /* Just the size */
1899 #ifdef ZDEBUG
1900     printf("FREAD=%d\n",zincnt);
1901 #endif /* ZDEBUG */
1902 #ifdef CK_CTRLZ
1903     /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
1904     if (zincnt > 0 && !binary && eofmethod == XYEOF_Z) {
1905         register int i;
1906         for (i = 0; i < zincnt; i++) {
1907             if (zinbuffer[i] == SUB) {
1908                 zincnt = i;             /* Stop at first Ctrl-Z */
1909                 if (i == 0)
1910                   return(-1);
1911                 break;
1912             }
1913         }
1914     }
1915 #endif /* CK_CTRLZ */
1916
1917     if (zincnt == 0) {                  /* Got nothing? */
1918         if (ferror(fp[ZIFILE])) {
1919             debug(F100,"ZINFILL ferror","",0);
1920             debug(F101,"ZINFILL errno","",errno);
1921 #ifdef ZDEBUG
1922             printf("ZINFILL errno=%d\n",errno);
1923 #endif /* ZDEBUG */
1924 #ifdef EWOULDBLOCK
1925             return((errno == EWOULDBLOCK) ? -3 : -2);
1926 #else
1927             return(-2);
1928 #endif /* EWOULDBLOCK */
1929         }
1930
1931     /* In case feof() didn't work just above -- sometimes it doesn't... */
1932
1933         if (feof(fp[ZIFILE]) ) {
1934             debug(F100,"ZINFILL count 0 EOF return -1","",0);
1935             return (-1);
1936         } else {
1937             debug(F100,"ZINFILL count 0 not EOF return -2","",0);
1938             return(-2);
1939         }
1940     }
1941     zinptr = zinbuffer;    /* set pointer to beginning, (== &zinbuffer[0]) */
1942     zincnt--;                           /* One less char in buffer */
1943     return((int)(*zinptr++) & 0377);    /* because we return the first */
1944 }
1945
1946 /*  Z S O U T  --  Write a string out to the given file, buffered.  */
1947
1948 int
1949 zsout(n,s) int n; char *s; {
1950     int rc = 0;
1951     rc = chkfn(n);
1952     if (rc < 1) return(-1);             /* Keep this, prevents memory faults */
1953     if (!s) return(0);                  /* Null pointer, do nothing, succeed */
1954     if (!*s) return(0);                 /* empty string, ditto */
1955
1956 #ifdef IKSD
1957     /*
1958       This happens with client-side Kermit server when a REMOTE command
1959       was sent from the server to the client and the server is supposed to
1960       display the text, but of course there is no place to display it
1961       since it is in remote mode executing Kermit protocol.
1962     */
1963     if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1964 #ifdef COMMENT
1965         return(ttol(s,((int)strlen(s)) < 0) ? -1 : 0);
1966 #else
1967         return(0);
1968 #endif /* COMMENT */
1969     }
1970 #endif /* IKSD */
1971
1972     if (n == ZSFILE)
1973       return(write(fileno(fp[n]),s,(int)strlen(s)));
1974     rc = fputs(s,fp[n]) == EOF ? -1 : 0;
1975     if (n == ZWFILE)
1976       fflush(fp[n]);
1977     return(rc);
1978 }
1979
1980 /*  Z S O U T L  --  Write string to file, with line terminator, buffered  */
1981
1982 int
1983 zsoutl(n,s) int n; char *s; {
1984     if (zsout(n,s) < 0)
1985         return(-1);
1986
1987 #ifdef IKSD
1988     if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1989 #ifdef COMMENT
1990         return(ttoc(LF));
1991 #else
1992         return(0);                      /* See comments in zsout() */
1993 #endif /* COMMENT */
1994     }
1995 #endif /* IKSD */
1996
1997     if (n == ZSFILE)                    /* Session log is unbuffered */
1998       return(write(fileno(fp[n]),"\n",1));
1999     else if (fputs("\n",fp[n]) == EOF)
2000       return(-1);
2001     if (n == ZDIFIL || n == ZWFILE)     /* Flush connection log records */
2002       fflush(fp[n]);
2003     return(0);
2004 }
2005
2006 /*  Z S O U T X  --  Write x characters to file, unbuffered.  */
2007
2008 int
2009 zsoutx(n,s,x) int n, x; char *s; {
2010 #ifdef IKSD
2011     if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2012 #ifdef COMMENT
2013         return(ttol(s,x));              /* See comments in zsout() */
2014 #else
2015         return(x);
2016 #endif /* COMMENT */
2017     }
2018 #endif /* IKSD */
2019
2020 #ifdef COMMENT
2021     if (chkfn(n) < 1) return(-1);
2022     return(write(fp[n]->_file,s,x));
2023 #endif /* COMMENT */
2024     return(write(fileno(fp[n]),s,x) == x ? x : -1);
2025 }
2026
2027 /*  Z C H O U T  --  Add a character to the given file.  */
2028
2029 /*  Should return 0 or greater on success, -1 on failure (e.g. disk full)  */
2030
2031 int
2032 #ifdef CK_ANSIC
2033 zchout(register int n, char c)
2034 #else
2035 zchout(n,c) register int n; char c;
2036 #endif /* CK_ANSIC */
2037 /* zchout() */ {
2038     /* if (chkfn(n) < 1) return(-1); */
2039
2040 #ifdef IKSD
2041     if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2042 #ifdef COMMENT
2043         return(ttoc(c));
2044 #else
2045         return(0);                      /* See comments in zsout() */
2046 #endif /* COMMENT */
2047     }
2048 #endif /* IKSD */
2049
2050     if (n == ZSFILE)                    /* Use unbuffered for session log */
2051       return(write(fileno(fp[n]),&c,1) == 1 ? 0 : -1);
2052                                 /* Buffered for everything else */
2053     if (putc(c,fp[n]) == EOF)   /* If true, maybe there was an error */
2054       return(ferror(fp[n])?-1:0);       /* Check to make sure */
2055     else                                /* Otherwise... */
2056       return(0);                        /* There was no error. */
2057 }
2058
2059 /* (PWP) buffered character output routine to speed up file IO */
2060
2061 int
2062 zoutdump() {
2063     int x;
2064     char * zp;
2065     zoutptr = zoutbuffer;               /* Reset buffer pointer in all cases */
2066 #ifdef DEBUG
2067     if (deblog)
2068       debug(F101,"zoutdump zoutcnt","",zoutcnt);
2069 #endif /* DEBUG */
2070     if (zoutcnt == 0) {                 /* Nothing to output */
2071         return(0);
2072     } else if (zoutcnt < 0) {           /* Unexpected negative argument */
2073         zoutcnt = 0;                    /* Reset output buffer count */
2074         return(-1);                     /* and fail. */
2075     }
2076
2077 #ifdef IKSD
2078     if (inserver && !local && fp[ZOFILE] == stdout) {
2079 #ifdef COMMENT
2080         x = ttol(zoutbuffer,zoutcnt);
2081 #else
2082         x = 1;                          /* See comments in zsout() */
2083 #endif /* COMMENT */
2084         zoutcnt = 0;
2085         return(x > 0 ? 0 : -1);
2086     }
2087 #endif /* IKSD */
2088
2089 /*
2090   Frank Prindle suggested that replacing this fwrite() by an fflush()
2091   followed by a write() would improve the efficiency, especially when
2092   writing to stdout.  Subsequent tests showed a 5-fold improvement.
2093 */
2094 #ifdef COMMENT
2095     if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) ...
2096 #endif /* COMMENT */
2097
2098 #ifndef CK_NONBLOCK
2099     fflush(fp[ZOFILE]);
2100 #endif /* CK_NONBLOCK */
2101     zp = zoutbuffer;
2102     while (zoutcnt > 0) {
2103         if ((x = write(fileno(fp[ZOFILE]),zp,zoutcnt)) > -1) {
2104 #ifdef DEBUG
2105             if (deblog)                 /* Save a function call... */
2106               debug(F101,"zoutdump wrote","",x);
2107 #endif /* DEBUG */
2108             zoutcnt -= x;               /* Adjust output buffer count */
2109             zp += x;                    /* and pointer */
2110         } else {
2111 #ifdef DEBUG
2112             if (deblog) {
2113                 debug(F101,"zoutdump write error","",errno);
2114                 debug(F101,"zoutdump write returns","",x);
2115             }
2116 #endif /* DEBUG */
2117             zoutcnt = 0;                /* Reset output buffer count */
2118             return(-1);                 /* write() failed */
2119         }
2120     }
2121     return(0);
2122 }
2123
2124 /*  C H K F N  --  Internal function to verify file number is ok  */
2125
2126 /*
2127  Returns:
2128   -1: File number n is out of range
2129    0: n is in range, but file is not open
2130    1: n in range and file is open
2131 */
2132 int
2133 chkfn(n) int n; {
2134     /* if (n != ZDFILE) debug(F101,"chkfn","",n); */
2135     if (n < 0 || n >= ZNFILS) {
2136         if (n != ZDFILE) debug(F101,"chkfn out of range","",n);
2137         return(-1);
2138     } else {
2139         /* if (n != ZDFILE) debug(F101,"chkfn fp[n]","",fp[n]); */
2140         return((fp[n] == NULL) ? 0 : 1);
2141     }
2142 }
2143
2144 /*  Z G E T F S -- Return file size regardless of accessibility */
2145 /*
2146   Used for directory listings, etc.
2147   Returns:
2148     The size of the file in bytes, 0 or greater, if the size can be learned.
2149     -1 if the file size can not be obtained.
2150   Also (and this is a hack just for UNIX):
2151     If the argument is the name of a symbolic link,
2152     the global variable issymlink is set to 1,
2153     and the global buffer linkname[] gets the link value.
2154     And it sets zgfs_dir to 1 if it's a directory, otherwise 0.
2155   This lets us avoid numerous redundant calls to stat().
2156 */
2157 int zgfs_link = 0;
2158 int zgfs_dir = 0;
2159 time_t zgfs_mtime = 0;
2160 unsigned int zgfs_mode = 0;
2161
2162 #ifdef CKSYMLINK
2163 char linkname[CKMAXPATH+1];
2164 #ifndef _IFLNK
2165 #define _IFLNK 0120000
2166 #endif /* _IFLNK */
2167 #endif /* CKSYMLINK */
2168
2169 long
2170 zgetfs(name) char *name; {
2171     struct stat buf;
2172     char fnam[CKMAXPATH+4];
2173     long size = -1L;
2174     int x;
2175     int needrlink = 0;
2176     char * s;
2177
2178     if (!name) name = "";
2179     if (!*name) return(-1);
2180
2181 #ifdef UNIX
2182     x = strlen(name);
2183     if (x == 9 && !strcmp(name,"/dev/null"))
2184       return(0);
2185 #endif /* UNIX */
2186
2187     s = name;
2188 #ifdef DTILDE
2189     if (*s == '~') {
2190         s = tilde_expand(s);
2191         if (!s) s = "";
2192         if (!*s) s = name;
2193     }
2194 #endif /* DTILDE */
2195     x = ckstrncpy(fnam,s,CKMAXPATH);
2196     s = fnam;
2197     debug(F111,"zgetfs fnam",s,x);
2198     if (x > 0 && s[x-1] == '/')
2199       s[x-1] = '\0';
2200
2201     zgfs_dir = 0;                       /* Assume it's not a directory */
2202     zgfs_link = 0;                      /* Assume it's not a symlink */
2203     zgfs_mtime = 0;                     /* No time yet */
2204     zgfs_mode = 0;                      /* No permission bits yet */
2205
2206 #ifdef CKSYMLINK                        /* We're doing symlinks? */
2207 #ifdef USE_LSTAT                        /* OK to use lstat()? */
2208     x = lstat(s,&buf);
2209     debug(F101,"STAT","",1);
2210     if (x < 0)                          /* stat() failed */
2211       return(-1);
2212     if (                                /* Now see if it's a symlink */
2213 #ifdef S_ISLNK
2214         S_ISLNK(buf.st_mode)
2215 #else
2216 #ifdef _IFLNK
2217         ((_IFMT & buf.st_mode) == _IFLNK)
2218 #endif /* _IFLNK */
2219 #endif /* S_ISLNK */
2220         ) {
2221         zgfs_link = 1;                  /* It's a symlink */
2222         linkname[0] = '\0';             /* Get the name */
2223         x = readlink(s,linkname,CKMAXPATH);
2224         debug(F101,"zgetfs readlink",s,x);
2225         if (x > -1 && x < CKMAXPATH) {  /* It's a link */
2226             linkname[x] = '\0';
2227             size = buf.st_size;         /* Remember size of link */
2228             x = stat(s,&buf);           /* Now stat the linked-to file */
2229             debug(F101,"STAT","",2);
2230             if (x < 0)                  /* so we can see if it's a directory */
2231               return(-1);
2232         } else {
2233             ckstrncpy(linkname,"(lookup failed)",CKMAXPATH);
2234         }
2235     }
2236 #else  /* !USE_LSTAT */
2237     x = stat(s,&buf);                   /* No lstat(), use stat() instead */
2238     debug(F101,"STAT","",3);
2239     if (x < 0)
2240       return(-1);
2241 #endif /* USE_LSTAT */
2242
2243     /* Do we need to call readlink()? */
2244
2245 #ifdef NOLINKBITS
2246 /*
2247   lstat() does not work in SCO operating systems.  From "man NS lstat":
2248
2249   lstat obtains information about the file named by path. In the case of a
2250   symbolic link, lstat returns information about the link, and not the file
2251   named by the link. It is only used by the NFS automount daemon and should
2252   not be utilized by users.
2253 */
2254     needrlink = 1;
2255     debug(F101,"zgetfs forced needrlink","",needrlink);
2256 #else
2257 #ifdef S_ISLNK
2258     needrlink = S_ISLNK(buf.st_mode);
2259     debug(F101,"zgetfs S_ISLNK needrlink","",needrlink);
2260 #else
2261 #ifdef _IFLNK
2262     needrlink = (_IFMT & buf.st_mode) == _IFLNK;
2263     debug(F101,"zgetfs _IFLNK needrlink","",needrlink);
2264 #else
2265     needrlink = 1;
2266     debug(F101,"zgetfs default needrlink","",needrlink);
2267 #endif /* _IFLNK */
2268 #endif /* S_ISLNK */
2269 #endif /* NOLINKBITS */
2270
2271     if (needrlink) {
2272         linkname[0] = '\0';
2273         errno = 0;
2274         x = readlink(s,linkname,CKMAXPATH);
2275 #ifdef DEBUG
2276         debug(F111,"zgetfs readlink",s,x);
2277         if (x < 0)
2278           debug(F101,"zgetfs readlink errno","",errno);
2279         else
2280           debug(F110,"zgetfs readlink result",linkname,0);
2281 #endif /* DEBUG */
2282         if (x > -1 && x < CKMAXPATH) {
2283             zgfs_link = 1;
2284             linkname[x] = '\0';
2285         }
2286     }
2287 #else  /* !CKSYMLINK */
2288     x = stat(s,&buf);                   /* Just stat the file */
2289     debug(F111,"zgetfs stat",s,x);
2290     if (x < 0)                          /* and get the size */
2291       return(-1);
2292 #endif /* CKSYMLINK */
2293
2294     zgfs_mtime = buf.st_mtime;
2295     zgfs_mode = buf.st_mode;
2296     zgfs_dir = (S_ISDIR(buf.st_mode)) ? 1 : 0; /* Set "is directory" flag */
2297     debug(F111,"zgetfs size",s,size);
2298     debug(F111,"zgetfs st_size",s,buf.st_size);
2299     return((size < 0L) ? buf.st_size : size); /* Return the size */
2300 }
2301
2302
2303 /*  Z C H K I  --  Check if input file exists and is readable  */
2304
2305 /*
2306   Returns:
2307    >= 0 if the file can be read (returns the size).
2308      -1 if file doesn't exist or can't be accessed,
2309      -2 if file exists but is not readable (e.g. a directory file).
2310      -3 if file exists but protected against read access.
2311
2312   For Berkeley Unix, a file must be of type "regular" to be readable.
2313   Directory files, special files, and symbolic links are not readable.
2314 */
2315 long
2316 zchki(name) char *name; {
2317     struct stat buf;
2318     char * s;
2319     int x, itsadir = 0;
2320     extern int zchkid, diractive, matchfifo;
2321
2322     if (!name)
2323       return(-1);
2324     x = strlen(name);
2325     if (x < 1)
2326       return(-1);
2327     s = name;
2328
2329 #ifdef UNIX
2330     if (x == 9 && !strcmp(s,"/dev/null"))
2331       return(0);
2332     if (x == 8 && !strcmp(s,"/dev/tty"))
2333       return(0);
2334 #endif /* UNIX */
2335
2336 #ifdef DTILDE
2337     if (*s == '~') {
2338         s = tilde_expand(s);
2339         if (!s) s = "";
2340         if (!*s) s = name;
2341     }
2342 #endif /* DTILDE */
2343
2344 #ifdef CKROOT
2345     debug(F111,"zchki setroot",ckroot,ckrootset);
2346     if (ckrootset) if (!zinroot(name)) {
2347         debug(F110,"zchki setroot violation",name,0);
2348         return(-1);
2349     }
2350 #endif /* CKROOT */
2351
2352     x = stat(s,&buf);
2353     debug(F101,"STAT","",5);
2354     if (x < 0) {
2355         debug(F111,"zchki stat fails",s,errno);
2356         return(-1);
2357     }
2358     if (S_ISDIR (buf.st_mode))
2359       itsadir = 1;
2360
2361     if (!(itsadir && zchkid)) {         /* Unless this... */
2362         if (!S_ISREG (buf.st_mode)      /* Must be regular file */
2363 #ifdef S_ISFIFO
2364             && (!matchfifo || !S_ISFIFO (buf.st_mode))  /* or FIFO */
2365 #endif /* S_ISFIFO */
2366             ) {
2367             debug(F111,"zchki not regular file (or fifo)",s,matchfifo);
2368             return(-2);
2369         }
2370     }
2371     debug(F111,"zchki stat ok:",s,x);
2372
2373     if (diractive) {                    /* If listing don't check access */
2374         x = 1;
2375     } else {
2376 #ifdef SW_ACC_ID
2377         debug(F100,"zchki swapping ids for access()","",0);
2378         priv_on();
2379 #endif /* SW_ACC_ID */
2380         if ((x = access(s,R_OK)) < 0)
2381           x = access(s,X_OK);           /* For RUN-class commands */
2382 #ifdef SW_ACC_ID
2383         priv_off();
2384         debug(F100,"zchki swapped ids restored","",0);
2385 #endif /* SW_ACC_ID */
2386     }
2387     if (x < 0) {                        /* Is the file accessible? */
2388         debug(F111,"zchki access failed:",s,x); /* No */
2389         return(-3);
2390     } else {
2391         iflen = buf.st_size;            /* Yes, remember size */
2392         ckstrncpy(nambuf,s,CKMAXPATH);  /* and name globally. */
2393         debug(F111,"zchki access ok:",s,iflen);
2394         return((iflen > -1L) ? iflen : 0L);
2395     }
2396 }
2397
2398 /*  Z C H K O  --  Check if output file can be created  */
2399
2400 /*
2401   Returns -1 if write permission for the file would be denied, 0 otherwise.
2402
2403   NOTE: The design is flawed.  There is no distinction among:
2404    . Can I overwrite an existing file?
2405    . Can I create a file (or directory) in an existing directory?
2406    . Can I create a file (or directory) and its parent(s)?
2407 */
2408 int
2409 zchko(name) char *name; {
2410     int i, x, itsadir = 0;
2411     char *s;
2412     char * oname;
2413     extern int zchkod;                  /* Used by IF WRITEABLE */
2414
2415     debug(F110,"zchko entry",name,0);
2416
2417     if (!name) return(-1);              /* Watch out for null pointer. */
2418
2419     oname = name;
2420
2421 #ifdef CKROOT
2422     debug(F111,"zchko setroot",ckroot,ckrootset);
2423     if (ckrootset) if (!zinroot(name)) {
2424         debug(F110,"zchko setroot violation",name,0);
2425         errno = EACCES;
2426         return(-1);
2427     }
2428 #endif /* CKROOT */
2429
2430     x = (int)strlen(name);              /* Get length of filename */
2431     debug(F111,"zchko len",name,x);
2432     debug(F111,"zchko zchkod",name,zchkod);
2433
2434 #ifdef UNIX
2435 /*
2436   Writing to null device is OK.
2437 */
2438     if (x == 9 && !strcmp(name,"/dev/null"))
2439       return(0);
2440     if (x == 8 && !strcmp(name,"/dev/tty"))
2441       return(0);
2442 #endif /* UNIX */
2443
2444     s = name;
2445 #ifdef DTILDE
2446     if (*s == '~') {
2447         s = tilde_expand(s);
2448         if (!s) s = "";
2449         if (!*s) s = name;
2450         x = strlen(s);
2451     }
2452 #endif /* DTILDE */
2453     name = s;
2454     s = NULL;
2455 /*
2456   zchkod is a global flag meaning we're checking not to see if the directory
2457   file is writeable, but if it's OK to create files IN the directory.
2458 */
2459     if (!zchkod && isdir(name))         /* Directories are not writeable */
2460       return(-1);
2461
2462     s = malloc(x+3);                    /* Must copy because we can't */
2463     if (!s) {                           /* write into our argument. */
2464         fprintf(stderr,"zchko: Malloc error 46\n");
2465         return(-1);
2466     }
2467     ckstrncpy(s,name,x+3);
2468
2469     for (i = x; i > 0; i--) {           /* Strip filename from right. */
2470         if (ISDIRSEP(s[i-1])) {
2471             itsadir = 1;
2472             break;
2473         }
2474     }
2475     debug(F101,"zchko i","",i);
2476     debug(F101,"zchko itsadir","",itsadir);
2477
2478 #ifdef COMMENT
2479 /* X/OPEN XPG3-compliant systems fail if argument ends with "/"...  */
2480     if (i == 0)                         /* If no path, use current directory */
2481       strcpy(s,"./");
2482     else                                /* Otherwise, use given one. */
2483       s[i] = '\0';
2484 #else
2485 #ifdef COMMENT
2486 /*
2487   The following does not work for "foo/bar" where the foo directory does
2488   not exist even though we could create it: access("foo/.") fails, but
2489   access("foo") works OK.
2490 */
2491 /* So now we use "path/." if path given, or "." if no path given. */
2492     s[i++] = '.';                       /* Append "." to path. */
2493     s[i] = '\0';
2494 #else
2495 /* So NOW we strip path segments from the right as long as they don't */
2496 /* exist -- we only call access() for path segments that *do* exist.. */
2497 /* (But this isn't quite right either since now zchko(/foo/bar/baz/xxx) */
2498 /* succeeds when I have write access to foo and bar but baz doesn't exit.) */
2499
2500     if (itsadir && i > 0) {
2501         s[i-1] = '\0';
2502         while (s[0] && !isdir(s)) {
2503             for (i = (int)strlen(s); i > 0; i--) {
2504                 if (ISDIRSEP(s[i-1])) {
2505                     s[i-1] = '\0';
2506                     break;
2507                 }
2508             }
2509             if (i == 0)
2510               s[0] = '\0';
2511         }
2512     } else {
2513         s[i++] = '.';                   /* Append "." to path. */
2514         s[i] = '\0';
2515     }
2516 #endif /* COMMENT */
2517 #endif /* COMMENT */
2518
2519     if (!s[0])
2520       ckstrncpy(s,".",x+3);
2521
2522 #ifdef SW_ACC_ID
2523     debug(F100,"zchko swapping ids for access()","",0);
2524     priv_on();
2525 #endif /* SW_ACC_ID */
2526
2527     x = access(s,W_OK);                 /* Check access of path. */
2528
2529 #ifdef SW_ACC_ID
2530     priv_off();
2531     debug(F100,"zchko swapped ids restored","",0);
2532 #endif /* SW_ACC_ID */
2533
2534     if (x < 0)
2535       debug(F111,"zchko access failed:",s,errno);
2536     else
2537       debug(F111,"zchko access ok:",s,x);
2538     free(s);                            /* Free temporary storage */
2539
2540     return((x < 0) ? -1 : 0);           /* and return. */
2541 }
2542
2543 /*  Z D E L E T  --  Delete the named file.  */
2544
2545 /* Returns: -1 on error, 0 on success */
2546
2547 int
2548 zdelet(name) char *name; {
2549     int x;
2550 #ifdef CK_LOGIN
2551     if (isguest)
2552       return(-1);
2553 #endif /* CK_LOGIN */
2554
2555 #ifdef CKROOT
2556     debug(F111,"zdelet setroot",ckroot,ckrootset);
2557     if (ckrootset) if (!zinroot(name)) {
2558         debug(F110,"zdelet setroot violation",name,0);
2559         return(-1);
2560     }
2561 #endif /* CKROOT */
2562
2563     x = unlink(name);
2564     debug(F111,"zdelet",name,x);
2565 #ifdef CKSYSLOG
2566     if (ckxsyslog >= SYSLG_FC && ckxlogging) {
2567         fullname[0] = '\0';
2568         zfnqfp(name,CKMAXPATH,fullname);
2569         debug(F110,"zdelet fullname",fullname,0);
2570         if (x < 0)
2571           syslog(LOG_INFO, "file[] %s: delete failed (%m)", fullname);
2572         else
2573           syslog(LOG_INFO, "file[] %s: delete ok", fullname);
2574     }
2575 #endif /* CKSYSLOG */
2576     return(x);
2577 }
2578
2579 /*  Z R T O L  --  Convert remote filename into local form  */
2580
2581 VOID
2582 zrtol(name,name2) char *name, *name2; {
2583     nzrtol(name,name2,1,0,CKMAXPATH);
2584 }
2585
2586 VOID
2587 nzrtol(name,name2,fncnv,fnrpath,max)
2588     char *name, *name2; int fncnv, fnrpath, max;
2589 { /* nzrtol */
2590     char *s, *p;
2591     int flag = 0, n = 0;
2592     char fullname[CKMAXPATH+1];
2593     int devnull = 0;
2594     int acase = 0;
2595     if (!name2) return;
2596     if (!name) name = "";
2597
2598     debug(F111,"nzrtol name",name,fncnv);
2599
2600 #ifdef DTILDE
2601     s = name;
2602     if (*s == '~') {
2603         s = tilde_expand(s);
2604         if (!s) s = "";
2605         if (*s) name = s;
2606     }
2607 #endif /* DTILDE */
2608
2609     /* Handle the path -- we don't have to convert its format, since */
2610     /* the standard path format and our (UNIX) format are the same. */
2611
2612     fullname[0] = NUL;
2613     devnull = !strcmp(name,"/dev/null");
2614
2615     if (!devnull && fnrpath == PATH_OFF) { /* RECEIVE PATHNAMES OFF */
2616         zstrip(name,&p);
2617         strncpy(fullname,p,CKMAXPATH);
2618     } else if (!devnull && fnrpath == PATH_ABS) { /* REC PATHNAMES ABSOLUTE */
2619         strncpy(fullname,name,CKMAXPATH);
2620     } else if (!devnull && isabsolute(name)) { /* RECEIVE PATHNAMES RELATIVE */
2621         ckmakmsg(fullname,CKMAXPATH,".",name,NULL,NULL);
2622     } else {                            /* Ditto */
2623         ckstrncpy(fullname,name,CKMAXPATH);
2624     }
2625     fullname[CKMAXPATH] = NUL;
2626     debug(F110,"nzrtol fullname",fullname,0);
2627
2628 #ifndef NOTRUNCATE
2629 /*
2630   The maximum length for any segment of a filename is MAXNAMLEN, defined
2631   above.  On some platforms (at least QNX) if a segment exceeds this limit,
2632   the open fails with ENAMETOOLONG, so we must prevent it by truncating each
2633   overlong name segment to the maximum segment length before passing the
2634   name to open().  This must be done even when file names are literal, so as
2635   not to halt a file transfer unnecessarily.
2636 */
2637     {
2638         char buf[CKMAXPATH+1];          /* New temporary buffer on stack */
2639         char *p = fullname;             /* Source and  */
2640         char *s = buf;                  /* destination pointers */
2641         int i = 0, n = 0;
2642         debug(F101,"nzrtol sizing MAXNAMLEN","",MAXNAMLEN);
2643         while (*p && n < CKMAXPATH) {   /* Copy name to new buffer */
2644             if (++i > MAXNAMLEN) {      /* If this segment too long */
2645                 while (*p && *p != '/') /* skip past the rest... */
2646                   p++;
2647                 i = 0;                  /* and reset counter. */
2648             } else if (*p == '/') {     /* End of this segment. */
2649                 i = 0;                  /* Reset counter. */
2650             }
2651             *s++ = *p++;                /* Copy this character. */
2652             n++;
2653         }
2654         *s = NUL;
2655         ckstrncpy(fullname,buf,CKMAXPATH); /* Copy back to original buffer. */
2656         debug(F111,"nzrtol sizing",fullname,n);
2657     }
2658 #endif /* NOTRUNCATE */
2659
2660     if (!fncnv || devnull) {            /* Not converting */
2661         ckstrncpy(name2,fullname,max);  /* We're done. */
2662         return;
2663     }
2664     name = fullname;                    /* Converting */
2665
2666     p = name2;
2667     for (; *name != '\0' && n < maxnam; name++) {
2668         if (*name > SP) flag = 1;       /* Strip leading blanks and controls */
2669         if (flag == 0 && *name < '!')
2670           continue;
2671         if (fncnv > 0) {
2672             if (*name == SP) {
2673                 *p++ = '_';
2674                 n++;
2675                 continue;
2676             }
2677             if (isupper(*name))         /* Check for mixed case */
2678               acase |= 1;
2679             else if (islower(*name))
2680               acase |= 2;
2681         }
2682         *p++ = *name;
2683         n++;
2684     }
2685     *p-- = '\0';                        /* Terminate */
2686     while (*p < '!' && p > name2)       /* Strip trailing blanks & controls */
2687       *p-- = '\0';
2688
2689     if (*name2 == '\0') {               /* Nothing left? */
2690         ckstrncpy(name2,"NONAME",max);  /* do this... */
2691     } else if (acase == 1) {            /* All uppercase? */
2692         p = name2;                      /* So convert all letters to lower */
2693         while (*p) {
2694             if (isupper(*p))
2695               *p = tolower(*p);
2696             p++;
2697         }
2698     }
2699     debug(F110,"nzrtol new name",name2,0);
2700 }
2701
2702
2703 /*  Z S T R I P  --  Strip device & directory name from file specification */
2704
2705 /*  Strip pathname from filename "name", return pointer to result in name2 */
2706
2707 static char work[CKMAXPATH+1];
2708
2709 VOID
2710 zstrip(name,name2) char *name, **name2; {
2711     char *cp, *pp;
2712     int n = 0;
2713
2714     debug(F110,"zstrip before",name,0);
2715     if (!name) { *name2 = ""; return; }
2716     pp = work;
2717 #ifdef DTILDE
2718     /* Strip leading tilde */
2719     if (*name == '~') name++;
2720     debug(F110,"zstrip after tilde-stripping",name,0);
2721 #endif /* DTILDE */
2722     for (cp = name; *cp; cp++) {
2723         if (ISDIRSEP(*cp)) {
2724             pp = work;
2725             n = 0;
2726         } else {
2727             *pp++ = *cp;
2728             if (n++ >= CKMAXPATH)
2729               break;
2730         }
2731     }
2732     *pp = '\0';                         /* Terminate the string */
2733     *name2 = work;
2734     debug(F110,"zstrip after",*name2,0);
2735 }
2736
2737 /*  Z L T O R  --  Local TO Remote */
2738
2739 VOID
2740 zltor(name,name2) char *name, *name2; {
2741     nzltor(name,name2,1,0,CKMAXPATH);
2742 }
2743
2744 /*  N Z L T O R  --  New Local TO Remote */
2745
2746 /*
2747   fncnv = 0 for no conversion, > 0 for regular conversion, < 0 for minimal.
2748 */
2749 VOID
2750 nzltor(name,name2,fncnv,fnspath,max)
2751     char *name, *name2; int fncnv, fnspath, max;
2752 { /* nzltor */
2753     char *cp, *pp;
2754 #ifdef COMMENT
2755     int dc = 0;
2756 #endif /* COMMENT */
2757     int n = 0;
2758     char *dotp = NULL;
2759     char *dirp = NULL;
2760     char fullname[CKMAXPATH+1];
2761     char *p;
2762     CHAR c;
2763
2764 #ifndef NOCSETS
2765     extern int fcharset, /* tcharset, */ language;
2766     int langsv;
2767     _PROTOTYP ( CHAR (*sxo), (CHAR) ) = NULL; /* Translation functions */
2768 #ifdef CK_ANSIC
2769     extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR);
2770 #else
2771     extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])();
2772 #endif /* CK_ANSIC */
2773     langsv = language;
2774     language = L_USASCII;
2775 #ifdef COMMENT
2776     /* Proper translation of filenames must be done elsewhere */
2777     n = tcharset ? tcharset : TC_USASCII;
2778     sxo = xls[n][fcharset];
2779 #else
2780     sxo = xls[TC_USASCII][fcharset];
2781 #endif /* COMMENT */
2782 #endif /* NOCSETS */
2783
2784     debug(F110,"nzltor name",name,0);
2785
2786     /* Handle pathname */
2787
2788     fullname[0] = NUL;
2789     if (fnspath == PATH_OFF) {          /* PATHNAMES OFF */
2790         zstrip(name,&p);
2791         ckstrncpy(fullname,p,CKMAXPATH);
2792     } else {                            /* PATHNAMES RELATIVE or ABSOLUTE */
2793         char * p = name;
2794         while (1) {
2795             if (!strncmp(p,"../",3))
2796               p += 3;
2797             else if (!strncmp(p,"./",2))
2798               p += 2;
2799             else
2800               break;
2801         }
2802         if (fnspath == PATH_ABS) {      /* ABSOLUTE */
2803             zfnqfp(p,CKMAXPATH,fullname);
2804         } else {                        /* RELATIVE */
2805             ckstrncpy(fullname,p,CKMAXPATH);
2806         }
2807     }
2808     debug(F110,"nzltor fullname",fullname,0);
2809
2810     if (!fncnv) {                       /* Not converting */
2811         ckstrncpy(name2,fullname,max);  /* We're done. */
2812 #ifndef NOCSETS
2813         langsv = language;
2814 #endif /* NOCSETS */
2815         return;
2816     }
2817     name = fullname;                    /* Converting */
2818
2819 #ifdef aegis
2820     char *namechars;
2821     int tilde = 0, bslash = 0;
2822
2823     if ((namechars = getenv("NAMECHARS")) != NULL) {
2824         if (ckstrchr(namechars, '~' ) != NULL) tilde  = '~';
2825         if (ckstrchr(namechars, '\\') != NULL) bslash = '\\';
2826     } else {
2827         tilde = '~';
2828         bslash = '\\';
2829     }
2830 #endif /* aegis */
2831
2832     pp = work;                          /* Output buffer */
2833     for (cp = name, n = 0; *cp && n < max; cp++,n++) { /* Convert name chars */
2834         c = *cp;
2835 #ifndef NOCSETS
2836         if (sxo) c = (*sxo)(c);         /* Convert to ASCII */
2837 #endif /* NOCSETS */
2838         if (fncnv > 0 && islower(c))    /* Uppercase letters */
2839           *pp++ = toupper(c);           /* Change tilde to hyphen */
2840         else if (c == '~')
2841           *pp++ = '-';
2842         else if (fncnv > 0 && c == '#') /* Change number sign to 'X' */
2843           *pp++ = 'X';
2844         else if (c == '*' || c == '?')  /* Change wildcard chars to 'X' */
2845           *pp++ = 'X';
2846         else if (c == ' ')              /* Change space to underscore */
2847           *pp++ = '_';
2848         else if (c < ' ')               /* Change controls to 'X' */
2849           *pp++ = 'X';
2850         else if (fncnv > 0 && c == '.') { /* Change dot to underscore */
2851             dotp = pp;                  /* Remember where we last did this */
2852             *pp++ = '_';
2853         } else {
2854             if (c == '/')
2855               dirp = pp;
2856             *pp++ = c;
2857         }
2858     }
2859     *pp = NUL;                          /* Tie it off. */
2860 #ifdef COMMENT
2861     if (dotp) *dotp = '.';              /* Restore last dot (if any) */
2862 #else
2863     if (dotp > dirp) *dotp = '.';       /* Restore last dot in file name */
2864 #endif /* COMMENT */
2865     cp = name2;                         /* If nothing before dot, */
2866     if (*work == '.') *cp++ = 'X';      /* insert 'X' */
2867     ckstrncpy(cp,work,max);
2868 #ifndef NOCSETS
2869     language = langsv;
2870 #endif /* NOCSETS */
2871     debug(F110,"nzltor name2",name2,0);
2872 }
2873
2874
2875 /*  Z C H D I R  --  Change directory  */
2876 /*
2877   Call with:
2878     dirnam = pointer to name of directory to change to,
2879       which may be "" or NULL to indicate user's home directory.
2880   Returns:
2881     0 on failure
2882     1 on success
2883 */
2884 int
2885 zchdir(dirnam) char *dirnam; {
2886     char *hd, *sp;
2887 #ifdef IKSDB
2888     _PROTOTYP (int slotdir,(char *,char *));
2889 #endif /* IKSDB */
2890 #ifndef NOSPL
2891     extern struct mtab *mactab;             /* Main macro table */
2892     extern int nmac;                        /* Number of macros */
2893 #endif /* NOSPL */
2894
2895     debug(F110,"zchdir",dirnam,0);
2896     if (!dirnam) dirnam = "";
2897     if (!*dirnam)                       /* If argument is null or empty, */
2898       dirnam = zhome();                 /* use user's home directory. */
2899     sp = dirnam;
2900     debug(F110,"zchdir 2",dirnam,0);
2901
2902 #ifdef DTILDE
2903     hd = tilde_expand(dirnam);          /* Attempt to expand tilde */
2904     if (!hd) hd = "";
2905     if (*hd == '\0') hd = dirnam;       /* in directory name. */
2906 #else
2907     hd = dirnam;
2908 #endif /* DTILDE */
2909     debug(F110,"zchdir 3",hd,0);
2910
2911 #ifdef CKROOT
2912     debug(F111,"zchdir setroot",ckroot,ckrootset);
2913     if (ckrootset) if (!zinroot(hd)) {
2914         debug(F110,"zchdir setroot violation",hd,0);
2915         return(0);
2916     }
2917 #endif /* CKROOT */
2918
2919 #ifdef pdp11
2920     /* Just to save some space */
2921     return((chdir(hd) == 0) ? 1 : 0);
2922 #else
2923     if (chdir(hd) == 0) {                       /* Try to cd */
2924 #ifdef IKSDB
2925 #ifdef CK_LOGIN
2926         if (inserver && ikdbopen)
2927           slotdir(isguest ? anonroot : "", zgtdir());
2928 #endif /* CK_LOGIN */
2929 #endif /* IKSDB */
2930
2931 #ifndef NOSPL
2932         if (nmac) {                     /* Any macros defined? */
2933             int k;                      /* Yes */
2934             static int on_cd = 0;
2935             if (!on_cd) {
2936                 on_cd = 1;
2937                 k = mlook(mactab,"on_cd",nmac);   /* Look this up */
2938                 if (k >= 0) {                     /* If found, */
2939                     if (dodo(k,zgtdir(),0) > -1)  /* set it up, */
2940                       parser(1);                  /* and execute it */
2941                 }
2942                 on_cd = 0;
2943             }
2944         }
2945 #endif /* NOSPL */
2946         return(1);
2947     }
2948     return(0);
2949 #endif /* pdp11 */
2950 }
2951
2952 int
2953 #ifdef CK_ANSIC
2954 zchkpid(unsigned long xpid)
2955 #else
2956 zchkpid(xpid) unsigned long xpid;
2957 #endif /* CK_ANSIC */
2958 {
2959     return((kill((PID_T)xpid,0) < 0) ? 0 : 1);
2960 }
2961
2962
2963 /*  Z H O M E  --  Return pointer to user's home directory  */
2964
2965 static char * zhomdir = NULL;
2966
2967 char *
2968 zhome() {
2969     char * home;
2970
2971 #ifdef CKROOT
2972     if (ckrootset)
2973       return((char *)ckroot);
2974 #endif /* CKROOT */
2975
2976 #ifdef Plan9
2977     home = getenv("home");
2978 #else
2979     home = getenv("HOME");
2980 #endif /* Plan9 */
2981     makestr(&zhomdir,home);
2982     return(home ? zhomdir : ".");
2983 }
2984
2985 /*  Z G T D I R  --  Returns a pointer to the current directory  */
2986
2987 /*
2988   The "preferred" interface for getting the current directory in modern UNIX
2989   is getcwd() [POSIX 1003.1 5.2.2].  However, on certain platforms (such as
2990   SunOS), it is implemented by forking a shell, feeding it the pwd command,
2991   and returning the result, which is not only inefficient but also can result
2992   in stray messages to the terminal.  In such cases -- as well as when
2993   getcwd() is not available at all -- getwd() can be used instead by defining
2994   USE_GETWD.  However, note that getwd() provides no buffer-length argument
2995   and therefore no safeguard against memory leaks.
2996 */
2997 #ifndef USE_GETWD
2998 #ifdef BSD42
2999 #define USE_GETWD
3000 #else
3001 #ifdef SUNOS4
3002 #define USE_GETWD
3003 #endif /* SUNOS4 */
3004 #endif /* BSD42 */
3005 #endif /* USE_GETWD */
3006
3007 #ifdef pdp11
3008 #define CWDBL 80                        /* Save every byte we can... */
3009 #else
3010 #define CWDBL CKMAXPATH
3011 #endif /* pdp11 */
3012 static char cwdbuf[CWDBL+2];
3013 /*
3014   NOTE: The getcwd() prototypes are commented out on purpose.  If you get
3015   compile-time warnings, search through your system's header files to see
3016   which one has the needed prototype, and #include it.  Usually it is
3017   <unistd.h>.  See the section for including <unistd.h> in ckcdeb.h and
3018   make any needed adjustments there (and report them).
3019 */
3020 char *
3021 zgtdir() {
3022     char * buf = cwdbuf;
3023     char * s;
3024
3025 #ifdef USE_GETWD
3026     extern char *getwd();
3027     s = getwd(buf);
3028     debug(F110,"zgtdir BSD4 getwd()",s,0);
3029     if (!s) s = "./";
3030     return(s);
3031 #else
3032 #ifdef BSD44
3033 #ifdef DCLGETCWD
3034 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3035 #endif /* DCLGETCWD */
3036     debug(F101,"zgtdir BSD44 CWDBL","",CWDBL);
3037     s = getcwd(buf,CWDBL);
3038     if (!s) s = "./";
3039     return(s);
3040 #else
3041 #ifdef MINIX2
3042 #ifdef DCLGETCWD
3043     _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3044 #endif /* DCLGETCWD */
3045     debug(F101,"zgtdir MINIX2 CWDBL","",CWDBL);
3046     s = getcwd(buf,CWDBL);
3047     if (!s) s = "./";
3048     return(s);
3049 #else
3050 #ifdef SVORPOSIX
3051 #ifdef COMMENT
3052 /* This non-ANSI prototype can be fatal at runtime! (e.g. in SCO3.2v5.0.5). */
3053 /* Anyway it's already prototyped in some header file that we have included. */
3054     extern char *getcwd();
3055 #else
3056 #ifdef DCLGETCWD
3057     _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3058 #endif /* DCLGETCWD */
3059 #endif /* COMMENT */
3060     debug(F101,"zgtdir SVORPOSIX CWDBL","",CWDBL);
3061     s = getcwd(buf,CWDBL);
3062     if (!s) s = "./";
3063     return(s);
3064 #else
3065 #ifdef COHERENT
3066 #ifdef _I386
3067 #ifdef DCLGETCWD
3068     extern char *getcwd();
3069 #endif /* DCLGETCWD */
3070     debug(F101,"zgtdir COHERENT _I386 CWDBL","",CWDBL);
3071     s = getcwd(buf,CWDBL);
3072     if (!s) s = "./";
3073     return(s);
3074 #else
3075     extern char *getwd();
3076     debug(F101,"zgtdir COHERENT CWDBL","",CWDBL);
3077     s = getwd(buf);
3078     if (!s) s = "./";
3079     return(s);
3080 #endif /* _I386 */
3081 #else
3082 #ifdef SUNOS4
3083     debug(F101,"zgtdir SUNOS CWDBL","",CWDBL);
3084     s = getcwd(buf,CWDBL);
3085     if (!s) s = "./";
3086     return(s);
3087 #else
3088     return("./");
3089 #endif /* SUNOS4 */
3090 #endif /* COHERENT */
3091 #endif /* SYSVORPOSIX */
3092 #endif /* MINIX2 */
3093 #endif /* BSD44 */
3094 #endif /* USE_GETWD */
3095 }
3096
3097 /*  Z X C M D -- Run a system command so its output can be read like a file */
3098
3099 #ifndef NOPUSH
3100 int
3101 zxcmd(filnum,comand) int filnum; char *comand; {
3102     int out;
3103     int pipes[2];
3104     extern int kactive;                 /* From ckcpro.w and ckcmai.c */
3105
3106     if (nopush) {
3107         debug(F100,"zxcmd fails: nopush","",0);
3108         return(-1);
3109     }
3110     debug(F111,"zxcmd",comand,filnum);
3111     if (chkfn(filnum) < 0) return(-1);  /* Need a valid Kermit file number. */
3112     if (filnum == ZSTDIO || filnum == ZCTERM) /* But not one of these. */
3113       return(0);
3114
3115     out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
3116     debug(F101,"zxcmd out",comand,out);
3117
3118 /* Output to a command */
3119
3120     if (out) {                          /* Need popen() to do this. */
3121         ckstrncpy(fullname,"(pipe)",CKMAXPATH);
3122 #ifdef NOPOPEN
3123         return(0);                      /* no popen(), fail. */
3124 #else
3125 /* Use popen() to run the command. */
3126
3127 #ifdef _POSIX_SOURCE
3128 /* Strictly speaking, popen() is not available in POSIX.1 */
3129 #define DCLPOPEN
3130 #endif /* _POSIX_SOURCE */
3131
3132         debug(F110,"zxcmd out",comand,0);
3133
3134         if (priv_chk()) {
3135             debug(F100,"zxcmd priv_chk failed","",0);
3136             return(0);
3137         }       
3138         errno = 0;
3139         fp[filnum] = popen(comand,"w");
3140         debug(F111,"zxcmd popen",fp[filnum] ? "OK" : "Failed", errno);
3141         if (fp[filnum] == NULL)
3142           return(0);
3143 #ifdef COMMENT
3144 /* I wonder what this is all about... */
3145         close(pipes[0]);                /* Don't need the input side */
3146         fp[filnum] = fdopen(pipes[1],"w"); /* Open output stream. */
3147         fp[ZSYSFN] = fp[filnum];           /* Remember. */
3148 #endif /* COMMENT */
3149         ispipe[filnum] = 1;
3150         zoutcnt = 0;                    /* (PWP) reset input buffer */
3151         zoutptr = zoutbuffer;
3152         return(1);
3153 #endif /* NOPOPEN */
3154     }
3155
3156 /* Input from a command */
3157
3158 #ifdef SNI541
3159     /* SINIX-L 5.41 does not like fdopen() */
3160     return(0);
3161 #else
3162     if (pipe(pipes) != 0) {
3163         debug(F100,"zxcmd pipe failure","",0);
3164         return(0);                      /* can't make pipe, fail */
3165     }
3166
3167 /* Create a fork in which to run the named process */
3168
3169     if ((
3170 #ifdef aegis
3171          pid = vfork()                  /* child */
3172 #else
3173          pid = fork()                   /* child */
3174 #endif /* aegis */
3175          ) == 0) {
3176
3177 /* We're in the fork. */
3178
3179         char *shpath, *shname, *shptr;  /* Find user's preferred shell */
3180 #ifndef aegis
3181         struct passwd *p;
3182         char *defshell;
3183 #ifdef HPUX10                           /* Default shell */
3184         defshell = "/usr/bin/sh";
3185 #else
3186 #ifdef Plan9
3187         defshell = "/bin/rc";
3188 #else
3189         defshell = "/bin/sh";
3190 #endif /* Plan9 */
3191 #endif /* HPUX10 */
3192 #endif /* aegis */
3193         if (priv_can()) exit(1);        /* Turn off any privileges! */
3194         debug(F101,"zxcmd pid","",pid);
3195         close(pipes[0]);                /* close input side of pipe */
3196         close(0);                       /* close stdin */
3197         if (open("/dev/null",0) < 0) return(0); /* replace input by null */
3198 #ifndef OXOS
3199 #ifndef SVORPOSIX
3200         dup2(pipes[1],1);               /* BSD: replace stdout & stderr */
3201         dup2(pipes[1],2);               /* by the pipe */
3202 #else
3203         close(1);                       /* AT&T: close stdout */
3204         if (dup(pipes[1]) != 1)         /* Send stdout to the pipe */
3205           return(0);
3206         close(2);                       /* Send stderr to the pipe */
3207         if (dup(pipes[1]) != 2)
3208           return(0);
3209 #endif /* SVORPOSIX */
3210 #else /* OXOS */
3211         dup2(pipes[1],1);
3212         dup2(pipes[1],2);
3213 #endif /* OXOS */
3214         close(pipes[1]);                /* Don't need this any more. */
3215
3216 #ifdef aegis
3217         if ((shpath = getenv("SERVERSHELL")) == NULL)
3218           shpath = "/bin/sh";
3219 #else
3220         shpath = getenv("SHELL");       /* What shell? */
3221         if (shpath == NULL) {
3222             p = getpwuid( real_uid() ); /* Get login data */
3223             debug(F111,"zxcmd shpath","getpwuid()",p);
3224             if (p == (struct passwd *)NULL || !*(p->pw_shell))
3225               shpath = defshell;
3226             else shpath = p->pw_shell;
3227         }
3228 #endif /* aegis */
3229         shptr = shname = shpath;
3230         while (*shptr != '\0')
3231           if (*shptr++ == '/')
3232             shname = shptr;
3233         debug(F110,shpath,shname,0);
3234         restorsigs();                   /* Restore ignored signals */
3235         execl(shpath,shname,"-c",comand,(char *)NULL); /* Execute the cmd */
3236         exit(0);                        /* just punt if it failed. */
3237     } else if (pid == (PID_T) -1) {
3238         debug(F100,"zxcmd fork failure","",0);
3239         return(0);
3240     }
3241     debug(F101,"zxcmd pid","",pid);
3242     close(pipes[1]);                    /* Don't need the output side */
3243     ispipe[filnum] = 1;                 /* Remember it's a pipe */
3244     fp[filnum] = fdopen(pipes[0],"r");  /* Open a stream for input. */
3245
3246 #ifdef DONDELAY
3247 #ifdef SELECT
3248     if (filnum == ZIFILE && kactive) {  /* Make pipe reads nonblocking */
3249         int flags, x;
3250         if ((flags = fcntl(fileno(fp[filnum]),F_GETFL,0)) > -1) {
3251             debug(F101,"zxcmd fcntl 1 pipe flags","",flags);
3252             x = fcntl(fileno(fp[filnum]),F_SETFL, flags |
3253 #ifdef QNX
3254                   O_NONBLOCK
3255 #else
3256                   O_NDELAY
3257 #endif /* QNX */
3258                   );
3259             debug(F101,"zxcmd fcntl 2 result","",x);
3260         }
3261     }
3262 #endif /* SELECT */
3263 #endif /* DONDELAY */
3264 #endif /* SNI541 */
3265     fp[ZSYSFN] = fp[filnum];            /* Remember. */
3266     zincnt = 0;                         /* (PWP) reset input buffer */
3267     zinptr = zinbuffer;
3268     fullname[0] = '\0';
3269     return(1);
3270 } /* zxcmd */
3271
3272 /*  Z C L O S F  - wait for the child fork to terminate and close the pipe. */
3273
3274 /*  Used internally by zclose - returns -1 on failure, 1 on success. */
3275
3276 int
3277 zclosf(filnum) int filnum; {
3278     int wstat, out;
3279     int statusp;
3280
3281     debug(F101,"zclosf filnum","",filnum);
3282     out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
3283     debug(F101,"zclosf out","",out);
3284
3285 #ifndef NOPOPEN
3286     if (ispipe[filnum]
3287         /* In UNIX we use popen() only for output files */
3288         && out
3289         ) {
3290         int x;
3291         x = pclose(fp[filnum]);
3292         pexitstat = x >> 8;
3293         debug(F101,"zclosf pclose","",x);
3294         debug(F101,"zclosf pexitstat","",pexitstat);
3295         fp[filnum] = fp[ZSYSFN] = NULL;
3296         ispipe[filnum] = 0;
3297         return((x != 0) ? -1 : 1);
3298     }
3299 #endif /* NOPOPEN */
3300     debug(F101,"zclosf fp[filnum]","", fp[filnum]);
3301     debug(F101,"zclosf fp[ZSYSFN]","", fp[ZSYSFN]);
3302
3303     if (pid != (PID_T) 0) {
3304         debug(F101,"zclosf killing pid","",pid);
3305 #ifdef Plan9
3306         kill(pid, SIGKILL);
3307 #else
3308         kill(pid,9);
3309 #endif /* Plan9 */
3310
3311 #ifndef CK_CHILD
3312 /*
3313   This is the original code (before 20 April 1997) and has proven totally
3314   portable.  But it does not give us the process's return code.
3315 */
3316         while ((wstat = wait((WAIT_T *)0)) != pid && wstat != -1) ;
3317 #else
3318 /* Here we try to get the return code.  Let's hope this is portable too. */
3319         while ((wstat = wait(&statusp)) != pid && wstat != -1) ;
3320         pexitstat = (statusp & 0xff) ? statusp : statusp >> 8;
3321         debug(F101,"zclosf wait statusp","",statusp);
3322         debug(F101,"zclosf wait pexitstat","",pexitstat);
3323 #endif /* CK_CHILD */
3324         pid = 0;
3325     }
3326     fclose(fp[filnum]);
3327     fp[filnum] = fp[ZSYSFN] = NULL;
3328
3329     ispipe[filnum] = 0;
3330     debug(F101,"zclosf fp[filnum]","",fp[filnum]);
3331 #ifdef CK_CHILD
3332     return(pexitstat == 0 ? 1 : -1);
3333 #else
3334     return(1);
3335 #endif /* CK_CHILD */
3336 }
3337
3338 #else  /* NOPUSH */
3339
3340 int
3341 zxcmd(filnum,comand) int filnum; char *comand; {
3342     return(0);
3343 }
3344 int
3345 zclosf(filnum) int filnum; {
3346     return(EOF);
3347 }
3348 #endif /* NOPUSH */
3349
3350
3351 /*  Z X P A N D  --  Expand a wildcard string into an array of strings  */
3352 /*
3353   As of C-Kermit 7.0, this API is obsolete, replaced by nzxpand(), and this
3354   function is only used internally.  See nzxpand() below.
3355
3356   Returns the number of files that match fnarg, with data structures set up
3357   so that first file (if any) will be returned by the next znext() call.
3358
3359   Depends on external variable wildxpand: 0 means we expand wildcards
3360   internally, nonzero means we call the shell to do it.
3361 */
3362 static int xdironly = 0;
3363 static int xfilonly = 0;
3364 static int xmatchdot = 0;
3365 static int xrecursive = 0;
3366 static int xnobackup = 0;
3367 static int xnolinks = 0;
3368
3369 static char *freeptr = NULL, **resptr = NULL; /* Copies of caller's args */
3370 static int remlen;                      /* Remaining space in caller's array */
3371 static int numfnd = 0;                  /* Number of matches found */
3372
3373 #define MINSPACE 1024
3374
3375 static int
3376 initspace(resarry,len) char * resarry[]; int len; {
3377 #ifdef DYNAMIC
3378     if (len < MINSPACE) len = MINSPACE;
3379     if (!sspace) {                      /* Need to allocate string space? */
3380         while (len >= MINSPACE) {
3381             if ((sspace = malloc(len+2))) { /* Got it. */
3382                 debug(F101,"fgen string space","",len);
3383                 break;
3384             }
3385             len = (len / 2) + (len / 4); /* Didn't, reduce by 3/4 */
3386         }
3387         if (len <= MINSPACE) {          /* Did we get it? */
3388             fprintf(stderr,"fgen can't malloc string space\n");
3389             return(-1);
3390         }
3391         ssplen = len;
3392     }
3393 #endif /* DYNAMIC */
3394
3395     freeptr = sspace;                   /* This is where matches are copied. */
3396     resptr = resarry;                   /* Static copies of these so */
3397     remlen = len;                       /* recursive calls can alter them. */
3398     debug(F101,"initspace ssplen","",ssplen);
3399     return(0);
3400 }
3401
3402 /*
3403   Z S E T F I L  --  Query or change the size of file list buffers.
3404
3405   fc = 1: Change current string space to n, return new size.
3406   fc = 2: Return current string space size.
3407   fc = 3: Change current maxnames to n, return new maxnames.
3408   fc = 4: Return current maxnames.
3409   Returns < 0 on error.
3410 */
3411 int
3412 zsetfil(n, fc) int n, fc; {
3413 #ifdef DYNAMIC
3414     switch (fc) {
3415       case 1:                           /* Stringspace */
3416         if (sspace) {
3417             free(sspace);
3418             sspace = NULL;
3419         }
3420         if (initspace(mtchs,n) < 0)
3421           return(-1);
3422       case 2:                           /* Fall thru deliberately */
3423         return(ssplen);
3424       case 3:                           /* Listsize */
3425         if (mtchs) {
3426             free((char *)mtchs);
3427             mtchs = NULL;
3428         }
3429         mtchs = (char **)malloc(n * sizeof(char *));
3430         if (!mtchs)
3431           return(-1);
3432         maxnames = n;
3433       case 4:                           /* Fall thru deliberately */
3434         return(maxnames);
3435     }
3436 #endif /* DYNAMIC */
3437     return(-1);
3438 }
3439
3440
3441
3442 #ifndef NONZXPAND
3443 #ifndef pdp11
3444 static
3445 #endif /* pdp11 */
3446 #endif /* NONZXPAND */
3447 int
3448 zxpand(fnarg) char *fnarg; {
3449     extern int diractive;
3450     char fnbuf[CKMAXPATH+8], * fn, * p;
3451
3452 #ifdef DTILDE                           /* Built with tilde-expansion? */
3453     char *tnam;
3454 #endif /* DTILDE */
3455     int x;
3456     int haveonedir = 0;
3457
3458     if (!fnarg) {                       /* If no argument provided */
3459         nxpand = fcount = 0;
3460         return(0);                      /* Return zero files found */
3461     }
3462     debug(F110,"zxpand entry",fnarg,0);
3463     debug(F101,"zxpand xdironly","",xdironly);
3464     debug(F101,"zxpand xfilonly","",xfilonly);
3465
3466     if (!*fnarg) {                      /* If no argument provided */
3467         nxpand = fcount = 0;
3468         return(0);                      /* Return zero files found */
3469     }
3470
3471 #ifdef CKROOT
3472     debug(F111,"zxpand setroot",ckroot,ckrootset);
3473     if (ckrootset) if (!zinroot(fnarg)) {
3474         debug(F110,"zxpand setroot violation",fnarg,0);
3475         nxpand = fcount = 0;
3476         return(0);
3477     }
3478 #endif /* CKROOT */
3479
3480 #ifdef COMMENT
3481 /*
3482   This would have been perfect, except it makes us return fully qualified
3483   pathnames for all files.
3484 */
3485     zfnqfp(fnarg,CKMAXPATH,fnbuf);
3486     debug(F110,"zxpand zfnqfp",fnbuf,0);
3487     s = zgtdir();
3488     debug(F110,"zxpand zgtdir",s,0);
3489     p = fnbuf;
3490     while (*p && *s)                    /* Make it relative */
3491       if (*s++ != *p++)
3492         break;
3493     fn = (*s) ? fnbuf : p;
3494     debug(F110,"zxpand fn 0",fn,0);
3495     if (!*fn) {
3496         fn = fnbuf;
3497         fnbuf[0] = '*';
3498         fnbuf[1] = '\0';
3499     }
3500     debug(F110,"zxpand fn 0.5",fn,0);
3501 #else
3502 #ifdef DTILDE                           /* Built with tilde-expansion? */
3503     if (*fnarg == '~') {                /* Starts with tilde? */
3504         tnam = tilde_expand(fnarg);     /* Try to expand it. */
3505         ckstrncpy(fnbuf,tnam,CKMAXPATH);
3506     } else
3507 #endif /* DTILDE */
3508       ckstrncpy(fnbuf,fnarg,CKMAXPATH);
3509     fn = fnbuf;                         /* Point to what we'll work with */
3510 #endif /* COMMENT */
3511     debug(F110,"zxpand fn 1",fn,0);
3512
3513     if (!*fn)                           /* But make sure something is there */
3514       return(0);
3515
3516     p = fn + (int)strlen(fn) - 1;
3517     if (*p == '/') {                    /* If last char = / it must be a dir */
3518         if (!xfilonly && !iswild(p)) haveonedir++;
3519         ckstrncat(fn, "*", CKMAXPATH+8); /* so append '*' */
3520     } else if (p > fn) {                /* If ends in "/." */
3521         if (*(p-1) == '/' && *p == '.') /* change '.' to '*' */
3522           *p = '*';
3523     } else if (p == fn) {               /* If it's '.' alone */
3524         if (*p == '.')                  /* change '.' to '*' */
3525           *p = '*';
3526     }
3527     debug(F110,"zxpand fn 2",fn,0);
3528     x = isdir(fn);                      /* Is it a directory? */
3529     debug(F111,"zxpand isdir 1",fn,x);
3530     if (x) {                            /* If so, make it into a wildcard */
3531         if (!xfilonly && !iswild(p))
3532           haveonedir++;
3533         if ((x = strlen(fn)) > 0) {
3534             if (!ISDIRSEP(fn[x-1]))
3535               fn[x++] = DIRSEP;
3536             fn[x++] = '*';
3537             fn[x] = '\0';
3538         }
3539     }
3540     debug(F111,"zxpand fn 3",fn,haveonedir);
3541 /*
3542   The following allows us to parse a single directory name without opening
3543   the directory and looking at its contents.  The diractive flag is a horrible
3544   hack (especially since DIR /NORECURSIVE turns it off), but otherwise we'd
3545   have to change the API.
3546 */
3547     if (!diractive && haveonedir) {
3548 #ifdef COMMENT
3549         fcount = (mtchs == NULL &&
3550                   (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
3551           ? 0 : 1;
3552 #else
3553         fcount = 0;
3554         if (!mtchs) {
3555             mtchs = (char **)malloc(maxnames * sizeof(*mtchs));
3556             if (mtchs)
3557               fcount = 1;
3558             if (!fcount)
3559               return(nxpand = fcount);
3560         }
3561 #endif /* COMMENT */
3562         debug(F110,"zxpand haveonedir A1",fnarg,0);
3563         initspace(mtchs,ssplen);
3564         addresult(fnarg,1);
3565         if (numfnd < 0) return(-1);
3566         mtchptr = mtchs;                /* Save pointer for next. */
3567         debug(F110,"zxpand haveonedir A2",*mtchptr,0);
3568         return(nxpand = fcount);
3569     }
3570
3571 #ifndef NOPUSH
3572     if (!nopush && wildxpand)           /* Who is expanding wildcards? */
3573       fcount = (mtchs == NULL &&        /* Shell */
3574                 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
3575         ? 0
3576           :  shxpand(fn,mtchs,maxnames);
3577     else
3578 #endif /* NOPUSH */
3579       fcount = (mtchs == NULL &&        /* Kermit */
3580                 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
3581         ? 0
3582           : fgen(fn,mtchs,maxnames);      /* Look up the file. */
3583
3584     if (fcount == 0 && haveonedir) {
3585         fcount = 1;
3586         debug(F110,"zxpand haveonedir B",fnarg,0);
3587         addresult(fnarg,1);
3588         if (numfnd < 0) return(-1);
3589     }
3590     mtchptr = mtchs;                    /* Save pointer for next. */
3591     nxpand = fcount;
3592
3593 #ifdef DEBUG
3594     if (deblog) {
3595         if (fcount > 1)
3596           debug(F111,"zxpand ok",mtchs[0],fcount);
3597         else
3598           debug(F101,"zxpand fcount","",fcount);
3599     }
3600 #endif /* DEBUG */
3601     return(fcount);
3602 }
3603
3604 #ifndef NONZXPAND
3605 /*  N Z X P A N D  --  Expand a file list, with options.  */
3606 /*
3607   Call with:
3608    s = pointer to filename or pattern.
3609    flags = option bits:
3610
3611      flags & ZX_FILONLY   Match regular files
3612      flags & ZX_DIRONLY   Match directories
3613      flags & ZX_RECURSE   Descend through directory tree
3614      flags & ZX_MATCHDOT  Match "dot files"
3615      flags & ZX_NOBACKUP  Don't match "backup files"
3616      flags & ZX_NOLINKS   Don't follow symlinks.
3617
3618    Returns the number of files that match s, with data structures set up
3619    so that first file (if any) will be returned by the next znext() call.
3620 */
3621 int
3622 nzxpand(s,flags) char * s; int flags; {
3623     char * p;
3624     int x;
3625
3626     debug(F111,"nzxpand",s,flags);
3627     x = flags & (ZX_DIRONLY|ZX_FILONLY);
3628     xdironly = (x == ZX_DIRONLY);
3629     xfilonly = (x == ZX_FILONLY);
3630     if (xdironly && xfilonly) {
3631         xdironly = 0;
3632         xfilonly = 0;
3633     }
3634     xmatchdot  = (flags & ZX_MATCHDOT);
3635     debug(F111,"nzxpand xmatchdot 1",s,xmatchdot);
3636     /* If xmatchdot not set by caller but pattern implies it, set it anyway */
3637     if (!xmatchdot && ((p = ckstrchr(s,'.')))) {
3638         if (p == s && p[1] != '/') {
3639             xmatchdot = 1;
3640             debug(F111,"nzxpand xmatchdot 2",s,xmatchdot);
3641         } else if (p > s) {
3642             xmatchdot = (*(p-1) == ',') || (*(p-1) == '{') || (*(p-1) == '/');
3643             debug(F111,"nzxpand xmatchdot 3",s,xmatchdot);
3644         }
3645     }
3646     xrecursive = (flags & ZX_RECURSE);
3647     xnobackup  = (flags & ZX_NOBACKUP);
3648     xnolinks   = (flags & ZX_NOLINKS);
3649
3650 #ifdef DEBUG
3651     if (deblog) {
3652         debug(F101,"nzxpand xdironly","",xdironly);
3653         debug(F101,"nzxpand xfilonly","",xfilonly);
3654         debug(F101,"nzxpand xmatchdot","",xmatchdot);
3655         debug(F101,"nzxpand xrecursive","",xrecursive);
3656         debug(F101,"nzxpand xnobackup","",xnobackup);
3657         debug(F101,"nzxpand xnolinks","",xnolinks);
3658     }
3659 #endif /* DEBUG */
3660
3661     x = zxpand(s);
3662     if (x > 1)
3663       sh_sort(mtchs,NULL,x,0,0,1);      /* Alphabetize the list */
3664     xdironly = 0;
3665     xfilonly = 0;
3666     xmatchdot = 0;
3667     xrecursive = 0;
3668     xnobackup = 0;
3669     xnolinks = 0;
3670     return(x);
3671 }
3672 #endif /* NONZXPAND */
3673
3674 #ifndef NOZXREWIND
3675 /*  Z X R E W I N D  --  Rewinds the zxpand() list */
3676
3677 int
3678 zxrewind() {
3679     /* if (!mtchs) return(-1); */
3680     fcount = nxpand;
3681     mtchptr = mtchs;
3682     return(nxpand);
3683 }
3684 #endif /* NOZXREWIND */
3685
3686 /*  Z N E X T  --  Get name of next file from list created by zxpand(). */
3687 /*
3688   Returns >0 if there's another file, with its name copied into the arg string,
3689   or 0 if no more files in list.
3690 */
3691 int
3692 znext(fn) char *fn; {
3693     if (fcount-- > 0) {
3694         ckstrncpy(fn,*mtchptr++,CKMAXPATH);
3695     } else {
3696         fn[0] = '\0';
3697     }
3698 #ifndef COMMENT
3699     debug(F111,"znext",fn,fcount+1);
3700     return(fcount+1);
3701 #else
3702     debug(F111,"znext",fn,fcount);      /* Return 0 if no filename to return */
3703     return(fcount);
3704 #endif /* COMMENT */
3705 }
3706
3707 /*  Z C H K S P A  --  Check if there is enough space to store the file  */
3708
3709 /*
3710  Call with file specification f, size n in bytes.
3711  Returns -1 on error, 0 if not enough space, 1 if enough space.
3712 */
3713 /*ARGSUSED*/
3714 int
3715 #ifdef CK_ANSIC
3716 zchkspa(char *f, long n)
3717 #else
3718 zchkspa(f,n) char *f; long n;
3719 #endif /* CK_ANSIC */
3720 /* zchkspa() */ {
3721     /* In UNIX there is no good (and portable) way. */
3722     return(1);                          /* Always say OK. */
3723 }
3724
3725 #ifdef COMMENT                          /* (not used) */
3726
3727 /*  I S B A C K U P  --  Tells if given file has a backup suffix  */
3728 /*
3729    Returns:
3730    -1: Invalid argument
3731     0: File does not have a backup suffix
3732    >0: Backup suffix number
3733 */
3734 int
3735 isbackup(fn) char * fn; {               /* Get backup suffix number */
3736     int i, j, k, x, state, flag;
3737
3738     if (!fn)                            /* Watch out for null pointers. */
3739       return(-1);
3740     if (!*fn)                           /* And empty names. */
3741       return(-1);
3742
3743     flag = state = 0;
3744     for (i = (int)strlen(fn) - 1; (!flag && (i > 0)); i--) {
3745         switch (state) {
3746           case 0:                       /* State 0 - final char */
3747             if (fn[i] == '~')           /* Is tilde */
3748               state = 1;                /* Switch to next state */
3749             else                        /* Otherwise */
3750               flag = 1;                 /* Quit - no backup suffix. */
3751             break;
3752           case 1:                       /* State 1 - digits */
3753             if (fn[i] == '~'  && fn[i-1] == '.') { /* Have suffix */
3754                 return(atoi(&fn[i+1]));
3755             } else if (fn[i] >= '0' && fn[i] <= '9') { /* In number part */
3756                 continue;               /* Keep going */
3757             } else {                    /* Something else */
3758                 flag = 1;               /* Not a backup suffix - quit. */
3759             }
3760             break;
3761         }
3762     }
3763     return(0);
3764 }
3765 #endif /* COMMENT */
3766
3767
3768 /*  Z N E W N  --  Make a new name for the given file  */
3769
3770 /*
3771   Given the name, fn, of a file that already exists, this function builds a
3772   new name of the form "<oldname>.~<n>~", where <oldname> is argument name
3773   (fn), and <n> is a version number, one higher than any existing version
3774   number for that file, up to 99999.  This format is consistent with that used
3775   by GNU EMACS.  If the constructed name is too long for the system's maximum,
3776   enough characters are truncated from the end of <fn> to allow the version
3777   number to fit.  If no free version numbers exist between 1 and 99999, a
3778   version number of "xxxx" is used.  Returns a pointer to the new name in
3779   argument s.
3780 */
3781 #ifdef pdp11
3782 #define ZNEWNBL 63                      /* Name buffer length */
3783 #define ZNEWNMD 3                       /* Max digits for version number */
3784 #else
3785 #define ZNEWNBL CKMAXPATH
3786 #define ZNEWNMD 4
3787 #endif /* pdp11 */
3788
3789 #define MAXBUDIGITS 5
3790
3791 static char znewbuf[ZNEWNBL+12];
3792
3793 VOID
3794 znewn(fn,s) char *fn, **s; {
3795     char * buf;                         /* Pointer to buffer for new name */
3796     char * xp, * namepart = NULL;       /* Pointer to filename part */
3797     struct zfnfp * fnfp;                /* znfqfp() result struct pointer */
3798     int d = 0, t, fnlen, buflen;
3799     int n, i, k, flag, state;
3800     int max = MAXNAMLEN;                /* Maximum name length */
3801     char * dname = NULL;
3802
3803     buf = znewbuf;
3804     *s = NULL;                          /* Initialize return value */
3805     if (!fn) fn = "";                   /* Check filename argument */
3806     i = strlen(fn);
3807
3808 /* If incoming file already has a backup suffix, remove it. */
3809 /* Then we'll tack a new on later, which will be the highest for this file. */
3810
3811     if (i <= max && i > 0 && fn[i-1] == '~') {
3812         char * p;
3813         i--;
3814         debug(F111,"znewn suffix removal",fn,i);
3815         if ((dname = (char *)malloc(i+1))) {
3816             ckstrncpy(dname,fn,i+1);
3817             p = dname;
3818             for (flag = state = 0; (!flag && (i > 0)); i--) {
3819                 switch (state) {
3820                   case 0:               /* State 0 - final char */
3821                     if (p[i] == '~')    /* Is tilde */
3822                       state = 1;        /* Switch to next state */
3823                     else                /* Otherwise */
3824                       flag = 1;         /* Quit - no backup suffix. */
3825                     break;
3826                   case 1:               /* State 1 - digits */
3827                     if (p[i] == '~'  && p[i-1] == '.') { /* Have suffix */
3828                         p[i-1] = NUL;   /* Trim it */
3829                         fn = dname;
3830                         debug(F111,"znewn suffix removal 2",fn,i);
3831                         flag = 1;       /* done */
3832                     } else if (p[i] >= '0' && p[i] <= '9') { /* Number part */
3833                         continue;       /* Keep going */
3834                     } else {            /* Something else */
3835                         flag = 1;       /* Not a backup suffix - quit. */
3836                     }
3837                     break;
3838                 }
3839             }
3840         }
3841     }
3842     if ((fnlen = strlen(fn)) < 1) {     /* Get length */
3843         if (dname) free(dname);
3844         return;
3845     }
3846     debug(F111,"znewn",fn,fnlen);
3847
3848     debug(F101,"znewn max 1","",max);
3849     if (max < 14) max = 14;             /* Make max reasonable for any UNIX */
3850     if (max > ZNEWNBL) max = ZNEWNBL;
3851     debug(F101,"znewn max 2","",max);
3852
3853     if ((fnfp = zfnqfp(fn, ZNEWNBL, buf))) { /* Get fully qualified name */
3854         namepart = fnfp->fname;         /* Isolate the filename */
3855         k = strlen(fn);                 /* Length of name part */
3856         debug(F111,"znewn namepart",namepart,k);
3857     } else {
3858         if (dname) free(dname);
3859         return;
3860     }
3861     buflen = fnfp->len;                 /* Length of fully qualified name */
3862     debug(F111,"znewn len",buf,buflen);
3863
3864     if (k + MAXBUDIGITS + 3 < max) {    /* Backup name fits - no overflow */
3865         /* Make pattern for backup names */
3866         ckstrncpy(buf+buflen,".~*~",ZNEWNBL+12-buflen);
3867         n = nzxpand(buf,ZX_FILONLY);    /* Expand the pattern */
3868         debug(F111,"znewn A matches",buf,n);
3869         while (n-- > 0) {               /* Find any existing name.~n~ files */
3870             xp = *mtchptr++;            /* Point at matching name */
3871             t = atoi(xp+buflen+2);      /* Get number */
3872             if (t > d) d = t;           /* Save d = highest version number */
3873         }
3874         sprintf(buf+buflen,".~%d~",d+1); /* Yes, make "name.~<d+1>~" */
3875         debug(F110,"znewn A newname",buf,0);
3876     } else {                            /* Backup name would be too long */
3877         int xlen;                       /* So we have to eat back into it */
3878         int delta;
3879         char buf2[ZNEWNBL+12];
3880
3881         delta = max - k;
3882         debug(F101,"znewn B delta","",delta);
3883
3884         for (i = MAXBUDIGITS; i > 0; i--) { /* In this case the format of */
3885             ckstrncpy(buf2,buf,ZNEWNBL+12); /* the backup name depends on */
3886             xlen = buflen - i - 3 + delta;  /* how many digits are in the */
3887             ckstrncpy(buf2+xlen,".~*~",ZNEWNBL+12-xlen); /* backup number */
3888             n = nzxpand(buf2,ZX_FILONLY);
3889             debug(F111,"znewn B matches",buf2,n);
3890             if (n > 0)
3891               break;
3892         }
3893         while (n-- > 0) {               /* Find any existing name.~n~ files */
3894             xp = *mtchptr++;            /* Point at matching name */
3895             t = atoi(xp+xlen+2);        /* Get number */
3896             if (t > d) d = t;           /* Save d = highest version number */
3897         }
3898         if (d > 0)                      /* If the odometer turned over... */
3899           if ((d % 10) == 9)            /* back up one space. */
3900             xlen--;
3901         sprintf(buf2+xlen,".~%d~",d+1); /* This just fits */
3902         ckstrncpy(buf,buf2,ZNEWNBL+12); /* (we could be more clever here...) */
3903         debug(F110,"znewn B new name",buf,0);
3904     }
3905     *s = buf;                           /* Point to new name */
3906     ck_znewn = d+1;                     /* Also make it available globally */
3907     if (dname) free(dname);
3908     return;
3909 }
3910
3911 /*  Z R E N A M E  --  Rename a file  */
3912 /*
3913    Call with old and new names.
3914    If new name is the name of a directory, the 'old' file is moved to
3915    that directory.
3916    Returns 0 on success, -1 on failure.
3917 */
3918 int
3919 zrename(old,new) char *old, *new; {
3920     char *p, *s;
3921     int x;
3922
3923     if (!old) old = "";
3924     if (!new) new = "";
3925     debug(F110,"zrename old",old,0);
3926     debug(F110,"zrename new",new,0);
3927     if (!*old) return(-1);
3928     if (!*new) return(-1);
3929
3930 #ifdef IKSD
3931 #ifdef CK_LOGIN
3932     if (inserver && isguest)
3933       return(-1);
3934 #endif /* CK_LOGIN */
3935 #endif /* IKSD */
3936
3937 #ifdef CKROOT
3938     debug(F111,"zrename setroot",ckroot,ckrootset);
3939     if (ckrootset) {
3940         if (!zinroot(old)) {
3941             debug(F110,"zrename old: setroot violation",old,0);
3942             return(-1);
3943         }
3944         if (!zinroot(new)) {
3945             debug(F110,"zrename new: setroot violation",new,0);
3946             return(-1);
3947         }
3948     }
3949 #endif /* CKROOT */
3950
3951     p = NULL;
3952     s = new;
3953
3954     if (isdir(new)) {
3955         char *q = NULL;
3956         x = strlen(new);
3957         if (!(p = malloc(strlen(new) + strlen(old) + 2)))
3958           return(-1);
3959         strcpy(p,new);                  /* (safe) Directory part */
3960         if (!ISDIRSEP(*(new+x-1)))      /* Separator, if needed */
3961           strcat(p,"/");                /* (safe) */
3962         zstrip(old,&q);                 /* Strip path part from old name */
3963         strcat(p,q);                    /* cat to new directory (safe) */
3964         s = p;
3965         debug(F110,"zrename dir",s,0);
3966     }
3967 #ifdef DEBUG
3968     else debug(F110,"zrename no dir",s,0);
3969 #endif /* DEBUG */
3970
3971 #ifdef IKSD
3972     if (inserver && (!ENABLED(en_del))) {
3973         if (zchki(s) > -1)              /* Destination file exists? */
3974           return(-1);
3975     }
3976 #endif /* IKSD */
3977
3978     x = -1;                             /* Return code. */
3979 #ifdef RENAME
3980 /* Atomic, preferred, uses a single system call, rename(), if available. */
3981     x = rename(old,s);
3982     debug(F111,"zrename rename()",old,x);
3983     if (x) x = -1;
3984 #endif /* RENAME */
3985
3986     /* If rename() failed or not available try link()/unlink() */
3987
3988     if (x < 0) {
3989         if (zchko(old) > -1) {          /* Requires write access to orignal */
3990             x = link(old,s);
3991             debug(F111,"zrename link()",old,x);
3992             if (x > -1) {               /* Make a link with the new name. */
3993                 x = unlink(old);
3994                 debug(F111,"zrename unlink()",old,x);
3995             }
3996             /* If link/unlink failed copy and delete */
3997             if (x < 0) {
3998                 x = zcopy(old,s);
3999                 debug(F111,"zrename zcopy()",old,x);
4000                 if (x > -1) {
4001                     x = zdelet(old);
4002                     debug(F111,"zrename zdelet()",old,x);
4003                 }
4004             }
4005         }
4006     }
4007     fullname[0] = '\0';                 /* Clear this out for next time. */
4008
4009 #ifdef CKSYSLOG
4010     if (ckxsyslog >= SYSLG_FC && ckxlogging) {
4011         zfnqfp(old,CKMAXPATH,fullname);
4012         tmp2[0] = '\0';
4013         zfnqfp(s,CKMAXPATH,tmp2);
4014         if (x > -1)
4015           syslog(LOG_INFO,"file[] %s: renamed to %s ok", fullname, tmp2);
4016         else
4017           syslog(LOG_INFO,"file[] %s: rename to %s failed (%m)",fullname,tmp2);
4018     }
4019 #endif /* CKSYSLOG */
4020
4021     if (p) free(p);
4022     return(x);
4023 }
4024
4025 /*  Z C O P Y  --  Copy a single file. */
4026 /*
4027   Call with source and destination names.
4028   If destination is a directory, the source file is
4029   copied to that directory with its original name.
4030   Returns:
4031    0 on success.
4032   <0 on failure:
4033   -2 = source file is not a regular file.
4034   -3 = source file not found.
4035   -4 = permission denied.
4036   -5 = source and destination are the same file.
4037   -6 = i/o error.
4038   -1 = other error.
4039 */
4040 int
4041 zcopy(source,destination) char *source, *destination; {
4042     char *src, *dst;                    /* Local pointers to filenames */
4043     int x, y, rc;                       /* Workers */
4044     int in = -1, out = -1;              /* i/o file descriptors */
4045     struct stat srcbuf;                 /* Source file info buffer */
4046     int perms;                          /* Output file permissions */
4047     char buf[1024];                     /* File copying buffer */
4048
4049     if (!source) source = "";
4050     if (!destination) destination = "";
4051
4052     debug(F110,"zcopy src arg",source,0);
4053     debug(F110,"zcopy dst arg",destination,0);
4054
4055     if (!*source) return(-1);
4056     if (!*destination) return(-1);
4057
4058 #ifdef IKSD
4059 #ifdef CK_LOGIN
4060     if (inserver && isguest)
4061       return(-4);
4062 #endif /* CK_LOGIN */
4063 #endif /* IKSD */
4064
4065 #ifdef CKROOT
4066     debug(F111,"zcopy setroot",ckroot,ckrootset);
4067     if (ckrootset) {
4068         if (!zinroot(source)) {
4069             debug(F110,"zcopy source: setroot violation",source,0);
4070             return(-1);
4071         }
4072         if (!zinroot(destination)) {
4073             debug(F110,"zcopy destination: setroot violation",destination,0);
4074             return(-1);
4075         }
4076     }
4077 #endif /* CKROOT */
4078
4079     src = source;
4080     dst = destination;
4081
4082     if (stat(src,&srcbuf) == 0) {       /* Get source file info */
4083         struct stat dstbuf;             /* Destination file info buffer */
4084         debug(F101,"STAT","",6);
4085         if (stat(dst,&dstbuf) == 0) {
4086             debug(F101,"STAT","",7);
4087             if (srcbuf.st_dev == dstbuf.st_dev)
4088               if (srcbuf.st_ino == dstbuf.st_ino) {
4089                   debug(F100,"zcopy files identical: stat()","",0);
4090                   return(-5);
4091               }
4092         }
4093     } else {                            /* stat() failed... */
4094         debug(F101,"STAT","",8);
4095         debug(F111,"source file not found",src,errno);
4096         return(-3);
4097     }
4098     fullname[0] = '\0';                 /* Get full pathnames */
4099     if (zfnqfp(source,CKMAXPATH,fullname))
4100       src = fullname;
4101     debug(F110,"zcopy src",src,0);
4102     tmp2[0] = '\0';
4103     if (zfnqfp(destination,CKMAXPATH,tmp2))
4104       dst = tmp2;
4105     debug(F110,"zcopy dst 1",dst,0);
4106     if (!strcmp(src,dst)) {             /* Src and dst are same file? */
4107         debug(F100,"zcopy files identical: strcmp()","",0); /* This... */
4108         return(-5);                     /* should not happen. */
4109     }
4110     if (isdir(src)) {                   /* Source file is a directory? */
4111         debug(F110,"zcopy source is directory",src,0);
4112         return(-2);                     /* Fail */
4113     }
4114     if (isdir(dst)) {                   /* Destination is a directory? */
4115         char *q = NULL;                 /* Yes, add filename to it. */
4116         x = strlen(dst);
4117         if (x < 1) return(-1);
4118         if (!ISDIRSEP(*(dst+x-1))) {    /* Add separator if needed */
4119             tmp2[x++] = '/';
4120             tmp2[x] = '\0';
4121         }
4122         debug(F111,"zcopy dst 2",dst,x);
4123         zstrip(src,&q);                 /* Strip path part from old name */
4124         ckstrncpy(tmp2+x,q,CKMAXPATH-x); /* Concatenate it to new name */
4125     }
4126     debug(F110,"zcopy dst 3",dst,0);
4127
4128 #ifdef IKSD
4129     if (inserver && (!ENABLED(en_del))) {
4130         if (zchki(dst) > -1)            /* Destination file exists? */
4131           return(-4);
4132     }
4133 #endif /* IKSD */
4134
4135     perms = umask(0);                   /* Get user's umask */
4136     umask(perms);                       /* Put it back! */
4137     perms ^= 0777;                      /* Flip the bits */
4138     perms &= 0666;                      /* Zero execute bits from umask */
4139     perms |= (srcbuf.st_mode & 0111);   /* OR in source file's execute bits */
4140     rc = -1;                            /* Default return code */
4141     errno = 0;                          /* Reset errno */
4142     in = open(src, O_RDONLY, 0);        /* Open source file */
4143     debug(F111,"zcopy open source",src,in);
4144     if (in > -1) {                      /* If open... */
4145         /* Open destination file */
4146 #ifdef O_TRUNC
4147         out = open(dst, O_WRONLY|O_CREAT|O_TRUNC, perms);
4148 #else
4149         out = open(dst, O_WRONLY|O_CREAT, perms);
4150 #endif /* O_TRUNC */
4151         debug(F111,"zcopy open dest",dst,out);
4152         if (out > -1) {                 /* If open... */
4153             while ((x = read(in,buf,1024)) > 0) { /* Copy in 1K blocks */
4154                 y = write(out,buf,x);
4155                 if (y < 0) {            /* On write failure */
4156                     x = -1;
4157                     rc = -6;            /* Indicate i/o error */
4158                     break;
4159                 }
4160             }
4161             debug(F101,"zcopy final read","",x);
4162             debug(F101,"zcopy errno","",errno);
4163             rc = (x == 0) ? 0 : -6;     /* In case of read failure */
4164         }
4165     }
4166     if (in > -1) close(in);             /* Close files */
4167     if (out > -1) close(out);
4168     if (rc == -1) {                     /* Set return code */
4169         switch (errno) {
4170           case ENOENT: rc = -3; break;
4171           case EACCES: rc = -4; break;
4172           case EIO:    rc = -6;
4173         }
4174     }
4175
4176 #ifdef CKSYSLOG
4177     if (rc > -1 && ckxsyslog >= SYSLG_FC && ckxlogging) {
4178         if (rc)
4179           syslog(LOG_INFO,"file[] %s: copy to %s failed (%m)", fullname, tmp2);
4180         else
4181           syslog(LOG_INFO,"file[] %s: copy to %s ok", fullname, tmp2);
4182     }
4183 #endif /* CKSYSLOG */
4184
4185     return(rc);
4186 }
4187
4188 /*  Z S A T T R */
4189 /*
4190  Fills in a Kermit file attribute structure for the file which is to be sent.
4191  Returns 0 on success with the structure filled in, or -1 on failure.
4192  If any string member is null, then it should be ignored.
4193  If any numeric member is -1, then it should be ignored.
4194 */
4195 #ifdef CK_PERMS
4196
4197 #ifdef CK_GPERMS
4198 #undef CK_GPERMS
4199 #endif /* CK_GPERMS */
4200
4201 #ifdef UNIX
4202 #ifndef S_IRUSR
4203 #define S_IRUSR 0400
4204 #endif /* S_IRUSR */
4205 #ifndef S_IWUSR
4206 #define S_IXUSR 0200
4207 #endif /* S_IWUSR */
4208 #ifndef S_IXUSR
4209 #define S_IXUSR 0100
4210 #endif /* S_IXUSR */
4211 #endif /* UNIX */
4212
4213 #ifdef S_IRUSR
4214 #ifdef S_IWUSR
4215 #ifdef S_IXUSR
4216 #define CK_GPERMS
4217 #endif /* S_IXUSR */
4218 #endif /* S_IWUSR */
4219 #endif /* S_IRUSR */
4220
4221 static char gperms[2];
4222
4223 #endif /* CK_GPERMS */
4224
4225 static char lperms[24];
4226
4227 #ifdef CK_PERMS
4228 static char xlperms[24];
4229
4230 /*  Z S E T P E R M  --  Set permissions of a file  */
4231
4232 int
4233 zsetperm(f,code) char * f; int code; {
4234     int x;
4235 #ifdef CK_SCO32V4
4236     mode_t mask;
4237 #else
4238     int mask;
4239 #endif /* CK_SCO32V4 */
4240     mask = code;
4241     if (inserver && guest) {
4242         debug(F110,"zsetperm guest",f,0);
4243         return(0);
4244     }
4245     x = chmod(f,mask);
4246     if (x < 0) {
4247         debug(F111,"zsetperm error",f,errno);
4248         return(0);
4249     }
4250     debug(F111,"zsetperm ok",f,mask);
4251     return(1);
4252 }
4253
4254 /*  Z G P E R M  --  Get permissions of a file as an octal string  */
4255
4256 char *
4257 zgperm(f) char *f; {
4258     extern int diractive;
4259     int x; char *s = (char *)xlperms;
4260     struct stat buf;
4261     debug(F110,"zgperm",f,0);
4262     if (!f) return("----------");
4263     if (!*f) return("----------");
4264
4265 #ifdef CKROOT
4266     debug(F111,"zgperm setroot",ckroot,ckrootset);
4267     if (ckrootset) if (!zinroot(f)) {
4268         debug(F110,"zgperm setroot violation",f,0);
4269         return("----------");
4270     }
4271 #endif /* CKROOT */
4272
4273 #ifdef USE_LSTAT
4274     if (diractive)
4275       x = lstat(f,&buf);
4276     else
4277 #endif /* USE_LSTAT */
4278       x = stat(f,&buf);
4279     debug(F101,"STAT","",9);
4280     if (x < 0)
4281       return("----------");
4282     sprintf(s,"%o",buf.st_mode);
4283     debug(F110,"zgperm",s,0);
4284     return(s);
4285 }
4286
4287 /* Like zgperm() but returns permissions in "ls -l" string format */
4288
4289 static char xsperms[24];
4290
4291 char *
4292 ziperm(f) char * f; {
4293     extern int diractive;
4294     int x; char *s = (char *)xsperms;
4295     struct stat buf;
4296     unsigned int perms = 0;
4297
4298     debug(F110,"ziperm",f,0);
4299
4300     if (!f) return(NULL);
4301     if (!*f) return(NULL);
4302
4303     if (diractive && zgfs_mode != 0) {
4304         perms = zgfs_mode;              /* zgetfs() already got them */
4305     } else {
4306 #ifdef USE_LSTAT
4307         if (diractive)
4308           x = lstat(f,&buf);
4309         else
4310 #endif /* USE_LSTAT */
4311           x = stat(f,&buf);
4312         debug(F101,"STAT","",10);
4313         if (x < 0)
4314           return("----------");
4315         perms = buf.st_mode;
4316     }
4317     switch (perms & S_IFMT) {
4318       case S_IFDIR:
4319         *s++ = 'd';
4320         break;
4321       case S_IFCHR:                     /* Character special */
4322         *s++ = 'c';
4323         break;
4324       case S_IFBLK:                     /* Block special */
4325         *s++ = 'b';
4326         break;
4327       case S_IFREG:                     /* Regular */
4328         *s++ = '-';
4329         break;
4330 #ifdef S_IFLNK
4331       case S_IFLNK:                     /* Symbolic link */
4332         *s++ = 'l';
4333         break;
4334 #endif /* S_IFLNK */
4335 #ifdef S_IFSOCK
4336       case S_IFSOCK:                    /* Socket */
4337         *s++ = 's';
4338         break;
4339 #endif /* S_IFSOCK */
4340 #ifdef S_IFIFO
4341 #ifndef Plan9
4342 #ifndef COHERENT
4343       case S_IFIFO:                     /* FIFO */
4344         *s++ = 'p';
4345         break;
4346 #endif /* COHERENT */
4347 #endif /* Plan9 */
4348 #endif /* S_IFIFO */
4349 #ifdef S_IFWHT
4350       case S_IFWHT:                     /* Whiteout */
4351         *s++ = 'w';
4352         break;
4353 #endif /* S_IFWHT */
4354       default:                          /* Unknown */
4355         *s++ = '?';
4356         break;
4357     }
4358     if (perms & S_IRUSR)          /* Owner's permissions */
4359       *s++ = 'r';
4360     else
4361       *s++ = '-';
4362     if (perms & S_IWUSR)
4363       *s++ = 'w';
4364     else
4365       *s++ = '-';
4366     switch (perms & (S_IXUSR | S_ISUID)) {
4367       case 0:
4368         *s++ = '-';
4369         break;
4370       case S_IXUSR:
4371         *s++ = 'x';
4372         break;
4373       case S_ISUID:
4374         *s++ = 'S';
4375         break;
4376       case S_IXUSR | S_ISUID:
4377         *s++ = 's';
4378         break;
4379     }
4380     if (perms & S_IRGRP)          /* Group permissions */
4381       *s++ = 'r';
4382     else
4383       *s++ = '-';
4384     if (perms & S_IWGRP)
4385       *s++ = 'w';
4386     else
4387       *s++ = '-';
4388     switch (perms & (S_IXGRP | S_ISGID)) {
4389       case 0:
4390         *s++ = '-';
4391         break;
4392       case S_IXGRP:
4393         *s++ = 'x';
4394         break;
4395       case S_ISGID:
4396         *s++ = 'S';
4397         break;
4398       case S_IXGRP | S_ISGID:
4399         *s++ = 's';
4400         break;
4401     }
4402     if (perms & S_IROTH)          /* World permissions */
4403       *s++ = 'r';
4404     else
4405       *s++ = '-';
4406     if (perms & S_IWOTH)
4407       *s++ = 'w';
4408     else
4409       *s++ = '-';
4410     switch (
4411 #ifdef Plan9
4412             perms & (S_IXOTH)
4413 #else
4414             perms & (S_IXOTH | S_ISVTX)
4415 #endif
4416             ) {
4417       case 0:
4418         *s++ = '-';
4419         break;
4420       case S_IXOTH:
4421         *s++ = 'x';
4422         break;
4423 #ifndef Plan9
4424       case S_ISVTX:
4425         *s++ = 'T';
4426         break;
4427       case S_IXOTH | S_ISVTX:
4428         *s++ = 't';
4429         break;
4430 #endif /* Plan9 */
4431     }
4432     *s = '\0';
4433     debug(F110,"ziperm",xsperms,0);
4434     return((char *)xsperms);
4435 }
4436
4437 #else
4438
4439 char *
4440 zgperm(f) char *f; {
4441     return("----------");
4442 }
4443 char *
4444 ziperms(f) char *f; {
4445     return("----------");
4446 }
4447 #endif /* CK_PERMS */
4448
4449 int
4450 zsattr(xx) struct zattr *xx; {
4451     long k; int x;
4452     struct stat buf;
4453
4454     k = iflen % 1024L;                  /* File length in K */
4455     if (k != 0L) k = 1L;
4456     xx->lengthk = (iflen / 1024L) + k;
4457     xx->type.len = 0;                   /* File type can't be filled in here */
4458     xx->type.val = "";
4459     if (*nambuf) {
4460         xx->date.val = zfcdat(nambuf);  /* File creation date */
4461         xx->date.len = (int)strlen(xx->date.val);
4462     } else {
4463         xx->date.len = 0;
4464         xx->date.val = "";
4465     }
4466     xx->creator.len = 0;                /* File creator */
4467     xx->creator.val = "";
4468     xx->account.len = 0;                /* File account */
4469     xx->account.val = "";
4470     xx->area.len = 0;                   /* File area */
4471     xx->area.val = "";
4472     xx->password.len = 0;               /* Area password */
4473     xx->password.val = "";
4474     xx->blksize = -1L;                  /* File blocksize */
4475     xx->xaccess.len = 0;                /* File access */
4476     xx->xaccess.val = "";
4477     xx->encoding.len = 0;               /* Transfer syntax */
4478     xx->encoding.val = 0;
4479     xx->disp.len = 0;                   /* Disposition upon arrival */
4480     xx->disp.val = "";
4481     xx->lprotect.len = 0;               /* Local protection */
4482     xx->lprotect.val = "";
4483     xx->gprotect.len = 0;               /* Generic protection */
4484     xx->gprotect.val = "";
4485     x = -1;
4486     if (*nambuf) x = stat(nambuf,&buf);
4487     debug(F101,"STAT","",11);
4488     if (x >= 0) {
4489         debug(F111,"zsattr buf.st_mode & 0777",nambuf,buf.st_mode & 0777);
4490         /* UNIX filemode as an octal string without filetype bits */
4491         sprintf(lperms,"%o",buf.st_mode & 0777);
4492         xx->lprotect.len = (int)strlen(lperms);
4493         xx->lprotect.val = (char *)lperms;
4494         x = 0;
4495 #ifdef CK_GPERMS
4496         /* Generic permissions only if we have stat.h symbols defined */
4497         if (buf.st_mode & S_IRUSR) x |= 1;      /* Read */
4498         if (buf.st_mode & S_IWUSR) x |= (2+16); /* Write and Delete */
4499         if (buf.st_mode & S_IXUSR) x |= 4;      /* Execute */
4500         gperms[0] = tochar(x);
4501         gperms[1] = NUL;
4502         xx->gprotect.len = 1;
4503         xx->gprotect.val = (char *)gperms;
4504 #endif /* CK_GPERMS */
4505     }
4506     debug(F111,"zsattr lperms",xx->lprotect.val,xx->lprotect.len);
4507     debug(F111,"zsattr gperms",xx->gprotect.val,xx->gprotect.len);
4508     xx->systemid.val = "U1";            /* U1 = UNIX */
4509     xx->systemid.len = 2;               /* System ID */
4510     xx->recfm.len = 0;                  /* Record format */
4511     xx->recfm.val = "";
4512     xx->sysparam.len = 0;               /* System-dependent parameters */
4513     xx->sysparam.val = "";
4514     xx->length = iflen;                 /* Length */
4515     return(0);
4516 }
4517
4518 /* Z F C D A T  --  Get file creation date */
4519 /*
4520   Call with pointer to filename.
4521   On success, returns pointer to modification date in yyyymmdd hh:mm:ss format.
4522   On failure, returns pointer to null string.
4523 */
4524 static char datbuf[40];
4525
4526 char *
4527 #ifdef CK_ANSIC
4528 zdtstr(time_t timearg)
4529 #else
4530 zdtstr(timearg) time_t timearg;
4531 #endif /* CK_ANSIC */
4532 /* zdtstr */ {
4533 #ifndef TIMESTAMP
4534     return("");
4535 #else
4536     struct tm * time_stamp;
4537     struct tm * localtime();
4538     int yy, ss;
4539
4540     debug(F101,"zdtstr timearg","",timearg);
4541     if (timearg < 0)
4542       return("");
4543     time_stamp = localtime(&(timearg));
4544     if (!time_stamp) {
4545         debug(F100,"localtime returns null","",0);
4546         return("");
4547     }
4548 /*
4549   We assume that tm_year is ALWAYS years since 1900.
4550   Any platform where this is not the case will have problems
4551   starting in 2000.
4552 */
4553     yy = time_stamp->tm_year;           /* Year - 1900 */
4554     debug(F101,"zdtstr tm_year","",time_stamp->tm_year);
4555     if (yy > 1000) {
4556         debug(F101,"zstrdt YEAR-2000 ALERT 1: localtime year","",yy);
4557     }
4558     yy += 1900;
4559     debug(F101,"zdatstr year","",yy);
4560
4561     if (time_stamp->tm_mon  < 0 || time_stamp->tm_mon  > 11)
4562       return("");
4563     if (time_stamp->tm_mday < 0 || time_stamp->tm_mday > 31)
4564       return("");
4565     if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 23)
4566       return("");
4567     if (time_stamp->tm_min  < 0 || time_stamp->tm_min  > 59)
4568       return("");
4569     ss = time_stamp->tm_sec;            /* Seconds */
4570     if (ss < 0 || ss  > 59)             /* Some systems give a BIG number */
4571       ss = 0;
4572     sprintf(datbuf,
4573 #ifdef pdp11
4574 /* For some reason, 2.1x BSD sprintf gets the last field wrong. */
4575             "%04d%02d%02d %02d:%02d:00",
4576 #else
4577             "%04d%02d%02d %02d:%02d:%02d",
4578 #endif /* pdp11 */
4579             yy,
4580             time_stamp->tm_mon + 1,
4581             time_stamp->tm_mday,
4582             time_stamp->tm_hour,
4583             time_stamp->tm_min
4584 #ifndef pdp11
4585             , ss
4586 #endif /* pdp11 */
4587             );
4588     yy = (int)strlen(datbuf);
4589     debug(F111,"zdatstr",datbuf,yy);
4590     if (yy > 17) datbuf[17] = '\0';
4591     return(datbuf);
4592 #endif /* TIMESTAMP */
4593 }
4594
4595 char *
4596 zfcdat(name) char *name; {
4597 #ifdef TIMESTAMP
4598     struct stat buffer;
4599     extern int diractive;
4600     unsigned int mtime;
4601     int x;
4602     char * s;
4603
4604     if (!name)
4605       return("");
4606     s = name;
4607     if (!*s)
4608       return("");
4609
4610 #ifdef CKROOT
4611     debug(F111,"zfcdat setroot",ckroot,ckrootset);
4612     if (ckrootset) if (!zinroot(name)) {
4613         debug(F110,"zfcdat setroot violation",name,0);
4614         return("");
4615     }
4616 #endif /* CKROOT */
4617
4618 #ifdef DTILDE
4619     if (*s == '~') {
4620         s = tilde_expand(s);
4621         if (!s) s = "";
4622         if (!*s) s = name;
4623     }
4624 #endif /* DTILDE */
4625
4626     datbuf[0] = '\0';
4627     x = 0;
4628     debug(F111,"zfcdat",s,diractive);
4629
4630     if (diractive && zgfs_mtime) {
4631         mtime = zgfs_mtime;
4632     } else {
4633 #ifdef USE_LSTAT
4634         if (diractive) {
4635             x = lstat(s,&buffer);
4636             debug(F101,"STAT","",12);
4637             debug(F101,"zfcdat lstat","",x);
4638         } else {
4639 #endif /* USE_LSTAT */
4640             x = stat(s,&buffer);
4641             debug(F101,"STAT","",13);
4642             debug(F101,"zfcdat stat","",x);
4643 #ifdef USE_LSTAT
4644         }
4645 #endif /* USE_LSTAT */
4646         if (x != 0) {
4647 #ifdef USE_LSTAT
4648             debug(F111,"zfcdat stat failed",s,errno);
4649 #else
4650             debug(F111,"zfcdat lstat failed",s,errno);
4651 #endif /* USE_LSTAT */
4652             return("");
4653         }
4654         debug(F101,"zfcdat buffer.st_mtime","",buffer.st_mtime);
4655         mtime = buffer.st_mtime;
4656     }
4657     return(zdtstr(mtime));
4658 #else
4659     return("");
4660 #endif /* TIMESTAMP */
4661 }
4662
4663 #ifndef NOTIMESTAMP
4664
4665 /* Z S T R D T  --  Converts local date string to internal representation */
4666 /*
4667   In our case (UNIX) this is seconds since midnite 1 Jan 1970 UTC,
4668   suitable for comparison with UNIX file dates.  As far as I know, there is
4669   no library or system call -- at least nothing reasonably portable -- to
4670   convert local time to UTC.
4671 */
4672 time_t
4673 zstrdt(date,len) char * date; int len; {
4674 #ifdef M_UNIX
4675 /*
4676   SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime().
4677   ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in
4678   dependence on the XPG4 supplement presence.  So always use
4679   what the system header file supplies in ODT 3.0...
4680 */
4681 #ifndef ODT30
4682 #ifndef _SCO_DS
4683     extern void ftime();  /* extern void ftime(struct timeb *) */
4684 #endif /* _SCO_DS */
4685 #endif /* ODT30 */
4686 #else
4687 #ifndef M_XENIX
4688     extern int ftime();
4689 #endif /* M_XENIX */
4690 #endif /* M_UNIX */
4691     extern struct tm * localtime();
4692
4693     /* And this should have been declared always through a header file */
4694 #ifdef HPUX10
4695     time_t tmx;
4696     long days;
4697 #else
4698 #ifdef BSD44
4699     time_t tmx;
4700     long days;
4701 #else
4702     long tmx, days;
4703 #endif /* BSD44 */
4704 #endif /* HPUX10 */
4705     int i, n, isleapyear;
4706                    /*       J  F  M  A   M   J   J   A   S   O   N   D   */
4707                    /*      31 28 31 30  31  30  31  31  30  31  30  31   */
4708     static
4709     int monthdays [13] = {  0,0,31,59,90,120,151,181,212,243,273,304,334 };
4710     char s[5];
4711     struct tm *time_stamp;
4712
4713 #ifdef BSD44
4714     struct timeval tp[2];
4715     long xtimezone = 0L;
4716 #else
4717 #ifdef V7
4718     struct utimbuf {
4719       time_t timep[2];          /* New access and modificaton time */
4720     } tp;
4721     char *tz;
4722     long timezone;              /* In case timezone not defined in .h file */
4723 #else
4724 #ifdef SYSUTIMEH
4725     struct utimbuf tp;
4726 #else
4727     struct utimbuf {
4728         time_t atime;
4729         time_t mtime;
4730     } tp;
4731 #endif /* SYSUTIMEH */
4732 #endif /* V7 */
4733 #endif /* BSD44 */
4734
4735 #ifdef ANYBSD
4736     long timezone = 0L;
4737     static struct timeb tbp;
4738 #endif /* ANYBSD */
4739
4740 #ifdef BEBOX
4741     long timezone = 0L;
4742 #endif /* BEBOX */
4743
4744     debug(F111,"zstrdt",date,len);
4745
4746     if ((len == 0)
4747         || (len != 17)
4748         || (date[8] != ' ')
4749         || (date[11] != ':')
4750         || (date[14] != ':') ) {
4751         debug(F111,"Bad creation date ",date,len);
4752         return(-1);
4753     }
4754     debug(F111,"zstrdt date check 1",date,len);
4755     for(i = 0; i < 8; i++) {
4756         if (!isdigit(date[i])) {
4757             debug(F111,"Bad creation date ",date,len);
4758             return(-1);
4759         }
4760     }
4761     debug(F111,"zstrdt date check 2",date,len);
4762     i++;
4763
4764     for (; i < 16; i += 3) {
4765         if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) {
4766             debug(F111,"Bad creation date ",date,len);
4767             return(-1);
4768         }
4769     }
4770     debug(F111,"zstrdt date check 3",date,len);
4771
4772
4773 #ifdef COMMENT /* was BSD44 */
4774 /*
4775    man gettimeofday on BSDI 3.1 says:
4776    "The timezone field is no longer used; timezone information is stored out-
4777      side the kernel.  See ctime(3) for more information."  So this chunk of
4778    code is effectively a no-op, at least in BSDI 3.x.
4779 */
4780     {
4781         int x;
4782         struct timezone tzp;
4783         x = gettimeofday(NULL, &tzp);
4784         debug(F101,"zstrdt BSD44 gettimeofday","",x);
4785         if (x > -1)
4786           xtimezone = tzp.tz_minuteswest * 60L;
4787         else
4788           xtimezone = 0L;
4789         debug(F101,"zstrdt BSD44 timezone","",xtimezone);
4790     }
4791 #else
4792 #ifdef ANYBSD
4793     debug(F100,"zstrdt BSD calling ftime","",0);
4794     ftime(&tbp);
4795     debug(F100,"zstrdt BSD back from ftime","",0);
4796     timezone = tbp.timezone * 60L;
4797     debug(F101,"zstrdt BSD timezone","",timezone);
4798 #else
4799 #ifdef SVORPOSIX
4800     tzset();                            /* Set timezone */
4801 #else
4802 #ifdef V7
4803     if ((tz = getenv("TZ")) == NULL)
4804       timezone = 0;                     /* UTC/GMT */
4805     else
4806       timezone = atoi(&tz[3]);          /* Set 'timezone'. */
4807     timezone *= 60L;
4808 #endif /* V7 */
4809 #endif /* SVORPOSIX */
4810 #endif /* ANYBSD */
4811 #endif /* COMMENT (was BSD44) */
4812
4813     debug(F100,"zstrdt so far so good","",0);
4814
4815     s[4] = '\0';
4816     for (i = 0; i < 4; i++)             /* Fix the year */
4817       s[i] = date[i];
4818
4819     n = atoi(s);
4820     debug(F111,"zstrdt year",s,n);
4821     if (n < 1970) {
4822         debug(F100,"zstrdt fails - year","",n);
4823         return(-1);
4824     }
4825
4826 /*  Previous year's leap days.  This won't work after year 2100. */
4827
4828     isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
4829     days = (long) (n - 1970) * 365;
4830     days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
4831
4832     s[2] = '\0';
4833
4834     for (i = 4; i < 16; i += 2) {
4835         s[0] = date[i];
4836         s[1] = date[i + 1];
4837         n = atoi(s);
4838         switch (i) {
4839           case 4:                       /* MM: month */
4840             if ((n < 1 ) || ( n > 12)) {
4841                 debug(F111,"zstrdt 4 bad date ",date,len);
4842                 return(-1);
4843             }
4844             days += monthdays [n];
4845             if (isleapyear && n > 2)
4846               ++days;
4847             continue;
4848
4849           case 6:                       /* DD: day */
4850             if ((n < 1 ) || ( n > 31)) {
4851                 debug(F111,"zstrdt 6 bad date ",date,len);
4852                 return(-1);
4853             }
4854             tmx = (days + n - 1) * 24L * 60L * 60L;
4855             i++;                        /* Skip the space */
4856             continue;
4857
4858           case 9:                       /* hh: hour */
4859             if ((n < 0 ) || ( n > 23)) {
4860                 debug(F111,"zstrdt 9 bad date ",date,len);
4861                 return(-1);
4862             }
4863             tmx += n * 60L * 60L;
4864             i++;                        /* Skip the colon */
4865             continue;
4866
4867           case 12:                      /* mm: minute */
4868             if ((n < 0 ) || ( n > 59)) {
4869                 debug(F111,"zstrdt 12 bad date ",date,len);
4870                 return(-1);
4871             }
4872 #ifdef COMMENT /* (was BSD44) */        /* Correct for time zone */
4873             tmx += xtimezone;
4874             debug(F101,"zstrdt BSD44 tmx","",tmx);
4875 #else
4876 #ifdef ANYBSD
4877             tmx += timezone;
4878 #else
4879 #ifndef CONVEX9 /* Don't yet know how to do this here */
4880 #ifdef ultrix
4881             tmx += (long) timezone;
4882 #else
4883 #ifdef Plan9
4884             {
4885                 extern time_t tzoffset;
4886                 tmx += tzoffset;
4887             }
4888 #else
4889 #ifndef BSD44
4890             tmx += timezone;
4891 #endif /* BSD44 */
4892 #endif /* Plan9 */
4893 #endif /* ultrix */
4894 #endif /* CONVEX9 */
4895 #endif /* ANYBSD */
4896 #endif /* COMMENT (was BSD44) */
4897             tmx += n * 60L;
4898             i++;                        /* Skip the colon */
4899             continue;
4900
4901           case 15:                      /* ss: second */
4902             if ((n < 0 ) || ( n > 59)) {
4903                 debug(F111,"zstrdt 15 bad date ",date,len);
4904                 return(-1);
4905             }
4906             tmx += n;
4907         }
4908         time_stamp = localtime(&tmx);
4909         debug(F101,"zstrdt tmx 1","",tmx);
4910         if (!time_stamp)
4911           return(-1);
4912 #ifdef COMMENT
4913         /* Why was this here? */
4914         time_stamp = localtime(&tmx);
4915         debug(F101,"zstrdt tmx 2","",tmx);
4916 #endif /* COMMENT */
4917 #ifdef BSD44
4918         {   /* New to 7.0 - Works in at at least BSDI 3.1 and FreeBSD 2.2.7 */
4919             long zz;
4920             zz = time_stamp->tm_gmtoff; /* Seconds away from Zero Meridian */
4921             debug(F101,"zstrdt BSD44 tm_gmtoff","",zz);
4922             tmx -= zz;
4923             debug(F101,"zstrdt BSD44 tmx 3 (GMT)","",tmx);
4924         }
4925 #else
4926         /*
4927            Daylight Savings Time adjustment.
4928            Do this everywhere BUT in BSD44 because in BSD44,
4929            tm_gmtoff also includes the DST adjustment.
4930         */
4931         if (time_stamp->tm_isdst) {
4932             tmx -= 60L * 60L;
4933             debug(F101,"zstrdt tmx 3 (DST)","",tmx);
4934         }
4935 #endif /* BSD44 */
4936         n = time_stamp->tm_year;
4937         if (n < 300) {
4938             n += 1900;
4939         }
4940     }
4941     return(tmx);
4942 }
4943
4944
4945 #ifdef ZLOCALTIME
4946 /* Z L O C A L T I M E  --  GMT/UTC time string to local time string */
4947
4948 /*
4949    Call with: "yyyymmdd hh:mm:ss" GMT/UTC date-time.
4950    Returns:   "yyyymmdd hh:mm:ss" local date-time on success, NULL on failure.
4951 */
4952 static char zltimbuf[64];
4953
4954 char *
4955 zlocaltime(gmtstring) char * gmtstring; {
4956 #ifdef M_UNIX
4957 /*
4958   SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime().
4959   ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in
4960   dependence on the XPG4 supplement presence.  So always use
4961   what the system header file supplies in ODT 3.0...
4962 */
4963 #ifndef ODT30
4964 #ifndef _SCO_DS
4965     extern void ftime();  /* extern void ftime(struct timeb *) */
4966 #endif /* _SCO_DS */
4967 #endif /* ODT30 */
4968 #else
4969 #ifndef M_XENIX
4970     extern int ftime();
4971 #endif /* M_XENIX */
4972 #endif /* M_UNIX */
4973     extern struct tm * localtime();
4974
4975     /* And this should have been declared always through a header file */
4976 #ifdef HPUX10
4977     time_t tmx;
4978     long days;
4979 #else
4980 #ifdef BSD44
4981     time_t tmx;
4982     long days;
4983 #else
4984     long tmx, days;
4985 #endif /* BSD44 */
4986 #endif /* HPUX10 */
4987     int i, n, x, isleapyear;
4988                    /*       J  F  M  A   M   J   J   A   S   O   N   D   */
4989                    /*      31 28 31 30  31  30  31  31  30  31  30  31   */
4990     static
4991     int monthdays [13] = {  0,0,31,59,90,120,151,181,212,243,273,304,334 };
4992     char s[5];
4993     struct tm *time_stamp;
4994
4995 #ifdef BSD44
4996     struct timeval tp[2];
4997 #else
4998 #ifdef V7
4999     struct utimbuf {
5000       time_t timep[2];          /* New access and modificaton time */
5001     } tp;
5002 #else
5003 #ifdef SYSUTIMEH
5004     struct utimbuf tp;
5005 #else
5006     struct utimbuf {
5007         time_t atime;
5008         time_t mtime;
5009     } tp;
5010 #endif /* SYSUTIMEH */
5011 #endif /* V7 */
5012 #endif /* BSD44 */
5013
5014 #ifdef ANYBSD
5015     static struct timeb tbp;
5016 #endif /* ANYBSD */
5017
5018     char * date = gmtstring;
5019     int len;
5020
5021     len = strlen(date);
5022     debug(F111,"zlocaltime",date,len);
5023
5024     if ((len == 0)
5025         || (len != 17)
5026         || (date[8] != ' ')
5027         || (date[11] != ':')
5028         || (date[14] != ':') ) {
5029         debug(F111,"Bad creation date ",date,len);
5030         return(NULL);
5031     }
5032     debug(F111,"zlocaltime date check 1",date,len);
5033     for(i = 0; i < 8; i++) {
5034         if (!isdigit(date[i])) {
5035             debug(F111,"Bad creation date ",date,len);
5036             return(NULL);
5037         }
5038     }
5039     debug(F111,"zlocaltime date check 2",date,len);
5040     i++;
5041
5042     for (; i < 16; i += 3) {
5043         if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) {
5044             debug(F111,"Bad creation date ",date,len);
5045             return(NULL);
5046         }
5047     }
5048     debug(F111,"zlocaltime date check 3",date,len);
5049
5050     debug(F100,"zlocaltime so far so good","",0);
5051
5052     s[4] = '\0';
5053     for (i = 0; i < 4; i++)             /* Fix the year */
5054       s[i] = date[i];
5055
5056     n = atoi(s);
5057     debug(F111,"zlocaltime year",s,n);
5058     if (n < 1970) {
5059         debug(F100,"zlocaltime fails - year","",n);
5060         return(NULL);
5061     }
5062
5063 /*  Previous year's leap days.  This won't work after year 2100. */
5064
5065     isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
5066     days = (long) (n - 1970) * 365;
5067     days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
5068
5069     s[2] = '\0';
5070
5071     for (i = 4; i < 16; i += 2) {
5072         s[0] = date[i];
5073         s[1] = date[i + 1];
5074         n = atoi(s);
5075         switch (i) {
5076           case 4:                       /* MM: month */
5077             if ((n < 1 ) || ( n > 12)) {
5078                 debug(F111,"zlocaltime 4 bad date ",date,len);
5079                 return(NULL);
5080             }
5081             days += monthdays [n];
5082             if (isleapyear && n > 2)
5083               ++days;
5084             continue;
5085
5086           case 6:                       /* DD: day */
5087             if ((n < 1 ) || ( n > 31)) {
5088                 debug(F111,"zlocaltime 6 bad date ",date,len);
5089                 return(NULL);
5090             }
5091             tmx = (days + n - 1) * 24L * 60L * 60L;
5092             i++;                        /* Skip the space */
5093             continue;
5094
5095           case 9:                       /* hh: hour */
5096             if ((n < 0 ) || ( n > 23)) {
5097                 debug(F111,"zlocaltime 9 bad date ",date,len);
5098                 return(NULL);
5099             }
5100             tmx += n * 60L * 60L;
5101             i++;                        /* Skip the colon */
5102             continue;
5103
5104           case 12:                      /* mm: minute */
5105             if ((n < 0 ) || ( n > 59)) {
5106                 debug(F111,"zlocaltime 12 bad date ",date,len);
5107                 return(NULL);
5108             }
5109             tmx += n * 60L;
5110             i++;                        /* Skip the colon */
5111             continue;
5112
5113           case 15:                      /* ss: second */
5114             if ((n < 0 ) || ( n > 59)) {
5115                 debug(F111,"zlocaltime 15 bad date ",date,len);
5116                 return(NULL);
5117             }
5118             tmx += n;
5119         }
5120
5121 /*
5122   At this point tmx is the time_t representation of the argument date-time
5123   string without any timezone or DST adjustments.  Therefore it should be
5124   the same as the time_t representation of the GMT/UTC time.  Now we should
5125   be able to feed it to localtime() and have it converted to a struct tm
5126   representing the local time equivalent of the given UTC time.
5127 */
5128         time_stamp = localtime(&tmx);
5129         if (!time_stamp)
5130           return(NULL);
5131     }
5132
5133 /* Now we simply reformat the struct tm to a string */
5134
5135     x = time_stamp->tm_year;
5136     if (time_stamp->tm_year < 70 || time_stamp->tm_year > 8099)
5137       return(NULL);
5138     if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11)
5139       return(NULL);
5140     if (time_stamp->tm_mday < 1 || time_stamp->tm_mday > 31)
5141       return(NULL);
5142     if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 24)
5143       return(NULL);
5144     if (time_stamp->tm_min < 0 || time_stamp->tm_min > 60)
5145       return(NULL);
5146     if (time_stamp->tm_sec < 0 || time_stamp->tm_sec > 60)
5147       return(NULL);
5148     sprintf(zltimbuf,"%04d%02d%02d %02d:%02d:%02d",
5149             time_stamp->tm_year + 1900,
5150             time_stamp->tm_mon + 1,
5151             time_stamp->tm_mday,
5152             time_stamp->tm_hour,
5153             time_stamp->tm_min,
5154             time_stamp->tm_sec
5155             );
5156     return((char *)zltimbuf);
5157 }
5158 #endif /* ZLOCALTIME */
5159 #endif /* NOTIMESTAMP */
5160
5161 /* Z S T I M E  --  Set modification date/time+permissions for incoming file */
5162 /*
5163  Call with:
5164  f  = pointer to name of existing file.
5165  yy = pointer to a Kermit file attribute structure in which yy->date.val
5166       is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00.
5167       yy->lprotect.val & yy->gprotect.val are permission/protection values.
5168  x  = is a function code: 0 means to set the file's attributes as given.
5169       1 means compare the date in struct yy with the file creation date.
5170  Returns:
5171  -1 on any kind of error.
5172   0 if x is 0 and the attributes were set successfully.
5173   0 if x is 1 and date from attribute structure <= file creation date.
5174   1 if x is 1 and date from attribute structure > file creation date.
5175 */
5176 int
5177 zstime(f,yy,x)
5178     char *f; struct zattr *yy; int x;
5179 /* zstime */ {
5180     int r = -1;                         /* Return code */
5181 #ifdef CK_PERMS
5182     int setperms = 0;
5183 #endif /* CK_PERMS */
5184     int setdate = 0;
5185
5186 /* It is ifdef'd TIMESTAMP because it might not work on V7. bk@kullmar.se.  */
5187
5188 #ifdef TIMESTAMP
5189 #ifdef BSD44
5190     extern int utimes();
5191 #else
5192     extern int utime();
5193 #endif /* BSD44 */
5194
5195     struct stat sb;
5196
5197 /* At least, the declarations for int functions are not needed anyway */
5198
5199 #ifdef BSD44
5200     struct timeval tp[2];
5201     long xtimezone;
5202 #else
5203 #ifdef V7
5204     struct utimbuf {
5205         time_t timep[2];                /* New access and modificaton time */
5206     } tp;
5207     char *tz;
5208     long timezone;                      /* In case not defined in .h file */
5209 #else
5210 #ifdef SYSUTIMEH
5211     struct utimbuf tp;
5212 #else
5213     struct utimbuf {
5214         time_t atime;
5215         time_t mtime;
5216     } tp;
5217 #endif /* SYSUTIMEH */
5218 #endif /* V7 */
5219 #endif /* BSD44 */
5220
5221     long tm = 0L;
5222
5223     if (!f) f = "";
5224     if (!*f) return(-1);
5225     if (!yy) return(-1);
5226
5227     debug(F110,"zstime",f,0);
5228     debug(F111,"zstime date",yy->date.val,yy->date.len);
5229
5230 #ifdef CKROOT
5231     debug(F111,"zstime setroot",ckroot,ckrootset);
5232     if (ckrootset) if (!zinroot(f)) {
5233         debug(F110,"zstime setroot violation",f,0);
5234         return(0);
5235     }
5236 #endif /* CKROOT */
5237
5238     if (yy->date.len == 0) {            /* No date in struct */
5239         if (yy->lprotect.len != 0) {    /* So go do permissions */
5240             goto zsperms;
5241         } else {
5242             debug(F100,"zstime: nothing to do","",0);
5243             return(0);
5244         }
5245     }
5246     if ((tm = zstrdt(yy->date.val,yy->date.len)) < 0) {
5247         debug(F101,"zstime: zstrdt fails","",0);
5248         return(-1);
5249     }
5250     debug(F101,"zstime: tm","",tm);
5251     debug(F111,"zstime: A-pkt date ok ",yy->date.val,yy->date.len);
5252
5253     if (stat(f,&sb)) {                  /* Get the time for the file */
5254         debug(F101,"STAT","",14);
5255         debug(F111,"zstime: Can't stat file:",f,errno);
5256         return(-1);
5257     }
5258     debug(F101,"STAT","",15);
5259     setdate = 1;
5260
5261   zsperms:
5262 #ifdef CK_PERMS
5263     {
5264         int i, x = 0, xx, flag = 0;
5265         char * s;
5266 #ifdef DEBUG
5267         char obuf[24];
5268         if (deblog) {
5269             debug(F111,"zstime lperms",yy->lprotect.val,yy->lprotect.len);
5270             debug(F111,"zstime gperms",yy->gprotect.val,yy->gprotect.len);
5271             debug(F110,"zstime system id",yy->systemid.val,0);
5272             sprintf(obuf,"%o",sb.st_mode);
5273             debug(F110,"zstime file perms before",obuf,0);
5274         }
5275 #endif /* DEBUG */
5276
5277 #ifdef CK_LOGIN
5278         debug(F101,"zstime isguest","",isguest);
5279         debug(F101,"zstime ckxperms","",ckxperms);
5280         if (isguest) {
5281 #ifdef COMMENT
5282             /* Clear owner permissions */
5283             sb.st_mode &= (unsigned) 0177077; /* (16 bits) */
5284 #else
5285             /* Set permissions from ckxperms variable */
5286             sb.st_mode = ckxperms;
5287 #endif /* COMMENT */
5288             debug(F101,"zstime isguest sb.st_mode","",sb.st_mode);
5289 #ifdef COMMENT
5290             /* We already set them in zopeno() */
5291             setperms = 1;
5292 #endif /* COMMENT */
5293             flag = 0;
5294         } else
5295 #endif /* CK_LOGIN */
5296           if ((yy->lprotect.len > 0 &&  /* Have local-format permissions */
5297             yy->systemid.len > 0 &&     /* from A-packet... */
5298 #ifdef UNIX
5299             !strcmp(yy->systemid.val,"U1") /* AND you are same as me */
5300 #else
5301             0
5302 #endif /* UNIX */
5303              ) || (yy->lprotect.len < 0) /* OR by inheritance from old file */
5304             ) {
5305             flag = 1;
5306             s = yy->lprotect.val;       /* UNIX filemode */
5307             xx = yy->lprotect.len;
5308             if (xx < 0)                 /* len < 0 means inheritance */
5309               xx = 0 - xx;
5310             for (i = 0; i < xx; i++) {  /* Decode octal string */
5311                 if (*s <= '7' && *s >= '0') {
5312                     x = 8 * x + (int)(*s) - '0';
5313                 } else {
5314                     flag = 0;
5315                     break;
5316                 }
5317                 s++;
5318             }
5319 #ifdef DEBUG
5320             sprintf(obuf,"%o",x);
5321             debug(F110,"zstime octal lperm",obuf,0);
5322 #endif /* DEBUG */
5323         } else if (!flag && yy->gprotect.len > 0) {
5324             int g;
5325 #ifdef CK_SCO32V4
5326             mode_t mask;
5327 #else
5328             int mask;
5329 #endif /* CK_SCO32V4 */
5330             mask = umask(0);            /* Get umask */
5331             debug(F101,"zstime mask 1","",mask);
5332             umask(mask);                /* Put it back */
5333             mask ^= 0777;               /* Flip the bits */
5334             debug(F101,"zstime mask 2","",mask);
5335             g = xunchar(*(yy->gprotect.val)); /* Decode generic protection */
5336             debug(F101,"zstime gprotect","",g);
5337 #ifdef S_IRUSR
5338             debug(F100,"zstime S_IRUSR","",0);
5339             if (g & 1) x |= S_IRUSR;    /* Read permission */
5340             flag = 1;
5341 #endif /* S_IRUSR */
5342 #ifdef S_IWUSR
5343             debug(F100,"zstime S_IWUSR","",0);
5344             if (g & 2) x |= S_IWUSR;    /* Write permission */
5345             if (g & 16) x |= S_IWUSR;   /* Delete permission */
5346             flag = 1;
5347 #endif /* S_IWUSR */
5348 #ifdef S_IXUSR
5349             debug(F100,"zstime S_IXUSR","",0);
5350             if (g & 4)                  /* Has execute permission bit */
5351               x |= S_IXUSR;
5352             else                        /* Doesn't have it */
5353               mask &= 0666;             /* so also clear it out of mask */
5354             flag = 1;
5355 #endif /* S_IXUSR */
5356             debug(F101,"zstime mask x","",x);
5357             x |= mask;
5358             debug(F101,"zstime mask x|mask","",x);
5359         }
5360         debug(F101,"zstime flag","",flag);
5361         if (flag) {
5362 #ifdef S_IFMT
5363             debug(F101,"zstime S_IFMT x","",x);
5364             sb.st_mode = (sb.st_mode & S_IFMT) | x;
5365             setperms = 1;
5366 #else
5367 #ifdef _IFMT
5368             debug(F101,"zstime _IFMT x","",x);
5369             sb.st_mode = (sb.st_mode & _IFMT) | x;
5370             setperms = 1;
5371 #endif /* _IFMT */
5372 #endif /* S_IFMT */
5373         }
5374 #ifdef DEBUG
5375         sprintf(obuf,"%04o",sb.st_mode);
5376         debug(F111,"zstime file perms after",obuf,setperms);
5377 #endif /* DEBUG */
5378     }
5379 #endif /* CK_PERMS */
5380
5381     debug(F101,"zstime: sb.st_atime","",sb.st_atime);
5382
5383 #ifdef BSD44
5384     tp[0].tv_sec = sb.st_atime;         /* Access time first */
5385     tp[1].tv_sec = tm;                  /* Update time second */
5386     debug(F100,"zstime: BSD44 modtime","",0);
5387 #else
5388 #ifdef V7
5389     tp.timep[0] = tm;                   /* Set modif. time to creation date */
5390     tp.timep[1] = sb.st_atime;          /* Don't change the access time */
5391     debug(F100,"zstime: V7 modtime","",0);
5392 #else
5393 #ifdef SYSUTIMEH
5394     tp.modtime = tm;                    /* Set modif. time to creation date */
5395     tp.actime = sb.st_atime;            /* Don't change the access time */
5396     debug(F100,"zstime: SYSUTIMEH modtime","",0);
5397 #else
5398     tp.mtime = tm;                      /* Set modif. time to creation date */
5399     tp.atime = sb.st_atime;             /* Don't change the access time */
5400     debug(F100,"zstime: default modtime","",0);
5401 #endif /* SYSUTIMEH */
5402 #endif /* V7 */
5403 #endif /* BSD44 */
5404
5405     switch (x) {                        /* Execute desired function */
5406       case 0:                           /* Set the creation date of the file */
5407 #ifdef CK_PERMS                         /* And permissions */
5408 /*
5409   NOTE: If we are inheriting permissions from a previous file, and the
5410   previous file was a directory, this would turn the new file into a directory
5411   too, but it's not, so we try to unset the right bit.  Luckily, this code
5412   will probably never be executed since the upper level modules do not allow
5413   reception of a file that has the same name as a directory.
5414
5415   NOTE 2: We change the permissions *before* we change the modification time,
5416   otherwise changing the permissions would set the mod time to the present
5417   time.
5418 */
5419         {
5420             int x;
5421             debug(F101,"zstime setperms","",setperms);
5422             if (S_ISDIR(sb.st_mode)) {
5423                 debug(F101,"zstime DIRECTORY bit on","",sb.st_mode);
5424                 sb.st_mode ^= 0040000;
5425                 debug(F101,"zstime DIRECTORY bit off","",sb.st_mode);
5426             }
5427             if (setperms) {
5428                 x = chmod(f,sb.st_mode);
5429                 debug(F101,"zstime chmod","",x);
5430             }
5431         }
5432         if (x < 0) return(-1);
5433 #endif /* CK_PERMS */
5434
5435         if (!setdate)                   /* We don't have a date */
5436           return(0);                    /* so skip the following... */
5437
5438         if (
5439 #ifdef BSD44
5440             utimes(f,tp)
5441 #else
5442             utime(f,&tp)
5443 #endif /* BSD44 */
5444             ) {                         /* Fix modification time */
5445             debug(F111,"zstime 0: can't set modtime for file",f,errno);
5446             r = -1;
5447         } else  {
5448             /* Including the modtime here is not portable */
5449             debug(F110,"zstime 0: modtime set for file",f,0);
5450             r = 0;
5451         }
5452         break;
5453
5454       case 1:                           /* Compare the dates */
5455 /*
5456   This was st_atime, which was wrong.  We want the file-data modification
5457   time, st_mtime.
5458 */
5459         debug(F111,"zstime 1: compare",f,sb.st_mtime);
5460         debug(F111,"zstime 1: compare","packet",tm);
5461
5462         r = (sb.st_mtime < tm) ? 0 : 1;
5463         break;
5464
5465       default:                          /* Error */
5466         r = -1;
5467     }
5468 #endif /* TIMESTAMP */
5469     return(r);
5470 }
5471
5472 /* Find initialization file. */
5473
5474 #ifdef NOTUSED
5475 int
5476 zkermini() {
5477 /*  nothing here for Unix.  This function added for benefit of VMS Kermit.  */
5478     return(0);
5479 }
5480 #endif /* NOTUSED */
5481
5482 #ifndef UNIX
5483 /* Historical -- not used in Unix any more (2001-11-03) */
5484 #ifndef NOFRILLS
5485 int
5486 zmail(p,f) char *p; char *f; {          /* Send file f as mail to address p */
5487 /*
5488   Returns 0 on success
5489    2 if mail delivered but temp file can't be deleted
5490   -2 if mail can't be delivered
5491   -1 on file access error
5492   The UNIX version always returns 0 because it can't get a good return
5493   code from zsyscmd.
5494 */
5495     int n;
5496
5497 #ifdef CK_LOGIN
5498     if (isguest)
5499       return(-2);
5500 #endif /* CK_LOGIN */
5501
5502     if (!f) f = "";
5503     if (!*f) return(-1);
5504
5505 #ifdef CKROOT
5506     debug(F111,"zmail setroot",ckroot,ckrootset);
5507     if (ckrootset) if (!zinroot(f)) {
5508         debug(F110,"zmail setroot violation",f,0);
5509         return(-1);
5510     }
5511 #endif /* CKROOT */
5512
5513 #ifdef BSD4
5514 /* The idea is to use /usr/ucb/mail, rather than regular mail, so that   */
5515 /* a subject line can be included with -s.  Since we can't depend on the */
5516 /* user's path, we use the convention that /usr/ucb/Mail = /usr/ucb/mail */
5517 /* and even if Mail has been moved to somewhere else, this should still  */
5518 /* find it...  The search could be made more reliable by actually using  */
5519 /* access() to see if /usr/ucb/Mail exists. */
5520
5521     n = strlen(f);
5522     n = n + n + 15 + (int)strlen(p);
5523
5524     if (n > ZMBUFLEN)
5525       return(-2);
5526
5527 #ifdef DGUX540
5528     sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
5529 #else
5530     sprintf(zmbuf,"Mail -s %c%s%c %s < %s", '"', f, '"', p, f);
5531 #endif /* DGUX540 */
5532     zsyscmd(zmbuf);
5533 #else
5534 #ifdef SVORPOSIX
5535 #ifndef OXOS
5536     sprintf(zmbuf,"mail %s < %s", p, f);
5537 #else /* OXOS */
5538     sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
5539 #endif /* OXOS */
5540     zsyscmd(zmbuf);
5541 #else
5542     *zmbuf = '\0';
5543 #endif
5544 #endif
5545     return(0);
5546 }
5547 #endif /* NOFRILLS */
5548 #endif /* UNIX */
5549
5550 #ifndef NOFRILLS
5551 int
5552 zprint(p,f) char *p; char *f; {         /* Print file f with options p */
5553     extern char * printername;          /* From ckuus3.c */
5554     extern int printpipe;
5555     int n;
5556
5557 #ifdef CK_LOGIN
5558     if (isguest)
5559       return(-2);
5560 #endif /* CK_LOGIN */
5561
5562     if (!f) f = "";
5563     if (!*f) return(-1);
5564
5565 #ifdef CKROOT
5566     debug(F111,"zprint setroot",ckroot,ckrootset);
5567     if (ckrootset) if (!zinroot(f)) {
5568         debug(F110,"zprint setroot violation",f,0);
5569         return(-1);
5570     }
5571 #endif /* CKROOT */
5572
5573     debug(F110,"zprint file",f,0);
5574     debug(F110,"zprint flags",p,0);
5575     debug(F110,"zprint printername",printername,0);
5576     debug(F101,"zprint printpipe","",printpipe);
5577
5578 #ifdef UNIX
5579 /*
5580   Note use of standard input redirection.  In some systems, lp[r] runs
5581   setuid to lp (or ...?), so if user has sent a file into a directory
5582   that lp does not have read access to, it can't be printed unless it is
5583   fed to lp[r] as standard input.
5584 */
5585     if (printpipe && printername) {
5586         n = 8 + (int)strlen(f) + (int)strlen(printername);
5587         if (n > ZMBUFLEN)
5588           return(-2);
5589         sprintf(zmbuf,"cat %s | %s", f, printername);
5590     } else if (printername) {
5591         n = 8 + (int)strlen(f) + (int)strlen(printername);
5592         if (n > ZMBUFLEN)
5593           return(-2);
5594         sprintf(zmbuf,"cat %s >> %s", f, printername);
5595     } else {
5596         n = 4 + (int)strlen(PRINTCMD) + (int)strlen(p) + (int)strlen(f);
5597         if (n > ZMBUFLEN)
5598           return(-2);
5599         sprintf(zmbuf,"%s %s < %s", PRINTCMD, p, f);
5600     }
5601     debug(F110,"zprint command",zmbuf,0);
5602     zsyscmd(zmbuf);
5603 #else /* Not UNIX */
5604     *zmbuf = '\0';
5605 #endif /* UNIX */
5606     return(0);
5607 }
5608 #endif /* NOFRILLS */
5609
5610 /*  Wildcard expansion functions...  */
5611
5612 static char scratch[MAXPATH+4];         /* Used by both methods */
5613
5614 static int oldmtchs = 0;                /* Let shell (ls) expand them. */
5615 #ifdef COMMENT
5616 static char *lscmd = "/bin/ls -d";      /* Command to use. */
5617 #else
5618 static char *lscmd = "echo";            /* Command to use. */
5619 #endif /* COMMENT */
5620
5621 #ifndef NOPUSH
5622 int
5623 shxpand(pat,namlst,len) char *pat, *namlst[]; int len; {
5624     char *fgbuf = NULL;                 /* Buffer for forming ls command */
5625     char *p, *q;                        /* Workers */
5626
5627     int i, x, retcode, itsadir;
5628     char c;
5629
5630     x = (int)strlen(pat) + (int)strlen(lscmd) + 3; /* Length of ls command */
5631     for (i = 0; i < oldmtchs; i++) {    /* Free previous file list */
5632         if (namlst[i] ) {               /* If memory is allocated  */
5633             free(namlst[i]);            /* Free the memory         */
5634             namlst[i] = NULL ;          /* Remember no memory is allocated */
5635         }
5636     }
5637     oldmtchs = 0 ;                      /* Remember there are no matches */
5638     fgbuf = malloc(x);                  /* Get buffer for command */
5639     if (!fgbuf) return(-1);             /* Fail if cannot */
5640     ckmakmsg(fgbuf,x,lscmd," ",pat,NULL); /* Form the command */
5641     zxcmd(ZIFILE,fgbuf);                /* Start the command */
5642     i = 0;                              /* File counter */
5643     p = scratch;                        /* Point to scratch area */
5644     retcode = -1;                       /* Assume failure */
5645     while ((x = zminchar()) != -1) {    /* Read characters from command */
5646         c = (char) x;
5647         if (c == ' ' || c == '\n') {    /* Got newline or space? */
5648             *p = '\0';                  /* Yes, terminate string */
5649             p = scratch;                /* Point back to beginning */
5650             if (zchki(p) == -1)         /* Does file exist? */
5651               continue;                 /* No, continue */
5652             itsadir = isdir(p);         /* Yes, is it a directory? */
5653             if (xdironly && !itsadir)   /* Want only dirs but this isn't */
5654               continue;                 /* so skip. */
5655             if (xfilonly && itsadir)    /* It's a dir but want only files */
5656               continue;                 /* so skip. */
5657             x = (int)strlen(p);         /* Keep - get length of name */
5658             q = malloc(x+1);            /* Allocate space for it */
5659             if (!q) goto shxfin;        /* Fail if space can't be obtained */
5660             strcpy(q,scratch);          /* (safe) Copy name to space */
5661             namlst[i++] = q;            /* Copy pointer to name into array */
5662             if (i >= len) goto shxfin;  /* Fail if too many */
5663         } else {                        /* Regular character */
5664             *p++ = c;                   /* Copy it into scratch area */
5665         }
5666     }
5667     retcode = i;                        /* Return number of matching files */
5668 shxfin:                                 /* Common exit point */
5669     free(fgbuf);                        /* Free command buffer */
5670     fgbuf = NULL;
5671     zclosf(ZIFILE);                     /* Delete the command fork. */
5672     oldmtchs = i;                       /* Remember how many files */
5673     return(retcode);
5674 }
5675 #endif /* NOPUSH */
5676
5677 /*
5678   Directory-reading functions for UNIX originally written for C-Kermit 4.0
5679   by Jeff Damens, CUCCA, 1984.
5680 */
5681 static char * xpat = NULL;              /* Global copy of fgen() pattern */
5682 static char * xpatlast = NULL;          /* Rightmost segment of pattern*/
5683 static int xpatslash = 0;               /* Slash count in pattern */
5684 static int xpatwild = 0;                /* Original pattern is wild */
5685 static int xleafwild = 0;               /* Last segment of pattern is wild */
5686 static int xpatabsolute = 0;
5687
5688 #ifdef aegis
5689 static char bslash;
5690 #endif /* aegis */
5691
5692
5693 /*  S P L I T P A T H  */
5694
5695 /*
5696   Splits the slash-separated portions of the argument string into
5697   a list of path structures.  Returns the head of the list.  The
5698   structures are allocated by malloc, so they must be freed.
5699   Splitpath is used internally by the filename generator.
5700
5701   Input:
5702     A path string.
5703
5704   Returns:
5705     A linked list of the slash-separated segments of the input.
5706 */
5707 static struct path *
5708 splitpath(p) char *p; {
5709     struct path *head,*cur,*prv;
5710     int i;
5711
5712     debug(F111,"splitpath",p,xrecursive);
5713     head = prv = NULL;
5714
5715     if (!p) return(NULL);
5716     if (!*p) return(NULL);
5717
5718     if (!strcmp(p,"**")) {              /* Fix this */
5719         p = "*";
5720     }
5721     if (ISDIRSEP(*p)) p++;              /* Skip leading slash if any */
5722
5723     /* Make linked list of path segments from pattern */
5724
5725     while (*p) {
5726         cur = (struct path *) malloc(sizeof (struct path));
5727         debug(F101,"splitpath malloc","",cur);
5728         if (cur == NULL) {
5729             debug(F100,"splitpath malloc failure","",0);
5730             prv -> fwd = NULL;
5731             return((struct path *)NULL);
5732         }
5733         cur -> fwd = NULL;
5734         if (head == NULL)               /* First, make list head */
5735           head = cur;
5736         else                            /* Not first, link into chain */
5737           prv -> fwd = cur;
5738         prv = cur;                      /* Link from previous to this one */
5739
5740 #ifdef aegis
5741         /* treat backslash as "../" */
5742         if (bslash && *p == bslash) {
5743             strcpy(cur->npart, "..");   /* safe */
5744             ++p;
5745         } else {
5746             for (i=0; i < MAXNAMLEN && *p && *p != '/' && *p != bslash; i++)
5747               cur -> npart[i] = *p++;
5748             cur -> npart[i] = '\0';     /* end this segment */
5749             if (i >= MAXNAMLEN)
5750               while (*p && *p != '/' && *p != bslash)
5751                 p++;
5752         }
5753         if (*p == '/') p++;
5754 #else
5755         /* General case (UNIX) */
5756         for (i = 0; i < MAXNAMLEN && !ISDIRSEP(*p) && *p != '\0'; i++) {
5757             cur -> npart[i] = *p++;
5758         }
5759
5760         cur -> npart[i] = '\0';         /* End this path segment */
5761         if (i >= MAXNAMLEN)
5762           while (!ISDIRSEP(*p) && *p != '\0') p++;
5763         if (ISDIRSEP(*p))
5764           p++;
5765
5766 #endif /* aegis */
5767     }
5768     if (prv) {
5769         makestr(&xpatlast,prv -> npart);
5770         debug(F110,"splitpath xpatlast",xpatlast,0);
5771     }
5772 #ifdef DEBUG
5773     /* Show original path list */
5774     if (deblog) {
5775         for (i = 0, cur = head; cur; i++) {
5776             debug(F111,"SPLITPATH",cur -> npart, i);
5777             cur = cur -> fwd;
5778         }
5779     }
5780 #endif /* DEBUG */
5781     return(head);
5782 }
5783
5784 /*  F G E N  --  Generate File List  */
5785
5786 /*
5787   File name generator.  It is passed a string, possibly containing wildcards,
5788   and an array of character pointers.  It finds all the matching filenames and
5789   stores pointers to them in the array.  The returned strings are allocated
5790   from a static buffer local to this module (so the caller doesn't have to
5791   worry about deallocating them); this means that successive calls to fgen
5792   will wipe out the results of previous calls.
5793
5794   Input:
5795     A wildcard string, an array to write names to, the length of the array.
5796
5797   Returns:
5798     The number of matches.
5799     The array is filled with filenames that matched the pattern.
5800     If there wasn't enough room in the array, -1 is returned.
5801
5802   Originally by: Jeff Damens, CUCCA, 1984.  Many changes since then.
5803 */
5804 static int
5805 fgen(pat,resarry,len) char *pat,*resarry[]; int len; {
5806     struct path *head;
5807     char *sptr, *s;
5808     int n;
5809
5810 #ifdef aegis
5811     char *namechars;
5812     int tilde = 0, bquote = 0;
5813
5814     if ((namechars = getenv("NAMECHARS")) != NULL) {
5815         if (ckstrchr(namechars, '~' ) != NULL) tilde  = '~';
5816         if (ckstrchr(namechars, '\\') != NULL) bslash = '\\';
5817         if (ckstrchr(namechars, '`' ) != NULL) bquote = '`';
5818     } else {
5819         tilde = '~'; bslash = '\\'; bquote = '`';
5820     }
5821     sptr = scratch;
5822
5823     /* copy "`node_data", etc. anchors */
5824     if (bquote && *pat == bquote)
5825       while (*pat && *pat != '/' && *pat != bslash)
5826         *sptr++ = *pat++;
5827     else if (tilde && *pat == tilde)
5828       *sptr++ = *pat++;
5829     while (*pat == '/')
5830       *sptr++ = *pat++;
5831     if (sptr == scratch) {
5832         strcpy(scratch,"./");           /* safe */
5833         sptr = scratch+2;
5834     }
5835     if (!(head = splitpath(pat))) return(-1);
5836
5837 #else /* not aegis */
5838
5839     debug(F111,"fgen pat",pat,len);
5840     debug(F110,"fgen current directory",zgtdir(),0);
5841     debug(F101,"fgen stathack","",stathack);
5842
5843     scratch[0] = '\0';
5844     xpatwild = 0;
5845     xleafwild = 0;
5846     xpatabsolute = 0;
5847
5848     if (!(head = splitpath(pat)))       /* Make the path segment list */
5849         return(-1);
5850
5851     sptr = scratch;
5852
5853 #ifdef COMMENT
5854     if (strncmp(pat,"./",2) && strncmp(pat,"../",3)) {
5855 #endif /* COMMENT */
5856         if (!ISDIRSEP(*pat))            /* If name is not absolute */
5857           *sptr++ = '.';                /* put "./" in front. */
5858         *sptr++ = DIRSEP;
5859 #ifdef COMMENT
5860     }
5861 #endif /* COMMENT */
5862     *sptr = '\0';
5863 #endif /* aegis */
5864
5865     makestr(&xpat,pat);                 /* Save copy of original pattern */
5866     debug(F110,"fgen scratch",scratch,0);
5867
5868     for (n = 0, s = xpat; *s; s++)      /* How many slashes in the pattern */
5869       if (*s == DIRSEP)                 /* since these are fences for */
5870         n++;                            /* pattern matching */
5871     xpatslash = n;
5872     debug(F101,"fgen xpatslash","",xpatslash);
5873
5874     numfnd = 0;                         /* None found yet */
5875
5876     if (initspace(resarry,ssplen) < 0)
5877       return(-1);
5878
5879     xpatwild = iswild(xpat);            /* Original pattern is wild? */
5880     xpatabsolute = isabsolute(xpat);
5881     xleafwild = iswild(xpatlast);
5882
5883     debug(F111,"fgen xpat",xpat,xpatwild);
5884     debug(F111,"fgen xpatlast",xpatlast,xleafwild);
5885     debug(F101,"fgen xpatabsolute","",xpatabsolute);
5886
5887     traverse(head,scratch,sptr);        /* Go walk the directory tree. */
5888     while (head != NULL) {              /* Done - free path segment list. */
5889         struct path *next = head -> fwd;
5890         free((char *)head);
5891         head = next;
5892     }
5893     debug(F101,"fgen","",numfnd);
5894     return(numfnd);                     /* Return the number of matches */
5895 }
5896
5897 /* Define LONGFN (long file names) automatically for BSD 2.9 and 4.2 */
5898 /* LONGFN can also be defined on the cc command line. */
5899
5900 #ifdef BSD29
5901 #ifndef LONGFN
5902 #define LONGFN
5903 #endif
5904 #endif
5905
5906 #ifdef BSD42
5907 #ifndef LONGFN
5908 #define LONGFN
5909 #endif
5910 #endif
5911
5912 /*
5913    T R A V E R S E  --  Traverse a directory tree.
5914
5915    Walks the directory tree looking for matches to its arguments.
5916    The algorithm is, briefly:
5917
5918     If the current pattern segment contains no wildcards, that
5919     segment is added to what we already have.  If the name so far
5920     exists, we call ourselves recursively with the next segment
5921     in the pattern string; otherwise, we just return.
5922
5923     If the current pattern segment contains wildcards, we open the name
5924     we've accumulated so far (assuming it is really a directory), then read
5925     each filename in it, and, if it matches the wildcard pattern segment, add
5926     that filename to what we have so far and call ourselves recursively on
5927     the next segment.
5928
5929     Finally, when no more pattern segments remain, we add what's accumulated
5930     so far to the result array and increment the number of matches.
5931
5932   Inputs:
5933     A pattern path list (as generated by splitpath), a string pointer that
5934     points to what we've traversed so far (this can be initialized to "/"
5935     to start the search at the root directory, or to "./" to start the
5936     search at the current directory), and a string pointer to the end of
5937     the string in the previous argument, plus the global "recursive",
5938     "xmatchdot", and "xdironly" flags.
5939
5940   Returns: void, with:
5941     mtchs[] containing the array of filename string pointers, and:
5942     numfnd containing the number of filenames.
5943
5944   Although it might be poor practice, the mtchs[] array is revealed to the
5945   outside in case it needs it; for example, to be sorted prior to use.
5946   (It is poor practice because not all platforms implement file lists the
5947   same way; some don't use an array at all.)
5948
5949   Note that addresult() acts as a second-level filter; due to selection
5950   criteria outside of the pattern, it might decline to add files that
5951   this routine asks it to, e.g. because we are collecting only directory
5952   names but not the names of regular files.
5953
5954   WARNING: In the course of C-Kermit 7.0 development, this routine became
5955   ridiculously complex, in order to meet approximately sixty specific
5956   requirements.  DON'T EVEN THINK ABOUT MODIFYING THIS ROUTINE!  Trust me;
5957   it is not possible to fix anything in it without breaking something else.
5958   This routine badly needs a total redesign and rewrite.  Note: There may
5959   be some good applications for realpath() and/or scandir() and/or fts_blah()
5960   here, on platforms where they are available.
5961 */
5962 static VOID
5963 traverse(pl,sofar,endcur) struct path *pl; char *sofar, *endcur; {
5964
5965 /* Appropriate declarations for directory routines and structures */
5966 /* #define OPENDIR means to use opendir(), readdir(), closedir()  */
5967 /* If OPENDIR not defined, we use open(), read(), close() */
5968
5969 #ifdef DIRENT                           /* New way, <dirent.h> */
5970 #define OPENDIR
5971     DIR *fd, *opendir();
5972     struct dirent *dirbuf;
5973     struct dirent *readdir();
5974 #else /* !DIRENT */
5975 #ifdef LONGFN                           /* Old way, <dir.h> with opendir() */
5976 #define OPENDIR
5977     DIR *fd, *opendir();
5978     struct direct *dirbuf;
5979 #else /* !LONGFN */
5980     int fd;                             /* Old way, <dir.h> with open() */
5981     struct direct dir_entry;
5982     struct direct *dirbuf = &dir_entry;
5983 #endif /* LONGFN */
5984 #endif /* DIRENT */
5985     int mopts = 0;                      /* ckmatch() opts */
5986     int depth = 0;                      /* Directory tree depth */
5987
5988     char nambuf[MAXNAMLEN+4];           /* Buffer for a filename */
5989     int itsadir = 0, segisdir = 0, itswild = 0, mresult, n, x /* , y */ ;
5990     struct stat statbuf;                /* For file info. */
5991
5992     debug(F101,"STAT","",16);
5993     if (pl == NULL) {                   /* End of path-segment list */
5994         *--endcur = '\0'; /* Terminate string, overwrite trailing slash */
5995         debug(F110,"traverse add: end of path segment",sofar,0);
5996         addresult(sofar,-1);
5997         return;
5998     }
5999     if (stathack) {
6000         /* This speeds up the search a lot and we still get good results */
6001         /* but it breaks the tagging of directory names done in addresult */
6002         if (xrecursive || xfilonly || xdironly || xpatslash) {
6003             itsadir = xisdir(sofar);
6004             debug(F101,"STAT","",17);
6005         } else
6006           itsadir = (strncmp(sofar,"./",2) == 0);
6007     } else {
6008         itsadir = xisdir(sofar);
6009         debug(F101,"STAT","",18);
6010     }
6011     debug(F111,"traverse entry sofar",sofar,itsadir);
6012
6013 #ifdef CKSYMLINK                        /* We're doing symlinks? */
6014 #ifdef USE_LSTAT                        /* OK to use lstat()? */
6015     if (itsadir && xnolinks) {          /* If not following symlinks */
6016         int x;
6017         struct stat buf;
6018         x = lstat(sofar,&buf);
6019         debug(F111,"traverse lstat 1",sofar,x);
6020         if (x > -1 &&
6021 #ifdef S_ISLNK
6022             S_ISLNK(buf.st_mode)
6023 #else
6024 #ifdef _IFLNK
6025             ((_IFMT & buf.st_mode) == _IFLNK)
6026 #endif /* _IFLNK */
6027 #endif /* S_ISLNK */
6028             )
6029           itsadir = 0;
6030     }
6031 #endif /* USE_LSTAT */
6032 #endif /* CKSYMLINK */
6033
6034     if (!xmatchdot && xpatlast[0] == '.')
6035       xmatchdot = 1;
6036     if (!xmatchdot && xpat[0] == '.' && xpat[1] != '/' && xpat[1] != '.')
6037       xmatchdot = 1;
6038
6039     /* ckmatch() options */
6040
6041     if (xmatchdot)   mopts |= 1;        /* Match dot */
6042     if (!xrecursive) mopts |= 2;        /* Dirsep is fence */
6043
6044     debug(F111,"traverse entry xpat",xpat,xpatslash);
6045     debug(F111,"traverse entry xpatlast",xpatlast,xmatchdot);
6046     debug(F110,"traverse entry pl -> npart",pl -> npart,0);
6047
6048 #ifdef RECURSIVE
6049     if (xrecursive > 0 && !itsadir) {
6050         char * s;         /* Recursive descent and this is a regular file */
6051         *--endcur = '\0'; /* Terminate string, overwrite trailing slash */
6052
6053         /* Find the nth slash from the right and match from there... */
6054         /* (n == the number of slashes in the original pattern - see fgen) */
6055         if (*sofar == '/') {
6056             debug(F110,"traverse xpatslash absolute",sofar,0);
6057             s = sofar;
6058         } else {
6059             debug(F111,"traverse xpatslash relative",sofar,xpatslash);
6060             for (s = endcur - 1, n = 0; s >= sofar; s--) {
6061                 if (*s == '/') {
6062                     if (++n >= xpatslash) {
6063                         s++;
6064                         break;
6065                     }
6066                 }
6067             }
6068         }
6069 #ifndef NOSKIPMATCH
6070         /* This speeds things up a bit. */
6071         /* If it causes trouble define NOSKIPMATCH and rebuild. */
6072         if (xpat[0] == '*' && !xpat[1])
6073           x = xmatchdot ? 1 : (s[0] != '.');
6074         else
6075 #endif /* NOSKIPMATCH */
6076           x = ckmatch(xpat, s, 1, mopts); /* Match with original pattern */
6077         debug(F111,"traverse xpatslash ckmatch",s,x);
6078         if (x > 0) {
6079             debug(F110,"traverse add: recursive, match, && !isdir",sofar,0);
6080             addresult(sofar,itsadir);
6081         }
6082         return;
6083     }
6084 #endif /* RECURSIVE */
6085
6086     debug(F111,"traverse sofar 2",sofar,0);
6087
6088     segisdir = ((pl -> fwd) == NULL) ? 0 : 1;
6089     itswild = iswild(pl -> npart);
6090
6091     debug(F111,"traverse segisdir",sofar,segisdir);
6092     debug(F111,"traverse itswild ",pl -> npart,itswild);
6093
6094 #ifdef RECURSIVE
6095     if (xrecursive > 0) {               /* If recursing and... */
6096         if (segisdir && itswild)        /* this is a dir and npart is wild */
6097           goto blah;                    /* or... */
6098         else if (!xpatabsolute && !xpatwild) /* search object is nonwild */
6099           goto blah;                    /* then go recurse */
6100     }
6101 #endif /* RECURSIVE */
6102
6103     if (!itswild) {                     /* This path segment not wild? */
6104 #ifdef COMMENT
6105         strcpy(endcur,pl -> npart);     /* (safe) Append next part. */
6106         endcur += (int)strlen(pl -> npart); /* Advance end pointer */
6107 #else
6108 /*
6109   strcpy() does not account for quoted metacharacters.
6110   We must remove the quotes before doing the stat().
6111 */
6112         {
6113             int quote = 0;
6114             char c, * s;
6115             s = pl -> npart;
6116             while ((c = *s++)) {
6117                 if (!quote) {
6118                     if (c == CMDQ) {
6119                         quote = 1;
6120                         continue;
6121                     }
6122                 }
6123                 *endcur++ = c;
6124                 quote = 0;
6125             }
6126         }
6127 #endif /* COMMENT */
6128         *endcur = '\0';                 /* End new current string. */
6129
6130         if (stat(sofar,&statbuf) == 0) { /* If this piece exists... */
6131             debug(F110,"traverse exists",sofar,0);
6132             *endcur++ = DIRSEP;         /* add slash to end */
6133             *endcur = '\0';             /* and end the string again. */
6134             traverse(pl -> fwd, sofar, endcur);
6135         }
6136 #ifdef DEBUG
6137         else debug(F110,"traverse not found", sofar, 0);
6138 #endif /* DEBUG */
6139         return;
6140     }
6141
6142     *endcur = '\0';                     /* End current string */
6143     debug(F111,"traverse sofar 3",sofar,0);
6144
6145     if (!itsadir)
6146       return;
6147
6148     /* Search is recursive or ... */
6149     /* path segment contains wildcards, have to open and search directory. */
6150
6151   blah:
6152
6153     debug(F110,"traverse opening directory", sofar, 0);
6154
6155 #ifdef OPENDIR
6156     debug(F110,"traverse opendir()",sofar,0);
6157     if ((fd = opendir(sofar)) == NULL) {        /* Can't open, fail. */
6158         debug(F101,"traverse opendir() failed","",errno);
6159         return;
6160     }
6161     while ((dirbuf = readdir(fd)))
6162 #else /* !OPENDIR */
6163     debug(F110,"traverse directory open()",sofar,0);
6164     if ((fd = open(sofar,O_RDONLY)) < 0) {
6165         debug(F101,"traverse directory open() failed","",errno);
6166         return;
6167     }
6168     while (read(fd, (char *)dirbuf, sizeof dir_entry))
6169 #endif /* OPENDIR */
6170       {                         /* Read each entry in this directory */
6171           int exists;
6172           char *eos, *s;
6173           exists = 0;
6174
6175           /* On some platforms, the read[dir]() can return deleted files, */
6176           /* e.g. HP-UX 5.00.  There is no point in grinding through this */
6177           /* routine when the file doesn't exist... */
6178
6179           if (          /* There  actually is an inode... */
6180 #ifdef BSD42
6181                          dirbuf->d_ino != -1
6182 #else
6183 #ifdef unos
6184                          dirbuf->d_ino != -1
6185 #else
6186 #ifdef QNX
6187                          dirbuf->d_stat.st_ino != 0
6188 #else
6189 #ifdef SOLARIS
6190                          dirbuf->d_ino != 0
6191 #else
6192 #ifdef sun
6193                          dirbuf->d_fileno != 0
6194 #else
6195 #ifdef bsdi
6196                          dirbuf->d_fileno != 0
6197 #else
6198 #ifdef __386BSD__
6199                          dirbuf->d_fileno != 0
6200 #else
6201 #ifdef __FreeBSD__
6202                          dirbuf->d_fileno != 0
6203 #else
6204 #ifdef ultrix
6205                          dirbuf->gd_ino != 0
6206 #else
6207 #ifdef Plan9
6208                          1
6209 #else
6210                          dirbuf->d_ino != 0
6211 #endif /* Plan9 */
6212 #endif /* ultrix */
6213 #endif /* __FreeBSD__ */
6214 #endif /* __386BSD__ */
6215 #endif /* bsdi */
6216 #endif /* sun */
6217 #endif /* SOLARIS */
6218 #endif /* QNX */
6219 #endif /* unos */
6220 #endif /* BSD42 */
6221               )
6222             exists = 1;
6223           if (!exists)
6224             continue;
6225
6226           ckstrncpy(nambuf,             /* Copy the name */
6227                   dirbuf->d_name,
6228                   MAXNAMLEN
6229                   );
6230           if (nambuf[0] == '.') {
6231               if (!nambuf[1] || (nambuf[1] == '.' && !nambuf[2])) {
6232                   debug(F110,"traverse skipping",nambuf,0);
6233                   continue;             /* skip "." and ".." */
6234               }
6235           }
6236           s = nambuf;                   /* Copy name to end of sofar */
6237           eos = endcur;
6238           while ((*eos = *s)) {
6239               s++;
6240               eos++;
6241           }
6242 /*
6243   Now we check the file for (a) whether it is a directory, and (b) whether
6244   its name matches our pattern.  If it is a directory, and if we have been
6245   told to build a recursive list, then we must descend regardless of whether
6246   it matches the pattern.  If it is not a directory and it does not match
6247   our pattern, we skip it.  Note: sofar is the full pathname, nambuf is
6248   the name only.
6249 */
6250           /* Do this first to save pointless function calls */
6251           if (nambuf[0] == '.' && !xmatchdot) /* Dir name starts with '.' */
6252             continue;
6253           if (stathack) {
6254               if (xrecursive || xfilonly || xdironly || xpatslash) {
6255                   itsadir = xisdir(sofar); /* See if it's a directory */
6256                   debug(F101,"STAT","",19);
6257               } else {
6258                   itsadir = 0;
6259               }
6260           } else {
6261               itsadir = xisdir(sofar);
6262               debug(F101,"STAT","",20);
6263           }
6264
6265 #ifdef CKSYMLINK
6266 #ifdef USE_LSTAT
6267           if (itsadir && xnolinks) {            /* If not following symlinks */
6268               int x;
6269               struct stat buf;
6270               x = lstat(sofar,&buf);
6271               debug(F111,"traverse lstat 2",sofar,x);
6272               if (x > -1 &&
6273 #ifdef S_ISLNK
6274                   S_ISLNK(buf.st_mode)
6275 #else
6276 #ifdef _IFLNK
6277                   ((_IFMT & buf.st_mode) == _IFLNK)
6278 #endif /* _IFLNK */
6279 #endif /* S_ISLNK */
6280                   )
6281                 itsadir = 0;
6282           }
6283 #endif /* USE_LSTAT */
6284 #endif /* CKSYMLINK */
6285
6286 #ifdef RECURSIVE
6287           if (xrecursive > 0 && itsadir &&
6288               (xpatlast[0] == '*') && !xpatlast[1]
6289               ) {
6290               debug(F110,
6291                     "traverse add: recursive && isdir && segisdir or match",
6292                     sofar,
6293                     segisdir
6294                     );
6295               addresult(sofar,itsadir);
6296               if (numfnd < 0) return;
6297           }
6298 #endif /* RECURSIVE */
6299
6300           debug(F111,"traverse mresult xpat",xpat,xrecursive);
6301           debug(F111,"traverse mresult pl -> npart",
6302                 pl -> npart,
6303                 ((pl -> fwd) ? 9999 : 0)
6304                 );
6305           debug(F111,"traverse mresult sofar segisdir",sofar,segisdir);
6306           debug(F111,"traverse mresult sofar itsadir",sofar,itsadir);
6307           debug(F101,"traverse mresult xmatchdot","",xmatchdot);
6308 /*
6309   Match the path so far with the pattern after stripping any leading "./"
6310   from either or both.  The pattern chosen is the full original pattern if
6311   the match candidate (sofar) is not a directory, or else just the name part
6312   (pl->npart) if it is.
6313 */
6314           {
6315               char * s1;                /* The pattern */
6316               char * s2 = sofar;        /* The path so far */
6317               char * s3;                /* Worker */
6318               int opts;                 /* Match options */
6319
6320               s1 = itsadir ? pl->npart : xpat;
6321
6322 #ifndef COMMENT
6323               /* I can't explain this but it unbreaks "cd blah/sub<Esc>" */
6324               if (itsadir && !xrecursive && xpatslash > 0 &&
6325                   segisdir == 0 && itswild) {
6326                   s1 = xpat;
6327                   debug(F110,"traverse mresult s1 kludge",s1,0);
6328               }
6329 #endif /* COMMENT */
6330
6331               if (xrecursive && xpatslash == 0)
6332                 s2 = nambuf;
6333               while ((s1[0] == '.') && (s1[1] == '/')) /* Strip "./" */
6334                 s1 += 2;
6335               while ((s2[0] == '.') && (s2[1] == '/')) /* Ditto */
6336                 s2 += 2;
6337               opts = mopts;             /* Match options */
6338               if (itsadir)              /* Current segment is a directory */
6339                 opts = mopts & 1;       /* No fences */
6340               s3 = s2;                  /* Get segment depth */
6341               depth = 0;
6342               while (*s3) { if (*s3++ == '/') depth++; }
6343 #ifndef NOSKIPMATCH
6344               /* This speeds things up a bit. */
6345               /* If it causes trouble define NOSKIPMATCH and rebuild. */
6346               if (depth == 0 && (s1[0] == '*') && !s1[1])
6347                 mresult = xmatchdot ? 1 : (s2[0] != '.');
6348               else
6349 #endif /* NOSKIPMATCH */
6350                 mresult = ckmatch(s1,s2,1,opts); /* Match */
6351           }
6352 #ifdef DEBUG
6353           if (deblog) {
6354               debug(F111,"traverse mresult depth",sofar,depth);
6355               debug(F101,"traverse mresult xpatslash","",xpatslash);
6356               debug(F111,"traverse mresult nambuf",nambuf,mresult);
6357               debug(F111,"traverse mresult itswild",pl -> npart,itswild);
6358               debug(F111,"traverse mresult segisdir",pl -> npart,segisdir);
6359           }
6360 #endif /* DEBUG */
6361           if (mresult ||                /* If match succeeded */
6362               xrecursive ||             /* Or search is recursive */
6363               depth < xpatslash         /* Or not deep enough to match... */
6364               ) {
6365               if (                      /* If it's not a directory... */
6366 /*
6367   The problem here is that segisdir is apparently not set appropriately.
6368   If I leave in the !segisdir test, then "dir /recursive blah" (where blah is
6369   a directory name) misses some regular files because sometimes segisdir
6370   is set and sometimes it's not.  But if I comment it out, then
6371   "dir <star>/<star>.txt lists every file in * and does not even open up the
6372   subdirectories.  However, "dir /rec <star>/<star>.txt" works right.
6373 */
6374 #ifdef COMMENT
6375                   mresult && (!itsadir && !segisdir)
6376 #else
6377                   mresult &&            /* Matched */
6378                   !itsadir &&           /* sofar is not a directory */
6379                   ((!xrecursive && !segisdir) || xrecursive)
6380 #endif /* COMMENT */
6381                   ) {
6382                   debug(F110,
6383                         "traverse add: match && !itsadir",sofar,0);
6384                   addresult(sofar,itsadir);
6385                   if (numfnd < 0) return;
6386               } else if (itsadir && (xrecursive || mresult)) {
6387                   struct path * xx = NULL;
6388                   *eos++ = DIRSEP;      /* Add directory separator */
6389                   *eos = '\0';          /* to end of segment */
6390 #ifdef RECURSIVE
6391                   /* Copy previous pattern segment to this new directory */
6392
6393                   if (xrecursive > 0 && !(pl -> fwd)) {
6394                       xx = (struct path *) malloc(sizeof (struct path));
6395                       pl -> fwd = xx;
6396                       if (xx) {
6397                           xx -> fwd = NULL;
6398                           strcpy(xx -> npart, pl -> npart); /* safe */
6399                       }
6400                   }
6401 #endif /* RECURSIVE */
6402                   traverse(pl -> fwd, sofar, eos); /* Traverse new directory */
6403               }
6404           }
6405       }
6406 #ifdef OPENDIR
6407     closedir(fd);
6408 #else /* !OPENDIR */
6409     close(fd);
6410 #endif /* OPENDIR */
6411 }
6412
6413 /*
6414  * addresult:
6415  *  Adds a result string to the result array.  Increments the number
6416  *  of matches found, copies the found string into our string
6417  *  buffer, and puts a pointer to the buffer into the caller's result
6418  *  array.  Our free buffer pointer is updated.  If there is no
6419  *  more room in the caller's array, the number of matches is set to -1.
6420  * Input: a result string.
6421  * Returns: nothing.
6422  */
6423 static VOID
6424 addresult(str,itsadir) char *str; int itsadir; {
6425     int len;
6426
6427     if (!freeptr) {
6428         debug(F100,"addresult string space not init'd","",0);
6429         initspace(mtchs,ssplen);
6430     }
6431     if (!str) str = "";
6432     debug(F111,"addresult",str,itsadir);
6433     if (!*str)
6434       return;
6435
6436     if (itsadir < 0) {
6437         itsadir = xisdir(str);
6438     }
6439     if ((xdironly && !itsadir) || (xfilonly && itsadir)) {
6440         debug(F111,"addresult skip",str,itsadir);
6441         return;
6442     }
6443     while (str[0] == '.' && ISDIRSEP(str[1])) /* Strip all "./" from front */
6444       str += 2;
6445     if (--remlen < 0) {                 /* Elements left in array of names */
6446         debug(F111,"addresult ARRAY FULL",str,numfnd);
6447         numfnd = -1;
6448         return;
6449     }
6450     len = (int)strlen(str);             /* Space this will use */
6451     debug(F111,"addresult len",str,len);
6452
6453     if (len < 1)
6454       return;
6455
6456     if ((freeptr + len + itsadir + 1) > (sspace + ssplen)) {
6457         debug(F111,"addresult OUT OF SPACE",str,numfnd);
6458 #ifdef DYNAMIC
6459         printf(
6460 "?String space %d exhausted - use SET FILE STRINGSPACE to increase\n",ssplen);
6461 #else
6462         printf("?String space %d exhausted\n",ssplen);
6463 #endif /* DYNAMIC */
6464         numfnd = -1;                    /* Do not record if not enough space */
6465         return;
6466     }
6467     strcpy(freeptr,str);                /* safe */
6468
6469     /* Tag directory names by putting '/' at the end */
6470
6471     if (itsadir && (freeptr[len-1] == '/')) {
6472         freeptr[len++] = DIRSEP;
6473         freeptr[len] = '\0';
6474     }
6475     if (numfnd >= maxnames) {
6476 #ifdef DYNAMIC
6477         printf(
6478 "?Too many files (%d max) - use SET FILE LISTSIZE to increase\n",maxnames);
6479 #else
6480         printf("?Too many files - %d max\n",maxnames);
6481 #endif /* DYNAMIC */
6482         numfnd = -1;
6483         return;
6484     }
6485     str = freeptr;
6486     *resptr++ = freeptr;
6487     freeptr += (len + 1);
6488     numfnd++;
6489     debug(F111,"addresult ADD",str,numfnd);
6490 }
6491
6492 #ifdef COMMENT
6493 /*
6494  * match(pattern,string):
6495  *  pattern matcher.  Takes a string and a pattern possibly containing
6496  *  the wildcard characters '*' and '?'.  Returns true if the pattern
6497  *  matches the string, false otherwise.
6498  * Orignally by: Jeff Damens, CUCCA, 1984
6499  * No longer used as of C-Kermit 7.0, now we use ckmatch() instead (ckclib.c).
6500  *
6501  * Input: a string and a wildcard pattern.
6502  * Returns: 1 if match, 0 if no match.
6503  */
6504 static int
6505 match(pattern, string) char *pattern, *string; {
6506     char *psave = NULL, *ssave = NULL;  /* Backup pointers for failure */
6507     int q = 0;                          /* Quote flag */
6508
6509     if (*string == '.' && *pattern != '.' && !xmatchdot) {
6510         debug(F110,"match skip",string,0);
6511         return(0);
6512     }
6513     while (1) {
6514         for (; *pattern == *string; pattern++,string++) /* Skip first */
6515           if (*string == '\0') return(1); /* End of strings, succeed */
6516
6517         if (*pattern == '\\' && q == 0) { /* Watch out for quoted */
6518             q = 1;                      /* metacharacters */
6519             pattern++;                  /* advance past quote */
6520             if (*pattern != *string) return(0);
6521             continue;
6522         } else q = 0;
6523
6524         if (q) {
6525             return(0);
6526         } else {
6527             if (*string != '\0' && *pattern == '?') {
6528                 pattern++;              /* '?', let it match */
6529                 string++;
6530             } else if (*pattern == '*') { /* '*' ... */
6531                 psave = ++pattern;      /* remember where we saw it */
6532                 ssave = string;         /* let it match 0 chars */
6533             } else if (ssave != NULL && *ssave != '\0') { /* if not at end  */
6534                                         /* ...have seen a star */
6535                 string = ++ssave;       /* skip 1 char from string */
6536                 pattern = psave;        /* and back up pattern */
6537             } else return(0);           /* otherwise just fail */
6538         }
6539     }
6540 }
6541 #endif /* COMMENT */
6542
6543 /*
6544   The following two functions are for expanding tilde in filenames
6545   Contributed by Howie Kaye, CUCCA, developed for CCMD package.
6546 */
6547
6548 /*  W H O A M I  --  Get user's username.  */
6549
6550 /*
6551   1) Get real uid
6552   2) See if the $USER environment variable is set ($LOGNAME on AT&T)
6553   3) If $USER's uid is the same as ruid, realname is $USER
6554   4) Otherwise get logged in user's name
6555   5) If that name has the same uid as the real uid realname is loginname
6556   6) Otherwise, get a name for ruid from /etc/passwd
6557 */
6558 char *
6559 whoami() {
6560 #ifdef DTILDE
6561 #ifdef pdp11
6562 #define WHOLEN 100
6563 #else
6564 #define WHOLEN 257
6565 #endif /* pdp11 */
6566     static char realname[UIDBUFLEN+1];  /* user's name */
6567     static int ruid = -1;               /* user's real uid */
6568     char loginname[UIDBUFLEN+1], envname[256]; /* temp storage */
6569     char *c;
6570     struct passwd *p;
6571     _PROTOTYP(extern char * getlogin, (void) );
6572
6573     if (ruid != -1)
6574       return(realname);
6575
6576     ruid = real_uid();                  /* get our uid */
6577
6578   /* how about $USER or $LOGNAME? */
6579     if ((c = getenv(NAMEENV)) != NULL) { /* check the env variable */
6580         ckstrncpy(envname, c, 255);
6581         if ((p = getpwnam(envname)) != NULL) {
6582             if (p->pw_uid == ruid) {    /* get passwd entry for envname */
6583                 ckstrncpy(realname, envname, UIDBUFLEN); /* uid's are same */
6584                 return(realname);
6585             }
6586         }
6587     }
6588
6589   /* can we use loginname() ? */
6590
6591     if ((c =  getlogin()) != NULL) {    /* name from utmp file */
6592         ckstrncpy (loginname, c, UIDBUFLEN);
6593         if ((p = getpwnam(loginname)) != NULL) /* get passwd entry */
6594           if (p->pw_uid == ruid)        /* for loginname */
6595             ckstrncpy(realname, envname, UIDBUFLEN); /* if uid's are same */
6596     }
6597
6598   /* Use first name we get for ruid */
6599
6600     if ((p = getpwuid(ruid)) == NULL) { /* name for uid */
6601         realname[0] = '\0';             /* no user name */
6602         ruid = -1;
6603         return(NULL);
6604     }
6605     ckstrncpy(realname, p->pw_name, UIDBUFLEN);
6606     return(realname);
6607 #else
6608     return(NULL);
6609 #endif /* DTILDE */
6610 }
6611
6612 /*  T I L D E _ E X P A N D  --  expand ~user to the user's home directory. */
6613
6614 char *
6615 tilde_expand(dirname) char *dirname; {
6616 #ifdef DTILDE
6617 #ifdef pdp11
6618 #define BUFLEN 100
6619 #else
6620 #define BUFLEN 257
6621 #endif /* pdp11 */
6622     struct passwd *user;
6623     static char olddir[BUFLEN+1];
6624     static char oldrealdir[BUFLEN+1];
6625     static char temp[BUFLEN+1];
6626     int i, j;
6627
6628     debug(F111,"tilde_expand",dirname,dirname[0]);
6629
6630     if (dirname[0] != '~')              /* Not a tilde...return param */
6631       return(dirname);
6632     if (!strcmp(olddir,dirname)) {      /* Same as last time */
6633       return(oldrealdir);               /* so return old answer. */
6634     } else {
6635         j = (int)strlen(dirname);
6636         for (i = 0; i < j; i++)         /* find username part of string */
6637           if (!ISDIRSEP(dirname[i]))
6638             temp[i] = dirname[i];
6639           else break;
6640         temp[i] = '\0';                 /* tie off with a NULL */
6641         if (i == 1) {                   /* if just a "~" */
6642 #ifdef IKSD
6643             if (inserver)
6644               user = getpwnam(uidbuf);  /* Get info on current user */
6645             else
6646 #endif /* IKSD */
6647             {
6648                 char * p = whoami();
6649                 if (p)
6650                   user = getpwnam(p);
6651                 else
6652                   user = NULL;
6653             }
6654         } else {
6655             user = getpwnam(&temp[1]);  /* otherwise on the specified user */
6656         }
6657     }
6658     if (user != NULL) {                 /* valid user? */
6659         ckstrncpy(olddir, dirname, BUFLEN); /* remember the directory */
6660         ckstrncpy(oldrealdir,user->pw_dir, BUFLEN); /* and home directory */
6661         ckstrncat(oldrealdir,&dirname[i], BUFLEN);
6662         oldrealdir[BUFLEN] = '\0';
6663         return(oldrealdir);
6664     } else {                            /* invalid? */
6665         ckstrncpy(olddir, dirname, BUFLEN); /* remember for next time */
6666         ckstrncpy(oldrealdir, dirname, BUFLEN);
6667         return(oldrealdir);
6668     }
6669 #else
6670     return(NULL);
6671 #endif /* DTILDE */
6672 }
6673
6674 /*
6675   Functions for executing system commands.
6676   zsyscmd() executes the system command in the normal, default way for
6677   the system.  In UNIX, it does what system() does.  Thus, its results
6678   are always predictable.
6679   zshcmd() executes the command using the user's preferred shell.
6680 */
6681 int
6682 zsyscmd(s) char *s; {
6683 #ifdef aegis
6684     if (nopush) return(-1);
6685     if (!priv_chk()) return(system(s));
6686 #else
6687     PID_T shpid;
6688 #ifdef COMMENT
6689 /* This doesn't work... */
6690     WAIT_T status;
6691 #else
6692     int status;
6693 #endif /* COMMENT */
6694
6695     if (nopush) return(-1);
6696     if ((shpid = fork())) {
6697         if (shpid < (PID_T)0) return(-1); /* Parent */
6698         while (shpid != (PID_T) wait(&status))
6699          ;
6700         return(status);
6701     }
6702     if (priv_can()) {                   /* Child: cancel any priv's */
6703         printf("?Privilege cancellation failure\n");
6704         _exit(255);
6705     }
6706     restorsigs();                       /* Restore ignored signals */
6707 #ifdef HPUX10
6708     execl("/usr/bin/sh","sh","-c",s,NULL);
6709     perror("/usr/bin/sh");
6710 #else
6711 #ifdef Plan9
6712     execl("/bin/rc", "rc", "-c", s, NULL);
6713     perror("/bin/rc");
6714 #else
6715     execl("/bin/sh","sh","-c",s,NULL);
6716     perror("/bin/sh");
6717 #endif /* Plan9 */
6718 #endif /* HPUX10 */
6719     _exit(255);
6720     return(0);                          /* Shut up ANSI compilers. */
6721 #endif /* aegis */
6722 }
6723
6724
6725 /*  Z _ E X E C  --  Overlay ourselves with another program  */
6726
6727 #ifndef NOZEXEC
6728 #ifdef HPUX5
6729 #define NOZEXEC
6730 #else
6731 #ifdef ATT7300
6732 #define NOZEXEC
6733 #endif /* ATT7300 */
6734 #endif /* HPUX5 */
6735 #endif /* NOZEXEC */
6736
6737 VOID
6738 z_exec(p,s,t) char * p, ** s; int t; {  /* Overlay ourselves with "p s..." */
6739 #ifdef NOZEXEC
6740     printf("EXEC /REDIRECT NOT IMPLEMENTED IN THIS VERSION OF C-KERMIT\n");
6741     debug(F110,"z_exec NOT IMPLEMENTED",p,0);
6742 #else
6743     int x;
6744     extern int ttyfd;
6745     debug(F110,"z_exec command",p,0);
6746     debug(F110,"z_exec arg 0",s[0],0);
6747     debug(F110,"z_exec arg 1",s[1],0);
6748     debug(F101,"z_exec t","",t);
6749     errno = 0;
6750     if (t) {
6751         if (ttyfd > 2) {
6752             dup2(ttyfd, 0);
6753             dup2(ttyfd, 1);
6754             /* dup2(ttyfd, 2); */
6755             close(ttyfd);
6756         }
6757     }
6758     restorsigs();                       /* Restore ignored signals */
6759     x = execvp(p,s);
6760     if (x < 0) debug(F101,"z_exec errno","",errno);
6761 #endif /* NOZEXEC */
6762 }
6763
6764 /*
6765   Z S H C M D  --  Execute a shell command (or program thru the shell).
6766
6767   Original UNIX code by H. Fischer; copyright rights assigned to Columbia U.
6768   Adapted to use getpwuid to find login shell because many systems do not
6769   have SHELL in environment, and to use direct calling of shell rather
6770   than intermediate system() call. -- H. Fischer (1985); many changes since
6771   then.  Call with s pointing to command to execute.  Returns:
6772    -1 on failure to start the command (can't find, can't fork, can't run).
6773     1 if command ran and gave an exit status of 0.
6774     0 if command ran and gave a nonzero exit status.
6775   with pexitstatus containing the command's exit status.
6776 */
6777 int
6778 zshcmd(s) char *s; {
6779     PID_T pid;
6780
6781 #ifdef NOPUSH
6782     return(0);
6783 #else
6784     if (nopush) return(-1);
6785     debug(F110,"zshcmd command",s,0);
6786
6787 #ifdef aegis
6788     if ((pid = vfork()) == 0) {         /* Make child quickly */
6789         char *shpath, *shname, *shptr;  /* For finding desired shell */
6790
6791         if (priv_can()) exit(1);        /* Turn off privs. */
6792         if ((shpath = getenv("SHELL")) == NULL) shpath = "/com/sh";
6793
6794 #else                                   /* All Unix systems */
6795     if ((pid = fork()) == 0) {          /* Make child */
6796         char *shpath, *shname, *shptr;  /* For finding desired shell */
6797         struct passwd *p;
6798 #ifdef HPUX10                           /* Default */
6799         char *defshell = "/usr/bin/sh";
6800 #else
6801 #ifdef Plan9
6802         char *defshell = "/bin/rc";
6803 #else
6804         char *defshell = "/bin/sh";
6805 #endif /* Plan9 */
6806 #endif /* HPUX10 */
6807         if (priv_can()) exit(1);        /* Turn off privs. */
6808 #ifdef COMMENT
6809 /* Old way always used /etc/passwd shell */
6810         p = getpwuid(real_uid());       /* Get login data */
6811         if (p == (struct passwd *) NULL || !*(p->pw_shell))
6812           shpath = defshell;
6813         else
6814           shpath = p->pw_shell;
6815 #else
6816 /* New way lets user override with SHELL variable, but does not rely on it. */
6817 /* This allows user to specify a different shell. */
6818         shpath = getenv("SHELL");       /* What shell? */
6819         debug(F110,"zshcmd SHELL",shpath,0);
6820         if (shpath == NULL) {
6821             p = getpwuid( real_uid() ); /* Get login data */
6822             if (p == (struct passwd *)NULL || !*(p->pw_shell))
6823               shpath = defshell;
6824             else shpath = p->pw_shell;
6825             debug(F110,"zshcmd shpath",shpath,0);
6826         }
6827 #endif /* COMMENT */
6828 #endif /* aegis */
6829         shptr = shname = shpath;
6830         while (*shptr != '\0')
6831           if (*shptr++ == DIRSEP)
6832             shname = shptr;
6833         restorsigs();                   /* Restore ignored signals */
6834         debug(F110,"zshcmd shname",shname,0);
6835         if (s == NULL || *s == '\0') {  /* Interactive shell requested? */
6836             execl(shpath,shname,"-i",NULL); /* Yes, do that */
6837         } else {                        /* Otherwise, */
6838             execl(shpath,shname,"-c",s,NULL); /* exec the given command */
6839         }                               /* If execl() failed, */
6840         exit(BAD_EXIT);                 /* return bad return code. */
6841
6842     } else {                            /* Parent */
6843
6844         int wstat;                      /* ... must wait for child */
6845 #ifdef CK_CHILD
6846         int child;                      /* Child's exit status */
6847 #endif /* CK_CHILD */
6848         SIGTYP (*istat)(), (*qstat)();
6849
6850         if (pid == (PID_T) -1) return(-1); /* fork() failed? */
6851
6852         istat = signal(SIGINT,SIG_IGN); /* Let the fork handle keyboard */
6853         qstat = signal(SIGQUIT,SIG_IGN); /* interrupts itself... */
6854
6855 #ifdef CK_CHILD
6856         while (((wstat = wait(&child)) != pid) && (wstat != -1))
6857 #else
6858         while (((wstat = wait((WAIT_T *)0)) != pid) && (wstat != -1))
6859 #endif /* CK_CHILD */
6860           ;                             /* Wait for fork */
6861         signal(SIGINT,istat);           /* Restore interrupts */
6862         signal(SIGQUIT,qstat);
6863 #ifdef CK_CHILD
6864         pexitstat = (child & 0xff) ? child : child >> 8;
6865         debug(F101,"zshcmd exit status","",pexitstat);
6866         return(child == 0 ? 1 : 0);     /* Return child's status */
6867 #endif /* CK_CHILD */
6868     }
6869     return(1);
6870 #endif /* NOPUSH */
6871 }
6872
6873 /*  I S W I L D  --  Check if filespec is "wild"  */
6874
6875 /*
6876   Returns:
6877     0 if argument is empty or is the name of a single file;
6878     1 if it contains wildcard characters.
6879   Note: must match the algorithm used by match(), hence no [a-z], etc.
6880 */
6881 int
6882 iswild(filespec) char *filespec; {
6883     char c, *p, *f; int x;
6884     int quo = 0;
6885     if (!filespec)
6886       return(0);
6887     f = filespec;
6888     if (wildxpand) {                    /* Shell handles wildcarding */
6889         if ((x = nzxpand(filespec,0)) > 1)
6890           return(1);
6891         if (x == 0) return(0);          /* File does not exist */
6892         p = malloc(MAXNAMLEN + 20);
6893         znext(p);
6894         x = (strcmp(filespec,p) != 0);
6895         free(p);
6896         p = NULL;
6897         return(x);
6898     } else {                            /* We do it ourselves */
6899         while ((c = *filespec++) != '\0') {
6900             if (c == '\\' && quo == 0) {
6901                 quo = 1;
6902                 continue;
6903             }
6904             if (!quo && (c == '*' || c == '?'
6905 #ifdef CKREGEX
6906 #ifndef VMS
6907                          || c == '['
6908 #endif /* VMS */
6909                          || c == '{'
6910 #endif /* CKREGEX */
6911                          )) {
6912                 debug(F111,"iswild",f,1);
6913                 return(1);
6914             }
6915             quo = 0;
6916         }
6917         debug(F111,"iswild",f,0);
6918         return(0);
6919     }
6920 }
6921
6922 /*
6923   I S D I R  --  Is a Directory.
6924
6925   Tell if string pointer s is the name of an existing directory.  Returns 1 if
6926   directory, 0 if not a directory.
6927
6928   The following no longer applies:
6929
6930   If the file is a symlink, we return 1 if
6931   it is a directory OR if it is a link to a directory and the "xrecursive" flag
6932   is NOT set.  This is to allow parsing a link to a directory as if it were a
6933   directory (e.g. in the CD or IF DIRECTORY command) but still prevent
6934   recursive traversal from visiting the same directory twice.
6935 */
6936
6937 #ifdef ISDIRCACHE
6938 /* This turns out to be unsafe and gives little benefit anyway. */
6939 /* See notes 28 Sep 2003.  Thus ISDIRCACHE is not defined. */
6940
6941 static char prevpath[CKMAXPATH+4] = { '\0', '\0' };
6942 static int prevstat = -1;
6943 int
6944 clrdircache() {
6945     debug(F100,"CLEAR ISDIR CACHE","",0);
6946     prevstat = -1;
6947     prevpath[0] = NUL;
6948 }
6949 #endif /* ISDIRCACHE */
6950
6951 int
6952 isdir(s) char *s; {
6953     int x, needrlink = 0, islink = 0;
6954     struct stat statbuf;
6955     char fnam[CKMAXPATH+4];
6956
6957     if (!s) return(0);
6958     if (!*s) return(0);
6959
6960 #ifdef ISDIRCACHE
6961     if (prevstat > -1) {
6962         if (s[0] == prevpath[0]) {
6963             if (!strcmp(s,prevpath)) {
6964                 debug(F111,"isdir cache hit",s,prevstat);
6965                 return(prevstat);
6966             }
6967         }
6968     }
6969 #endif /* ISDIRCACHE */
6970
6971 #ifdef CKSYMLINK
6972 #ifdef COMMENT
6973 /*
6974   The following over-clever bit has been commented out because it presumes
6975   to know when a symlink might be redundant, which it can't possibly know.
6976   Using plain old stat() gives Kermit the same results as ls and ls -R, which
6977   is just fine: no surprises.
6978 */
6979 #ifdef USE_LSTAT
6980     if (xrecursive) {
6981         x = lstat(s,&statbuf);
6982         debug(F111,"isdir lstat",s,x);
6983     } else {
6984 #endif /* USE_LSTAT */
6985         x = stat(s,&statbuf);
6986         debug(F111,"isdir stat",s,x);
6987 #ifdef USE_LSTAT
6988     }
6989 #endif /* USE_LSTAT */
6990 #else
6991     x = stat(s,&statbuf);
6992     debug(F111,"isdir stat",s,x);
6993 #endif /* COMMENT */
6994     if (x == -1) {
6995         debug(F101,"isdir errno","",errno);
6996         return(0);
6997     }
6998     islink = 0;
6999     if (xrecursive) {
7000 #ifdef NOLINKBITS
7001         needrlink = 1;
7002 #else
7003 #ifdef S_ISLNK
7004         islink = S_ISLNK(statbuf.st_mode);
7005         debug(F101,"isdir S_ISLNK islink","",islink);
7006 #else
7007 #ifdef _IFLNK
7008         islink = (_IFMT & statbuf.st_mode) == _IFLNK;
7009         debug(F101,"isdir _IFLNK islink","",islink);
7010 #endif /* _IFLNK */
7011 #endif /* S_ISLNK */
7012 #endif /* NOLINKBITS */
7013         if (needrlink) {
7014             if (readlink(s,fnam,CKMAXPATH) > -1)
7015               islink = 1;
7016         }
7017     }
7018 #else
7019     x = stat(s,&statbuf);
7020     if (x == -1) {
7021         debug(F101,"isdir errno","",errno);
7022         return(0);
7023     }
7024     debug(F111,"isdir stat",s,x);
7025 #endif /* CKSYMLINK */
7026     debug(F101,"isdir islink","",islink);
7027     debug(F101,"isdir statbuf.st_mode","",statbuf.st_mode);
7028     x = islink ? 0 : (S_ISDIR (statbuf.st_mode) ? 1 : 0);
7029 #ifdef ISDIRCACHE
7030     prevstat = x;
7031     ckstrncpy(prevpath,s,CKMAXPATH+1);
7032 #endif /* ISDIRCACHE */
7033     return(x);
7034 }
7035
7036 #ifdef CK_MKDIR
7037 /* Some systems don't have mkdir(), e.g. Tandy Xenix 3.2.. */
7038
7039 /* Z M K D I R  --  Create directory(s) if necessary */
7040 /*
7041    Call with:
7042     A pointer to a file specification that might contain directory
7043     information.  The filename is expected to be included.
7044     If the file specification does not include any directory separators,
7045     then it is assumed to be a plain file.
7046     If one or more directories are included in the file specification,
7047     this routine tries to create them if they don't already exist.
7048    Returns:
7049     0 or greater on success, i.e. the number of directories created.
7050    -1 on failure to create the directory
7051 */
7052 int
7053 zmkdir(path) char *path; {
7054     char *xp, *tp, c;
7055     int x, count = 0;
7056
7057     if (!path) path = "";
7058     if (!*path) return(-1);
7059
7060 #ifdef CKROOT
7061     debug(F111,"zmkdir setroot",ckroot,ckrootset);
7062     if (ckrootset) if (!zinroot(path)) {
7063         debug(F110,"zmkdir setroot violation",path,0);
7064         return(-1);
7065     }
7066 #endif /* CKROOT */
7067
7068     x = strlen(path);
7069     debug(F111,"zmkdir",path,x);
7070     if (x < 1 || x > MAXPATH)           /* Check length */
7071       return(-1);
7072     if (!(tp = malloc(x+1)))            /* Make a temporary copy */
7073       return(-1);
7074     strcpy(tp,path);                    /* safe (prechecked) */
7075 #ifdef DTILDE
7076     if (*tp == '~') {                   /* Starts with tilde? */
7077         xp = tilde_expand(tp);          /* Attempt to expand tilde */
7078         if (!xp) xp = "";
7079         if (*xp) {
7080             char *zp;
7081             debug(F110,"zmkdir tilde_expand",xp,0);
7082             if (!(zp = malloc(strlen(xp) + 1))) { /* Make a place for it */
7083                 free(tp);
7084                 tp = NULL;
7085                 return(-1);
7086             }
7087             free(tp);                   /* Free previous buffer */
7088             tp = zp;                    /* Point to new one */
7089             strcpy(tp,xp);              /* Copy expanded name to new buffer */
7090         }
7091     }
7092 #endif /* DTILDE */
7093     debug(F110,"zmkdir tp after tilde_expansion",tp,0);
7094     xp = tp;
7095     if (ISDIRSEP(*xp))                  /* Don't create root directory! */
7096       xp++;
7097
7098     /* Go thru filespec from left to right... */
7099
7100     for (; *xp; xp++) {                 /* Create parts that don't exist */
7101         if (!ISDIRSEP(*xp))             /* Find next directory separator */
7102           continue;
7103         c = *xp;                        /* Got one. */
7104         *xp = NUL;                      /* Make this the end of the string. */
7105         if (!isdir(tp)) {               /* This directory exists already? */
7106 #ifdef CK_LOGIN
7107             if (isguest)                    /* Not allowed for guests */
7108               return(-1);
7109 #ifndef NOXFER
7110             /* Nor if MKDIR and/or CD are disabled */
7111             else
7112 #endif /* CK_LOGIN */
7113               if ((server
7114 #ifdef IKSD
7115                    || inserver
7116 #endif /* IKSD */
7117                    ) && (!ENABLED(en_mkd) || !ENABLED(en_cwd)))
7118                 return(-1);
7119 #endif /* IKSD */
7120
7121             debug(F110,"zmkdir making",tp,0);
7122             x =                         /* No, try to create it */
7123 #ifdef NOMKDIR
7124                -1                       /* Systems without mkdir() */
7125 #else
7126                mkdir(tp,0777)           /* UNIX */
7127 #endif /* NOMKDIR */
7128                  ;
7129             if (x < 0) {
7130                 debug(F101,"zmkdir failed, errno","",errno);
7131                 free(tp);               /* Free temporary buffer. */
7132                 tp = NULL;
7133                 return(-1);             /* Return failure code. */
7134             } else
7135               count++;
7136         }
7137         *xp = c;                        /* Replace the separator. */
7138     }
7139     free(tp);                           /* Free temporary buffer. */
7140     return(count);                      /* Return success code. */
7141 }
7142 #endif /* CK_MKDIR */
7143
7144 int
7145 zrmdir(path) char *path; {
7146 #ifdef CK_LOGIN
7147     if (isguest)
7148       return(-1);
7149 #endif /* CK_LOGIN */
7150
7151     if (!path) path = "";
7152     if (!*path) return(-1);
7153
7154 #ifdef CKROOT
7155     debug(F111,"zrmdir setroot",ckroot,ckrootset);
7156     if (ckrootset) if (!zinroot(path)) {
7157         debug(F110,"zrmdir setroot violation",path,0);
7158         return(-1);
7159     }
7160 #endif /* CKROOT */
7161
7162 #ifndef NOMKDIR
7163     return(rmdir(path));
7164 #else
7165     return(-1);
7166 #endif /* NOMKDIR */
7167 }
7168
7169 /* Z F S E E K  --  Position input file pointer */
7170 /*
7171    Call with:
7172     Long int, 0-based, indicating desired position.
7173    Returns:
7174     0 on success.
7175    -1 on failure.
7176 */
7177 #ifndef NORESEND
7178 int
7179 #ifdef CK_ANSIC
7180 zfseek(long pos)
7181 #else
7182 zfseek(pos) long pos;
7183 #endif /* CK_ANSIC */
7184 /* zfseek */ {
7185     zincnt = -1;                        /* Must empty the input buffer */
7186     debug(F101,"zfseek","",pos);
7187     return(fseek(fp[ZIFILE], pos, 0)?-1:0);
7188 }
7189 #endif /* NORESEND */
7190
7191 /*  Z F N Q F P  --  Convert filename to fully qualified absolute pathname */
7192
7193 static struct zfnfp fnfp = { 0, NULL, NULL };
7194
7195 struct zfnfp *
7196 zfnqfp(fname, buflen, buf)  char * fname; int buflen; char * buf; {
7197     char * s;
7198     int len;
7199 #ifdef MAXPATHLEN
7200     char zfntmp[MAXPATHLEN+4];
7201 #else
7202     char zfntmp[CKMAXPATH+4];
7203 #endif /* MAXPATHLEN */
7204
7205     char sb[32], * tmp;
7206     int i = 0, j = 0, k = 0, x = 0, y = 0;
7207     int itsadir = 0;
7208
7209     s = fname;
7210     if (!s)
7211       return(NULL);
7212     if (!*s)
7213       return(NULL);
7214     if (!buf)
7215       return(NULL);
7216
7217     /* Initialize the data structure */
7218
7219     fnfp.len = ckstrncpy(buf,fname,buflen);
7220     fnfp.fpath = buf;
7221     fnfp.fname = NULL;
7222     len = buflen;
7223     debug(F111,"zfnqfp fname",fname,len);
7224
7225 #ifdef DTILDE
7226     if (*s == '~') {                    /* Starts with tilde? */
7227         char * xp;
7228         xp = tilde_expand(s);           /* Attempt to expand tilde */
7229         debug(F110,"zfnqfp xp",xp,0);   /* (realpath() doesn't do this) */
7230         if (!xp) xp = "";
7231         if (*xp)
7232           s = xp;
7233     }
7234 #endif /* DTILDE */
7235
7236 #ifdef CKREALPATH
7237
7238 /* N.B.: The realpath() result buffer MUST be MAXPATHLEN bytes long */
7239 /* otherwise we write over memory. */
7240
7241     if (!realpath(s,zfntmp)) {
7242         debug(F111,"zfnqfp realpath fails",s,errno);
7243 #ifdef COMMENT
7244         if (errno != ENOENT)
7245           return(NULL);
7246 #else
7247         /* If realpath() fails use the do-it-yourself method */
7248         /* 16 Jan 2002 */
7249         goto norealpath;
7250 #endif /* COMMENT */
7251     }
7252     len = strlen(zfntmp);
7253     if (len > buflen) {
7254         debug(F111,"zfnqfp result too long",ckitoa(buflen),len);
7255         return(NULL);
7256     } else {
7257         ckstrncpy(buf,zfntmp,buflen);
7258     }
7259     if (buf[len-1] != '/') {
7260         if ((itsadir = isdir(buf)) && len < (buflen - 1)) {
7261             buf[len++] = '/';
7262             buf[len] = NUL;
7263         }
7264     }
7265     fnfp.len = len;
7266     fnfp.fpath = buf;
7267     debug(F110,"zfnqfp realpath path",fnfp.fpath,0);
7268     tmp = buf + fnfp.len - 1;
7269     if (!itsadir) {
7270         while (tmp >= buf) {
7271             if (*tmp == '/') {
7272                 fnfp.fname = tmp + 1;
7273                 debug(F110,"zfnqfp realpath name",fnfp.fname,0);
7274                 break;
7275             }
7276             tmp--;
7277         }
7278     }
7279     return(&fnfp);
7280
7281 #endif /* CKREALPATH */
7282
7283   norealpath:
7284
7285     tmp = zfntmp;
7286     while (*s) {                        /* Remove leading "./" (0 or more) */
7287         debug(F110,"zfnqfp while *s",s,0);
7288         if (*s == '.' && *(s+1) == '/') {
7289             s += 2;
7290             while (*s == '/') s++;
7291         } else
7292           break;
7293     }
7294     if (!*s) return(NULL);
7295     if (*s == '/') {                    /* Pathname is absolute */
7296         ckstrncpy(buf,s,len);
7297         x = strlen(buf);
7298         y = 0;
7299     } else {                            /* Pathname is relative */
7300         char * p;
7301         if (p = zgtdir()) {             /* So get current directory */
7302             debug(F110,"zfnqfp zgtdir",p,0);
7303             x = ckstrncpy(buf,p,len);
7304             buf[x++] = '/';
7305             debug(F110,"zfnqfp buf 1",buf,0);
7306             len -= x;                   /* How much room left in buffer */
7307             if ((y = (int)strlen(s)) > len) /* If enough room... */
7308               return(NULL);
7309             ckstrncpy(buf+x,s,len);     /* ... append the filename */
7310             debug(F110,"zfnqfp buf 2",buf,0);
7311         } else {
7312             return(NULL);
7313         }
7314     }
7315
7316     /* Buf now holds full path but maybe containing some . or .. tricks */
7317
7318     j = x + y;                          /* Length of what's in buf */
7319     len = j;
7320     debug(F101,"zfnqfp len","",len);
7321
7322     /* Catch dangling "/." or "/.." */
7323     if ((j > 1 && buf[j-1] == '.' && buf[j-2] == '/') ||
7324         (j > 2 && buf[j-1] == '.' && buf[j-2] == '.' && buf[j-3] == '/')) {
7325         if (j < buflen - 2) {
7326             buf[j] = '/';
7327             buf[j+1] = NUL;
7328         }
7329     }
7330     j = -1;                             /* j = position of rightmost "/" */
7331     i = 0;                              /* i = destination index */
7332     tmp[i] = NUL;                       /* destination is temporary buffer  */
7333
7334     for (x = 0; x < len; x++) {         /* x = source index */
7335         if (buf[x] == '/') {
7336             for (k = 0; k < 4; k++) {
7337                 sb[k] = buf[x+k];
7338                 sb[k+1] = '\0';
7339                 if (!sb[k]) break;
7340             }
7341             if (!strncmp(sb,"/./",3)) { /* Eliminate "./" in "/./" */
7342                 x += 1;
7343                 continue;
7344             } else if (!strncmp(sb,"//",2)) { /* Change "//" to "/" */
7345                 continue;
7346             } else if (!strncmp(sb,"/../",4)) { /* ".." in path */
7347                 for (k = i - 1; k >= 0; k--) { /* Back up one level */
7348                     if (tmp[k] == '/') {
7349                         i = k;
7350                         tmp[i] = NUL;
7351                         break;
7352                     }
7353                 }
7354                 x += 2;
7355                 continue;
7356             }
7357         }
7358         if (i >= (buflen - 1)) {
7359             debug(F111,"zfnqfp overflow",tmp,i);
7360             return(NULL);
7361         }
7362         tmp[i++] = buf[x];              /* Regular character, copy */
7363         tmp[i] = NUL;
7364         if (buf[x] == '/')              /* Remember rightmost "/" */
7365           j = i;
7366     }
7367     ckstrncpy(buf,tmp,buflen-1);        /* Copy the result back */
7368
7369     buf[buflen-1] = NUL;
7370     if (!buf[0]) {                      /* If empty, say root */
7371         buf[0] = '/';
7372         buf[2] = NUL;
7373         j = 0;
7374         i = 1;
7375     }
7376     if ((itsadir = isdir(buf))) {
7377         if (buf[i-1] != '/' && i < (buflen - 1)) {
7378             buf[i++] = '/';
7379             buf[i] = NUL;
7380         }
7381     }
7382     if (!itsadir && (j > -1)) {         /* Set pointer to basename */
7383         fnfp.fname = (char *)(buf + j);
7384         fnfp.fpath = (char *)buf;
7385         fnfp.len = i;
7386         debug(F111,"zfnqfp path",fnfp.fpath,i);
7387         debug(F110,"zfnqfp name",fnfp.fname,0);
7388         return(&fnfp);
7389     }
7390     return(NULL);
7391 }
7392
7393 /*  Z C M P F N  --  Compare two filenames  */
7394
7395 /*  Returns 1 if the two names refer to the same existing file, 0 otherwise. */
7396
7397 int
7398 zcmpfn(s1,s2) char * s1, * s2; {
7399     char buf1[CKMAXPATH+1];
7400     char buf2[CKMAXPATH+1];
7401
7402 #ifdef USE_LSTAT
7403     char linkname[CKMAXPATH+1];
7404     struct stat buf;
7405 #endif /* USE_LSTAT */
7406     int x, rc = 0;
7407
7408     if (!s1) s1 = "";
7409     if (!s2) s2 = "";
7410     if (!*s1 || !*s2) return(0);
7411
7412 #ifdef CKSYMLINK                        /* We're doing symlinks? */
7413 #ifdef USE_LSTAT                        /* OK to use lstat()? */
7414     x = lstat(s1,&buf);
7415     if (x > -1 &&                       /* Now see if it's a symlink */
7416 #ifdef S_ISLNK
7417         S_ISLNK(buf.st_mode)
7418 #else
7419 #ifdef _IFLNK
7420         ((_IFMT & buf.st_mode) == _IFLNK)
7421 #endif /* _IFLNK */
7422 #endif /* S_ISLNK */
7423         ) {
7424         linkname[0] = '\0';             /* Get the name */
7425         x = readlink(s1,linkname,CKMAXPATH);
7426         if (x > -1 && x < CKMAXPATH) {  /* It's a link */
7427             linkname[x] = '\0';
7428             s1 = linkname;
7429         }
7430     }
7431 #endif /* USE_LSTAT */
7432 #endif /* CKSYMLINK */
7433
7434     if (zfnqfp(s1,CKMAXPATH,buf1)) {    /* Convert to full pathname */
7435
7436 #ifdef CKSYMLINK                        /* Same deal for second name... */
7437 #ifdef USE_LSTAT
7438         x = lstat(s2,&buf);
7439         if (x > -1 &&
7440 #ifdef S_ISLNK
7441             S_ISLNK(buf.st_mode)
7442 #else
7443 #ifdef _IFLNK
7444             ((_IFMT & buf.st_mode) == _IFLNK)
7445 #endif /* _IFLNK */
7446 #endif /* S_ISLNK */
7447             ) {
7448             linkname[0] = '\0';
7449             x = readlink(s2,linkname,CKMAXPATH);
7450             if (x > -1 && x < CKMAXPATH) {
7451                 linkname[x] = '\0';
7452                 s2 = linkname;
7453             }
7454         }
7455 #endif /* USE_LSTAT */
7456 #endif /* CKSYMLINK */
7457         if (zfnqfp(s2,CKMAXPATH,buf2)) {
7458             debug(F110,"zcmpfn s1",buf1,0);
7459             debug(F110,"zcmpfn s2",buf2,0);
7460             if (!strncmp(buf1,buf2,CKMAXPATH))
7461               rc = 1;
7462         }
7463     }
7464     debug(F101,"zcmpfn result","",rc);
7465     return(rc);
7466 }
7467
7468 #ifdef CKROOT
7469
7470 /* User-mode chroot() implementation */
7471
7472 int
7473 zsetroot(s) char * s; {                 /* Sets the root */
7474     char buf[CKMAXPATH+1];
7475     if (!s) return(-1);
7476     if (!*s) return(-1);
7477     debug(F110,"zsetroot",s,0);
7478     if (!isdir(s)) return(-2);
7479     if (!zfnqfp(s,CKMAXPATH,buf))       /* Get full, real path */
7480       return(-3);
7481     if (access(buf,R_OK) < 0) {         /* Check access */
7482         debug(F110,"zsetroot access denied",buf,0);
7483         return(-4);
7484     }
7485     s = buf;
7486     if (ckrootset) {                    /* If root already set */
7487         if (!zinroot(s)) {              /* make sure new root is in it */
7488             debug(F110,"zsetroot new root not in root",ckroot,0);
7489             return(-5);
7490         }
7491     }
7492     if (zchdir(buf) < 1) return(-4);    /* Change directory to new root */
7493     ckrootset = ckstrncpy(ckroot,buf,CKMAXPATH); /* Now set the new root */
7494     if (ckroot[ckrootset-1] != '/') {
7495         ckroot[ckrootset++] = '/';
7496         ckroot[ckrootset] = '\0';
7497     }
7498     debug(F111,"zsetroot rootset",ckroot,ckrootset);
7499     ckrooterr = 0;                      /* Reset error flag */
7500     return(1);
7501 }
7502
7503 char *
7504 zgetroot() {                            /* Returns the root */
7505     if (!ckrootset)
7506       return(NULL);
7507     return((char *)ckroot);
7508 }
7509
7510 int
7511 zinroot(s) char * s; {                  /* Checks if file s is in the root */
7512     int x, n;
7513     struct zfnfp * f = NULL;
7514     char buf[CKMAXPATH+2];
7515
7516     debug(F111,"zinroot setroot",ckroot,ckrootset);
7517     ckrooterr = 0;                      /* Reset global error flag */
7518     if (!ckrootset)                     /* Root not set */
7519       return(1);                        /* so it's ok - no need to check */
7520     if (!(f = zfnqfp(s,CKMAXPATH,buf))) /* Get full and real pathname */
7521       return(0);                        /* Fail if we can't  */
7522     n = f->len;                         /* Length of full pathname */
7523     debug(F111,"zinroot n",buf,n);
7524     if (n < (ckrootset - 1) || n > CKMAXPATH) { /* Bad length */
7525         ckrooterr = 1;                          /* Fail */
7526         return(0);
7527     }
7528     if (isdir(buf) && buf[n-1] != '/') {  /* If it's a directory name */
7529         buf[n++] = '/';                   /* make sure it ends with '/' */
7530         buf[n] = '\0';
7531     }
7532     x = strncmp(buf,ckroot,ckrootset);  /* Compare, case-sensitive */
7533     debug(F111,"zinroot checked",buf,x);
7534     if (x == 0)                         /* OK */
7535       return(1);
7536     ckrooterr = 1;                      /* Not OK */
7537     return(0);
7538 }
7539 #endif /* CKROOT */
7540
7541 #ifdef CK_LOGIN
7542 /*
7543   The following code provides support for user login and logout
7544   including anonymous accounts.  If this feature is to be supported
7545   outside of UNIX, it should be spread out among the ck?fio.c modules...
7546 */
7547 #ifndef _PATH_BSHELL
7548 #define _PATH_BSHELL    "/usr/bin/bash"
7549 #endif /* _PATH_BSHELL */
7550 #ifndef _PATH_FTPUSERS
7551 #define _PATH_FTPUSERS  "/etc/ftpusers"
7552 #endif /* _PATH_FTPUSERS */
7553
7554 /*
7555  * Helper function for sgetpwnam().
7556  */
7557 char *
7558 sgetsave(s) char *s; {
7559     char *new = malloc((unsigned) strlen(s) + 1);
7560     if (new == NULL) {
7561         printf("?Local resource failure: malloc\n");
7562         exit(1);
7563         /* NOTREACHED */
7564     }
7565     (void) strcpy(new, s);              /* safe */
7566     return (new);
7567 }
7568
7569 /*
7570  * Save the result of getpwnam().  Used for USER command, since
7571  * the data returned must not be clobbered by any other command
7572  * (e.g., globbing).
7573  */
7574 struct passwd *
7575 sgetpwnam(name) char *name; {
7576     static struct passwd save;
7577     register struct passwd *p;
7578 #ifdef CK_SHADOW
7579     register struct spwd *sp;
7580 #endif /* CK_SHADOW */
7581     char *sgetsave();
7582
7583 #ifdef HPUX10_TRUSTED
7584     struct pr_passwd *pr;
7585 #endif /* HPUX10_TRUSTED */
7586
7587 #ifdef CK_SHADOW
7588     sp = getspnam(name);
7589     debug(F111,"sgetpwnam","getspnam()",sp);
7590     if (sp == NULL)
7591       return (NULL);
7592 #endif /* CK_SHADOW */
7593
7594 #ifdef HPUX10_TRUSTED
7595     if ((pr = getprpwnam(name)) == NULL)
7596       return(NULL);
7597 #endif /* HPUX10_TRUSTED */
7598
7599     p = getpwnam(name);
7600     debug(F111,"sgetpwnam","getpwnam()",p);
7601     if (p == NULL)
7602       return(NULL);
7603     if (save.pw_name) {
7604         free(save.pw_name);
7605         free(save.pw_passwd);
7606         free(save.pw_gecos);
7607         free(save.pw_dir);
7608         free(save.pw_shell);
7609     }
7610     save = *p;
7611     save.pw_name = sgetsave(p->pw_name);
7612 #ifdef CK_SHADOW
7613     save.pw_passwd = sgetsave(sp->sp_pwdp);
7614 #else /* CK_SHADOW */
7615 #ifdef HPUX10_TRUSTED
7616     if (pr->uflg.fg_encrypt && pr->ufld.fd_encrypt && *pr->ufld.fd_encrypt)
7617       save.pw_passwd = sgetsave(pr->ufld.fd_encrypt);
7618     else
7619       save.pw_passwd = sgetsave("");
7620 #else /* HPUX10_TRUSTED */
7621     save.pw_passwd = sgetsave(p->pw_passwd);
7622 #endif /* HPUX10_TRUSTED */
7623 #endif /* CK_SHADOW */
7624     save.pw_gecos = sgetsave(p->pw_gecos);
7625     save.pw_dir = sgetsave(p->pw_dir);
7626     save.pw_shell = sgetsave(p->pw_shell);
7627     return(&save);
7628 }
7629
7630 #define CKXLOGBSIZ 256
7631
7632 struct passwd * pw = NULL;
7633 char * home = NULL;                     /* Home directory pointer for glob */
7634 #ifdef CMASK
7635 #undef CMASK
7636 #endif /* CMASK */
7637
7638 #define CMASK 027
7639
7640 int defumask = CMASK;                   /* Default umask value */
7641
7642 /*  Z V U S E R  --  Verify user, Returns 1 if user OK, 0 otherwise.  */
7643
7644 /* Sets global passwd pointer pw if named account exists and is acceptable;
7645  * sets askpasswd if a PASS command is expected.  If logged in previously,
7646  * need to reset state.  If name is "ftp" or "anonymous", the name is not in
7647  * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
7648  * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
7649  * requesting login privileges.  Disallow anyone who does not have a standard
7650  * shell as returned by getusershell().  Disallow anyone mentioned in the file
7651  * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
7652  */
7653 _PROTOTYP(static int checkuser, (char *) );
7654
7655 char zvuname[64] = { NUL, NUL };
7656 char zvhome[CKMAXPATH+1] = { NUL, NUL };
7657 #define ZENVUSER 70
7658 #define ZENVHOME CKMAXPATH+12
7659 #define ZENVLOGNAME 74
7660 static char zenvuser[ZENVUSER];
7661 static char zenvhome[ZENVHOME];
7662 static char zenvlogname[ZENVLOGNAME];
7663
7664 #ifdef CK_PAM
7665 static char pam_data[500];
7666 struct pam_conv pam_conv = {pam_cb, pam_data}; /* PAM structure */
7667 struct pam_handle * pamh = NULL;               /* PAM reference handle */
7668 #endif /* CK_PAM */
7669
7670 int
7671 zvuser(name) char *name; {
7672     register char *cp = NULL;
7673     int x;
7674     char *shell;
7675 #ifdef GETUSERSHELL
7676     char *getusershell();
7677 #endif /* GETUSERSHELL */
7678
7679 #ifdef CK_PAM
7680     int pam_status;
7681     const char * reply = NULL;
7682 #endif /* CK_PAM */
7683
7684     debug(F111,"user",name,logged_in);
7685
7686     if (!name) name = "";
7687     zvuname[0] = NUL;
7688
7689     debug(F101,"zvuser ckxsyslog","",ckxsyslog);
7690
7691 #ifdef CKSYSLOG
7692     debug(F100,"zvuser CKSYSLOG defined","",0);
7693 #endif /* CKSYSLOG */
7694
7695     if (logged_in)                      /* Should not be called if logged in */
7696       return(0);
7697
7698 #ifdef CKSYSLOG
7699     if (ckxsyslog && ckxlogging) {
7700         syslog(LOG_INFO,
7701                 "login: user %s",name
7702                 );
7703     }
7704 #endif /* CKSYSLOG */
7705
7706     guest = 0;                          /* Assume not guest */
7707     askpasswd = 0;
7708
7709     if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
7710         debug(F101,"zvuser anonymous ckxanon","",ckxanon);
7711         if (!ckxanon) {                 /* Anonymous login not allowed */
7712 #ifdef CKSYSLOG
7713             if (ckxsyslog && ckxlogging) {
7714                 syslog(LOG_INFO,
7715                        "login: anonymous login not allowed: %s",
7716                        clienthost ? clienthost : "(unknown host)"
7717                        );
7718             }
7719 #endif /* CKSYSLOG */
7720             return(0);
7721         }
7722         if (checkuser("ftp") || checkuser("anonymous")) {
7723             debug(F100,"zvuser anon forbidden by ftpusers file","",0);
7724 #ifdef CKSYSLOG
7725             if (ckxsyslog && ckxlogging) {
7726                 syslog(LOG_INFO,
7727                        "login: anonymous login forbidden by ftpusers file: %s",
7728                        clienthost ? clienthost : "(unknown host)"
7729                        );
7730             }
7731 #endif /* CKSYSLOG */
7732             return(0);
7733         } else if ((pw = sgetpwnam("ftp")) != NULL) {
7734             debug(F100,"zvuser anon sgetpwnam(ftp) OK","",0);
7735             guest = 1;
7736             askpasswd = 1;
7737             ckstrncpy(zvuname,"anonymous",64);
7738             return(1);
7739         } else {
7740             debug(F100,"zvuser anon sgetpwnam(ftp) FAILED","",0);
7741 #ifdef CKSYSLOG
7742             if (ckxsyslog && ckxlogging) {
7743                 syslog(LOG_INFO,
7744                        "login: anonymous getpwnam(ftp) failed: %s",
7745                        clienthost ? clienthost : "(unknown host)"
7746                        );
7747             }
7748 #endif /* CKSYSLOG */
7749             return(0);
7750         }
7751     }
7752     pw = sgetpwnam(name);
7753     if (pw) {
7754 /*
7755   Of course some UNIX platforms (like AIX) don't have getusershell().
7756   In that case we can't check if the user's account has been "turned off"
7757   or somesuch, e.g. by setting their shell to "/etc/nologin" or somesuch,
7758   which runs (usually just printing a message and exiting), but which is
7759   not listed in /etc/shells.  For that matter, if getusershell() is not
7760   available, then probably neither is /etc/shells.
7761 */
7762         debug(F100,"zvuser sgetpwnam ok","",0);
7763         shell = pw->pw_shell;
7764         if (!shell) shell = "";
7765         if (!*shell)
7766           shell = _PATH_BSHELL;
7767         debug(F110,"zvuser shell",shell,0);
7768 #ifdef GETUSERSHELL
7769         while ((cp = getusershell()) != NULL) {
7770             debug(F110,"zvuser getusershell",cp,0);
7771             if (strcmp(cp, shell) == 0)
7772               break;
7773         }
7774         debug(F100,"zvuser endusershell 1","",0);
7775         endusershell();
7776         debug(F100,"zvuser endusershell 2","",0);
7777 #else /* GETUSERSHELL */
7778         cp = "";                        /* Do not refuse if we cannot check */
7779 #endif /* GETUSERSHELL */
7780         x = checkuser(name);
7781         debug(F101,"zvuser checkuser","",x);
7782         if (cp == NULL) {
7783             debug(F100,"zvuser refused 1","",0);
7784             pw = (struct passwd *) NULL;
7785 #ifdef CKSYSLOG
7786             if (ckxsyslog && ckxlogging) {
7787                 syslog(LOG_INFO,
7788                        "login: invalid shell %s for %s %s",shell, name,
7789                        clienthost ? clienthost : "(unknown host)"
7790                        );
7791             }
7792 #endif /* CKSYSLOG */
7793             return(0);
7794         } else if (x) {
7795             debug(F100,"zvuser refused 2","",0);
7796             pw = (struct passwd *) NULL;
7797 #ifdef CKSYSLOG
7798             if (ckxsyslog && ckxlogging) {
7799                 syslog(LOG_INFO,
7800                        "login: %s login forbidden by ftpusers file: %s",
7801                        name, clienthost ? clienthost : "(unknown host)"
7802                        );
7803             }
7804 #endif /* CKSYSLOG */
7805             return(0);
7806         } else {
7807             x = 0;
7808 #ifdef CK_PAM
7809             /* Get PAM authentication details */
7810             debug(F110,"zvuser","calling pam_start",0);
7811             if ((pam_status =
7812                  pam_start(PAM_SERVICE_TYPE,name,&pam_conv,&pamh))
7813                 != PAM_SUCCESS) {
7814                 reply = pam_strerror(NULL, pam_status);
7815                 debug(F110,"zvuser PAM failure",reply,0);
7816                 printf("%s\n",reply);
7817 #ifdef CKSYSLOG
7818                 if (ckxsyslog && ckxlogging) {
7819                     syslog(LOG_INFO,
7820                            "login: %s refused by PAM \"%s\": %s",
7821                            name,reply,
7822                            clienthost ? clienthost : "(unknown host)"
7823                            );
7824                 }
7825 #endif /* CKSYSLOG */
7826                 return(0);
7827             }
7828 #endif /* CK_PAM */
7829             askpasswd = 1;
7830             ckstrncpy(zvuname,name,64);
7831             return(1);
7832         }
7833     } else {
7834         x = 0;
7835         debug(F100,"zvuser sgetpwnam NULL","",0);
7836 #ifdef CKSYSLOG
7837         if (ckxsyslog && ckxlogging) {
7838             syslog(LOG_INFO,
7839                    "login: getpwnam(%s) failed: %s",name,
7840                    clienthost ? clienthost : "(unknown host)"
7841                    );
7842         }
7843 #endif /* CKSYSLOG */
7844         return(0);
7845     }
7846
7847 #ifdef FTP_KERBEROS
7848     if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) {
7849 #ifdef COMMENT
7850         /* Why sprintf and then printf? */
7851         /* Also, what is kerb_ok?  And is the test on it right? */
7852         char buf[CKXLOGBSIZ];
7853         sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s%s",
7854                  kdata.pname, *kdata.pinst ? "." : "",
7855                  kdata.pinst, kdata.prealm,
7856                  (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not",
7857                  name, kerb_ok ? "" : "; Password required.");
7858         printf("%s", buf);
7859 #else
7860         printf("Kerberos user %s%s%s@%s is%s authorized as %s%s",
7861                  kdata.pname, *kdata.pinst ? "." : "",
7862                  kdata.pinst, kdata.prealm,
7863                  (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not",
7864                  name, kerb_ok ? "" : "; Password required.");
7865 #endif /* COMMENT */
7866         if (kerb_ok) return(1);
7867     } else
7868       return(0);
7869 #endif /* FTP_KERBEROS */
7870 }
7871
7872 /* Check if the given user is in the forbidden-user file */
7873
7874 static int
7875 checkuser(name) char *name; {
7876     extern char * userfile;
7877     FILE *fd;
7878     int i;
7879     char line[CKXLOGBSIZ+1];
7880
7881     if (!name)
7882       name = "";
7883     i = strlen(name);
7884     debug(F111,"checkuser name",name,i);
7885     if (!*name)
7886       return(1);
7887
7888     fd = fopen(userfile ? userfile : _PATH_FTPUSERS, "r");
7889     debug(F111,"checkuser userfile",userfile,fd);
7890     if (fd) {
7891         line[0] = '\0';
7892         while (fgets(line, sizeof(line), fd)) {
7893             debug(F110,"checkuser line",line,0);
7894             if (line[0] <= '#')
7895               continue;
7896             if (strncmp(line, name, i) == 0) {
7897                 debug(F110,"checkuser REFUSED",name,0);
7898                 return(1);
7899             }
7900             line[0] = '\0';
7901         }
7902         (VOID) fclose(fd);
7903     }
7904     debug(F110,"checkuser OK",name,0);
7905     return(0);
7906 }
7907
7908 /*  Z V L O G O U T  --  Log out from Internet Kermit Service  */
7909
7910 VOID
7911 zvlogout() {
7912 #ifdef COMMENT
7913     /* This could be dangerous */
7914     if (setuid((UID_T)0) < 0) {
7915         debug(F100,"zvlogout setuid FAILED","",0);
7916         goto bad;
7917     }
7918     debug(F100,"zvlogout setuid OK","",0);
7919 #endif /* COMMENT */
7920 #ifdef CKSYSLOG
7921     if (ckxsyslog >= SYSLG_LI && ckxlogging) {
7922         cksyslog(SYSLG_LI, 1, "logout",(char *) uidbuf, clienthost);
7923     }
7924 #endif /* CKSYSLOG */
7925 #ifdef CKWTMP
7926     debug(F110,"WTMP logout",cksysline,logged_in);
7927     if (logged_in)
7928       logwtmp(cksysline, "", "");
7929 #endif /* CKWTMP */
7930     pw = NULL;
7931     logged_in = 0;
7932     guest = 0;
7933     isguest = 0;
7934 }
7935
7936 #ifdef FTP_KERBEROS
7937 kpass(name, p) char *name, *p; {
7938     char instance[INST_SZ];
7939     char realm[REALM_SZ];
7940     char tkt_file[20];
7941     KTEXT_ST ticket;
7942     AUTH_DAT authdata;
7943     unsigned long faddr;
7944     struct hostent *hp;
7945
7946     if (krb_get_lrealm(realm, 1) != KSUCCESS)
7947       return(0);
7948
7949     ckstrncpy(tkt_file, TKT_ROOT, 20);
7950     ckstrncat(tkt_file, "_ftpdXXXXXX", 20);
7951     krb_set_tkt_string(mktemp(tkt_file));
7952
7953     (VOID) ckstrncpy(instance, krb_get_phost(hostname), sizeof(instance));
7954
7955     if ((hp = gethostbyname(instance)) == NULL)
7956       return(0);
7957
7958 #ifdef HADDRLIST
7959     hp = ck_copyhostent(hp);            /* safe copy that won't change */
7960 #endif /* HADDRLIST */
7961     bcopy((char *)hp->h_addr, (char *) &faddr, sizeof(faddr));
7962
7963     if (krb_get_pw_in_tkt(name, "", realm, "krbtgt", realm, 1, p) ||
7964         krb_mk_req(&ticket, "rcmd", instance, realm, 33) ||
7965         krb_rd_req(&ticket, "rcmd", instance, faddr, &authdata, "") ||
7966         kuserok(&authdata, name)) {
7967         dest_tkt();
7968         return(0);
7969     }
7970     dest_tkt();
7971     return(1);
7972 }
7973 #endif /* FTP_KERBEROS */
7974
7975 VOID
7976 zsyslog() {
7977 #ifdef CKSYSLOG
7978     if (ckxsyslog && !ckxlogging) {
7979 #ifdef LOG_DAEMON
7980         openlog(inserver ? "iksd" : "ckermit", LOG_PID, LOG_DAEMON);
7981 #else
7982         openlog(inserver ? "iksd" : "ckermit", LOG_PID);
7983 #endif /* LOG_DAEMON */
7984         ckxlogging = 1;
7985         debug(F100,"zsyslog syslog opened","",0);
7986     }
7987 #endif /* CKSYSLOG */
7988 }
7989
7990 /*  Z V P A S S  --  Verify password; returns 1 if OK, 0 otherwise  */
7991
7992 #ifndef AUTH_USER
7993 #define AUTH_USER 3
7994 #endif /* AUTH_USER */
7995 #ifndef AUTH_VALID
7996 #define AUTH_VALID 4
7997 #endif /* AUTH_VALID */
7998
7999 int
8000 zvpass(p) char *p; {
8001     char *xpasswd, *salt;
8002     char * dir = NULL;
8003 #ifdef CK_PAM
8004     int pam_status;
8005     const char * reply = NULL;
8006 #endif /* CK_PAM */
8007
8008     if (logged_in || askpasswd == 0) {
8009         return(0);
8010     }
8011     debug(F111,"zvpass",p ? (guest ? p : "xxxxxx") : "(null)",guest);
8012     if (!p) p = "";
8013     askpasswd = 0;
8014     if (guest && !*p) {                 /* Guests must specify a password */
8015 #ifdef CKSYSLOG
8016         if (ckxsyslog && ckxlogging) {
8017             syslog(LOG_INFO,
8018                    "login: anonymous guests must specify a password"
8019                    );
8020         }
8021 #endif /* CKSYSLOG */
8022         return(0);
8023     }
8024     if (!guest
8025 #ifdef CK_AUTHENTICATION
8026         && ck_tn_auth_valid() != AUTH_VALID
8027 #endif /* CK_AUTHENTICATION */
8028         ) {                     /* "ftp" is only account allowed no password */
8029 #ifdef CK_PAM
8030         debug(F110,"zvpass","calling pam_set_item(AUTHTOK)",0);
8031         if ((pam_status = pam_set_item(pamh,PAM_AUTHTOK,p)) != PAM_SUCCESS) {
8032             reply = pam_strerror(pamh, pam_status);
8033             debug(F110,"zvpass PAM failure",reply,0);
8034             /* if no password given treat as non-fatal error */
8035             /* pam will prompt for password in pam_authenticate() */
8036             if (!p) {
8037                 printf("%s\n",reply);
8038                 pam_end(pamh, 0);
8039                 debug(F100,"zvpass denied","",0);
8040                 pw = NULL;
8041                 zvuname[0] = NUL;
8042                 return(0);
8043             }
8044         }
8045         debug(F110,"zvpass","calling pam_authenticate",0);
8046         if (*p)
8047           pam_pw = p;
8048         if ((pam_status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
8049             reply = pam_strerror(pamh, pam_status);
8050             debug(F110,"zvpass PAM failure",reply,0);
8051             printf("%s\n",reply);
8052             pam_end(pamh, 0);
8053             debug(F100,"zvpass denied","",0);
8054             pam_pw = NULL;
8055             pw = NULL;
8056             zvuname[0] = NUL;
8057             return(0);
8058         }
8059         pam_pw = NULL;
8060         debug(F110,"zvpass","calling pam_acct_mgmt",0);
8061         if ((pam_status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
8062             reply = pam_strerror(pamh, pam_status);
8063             debug(F110,"zvpass PAM failure",reply,0);
8064             printf("%s\n",reply);
8065             pam_end(pamh, 0);
8066             debug(F100,"zvpass denied","",0);
8067             pw = NULL;
8068             zvuname[0] = NUL;
8069             return(0);
8070         }
8071         debug(F110,"zvpass","PAM validates OK",0);
8072         pam_end(pamh,0);
8073 #else /* CK_PAM */
8074         if (pw == NULL)
8075           salt = "xx";
8076         else
8077           salt = pw->pw_passwd;
8078
8079 #ifdef HPUX10_TRUSTED
8080         xpasswd = bigcrypt(p, salt);
8081 #else
8082 /*
8083   On 64-bit platforms this can give "cast to pointer from integer of
8084   different size" warning, but I'm not sure what the effect is at runtime,
8085   or what to do about it.
8086  */
8087         xpasswd = (char *)crypt(p, salt);
8088 #endif /* HPUX10_TRUSTED */
8089
8090         if (
8091 #ifdef FTP_KERBEROS
8092             /* null pw_passwd ok if Kerberos password ok */
8093             pw == NULL ||
8094             ((*pw->pw_passwd != '\0' ||
8095               strcmp(xpasswd, pw->pw_passwd))
8096              && !kpass(pw->pw_name, p))
8097 #else
8098 #ifdef CK_SRP
8099             /* check with tpasswd first if there */
8100             pw == NULL || *pw->pw_passwd == '\0' ||
8101             t_verifypw (pw->pw_name, p) == 0 ||
8102             (t_verifypw (pw->pw_name, p) < 0 &&
8103             strcmp (xpasswd, pw->pw_passwd))
8104 #else /* CK_SRP */
8105             /* The strcmp does not catch null passwords! */
8106             (pw == NULL) || (*pw->pw_passwd == '\0') ||
8107             strcmp(xpasswd, pw->pw_passwd)
8108 #endif /* CK_SRP */
8109 #endif /* FTP_KERBEROS */
8110             ) {
8111             debug(F100,"zvpass denied","",0);
8112             pw = NULL;
8113             zvuname[0] = NUL;
8114             return(0);
8115         }
8116 #endif /* CK_PAM */
8117     }
8118
8119     (VOID) setgid((GID_T)pw->pw_gid);   /* Set group ID */
8120
8121 #ifndef NOINITGROUPS
8122     (VOID) initgroups(pw->pw_name, pw->pw_gid);
8123 #endif /* NOINITGROUPS */
8124
8125     logged_in = 1;
8126     dir = pw->pw_dir;
8127
8128 #ifdef CKWTMP
8129     /* Open wtmp before chroot */
8130     if (ckxwtmp) {
8131         sprintf(cksysline,"iks_%04x", getpid()); /* safe */
8132         logwtmp(cksysline, pw->pw_name,
8133                  clienthost ? clienthost : "(unknown host)"
8134                 );
8135         debug(F110,"WTMP login",cksysline,logged_in);
8136     }
8137 #endif /* CKWTMP */
8138 /*
8139   For anonymous users, we chroot to user ftp's home directory unless
8140   started with --anonroot:xxx, in which case we chroot to xxx.  We must
8141   immediately chdir() to the same directory we chroot() to or else the
8142   old current directory remains accessible as "." outside the new root.
8143 */
8144     if (guest) {
8145         if (anonroot)                   /* Non-default anonymous root */
8146           dir = anonroot;
8147         else
8148           makestr(&anonroot,dir);
8149         errno = 0;
8150         debug(F110,"zvpass anon chroot",dir,0);
8151         if (chroot(dir) < 0) {
8152             debug(F111,"zvpass anon chroot FAILED",dir,errno);
8153             goto bad;
8154         }
8155         errno = 0;
8156         if (chdir("/") < 0) {
8157             debug(F111,"zvpass anon chdir FAILED",dir,errno);
8158             goto bad;
8159         }
8160         debug(F110,"zvpass anon chroot/chdir OK",dir,0);
8161     } else if (chdir(dir) < 0) {        /* Not guest */
8162 #ifdef COMMENT
8163         if (chdir("/") < 0) {
8164             debug(F110,"Non-guest chdir FAILED",dir,0);
8165             goto bad;
8166         } else
8167           printf("?No directory! Logging in with home=/\n");
8168 #else
8169         debug(F110,"zvpass non-guest chdir FAILED",dir,0);
8170         goto bad;                       /* Be conservative at first */
8171 #endif /* COMMENT */
8172     }
8173     debug(F110,"zvpass non-guest chdir OK",dir,0);
8174     if (setuid((UID_T)pw->pw_uid) < 0) {
8175         debug(F101,"zvpass setuid FAILED","",pw->pw_uid);
8176         goto bad;
8177     }
8178     debug(F101,"zvpass setuid OK","",pw->pw_uid);
8179
8180     guestpass[0] = '\0';
8181     if (guest) {
8182         extern int fncact;
8183         isguest = 1;
8184         fncact = XYFX_R;                /* FILE COLLISION = RENAME */
8185         debug(F110,"GUEST fncact=R",p,0);
8186         lset(guestpass,"anonymous:",10,32);
8187         ckstrncpy(&guestpass[10],p,GUESTPASS-10);
8188         home = "/";
8189         printf("Anonymous login.\r\n");
8190
8191 #ifdef SETPROCTITLE
8192         /* proctitle declared where?  Obviously this code is never compiled. */
8193         sprintf(proctitle, "%s: anonymous/%.*s",
8194                 clienthost ? clienthost : "(unk)",
8195                 sizeof(proctitle) - sizeof(clienthost) -
8196                 sizeof(": anonymous/"), p);
8197         setproctitle(proctitle);
8198 #endif /* SETPROCTITLE */
8199
8200 #ifdef CKSYSLOG
8201         if (ckxsyslog && ckxlogging) {
8202             syslog(LOG_INFO,
8203                    "login: anonymous %s %s",
8204                    clienthost ? clienthost : "(unknown host)",
8205                    p
8206                    );
8207         }
8208 #endif /* CKSYSLOG */
8209
8210     } else {                            /* Real user */
8211         isguest = 0;
8212         home = dir;
8213         ckstrncpy(guestpass,zvuname,GUESTPASS);
8214
8215         printf("User %s logged in.\r\n", pw->pw_name);
8216 #ifdef SETPROCTITLE
8217         /* not used */
8218         sprintf(proctitle, "%s: %s",
8219                 clienthost ? clienthost : "(unk)",
8220                 pw->pw_name
8221                 );
8222         setproctitle(proctitle);
8223 #endif /* SETPROCTITLE */
8224
8225 #ifdef CKSYSLOG
8226         if (ckxsyslog && ckxlogging)
8227           syslog(LOG_INFO, "login: %s %s",
8228                  pw->pw_name,
8229                  clienthost ? clienthost : "(unknown host)"
8230                  );
8231 #endif /* CKSYSLOG */
8232     }
8233     ckstrncpy(zvhome,home,CKMAXPATH);   /* Set environment variables */
8234 #ifndef NOPUTENV
8235
8236     ckmakmsg(zenvuser,ZENVUSER,"USER=",zvuname,NULL,NULL);
8237     putenv((char *)zenvuser);
8238     ckmakmsg(zenvlogname,ZENVLOGNAME,"LOGNAME=",zvuname,NULL,NULL);
8239     putenv((char *)zenvlogname);
8240     ckmakmsg(zenvhome,ZENVHOME,"HOME=",zvhome,NULL,NULL);
8241     putenv((char *)zenvhome);
8242 #endif /* NOPUTENV */
8243     /* homdir = (char *)zvhome; */
8244     ckstrncpy((char *)uidbuf,(char *)zvuname,64);
8245     (VOID) umask(defumask);
8246 #ifdef IKSDB
8247     if (ikdbopen) {
8248         char * p2;
8249         int k;
8250         extern char dbrec[];
8251         extern unsigned long myflags;
8252         extern unsigned int mydbslot;
8253         extern struct iksdbfld dbfld[];
8254 #ifdef CK_AUTHENTICATION
8255         extern unsigned long myamode, myatype;
8256 #endif /* CK_AUTHENTICATION */
8257         myflags |= DBF_LOGGED;
8258 #ifdef DEBUG
8259         if (deblog) {
8260             debug(F101,"zvpass guest","",guest);
8261             debug(F111,"zvpass zvuname",zvuname,0);
8262             debug(F110,"zvpass guestpass",guestpass,0);
8263             debug(F110,"zvpass dir",dir,0);
8264             debug(F110,"zvpass home",home,0);
8265             debug(F110,"zvpass anonroot",anonroot,0);
8266         }
8267 #endif /* DEBUG */
8268         p2 = guest ? guestpass : zvuname;
8269         if (guest) {
8270             p2 = (char *)guestpass;
8271             myflags &= ~DBF_USER;
8272         } else {
8273             p2 = (char *)zvuname;
8274             myflags |= DBF_USER;
8275         }
8276         k = strlen(p2);
8277         strncpy(&dbrec[DB_ULEN],ulongtohex((unsigned long)k,4),4);
8278         lset(&dbrec[dbfld[db_USER].off],p2,1024,32);
8279         strncpy(&dbrec[DB_FLAGS],ulongtohex(myflags,4),4);
8280 #ifdef CK_AUTHENTICATION
8281         myamode = ck_tn_auth_valid();
8282         strncpy(&dbrec[DB_AMODE],ulongtohex(myamode,4),4);
8283         myatype = ck_tn_authenticated();
8284         strncpy(&dbrec[DB_ATYPE],ulongtohex(myatype,4),4);
8285 #endif /* CK_AUTHENTICATION */
8286         if (guest) {
8287             p2 = dir;
8288         } else {
8289             p2 = zgtdir();
8290             if (!p2) p2 = "";
8291             if (!*p2) p2 = home;
8292         }
8293         strncpy(&dbrec[DB_DLEN],
8294                 ulongtohex((unsigned long)strlen(p2),4),
8295                 4
8296                 );
8297         lset(&dbrec[dbfld[db_DIR].off],p2,1024,32);
8298         updslot(mydbslot);
8299     }
8300 #endif /* IKSDB */
8301     return(1);
8302
8303 bad:                                    /* Common failure exit */
8304     zvuname[0] = NUL;
8305     zvlogout();
8306     return(0);
8307 }
8308 #endif /* CK_LOGIN */
8309
8310 /* Buggy Xenix 2.3.4 cc needs this line after the endif */