source: trunk/third/kermit/ckufio.c @ 20081

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