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