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