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