home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / c-kermit / cksfio.c < prev    next >
C/C++ Source or Header  |  2020-01-01  |  51KB  |  1,742 lines

  1. char *ckzv = "GEM file support, 5A(059) 16 Jul 92";
  2.  
  3. /* C K S F I O  --  Kermit file system support for Atari ST systems */
  4.  
  5. /*
  6.  Author: Frank da Cruz (fdc@columbia.edu, FDCCU@CUVMA.BITNET),
  7.  Columbia University Center for Computing Activities.  Many other contributors.
  8.  First released January 1985.
  9.  Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New
  10.  York.  Permission is granted to any individual or institution to use this
  11.  software as long as it is not sold for profit.  This copyright notice must be
  12.  retained.  This software may not be included in commercial products without
  13.  written permission of Columbia University.
  14.  Extensively modified by: Bruce Moore (mooreb@iccgcc.decnet.ab.com).
  15. */
  16.  
  17. /* Include Files */
  18.  
  19. #include "ckcdeb.h"
  20.  
  21. /* Directory structure header file */
  22.  
  23. #ifdef SDIRENT
  24. #define DIRENT
  25. #endif
  26.  
  27. #ifdef GEMDOS
  28. #define TIMESTAMP            /* Can do file dates */
  29. #include <time.h>            /* System time stuff */
  30. extern long timezone;
  31. extern int dstadjust;
  32. #endif
  33.  
  34. /* Is `y' a leap year? */
  35. #define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
  36.  
  37. /* Number of leap years from 1970 to `y' (not including `y' itself). */
  38. #define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
  39.  
  40. #include <stat.h>            /* File status */
  41.  
  42. /*
  43.   Functions (n is one of the predefined file numbers from ckcker.h):
  44.  
  45.    zopeni(n,name)   -- Opens an existing file for input.
  46.    zopeno(n,name,attr,fcb) -- Opens a new file for output.
  47.    zclose(n)        -- Closes a file.
  48.    zchin(n,&c)      -- Gets the next character from an input file.
  49.    zsinl(n,&s,x)    -- Read a line from file n, max len x, into address s.
  50.    zsout(n,s)       -- Write a null-terminated string to output file, buffered.
  51.    zsoutl(n,s)      -- Like zsout, but appends a line terminator.
  52.    zsoutx(n,s,x)    -- Write x characters to output file, unbuffered.
  53.    zchout(n,c)      -- Add a character to an output file, unbuffered.
  54.    zchki(name)      -- Check if named file exists and is readable, return size.
  55.    zchko(name)      -- Check if named file can be created.
  56.    zchkspa(name,n)  -- Check if n bytes available to create new file, name.
  57.    znewn(name,s)    -- Make a new unique file name based on the given name.
  58.    zdelet(name)     -- Delete the named file.
  59.    zxpand(string)   -- Expands the given wildcard string into a list of files.
  60.    znext(string)    -- Returns the next file from the list in "string".
  61.    zxcmd(n,cmd)     -- Execute the command in a lower fork on file number n.
  62.    zclosf()         -- Close input file associated with zxcmd()'s lower fork.
  63.    zrtol(n1,n2)     -- Convert remote filename into local form.
  64.    zltor(n1,n2)     -- Convert local filename into remote form.
  65.    zchdir(dirnam)   -- Change working directory.
  66.    zhome()          -- Return pointer to home directory name string.
  67.    zkself()         -- Kill self, log out own job.
  68.    zsattr(struct zattr *) -- Return attributes for file which is being sent.
  69.    zstime(f, struct zattr *, x) - Set file creation date from attribute packet.
  70.    zrename(old, new) -- Rename a file.
  71.  */
  72.  
  73. /* Kermit-specific includes */
  74. /*
  75.   Definitions here supersede those from system include files.
  76.   ckcdeb.h is included above.
  77. */
  78. #include "ckcker.h"            /* Kermit definitions */
  79. #include "ckucmd.h"            /* For sys-dependent keyword tables */
  80. #include "ckuver.h"
  81.  
  82. char *ckzsys = HERALD;
  83.  
  84. /* Definitions of some Unix system commands */
  85.  
  86. char *DELCMD = "rm ";            /* For file deletion */
  87. char *PWDCMD = "pwd ";            /* For saying where I am */
  88. char *DIRCMD = "ls -l ";        /* For directory listing */
  89. char *DIRCM2 = "ls -l ";        /* For directory listing, no args */
  90. char *TYPCMD = "cat ";            /* For typing a file */
  91. char *SPACMD = "df $cwd";
  92. char *SPACM2 = "df ";            /* For space in specified directory */
  93. char *WHOCMD = "echo just we frogs ";    /* For seeing who's logged in */
  94.  
  95. #ifdef DTILDE                /* For tilde expansion */
  96. _PROTOTYP( char * tilde_expand, (char *) );
  97. #endif /* DTILDE */
  98.  
  99. /* More system-dependent includes, which depend on symbols defined */
  100. /* in the Kermit-specific includes.  Oh what a tangled web we weave... */
  101.  
  102. #include <access.h>            /* File access */
  103. #include <osbind.h>            /* GEM o.s. bindings */
  104.  
  105. #ifndef S_ISREG
  106. #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
  107. #endif /* S_ISREG */
  108. #ifndef S_ISDIR
  109. #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
  110. #endif /* S_ISDIR */
  111.  
  112. /* Define maximum length for a file name if not already defined */
  113.  
  114. #ifndef MAXNAMLEN
  115. #define MAXNAMLEN 12
  116. #endif /* MAXNAMLEN */
  117.  
  118. /* Longest pathname */
  119.  
  120. #ifndef MAXPATH
  121. #define MAXPATH 255
  122. #endif /* MAXPATH */
  123.  
  124. /* Maximum number of filenames for wildcard expansion */
  125.  
  126. #define MAXWLD 50
  127.  
  128. /* More internal function prototypes */
  129. /*
  130.  * The path structure is used to represent the name to match.
  131.  * Each slash-separated segment of the name is kept in one
  132.  * such structure, and they are linked together, to make
  133.  * traversing the name easier.
  134.  */
  135. struct path {
  136.     char npart[MAXNAMLEN+4];        /* name part of path segment */
  137.     struct path *fwd;            /* forward ptr */
  138. };
  139. _PROTOTYP( int shxpand, (char *, char **, int ) );
  140. _PROTOTYP( static int fgen, (char *, char **, int ) );
  141. _PROTOTYP( static VOID traverse, (struct path *, char *, char *) );
  142. _PROTOTYP( static VOID addresult, (char *) );
  143. _PROTOTYP( static int match, (char *, char *) );
  144. _PROTOTYP( char * xindex, (char *, char) );
  145. _PROTOTYP( UID_T real_uid, (void) );
  146. _PROTOTYP( struct path *splitpath, (char *p) );
  147.  
  148. /* Some systems define these symbols in include files, others don't... */
  149.  
  150. #ifndef R_OK
  151. #define R_OK AREAD            /* For access */
  152. #endif
  153.  
  154. #ifndef W_OK
  155. #define W_OK AWRITE
  156. #endif
  157.  
  158. #ifndef O_RDONLY
  159. #define O_RDONLY 000
  160. #endif
  161.  
  162. /* Declarations */
  163.  
  164. int maxnam = MAXNAMLEN;            /* Available to the outside */
  165. int maxpath = MAXPATH;
  166.  
  167. FILE *fp[ZNFILS] = {             /* File pointers */
  168.     NULL, NULL, NULL, NULL, NULL, NULL, NULL };
  169.  
  170. /* (PWP) external def. of things used in buffered file input and output */
  171. #ifdef DYNAMIC
  172. extern CHAR *zinbuffer, *zoutbuffer;
  173. #else
  174. extern CHAR zinbuffer[], zoutbuffer[];
  175. #endif /* DYNAMIC */
  176. extern CHAR *zinptr, *zoutptr;
  177. extern int zincnt, zoutcnt;
  178. extern int wildxpand;
  179.  
  180. static long iflen = -1L;        /* Input file length */
  181.  
  182. static int pid = 0;            /* pid of child fork */
  183. static int fcount;            /* Number of files in wild group */
  184. static char nambuf[MAXNAMLEN+4];    /* Buffer for a filename */
  185. static char pipename[8*MAXNAMLEN];    /* Pipe name */
  186. static char zmbuf[200];            /* For mail, remote print strings */
  187. char *malloc(), *getenv(), *strcpy();    /* System functions */
  188. extern char *strrchr(), *strcat();    /* and a couple more */
  189. extern char *strchr(), *strncpy();    /* and yet more */
  190. extern errno;                /* System error code */
  191.  
  192. /* static */                /* Not static, must be global now. */
  193. char *mtchs[MAXWLD],            /* Matches found for filename */
  194.      **mtchptr;                /* Pointer to current match */
  195.  
  196. /*  Z K S E L F  --  Kill Self: log out own job, if possible.  */
  197.  
  198. /* Note, should get current pid, but if your system doesn't have */
  199. /* getppid(), then just kill(0,9)...  */
  200.  
  201. zkself() {                /* For "bye", but no guarantee! */
  202.     exit(GOOD_EXIT);
  203. }
  204.  
  205. /*  Z O P E N I  --  Open an existing file for input. */
  206.  
  207. int
  208. zopeni(n,name) int n; char *name; {
  209.     debug(F111," zopeni",name,n);
  210.     debug(F101,"  fp","", fp[n]);
  211.     if (chkfn(n) != 0) return(0);
  212.     zincnt = 0;                /* Reset input buffer */
  213.     if (n == ZSYSFN) {            /* Input from a system function? */
  214. /*** Note, this function should not be called with ZSYSFN ***/
  215. /*** Always call zxcmd() directly, and give it the real file number ***/
  216. /*** you want to use.  ***/
  217.         debug(F110,"zopeni called with ZSYSFN, failing!",name,0);
  218.     *nambuf = '\0';            /* No filename. */
  219.     return(0);            /* fail. */
  220. #ifdef COMMENT
  221.     return(zxcmd(n,name));        /* Try to fork the command */
  222. #endif
  223.     }
  224.     if (n == ZSTDIO) {            /* Standard input? */
  225.     if (isatty(0)) {
  226.         fprintf(stderr,"Terminal input not allowed");
  227.         debug(F110,"zopeni: attempts input from unredirected stdin","",0);
  228.         return(0);
  229.     }
  230.     fp[ZIFILE] = stdin;
  231.     return(1);
  232.     }
  233.     fp[n] = fopen(name,"rb");        /* Real file, open it. */
  234.     debug(F111," zopeni", name, fp[n]);
  235.     if (fp[n] == NULL) perror("zopeni");
  236.     return((fp[n] != NULL) ? 1 : 0);
  237. }
  238.  
  239. /*  Z O P E N O  --  Open a new file for output.  */
  240.  
  241. int
  242. zopeno(n,name,zz,fcb)
  243. /* zopeno */  int n; char *name; struct zattr *zz; struct filinfo *fcb; {
  244.  
  245.     char p[8];                /* (===OS2 change===) */
  246. /*  char *p; */                /* Local-use pointer */
  247.  
  248.  
  249. /* As of Version 5A, the attribute structure and the file information */
  250. /* structure are included in the arglist. */
  251.  
  252.     debug(F111,"zopeno",name,n);
  253.     if (fcb) {
  254.     debug(F101,"zopeno fcb disp","",fcb->dsp);
  255.     debug(F101,"zopeno fcb type","",fcb->typ);
  256.     debug(F101,"zopeno fcb char","",fcb->cs);
  257.     } else {
  258.     debug(F100,"zopeno fcb is NULL","",0);
  259.     }
  260.     if (n != ZDFILE)
  261.       debug(F101," fp[]=stdout", "", fp[n]);
  262.     if (chkfn(n) != 0) return(0);
  263.     if ((n == ZCTERM) || (n == ZSTDIO)) {   /* Terminal or standard output */
  264.     fp[ZOFILE] = stdout;
  265.     if (n != ZDFILE)
  266.       debug(F101," fp[]=stdout", "", (int) fp[n]);
  267.     zoutcnt = 0;
  268.     zoutptr = zoutbuffer;
  269.     return(1);
  270.     }
  271.  
  272. /* A real file.  Open it in desired mode (create or append). */
  273.  
  274.     strcpy(p,"w");            /* Assume write/create mode */
  275.     if (fcb) {                /* If called with an FCB... */
  276.     if (fcb->dsp == XYFZ_A)        /* Does it say Append? */
  277.       strcpy(p,"a");        /* Yes. */
  278.     }
  279.     if (n == ZOFILE) strcat(p,"b");    /* Binary mode */
  280.     fp[n] = fopen(name,p);        /* Open the file */
  281.  
  282.     if (fp[n] == NULL) {
  283.         perror("zopeno can't open");
  284.     } else {
  285.         if (n == ZDFILE) setbuf(fp[n],NULL); /* Debugging file unbuffered */
  286.     }
  287.     zoutcnt = 0;        /* (PWP) reset output buffer */
  288.     zoutptr = zoutbuffer;
  289.     if (n != ZDFILE)
  290.       debug(F101, " fp[n]", "", fp[n]);
  291.     return((fp[n] != NULL) ? 1 : 0);
  292. }
  293.  
  294. /*  Z C L O S E  --  Close the given file.  */
  295.  
  296. /*  Returns 0 if arg out of range, 1 if successful, -1 if close failed.  */
  297.  
  298. int
  299. zclose(n) int n; {
  300.     int x, x2;
  301.     if (chkfn(n) < 1) return(0);    /* Check range of n */
  302.  
  303.     if ((n == ZOFILE) && (zoutcnt > 0))    /* (PWP) output leftovers */
  304.       x2 = zoutdump();
  305.     else
  306.       x2 = 0;
  307.  
  308.     x = 0;                /* Initialize return code */
  309.     if (fp[ZSYSFN]) {            /* If file is really pipe */
  310.         x = zclosf(n);            /* do it specially */
  311.     } else {
  312.         if ((fp[n] != stdout) && (fp[n] != stdin)) x = fclose(fp[n]);
  313.     fp[n] = NULL;
  314.     }
  315.     iflen = -1L;            /* Invalidate file length */
  316.     if (x == EOF)            /* if we got a close error */
  317.     return (-1);
  318.     else if (x2 < 0)        /* or an error flushing the last buffer */
  319.     return (-1);        /* then return an error */
  320.     else
  321.     return (1);
  322. }
  323.  
  324. /*  Z C H I N  --  Get a character from the input file.  */
  325.  
  326. /*  Returns -1 if EOF, 0 otherwise with character returned in argument  */
  327.  
  328. int
  329. zchin(n,c) int n; int *c; {
  330.     int a, x;
  331.  
  332.     /* (PWP) Just in case this gets called when it shouldn't. */
  333.     if (n == ZIFILE) {
  334.     x = zminchar();
  335.     *c = x;
  336.     return(x);
  337.     }
  338.     /* if (chkfn(n) < 1) return(-1); */
  339.     a = getc(fp[n]);
  340.     if (a == EOF) return(-1);
  341.     *c = (CHAR) a & 0377;
  342.     return(0);
  343. }
  344.  
  345. /*  Z S I N L  --  Read a line from a file  */
  346.  
  347. /*
  348.   Writes the line into the address provided by the caller.
  349.   n is the Kermit "channel number".
  350.   Writing terminates when newline is encountered, newline is not copied.
  351.   Writing also terminates upon EOF or if length x is exhausted.
  352.   Returns 0 on success, -1 on EOF or error.
  353. */
  354. int
  355. zsinl(n,s,x) int n, x; char *s; {
  356.     int a, z = 0;
  357.  
  358.     if (chkfn(n) < 1) {            /* Make sure file is open */
  359.     return(-1);
  360.     }
  361.     a = -1;
  362.     while (x--) {
  363. #ifndef NLCHAR
  364.     int old;
  365.     old = a;            /* Previous character */
  366. #endif
  367.     if (zchin(n,&a) < 0) {        /* Read a character from the file */
  368.         z = -1;
  369.         break;
  370.     }
  371. #ifdef NLCHAR
  372.     if (a == (char) NLCHAR) break;    /* Single-character line terminator */
  373. #else
  374.     if (a == '\r') continue;    /* CRLF line terminator */
  375.     if (old == '\r') {
  376.         if (a == '\n') break;
  377.         else *s++ = '\r';
  378.     }
  379. #endif /* NLCHAR */
  380.     *s = a;
  381.     s++;
  382.     }
  383.     *s = '\0';
  384.     return(z);
  385. }
  386.  
  387. /*
  388.  * (PWP) (re)fill the buffered input buffer with data.  All file input
  389.  * should go through this routine, usually by calling the zminchar()
  390.  * macro (in ckcker.h).
  391.  */
  392.  
  393. /*
  394.  * Suggestion: if fread() returns 0, call ferror to find out what the
  395.  * problem was.  If it was not EOF, then return -2 instead of -1.
  396.  * Upper layers (getpkt function in ckcfns.c) should set cxseen flag
  397.  * if it gets -2 return from zminchar macro.
  398.  */
  399. int
  400. zinfill() {
  401.     int x;
  402.  
  403.     errno = 0;
  404.     zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]);
  405. #ifdef COMMENT
  406.     debug(F101,"zinfill fp","",fp[ZIFILE]);
  407.     debug(F101,"zinfill zincnt","",zincnt);
  408. #endif
  409.     if (zincnt == 0) {
  410. #ifndef UTEK
  411. #ifdef ferror
  412.     x = ferror(fp[ZIFILE]);
  413.     debug(F101,"zinfill errno","",errno);
  414.     debug(F101,"zinfill ferror","",x);
  415.     if (x) return(-2);
  416. #endif /* ferror */
  417. #else
  418.      x = feof(fp[ZIFILE]);
  419.      debug(F101,"zinfill errno","",errno);
  420.      debug(F101,"zinfill feof","",x);
  421.      if (!x && ferror(fp[ZIFILE])) return(-2);
  422. #endif /* UTEK */
  423.     return(-1);
  424.     }
  425.     zinptr = zinbuffer;       /* set pointer to beginning, (== &zinbuffer[0]) */
  426.     zincnt--;            /* one less char in buffer */
  427.     return((int)(*zinptr++) & 0377); /* because we return the first */
  428. }
  429.  
  430. /*  Z S O U T  --  Write a string out to the given file, buffered.  */
  431.  
  432. int
  433. zsout(n,s) int n; char *s; {
  434.     if (chkfn(n) < 1) return(-1); /* Keep this here, prevents memory faults */
  435. #ifdef COMMENT
  436.     while (*s) {            /* (unbuffered for debugging) */
  437.     write(fileno(fp[n]),s,1); ++s;
  438.     }
  439. #endif
  440. #ifdef COMMENT
  441.     return(fputs(s,fp[n]) == EOF ? -1 : 0);
  442. #else
  443. #ifdef COMMENT
  444.     if (n == ZSFILE)
  445.     return(write(fileno(fp[n]),s,strlen(s)));
  446.     else
  447. #endif
  448.         return(fputs(s,fp[n]) == EOF ? -1 : 0);
  449. #endif
  450. }
  451.  
  452. /*  Z S O U T L  --  Write string to file, with line terminator, buffered  */
  453.  
  454. int
  455. zsoutl(n,s) int n; char *s; {
  456.     /* if (chkfn(n) < 1) return(-1); */
  457.     if (fputs(s,fp[n]) == EOF) return(-1);
  458.     if (fputs("\n",fp[n]) == EOF) return(-1);
  459.     return(0);
  460. }
  461.  
  462. /*  Z S O U T X  --  Write x characters to file, unbuffered.  */
  463.  
  464. int
  465. zsoutx(n,s,x) int n, x; char *s; {
  466. #ifdef COMMENT
  467.     if (chkfn(n) < 1) return(-1);
  468.     return(write(fp[n]->_file,s,x));
  469. #endif
  470.     return(write(fileno(fp[n]),s,x) == x ? x : -1);
  471. }
  472.  
  473.  
  474. /*  Z C H O U T  --  Add a character to the given file.  */
  475.  
  476. /*  Should return 0 or greater on success, -1 on failure (e.g. disk full)  */
  477.  
  478. int
  479. #ifdef CK_ANSIC
  480. zchout(register int n, char c)
  481. #else
  482. zchout(n,c) register int n; char c;
  483. #endif /* CK_ANSIC */
  484. /* zchout() */ {
  485.     /* if (chkfn(n) < 1) return(-1); */
  486.     if (n == ZSFILE) {            /* Use unbuffered for session log */
  487. #ifdef COMMENT
  488.         return(write(fileno(fp[n]),&c,1) == 1 ? 0 : -1);
  489. #else
  490.     if (putc(c,fp[n]) == EOF)    /* If true, maybe there was an error */
  491.       return(ferror(fp[n])?-1:0);    /* Check to make sure */
  492.     else                /* Otherwise... */
  493.       return(0);            /* There was no error. */
  494. #endif
  495.     } else {                /* Buffered for everything else */
  496.     if (putc(c,fp[n]) == EOF)    /* If true, maybe there was an error */
  497.       return(ferror(fp[n])?-1:0);    /* Check to make sure */
  498.     else                /* Otherwise... */
  499.       return(0);            /* There was no error. */
  500.     }
  501. }
  502.  
  503. /* (PWP) buffered character output routine to speed up file IO */
  504.  
  505. int
  506. zoutdump() {
  507.     int x;
  508.     zoutptr = zoutbuffer;        /* Reset buffer pointer in all cases */
  509.     debug(F101,"zoutdump chars","",zoutcnt);
  510.     if (zoutcnt == 0) {            /* Nothing to output */
  511.     return(0);
  512.     } else if (zoutcnt < 0) {        /* Unexpected negative argument */
  513.     zoutcnt = 0;            /* Reset output buffer count */
  514.     return(-1);            /* and fail. */
  515.     }
  516.  
  517. /* Frank Prindle suggested that replacing this fwrite() by an fflush() */
  518. /* followed by a write() would improve the efficiency, especially when */
  519. /* writing to stdout.  Subsequent tests showed a 5-fold improvement!   */
  520. /* if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) {              */
  521.  
  522.     fflush(fp[ZOFILE]);
  523.     if ((x = write(fileno(fp[ZOFILE]),zoutbuffer,zoutcnt)) == zoutcnt) {
  524.     debug(F101,"zoutdump write ok","",zoutcnt);
  525.     zoutcnt = 0;            /* Reset output buffer count */
  526.     return(0);            /* write() worked OK */
  527.     } else {
  528.     debug(F101,"zoutdump write error","",errno);
  529.     debug(F101,"zoutdump write returns","",x);
  530.     zoutcnt = 0;            /* Reset output buffer count */
  531.     return(-1);            /* write() failed */
  532.     }
  533. }
  534.  
  535. /*  C H K F N  --  Internal function to verify file number is ok  */
  536.  
  537. /*
  538.  Returns:
  539.   -1: File number n is out of range
  540.    0: n is in range, but file is not open
  541.    1: n in range and file is open
  542. */
  543. int
  544. chkfn(n) int n; {
  545.     switch (n) {
  546.     case ZCTERM:
  547.     case ZSTDIO:
  548.     case ZIFILE:
  549.     case ZOFILE:
  550.     case ZDFILE:
  551.     case ZTFILE:
  552.     case ZPFILE:
  553.     case ZSFILE:
  554.     case ZSYSFN:
  555.         case ZRFILE:
  556.         case ZWFILE: break;
  557.     default:
  558.         debug(F101,"chkfn: file number out of range","",n);
  559.         fprintf(stderr,"?File number out of range - %d\n",n);
  560.         return(-1);
  561.     }
  562.     return( (fp[n] == NULL) ? 0 : 1 );
  563. }
  564.  
  565. /*  Z C H K I  --  Check if input file exists and is readable  */
  566.  
  567. /*
  568.   Returns:
  569.    >= 0 if the file can be read (returns the size).
  570.      -1 if file doesn't exist or can't be accessed,
  571.      -2 if file exists but is not readable (e.g. a directory file).
  572.      -3 if file exists but protected against read access.
  573. */
  574. /*
  575.  For Berkeley Unix, a file must be of type "regular" to be readable.
  576.  Directory files, special files, and symbolic links are not readable.
  577. */
  578. long
  579. zchki(name) char *name; {
  580.     struct stat buf;
  581.     int x;
  582.  
  583.     x = stat(name,&buf);
  584.     if (x < 0) {
  585.     debug(F111,"zchki stat fails",name,errno);
  586.     return(-1);
  587.     }
  588.     if (!S_ISREG (buf.st_mode)) {    /* Must be regular file */
  589.     debug(F111,"zchki skipping:",name,x);
  590.     return(-2);
  591.     }
  592.     debug(F111,"zchki stat ok:",name,x);
  593.  
  594.     if ((x = access(name,R_OK)) < 0) {     /* Is the file accessible? */
  595.     debug(F111," access failed:",name,x); /* No */
  596.         return(-3);
  597.     } else {
  598.     iflen = buf.st_size;              /* Yes, remember size */
  599.     strncpy(nambuf,name,MAXNAMLEN);          /* and name globally. */
  600.     debug(F111," access ok:",name,(int) iflen);
  601.     return( (iflen > -1L) ? iflen : 0L );
  602.     }
  603. }
  604.  
  605. /*  Z C H K O  --  Check if output file can be created  */
  606.  
  607. /*
  608.  Returns -1 if write permission for the file would be denied, 0 otherwise.
  609. */
  610. int zchko(name) char *name; {
  611.     int i, x;
  612.     char *s;
  613.  
  614.     if (!name) return(-1);        /* Watch out for null pointer. */
  615.     x = strlen(name);            /* Get length of filename */
  616.     debug(F101," length","",x);
  617.     s = malloc(x+3);            /* Must copy because we can't */
  618.     if (!s) {                /* write into our argument. */
  619.     fprintf(stderr,"Malloc error 46\n");
  620.     return(-1);
  621.     }
  622.     strcpy(s,name);
  623.  
  624.     for (i = x; i > 0; i--)        /* Strip filename from right. */
  625.       if (s[i-1] == '\\') break;
  626.     debug(F101," i","",i);
  627.  
  628. #ifdef COMMENT
  629. /* X/OPEN XPG3-compliant systems fail if argument ends with "/"...  */
  630.     if (i == 0)                /* If no path, use current directory */
  631.       strcpy(s,".\\");            
  632.     else                /* Otherwise, use given one. */
  633.       s[i] = '\0';
  634. #else
  635. /* So now we use "path/." if path given, or "." if no path given. */
  636.     s[i++] = '.';            /* Append "." to path. */
  637.     s[i] = '\0';
  638. #endif /* COMMENT */
  639.  
  640.     x = access(s,W_OK);            /* Check access of path. */
  641.     if (x < 0)
  642.       debug(F111,"zchko access failed:",s,errno);
  643.     else
  644.       debug(F111,"zchko access ok:",s,x);
  645.     free(s);                /* Free temporary storage */
  646.     return((x < 0) ? -1 : 0);        /* and return. */
  647. }
  648.  
  649. /*  Z D E L E T  --  Delete the named file.  */
  650.  
  651. int
  652. zdelet(name) char *name; {
  653.     return(unlink(name));
  654. }
  655.  
  656.  
  657. /*  Z R T O L  --  Convert remote filename into local form  */
  658.  
  659. /*  For UNIX, this means changing uppercase letters to lowercase.  */
  660. /*  For GEM, this means 8.3 filenames and other DOS baggage too */
  661.  
  662. VOID
  663. zrtol(name,name2) char *name, *name2; {
  664.     zltor(name, name2);
  665.     for ( ; *name != '\0'; name++) {
  666.         *name2++ = isupper(*name) ? tolower(*name) : *name;
  667.     }
  668.     *name2 = '\0';
  669.     debug(F110,"zrtol:",name2,0);
  670. }
  671.  
  672.  
  673. /*  Z S T R I P  --  Strip device & directory name from file specification */
  674.  
  675. /*  Strip pathname from filename "name", return pointer to result in name2 */
  676.  
  677. static char work[257];        /* buffer for use by zstrip and zltor */
  678.  
  679. VOID
  680. zstrip(name,name2) char *name, **name2; {
  681.     char *cp, *pp;
  682.     debug(F110,"zstrip before",name,0);
  683.     pp = work;
  684. #ifdef DTILDE
  685.     if (*name == '~') name++;
  686. #endif
  687.     for (cp = name; *cp != '\0'; cp++) {
  688.         if (*cp == '\\')
  689.       pp = work;
  690.     else
  691.       *pp++ = *cp;
  692.     }
  693.     *pp = '\0';                /* Terminate the string */
  694.     *name2 = work;
  695.     debug(F110,"zstrip after",*name2,0);
  696. }
  697.  
  698. /*  Z L T O R  --  Local TO Remote */
  699.  
  700. zltor(name,name2) char *name, *name2; {
  701.     char *cp, *pp;
  702.     int dc = 0;
  703.  
  704.     debug(F110,"zltor",name,0);
  705.     pp = work;
  706.     for (cp = name; *cp != '\0'; cp++) {    /* strip path name */
  707.         if (*cp == '\\') {
  708.         dc = 0;
  709.         pp = work;
  710.     }
  711.     else if (islower(*cp)) *pp++ = toupper(*cp); /* Uppercase letters */
  712.     else if (*cp == '~') *pp++ = 'X';    /* Change tilde to 'X' */
  713.     else if (*cp == '#') *pp++ = 'X';    /* Change number sign to 'X' */
  714.     else if ((*cp == '.') && (++dc > 1)) *pp++ = 'X'; /* & extra dots */
  715.     else if (dc == 0 && (cp - name) >= 8) *pp++ = '.';  /* base too long */
  716.     else *pp++ = *cp;
  717.     }
  718.     *pp = '\0';                /* Tie it off. */
  719.     cp = name2;                /* If nothing before dot, */
  720.     if (*work == '.') *cp++ = 'X';    /* insert 'X' */
  721.     strcpy(cp,work);
  722.     debug(F110," name2",name2,0);
  723. }
  724.  
  725.  
  726. /*  Z C H D I R  --  Change directory  */
  727. /*
  728.   Call with:
  729.     dirnam = pointer to name of directory to change to,
  730.       which may be "" or NULL to indicate user's home directory.
  731.   Returns:
  732.     0 on failure
  733.     1 on success
  734. */
  735. int
  736. zchdir(dirnam) char *dirnam; {
  737.     char *hd, *sp, *p;
  738.  
  739.     debug(F110,"zchdir",dirnam,0);
  740.     if (dirnam == NULL || dirnam == "" || *dirnam == '\0') /* If arg is null */
  741.       dirnam = getenv("HOME");        /* use user's home directory. */
  742.     if (dirnam == NULL)
  743.     return(0);
  744.     sp = dirnam;
  745.     debug(F110,"zchdir 2",dirnam,0);
  746.  
  747. #ifdef DTILDE
  748.     hd = tilde_expand(dirnam);        /* Attempt to expand tilde */
  749.     if (*hd == '\0') hd = dirnam;    /* in directory name. */
  750. #else
  751.     hd = dirnam;
  752. #endif /* DTILDE */
  753.     debug(F110,"zchdir 3",hd,0);
  754.     if (toschdir(hd) == 0) return(1);    /* Try to cd */ /* (===OS2===) */
  755.     p = sp;                /* Failed, lowercase it. */
  756.     while (*p) {
  757.     if (isupper(*p)) *p = tolower(*p);
  758.     p++;
  759.     }
  760.     debug(F110,"zchdir 4",hd,0);
  761. #ifdef DTILDE
  762.     hd = tilde_expand(sp);        /* Try again to expand tilde */
  763.     if (*hd == '\0') hd = sp;
  764. #else
  765.     hd = sp;                /* Point to result */
  766. #endif
  767.     debug(F110,"zchdir 5",hd,0);
  768.     return((toschdir(hd) == 0) ? 1 : 0);
  769. }
  770.  
  771. /*  T O S C H D I R  -- Change directory under TOS */
  772. /*
  773.  *  Return 0 if it worked, 1 otherwise
  774.  */
  775. int
  776. toschdir(dirnam) char *dirnam; {
  777.     char *path;
  778.     int drive;
  779.  
  780.     if (path = strchr(dirnam, ':')) {
  781.     drive = toupper(*--path) - 'A';
  782.     if (drive < 0 || drive > 25)
  783.         return(1);
  784.     Dsetdrv(drive);
  785.     path += 2;
  786.     } else
  787.     path = dirnam;
  788.     return((int) Dsetpath(path));
  789. }
  790.  
  791. /*  Z H O M E  --  Return pointer to user's home directory  */
  792.  
  793. char *
  794. zhome() {
  795.     char *home = NULL;
  796.     home = getenv("HOME");
  797.     return(home ? home : ".");        /* (===OS2===) */
  798. }
  799.  
  800. /*  Z G T D I R  --  Return pointer to user's current directory  */
  801.  
  802. #ifdef MAXPATHLEN
  803. #define CWDBL MAXPATHLEN
  804. #else
  805. #define CWDBL 100
  806. #endif
  807. static char cwdbuf[CWDBL+1];
  808.  
  809. char *
  810. zgtdir() {
  811.     int drive;
  812.     char *p;
  813.     int c;
  814.  
  815.     p = cwdbuf;
  816.     drive = Dgetdrv();
  817.     *p++ = (drive + 'a');            /* Drive letter */
  818.     *p++ = ':';
  819.     Dgetpath(p, 0);                /* Rest of path */
  820.     for (p = cwdbuf; c = *p; p++) {
  821.     if (isupper(c))
  822.         *p = _tolower(c);
  823.     }
  824.     if (strlen(cwdbuf) == 2)
  825.     strcat(cwdbuf, "\\");
  826.  
  827.     return(cwdbuf);
  828. }
  829.  
  830. /*  Z X C M D -- Run a system command so its output can be read like a file */
  831.  
  832. int
  833. zxcmd(filnum,comand) int filnum; char *comand; {
  834.     char *name;
  835.     char *cmdbuf;
  836.     extern char *tempnam();
  837.  
  838.     if (! pipename[0])        /* If no pipe has been used yet */
  839.     strcpy(pipename, tempnam(NULL, "ck"));    /* Get a name for the pipe */
  840.  
  841.     cmdbuf = malloc(strlen(comand) + 2 + strlen(pipename) + 1);
  842.     strcpy(cmdbuf, comand);
  843.     strcat(cmdbuf, " >");
  844.     strcat(cmdbuf, pipename);
  845.     tossystem(cmdbuf);
  846.     free(cmdbuf);
  847.  
  848.     fp[ZIFILE] = fopen(pipename,"r");    /* open a stream for it */
  849.     fp[ZSYSFN] = fp[ZIFILE];        /* Remember. */
  850.     debug(F101,"zxcmd fp","", fp[ZSYSFN]);
  851.     return(1);
  852. }
  853.  
  854. /*  Z C L O S F  - wait for the child fork to terminate and close the pipe. */
  855.  
  856. int
  857. zclosf(filnum) int filnum; {
  858.     debug(F101,"zclosf filnum","",filnum);
  859.     debug(F101,"zclosf fp[filnum]","",(int) fp[filnum]);
  860.     debug(F101,"zclosf fp[ZSYSFN]","",(int) fp[ZSYSFN]);
  861.  
  862.     fclose(fp[filnum]);
  863.     zdelet(pipename);
  864.     fp[filnum] = fp[ZSYSFN] = NULL;
  865.     return(1);
  866. }
  867.  
  868. /*  Z X P A N D  --  Expand a wildcard string into an array of strings  */
  869. /*
  870.   Returns the number of files that match fn1, with data structures set up
  871.   so that first file (if any) will be returned by the next znext() call.
  872.   Depends on external variable wildxpand: 0 means we expand wildcards
  873.   internally, nonzero means we call the shell to do it.
  874. */
  875.  
  876. int
  877. zxpand(fn) char *fn; {
  878.     char *p;
  879.  
  880. #ifdef DTILDE                /* Built with tilde-expansion? */
  881.     char *tnam;
  882. #endif /* DTILDE */
  883.     debug(F111,"zxpand entry",fn,wildxpand);
  884. #ifdef DTILDE                /* Built with tilde-expansion? */
  885.     if (*fn == '~') {            /* Starts with tilde? */
  886.     tnam = tilde_expand(fn);    /* Try to expand it. */
  887.     if (tnam) fn = tnam;
  888.     }
  889.     debug(F110,"zxpand after tilde_x",fn,0);
  890. #endif /* DTILDE */
  891.     if (wildxpand)            /* Who is expanding wildcards? */
  892.       fcount = shxpand(fn,mtchs,MAXWLD); /* Shell */
  893.     else
  894.       fcount = fgen(fn,mtchs,MAXWLD);    /* Kermit */
  895.     if (fcount > 0) {
  896.     mtchptr = mtchs;        /* Save pointer for next. */
  897.     }
  898.     if (fcount > 0) {
  899.     debug(F111,"zxpand ok",mtchs[0],fcount);
  900.     return(fcount);
  901.     }
  902.     debug(F111,"zxpand fgen1",fn,fcount); /* Didn't get one, or got too many */
  903.     p = malloc(strlen(fn) + 10);    /* Make space */
  904.     if (!p) return(0);
  905.     zrtol(fn,p);            /* Try again, maybe lowercase */
  906.     if (wildxpand)
  907.       fcount = shxpand(p,mtchs,MAXWLD); /* Shell */
  908.     else
  909.       fcount = fgen(p,mtchs,MAXWLD);    /* Kermit */
  910.     if (fcount > 0) {            /* Got one? */
  911.     mtchptr = mtchs;        /* Save pointer for next. */
  912.     debug(F111,"zxpand fgen2 ok",mtchs[0],fcount);
  913.     } else debug(F111,"zxpand 2 not ok",p,fcount);
  914.     free(p);
  915.     return(fcount);
  916. }
  917.  
  918.  
  919. /*  Z N E X T  --  Get name of next file from list created by zxpand(). */
  920. /*
  921.  Returns >0 if there's another file, with its name copied into the arg string,
  922.  or 0 if no more files in list.
  923. */
  924. int
  925. znext(fn) char *fn; {
  926.     if (fcount-- > 0) {
  927.     strcpy(fn,*mtchptr);
  928.     free(*mtchptr++);
  929.     }
  930.     else *fn = '\0';
  931.     debug(F111,"znext",fn,fcount+1);
  932.     return(fcount+1);
  933. }
  934.  
  935.  
  936. /*  Z C H K S P A  --  Check if there is enough space to store the file  */
  937.  
  938. /*
  939.  Call with file specification f, size n in bytes.
  940.  Returns -1 on error, 0 if not enough space, 1 if enough space.
  941. */
  942. struct disk_info {
  943.     unsigned long di_free;        /* Free allocation units */
  944.     unsigned long di_many;        /* Allocation units per disk */
  945.     unsigned long di_ssize;        /* Sector size (in bytes) */
  946.     unsigned long di_spau;        /* Sectors per allocation unit */
  947. };
  948.  
  949. int
  950. zchkspa(f,n) char *f; long n; {
  951.     int drive;
  952.     struct disk_info disk;
  953.     long bytesleft;
  954.  
  955.     drive = Dgetdrv();
  956.     Dfree(&disk, 0);            /* Check current drive */
  957.     bytesleft = disk.di_free * disk.di_spau * disk.di_ssize;
  958.     if (n > bytesleft)
  959.     return(0);
  960.  
  961.     return(1);                /* Always say OK. */
  962. }
  963.  
  964.  
  965. /*  Z N E W N  --  Make a new name for the given file  */
  966.  
  967. /*
  968.   Given the name, fn, of a file that already exists, this function builds a
  969.   new name of the form "<oldname><n>" where <oldname> is the (possibly
  970.   truncated) argument name (fn) and <n> is a version number, one higher
  971.   than any existing version number for that file, up to 99.  If the
  972.   constructed name is too long for the system's maximum, enough characters
  973.   are truncated from the end of <fn> to allow the version number to fit.
  974.   If no free version numbers exist between 0 and 99, a version number of
  975.   "xx" is used.  Returns a pointer to the new name in argument s.
  976. */
  977.  
  978. VOID
  979. znewn(fn,s) char *fn, **s; {
  980. #define ZNEWNBL 13            /* Name buffer length */
  981. #define ZNEWNMD 2            /* Max digits for version number */
  982. #define ZNEWEXT 3            /* Max chars in extension */
  983. #define ZNEWPRE 8            /* Max chars in prefix */
  984. #define ZNEWTRC (ZNEWPRE-ZNEWNMD)    /* Max chars in truncated prefix */
  985.     static char buf[ZNEWNBL+1];
  986.     char    prefix[ZNEWPRE+1];
  987.     char    extension[ZNEWEXT+1];
  988.     char    *bp, *xp;
  989.     int        len, n, t, d;
  990.  
  991.     bp = strchr(fn, '.');        /* First save the extension */
  992.     if (bp) {
  993.     strncpy(extension, bp+1, ZNEWEXT);
  994.     extension[ZNEWEXT] = '\0';
  995.     } else
  996.     extension[0] = '\0';
  997.  
  998.     strncpy(prefix, fn, ZNEWPRE);    /* Now save the prefix */
  999.     prefix[ZNEWPRE] = '\0';
  1000.     bp = strchr(prefix, '.');        /* Seems wasteful to do this again */
  1001.     if (bp)
  1002.     *bp = '\0';            /* Get rid of period and extension */
  1003.  
  1004.     len = strlen(prefix);        /* Pad to six bytes with zeroes */
  1005.     for (; len < ZNEWTRC; len++)
  1006.     prefix[len] = '0';
  1007.     prefix[ZNEWTRC] = '\0';
  1008.  
  1009.     strcpy(buf, prefix);        /* Create wildcard string */
  1010.     strcat(buf, "*.");
  1011.     strcat(buf, extension);
  1012.     n = zxpand(buf);            /* Expand the resulting wild name */
  1013.  
  1014.     d = -1;                /* Initialize lowest unused */
  1015.     while (n-- > 0) {            /* Find any existing name<n> files */
  1016.     xp = *mtchptr++;        /* Point at matching name */
  1017.     if ( (strchr(xp, '.') - xp) > ZNEWTRC &&
  1018.     isdigit(xp[ZNEWTRC]) ) {    /* Look for <n> prefix end */
  1019.         t = atoi(&xp[ZNEWTRC]);    /* Get it */
  1020.         if (t > d) d = t;        /* Save d = highest version number */
  1021.     }
  1022.     }
  1023.     d++;                /* Lowest unused version number */
  1024.  
  1025.     strcpy(buf, prefix);        /* First copy truncated prefix */
  1026.     for (len = ZNEWPRE-1; len >= ZNEWTRC; len--) {
  1027.     buf[len] = '0' + d % 10;
  1028.     d /= 10;
  1029.     }
  1030.     if (d) {                /* If it didn't fit use 'X' */
  1031.     for (len = ZNEWPRE-1; len >= ZNEWTRC; len--)
  1032.         buf[len] = 'X';
  1033.     }
  1034.     buf[ZNEWPRE] = '.';            /* Don't forget the period */
  1035.     buf[ZNEWPRE+1] = '\0';
  1036.     strcat(buf, extension);        /* Tack on extension */
  1037.     *s = buf;                /* Tell caller buffer location */
  1038.     return;
  1039. }
  1040.  
  1041. /*  Z R E N A M E  --  Rename a file  */
  1042.  
  1043. /*  Note, link() and unlink() are used because rename() is not available  */
  1044. /*  in some versions of UNIX.   */
  1045. /*  Call with old and new names */
  1046. /*  Returns 0 on success, -1 on failure. */
  1047.  
  1048. int
  1049. zrename(old,new) char *old, *new; {
  1050.     return(Frename(0, old, new) != 0 ? -1 : 0);
  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. int
  1061. zsattr(xx) struct zattr *xx; {
  1062.     long k;
  1063.     char *zfcdat();
  1064.  
  1065.     k = iflen % 1024L;            /* File length in K */
  1066.     if (k != 0L) k = 1L;
  1067.     xx->lengthk = (iflen / 1024L) + k;
  1068.     xx->type.len = 0;            /* File type can't be filled in here */
  1069.     xx->type.val = "";
  1070.     if (*nambuf) {
  1071.     xx->date.val = zfcdat(nambuf);    /* File creation date */
  1072.     xx->date.len = strlen(xx->date.val);
  1073.     } else {
  1074.     xx->date.len = 0;
  1075.     xx->date.val = "";
  1076.     }
  1077.     xx->creator.len = 0;        /* File creator */
  1078.     xx->creator.val = "";
  1079.     xx->account.len = 0;        /* File account */
  1080.     xx->account.val = "";
  1081.     xx->area.len = 0;            /* File area */
  1082.     xx->area.val = "";
  1083.     xx->password.len = 0;        /* Area password */
  1084.     xx->password.val = "";
  1085.     xx->blksize = -1L;            /* File blocksize */
  1086.     xx->xaccess.len = 0;        /* File access */
  1087.     xx->xaccess.val = "";
  1088.     xx->encoding.len = 0;        /* Transfer syntax */
  1089.     xx->encoding.val = 0;
  1090.     xx->disp.len = 0;            /* Disposition upon arrival */
  1091.     xx->disp.val = "";
  1092.     xx->lprotect.len = 0;        /* Local protection */
  1093.     xx->lprotect.val = "";
  1094.     xx->gprotect.len = 0;        /* Generic protection */
  1095.     xx->gprotect.val = "";
  1096.     xx->systemid.len = 2;        /* System ID */
  1097.     xx->systemid.val = "U1";
  1098.     xx->recfm.len = 0;            /* Record format */
  1099.     xx->recfm.val = "";
  1100.     xx->sysparam.len = 0;        /* System-dependent parameters */
  1101.     xx->sysparam.val = "";
  1102.     xx->length = iflen;            /* Length */
  1103.     return(0);
  1104. }
  1105.  
  1106. /* Z F C D A T  --  Get file creation date */
  1107. /*
  1108.   Call with pointer to filename.
  1109.   On success, returns pointer to creation date in yyyymmdd hh:mm:ss format.
  1110.   On failure, returns pointer to null string.
  1111. */
  1112. static char datbuf[40];
  1113.  
  1114. char * 
  1115. zfcdat(name) char *name; {
  1116.  
  1117. #ifdef TIMESTAMP
  1118.     struct stat buffer;
  1119.     struct tm *time_stamp, *localtime();
  1120.     int yy, ss;
  1121.  
  1122.     datbuf[0] = '\0';
  1123.     if(stat(name,&buffer) != 0) {
  1124.     debug(F110,"zfcdat stat failed",name,0);
  1125.     return("");
  1126.     }
  1127.     time_stamp = localtime(&(buffer.st_mtime));
  1128.     yy = time_stamp->tm_year;
  1129.     if (yy < 100)            /* In case it returns 2-digit year? */
  1130.       yy += 1900;
  1131.     if (yy < 0 || yy > 9999) {        /* Make sure year is ok */
  1132.     debug(F110,"zfcdat date failed",name,0);
  1133.     return("");
  1134.     }
  1135.     if (time_stamp->tm_mon  < 0 || time_stamp->tm_mon  > 11)
  1136.       return("");
  1137.     if (time_stamp->tm_mday < 0 || time_stamp->tm_mday > 31)
  1138.       return("");
  1139.     if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 23)
  1140.       return("");
  1141.     if (time_stamp->tm_min  < 0 || time_stamp->tm_min  > 59)
  1142.       return("");
  1143.     ss = time_stamp->tm_sec;        /* Seconds */
  1144.     if (ss < 0 || ss  > 59)        /* Some systems give a BIG number */
  1145.       ss = 0;
  1146.     sprintf(datbuf,
  1147.         "%04d%02d%02d %02d:%02d:%02d",
  1148.         yy,
  1149.           time_stamp->tm_mon + 1,
  1150.           time_stamp->tm_mday,
  1151.           time_stamp->tm_hour,
  1152.         time_stamp->tm_min
  1153.         , ss
  1154.         );
  1155.     yy = strlen(datbuf);
  1156.     debug(F111,"zfcdat",datbuf,yy);
  1157.     if (yy > 17) datbuf[17] = '\0';
  1158.     return(datbuf);
  1159. #else
  1160.     return("");
  1161. #endif /* TIMESTAMP */
  1162. }
  1163.  
  1164. /* Z S T I M E  --  Set creation date for incoming file */
  1165. /*
  1166.  Call with:
  1167.  f  = pointer to name of existing file.
  1168.  yy = pointer to a Kermit file attribute structure in which yy->date.val
  1169.       is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00.
  1170.  x  = is a function code: 0 means to set the file's creation date as given.
  1171.       1 means compare the given date with the file creation date.
  1172.  Returns:
  1173.  -1 on any kind of error.
  1174.   0 if x is 0 and the file date was set successfully.
  1175.   0 if x is 1 and date from attribute structure <= file creation date.
  1176.   1 if x is 1 and date from attribute structure > file creation date.
  1177. */
  1178. int
  1179. zstime(f,yy,x) char *f; struct zattr *yy; int x; {
  1180.     int r = -1;                /* return code */
  1181. /*
  1182.   It is ifdef'd TIMESTAMP because it may not work on V7. bk@kullmar.se.
  1183. */
  1184. #ifdef TIMESTAMP
  1185. /*
  1186.   To do: adapt code from OS-9 Kermit's ck9fio.c zstime function, which
  1187.   is more flexible, allowing [yy]yymmdd[ hh:mm[:ss]].
  1188. */
  1189.     extern int ftime();
  1190.     long tm, days;
  1191.     int i, n, isleapyear, stat(), utime();
  1192.                    /*       J  F  M  A   M   J   J   A   S   O   N   D   */
  1193.                    /*      31 28 31 30  31  30  31  31  30  31  30  31   */
  1194.     static
  1195.     int monthdays [13] = {  0,0,31,59,90,120,151,181,212,243,273,304,334 };
  1196.     char s[5], *getenv();
  1197.     extern struct tm *localtime();
  1198.     struct stat sb;
  1199.     struct utimbuf {
  1200.       time_t atime;        /* New access time */
  1201.       time_t mtime;        /* New modification time */
  1202.     } tp;
  1203.  
  1204.     debug(F110,"zstime",f,0);
  1205.  
  1206.     if ((yy->date.len == 0)
  1207.         || (yy->date.len != 17)
  1208.         || (yy->date.val[8] != ' ')
  1209.         || (yy->date.val[11] != ':')
  1210.         || (yy->date.val[14] != ':') ) {
  1211.         debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1212.         return(-1);
  1213.     }
  1214.     debug(F111,"zstime date check 1",yy->date.val,yy->date.len);
  1215.     for(i = 0; i < 8; i++) {
  1216.     if (!isdigit(yy->date.val[i])) {
  1217.         debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1218.         return(-1);
  1219.     }
  1220.     }
  1221.     debug(F111,"zstime date check 2",yy->date.val,yy->date.len);
  1222.     i++;
  1223.  
  1224.     for (; i < 16; i += 3) {
  1225.     if ((!isdigit(yy->date.val[i])) || (!isdigit(yy->date.val[i + 1]))) {
  1226.         debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1227.         return(-1);
  1228.     }
  1229.     }
  1230.     debug(F111,"zstime date check 3",yy->date.val,yy->date.len);
  1231.  
  1232.     s[4] = '\0';
  1233.     for (i = 0; i < 4; i++)    /* Fix the year */
  1234.       s[i] = yy->date.val[i];
  1235.  
  1236.     debug(F110,"zstime year",s,0);
  1237.  
  1238.     n = atoi(s);
  1239.  
  1240.     debug(F111,"zstime year",s,n);
  1241.  
  1242. /* Previous year's leap days. This won't work after year 2100, */
  1243. /* I don't care about that! */
  1244.     isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
  1245.     days = (long) (n - 1970) * 365;
  1246.     days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
  1247.  
  1248.     s[2] = '\0';
  1249.  
  1250.     for (i = 4 ; i < 16; i += 2) {
  1251.     s[0] = yy->date.val[i];
  1252.     s[1] = yy->date.val[i + 1];
  1253.     n = atoi(s);
  1254.     debug(F110,"zstime entering switch",s,0);
  1255.     switch (i) {
  1256.       case 4:            /* MM: month */
  1257.         if ((n < 1 ) || ( n > 12)) {
  1258.         debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1259.         return(-1);
  1260.         }
  1261.         days += monthdays [n];
  1262.         if (isleapyear && n > 2)
  1263.           ++days;
  1264.         continue;
  1265.  
  1266.       case 6:            /* DD: day */
  1267.         if ((n < 1 ) || ( n > 31)) {
  1268.         debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1269.         return(-1);
  1270.         }
  1271.         tm = (days + n - 1) * 24L * 60L * 60L;
  1272.         i++;            /* Skip the space */
  1273.         continue;
  1274.  
  1275.       case 9:            /* hh: hour */
  1276.         if ((n < 0 ) || ( n > 23)) {
  1277.         debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1278.         return(-1);
  1279.         }
  1280.         tm += n * 60L * 60L;
  1281.         i++;            /* Skip the colon */
  1282.         continue;
  1283.  
  1284.       case 12:            /* mm: minute */
  1285.         if ((n < 0 ) || ( n > 59)) {
  1286.         debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1287.         return(-1);
  1288.         }
  1289.         tm += timezone - dstadjust;    /* Correct for time zone */
  1290.  
  1291.         tm += n * 60L;
  1292.         i++;            /* Skip the colon */
  1293.         continue;
  1294.  
  1295.       case 15:            /* ss: second */
  1296.         if ((n < 0 ) || ( n > 59)) {
  1297.         debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1298.         return(-1);
  1299.         }
  1300.         tm += n;
  1301.     }
  1302.     }
  1303.  
  1304.     debug(F111,"Attribute creation date ok ",yy->date.val,yy->date.len);
  1305.  
  1306.     if (stat(f,&sb)) {            /* Get the time for the file */
  1307.     debug(F110,"Can't stat file:",f,0);
  1308.     return(-1);
  1309.     }
  1310.     tp.mtime = tm;            /* Set modif. time to creation date */
  1311.     tp.atime = sb.st_atime;        /* Don't change the access time */
  1312.  
  1313.     switch (x) {            /* Execute desired function */
  1314.       case 0:                /* Set the creation date of the file */
  1315.     if (utime(f,&tp)) {        /* Fix modification time */
  1316.         debug(F110,"Can't set modification time for file: ",f,0);
  1317.         r = -1;
  1318.     } else  {
  1319.         debug(F110,"Modification time is set for file: ",f,0);
  1320.         r = 0;
  1321.     }
  1322.     break;
  1323.       case 1:                /* Compare the dates */
  1324.     debug(F111,"zstime compare",f,sb.st_atime);
  1325.     debug(F111,"zstime compare","packet",tm);
  1326.     if (sb.st_atime < tm) r = 0; else r = 1;
  1327.     break;
  1328.       default:                /* Error */
  1329.     r = -1;
  1330.     }
  1331. #endif /* TIMESTAMP */
  1332.     return(r);
  1333. }
  1334.  
  1335. /* Find initialization file. */
  1336.  
  1337. int
  1338. zkermini(line, flag, def) char *line; int flag; char *def; {
  1339. #ifdef NOICP
  1340.     return(0);
  1341. #else
  1342.     extern char *homdir, *lp;
  1343.  
  1344.     homdir = zhome();
  1345.     lp = line;
  1346.     strcpy(lp, def);
  1347.  
  1348.     if (flag)                /* If init file name from cmd line */
  1349.     ;                /* use it */
  1350.     else if (zchki(lp) >= 0)        /* Then try current directory */
  1351.     ;                /* If it exists, use it */
  1352.     else if (homdir) {            /* Then try home directory (if any) */
  1353.     strcpy(lp, homdir);
  1354.     strcat(lp, "\\");
  1355.     strcat(lp, def);
  1356.     if (zchki(lp) >= 0)        /* If it exists, use it */
  1357.         return(0);
  1358.     } else {                /* Then try root directory */
  1359.     strcpy(lp, "\\");
  1360.     strcat(lp, def);
  1361.     }
  1362.     return(0);
  1363. #endif /* NOICP */
  1364. }
  1365.  
  1366. #ifndef NOFRILLS
  1367. int
  1368. zmail(p,f) char *p; char *f; {        /* Send file f as mail to address p */
  1369.     return(0);
  1370. }
  1371. #endif /* NOFRILLS */
  1372.  
  1373. #ifndef NOFRILLS
  1374. int
  1375. zprint(p,f) char *p; char *f; {        /* Print file f with options p */
  1376.     sprintf(zmbuf,"pr %s >prn:", f);    /* Construct print command */
  1377.     zsyscmd(zmbuf);
  1378.     return(0);
  1379. }
  1380. #endif /* NOFRILLS */
  1381.  
  1382. /*
  1383.   Wildcard expansion functions.  C-Kermit used to do this itself (see #else
  1384.   part...).  New code (version 5A, 1990-91) just asks UNIX to do it.
  1385.   This lets users use the wildcard expansion features of their favorite shell.
  1386.   Operation is slower because of the forking & piping, but flexibility is
  1387.   greater and program is smaller.
  1388. */
  1389.  
  1390. static char scratch[MAXPATH+4];        /* Used by both methods */
  1391.  
  1392. static int oldmtchs = 0;        /* Let shell (ls) expand them. */
  1393. #ifdef COMMENT
  1394. static char *lscmd = "/bin/ls -d";     /* Command to use. */
  1395. #else
  1396. static char *lscmd = "echo";        /* Command to use. */
  1397. #endif /* COMMENT */
  1398.  
  1399. int
  1400. shxpand(pat,namlst,len) char *pat, *namlst[]; int len; {
  1401.     char *fgbuf = NULL;            /* Buffer for forming ls command */
  1402.     char *p, *q;            /* Workers */
  1403.     int i, x, retcode; char c;        /* ... */
  1404.  
  1405.     x = strlen(pat) + strlen(lscmd) + 3; /* Length of ls command */
  1406.     for (i = 0; i < oldmtchs; i++)    /* Free previous file list */
  1407.       free(namlst[i]);
  1408.     fgbuf = malloc(x);            /* Get buffer for command */
  1409.     if (!fgbuf) return(-1);        /* Fail if cannot */
  1410.     sprintf(fgbuf,"%s %s",lscmd,pat);    /* Form the command */
  1411.     zxcmd(ZIFILE,fgbuf);        /* Start the command */
  1412.     i = 0;                /* File counter */
  1413.     p = scratch;            /* Point to scratch area */
  1414.     retcode = -1;            /* Assume failure */
  1415.     while ((c = zminchar()) != -1) {    /* Read characters from command */
  1416.     if (c == ' ' || c == '\n') {    /* Got newline or space? */
  1417.         *p = '\0';            /* Yes, terminate string */
  1418.         p = scratch;        /* Point back to beginning */
  1419.         if (zchki(p) == -1)        /* Does file exist? */
  1420.           continue;            /* No, continue */
  1421.         x = strlen(p);        /* Yes, get length of name */
  1422.         q = malloc(x+1);        /* Allocate space for it */
  1423.         if (!q) goto shxfin;    /* Fail if space can't be obtained */
  1424.         strcpy(q,scratch);        /* Copy name to space */
  1425.         namlst[i++] = q;        /* Copy pointer to name into array */
  1426.         if (i > len) goto shxfin;    /* Fail if too many */
  1427.     } else {            /* Regular character */
  1428.         *p++ = c;            /* Copy it into scratch area */
  1429.     }
  1430.     }
  1431.     retcode = i;            /* Return number of matching files */
  1432. shxfin:                    /* Common exit point */
  1433.     free(fgbuf);            /* Free command buffer */
  1434.     zclosf(ZIFILE);            /* Delete the command fork. */
  1435.     oldmtchs = i;            /* Remember how many files */
  1436.     return(retcode);
  1437. }
  1438.  
  1439. /* Directory Functions for Unix, written by Jeff Damens, CUCCA, 1984. */
  1440.  
  1441. /* Define the size of the string space for filename expansion. */
  1442.  
  1443. #ifndef DYNAMIC
  1444. #ifdef PROVX1
  1445. #define SSPACE 500
  1446. #else
  1447. #ifdef BSD29
  1448. #define SSPACE 500
  1449. #else
  1450. #ifdef pdp11
  1451. #define SSPACE 500
  1452. #else
  1453. #ifdef aegis
  1454. #define SSPACE 10000            /* size of string-generating buffer */
  1455. static char bslash;            /* backslash character if active */
  1456. #else                    /* Default static buffer size */
  1457. #define SSPACE 2000            /* size of string-generating buffer */
  1458. #endif /* aegis */
  1459. #endif /* pdp11 */
  1460. #endif /* BSD29 */
  1461. #endif /* PROVX1 */
  1462. static char sspace[SSPACE];             /* buffer for generating filenames */
  1463. #else /* DYNAMIC */
  1464. #define SSPACE 10000
  1465. static char *sspace = (char *)0;
  1466. #endif /* DYNAMIC */
  1467. static int ssplen = SSPACE;        /* length of string space buffer */
  1468.  
  1469. static char *freeptr,**resptr;             /* copies of caller's arguments */
  1470. static int remlen;                      /* remaining length in caller's array*/
  1471. static int numfnd;                      /* number of matches found */
  1472.  
  1473. /*
  1474.  * splitpath:
  1475.  *  takes a string and splits the slash-separated portions into
  1476.  *  a list of path structures.  Returns the head of the list.  The
  1477.  *  structures are allocated by malloc, so they must be freed.
  1478.  *  Splitpath is used internally by the filename generator.
  1479.  *
  1480.  * Input: A string.
  1481.  * Returns: A linked list of the slash-separated segments of the input.
  1482.  */
  1483.  
  1484. struct path *
  1485. splitpath(p) char *p; {
  1486.     struct path *head,*cur,*prv;
  1487.     int i;
  1488.  
  1489.     debug(F110,"splitpath",p,0);
  1490.  
  1491.     head = prv = NULL;
  1492.     if (*p == '\\') p++;            /* skip leading slash */
  1493.     while (*p != '\0') {
  1494.     cur = (struct path *) malloc(sizeof (struct path));
  1495.     debug(F101,"splitpath malloc","",cur);
  1496.     if (cur == NULL)
  1497.       fatal("malloc fails in splitpath()");
  1498.     cur -> fwd = NULL;
  1499.     if (head == NULL)
  1500.       head = cur;
  1501.     else
  1502.       prv -> fwd = cur;        /* link into chain */
  1503.     prv = cur;
  1504.  
  1505.     for (i=0;
  1506.     i < MAXNAMLEN && *p != '\\' && *p != ':' && *p != '\0';
  1507.     i++)
  1508.         cur -> npart[i] = *p++;
  1509.         if ( *p == ':' ) {
  1510.             cur -> npart[i++] = *p++;
  1511.             if ( *p != '\\' )
  1512.                 cur -> npart[i++] = '.';
  1513.         }
  1514.     cur -> npart[i] = '\0';        /* end this segment */
  1515.     if (i >= MAXNAMLEN)
  1516.         while (*p != '\\' && *p != '\0') p++;
  1517.     if (*p == '\\')
  1518.       p++;
  1519.     }
  1520.     return(head);
  1521. }
  1522.  
  1523. /* Directory Functions for GEMDOS, hacked up by Bruce Moore, 1991 */
  1524.  
  1525. /*
  1526.  * fgen:
  1527.  *  This is the actual name generator.  It is passed a string,
  1528.  *  possibly containing wildcards, and an array of character pointers.
  1529.  *  It finds all the matching filenames and stores them into the array.
  1530.  *  The returned strings are allocated from a static buffer local to
  1531.  *  this module (so the caller doesn't have to worry about deallocating
  1532.  *  them); this means that successive calls to fgen will wipe out
  1533.  *  the results of previous calls.  This isn't a problem here
  1534.  *  because we process one wildcard string at a time.
  1535.  *
  1536.  * Input: a wildcard string, an array to write names to, the
  1537.  *        length of the array.
  1538.  * Returns: the number of matches.  The array is filled with filenames
  1539.  *          that matched the pattern.  If there wasn't enough room in the
  1540.  *        array, -1 is returned.
  1541.  * By: Jeff Damens, CUCCA, 1984.
  1542.  */
  1543. static int
  1544. fgen(pat,resarry,len) char *pat,*resarry[]; int len; {
  1545.     int numfnd;
  1546.  
  1547.     numfnd = traverse(pat,resarry,len);        /* go walk the directory tree */
  1548.     if (numfnd >= len)
  1549.     return(-1);
  1550.     return(numfnd);        /* and return the number of matches */
  1551. }
  1552.  
  1553. /* traverse:
  1554.  *  Walks the directory tree looking for matches to its arguments.
  1555.  *  The algorithm is, briefly:
  1556.  *   If the current pattern segment contains no wildcards, that
  1557.  *   segment is added to what we already have.  If the name so far
  1558.  *   exists, we call ourselves recursively with the next segment
  1559.  *   in the pattern string; otherwise, we just return.
  1560.  *
  1561.  *   If the current pattern segment contains wildcards, we open the name
  1562.  *   we've accumulated so far (assuming it is really a directory), then read
  1563.  *   each filename in it, and, if it matches the wildcard pattern segment, add
  1564.  *   that filename to what we have so far and call ourselves recursively on the
  1565.  *   next segment.
  1566.  *
  1567.  *   Finally, when no more pattern segments remain, we add what's accumulated
  1568.  *   so far to the result array and increment the number of matches.
  1569.  *
  1570.  * Input: a pattern path list (as generated by splitpath), a string
  1571.  *      pointer that points to what we've traversed so far (this
  1572.  *      can be initialized to "/" to start the search at the root
  1573.  *      directory, or to "./" to start the search at the current
  1574.  *      directory), and a string pointer to the end of the string
  1575.  *      in the previous argument.
  1576.  * Returns: nothing.
  1577.  */
  1578. static VOID
  1579. traverse(name, wildnames, maxwild)
  1580. char *name;
  1581. char *wildnames[];
  1582. int maxwild;
  1583. {
  1584.     DMABUFFER dma;
  1585.     DMABUFFER *saved;
  1586.     char *p;
  1587.     int found;
  1588.  
  1589.     found = 0;
  1590.     saved = (DMABUFFER *) Fgetdta();
  1591.     Fsetdta(&dma);
  1592.  
  1593.     if (Fsfirst(name, S_IJRON) == 0) {    /* Found one */
  1594.     do {
  1595.         strcpy(scratch, name);
  1596.         if (p = strrchr(scratch, '\\'))
  1597.         p++;
  1598.         else
  1599.         p = scratch;
  1600.         *p = '\0';
  1601.         strcat(scratch, dma.d_fname);
  1602.         wildnames[found] = malloc(strlen(scratch) + 1);
  1603.         strcpy(wildnames[found++], scratch);
  1604.         if (found >= maxwild)
  1605.         break;
  1606.     } while (Fsnext() == 0);
  1607.     }
  1608.     Fsetdta(saved);
  1609.     return(found);
  1610. }
  1611.  
  1612. /* The following function is for expanding tilde in filenames
  1613.  * Contributed by Howie Kaye, CUCCA, developed for CCMD package.
  1614.  */
  1615.  
  1616. /*  T I L D E _ E X P A N D  --  expand ~user to the user's home directory. */
  1617.  
  1618. #define DIRSEP '\\'
  1619.  
  1620. char *
  1621. tilde_expand(dirname) char *dirname; {
  1622. #define BUFLEN 256
  1623. #ifdef DTILDE
  1624.     static char olddir[BUFLEN];
  1625.     static char oldrealdir[BUFLEN];
  1626.     static char temp[BUFLEN];
  1627.     int i, j;
  1628.     char *user;
  1629.  
  1630.     debug(F111,"tilde_expand",dirname,dirname[0]);
  1631.  
  1632.     if (dirname[0] != '~')        /* Not a tilde...return param */
  1633.       return(dirname);
  1634.     if (!strcmp(olddir,dirname)) {    /* Same as last time */
  1635.       return(oldrealdir);        /* so return old answer. */
  1636.     } else {
  1637.     j = strlen(dirname);
  1638.     for (i = 0; i < j; i++)        /* find username part of string */
  1639.       if (dirname[i] != DIRSEP)
  1640.         temp[i] = dirname[i];
  1641.       else break;
  1642.     temp[i] = '\0';            /* tie off with a NULL */
  1643.     if (i == 1) {            /* if just a "~" */
  1644.         user = "?";            /* No info, we're single user */
  1645.     } else {
  1646.         user = NULL;        /* otherwise on the specified user */
  1647.     }
  1648.     }
  1649.     if (user != NULL) {            /* valid user? */
  1650.     strcpy(olddir, dirname);    /* remember the directory */
  1651.     strcpy(oldrealdir, zhome());    /* and their home directory */
  1652.     strcat(oldrealdir,&dirname[i]);
  1653.     return(oldrealdir);
  1654.     } else {                /* invalid? */
  1655.     strcpy(olddir, dirname);    /* remember for next time */
  1656.     strcpy(oldrealdir, dirname);
  1657.     return(oldrealdir);
  1658.     }
  1659. #else
  1660.     return(NULL);
  1661. #endif /* dtilde */
  1662. }
  1663.  
  1664. /*
  1665.   Functions for executing system commands.
  1666.   zsyscmd() executes the system command in the normal, default way for
  1667.   the system.  In UNIX, it does what system() does.  Thus, its results
  1668.   are always predictable.
  1669.   zshcmd() executes the command using the user's preferred shell.
  1670. */
  1671. int
  1672. zsyscmd(s) char *s; {
  1673.     return(tossystem(s));
  1674. }
  1675.  
  1676. /*
  1677.   UNIX code by H. Fischer; copyright rights assigned to Columbia Univ.
  1678.   Adapted to use getpwuid to find login shell because many systems do not
  1679.   have SHELL in environment, and to use direct calling of shell rather
  1680.   than intermediate system() call. -- H. Fischer
  1681.   Call with s pointing to command to execute.
  1682. */
  1683.  
  1684. int
  1685. zshcmd(s) char *s; {
  1686.     tossystem(s);
  1687.     return(1);
  1688. }
  1689.  
  1690. /*  I S W I L D  --  Check if filespec is "wild"  */
  1691.  
  1692. /*
  1693.   Returns 0 if it is a single file, 1 if it contains wildcard characters.
  1694.   Note: must match the algorithm used by match(), hence no [a-z], etc.
  1695. */
  1696. int
  1697. iswild(filespec) char *filespec; {
  1698.     char c; int x; char *p;
  1699.     if (wildxpand) {
  1700.     if ((x = zxpand(filespec)) > 1) return(1);
  1701.     p = malloc(MAXNAMLEN + 20);
  1702.     znext(p);
  1703.     x = (strcmp(filespec,p) != 0);
  1704.     free(p);
  1705.     return(x);
  1706.     } else {
  1707.     while ((c = *filespec++) != '\0')
  1708.       if (c == '*' || c == '?') return(1);
  1709.     return(0);
  1710.     }
  1711. }
  1712.  
  1713. /*  T O S S Y S T E M --  Execute a system command */
  1714. /*
  1715.   Use gulam if present, otherwise hope for MSH, the shell supplied
  1716.   with the Mark Williams C package.
  1717. */
  1718. #define  SHELLP         ((char **) 0x04f6L)
  1719. #define  G_MAGIC        0x0135
  1720.  
  1721. tossystem(cmd)
  1722. char    *cmd;
  1723. {
  1724.     long save_ssp;
  1725.     short sh_magic;
  1726.     char *tgptr;                /* storage for togu_ */
  1727.     int  (* cgp)();                /* pointer to callgulam() */
  1728.  
  1729.     save_ssp = Super(0L);    /* Get gulam stuff in Supervisor mode */
  1730.     if ((tgptr = *SHELLP)) {        /* NULL need not apply */
  1731.         sh_magic = *((short *)(tgptr - 8));
  1732.         cgp = *((int (*)()) tgptr);
  1733.     } else
  1734.         sh_magic = 0;
  1735.     Super(save_ssp);
  1736.  
  1737.     if (sh_magic == G_MAGIC)        /* Gulam??? */
  1738.         (* cgp) (cmd);
  1739.     else                    /* No, hope for MSH */
  1740.         system(cmd);
  1741. }
  1742.