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