home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / c-kermit / ck9fio.c < prev    next >
C/C++ Source or Header  |  2020-01-01  |  67KB  |  2,164 lines

  1. char *ckzv = "OS-9 file support, 7.0.018, 1 Jan 2000";
  2. char *ckzsys = " OS-9/68000";
  3.  
  4. /* c k 9 F I O  --  Kermit file system support for OS-9/68k systems */
  5.  
  6. /*
  7.  Author: Peter Scholz,
  8.  Ruhr University Bochum, Department for Analytical Chemistry,
  9.  Federal Republic of Germany,   February 1987
  10.  
  11.  04/30/87 Robert Larson         Cleanup, merge with standard C-kermit
  12.  04/07/89 Robert Larson         Update for ckermit 4f(77)
  13.  07/16/89 Robert Larson         4f(85)
  14.  Edition: 5A(01)
  15.  06/21/91 Chris  Hemsing        general adaption to 5A(171)
  16.                                 miscellaneous bug fixes, utilization of
  17.                                 initial file size.
  18.  07/25/91 Chris  Hemsing        minor bug fixes, changes for gnu (ansi) C
  19.  01/14/92 Chris  Hemsing        uct/localtime  bug fix
  20.  05/26/92 Chris  Hemsing        zltor and zrtol bug fix
  21.  Edition: 5A(05)
  22.  06/30/92 Chris  Hemsing        pipeopen stderr bug fix,dir shows filesize
  23.  Edition: 5A(06)
  24.  07/09/92 Chris  Hemsing        removed all chaining which would save a
  25.                                 process but f$chain does not look in PATH
  26.  Edition: 5A(07)
  27.  07/10/92 Chris  Hemsing        remove whole process tree of a lower fork
  28.  Edition: 5A(08)
  29.  12/01/92 Chris  Hemsing        zgtdir bugs repaired: upmost dir not closed
  30.                                 closedir of non-open dir could be called
  31.  Edition: 5A(09)
  32.  04/10/94 ?                     adapted password tag of struct zattr
  33.  Edition: 5A(10)
  34.  01/05/95 Ulli Schlueter        changed rename(), added isdir(), zmkdir() and
  35.                                 zfseek()
  36.  Edition: 5A(11)
  37.  01/13/95 Ulli Schlueter        changed zrtol() to enable directories in as-
  38.                                 names, corrected MAXNAMLEN, adapted znewn() to
  39.                                 the fact that it is called with full path
  40.                                 names, fixed bug in tilde_expand() (buffer to
  41.                                 small)
  42.  Edition: 5A(12)
  43.  03/13/95 Ulli Schlueter        some little bug fixies and cleanups
  44.  Edition: 5A(13)
  45.  03/20/95 Ulli Schlueter        kill_tree() now takes care of parent pid
  46.  Edition: 5A(14)
  47.  04/19/95 Ulli Schlueter        Rewrote zgtdir(), made some large buffers
  48.                                 dynamic.
  49.  Edition: 6.1.015
  50.  4 Dec 97 fdc                   Updated chkfn() to full capacity.
  51.  15 Apr 99 Martin Whitaker    Added fix for name changes in dir.h.
  52.  25 Apr 99 Martin Whitaker    Rewrote zsyscmd() and zshcmd().
  53.  01 May 99 Martin Whitaker    Added nzxpand() and zxrewind(). Modified
  54.                 fgen() and traverse() to support nzxpand().
  55.  Edition: 7.0.016
  56.  31 May 99 Martin Whitaker    Fixed zchin to work when (n == ZIFILE).
  57.  Edition: 7.0.017
  58.  Edition: 7.0.018
  59.  January 2000 Steve Rance, Frank da Cruz: adapt to Ultra C.
  60.  
  61.   Adapted from UNIX C-Kermit.
  62.   Author: Frank da Cruz <fdc@columbia.edu>.
  63.  
  64.   Copyright (C) 1985, 2000,
  65.     Trustees of Columbia University in the City of New York.
  66.     All rights reserved.  See the C-Kermit COPYING.TXT file or the
  67.     copyright text in the ckcmai.c module for disclaimer and permissions.
  68. */
  69. /* In OS-9 V2.3, dir.h uses the field name _addr in the structure direct,
  70.    whereas this file expects the field name d_addr. On the assumption that
  71.    this was a change made in a later version of OS-9, I have fixed this up
  72.    by the following macro definition, rather than changing the name in
  73.    this file - MTW 15-Apr-99 */
  74. #define    _addr    d_addr
  75.  
  76. /* Includes */
  77.  
  78. #include "ckcsym.h"
  79. #include "ckcdeb.h"             /* Typedefs, debug formats, etc */
  80. #include "ckcker.h"             /* Kermit definitions */
  81. #include "ckcasc.h"
  82. #include <errno.h>
  83.  
  84. #ifndef EOS_FULL
  85. #ifdef E_FULL
  86. #define EOS_FULL E_FULL
  87. #endif /* E_FULL */
  88. #endif /* EOS_FULL */
  89.  
  90. #include <dir.h>                /* Directory structure */
  91. #include <direct.h>
  92. #include <modes.h>
  93. #include <procid.h>
  94. #include <signal.h>
  95. #include <strings.h>
  96. #include <time.h>
  97.  
  98. /* special case since fdopen is not an ANSI function (see stdio.h) */
  99. extern   FILE *fdopen(int, char *);
  100.  
  101. /* and this is not ANSI bust used here */
  102. #define fileno(p)       ((p)->_fd)
  103.  
  104. #define ISDIRSEP(c) ((c)=='/')
  105.  
  106. #define    ISHIDDEN(name) \
  107. ((name[0] == '.') && (name[1] != '\0') && (name[1] != '.'))
  108.  
  109. /* Definitions of some system commands */
  110.  
  111. char *DIRCMD = "dir -e ";    /* For directory listing with filename*/
  112. char *DIRCM2 = "dir -e ";    /* For directory listing without filename*/
  113. char *DELCMD = "del ";        /* For file deletion */
  114. char *TYPCMD = "list ";        /* For typing a file */
  115. char *PWDCMD = "pd ";        /* For saying where I am */
  116.  
  117. char *SPACMD = "free ";
  118. char *SPACM2 = "free ";         /* For space in specified directory */
  119.  
  120. char *WHOCMD = "procs -e ";    /* We have no who yet */
  121.  
  122. /*
  123.   Functions (n is one of the predefined file numbers from ckermi.h):
  124.  
  125.    zopeni(n,name)   -- Opens an existing file for input.
  126.    zopeno(n,name,attr,fcb) -- Opens a new file for output.
  127.    zclose(n)        -- Closes a file.
  128.    zchin(n,&c)      -- Gets the next character from an input file.
  129.    zsinl(n,&s,x)    -- Read a line from file n, max len x, into address s.
  130.    zsout(n,s)       -- Write a null-terminated string to output file,buffered.
  131.    zsoutl(n,s)      -- Like zsout, but appends a line terminator.
  132.    zsoutx(n,s,x)    -- Write x characters to output file, unbuffered.
  133.    zchout(n,c)      -- Add a character to an output file, unbuffered.
  134.    zchki(name)      -- Check if named file exists and is readable,return size.
  135.    zchko(name)      -- Check if named file can be created.
  136.    zchkspa(name,n)  -- Check if n bytes available to create new file, name.
  137.    znewn(name,s)    -- Make a new unique file name based on the given name.
  138.    zdelet(name)     -- Delete the named file.
  139.    nzxpand(string,flags) -- Expands wildcard string into a list of files.
  140.    zxrewind()       -- Rewind nzxpand list.
  141.    znext(string)    -- Returns the next file from the list in "string".
  142.    zxcmd(cmd)       -- Execute the command in a lower fork.
  143.    zclosf()         -- Close input file associated with zxcmd()'s lower fork.
  144.    zrtol(n1,n2)     -- Convert remote filename into local form.
  145.    zltor(n1,n2)     -- Convert local filename into remote form.
  146.    zchdir(dirnam)   -- Change working directory.
  147.    zhome()          -- Return pointer to home directory name string.
  148.    zkself()         -- Kill self, log out own job.
  149.    zsattr(struc zattr *) -- Return attributes for file which is being sent.
  150.    zstime(f, struct zattr *, x) - Set file creation date from attribute packet.
  151.    zrename(old, new) -- Rename a file.
  152.    zmkdir(path)     -- Create the directory path if possible  
  153.  */
  154.  
  155. /* Some systems define these in include files, others don't... */
  156.  
  157. #define MAXWLD 500
  158.  
  159. /* MAXNAMLEN in <dir.h> is incorrect */
  160. #ifdef MAXNAMLEN
  161. #undef MAXNAMLEN
  162. #endif /* MAXNAMLEN */
  163. #define MAXNAMLEN 28
  164.  
  165. /* Declarations */
  166. #define MAXPATH 256                     /* not really an OS-9 restriction */
  167.  
  168. FILE *fp[ZNFILS] = {    /* File pointers */
  169.     NULL, NULL, NULL, NULL, NULL, NULL, NULL };
  170.  
  171. /* (PWP) external def. of things used in buffered file input and output */
  172. #ifdef DYNAMIC
  173. extern CHAR *zinbuffer, *zoutbuffer;
  174. #else
  175. extern CHAR zinbuffer[], zoutbuffer[];
  176. #endif /* DYNAMIC */
  177. extern CHAR *zinptr, *zoutptr;
  178. extern int zincnt, zoutcnt;
  179.  
  180. struct fildes cur_in_fd;                /* current input filedesc */
  181. static int cur_in_size = -1;            /* current input file length */
  182.  
  183. static int pipe_pid;                    /* pid of child fork */
  184. static int nxpand = 0;            /* Number of files in wild group */
  185. static int fcount;                      /* Files remaining for znext() */
  186. char *getenv(), *strcpy();              /* System functions */
  187. /* VOID *malloc(); */
  188. extern int errno;                       /* System error code */
  189.  
  190. char **mtchs,                           /* Matches found for filename */
  191.      **mtchptr;                         /* Pointer to current match */
  192.  
  193. /*  Z K S E L F  --  Kill Self: log out own job, if possible.  */
  194.  
  195. zkself() {                              /* For "bye" */
  196. procid pbuf;
  197.   _get_process_desc(getpid(),sizeof(pbuf),&pbuf);
  198.   if (kill((int)pbuf._pid,SIGKILL)==0)
  199.     doexit(GOOD_EXIT,-1);/*kill parent process*/
  200.   else
  201.     doexit(errno,-1);
  202. }
  203.  
  204. /*  Z G T D I R  --  Get current working directory. */
  205.  
  206. static char *
  207. getcwd (pwdbuf, size)
  208.      char *pwdbuf;
  209.      unsigned size;
  210. {
  211.     DIR *dirp;
  212.     long cur, tmp, here;
  213. #ifdef CWDNU
  214.     void *mem = NULL;
  215. #endif /* CWDNU */
  216.     char *cp;
  217.     char *cd;
  218.     struct direct *ent;
  219.     char buffer[128];
  220. #define MAXDEPTH 64
  221.     char dots[MAXDEPTH+1];
  222.  
  223. #ifdef CWDNU
  224.     if (size < MAXNAMLEN + 2) { /* '/' and '\0' */
  225.         errno = E_ILLARG;
  226.         return NULL;
  227.     }
  228.     if (pwdbuf == NULL) {
  229.         if ((pwdbuf = malloc(size)) == NULL)
  230.           return NULL;
  231.         mem = pwdbuf;
  232.     }
  233. #endif /* CWDNU */
  234.     cp = pwdbuf + size;
  235.     *--cp = '\0';
  236.     cd = dots + sizeof dots;
  237.     *--cd = '\0';
  238.     here = 0;
  239.     do {
  240.         if (cd <= dots) {       /* out of space */
  241.             errno = EOS_FULL;
  242. #ifdef CWDNU
  243.             if (mem) free(mem);
  244. #endif /* CWDNU */
  245.             return NULL;
  246.         }
  247.         *--cd = '.';
  248.         if ((dirp = opendir(cd)) == NULL) {
  249. #ifdef CWDNU
  250.             if (mem) free(mem);
  251. #endif /* CWDNU */
  252.             return NULL;
  253.         }
  254.         /* read '..' and '.' entries */
  255.         if ((ent = readdir(dirp)) == NULL
  256.             || (tmp = ent->d_addr, (ent = readdir(dirp)) == NULL)) {
  257.             closedir(dirp);
  258. #ifdef CWDNU
  259.             if (mem) free(mem);
  260. #endif /* CWDNU */
  261.             return NULL;
  262.         }
  263.         cur = ent->d_addr;
  264.         if (cur == tmp) cur = 0;
  265.         if (here == 0) {
  266.             here = cur;
  267.             continue;           /* Break if in the root */
  268.         }
  269.         while ((ent = readdir(dirp)) != NULL)
  270.         {
  271.             if (ent->d_addr == here)
  272.             {
  273.                 strcpy(buffer, ent->d_name);
  274.                 cp -= (tmp = strlen(buffer));
  275.                 if (cp <= pwdbuf) { /* out of space */
  276.                     errno = EOS_FULL;
  277.                     break;
  278.                 }
  279.                 memcpy(cp, buffer, tmp);
  280.                 *--cp = '/';
  281.                 here = cur;
  282.                 break;
  283.             }
  284.         }
  285.         if (here != cur) {      /* read error, entry not found or no space */
  286.             closedir(dirp);
  287. #ifdef CWDNU
  288.             if (mem) free(mem);
  289. #endif /* CWDNU */
  290.             return NULL;
  291.         }
  292.     } while (cur != 0 && (closedir(dirp), 1));
  293.     if (_gs_devn(dirp->dd_fd, buffer) < 0) { /* get device name */
  294.         closedir(dirp);
  295. #ifdef CWDNU
  296.         if (mem) free(mem);
  297. #endif /* CWDNU */
  298.         return NULL;
  299.     }
  300.     closedir(dirp);
  301.     cp -= (tmp = strlen(buffer));
  302.     if (cp <= pwdbuf) {
  303.         errno = EOS_FULL;
  304. #ifdef CWDNU
  305.         if (mem) free(mem);
  306. #endif /* CWDNU */
  307.         return NULL;
  308.     }
  309.     memcpy(cp, buffer, tmp);
  310.     *--cp = '/';
  311.     memcpy(pwdbuf, cp, pwdbuf + size - cp);
  312.     return pwdbuf;
  313. }
  314.  
  315. char *zgtdir() {
  316.     static char *cwd;
  317. #define CWDBL (MAXPATH + 1)
  318.  
  319.     if (cwd == NULL && (cwd = malloc(CWDBL)) == NULL) return "";
  320.     cwd[0] = '\0';
  321.     getcwd(cwd, CWDBL);
  322.     return cwd;
  323. }
  324.  
  325.  
  326. /*  Z O P E N I  --  Open an existing file for input. */
  327.  
  328. zopeni(n,name) int n; char *name; {
  329.     debug(F111," zopeni",name,n);
  330.     debug(F101,"  fp","",(int) fp[n]);
  331.     if (chkfn(n) != 0) return(0);
  332.     zincnt = 0;                         /* Reset input buffer */
  333.     if (n == ZSYSFN) {                  /* Input from a system function? */
  334. /*** Note, this function should not be called with ZSYSFN ***/
  335. /*** Always call zxcmd() directly, and give it the real file number ***/
  336. /*** you want to use.  ***/
  337.         debug(F110,"zopeni called with ZSYSFN, failing!",name,0);
  338.                 return(0);                      /* fail. */
  339.     }
  340.     if (n == ZSTDIO) {   /* Standard input? */
  341.         if (isatty(0)) {
  342.             ermsg("Terminal input not allowed");
  343.             debug(F110,"zopeni: attempts input from unredirected stdin","",0);
  344.             return(0);
  345.         }
  346.         fp[ZIFILE] = stdin;
  347.         return(1);
  348.     }
  349.     fp[n] = fopen(name,"r");            /* Real file. */
  350.     debug(F111," zopeni", name, (int) fp[n]);
  351.     if (fp[n] == NULL) perror("zopeni");
  352.     return((fp[n] != NULL) ? 1 : 0);
  353. }
  354.  
  355. /*  Z O P E N O  --  Open a new file for output.  */
  356.  
  357. zopeno(n,name,zz,fcb)
  358. /* zopeno */  int n; char *name; struct zattr *zz; struct filinfo *fcb; {
  359.  
  360. /* As of Version 5A, the attribute structure and the file information */
  361. /* structure are included in the arglist. */
  362.  
  363.     char *p;
  364.     int pn;
  365.     debug(F111,"zopeno",name,n);
  366.     if (chkfn(n) != 0) return(0);
  367.     zoutcnt = 0;
  368.     zoutptr = zoutbuffer;
  369.     if (fcb)
  370.     {
  371.           debug(F101,"zopeno fcb disp","",fcb->dsp);
  372.           debug(F101,"zopeno fcb type","",fcb->typ);
  373.           debug(F101,"zopeno fcb char","",fcb->cs);
  374.     }
  375.     else debug(F100,"zopeno fcb is NULL","",0);
  376.     if ((n == ZCTERM) || (n == ZSTDIO))
  377.     {   /* Terminal or standard output */
  378.           fp[ZOFILE] = stdout;
  379.           debug(F101," fp[]=stdout", "", (int) fp[n]);
  380.           return(1);
  381.     }
  382.  
  383. /* A real file.  Open it in desired mode (create or append). */
  384.     p = "w";                            /* Assume write/create mode */
  385.     if (fcb) {                          /* If called with an FCB... */
  386.         if (fcb->dsp == XYFZ_A)         /* Does it say Append? */
  387.           p = "a";                      /* Yes. */
  388.     }
  389.     if (access(name,0) == 0) /* does file exist ? */
  390.     {
  391.       if ((fp[n] = fopen(name,p)) == NULL)      /* Simply open the file */
  392.       {
  393.         perror("zopeno can't open");
  394.         return(0);
  395.       }
  396.       debug(F111, " exist:fp[n]",p, (int) fp[n]);
  397.     }
  398.     else /* file does not exist, we can open with initial size */
  399.          /* this is very important for long files not to be too fragmented */
  400.          /* so that after a long transmission OS-9 may finish saying that */
  401.          /* the segment allocation table is full */
  402.     {
  403.       if ((zz) && ((zz->length > 0) || (zz->lengthk > 0)))
  404.                                     /* length estimate available ? */
  405.       {
  406.         if ((pn = create(name,S_IREAD+S_IWRITE+S_ISIZE,
  407.              S_IREAD+S_IWRITE,
  408.              (zz->length > 0) ? zz->length : (zz->lengthk*1024L))) != -1)
  409.         {
  410.           debug(F111, " create with init. size:",p,
  411.                 (zz->length > 0) ? zz->length : (zz->lengthk*1024L));
  412.           if ((fp[n] = fdopen(pn,"w")) == NULL) /* get filepntr for pn */
  413.           {
  414.             perror("zopeno can't open");
  415.             return(0);
  416.           }
  417.           debug(F111, " create with init. size:fp[n]",p, (int) fp[n]);
  418.         }
  419.         else
  420.         {
  421.           perror("zopeno can't open");
  422.           return(0);
  423.         }
  424.       }
  425.       else /* no length available */
  426.       {
  427.         if ((fp[n] = fopen(name,p)) == NULL)    /* Simply open the file */
  428.         {
  429.           perror("zopeno can't open");
  430.           return(0);
  431.         }
  432.         debug(F111, " create without init size fp[n]",p, (int) fp[n]);
  433.       }
  434.     }
  435.     if((zz) && (zz->systemid.val[0] == 'U') && ( zz->systemid.val[1] == 'D'))
  436.     {
  437.       debug(F101, "zopeno:file originated from OS-9","",zz->lprotect.val[0]);
  438.       if (zz->lprotect.len != 0)
  439.       {
  440.         if (_ss_attr(fp[n]->_fd,(zz->lprotect.val[0]&0x7f)|S_IWRITE) == -1)
  441.         {
  442.           debug(F101, "zopeno:can't set file attr","",errno);
  443.           return(0);
  444.         }
  445.       }
  446.     }
  447.     if (n == ZDFILE) setbuf(fp[n],(char *)NULL); /* Debug file unbuffered */
  448.     return(1);
  449. }
  450.  
  451. /*  Z C L O S E  --  Close the given file.  */
  452. /*  Returns 0 if arg out of range, 1 if successful, -1 if close failed.  */
  453.  
  454. zclose(n) int n; {
  455.     int x, x2;
  456.     if (chkfn(n) < 1) return(0);        /* Check range of n */
  457.  
  458.     if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */
  459.       x2 = zoutdump();
  460.     else
  461.       x2 = 0;
  462.  
  463.     x = 0;                              /* Initialize return code */
  464.     if (fp[ZSYSFN]) {                   /* If file is really pipe */
  465.         x = zclosf(n);                  /* do it specially */
  466.     } else {
  467.         if ((fp[n] != stdout) && (fp[n] != stdin)) x = fclose(fp[n]);
  468.             fp[n] = NULL;
  469.     }
  470.     if (x == EOF)                       /* if we got a close error */
  471.                 return (-1);
  472.     else if (x2 < 0)            /* or an error flushing the last buffer */
  473.                 return (-1);            /* then return an error */
  474.     else
  475.                 return (1);
  476. }
  477.  
  478. /*  Z C H I N  --  Get a character from the input file.  */
  479.  
  480. /*  Returns -1 if EOF, 0 otherwise with character returned in argument  */
  481.  
  482. zchin(n,c) int n; int *c; {
  483.     int a;
  484.  
  485.     /* (PWP) Just in case this gets called when it shoudn't */
  486.     if (n == ZIFILE)
  487.       a = zminchar();
  488.     else
  489.       a = getc(fp[n]);
  490.     if ((a == -1) || (a == EOF)) return(-1);
  491.     *c = a & 0377;
  492.     return(0);
  493. }
  494.  
  495. /*  Z I N F I L L  --  Get a character from the input file.
  496.  * (PWP) (re)fill the buffered input buffer with data.  All file input
  497.  * should go through this routine, usually by calling the zminchar()
  498.  * macro (in ckcker.h).
  499.  */
  500.  
  501. zinfill() {
  502.     zincnt = fread((char *)zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]);
  503.     if (zincnt == 0) return (-1);       /* end of file */
  504.     zinptr = zinbuffer;    /* set pointer to beginning, (== &zinbuffer[0]) */
  505.     zincnt--;                           /* one less char in buffer */
  506.     return((int)(*zinptr++) & 0377);    /* because we return the first */
  507. }
  508.  
  509. /*  Z S O U T  --  Write a string to the given file, buffered.  */
  510.  
  511. zsout(n,s) int n; char *s; {
  512.     if (chkfn(n) < 1) return(-1);
  513.     fputs(s,fp[n]);
  514.     return(0);
  515. }
  516.  
  517. /*  Z S O U T L  --  Write string to file, with line terminator, buffered  */
  518.  
  519. zsoutl(n,s) int n; char *s; {
  520.     /* if (chkfn(n) < 1) return(-1); */
  521.     fputs(s,fp[n]);
  522.     fputs("\n",fp[n]);
  523.     return(0);
  524. }
  525.  
  526. /*  Z S O U T X  --  Write x characters to file, unbuffered.  */
  527.  
  528. zsoutx(n,s,x) int n, x; char *s; {
  529.     /* if (chkfn(n) < 1) return(-1); */
  530.     return(write(fileno(fp[n]),s,(unsigned int)x));
  531. }
  532.  
  533.  
  534. /*  Z C H O U T  --  Add a character to the given file.  */
  535.  
  536. /*  Should return 0 or greater on success, -1 on failure (e.g. disk full)  */
  537.  
  538. int
  539. #ifdef CK_ANSIC
  540. zchout(register int n, char c)
  541. #else
  542. zchout(n,c) register int n; char c;
  543. #endif /* CK_ANSIC */
  544. /* zchout */ {
  545.     /* if (chkfn(n) < 1) return(-1); */
  546.     if (n == ZSFILE)
  547.         return(write(fileno(fp[n]),&c,1));/*Use unbuffered for session log*/
  548.     else {    /* Buffered for everything else */
  549.         if (putc(c,fp[n]) == EOF) /* If true, maybe there was an error */
  550.         {
  551.             return(ferror(fp[n])); /* Check to make sure */
  552.         }
  553.         else return(0);   /* There was no error. */
  554.     }
  555. }
  556.  
  557. /* (PWP) buffered character output routine to speed up file IO */
  558. zoutdump()
  559. {
  560.     if (zoutcnt == 0) return (0); /* nothing to output */
  561.     if (zoutcnt < 0) return (-1); /* unexpected */
  562.  
  563.     zoutptr = zoutbuffer;      /* Reset buffer pointer in all cases */
  564.     if (fwrite ((char *)zoutbuffer, 1, zoutcnt, fp[ZOFILE]))
  565.     {
  566.         fflush(fp[ZOFILE]);
  567.         zoutcnt = 0;            /* reset output buffer */
  568.         zoutptr = zoutbuffer;
  569.         return(0);              /* things worked OK */
  570.     }
  571.     zoutcnt = 0;            /* reset output buffer */
  572.     return(ferror(fp[ZOFILE])?-1:0); /* Check to make sure */
  573. }
  574.  
  575. /*  C H K F N  --  Internal function to verify file number is ok  */
  576.  
  577. /*
  578.  Returns:
  579.   -1: File number n is out of range
  580.    0: n is in range, but file is not open
  581.    1: n in range and file is open
  582. */
  583. chkfn(n) int n; {
  584.     if (n < 0 || n >= ZNFILS) {
  585.     if (n != ZDFILE) debug(F101,"chkfn out of range","",n);
  586.     return(-1);
  587.     } else {
  588.     /* if (n != ZDFILE) debug(F101,"chkfn fp[n]","",fp[n]); */
  589.     return((fp[n] == NULL) ? 0 : 1);
  590.     }
  591. }
  592.  
  593. /*  Z C H K I  --  Check if input file exists and is readable  */
  594.  
  595. /*
  596.   Returns:
  597.    >= 0 if the file can be read (returns the size).
  598.      -1 if file doesn't exist or can't be accessed,
  599.      -2 if file exists but is not readable (e.g. a directory file).
  600.      -3 if file exists but protected against read access.
  601. */
  602. long
  603. zchki(name) char *name; {
  604.     int x;
  605.  
  606.     if (strcmp(name, "/nil") == 0) {
  607.         memset(&cur_in_fd, 0, sizeof cur_in_fd);
  608.         cur_in_fd.fd_att = S_IREAD|S_IWRITE;
  609.         getime((struct sgtbuf *)cur_in_fd.fd_date);
  610.         cur_in_size = -1;
  611.         return 0;
  612.     }
  613.     if (access(name,0) < 0) {
  614.         if(access(name,S_IFDIR)>=0) {
  615.             debug(F111,"zchki skipping:",name,errno);
  616.             return(-2);
  617.         }
  618.         debug(F111,"zchki can't access",name,errno);
  619.         return(-1);
  620.     }
  621.  
  622.     if ((x = open(name,S_IREAD)) < 0) {  /* Is the file accessible? */
  623.         debug(F111," access failed:",name,x); /* No */
  624.         return(-3);
  625.     }
  626.     debug(F110," access ok:",name,0);
  627.     cur_in_size = _gs_size(x);                  /* remember size */
  628.     _gs_gfd(x,&cur_in_fd,sizeof(cur_in_fd));    /* remember filedespr. */
  629.     close(x);
  630.     debug(F111," access ok:",name,cur_in_size);
  631.     return( (cur_in_size > -1) ? cur_in_size : 0 );
  632. }
  633.  
  634. /*  Z C H K O  --  Check if output file can be created  */
  635.  
  636. /*
  637.  Returns -1 if write permission for the file would be denied, 0 otherwise.
  638. */
  639. zchko(name) char *name; {
  640.     int i, x;
  641.     char *s;
  642.  
  643.     x = strlen(name);
  644.     debug(F101," length","",x);
  645.     if (x == 0) return(-1);             /* If no filename, fail. */
  646.     if (strcmp(name, "/nil") == 0) return 0;
  647.     if(isdir(name)) return -1;          /* it's a directory */
  648.     for (s = name, i = x; i > 0; i--) { /* Strip filename from right. */
  649.         if (ISDIRSEP(s[i-1])) break;
  650.     }
  651.     debug(F101," i","",i);
  652.     if (_prsnam(s + i) != strlen(s + i)) {
  653.         debug(F111," _prsnam failed:",s,errno);
  654.         return -1;
  655.     }
  656. /* We don't want to write in our arg, so make a copy */
  657.     if ((s = malloc(x + 1 + 1)) == NULL) return -1;
  658.     strcpy(s, name);
  659. /* Now we use "path/." if path given, or "." if no path given. */
  660.     s[i++] = '.';                       /* Append "." to path. */
  661.     s[i] = '\0';
  662.     x = access(s,S_IFDIR|S_IWRITE);     /* Check access of path. */
  663.     if (x < 0) {
  664.         debug(F111,"zchko access failed:",s,errno);
  665.     } else {
  666.         debug(F111,"zchko access ok:",s,x);
  667.     }
  668.     free(s);
  669.     return x < 0 ? -1 : 0;
  670. }
  671.  
  672. /*  Z C H K S P A  --  Check if there is enough space to store the file  */
  673.  
  674. /*
  675.  Call with file specification f, size n in bytes.
  676.  Returns -1 on error, 0 if not enough space, 1 if enough space.
  677. */
  678. zchkspa(f,n) char *f; long n; {         /* Just dummy for now. */
  679.     return(1);                          /* Always say OK. */
  680. }
  681.  
  682.  
  683. /*  Z D E L E T  --  Delete the named file.  */
  684.  
  685. zdelet(name) char *name; {
  686.     return(unlink(name));
  687. }
  688.  
  689.  
  690. /*  Z R T O L  --  Convert remote filename into local form  */
  691.  
  692. /*  For OS9, this means changing uppercase letters to lowercase
  693.     and making shure only a-z,A-Z,0-9,$,_,. are used
  694.     keep the / for dir name (as-names)
  695. */
  696. VOID
  697. zrtol(name,name2) char *name, *name2; {
  698.     char *p; int flag = 0, n = 0;
  699.     int maxnam = MAXNAMLEN;
  700.     if (!name || !name2) return;
  701.     debug(F101,"zrtol original name","",name);
  702.     for (p = name2; *name != '\0' && n < maxnam; name++) {
  703.         if (*name > ' ') flag = 1;      /* Strip leading blanks and controls */
  704.         if (flag == 0 && *name < '!') continue;
  705.         *p = isupper(*name) ? tolower(*name) : *name;
  706.         n++;
  707.         if (ISDIRSEP(*p++)) n = 0;
  708.     }
  709.     *p-- = '\0';                        /* Terminate */
  710.     while (*p < '!' && p > name2)       /* Strip trailing blanks & controls */
  711.       *p-- = '\0';
  712.     if (*name2 == '\0') strcpy(name2,"noname");
  713.     for (p = name2; *p != '\0'; p++) {
  714.         if (!isalnum(*p) && *p != '$' && *p != '.' && *p != '_'
  715.           && !ISDIRSEP(*p))
  716.           *p = '_';
  717.     }
  718.     debug(F110,"zrtol new name",name2,0);
  719. }
  720.  
  721.  
  722. /*  Z S T R I P  --  Strip device & directory name from file specification */
  723.  
  724. /*  Strip pathname from filename "name", return pointer to result in name2 */
  725.  
  726. static char work[MAXPATH+1]; /* buffer for use by zstrip and zltor */
  727.  
  728. VOID
  729. zstrip(name,name2) char *name, **name2; {
  730.     char *cp, *pp;
  731.     debug(F110,"zstrip before",name,0);
  732.     pp = work;
  733. #ifdef DTILDE
  734.     if (*name == '~') name++;
  735. #endif
  736.     for (cp = name; *cp != '\0'; cp++) {
  737.         if (*cp == '/')
  738.           pp = work;
  739.         else
  740.           *pp++ = *cp;
  741.     }
  742.     *pp = '\0';                         /* Terminate the string */
  743.     *name2 = work;
  744.     debug(F110,"zstrip after",*name2,0);
  745. }
  746.  
  747.  
  748. /*  Z L T O R  --  Local TO Remote */
  749.  
  750. /*  Convert filename from local format to common (remote) form.  */
  751. VOID
  752. zltor(name,name2) char *name, *name2; {
  753.     char *cp, *pp;
  754.     int dc = 0;
  755.  
  756.     debug(F110,"zltor",name,0);
  757.     pp = work;
  758.     for (cp = name; *cp != '\0'; cp++) { /* strip path name */
  759.         if (*cp == '/') {
  760.             dc = 0;
  761.             pp = work;
  762.         }
  763.         else if (islower(*cp)) *pp++ = toupper(*cp); /* Uppercase letters */
  764.         else if (*cp == '$') *pp++ = 'X';    /* Change '$' to 'X' */
  765.         else if ((*cp == '.') && (++dc > 1)) *pp++ = 'X'; /* & extra dots */
  766.         else *pp++ = *cp;
  767.     }
  768.     *pp = '\0';    /* Tie it off. */
  769.     cp = name2;    /* If nothing before dot, */
  770.     if (*work == '.') *cp++ = 'X'; /* insert 'X' */
  771.     strcpy(cp,work);
  772.     debug(F110," name2",name2,0);
  773. }
  774.  
  775.  
  776. /*  Z C H D I R  --  Change directory  */
  777.  
  778. zchdir(dirnam) char *dirnam; {
  779.     char *hd;
  780.     if (dirnam == NULL || *dirnam == '\0') hd = zhome();
  781.     else hd = dirnam;
  782. #ifdef DTILDE
  783.     hd = tilde_expand(dirnam);          /* Attempt to expand tilde */
  784.     if (*hd == '\0') hd = dirnam;       /* in directory name. */
  785. #endif /* DTILDE */
  786.     return((chdir(hd) == 0) ? 1 : 0);
  787. }
  788.  
  789.  
  790. /*  Z H O M E  --  Return pointer to user's home directory  */
  791.  
  792. char *
  793. zhome() {
  794.     char *home = getenv("HOME");
  795.     return home ? home : ".";
  796. }
  797.  
  798. /*  Z X C M D -- Run a system command so its output can be read like a file */
  799.  
  800. zxcmd(filnum,comand) int filnum;char *comand; {
  801. FILE *pipeopen(),*filedesc;
  802.  
  803.     if (chkfn(filnum) < 0) return(-1); /* Need a valid kermit file number */
  804.     if (filnum == ZSTDIO || filnum == ZCTERM) return(0);
  805.  
  806.     if (filnum == ZIFILE || filnum == ZRFILE) /* kermit wants to read */
  807.     {
  808.       debug(F111," zxcmd read:",comand,filnum);
  809.       if ((filedesc = pipeopen(comand,"r")) != (FILE *)NULL)
  810.       {
  811.         fp[filnum] = filedesc;
  812.         fp[ZSYSFN] = fp[filnum];    /* Remember. */
  813.         zincnt = 0;
  814.         zinptr = zinbuffer;
  815.       }
  816.       else
  817.         return(0);
  818.     }
  819.     else
  820.     {
  821.       debug(F111," zxcmd write:",comand,filnum);
  822.       if ((filedesc = pipeopen(comand,"w")) != (FILE *)NULL)
  823.       {
  824.         fp[filnum] = filedesc;
  825.         fp[ZSYSFN] = fp[filnum];    /* Remember. */
  826.         zoutcnt = 0;
  827.         zoutptr = zoutbuffer;
  828.       }
  829.       else
  830.         return(0);
  831.     }
  832.     return(1); /* return success if we ever get here */
  833. }
  834.  
  835. /* KILL_TREE recursively kill all processes of a forked tree */
  836. static void
  837. #ifdef CK_ANSIC
  838. kill_tree(int root, int ppid)
  839. #else
  840. kill_tree(root, ppid) int root; int ppid;
  841. #endif /* CK_ANSIC */
  842. {
  843.   struct {
  844.     unsigned short
  845.       _id,      /* process id */
  846.       _pid,     /* parent's id */
  847.       _sid,     /* sibling's id */
  848.       _cid;     /* child's id */
  849.   } prdesc;     /* NOT whole process descriptor! too much stack needed */
  850.   int child;
  851.  
  852.   if(_get_process_desc(root, sizeof(prdesc), &prdesc) == -1) return;
  853.   if (prdesc._pid != ppid) return;
  854.   /* kill child's tree */
  855.   if ((child = prdesc._cid) != 0) kill_tree(child, root);
  856.   /* kill siblings tree */
  857.   if ((child = prdesc._sid) != 0) kill_tree(child, ppid);
  858.   kill(root,SIGKILL);
  859. }
  860.  
  861. /* Z C L O S F - kill the child(action may have aborted and close the pipe.*/
  862.  
  863. zclosf(filnum) int filnum;{
  864.     int wstat;
  865.     debug(F101," zclosf filnum:","",filnum);
  866.     fclose(fp[filnum]);
  867.     fp[filnum] = fp[ZSYSFN] = NULL;
  868.     if (pipe_pid == 0) return(-1);
  869.     /* kill all lower processes in case things have been aborted */
  870.     kill_tree(pipe_pid, getpid());
  871.     debug(F101," zclosf killed:","",pipe_pid);
  872.     while ((wstat = wait((unsigned int *)0)) != pipe_pid && wstat != -1) ;
  873.     pipe_pid = 0;
  874.     return(1);
  875. }
  876.  
  877. /*  N Z X P A N D  --  Expand a file list, with options.  */
  878. /*
  879.   Call with:
  880.    fnarg = pointer to filename or pattern.
  881.    flags = option bits:
  882.  
  883.      flags & ZX_FILONLY   Match regular files
  884.      flags & ZX_DIRONLY   Match directories
  885.      flags & ZX_RECURSE   Descend through directory tree
  886.      flags & ZX_MATCHDOT  Match "dot files"
  887.      flags & ZX_NOBACKUP  Don't match "backup files"
  888.  
  889.    Returns the number of files that match s, with data structures set up
  890.    so that first file (if any) will be returned by the next znext() call.
  891. */
  892. static int xdironly = 0;
  893. static int xfilonly = 0;
  894. static int xmatchdot = 0;
  895. static int xrecursive = 0;
  896.  
  897. int
  898. nzxpand(fnarg, flags) char *fnarg; int flags; {
  899.     char fnbuf[CKMAXPATH+8];
  900.     char *fn;
  901.     char *p;
  902. #ifdef DTILDE                /* Built with tilde-expansion? */
  903.     char *tnam;
  904. #endif /* DTILDE */
  905.     int x;
  906.  
  907.     debug(F111,"nzxpand",fnarg,flags);
  908.     xdironly   = (flags & ZX_DIRONLY);
  909.     xfilonly   = (flags & ZX_FILONLY);
  910.     xmatchdot  = (flags & ZX_MATCHDOT);
  911.     xrecursive = (flags & ZX_RECURSE);
  912.  
  913.     debug(F101,"nzxpand xdironly","",xdironly);
  914.     debug(F101,"nzxpand xfilonly","",xfilonly);
  915.     debug(F101,"nzxpand xmatchdot","",xmatchdot);
  916.     debug(F101,"nzxpand xrecursive","",xrecursive);
  917.  
  918.     if (!fnarg)                /* If no argument provided */
  919.       return(0);            /* Return zero files found */
  920.  
  921. #ifdef DTILDE                /* Built with tilde-expansion? */
  922.     if (*fnarg == '~') {        /* Starts with tilde? */
  923.     tnam = tilde_expand(fnarg);    /* Try to expand it */
  924.     strncpy(fnbuf,tnam,CKMAXPATH);
  925.     } else
  926. #endif /* DTILDE */
  927.         strncpy(fnbuf,fnarg,CKMAXPATH);
  928.   
  929.     fn = fnbuf;                /* Point to what we'll work with */
  930.  
  931.     debug(F110,"nzxpand fn 1",fn,0);
  932.  
  933.     if (!*fn)                /* But make sure something is there */
  934.       return(0);
  935.  
  936.     p = fn + (int)strlen(fn) - 1;
  937.     if (*p == '/') {            /* If last char = / it must be a dir */
  938.     strcat(fn, "*");        /* so append '*' */
  939.     } else if (p > fn) {        /* If ends in "/." */
  940.     if (*(p-1) == '/' && *p == '.')    /* change '.' to '*' */
  941.       *p = '*';
  942.     } else if (p == fn) {        /* If it's '.' alone */
  943.     if (*p == '.')            /* change '.' to '*' */
  944.       *p = '*';
  945.     }
  946.     debug(F110,"nzxpand fn 2",fn,0);
  947.     x = isdir(fn);            /* Is it a directory? */
  948.     debug(F111,"nzxpand isdir 1",fn,x);
  949.     if (x) {                /* If so, make it into a wildcard */
  950.     if ((x = strlen(fn)) > 0) {
  951.         if (!ISDIRSEP(fn[x-1]))
  952.           fn[x++] = '/';
  953.         fn[x++] = '*';
  954.         fn[x] = '\0';
  955.     }
  956.     }
  957.     debug(F110,"nzxpand fn 3",fn,0);
  958.  
  959.     if (mtchs == NULL) mtchs = (char **)malloc(MAXWLD * sizeof(*mtchs));
  960.  
  961.     /* Look up the file */
  962.     nxpand = (mtchs == NULL) ? 0 : fgen(fn,mtchs,MAXWLD);
  963.  
  964.     mtchptr = mtchs;            /* Save pointer for next */
  965.     fcount = nxpand;
  966.  
  967.     debug(F101,"nzxpand fcount","",fcount);
  968.  
  969.     return(fcount);
  970. }
  971.  
  972. /*  Z X R E W I N D  --  Rewinds the nzxpand() list */
  973.  
  974. int
  975. zxrewind() {
  976.     if (!mtchs) return(-1);
  977.     fcount = nxpand;
  978.     mtchptr = mtchs;
  979.     return(fcount);
  980. }
  981.  
  982. /*  Z N E X T  --  Get name of next file from list created by nzxpand(). */
  983. /*
  984.  Returns >0 if there's another file,with its name copied into the arg string
  985.  or 0 if no more files in list.
  986. */
  987. znext(fn) char *fn; {
  988.     if (fcount > 0) strcpy(fn,*mtchptr++); else *fn = '\0';
  989.     debug(F111,"znext",fn,fcount);
  990.     return(fcount--);
  991. }
  992.  
  993. /*  Z N E W N  --  Make a new name for the given file  */
  994. VOID
  995. znewn(fn,s) char *fn, **s; {
  996. #define ZNEWNBL MAXPATH                 /* Name buffer length */
  997. #define ZNEWNMD 4                       /* Max digits for version number */
  998. /* OS-9 names are short, so take a short version number extension */
  999. #define ZNEWNVC 1                       /* Number of additional vers chars */
  1000.     static char buf[ZNEWNBL+1];
  1001.     char *bp, *xp;
  1002.     int len = 0, n = 0, d = 0, t, i, power = 1;
  1003.  
  1004.     int max;
  1005.  
  1006.     bp = buf;
  1007.     xp = bp;
  1008.     while (*fn) {                       /* Copy old name into buf */
  1009.         *bp++ = *fn++;
  1010.         if (ISDIRSEP(bp[-1])) xp = bp;  /* Remember latest filename */
  1011.         if (len++ > ZNEWNBL) break;     /* ...up to buffer length */
  1012.     }
  1013.     max = (xp - buf) + MAXNAMLEN;
  1014.     if (max > ZNEWNBL) max = ZNEWNBL;
  1015.  
  1016.     *s = NULL;
  1017.     for (i = 1; i < ZNEWNMD; i++) {     /* Version numbers up to 10**i - 1 */
  1018.         int j, k;
  1019.         power *= 10;                    /* Next power of 10 */
  1020.         j = max - len;                  /* Space left for version number */
  1021.         k = ZNEWNVC + i;                /* Space needed for it */
  1022.         if (j < k) {                    /* Make room if necessary */
  1023.             len -= k - j;               /* Adjust length of filename */
  1024.             bp = buf + len;             /* Point to new end */
  1025.         }
  1026.         *bp++ = '*';                    /* Put a star on the end */
  1027.         *bp-- = '\0';
  1028.  
  1029.         debug(F110, "znewn: about to expand", buf, 0);
  1030.         n = nzxpand(buf, 0);             /* Expand the resulting wild name */
  1031.                                         /* n is the number of matches */
  1032.         while (n-- > 0) {               /* Find any existing name_n files */
  1033.             xp = *mtchptr++;            /* Point at matching name */
  1034.             xp += len;                  /* Lock for _<n> at the end of it */
  1035.             if (*xp == '_') {           /* Has it a version number */
  1036.                 t = atoi(xp+1);         /* Get it */
  1037.                 if (t > d) d = t;       /* Get maximum d */
  1038.             }
  1039.         }
  1040.         if (d < power-1) {              /* Less than maximum possible? */
  1041.             sprintf(bp,"_%d",d+1);      /* Yes, make "name_<d+1>" */
  1042.             *s = buf;
  1043.             break;
  1044.         }
  1045.     }
  1046.     if (*s == NULL) {
  1047.         debug(F110, "znewn: too many names", buf, 0);
  1048.         sprintf(bp, "_xxxx");           /* Too many, use xxxx. */
  1049.         *s = buf;
  1050.     }
  1051. }
  1052.  
  1053. /*  Z S A T T R */
  1054. /*
  1055.  Fills in a Kermit file attribute structure for the file which is to be sent.
  1056.  Returns 0 on success with the structure filled in, or -1 on failure.
  1057.  If any string member is null, then it should be ignored.
  1058.  If any numeric member is -1, then it should be ignored.
  1059. */
  1060. zsattr(xx) struct zattr *xx; {
  1061.     int k;
  1062.     static char protection[2];
  1063.     char *get_gmtime();
  1064.  
  1065.     k = cur_in_size % 1024L;                  /* File length in K */
  1066.     if (k != 0L) k = 1L;
  1067.     xx->lengthk = (cur_in_size / 1024L) + k;
  1068. /*  if ((cur_in_fd.fd_att & S_IEXEC) || (cur_in_fd.fd_att & S_IOEXEC))
  1069.     {
  1070.       xx->type.len = 1;
  1071.       xx->type.val = "B";
  1072.     }
  1073.     else
  1074.     {
  1075.       xx->type.len = 0;
  1076.       xx->type.val = "";
  1077.     }
  1078. */  xx->type.len = 0;               /* better let the user decide */
  1079.     xx->type.val = "";
  1080.     xx->date.val = get_gmtime(&cur_in_fd); /* get UCT time, if possible */
  1081.     xx->date.len = strlen(xx->date.val);
  1082.     debug(F111,"zsattr date",xx->date.val,xx->date.len);
  1083.     xx->creator.len = 0;                /* File creator */
  1084.     xx->creator.val = "";
  1085.     xx->account.len = 0;                /* File account */
  1086.     xx->account.val = "";
  1087.     xx->area.len = 0;                   /* File area */
  1088.     xx->area.val = "";
  1089.     xx->password.len = 0;                 /* Area password */
  1090.     xx->password.val = "";
  1091.     xx->blksize = -1L;                  /* File blocksize */
  1092.     xx->xaccess.len = 0;        /* File access */
  1093.     xx->xaccess.val = "";
  1094.     xx->encoding.len = 0;               /* Transfer syntax */
  1095.     xx->encoding.val = 0;
  1096.     xx->disp.len = 0;                   /* Disposition upon arrival */
  1097.     xx->disp.val = "";
  1098.  
  1099.     protection[0] = cur_in_fd.fd_att & 0x7f;
  1100.     protection[1] = 0;
  1101.     xx->lprotect.val = protection;
  1102.     xx->lprotect.len = 1;
  1103.     debug(F101,"zsattr lprotect","",xx->lprotect.val[0]);
  1104.  
  1105.     xx->gprotect.len = 0;               /* Generic protection */
  1106.     xx->gprotect.val = "";
  1107.     xx->systemid.len = 2;               /* System ID */
  1108.     xx->systemid.val = "UD";
  1109.     xx->recfm.len = 0;                  /* Record format */
  1110.     xx->recfm.val = "";
  1111.     xx->sysparam.len = 0;               /* System-dependent parameters */
  1112.     xx->sysparam.val = "";
  1113.     debug(F101,"zsattr size","",cur_in_size);
  1114.     xx->length = cur_in_size;                 /* Length */
  1115.     return(0);
  1116. }
  1117.  
  1118. zmail(p,f) char *p; char *f; {
  1119.     /* Send file f as mail to address p; no mail on OS-9 */
  1120.     return(-1);
  1121. }
  1122.  
  1123. zprint(p,f) char *p; char *f; {
  1124.     /* Print file f with options p; no unique print device on OS-9 */
  1125.     return(-1);
  1126. }
  1127.  
  1128. /* Directory Functions for Unix, written by Jeff Damens, CUCCA, 1984. */
  1129. /*
  1130.  * The path structure is used to represent the name to match.
  1131.  * Each slash-separated segment of the name is kept in one
  1132.  * such structure, and they are linked together, to make
  1133.  * traversing the name easier.
  1134.  */
  1135.  
  1136. struct path {
  1137.               char npart[MAXNAMLEN+4]; /* name part of path segment */
  1138.               struct path *fwd;  /* forward ptr */
  1139.             };
  1140.  
  1141. #define SSPACE 2000   /* size of string-generating buffer */
  1142. static char *sspace;                 /* buffer to generate names in */
  1143. static char *freeptr,**resptr;       /* copies of caller's arguments */
  1144. static int remlen;                   /* remaining length in caller's array*/
  1145. static int numfnd;                   /* number of matches found */
  1146.  
  1147. /*
  1148.  * splitpath:
  1149.  *  takes a string and splits the slash-separated portions into
  1150.  *  a list of path structures.  Returns the head of the list.  The
  1151.  *  structures are allocated by malloc, so they must be freed.
  1152.  *  Splitpath is used internally by the filename generator.
  1153.  *
  1154.  * Input: A string.
  1155.  * Returns: A linked list of the slash-separated segments of the input.
  1156.  */
  1157.  
  1158. struct path *
  1159. splitpath(p)
  1160. char *p;
  1161. {
  1162.     struct path *head,*cur,*prv;
  1163.     int i;
  1164.     head = prv = NULL;
  1165.     if (*p == '/') p++;            /* skip leading slash */
  1166.     while (*p != '\0') {
  1167.         cur = (struct path *) malloc(sizeof (struct path));
  1168.         debug(F101,"splitpath malloc","",(int)cur);
  1169.         if (cur == NULL) fatal("malloc fails in splitpath()");
  1170.         cur->fwd = NULL;
  1171.         if (head == NULL) head = cur;
  1172.         else prv->fwd = cur;       /* link into chain */
  1173.         prv = cur;
  1174.         for (i=0; i < MAXNAMLEN && *p != '/' && *p != '\0'; i++)
  1175.             cur->npart[i] = *p++;
  1176.         cur->npart[i] = '\0';      /* end this segment */
  1177.         if (i >= MAXNAMLEN) while (*p != '/' && *p != '\0') p++;
  1178.         if (*p == '/') p++;
  1179.     }
  1180.     return(head);
  1181. }
  1182.  
  1183. /*
  1184.  * fgen:
  1185.  *  This is the actual name generator.  It is passed a string,
  1186.  *  possibly containing wildcards, and an array of character pointers.
  1187.  *  It finds all the matching filenames and stores them into the array.
  1188.  *  The returned strings are allocated from a static buffer local to
  1189.  *  this module (so the caller doesn't have to worry about deallocating
  1190.  *  them); this means that successive calls to fgen will wipe out
  1191.  *  the results of previous calls.  This isn't a problem here
  1192.  *  because we process one wildcard string at a time.
  1193.  *
  1194.  * Input: a wildcard string, an array to write names to, the
  1195.  *        length of the array.
  1196.  * Returns: the number of matches.  The array is filled with filenames
  1197.  *          that matched the pattern.  If there wasn't enough room in the
  1198.  *     array, -1 is returned.
  1199.  * By: Jeff Damens, CUCCA, 1984.
  1200.  */
  1201.  
  1202. fgen(pat,resarry,len)
  1203. char *pat,*resarry[];
  1204. int len;
  1205. {
  1206.     VOID traverse();
  1207.     struct path *head;
  1208.     char scratch[100+MAXNAMLEN],*sptr;
  1209.     head = splitpath(pat);
  1210.     sptr = scratch;
  1211.     if (*pat != '/') {
  1212.         *sptr++ = '.';
  1213.     }
  1214.     *sptr = '\0';
  1215.     numfnd = 0;                            /* none found yet */
  1216.     if (sspace == NULL && (sspace = malloc(SSPACE)) == NULL) {
  1217.         fprintf(stderr,"fgen can't malloc string space\n");
  1218.         return(-1);
  1219.     }
  1220.     freeptr = sspace;   /* this is where matches are copied */
  1221.     resptr = resarry;   /* static copies of these so*/
  1222.     remlen = len;    /* recursive calls can alter them */
  1223.     traverse(head,scratch,sptr);  /* go walk the directory tree */
  1224.     while (head != NULL) {
  1225.         struct path *next = head->fwd;
  1226.         free((char *)head);
  1227.         head = next;
  1228.     }
  1229.     return(numfnd);   /* and return the number of matches */
  1230. }
  1231.  
  1232. /* traverse:
  1233.  *  Walks the directory tree looking for matches to its arguments.
  1234.  *  The algorithm is, briefly:
  1235.  *   If the current pattern segment contains wildcards, we open the name
  1236.  *   we've accumulated so far (assuming it is really a directory), then read
  1237.  *   each filename in it, and, if it matches the wildcard pattern segment,add
  1238.  *   that filename to what we have so far and call ourselves recursively on
  1239.  *   the next segment.
  1240.  *
  1241.  *   If the current pattern segment contains no wildcards, that
  1242.  *   segment is added to what we already have.  If the name so far
  1243.  *   exists, we call ourselves recursively with the next segment
  1244.  *   in the pattern string; otherwise, we just return.
  1245.  *
  1246.  *   Finally, when no more pattern segments remain, we add what's accumulated
  1247.  *   so far to the result array and increment the number of matches.
  1248.  *
  1249.  * Input: a pattern path list (as generated by splitpath), a string
  1250.  *   pointer that points to what we've traversed so far (this
  1251.  *   can be initialized to "" to start the search at the root
  1252.  *   directory, or to "." to start the search at the current
  1253.  *   directory), and a string pointer to the end of the string
  1254.  *   in the previous argument.
  1255.  * Returns: nothing.
  1256.  */
  1257. VOID
  1258. traverse(pl,sofar,endcur)
  1259. struct path *pl;
  1260. register char *sofar,*endcur;
  1261. {
  1262.     DIR *fd, *opendir();
  1263.     VOID recurse(), addresult();
  1264.     struct direct *dirbuf;
  1265.     unsigned char attr;
  1266.     char *name;
  1267.  
  1268.     debug(F110,"traverse ",sofar,0);
  1269.     if (iswild(pl->npart)) {    /* segment contains wildcards */
  1270.         fd = opendir(sofar);
  1271.         if (fd == NULL) {
  1272.             debug(F111,"traverse can't open directory",sofar,errno);
  1273.             return;        /* can't open, forget it */
  1274.         }
  1275.         dirbuf = readdir(fd);    /* skip '..' entry */
  1276.         dirbuf = readdir(fd);    /* and '.' entry */
  1277.         *endcur++ = '/';
  1278.         name = endcur;
  1279.         while (dirbuf = readdir(fd)) {
  1280.             if (dirbuf->d_addr != 0) {
  1281.                 /* Get a null terminated copy!!! */
  1282.                 strncpy(name,dirbuf->d_name,MAXNAMLEN);
  1283.                 name[MAXNAMLEN] = '\0';
  1284.                 if (match(pl->npart,name)) {
  1285.                     endcur = name + strlen(name);
  1286.                     if ((_gs_gfdinf(fd->dd_fd,
  1287.                     dirbuf->d_addr,
  1288.                     &attr,
  1289.                     sizeof(attr)) != -1) && (attr & S_IFDIR)) {
  1290.                         /* it's a dir */
  1291.                         if (pl->fwd == NULL) {
  1292.                             if (!xfilonly) addresult(sofar);
  1293.                             if (xrecursive) recurse(sofar,endcur);
  1294.                         } else {
  1295.                             traverse(pl->fwd,sofar,endcur);
  1296.                         }
  1297.                     } else {
  1298.                         /* it's a file */
  1299.                         if (pl->fwd == NULL) {
  1300.                             if (!xdironly) addresult(sofar);
  1301.                         }
  1302.                     }
  1303.                 }
  1304.             }
  1305.         }
  1306.         closedir(fd);
  1307.     } else {
  1308.         *endcur++ = '/';
  1309.         strcpy(endcur,pl->npart);
  1310.         endcur += strlen(endcur);
  1311.         if (access(sofar,S_IFDIR) == 0) {
  1312.             /* exists and is a dir */
  1313.             if (pl->fwd == NULL) {
  1314.                 if (!xfilonly) addresult(sofar);
  1315.                 if (xrecursive) recurse(sofar,endcur);
  1316.             } else {
  1317.                 traverse(pl->fwd,sofar,endcur);
  1318.             }
  1319.         } else if (access(sofar,0) == 0) {
  1320.            /* exists and is a file */
  1321.             if (pl->fwd == NULL) {
  1322.                 if (!xdironly) addresult(sofar);
  1323.             }
  1324.         }
  1325.     }
  1326. }
  1327.  
  1328. VOID
  1329. recurse(sofar,endcur)
  1330. register char *sofar,*endcur;
  1331. {
  1332.     DIR *fd, *opendir();
  1333.     VOID addresult();
  1334.     struct direct *dirbuf;
  1335.     unsigned char attr;
  1336.     char *name;
  1337.  
  1338.     debug(F110,"recurse ",sofar,0);
  1339.     fd = opendir(sofar);
  1340.     if (fd == NULL) {
  1341.         debug(F111,"recurse can't open directory",sofar,errno);
  1342.         return;
  1343.     }
  1344.     dirbuf = readdir(fd);    /* skip '..' entry */
  1345.     dirbuf = readdir(fd);    /* and '.' entry */
  1346.     *endcur++ = '/';
  1347.     name = endcur;
  1348.     while (dirbuf = readdir(fd)) {
  1349.         if (dirbuf->d_addr != 0) {
  1350.             /* Get a null terminated copy!!! */
  1351.             strncpy(name,dirbuf->d_name,MAXNAMLEN);
  1352.             name[MAXNAMLEN] = '\0';
  1353.             endcur = name + strlen(name);
  1354.             if ((_gs_gfdinf(fd->dd_fd,
  1355.                 dirbuf->d_addr,
  1356.                 &attr,
  1357.                 sizeof(attr)) != -1) && (attr & S_IFDIR)) {
  1358.                 /* it's a dir */
  1359.                 if (!xfilonly) addresult(sofar);
  1360.                 recurse(sofar,endcur);
  1361.             } else {
  1362.                 /* it's a file */
  1363.                 if (!xdironly) addresult(sofar);
  1364.             }
  1365.         }
  1366.     }
  1367.     closedir(fd);
  1368. }
  1369.  
  1370. /*
  1371.  * addresult:
  1372.  *  Adds a result string to the result array.  Increments the number
  1373.  *  of matches found, copies the found string into our string
  1374.  *  buffer, and puts a pointer to the buffer into the caller's result
  1375.  *  array.  Our free buffer pointer is updated.  If there is no
  1376.  *  more room in the caller's array, the number of matches is set to -1.
  1377.  * Input: a result string.
  1378.  * Returns: nothing.
  1379.  */
  1380.  
  1381. VOID
  1382. addresult(str)
  1383. register char *str;
  1384. {
  1385.     register int l;
  1386.     if (strncmp(str,"./",2) == 0) str += 2;
  1387.     if (--remlen < 0) {
  1388.         numfnd = -1;
  1389.         return;
  1390.     }
  1391.     l = strlen(str) + 1;   /* size this will take up */
  1392.     if ((freeptr + l) > &sspace[SSPACE]) {
  1393.         numfnd = -1;   /* do not record if not enough space */
  1394.         return;
  1395.     }
  1396.     strcpy(freeptr,str);
  1397.     *resptr++ = freeptr;
  1398.     freeptr += l;
  1399.     numfnd++;
  1400. }
  1401.  
  1402. iswild(str)
  1403. register char *str;
  1404. {
  1405.     register char c;
  1406.     while ((c = *str++) != '\0')
  1407.         if (c == '*' || c == '?') return 1;
  1408.     return 0;
  1409. }
  1410.  
  1411. /*
  1412.  * match:
  1413.  *  pattern matcher.  Takes a name and a pattern possibly containing
  1414.  *  the wildcard characters '*' and '?'.  Returns true if the pattern
  1415.  *  matches the name, false otherwise. Hidden files are only matched
  1416.  *  if xmatchdot is true or if the pattern starts with '.'.
  1417.  * Input: a name and a wildcard pattern.
  1418.  * Returns: 1 if match, 0 if no match.
  1419.  */
  1420.  
  1421. match(pattern,name)
  1422. char *pattern,*name;
  1423. {
  1424.     if (ISHIDDEN(name) && !(xmatchdot || (pattern[0] == '.'))) {
  1425.         return 0;
  1426.     } else if ((pattern[0] == '*') && (pattern[1] == '\0')) {
  1427.         return 1;
  1428.     } else {
  1429.         return (_cmpnam(name,pattern,strlen(pattern)) == 0);
  1430.     }
  1431. }
  1432.  
  1433. /** Get file descriptor info from given sector **/
  1434. /* _gs_gfdinf(path, sector, buffer, count) */
  1435.  
  1436. #ifdef _UCC
  1437. _asm("
  1438. _gs_gfdinf:    movem.l    d0-d4/a0,-(a7)    * Save used registers
  1439.         os9    F$ID        * Get current user id
  1440.         move.l    d1,d4        * Save it for later
  1441.         moveq.l    #0,d1        * Load superuser id
  1442.         os9    F$SUser        * Change to superuser
  1443.         bcs.s    _gs_gfdinf10    * Exit if failed
  1444.         move.l    0(a7),d0    * Load path number
  1445.         move.l    4(a7),d3    * Load sector address
  1446.         move.l    28(a7),a0    * Load buffer pointer
  1447.         move.l    32(a7),d2    * Load buffer size
  1448.         move.w    #SS_FDInf,d1    * Load function code
  1449.         os9    I$GetStt    * Call getstat function
  1450.         bcs.s    _gs_gfdinf10    * If no error
  1451.         moveq.l    #0,d0        *  set return code to 0
  1452.         bra.s    _gs_gfdinf20    * else
  1453. _gs_gfdinf10    moveq.l    #-1,d0        *  set return code to 1
  1454. _gs_gfdinf20    move.l    d4,d1        * Load original user id
  1455.         os9    F$SUser        * Change to original user
  1456.         lea.l    4(a7),a7    * Discard saved copy of d0
  1457.         movem.l (a7)+,d1-d4/a0    * Restore other registers
  1458.         rts
  1459. ");
  1460. #else
  1461. #asm
  1462. _gs_gfdinf:    movem.l    d0-d4/a0,-(a7)    * Save used registers
  1463.         os9    F$ID        * Get current user id
  1464.         move.l    d1,d4        * Save it for later
  1465.         moveq.l    #0,d1        * Load superuser id
  1466.         os9    F$SUser        * Change to superuser
  1467.         bcs.s    _gs_gfdinf10    * Exit if failed
  1468.         move.l    0(a7),d0    * Load path number
  1469.         move.l    4(a7),d3    * Load sector address
  1470.         move.l    28(a7),a0    * Load buffer pointer
  1471.         move.l    32(a7),d2    * Load buffer size
  1472.         move.w    #SS_FDInf,d1    * Load function code
  1473.         os9    I$GetStt    * Call getstat function
  1474.         bcs.s    _gs_gfdinf10    * If no error
  1475.         moveq.l    #0,d0        *  set return code to 0
  1476.         bra.s    _gs_gfdinf20    * else
  1477. _gs_gfdinf10    moveq.l    #-1,d0        *  set return code to 1
  1478. _gs_gfdinf20    move.l    d4,d1        * Load original user id
  1479.         os9    F$SUser        * Change to original user
  1480.         lea.l    4(a7),a7    * Discard saved copy of d0
  1481.         movem.l (a7)+,d1-d4/a0    * Restore other registers
  1482.         rts
  1483. #endasm
  1484. #endif /* _UCC */
  1485.  
  1486. #ifndef _UCC
  1487. /* emulate unix perror function */
  1488.  
  1489. perror(string)
  1490. char *string;
  1491. {
  1492.     extern int errno;
  1493.     fprintf(stderr,"%s ERRNO: %d\n",string,errno);
  1494. }
  1495. #endif /* _UCC */
  1496.  
  1497. #ifdef DTILDE
  1498. char *
  1499. tilde_expand(dirname)
  1500. register char *dirname;
  1501. {
  1502.     static char *home = NULL;
  1503.     static char *temp;
  1504.  
  1505.     debug(F111,"tilde_expand dirname", dirname, dirname[0]);
  1506.     if (temp == NULL && (temp = malloc(MAXPATH)) == NULL)
  1507.       return dirname;
  1508.     if(*dirname++ != '~' || (*dirname != '\0' && *dirname != '/'))
  1509.                 return --dirname;
  1510.     if(home == NULL && (home = zhome()) == NULL) return --dirname;
  1511.     if(*dirname == '\0') return home;
  1512.     strcpy(temp, home);
  1513.     strcat(temp, dirname);
  1514.     return temp;
  1515. }
  1516. #endif /* DTILDE */
  1517.  
  1518. #ifdef ZFCDAT
  1519. /* Z F C D A T  --  Get file creation date */
  1520. /*
  1521.   Call with pointer to filename.
  1522.   On success, returns pointer to modification date in yyyymmdd hh:mm:ss format.
  1523.   On failure, returns pointer to null string.
  1524. */
  1525. /* static */
  1526. char *
  1527. zfcdat(name)
  1528. char *name;
  1529. {
  1530.     static char datbuf[20];
  1531.     struct my_fildes {                  /* free stack of charge */
  1532.         unsigned char fd_att, fd_own[2], fd_date[5], fd_link, fd_fsize[4],
  1533.         fd_dcr[3];
  1534.     } fdbuf;
  1535.     int fd;
  1536.     int yy;
  1537.  
  1538.     datbuf[0] = '\0';
  1539.    
  1540.     fd = open(name, 0);
  1541.     if (fd == -1)
  1542.         fd = open(name, S_IFDIR);
  1543.     if (fd != -1) {
  1544.         if (_gs_gfd(fd, &fdbuf, sizeof fdbuf) != -1) {
  1545.             yy = fdbuf.fd_date[0] + 1900;
  1546.             if (fdbuf.fd_date[0] < (unsigned char)70) yy += 100;
  1547.             sprintf(datbuf, "%04d%02d%02d %02d:%02d:00",
  1548.               yy, fdbuf.fd_date[1], fdbuf.fd_date[2], fdbuf.fd_date[3],
  1549.               fdbuf.fd_date[4]);
  1550.             yy = strlen(datbuf);
  1551.             debug(F111, "zfcdat", datbuf, yy);
  1552.             datbuf[17] = '\0';
  1553.         }
  1554.         close(fd);
  1555.     }
  1556.     return(datbuf);
  1557. }
  1558. #endif /* ZFCDAT */
  1559.  
  1560. /* Z S T I M E  --  Set creation date for incoming file */
  1561. /*
  1562.  Call with:
  1563.  f  = pointer to name of existing file.
  1564.  yy = pointer to a Kermit file attribute structure in which yy->date.val
  1565.       is a date of the form [yy]yymmdd[ hh:mm[:ss]] e.g. 19900208 13:00:00.
  1566.  x  = is a function code: 0 means to set the file's creation date as given.
  1567.       1 means compare the given date with the file creation date.
  1568.  Returns:
  1569.  -1 on any kind of error.
  1570.   0 if x is 0 and the file date was set successfully.
  1571.   0 if x is 1 and date from attribute structure > file  creation date.
  1572.   1 if x is 1 and date from attribute structure <= file creation date.
  1573. */
  1574.  
  1575. zstime(f,yy,x) char *f; register struct zattr *yy; int x;
  1576. {
  1577.   extern int errno;
  1578.   int r = -1;                             /* return code */
  1579.   int path;
  1580.   register char *point;
  1581.   register char save;
  1582.   unsigned char year,month,day,hour,minute,second;
  1583.   struct fildes buffer;
  1584.   unsigned char long_year=0,hour_min=0;
  1585.   time_t mktime(),file_date;
  1586.   VOID convert_to_local_time();
  1587.   struct tm tp;
  1588.  
  1589.     debug(F110,"zstime",f,0);
  1590.  
  1591.     switch (yy->date.len)
  1592.     {
  1593.     case 6:   /* yymmdd */
  1594.         break;
  1595.     case 8:   /* yyyymmdd */
  1596.         long_year = 1;
  1597.         break;
  1598.     case 12:  /* yymmdd hh:mm */
  1599.         hour_min = 1;
  1600.         break;
  1601.     case 14:  /* yyyymmdd hh:mm */
  1602.         long_year = 1;
  1603.         hour_min = 1;
  1604.         break;
  1605.     case 15:  /* yymmdd hh:mm:ss */
  1606.         hour_min = 1;
  1607.         break;
  1608.     case 17:  /* yyyymmdd hh:mm:ss */
  1609.         long_year = 1;
  1610.         hour_min = 1;
  1611.         break;
  1612.     default:
  1613.         debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1614.         return(-1);
  1615.     }
  1616.  
  1617.     point = yy->date.val;
  1618.     if (long_year)
  1619.     {
  1620.       save = *(point+4);
  1621.       *(point+4) = '\0';
  1622.       year = atoi(point) % 100; /* OS-9 only has 2 digit year */
  1623.       *(point+4) = save;
  1624.       point += 4;
  1625.     }
  1626.     else
  1627.     {
  1628.       save = *(point+2);
  1629.       *(point+2) = '\0';
  1630.       year = atoi(point);
  1631.       point += 2; /* don't add 1900: OS-9 only has 2 digit year anyway */
  1632.     }
  1633.  
  1634.     save = *(point+2);
  1635.     *(point+2) = '\0';
  1636.     month = atoi(point);
  1637.     *(point+2) = save;
  1638.  
  1639.     point += 2;
  1640.     save = *(point+2);
  1641.     *(point+2) = '\0';
  1642.     day = atoi(point);
  1643.     *(point+2) = save;
  1644.  
  1645.     if (hour_min)
  1646.     {
  1647.         point += 3; /* skip the blank */
  1648.         save = *(point+2);
  1649.         *(point+2) = '\0';
  1650.         hour = atoi(point);
  1651.         *(point+2) = save;
  1652.  
  1653.         point += 3; /* skip the : */
  1654.         save = *(point+2);
  1655.         *(point+2) = '\0';
  1656.         minute = atoi(point);
  1657.         *(point+2) = save;
  1658.  
  1659.         second = 0; /* no OS-9 seconds */
  1660.     }
  1661.     else /* no hours and minutes given */
  1662.     {
  1663.         hour = minute = second = 0;
  1664.     }
  1665.     if (   (year   > 99)
  1666.         || (month  > 12) || (month  < 1)
  1667.         || (day    > 31) || (day    < 1)
  1668.         || (hour   > 23)
  1669.         || (minute > 59))
  1670.     {
  1671.         debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1672.         return(-1);
  1673.     }
  1674.  
  1675.     /* Convert GMT broken down time to local broken down time */
  1676.     convert_to_local_time(&year,&month,&day,&hour,&minute,&second);
  1677.  
  1678.     if ((path = open(f,0)) == -1) /* mode 0 is sufficient for _gs_gfd */
  1679.     {
  1680.         debug(F110,"Can't open file to get fdesc:",f,0);
  1681.         return(r);
  1682.     }
  1683.     if (_gs_gfd(path,&buffer,sizeof(buffer)) == -1)
  1684.     {
  1685.         debug(F111,"Can't get descriptor for file:",f,errno);
  1686.         close(path);
  1687.         return(r);
  1688.     }
  1689.     close(path);
  1690.     switch (x)       /* Execute desired function */
  1691.     {
  1692.         case 0:      /* Set the creation and modification date of the file */
  1693.             buffer.fd_date[0] = year;
  1694.             buffer.fd_date[1] = month;
  1695.             buffer.fd_date[2] = day;
  1696.             buffer.fd_date[3] = hour;
  1697.             buffer.fd_date[4] = minute;
  1698. #ifdef COMMENT
  1699. /* The creation date can't be set */
  1700.             buffer.fd_dcr[0] = year;
  1701.             buffer.fd_dcr[1] = month;
  1702.             buffer.fd_dcr[2] = day;
  1703. #endif /* COMMENT */
  1704.             if ((path = open(f,S_IWRITE)) == -1) /* _ss_pfd needs writing */
  1705.             {
  1706.                 debug(F110,"Can't open file for write:",f,0);
  1707.                 return(r);
  1708.             }
  1709.             if (_ss_pfd(path,&buffer) == -1)
  1710.             {
  1711.               debug(F111,"Can't update descriptor for file:",f,errno);
  1712.             }
  1713.             else
  1714.             {
  1715.               debug(F110,"Mod. time is set for file: ",f,0);
  1716.               r = 0;
  1717.             }
  1718.             close(path);
  1719.             break;
  1720.     case 1:             /* Compare the dates */
  1721.             tp.tm_sec  = 0;
  1722.             tp.tm_min  = buffer.fd_date[4];
  1723.             tp.tm_hour = buffer.fd_date[3];
  1724.             tp.tm_mday = buffer.fd_date[2];
  1725.             tp.tm_mon  = buffer.fd_date[1]-1;
  1726.             tp.tm_year = buffer.fd_date[0];
  1727.             if (buffer.fd_date[0] < (unsigned char)70) tp.tm_year += 100;
  1728.  
  1729.             file_date = mktime(&tp);
  1730.  
  1731.             tp.tm_min  = minute;
  1732.             tp.tm_hour = hour;
  1733.             tp.tm_mday = day;
  1734.             tp.tm_mon  = month-1;
  1735.             tp.tm_year = year;
  1736.             if (year < (unsigned char)70) tp.tm_year += 100;
  1737.  
  1738. /* 1 if x is 1 and date from attribute structure <= file creation date. */
  1739.             r = mktime(&tp) <= file_date;
  1740.             break;
  1741.     default:                            /* Error */
  1742.             break;
  1743.     }
  1744.     return(r);
  1745. }
  1746.  
  1747. #ifdef RENAME
  1748. /*  Z R E N A M E  --  Rename a file  */
  1749. /*
  1750.    Call with old and new names.
  1751.    If new name is the name of a directory, the 'old' file is moved to
  1752.    that directory.
  1753.    Returns 0 on success, -1 on failure.
  1754. */
  1755. int
  1756. zrename(old,new) char *old, *new; {
  1757.     char *p = NULL, *s = new;
  1758.     int x;
  1759.  
  1760.     debug(F110,"zrename old",old,0);
  1761.     debug(F110,"zrename new",s,0);
  1762.     if (isdir(new)) {
  1763.         char *q = NULL;
  1764.         x = strlen(new);
  1765.         if (!(p = malloc(strlen(new) + strlen(old) + 2)))
  1766.           return(-1);
  1767.         strcpy(p,new);                  /* Directory part */
  1768.         if (!ISDIRSEP(*(new+x-1)))      /* Separator, if needed */
  1769.           strcat(p,"/");
  1770.         zstrip(old,&q);                 /* Strip path part from old name */
  1771.         strcat(p,q);                    /* Concatenate to new directory */
  1772.         s = p;
  1773.         debug(F110,"zrename dir",s,0);
  1774.     } else debug(F110,"zrename no dir",s,0);
  1775. /*
  1776.   Atomic, preferred, uses a single system call, rename(), if available.
  1777.   OS/2 rename() returns nonzero, but not necessarily -1 (?), on failure.
  1778. */
  1779.     x = rename(old,s);
  1780.     if (p) free(p);
  1781.     return(x ? -1 : 0);
  1782. }
  1783.  
  1784. #else /* RENAME */
  1785.  
  1786. /*  Z R E N A M E  --  Rename a file  */
  1787.  
  1788. /*  Call with old and new names */
  1789. /*  Returns 0 on success, -1 on failure. */
  1790.  
  1791. zrename(old,new) register char *old, *new;
  1792. {
  1793. char buffer[256];
  1794.         sprintf(buffer,"rename %s %s",old,new);
  1795.         if (zsyscmd(buffer) != 0)
  1796.         {
  1797.           debug(F111,"Can't rename:",old,errno);
  1798.           return(-1);
  1799.         }
  1800.         return(0);
  1801. }
  1802. #endif /* !RENAME */
  1803.  
  1804. /* Sorry...gotta avoid namespace pollution! */
  1805. #ifdef _UCC
  1806. #define environ    _environ
  1807. #endif /* _UCC */
  1808. /*
  1809.   Functions for executing system commands.
  1810.   zsyscmd() executes the system command in the normal, default way for
  1811.   the system.  In UNIX, it does what system() does.  Thus, its results
  1812.   are always predictable.
  1813.   zshcmd() executes the command using the user's preferred shell.
  1814. */
  1815. static int
  1816. zcmd(shell,command)
  1817. char * shell;            /* Name of shell to use        */
  1818. char * command;            /* Command line to execute    */
  1819. {
  1820.   char *argblock[3];        /* Argument block for fork    */
  1821.   int pid;            /* Process ID for forked shell    */
  1822.   int wstatus;            /* Status from wait        */
  1823.   unsigned pstatus;        /* Process exit status        */
  1824.  
  1825.   extern char ** environ;
  1826.   extern int os9fork();
  1827.  
  1828.   debug(F110,shell,command,0);
  1829.  
  1830.   /* If no command line, force interactive operation */
  1831.   if ((command != NULL) && (*command == '\0')) command = NULL;
  1832.  
  1833.   /* Build argument block */
  1834.   argblock[0] = shell;
  1835.   argblock[1] = command;
  1836.   argblock[2] = NULL;
  1837.  
  1838.   /* Fork the shell and wait for it to terminate */
  1839.   pid = os9exec(os9fork, argblock[0], argblock, environ, 0, 0, 0);
  1840.   if (pid != -1) {
  1841.     wstatus = wait(&pstatus);
  1842.     if (wstatus == pid) {
  1843.       debug(F111,"zcmd: process done",command,pstatus);
  1844.       if (pstatus == 0) return 1; else return 0;
  1845.     } else {
  1846.       debug(F111,"zcmd: wait failed",command,wstatus);
  1847.       return -1;
  1848.     }
  1849.   } else {
  1850.     debug(F111,"zcmd: fork failed",command,errno);
  1851.     return -1;
  1852.   }
  1853. }
  1854.  
  1855. int
  1856. zsyscmd(command)
  1857. char * command;            /* Command line to execute    */
  1858. {
  1859.   debug(F110,"zsyscmd",command,0);
  1860.  
  1861.   return zcmd("shell",command);
  1862. }
  1863.  
  1864. int
  1865. zshcmd(command)
  1866. char * command;            /* Command line to execute    */
  1867. {
  1868.   char * shell;            /* Name of shell to use        */
  1869.  
  1870.   extern char * getenv();
  1871.  
  1872.   debug(F110,"zshcmd",command,0);
  1873.  
  1874.   shell = getenv("SHELL");
  1875.   if (shell == NULL) {
  1876.     shell = "shell";
  1877.   }
  1878.   return zcmd(shell,command);
  1879. }
  1880.  
  1881. /*  Z S I N L  --  Read a line from a file  */
  1882.  
  1883. /*
  1884.   Writes the line into the address provided by the caller.
  1885.   n is the Kermit "channel number".
  1886.   Writing terminates when newline is encountered, newline is not copied.
  1887.   Writing also terminates upon EOF or if length x is exhausted.
  1888.   Returns 0 on success, -1 on EOF or error.
  1889. */
  1890. zsinl(n,s,x) int n, x; char *s; {
  1891.     int a, z = 0;
  1892.  
  1893.     if (chkfn(n) < 1) {                 /* Make sure file is open */
  1894.           return(-1);
  1895.     }
  1896.     a = -1;
  1897.     while (x--) {
  1898.       if (zchin(n,&a) < 0) {       /* Read a character from the file */
  1899.         z = -1;
  1900.         break;
  1901.       }
  1902.       if ( a == (char) NLCHAR) break; /* Single-character line terminator */
  1903.       *s = a;
  1904.       s++;
  1905.     }
  1906.     *s = '\0';
  1907.     return(z);
  1908. }
  1909.  
  1910. static char
  1911. *get_gmtime(file_desc) struct fildes *file_desc;
  1912. /* returns UCT/GMT yyyymmdd hh:mm:00 time out of the file descriptor
  1913.    if impossible to get GMT it takes the time from the file descriptor
  1914.    without converion */
  1915. {
  1916.   static char datbuf[18]; /* must be static cause returned!!! */
  1917.   struct tm *gmt = NULL, tp;
  1918.   time_t local_file_date;
  1919.  
  1920.   tp.tm_sec  = 0;
  1921.   tp.tm_min  = file_desc->fd_date[4];
  1922.   tp.tm_hour = file_desc->fd_date[3];
  1923.   tp.tm_mday = file_desc->fd_date[2];
  1924.   tp.tm_mon  = file_desc->fd_date[1]-1;
  1925.   tp.tm_year = ((unsigned char *)file_desc->fd_date)[0];
  1926.   if (tp.tm_year < 70) tp.tm_year += 100;
  1927.  
  1928.   /* If TZ not defined send modification date literally */
  1929.   if (getenv("TZ") != (char *)NULL) { 
  1930.     local_file_date = mktime(&tp);
  1931.     if (local_file_date != (time_t)-1) /* Take time literally */
  1932.       gmt = gmtime(&local_file_date);
  1933.   }
  1934.  
  1935.   if (gmt == NULL) gmt = &tp;
  1936.  
  1937.   sprintf(datbuf,"%4d%02d%02d %02d:%02d:00",
  1938.           1900+(unsigned int)gmt->tm_year,
  1939.           gmt->tm_mon+1,gmt->tm_mday,gmt->tm_hour,gmt->tm_min);
  1940.  
  1941.   return(datbuf);
  1942. }
  1943.  
  1944. static VOID
  1945. convert_to_local_time(year,month,day,hour,minute,second)
  1946. unsigned char *year,*month,*day,*hour,*minute,*second;
  1947. /* will convert to local time if timezone information is available assuming
  1948.    UCT/GMT time as input , otherwise just return.
  1949.    year must be since 1900 */
  1950. {
  1951.   struct tm *tp;
  1952.   time_t cal_date;
  1953.   int time,date;
  1954.   char *getenv();
  1955.   int y;
  1956.  
  1957.   if (getenv("TZ") == (char *)NULL) return;
  1958.   y = *year + 1900;
  1959.   if (*year < (unsigned char)70) y += 100;
  1960.   date = (y<<16)+(*month<<8)+*day;
  1961.   time = (*hour<<16)+(*minute<<8)+*second;
  1962.   if (_julian(&time,&date) == -1) return;
  1963.   cal_date = (date-2440587)*86400+time; /* 2440587 = 1st of Jan 1970 */
  1964.  
  1965.   tp = localtime(&cal_date);
  1966.  
  1967.   if (tp == (struct tm*)NULL) return;
  1968.  
  1969.   *second = tp->tm_sec;
  1970.   *minute = tp->tm_min;
  1971.   *hour = tp->tm_hour;
  1972.   *day = tp->tm_mday;
  1973.   *month = tp->tm_mon+1;
  1974.   *year = tp->tm_year % 100;
  1975.  
  1976. }
  1977.  
  1978. FILE *
  1979. pipeopen(command,access_type) char *command,*access_type;
  1980. {
  1981.   extern int os9forkc();
  1982.   extern char **environ;
  1983.   int duped,dupederr,stdinout;
  1984.   FILE *pipe;
  1985.   char *argv[3];
  1986.  
  1987.     if (*access_type=='r') stdinout = 1; /* redirect stdout and stderr*/
  1988.     else if (*access_type=='w') stdinout = 0; /* redirect stdin */
  1989.     else return((FILE *)NULL);
  1990.  
  1991.     if((duped = dup(stdinout)) <= 0) return((FILE *)NULL);
  1992.     if(stdinout == 1)
  1993.     {
  1994.       if((dupederr = dup(2)) <= 0)
  1995.       {
  1996.         close(duped);
  1997.         return((FILE *)NULL);
  1998.       }
  1999.     }
  2000.     if((pipe = fopen("/pipe", "r+")) == NULL)
  2001.     {
  2002.         close(duped);
  2003.         if(stdinout == 1) close(dupederr);
  2004.         return((FILE *)NULL);
  2005.     }
  2006.     close(stdinout);
  2007.     dup(fileno(pipe));
  2008.     if (stdinout == 1) /* also dupe stderr */
  2009.     {
  2010.       close(2);
  2011.       dup(fileno(pipe));
  2012.     }
  2013.     argv[0] = "shell";
  2014.     argv[1] = command;
  2015.     argv[2] = (char *)NULL;
  2016.     if((pipe_pid = os9exec(os9forkc,argv[0],argv,environ,0,0,3)) < 0)
  2017.     {
  2018.         pipe_pid = 0;
  2019.         fclose(pipe);
  2020.  
  2021.         close(stdinout);
  2022.         dup(duped); /* restore old stdinout */
  2023.         close(duped);
  2024.         if(stdinout == 1) /* also restore old sterr */
  2025.         {
  2026.           close(2);
  2027.           dup(dupederr);
  2028.           close(dupederr);
  2029.         }
  2030.         return (FILE *)NULL;
  2031.     }
  2032.     debug(F101," pipeopen success id:","",pipe_pid);
  2033.     close(stdinout);
  2034.     dup(duped);
  2035.     close(duped);
  2036.     if(stdinout == 1)
  2037.     {
  2038.       close(2);
  2039.       dup(dupederr);
  2040.       close(dupederr);
  2041.     }
  2042.     return(pipe);
  2043. }
  2044.  
  2045. /*
  2046.    Tell if string pointer s is the name of an existing directory.
  2047.    Returns 1 if directory, 0 if not a directory.
  2048. */
  2049. int
  2050. isdir(s) char *s; {
  2051.     int x;
  2052.  
  2053.     if (!s) return(0);
  2054.     if (!*s) return(0);
  2055.  
  2056.     x = access(s, S_IFDIR);
  2057.     debug(F111,"isdir stat",s,x);
  2058.     if (x == -1) {
  2059.         debug(F101,"isdir errno","",errno);
  2060.         return(0);
  2061.     } else {
  2062.         return 1;
  2063.     }
  2064. }
  2065.  
  2066. #ifdef CK_MKDIR
  2067. /* Some systems don't have mkdir(), e.g. Tandy Xenix 3.2.. */
  2068.  
  2069. /* Z M K D I R  --  Create directory(s) if necessary */
  2070. /*
  2071.    Call with:
  2072.     A pointer to a file specification that might contain directory
  2073.     information.  The filename is expected to be included.
  2074.     If the file specification does not include any directory separators,
  2075.     then it is assumed to be a plain file.
  2076.     If one or more directories are included in the file specification,
  2077.     this routine tries to create them if they don't already exist.
  2078.    Returns:
  2079.     0 on success, i.e. the directory was created
  2080.    -1 on failure to create the directory
  2081. */
  2082. int
  2083. zmkdir(path) char *path; {
  2084.     char *xp, *tp, c;
  2085.     int x;
  2086.  
  2087.     x = strlen(path);
  2088.     debug(F111,"zmkdir",path,x);
  2089.     if (x < 1 || x > MAXPATH)           /* Check length */
  2090.       return(-1);
  2091.     if (!(tp = malloc(x+1)))            /* Make a temporary copy */
  2092.       return(-1);
  2093.     strcpy(tp,path);
  2094. #ifdef DTILDE
  2095.     if (*tp == '~') {                   /* Starts with tilde? */
  2096.         xp = tilde_expand(tp);          /* Attempt to expand tilde */
  2097.         if (*xp) {
  2098.             char *zp;
  2099.             debug(F110,"zmkdir tilde_expand",xp,0);
  2100.             if (!(zp = malloc(strlen(xp) + 1))) { /* Make a place for it */
  2101.                 free(tp);
  2102.                 return(-1);
  2103.             }
  2104.             free(tp);                   /* Free previous buffer */
  2105.             tp = zp;                    /* Point to new one */
  2106.             strcpy(tp,xp);              /* Copy expanded name to new buffer */
  2107.         }
  2108.         debug(F110,"zmkdir tp after tilde_expansion",tp,0);
  2109.     }
  2110. #endif /* DTILDE */
  2111.     xp = tp;
  2112.     if (ISDIRSEP(*xp))                  /* Don't create root directory! */
  2113.       xp++;
  2114.  
  2115.     /* Go thru filespec from left to right... */
  2116.  
  2117.     for (; *xp; xp++) {                 /* Create parts that don't exist */
  2118.         if (!ISDIRSEP(*xp))             /* Find next directory separator */
  2119.           continue;
  2120.         c = *xp;                        /* Got one. */
  2121.         *xp = NUL;                      /* Make this the end of the string. */
  2122.         if (!isdir(tp)) {               /* This directory exists already? */
  2123.             debug(F110,"zmkdir making",tp,0);
  2124.             x =                         /* No, try to create it */
  2125. #ifdef NOMKDIR
  2126.                -1                       /* Systems without mkdir() */
  2127. #else
  2128.                makdir(tp,0,077)
  2129. #endif /* NOMKDIR */
  2130.                  ;
  2131.             if (x < 0) {
  2132.                 debug(F101,"zmkdir failed, errno","",errno);
  2133.                 free(tp);               /* Free temporary buffer. */
  2134.                 return(-1);             /* Freturn failure code. */
  2135.             }
  2136.         }
  2137.         *xp = c;                        /* Replace the separator. */
  2138.     }
  2139.     free(tp);                           /* Free temporary buffer. */
  2140.     return(0);                          /* Return success code. */
  2141. }
  2142. #endif /* CK_MKDIR */
  2143.  
  2144. /* Z F S E E K  --  Position input file pointer */
  2145. /*
  2146.    Call with:
  2147.     Long int, 0-based, indicating desired position.
  2148.    Returns:
  2149.     0 on success.
  2150.    -1 on failure.
  2151. */
  2152. #ifndef NORESEND
  2153. int
  2154. #ifdef CK_ANSIC
  2155. zfseek(long pos)
  2156. #else
  2157. zfseek(pos) long pos;
  2158. #endif /* CK_ANSIC */
  2159. /* zfseek */ {
  2160.     debug(F101,"zfseek","",pos);
  2161.     return(fseek(fp[ZIFILE], pos, 0));
  2162. }
  2163. #endif /* NORESEND */
  2164.