home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / c-kermit / ckufio.c < prev    next >
C/C++ Source or Header  |  2020-01-01  |  252KB  |  8,546 lines

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