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