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

  1. char *ckzv = "Data General file support, 7.0.073, 8 Feb 1999";
  2.  
  3. /* C K D F I O  --  Kermit file system support for DG AOS/VS & AOS/VS-II */
  4.  
  5. /*
  6.   Author: Frank da Cruz <fdc@columbia.edu>,
  7.   Columbia University Academic Information Systems, New York City.
  8.  
  9.   Copyright (C) 1985, 2000,
  10.     Trustees of Columbia University in the City of New York.
  11.     All rights reserved.  See the C-Kermit COPYING.TXT file or the 
  12.     copyright text in the ckcmai.c module for disclaimer and permissions.
  13.  
  14.   UNIX file support ckufio.c adapted to Data General computers in 1985 by:
  15.   Phil Julian, SAS Institute, Inc., Box 8000, Cary, NC 27512-8000,
  16.   with additional help:
  17.     For using sys_gnfn() in fgen(), instead of my kludge:
  18.         Victor Johansen, Micro_rel, 2343 W 10th Place, Tempe AZ 85281
  19.     For using dg_open() for the pipe in zxcmd(), and also for using
  20.     sys_proc() in start_cli():
  21.         Richard Lamb, MIT, 77 Mass. Ave., Room 35-437, Cambridge MA 02139
  22.   Adapted to C-Kermit 5A by Eugenia Harris (ENH) at Data General, who
  23.     thanks Larry McCoskery, also at Data General, for many library routines
  24.     and much debugging assistance.
  25.   Developed and maintained since 1992 by fdc, with grateful thanks to DG
  26.     for an MV 2500 AOS/VS-II system on which to build and test it.
  27.  
  28.   Note that most of these routines, as written, depend on C and UNIX
  29.   compatibility library functions.  We would do much better to use native
  30.   functions, but it's too late now.  In particular, there is much confusion
  31.   between / and : as the directory separator, in what is expected by and/or
  32.   returned by various functions.  - fdc, July 1997.
  33. */
  34.  
  35. /* Includes */
  36.  
  37. #nolist
  38. #include "ckcdeb.h"                     /* Typedefs, debug formats, etc */
  39. #include "ckcker.h"                     /* Kermit definitions */
  40. #include "ckcasc.h"            /* ASCII chars */
  41. #include <ctype.h>                      /* Character types */
  42. #include <stdio.h>                      /* Standard i/o */
  43. #include <time.h>
  44. #include <packets:create.h>
  45. #include <packets:common.h>
  46. #include <memory.h>
  47. #include <dglib.h>
  48. #include <sys_calls.h>
  49. #include <packets:process.h>
  50. #include <packets/filestatus.h>         /* Used for ?GNFN */
  51. #include <paru.h>
  52. #include <bit.h>
  53. #include <sys/dir.h>                    /* Directory structure */
  54. #include <sys/stat.h>                   /* File status */
  55. #list
  56.  
  57. /* The DG compiler version 3.21 has a bug in the get*id() functions, which
  58.  * cause the program to go into an infinite loop.  These functions should
  59.  * return -1 unless the system is in a UNIX environment, so I made the
  60.  * appropriate kludges.   -- Phil Julian, 8 April 87
  61.  */
  62. #define getgid() -1
  63. #define getuid() -1
  64. #define geteuid() -1
  65. #define MAXNAMLEN ($MXFN-1)
  66. #define MAXPATH ($MXPL-1)               /* both from system PARU.32 */
  67. #define DIRSEP ':'
  68. #define ISDIRSEP(c)  (((c)==':') || ((c)=='^') || ((c)=='='))
  69. #define TIMESTAMP
  70. #ifndef MAXWLD
  71. #define MAXWLD 500
  72. #endif /* MAXWLD */
  73. #define fork() vfork()
  74.  
  75. char *ckzsys = " Data General AOS/VS";
  76.  
  77. /* Definitions of system commands for AOS/VS */
  78.  
  79. char *DIRCMD = "filestatus/sort/assortment ";   /* For directory listing */
  80. char *DIRCM2 = "filestatus/sort/assortment";    /* For full directory list */
  81. char *DELCMD = "delete/v ";                     /* For file deletion */
  82. char *TYPCMD = "type ";                         /* For typing a file */
  83.  
  84. char *PWDCMD = "directory";                     /* For saying where I am */
  85.  
  86. /* SPACMD now returns space in current directory rather than home dir. */
  87. char *SPACMD = "space =";               /* For space in current directory */
  88. char *SPACM2 = "space ";                /* For space in specified directory */
  89.  
  90. char *WHOCMD = "who [!sons op:exec]";
  91.  
  92. /*
  93.   Functions (n is one of the predefined file numbers from ckermi.h):
  94.  
  95.    zopeni(n,name)   -- Opens an existing file for input.
  96.    zopeno(n,name)   -- Opens a new file for output.
  97.    zclose(n)        -- Closes a file.
  98.    zchin(n,&c)      -- Gets the next character from an input file.
  99.    zsout(n,s)       -- Write a null-terminated string to output file, buffered.
  100.    zsoutl(n,s)      -- Like zsout, but appends a line terminator.
  101.    zsoutx(n,s,x)    -- Write x characters to output file, unbuffered.
  102.    zchout(n,c)      -- Add a character to an output file, unbuffered.
  103.    zchki(name)      -- Check if named file exists and is readable, return size.
  104.    zchko(name)      -- Check if named file can be created.
  105.    zchkspa(name,n)  -- Check if n bytes available for file 
  106.    znewn(name,s)    -- Make a new unique file name based on the given name.
  107.    zdelet(name)     -- Delete the named file.
  108.    zxpand(string)   -- Expands the given wildcard string into a list of files.
  109.    znext(string)    -- Returns the next file from the list in "string".
  110.    zxcmd(cmd)       -- Execute the command in a lower fork.
  111.    zclosf()         -- Close input file associated with zxcmd()'s lower fork.
  112.    zrtol(n1,n2)     -- Convert remote filename into local form.
  113.    zltor(n1,n2)     -- Convert local filename into remote form.
  114.    zchdir(dirnam)   -- Change working directory.
  115.    zhome()          -- Return pointer to home directory name string.
  116.    zkself()         -- Kill self, log out own job.
  117.    zsattr(struct zattr *) -- Return attributes for file which is being sent.
  118.    zstime(f, struct zattr *, x) - Set file creation date from attribute packet.
  119.    zrename(old, new) -- Rename a file.
  120.  */
  121.  
  122. /* Some systems define these in include files, others don't... */
  123. #ifndef R_OK
  124. #define R_OK 4                          /* For access */
  125. #endif /* ifndef R_OK */
  126.  
  127. #ifndef W_OK
  128. #define W_OK 2
  129. #endif /* ifndef W_OK */
  130.  
  131. #ifdef UXIII
  132. #include <fcntl.h>
  133. #define MAXNAMLEN DIRSIZ
  134. #endif
  135.  
  136. #ifndef O_RDONLY
  137. #define O_RDONLY 000
  138. #endif /* ifndef O_RDONLY */
  139.  
  140. /* Declarations */
  141.  
  142. int wildcarlb;                          /* Wild card ^ or # */
  143. int maxnam = MAXNAMLEN;                 /* Available to the outside */
  144. int maxpath = MAXPATH;
  145.  
  146.  
  147. FILE *fp[ZNFILS] = {                    /* File pointers */
  148.     NULL, NULL, NULL, NULL, NULL, NULL, NULL };
  149.  
  150. /* Buffers and pointers used in buffered file input */
  151.  
  152. #ifdef DYNAMIC
  153. extern char *zinbuffer, *zoutbuffer;
  154. #else
  155. extern char zinbuffer[], zoutbuffer[];
  156. #endif /* ifdef DYNAMIC */
  157.  
  158. extern char *zinptr, *zoutptr;
  159. extern int zincnt, zoutcnt;
  160.  
  161. static long iflen = -1L;                /* Input file length */
  162. static PID_T pid;                       /* pid of child fork */
  163. #ifdef SASMOD                           /* For remote Kermit command */
  164. static int savout, saverr;              /* saved stdout and stderr streams */
  165. #endif /* ifdef SASMOD */
  166. static int fcount = 0;            /* Number of files in wild group */
  167. static int nxpand = 0;            /* Copy of fcount */
  168. static char nambuf[MAXNAMLEN+1];        /* Buffer for a filename */
  169. char *malloc(), *getenv(), *strcpy();   /* System functions */
  170. extern errno;                           /* System error code */
  171.  
  172. char *mtchs[MAXWLD];                    /* Matches found for filename */
  173. static char **mtchptr;                  /* Pointer to current match */
  174. static char zmbuf[200];
  175.  
  176. /*  Z K S E L F  --  Kill Self: log out own job, if possible.  */
  177.  
  178. zkself() {                              /* For "bye", but no guarantee! */
  179.     
  180.     /* sys_term works better than kill() on the DG, but does not log off. */
  181.  
  182.     char *msg ="bye ";
  183.     int ac2;
  184.     ac2 = (int) msg;
  185.     return(sys_term(getpid(),0,ac2));
  186. }
  187.  
  188. /*  Z O P E N I  --  Open an existing file for input. */
  189.  
  190. zopeni(n,name) int n; char *name; {
  191.     debug(F111," zopeni",name,n);
  192.     debug(F101,"  fp","",(int) fp[n]);
  193.     if (chkfn(n) != 0) return(0);
  194.     if (n == ZSYSFN) {                  /* Input from a system function? */
  195.         debug(F110," invoking zxcmd",name,0);
  196.         return(zxcmd(name));            /* Try to fork the command */
  197.     }
  198.     if (n == ZSTDIO) {                  /* Standard input? */
  199.         if (isatty(0)) {
  200.             ermsg("Terminal input not allowed");
  201.             debug(F110,"zopeni: attempts input from unredirected stdin","",0);
  202.             return(0);
  203.         }
  204.         fp[ZIFILE] = stdin;
  205.         return(1);
  206.     }
  207.     fp[n] = fopen(name,"r");            /* Real file. */
  208.     debug(F111," zopeni", name, (int) fp[n]);
  209.     if (fp[n] == NULL) perror("zopeni");
  210.     return((fp[n] != NULL) ? 1 : 0);
  211. }
  212.  
  213. /*  Z O P E N O  --  Open a new file for output.  */
  214.  
  215. zopeno(n,name,zz,fcb)
  216. int n; char *name; struct zattr *zz; struct filinfo *fcb; {
  217.  
  218.     char p[3];
  219.     char myname[] = "zopeno()";
  220.     long validate();
  221.  
  222.     debug(F111," zopeno",name,n);
  223.  
  224.     if (fcb) {
  225.         debug(F101,"zopeno fcb disp","",fcb->dsp);
  226.         debug(F101,"zopeno fcb type","",fcb->typ);
  227.         debug(F101,"zopeno fcb char","",fcb->cs);
  228.     } else {
  229.         debug(F100,"zopeno fcb is NULL","",0);
  230.     }
  231.     if (n != ZDFILE)
  232.         debug(F111," zopeno",name,n);
  233.  
  234.     if (chkfn(n) != 0) return(0);
  235.     if ((n == ZCTERM) || (n == ZSTDIO)) {   /* Terminal or standard output */
  236.         fp[ZOFILE] = stdout;
  237.         if (n != ZDFILE)
  238.             debug(F101," fp[]=stdout", "",fp[n]);
  239.         zoutcnt = 0;
  240.         zoutptr = zoutbuffer;
  241.         return(1);
  242.     }
  243.  
  244.     /* ENH - in VS, the creation time can only be set on the              */
  245.     /* create, so if the file exists and a creation date is specified     */
  246.     /* we will delete the existing file and create the new with requested */
  247.     /* creation date.  If no date is given, or it's invalid, we will      */
  248.     /* not bother creating the file, as it will be created implicitly     */
  249.     /* on the open.  Note that we ignore errors from the routine to       */
  250.     /* set the creation date (and create the file), the assumption        */
  251.     /* being that if it fails, the file is not created, but there's       */
  252.     /* still a possibility that it might be created on the open.  If      */
  253.     /* not, the error will be caught there.  Note that this whole pro-    */
  254.     /* cedure is only invoked when the file collsion action is "update".  */
  255.     /* That is the only time we will get to this point and find that a    */
  256.     /* file of the same name already exists.                              */
  257.  
  258.     strcpy(p,"w");
  259.     if (fcb) {                          /* If called with an FCB... */
  260.         if (fcb->dsp == XYFZ_A) {       /* Does it say Append? */
  261.             debug(F101,"zopeno opening for append access",name,0);
  262.             strcpy(p,"a");              /* If so, open for append access */
  263.         } else if (file_exists(name) != 0) { /* If it exists, delete it */
  264.             if (zdelet(name) != 0)      /* and create w/incoming date */
  265.                 debug(F101,"zopeno error deleting existing file",name,0);
  266.             else debug(F101,"zopeno existing file deleted",name,0);
  267.             if (zz->date.len > 0) {    /* if creation date specified */
  268.                 debug(F101,"zopeno creating file with incoming date",
  269.                       name,0);
  270.                 set_creation_date(name,zz->date.val);   /* set it */
  271.                 strcpy(p,"r+");
  272.             }
  273.         }
  274.     }
  275.  
  276.     fp[n] = fopen(name,p);              /* A real file, try to open */
  277.     if (fp[n] == NULL) {
  278.         perror("zopeno can't open");
  279.     } else {
  280.         if (n == ZDFILE) setbuf(fp[n],NULL); /* Debugging file unbuffered */
  281.     }
  282.   
  283.     zoutcnt = 0;                /* (PWP) reset output buffer */
  284.     zoutptr = zoutbuffer;
  285.     if (n != ZDFILE)
  286.       debug(F101, " fp[n]", "", (int) fp[n]);
  287.     return((fp[n] != NULL) ? 1 : 0);
  288. }
  289.  
  290. /*  Z C L O S E  --  Close the given file.  */
  291.  
  292. /*  Returns 0 if arg out of range, 1 if successful, -1 if close failed.  */
  293.  
  294. int
  295. zclose(n) int n; {
  296.     int x, x2;
  297.  
  298.     if (chkfn(n) < 1) return(0);        /* Check range of n */
  299.  
  300.     if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */
  301.       x2 = zoutdump();
  302.     else
  303.       x2 = 0;
  304.  
  305.     x = 0;
  306.  
  307.     if (fp[ZSYSFN]) {                   /* If system function */
  308.         x = zclosf(n);                  /* do it specially */
  309.     } else {
  310.         if ((fp[n] != stdout) && (fp[n] != stdin)) x = fclose(fp[n]);
  311.         fp[n] = NULL;
  312.     }
  313.     iflen = -1L;                        /* invalidate file length */
  314.     if (x == EOF)
  315.         return (-1);
  316.     else if (x2 < 0)
  317.         return (-1);
  318.     else
  319.         return (1);
  320. }
  321.  
  322. /*  Z C H I N  --  Get a character from the input file.  */
  323.  
  324. /*  Returns -1 if EOF, 0 otherwise with character returned in argument  */
  325. int 
  326. zchin(n,c) int n; int *c; {
  327.     int a, x;
  328.  
  329.  /* (PWP) Just in case this gets called when it shouldn't. */
  330.     if (n == ZIFILE) {
  331.         x = zminchar();
  332.         *c = x;
  333.         return(x);
  334.     }
  335.  
  336.     /* if (chkfn(n) < 1) return(-1); */
  337.     a = getc(fp[n]);
  338.     if (a == EOF) return(-1);
  339.     *c = (CHAR) a & 0377;
  340.     return(0);
  341. }
  342.  
  343. /* Z S I N L  --  Reads a line from file number n  */
  344. /*
  345.  Reads line from file number n and writes it to address provided by caller.
  346.  Writing terminates when a newline is read, but the newline is discarded.
  347.  Writing also terminates on EOF or if length x is exhausted.  Returns 0
  348.  on success and -1 on EOF
  349. */
  350. int
  351. zsinl(n,s,x) int n, x; char *s; {
  352.     int a, z = 0;                       /* lifted verbatim from ckufio.c */
  353.  
  354.     if (chkfn(n) < 1) {                 /* Make sure file is open */
  355.         return(-1);
  356.     }
  357.     a = -1;
  358.     while (x--) {
  359.  
  360. #ifndef NLCHAR
  361.         int old;
  362.         old = a;                        /* Previous character */
  363. #endif
  364.         
  365.         if (zchin(n,&a) < 0) {          /* Read a character from the file */
  366.             z = -1;
  367.             break;
  368.         }
  369.  
  370. #ifdef NLCHAR
  371.         if (a == (char) NLCHAR) break;  /* Single-character line terminator */
  372. #else
  373.         if (a == '\r') continue;        /* CRLF line terminator */
  374.         if (old == '\r') {
  375.             if (a == '\n') break;
  376.             else *s++ = '\r';
  377.         }
  378. #endif /* NLCHAR */
  379.  
  380.         *s = a;
  381.         s++;
  382.     }
  383.     *s = '\0';
  384.     return(z);
  385. }
  386.  
  387.  
  388.  
  389. /*  Z I N F I L L  --  Reads characters from ZIFILE when sending files.  */
  390. /*
  391.  * Suggestion: if fread() returns 0, call ferror to find out what the
  392.  * problem was.  If it was not EOF, then return -2 instead of -1.
  393.  * Upper layers (getpkt function in ckcfns.c) should set cxseen flag
  394.  * if it gets -2 return from zminchar macro.
  395.  */
  396. int
  397. zinfill() {
  398.     int x;
  399.  
  400.     errno = 0;
  401.     zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]);
  402.  
  403. #ifdef COMMENT
  404.     debug(F101,"zinfill fp","",fp[ZIFILE]);
  405.     debug(F101,"zinfill zincnt","",zincnt);
  406. #endif
  407.  
  408.     if (zincnt == 0) {
  409.  
  410. #ifdef ferror
  411.         x = ferror(fp[ZIFILE]);
  412.         debug(F101,"zinfill errno","",errno);
  413.         debug(F101,"zinfill ferror","",x);
  414.         if (x) return(-2);
  415. #endif /* ferror */
  416.  
  417.         x = feof(fp[ZIFILE]);
  418.         debug(F101,"zinfill errno","",errno);
  419.         debug(F101,"zinfill feof","",x);
  420.         if (!x && ferror(fp[ZIFILE])) return(-2);
  421.         return(-1);
  422.     }
  423.     zinptr = zinbuffer; /* set ptr to beginning, (== &zinbuffer[0]) */
  424.     zincnt--;                   /* one less char in buffer */
  425.     return((int)(*zinptr++) & 0377); /* because we return the first */
  426. }
  427.  
  428. /*  Z S O U T  --  Write a string to the given file, buffered.  */
  429.  
  430. int 
  431. zsout(n,s) int n; char *s; {
  432.    if (chkfn(n) < 1) return(-1); /* Keep this here, prevents memory faults */
  433.  
  434. #ifdef COMMENT
  435.     while (*s) {                        /* (unbuffered for debugging) */
  436.         write(fileno(fp[n]),s,1); ++s;
  437.     }
  438.     return(fputs(s,fp[n]) == EOF ? -1 : 0);
  439. #else
  440.     if (n == ZSFILE)
  441.         return(write(fileno(fp[n]),s,(int)strlen(s)));
  442.     else
  443.         return((fputs(s,fp[n]) == EOF) ? -1 : 0);
  444. #endif
  445. }
  446.  
  447.  
  448. /*  Z S O U T L  --  Write string to file, with line terminator, buffered  */
  449.  
  450. int 
  451. zsoutl(n,s) int n; char *s; {
  452.     /* if (chkfn(n) < 1) return(-1); */
  453.     if (fputs(s,fp[n]) == EOF) return(-1);
  454.     fputs("\n",fp[n]);
  455.     return(0);
  456. }
  457.  
  458. /*  Z S O U T X  --  Write x characters to file, unbuffered.  */
  459.  
  460. int 
  461. zsoutx(n,s,x) int n, x; char *s; {
  462.  
  463. #ifdef COMMENT
  464.     if (chkfn(n) < 1) return(-1);
  465.     return(write(fp[n]->_file,s,x));
  466. #endif
  467.  
  468.     return(write(fileno(fp[n]),s,x));
  469. }
  470.  
  471.  
  472. /*  Z C H O U T  --  Add a character to the given file.  */
  473.  
  474. /*  Should return 0 or greater on success, -1 on failure (e.g. disk full)  */
  475.  
  476. #ifdef CK_ANSIC
  477. zchout (register int n, char c)
  478. #else 
  479. zchout(n,c) register int n; char c;        
  480. #endif /* CK_ANSIC */
  481.  
  482. /* zchout() */ {
  483.  
  484.     /* if (chkfn(n) < 1) return(-1); */
  485.     if (n == ZSFILE)
  486.         return(write(fileno(fp[n]),&c,1)); /* Use unbuffered for session log */
  487.     else {                              /* Buffered for everything else */
  488.         if (putc(c,fp[n]) == EOF)       /* If true, maybe there was an error */
  489.             return(ferror(fp[n])?-1:0); /* Check to make sure */
  490.         else                            /* Otherwise... */
  491.             return(0);                  /* There was no error. */
  492.     }
  493. }
  494.  
  495. zoutdump() {
  496.     int x;
  497.     zoutptr = zoutbuffer;               /* Reset buffer pointer in all cases */
  498.     debug(F101,"zoutdump chars","",zoutcnt);
  499.     if (zoutcnt == 0) {                 /* Nothing to output */
  500.         return(0);
  501.     } else if (zoutcnt < 0) {           /* Unexpected negative argument */
  502.         zoutcnt = 0;                    /* Reset output buffer count */
  503.         return(-1);                     /* and fail. */
  504.     }
  505.  
  506. /* Frank Prindle suggested that replacing this fwrite() by an fflush() */
  507. /* followed by a write() would improve the efficiency, especially when */
  508. /* writing to stdout.  Subsequent tests showed a 5-fold improvement!   */
  509. /* if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) {              */
  510.  
  511.     fflush(fp[ZOFILE]);
  512.     if ((x = write(fileno(fp[ZOFILE]),zoutbuffer,zoutcnt)) == zoutcnt) {
  513.         debug(F101,"zoutdump write ok","",zoutcnt);
  514.         zoutcnt = 0;                    /* Reset output buffer count */
  515.         return(0);                      /* write() worked OK */
  516.     } else {
  517.  
  518.         debug(F101,"zoutdump write error","",errno);
  519.         debug(F101,"zoutdump write returns","",x);
  520.         zoutcnt = 0;                    /* Reset output buffer count */
  521.         return(-1);                     /* write() failed */
  522.     }
  523. }
  524.  
  525.  
  526. /*  C H K F N  --  Internal function to verify file number is ok  */
  527. /*
  528.  Returns:
  529.   -1: File number n is out of range
  530.    0: n is in range, but file is not open
  531.  
  532.    1: n in range and file is open
  533. */
  534. chkfn(n) int n; {
  535.     if (n < 0 || n >= ZNFILS) {
  536.     if (n != ZDFILE) debug(F101,"chkfn out of range","",n);
  537.     return(-1);
  538.     } else {
  539.     /* if (n != ZDFILE) debug(F101,"chkfn fp[n]","",fp[n]); */
  540.     return((fp[n] == NULL) ? 0 : 1);
  541.     }
  542. }
  543.  
  544. /*  Z C H K I  --  Check if input file exists and is readable  */
  545.  
  546. /*
  547.   Returns:
  548.    >= 0 if the file can be read (returns the size).
  549.      -1 if file doesn't exist or can't be accessed,
  550.      -2 if file exists but is not readable (e.g. a directory file).
  551.      -3 if file exists but protected against read access.
  552. */
  553. /*
  554.  For Berkeley Unix, a file must be of type "regular" to be readable.
  555.  Directory files, special files, and symbolic links are not readable.
  556. */
  557. long
  558. zchki(name) char *name; {
  559.     
  560.     /* For some reason, the DG croaks intermittently with a call to stat(),
  561.      * and goes into an infinite loop.  This routine needs to see if the
  562.      * file is okay to look at and access, so I will just call ?fstat to
  563.      * do that.  The only files that cannot be accessed by normal i/o routines
  564.      * are directories.  Other files are fair game.
  565.      */
  566.     extern int zchkid;
  567.  
  568.     int x; long y;                      
  569.     P_FSTAT buf;        /* struct stat buf; */  
  570.     int ac0,ac2;
  571.     char * temp;         /* ENH - in case we have to prepend an = */
  572.                         /* because ?fstat looks in searchlist    */
  573.     temp = alloc (strlen(name)+2);
  574.     if ((*name != '^') && (*name != '@') && (*name != ':') && (*name != '=')) {
  575.         strcpy (temp+1,name);
  576.         *temp = '=';
  577.     } else
  578.       strcpy(temp,name);
  579.  
  580.     ac0 = (int) temp;  ac2 = (int) &buf;
  581.     x = sys_fstat(ac0,0,ac2);
  582.     
  583.     free(temp);
  584.     if (x != 0) {
  585.         debug(F111,"zchki sys_fstat fails",name,x);
  586.         return(-1);
  587.     }
  588.     x = buf.styp_type;                  /* Isolate file format field */
  589.     iflen = buf.sefm;
  590.  
  591.     if (!zchkid && (x >= $LDIR) && (x <= $HDIR)) {
  592.         debug(F111,"zchki skipping DIR type:",name,x);
  593.         return(-2);
  594.     }
  595.  
  596.     debug(F111,"zchki stat ok:",name,x);
  597.  
  598.     if ((x = access(name,R_OK)) < 0) {  /* Is the file accessible? */
  599.         debug(F111," access failed:",name,x); /* No */
  600.         return(-3);                     
  601.     } else {
  602.         y = buf.sefm;
  603.         debug(F111," access ok:",name,(int) y); /* Yes */
  604.         return( (y > -1) ? y : 0 );
  605.     }
  606. }
  607.  
  608. /*  Z C H K O  --  Check if output file can be created  */
  609.  
  610. /*
  611.  Returns -1 if write permission for the file would be denied, 0 otherwise.
  612. */
  613. zchko(name) char *name; {
  614.     int i, x;
  615.     char s[50], *sp;    
  616.  
  617.     sp = s;                             /* Make a copy, get length */
  618.     x = 0;
  619.     while ((*sp++ = *name++) != '\0')
  620.         x++;
  621.     if (x == 0) return(-1);             /* If no filename, fail. */
  622.  
  623.     debug(F101," length","",x);
  624.     for (i = x; i > 0; i--)             /* Strip filename. */
  625.       if (ISDIRSEP(s[i-1]))
  626.     break;
  627.  
  628.     debug(F101," i","",i);
  629.     if (i == 0)                         /* If no path, use current directory */
  630.       strcpy(s,"=");
  631.     else                                /* Otherwise, use given one. */
  632.       s[i] = '\0';
  633.  
  634.     x = access(s,W_OK);                 /* Check access of path. */
  635.     if (x < 0) {
  636.         debug(F111,"zchko access failed:",s,errno);
  637.         return(-1);
  638.     } else {
  639.         debug(F111,"zchko access ok:",s,x);
  640.         return(0);
  641.     }
  642. }
  643.  
  644. /*  Z D E L E T  --  Delete the named file.  */
  645.  
  646. zdelet(name) char *name; {
  647.     if (*name == '\0') {
  648.         debug(F111,"zdelet passed null filename","",0);
  649.         return(-1);
  650.     }
  651.     return(unlink(name));
  652. }
  653.  
  654.  
  655. /*  Z R T O L  --  Convert remote filename into local form  */
  656.  
  657. /*  For AOS/VS, we don't care about case -- we only care about certain chars */
  658.  
  659. zrtol(name,name2) char *name, *name2; {
  660.     char *p; int flag;
  661.     if (!name || !name2)
  662.       return;
  663.     debug(F110,"zrtol original name ",name,0);
  664.     p = name2;                /* Output pointer to new name */
  665.     for ( ; *name != '\0'; name++) {
  666.     if (*name > ' ') flag = 1;    /* Strip leading blanks and controls */
  667.     if (flag == 0 && *name < '!')
  668.       continue; 
  669.         if (*name == '-') *p = '_';    /* Change dash to underscore */
  670.         else if (*name == '^' ||    /* Change template characters to 'X' */
  671.          *name == '*' ||
  672.          *name == '+' ||
  673.          *name == '#' ||
  674.          *name == '\\' )
  675.       *p = 'X';
  676.     else *p = *name;        /* Other characters are just copied */
  677.     p++;    
  678.     }
  679.     *p-- = '\0';            /* Terminate */
  680.     while (*p < '!' && p > name2)    /* Strip trailing blanks & controls */
  681.       *p-- = '\0';
  682.     if (*name2 == '\0') strcpy(name2,"NONAME");
  683.     debug(F110,"zrtol translated name ",name2,0); /* Done */
  684. }
  685. /*  Z S T R I P  --  Strip device & directory name from file specification */
  686.  
  687. /*  Strip pathname from filename "name", return pointer to result in name2 */
  688.  
  689. static char work[257];          /* buffer for use by zstrip and zltor */
  690.  
  691. VOID                                    
  692. zstrip(name,name2) char *name, **name2; {
  693.     char *cp, *pp, *p2;
  694.     debug(F110,"zstrip before",name,0);
  695.     pp = work;
  696.  
  697.     for (cp = name; *cp != '\0'; cp++) {
  698.         if (ISDIRSEP(*cp))
  699.           pp = work;
  700.         else
  701.           *pp++ = *cp;
  702.     }
  703.     *pp = '\0';                         /* Terminate the string */
  704.     *name2 = work;
  705.     debug(F110,"zstrip after",*name2,0);
  706. }
  707.  
  708. /*  Z L T O R  --  Local TO Remote */
  709.  
  710. /*  Convert filename from local format to common (remote) form.  */
  711.  
  712. VOID 
  713. zltor(name,name2) char *name, *name2; {
  714.     char work[100], *cp, *pp;
  715.     int dc = 0;
  716.  
  717.     debug(F110,"zltor",name,0);
  718.     pp = work;
  719.     /* Strip off the DG directory prefix */
  720.     for (cp=name; *cp != '\0'; cp++) {
  721.         if (ISDIRSEP(*cp)) {
  722.             dc = 0;
  723.             pp = work;
  724.         }
  725.         else if (islower(*cp)) *pp++ = toupper(*cp); /* Uppercase letters */
  726.         else if (*cp == '~') *pp++ = 'X';       /* Change tilde to 'X' */
  727.         else if (*cp == '#') *pp++ = 'X';       /* Change number sign to 'X' */
  728.         else if (*cp == '$') *pp++ = 'X';       /* Change dollar sign to 'X' */
  729.         else if (*cp == '?') *pp++ = 'X';     /* Change question mark to 'X' */
  730.         else if ((*cp == '.') && (++dc > 1)) *pp++ = 'X'; /* & extra dots */
  731.         else *pp++ = *cp;
  732.     }
  733.     *pp = '\0';                         /* Tie it off. */
  734.     cp = name2;                         /* If nothing before dot, */
  735.     if (*work == '.') *cp++ = 'X';      /* insert 'X' */
  736.     strcpy(cp,work);
  737.     debug(F110," name2",name2,0);
  738. }
  739.  
  740.  
  741. /*  Z C H D I R  --  Change directory  */
  742.  
  743. zchdir(dirnam) char *dirnam; {
  744.     int x;
  745.     char *hd;
  746.     if (!dirnam) dirnam = "";
  747.     if (*dirnam == '\0')
  748.       hd = getenv("HOME");
  749.     else
  750.       hd = dirnam;
  751.     x = isdir(hd);
  752.     debug(F111,"zchdir isdir",hd,x);
  753.     x = chdir(hd);
  754.     debug(F111,"zchdir chdir",hd,x);    
  755.     return((x == 0) ? 1 : 0);
  756. }
  757.  
  758. /*  Z H O M E  --  Return pointer to user's home directory  */
  759.  
  760. char *
  761. zhome() {
  762.     char *result,*ptr;
  763.     static char *buf = NULL;
  764.     static int homeflag = 0;
  765.  
  766.     if (homeflag)            /* if homeflag, result already */
  767.       return(buf);            /* in buf - don't do it again. */
  768.  
  769.     /* The following returns result in UNIX format, e.g. "/UDD/NAME". */
  770.  
  771.     result = (getenv("HOME"));
  772.     if (result) {
  773.         buf = malloc((int) strlen(result) + 2); /* 1 for null, 1 for final : */
  774.         if (!buf) {
  775.             debug(F111,"zhome can't allocate memory","",0);
  776.             return(NULL);
  777.         }
  778.         for (ptr = buf ; *result != '\0'; ptr++,result++)
  779.       *ptr = (*result == '/') ? DIRSEP : toupper(*result);
  780.         *ptr++ = DIRSEP;
  781.         *ptr = '\0';
  782.         homeflag = 1;
  783.     }
  784.     return(buf);
  785. }
  786.  
  787. /*  Z G T D I R  --  Return pointer to user's current directory  */
  788.  
  789. #ifdef MAXPATH
  790. #define CWDBL MAXPATH
  791. #else
  792. #define CWDBL 100
  793. #endif
  794.  
  795. static char cwdbuf[CWDBL+1];
  796.  
  797. char *
  798. zgtdir() {
  799.     char *buf;
  800.     int error;
  801.     buf = cwdbuf;
  802.     if ((error = get_dir(buf)) != 0) {
  803.     debug(F111,"zgtdir can't get dir name, VS error","",error);
  804.     return(NULL);
  805.     } else {
  806.     debug(F110,"zgtdir",cwdbuf,0);
  807.     return(buf); 
  808.     }
  809. }
  810.  
  811. /*********************************************************************
  812.                 P I P E _ C A L L
  813.  
  814.    Split off the arguments from a command to the system, and
  815.    pass the command and arguments to the system routine.
  816.    Return the name of the temp file which stored the results of the
  817.    command.
  818.    This routine assumes that the "l=" option is available on all
  819.    commands, which is true of AOS/VS CLI.  Other DG systems may need
  820.    another method, if the l= option is not available.
  821.  
  822.    Returns 0 if the system() function succeeds (>1 status)
  823.            1 if the system() function fails.
  824.  
  825.    I would have deleted this routine, except that I use it in zxpand()
  826.    if a # is used as a wild card character.  I used to depend on this
  827.    routine for zxcmd() as well as zxpand(), but I now use sys_proc and
  828.    sys_gnfn() respectively.
  829.    
  830. *********************************************************************/
  831.  
  832. int pipe_call(command,output_fname)
  833.    char  *command,output_fname[];
  834. {
  835.    /* Look for the first semi-colon, if any, and strip up to that point, plus 
  836.       any blanks following it.  Because the system() command refers to
  837.       the initial working directory, a "dir xxx;" is usually pre-pended. 
  838.    */
  839.    char  *arguments, syscommand[512], locommand[512];
  840.    char *insert,*fname,*cp,*tempname;
  841.    FILE *dummy;
  842.    
  843.    strcpy(locommand,command);
  844.    /* Find the first ;, if any */
  845.    for (insert=locommand; ((*insert) && (*insert!=';')); insert++);
  846.    if (*insert) arguments = insert+1;
  847.    else arguments = locommand;          /* No ; found */
  848.    /* Skip leading spaces */
  849.    for (; ((*arguments) && isspace(*arguments)); arguments++);
  850.    /* Find the end of the command to CLI */
  851.    for (; ((*arguments) && (*arguments!=',') && !isspace(*arguments));
  852.         arguments++);
  853.    /* If *arguments is NULL, there are no parameters to the command.
  854.       Otherwise, null-terminate command and address the arguments */
  855.    if (*arguments) *arguments++ = '\0';
  856.  
  857.    /* create the @OUTPUT file name -- a unique file name */
  858.    fname = getenv("HOME");  
  859.    for (cp=fname; *cp; cp++) if (*cp == '/') *cp=':';
  860.    strcpy(syscommand,fname);   strcat(syscommand,":?kermit_pipe_123456");
  861.    fname = &syscommand[0];
  862.    mktemp(fname);
  863.    strcpy(output_fname,fname);
  864.  
  865.    /* Create the file to establish its file type first */
  866.    close(dummy=open(output_fname,O_WRONLY));
  867.  
  868.    /* Append an L= (listing =) switch.  All CLI commands have this. */
  869.    sprintf(syscommand,"%s/l=%s%s",locommand,output_fname,arguments);
  870.    debug(F101,"syscommand",syscommand,0);
  871.    /* now execute the command */
  872.    if (system(syscommand) > 1) {
  873.         perror("system");
  874.         return(1);
  875.    }
  876.    return(0);
  877. }
  878.  
  879.  
  880. /*  Z X C M D -- Run a system command so its output can be read like a file */
  881.  
  882. int
  883. zxcmd(filenum,comand) int filenum; char *comand; {
  884. /******** Start of Rick Lamb's addition to Kermit history. ****************/
  885. /* I modified Rick's code to use a tempname type of file.  -- Phil Julian. */
  886. /* Can open and start a task in AOS using their dg_open command
  887.  * and a system call "sys_proc" in "start_cli". R.H.Lamb 12/86 
  888.  */
  889.         FILE *piped;
  890.         char temp[256];
  891.         char *tempfile,*cp,pipename[256];
  892.         char *shell, *savep, *comand2;
  893.         int i;
  894.          
  895.         /* Create a unique pipe file in the users home directory, because
  896.          * the actual working directory could be where the user has no
  897.          * write privelages, or two users may collide in the same directory.
  898.          */
  899.         debug(F000,"Entering zxcmd","",0);
  900.         if (chkfn(filenum) < 0) return(-1);
  901.         tempfile = getenv("HOME");  
  902.         for (cp = tempfile; *cp; cp++) if (*cp == '/') *cp=':';
  903.         /* Make sure and copy the NULL also */
  904.         memcpy (pipename, tempfile, strlen(tempfile)+1);
  905.         strcat (pipename,":?kermit_pipe_123456");
  906.         mktemp(pipename);
  907.  
  908. /* The command interpreter for AOS is "cli.pr"
  909.  * for MV/UX its :bin:sh:pr 
  910.  */
  911.         if ((piped=dg_open(pipename,$ICRF+$OFIN+$OFCR+$RTDY,$FPIP))==NULL) {
  912.             perror("Trouble creating a pipe in zxcmd\n"); 
  913.             fprintf(stderr,"Pipe filename was [%s]\n",pipename);
  914.             return(0);
  915.         }
  916.         debug(F000," pipe file created","",0);
  917.         debug(F110," zxcmd: received command = ",comand,0);
  918.  
  919.         savep = strchr(comand,' ');     /* enh - find blank and if there */
  920.         if (savep != NULL) {            /* is one, split comand in 2     */
  921.             *savep = '\0';
  922.             comand2 = ++savep;          /* comand2 points to 1st arg */
  923.         } else comand2 = NULL;          /* we're doing all this so we can */
  924.         debug(F110," zxcmd: command = ",comand,0); /* put the L= switch on*/
  925.         if (comand2 != NULL)            /* the command rather than the proc */
  926.             debug(F110," zxcmd: arguments = ",comand2,0);
  927.         else
  928.             debug(F110," zxcmd: arguments = none","",0);
  929. #ifdef mvux
  930.         shell = ":bin:sh.pr";
  931. #else
  932.         shell = ":cli.pr";
  933. #endif
  934.         if (comand2 != NULL)
  935.             sprintf(temp,"%s,%s/L,%s",shell,comand,comand2);
  936.         else
  937.             sprintf(temp,"%s,%s/L",shell,comand);
  938.  
  939.         debug(F110," zxcmd: revised command = ",temp,0);
  940.  
  941.         if (start_cli(temp,pipename))
  942.                 {perror("Can't execute command in zxcmd\n"); return(0);}
  943.         fp[filenum]=piped;
  944.         fp[ZSYSFN]=fp[filenum];
  945.         zincnt = 0;
  946.         zinptr = zinbuffer;
  947.         return(1);
  948. }
  949.  
  950. #ifdef SASMOD
  951.  
  952. /* For remote Kermit command */
  953. /*  Z X L O G  --  redirect stderr and stdout for logging. */
  954.  
  955. zxlog() {
  956.     FILE *tmpf, *tmpfile();
  957.  
  958.     if (chkfn(ZSYSFN) != 0) return(0);
  959.     /* Unix magic to redirect stdout and stderr to temporary file */
  960.     fflush(stdout); fflush(stderr);     /* synchronize */
  961.     if ((tmpf = tmpfile()) == NULL) return(0);
  962.     if ((savout = dup(1)) < 0 || (saverr = dup(2)) < 0) return(0);
  963.     dup2(fileno(tmpf), 1); dup2(fileno(tmpf), 2);
  964.     fp[ZSYSFN] = tmpf;
  965.     return(1);
  966. }
  967.  
  968.  
  969. /*  Z X U N L O G  --  restore stderr and stdout from logging. */
  970.  
  971. zxunlog() {
  972.     /* restore stdout and stderr */
  973.     fflush(stdout); fflush(stderr);     /* synchronize */
  974.     dup2(savout, 1); close(savout);
  975.     dup2(saverr, 2); close(saverr);
  976.  
  977.     /* rewind to start of temporary file */
  978.     rewind(fp[ZSYSFN]);
  979.  
  980.     fp[ZIFILE] = fp[ZSYSFN];
  981.     pid = 0;
  982.     return(1);
  983. }
  984.  
  985. #endif SASMOD
  986.  
  987. /*  Z C L O S F  - wait for the child fork to terminate and close the pipe. */
  988.  
  989. zclosf() {
  990.     int wstat;
  991.     fclose(fp[ZIFILE]);
  992.     fp[ZIFILE] = fp[ZSYSFN] = NULL;
  993.     while ((wstat = wait(0)) != pid && wstat != -1) ;
  994.     return(1);
  995. }
  996.  
  997.  
  998. /******** More of Rick Lamb's addition to Kermit history. */
  999.  
  1000. /* S T A R T _ C L I - starts a command as another concurrent task.
  1001.  * The command is equivalent to the CLI command of
  1002.  * "proc/def/output=pipename :cli command" 
  1003.  */
  1004. start_cli(command,pipename) char *command,*pipename; {       
  1005.         P_PROC packet;
  1006.         P_ISEND message;
  1007.         int len,pid,err;
  1008.         short int *string = (short int *)command;
  1009.         
  1010.         debug(F110,"Start_cli: command = ",command,0);
  1011.  
  1012.         len = strlen(command);
  1013.  
  1014.         message.isfl = (short) 0;       /* System flags */
  1015.         message.iufl = (short) $RFCF;   /* User flags */
  1016.         message.idph = (long) 0;        /* Destination port number */
  1017.         message.iopn = (short) 0;       /* Local origin port number */
  1018.         message.ilth = (short) (len / 2 + 1);   /* Length (in words) of message */
  1019.         message.iptr = string;          /* Pointer to message buffer */
  1020.  
  1021.         packet.pflg = (short) 0;        /* Flags for process creation */
  1022.         packet.ppri = (short) -1;       /* Process priority -- was 3 */
  1023.         packet.psnm = ":cli.pr";        /* Byte pointer to program name */
  1024.         packet.pipc = &message;         /* Pointer to initial msg. or -1 */
  1025.         packet.pnm  = (char *) -1;      /* Byte ptr to process name or -1 */
  1026.         packet.pmem = (long) -1;        /* Maximum memory pages or -1 */
  1027.         packet.pdir = (char *) 0;       /* Byte ptr to initial dir. or -1/0 */
  1028.         packet.pcon = (char *) 0;       /* Byte ptr to console name or -1/0 */
  1029.         packet.pcal = (short) -1;       /* Max concurrent system calls or -1 */
  1030.         packet.pwss = (short) -1;       /* Max working set size or -1 */
  1031.         packet.punm = -1;               /* Byte ptr to username or -1 */
  1032.         /* Note that $PVPC (unlimited sons) causes privelage violations
  1033.          * for users that are not royally endowed.  Anyway, following are
  1034.          * the privileges bits.  -- Phil Julian
  1035.          */
  1036.         packet.pprv = (short) ( /* $PVPC+ */ $PVWS+$PVEX+$PVIP); 
  1037.         packet.ppcr = (short) -1;       /* Maximum sons or -1 */
  1038.         packet.pwmi = (short) -1;       /* Working set minimum or -1 */
  1039.                                         /* reserved */
  1040.         packet.pifp = "@Null";          /* Byte ptr to @INPUT  or -1/0 */
  1041.         packet.pofp = "@Null";          /* Byte ptr to @OUTPUT or -1/0 */
  1042.         packet.plfp = pipename;         /* Byte ptr to @LIST   or -1/0 */
  1043.         packet.pdfp = (char *) 0;       /* Byte ptr to @DATA   or -1/0 */
  1044.         packet.smch = (_ulong) -1;      /* Max CPU time or -1 */
  1045.  
  1046.         if (err = sys_proc(&packet,&pid)) {
  1047.                 perror("Start_cli: sys_proc ");
  1048.                 fprintf(stderr,"Start_cli: Error in sys_proc = %#o\n",err);
  1049.                 return(1);
  1050.         }
  1051.         else
  1052.         return(0);
  1053. }
  1054. /******** End of Rick Lamb's addition to Kermit history. */
  1055.  
  1056. /*  Z X P A N D  --  Expand a wildcard string into an array of strings  */
  1057. /*
  1058.   Returns the number of files that match fn1, with data structures set up
  1059.   so that first file (if any) will be returned by the next znext() call.
  1060. */
  1061. static int xdironly = 0;
  1062. static int xfilonly = 0;
  1063. static int xrecursive = 0;
  1064. static int xnobackup = 0;
  1065.  
  1066. static int
  1067. zxpand(fn) char *fn; {
  1068.  
  1069.    /* Victor Johansen's code requires no change in zxpand(). 
  1070.     * However, it is difficult to expand wild card strings that
  1071.     * contain some ^ or #.  For a #, you must check the file type for
  1072.     * being a directory, then open up the directory, and keep searching
  1073.     * for sub-directories, etc.  However, a simple kludge can be used
  1074.     * in this case, which saves me the programming effort.  Anyone
  1075.     * using the # should pay an execution time penalty for using it.
  1076.     * So when a string contains the #, we use the original kludge that
  1077.     * I used before getting Victor's code.  Victor Johansen's code is more 
  1078.     * efficient because it uses sys_gnfn(), but I have to do more work
  1079.     * in some cases.  With the ^'s, the directory name must be parsed 
  1080.     * and adjusted, before opening the correct directory.
  1081.     */
  1082.    char tempname[256],command[256];
  1083.    int pipe_call();
  1084.    FILE *sysout;
  1085.    char *curptr, *saveptr, buffer[257], *pos, *end;
  1086.    int n;
  1087.  
  1088.    debug(F110,"zxpand",fn,0);
  1089.    wildcarlb = 0;
  1090.    for (curptr = fn; *curptr; curptr++) 
  1091.         if (*curptr == '#') { wildcarlb = 0; goto nonvictor; }
  1092.    wildcarlb = 1; goto victor;
  1093.  
  1094. nonvictor:           
  1095.    curptr = command;
  1096.    sprintf(curptr, "filestatus/cpl=16/nheader %s", fn);
  1097.    curptr = tempname;
  1098.    debug(F110,"zxpand nonvictor command",curptr,0);
  1099.    pipe_call(command,curptr);
  1100.  
  1101.    /* Read the file of filenames, and parse out a universal name */
  1102.    sysout = fopen(tempname,"r");
  1103.    for (fcount=0; n=dg_fgets(buffer,256,sysout); ) {
  1104.        curptr = (char *) malloc(min(256,strlen(buffer)));
  1105.        mtchs[fcount]=curptr;
  1106.        /* delete leading spaces, leading directory name, and and trailing LF */
  1107.        if (iscntrl(*(pos = &buffer[strlen(buffer)-1])))
  1108.      *pos-- = '\0';
  1109.        /*
  1110.       First char will be =, if working dir, or : or @ if not.
  1111.       Delete the =, but keep others.
  1112.        */
  1113.        for (pos=buffer; *pos; pos++) {
  1114.        if (*pos == '=') break;
  1115.        if ((*pos == ':') || (*pos == '@'))  { pos--; break; }
  1116.        }
  1117.        strcpy(mtchs[fcount],pos+1);
  1118.        fcount++;
  1119.    }
  1120.    fclose(sysout);
  1121.    zdelet(tempname);
  1122.    debug(F101,"zxpand nonvictor count","",fcount);
  1123.  
  1124. victor:
  1125.    if (wildcarlb)
  1126.      fcount = fgen(fn,mtchs,MAXWLD); /* Look up the file. */
  1127.  
  1128.    debug(F101,"zxpand victor count","",fcount);
  1129.    if (fcount > 0) {
  1130.        mtchptr = mtchs;            /* Save pointer for next. */
  1131.    }
  1132.    nxpand = fcount;
  1133.    debug(F111,"zxpand",mtchs[0],fcount);
  1134.    return(fcount);
  1135. }
  1136.  
  1137. int
  1138. nzxpand(s,flags) char * s; int flags; {
  1139.     int x;
  1140.  
  1141.     debug(F111,"nzxpand",s,flags);
  1142.     x = flags & (ZX_DIRONLY|ZX_FILONLY);
  1143.     xdironly = (x == ZX_DIRONLY);
  1144.     xfilonly = (x == ZX_FILONLY);
  1145.     if (xdironly && xfilonly) {
  1146.     xdironly = 0;
  1147.     xfilonly = 0;
  1148.     }
  1149.     xrecursive = (flags & ZX_RECURSE);
  1150.     xnobackup  = (flags & ZX_NOBACKUP);
  1151.     x = zxpand(s);
  1152.     xdironly = 0;
  1153.     xfilonly = 0;
  1154.     xrecursive = 0;
  1155.     xnobackup = 0;
  1156.     return(x);
  1157. }
  1158.  
  1159. /*  Z X R E W I N D  --  Rewinds the zxpand() list */
  1160.  
  1161. int
  1162. zxrewind() {
  1163.     if (!mtchs) return(-1);
  1164.     fcount = nxpand;
  1165.     mtchptr = mtchs;
  1166.     return(nxpand);
  1167. }
  1168.  
  1169. /*  Z N E X T  --  Get name of next file from list created by zxpand(). */
  1170. /*
  1171.  Returns >0 if there's another file, with its name copied into the arg string,
  1172.  or 0 if no more files in list.
  1173. */
  1174. znext(fn) char *fn; {
  1175.     if (fcount-- > 0)
  1176.        {
  1177.           /* Victor Johansen's code requires no change in znext(), but my
  1178.            * code does.  The flag, wildcardlb is 1, if Victor's code is
  1179.            * in effect.
  1180.            */
  1181.           strcpy(fn,*mtchptr++);
  1182.           if (wildcarlb == 0)  /* My old code: Phil Julian */ 
  1183.                free (*(mtchptr-1));
  1184.        }
  1185.     else *fn = '\0';
  1186.     debug(F111,"znext",fn,fcount+1);
  1187.     return(fcount+1);
  1188. }
  1189.  
  1190. /*  Z C H K S P A  --  Check if there is enough space to store the file  */
  1191.  
  1192. /*
  1193.  Call with file specification f, size n in bytes.
  1194.  Returns -1 on error, 0 if not enough space, 1 if enough space.
  1195. */
  1196. int
  1197. zchkspa(f,n) char *f; long n; {         /* ENH - this is not feasible in VS */
  1198.     return(1);                          /* where the space returned is an   */
  1199. }                                       /* allocation limit rather than a   */
  1200.                                         /* physical one - so always return 1*/
  1201.  
  1202. /*  Z N E W N  --  Make a new name for the given file  */
  1203.  
  1204. znewn(fn,s) char *fn, **s; {
  1205.     static char buf[100];
  1206. #define ZNEWNMD 4                       /* Max digits for version number */
  1207.     char *bp, *xp;
  1208.     int len = 0, n = 0, d = 0, t, i, power = 1;
  1209.     int j, k;
  1210.     int max = MAXNAMLEN;
  1211.  
  1212.     bp = buf;
  1213.     while (*fn) {                       /* Copy name into buf */
  1214.         *bp++ = *fn++;
  1215.         len++;
  1216.     }
  1217.     if (len > max-2) {                  /* Don't let it get too long */
  1218.         bp = buf + max-2;
  1219.         len = max - 2;
  1220.     }
  1221.         
  1222.     for (i = 1; i < (ZNEWNMD - 1); i++) {
  1223.         power *= 10;
  1224.         *bp++ = '+';
  1225.         *bp-- = '\0';
  1226.         
  1227.         n = nzxpand(buf,1);        /* Expand the resulting wild name */
  1228.  
  1229.         while (n-- > 0) {               /* Find any existing name~d files */
  1230.             xp = *mtchptr++;
  1231.             xp += len;
  1232.             if (*xp == '.') {
  1233.                 t = atoi(xp+1);
  1234.                 if (t > d) d = t;       /* Get maximum d */
  1235.             }
  1236.         }
  1237.         if (d < power-1) {
  1238.             sprintf(bp,".%d",d+1);      /* Make name~(d+1) */
  1239.             *s = buf;
  1240.             return;
  1241.         }
  1242.         bp--; len--;
  1243.     }
  1244. /* If we ever get here, we'll overwrite the xxx~100 file... */
  1245. }
  1246.  
  1247. #ifndef NOFRILLS
  1248.  
  1249. int
  1250. zmail(p,f) char *p, *f; { /* Send f as mail to addr p */
  1251. /*
  1252.   Returns 0 on success
  1253.    2 if mail delivered but temp file can't be deleted
  1254.   -2 if mail can't be delivered
  1255.   The UNIX version always returns 0 because it can't get a good return
  1256.   code from zsyscmd.
  1257.  
  1258.     *zmbuf = '\0';                      /* Not implemented in AOS/VS Kermit */
  1259.     return(-2);
  1260. }
  1261. #endif /* NOFRILLS */
  1262.  
  1263. #ifndef NOFRILLS
  1264. int
  1265. zprint(p,f) char *p; char *f; {         /* Print file f with options p */
  1266.  
  1267.     sprintf(zmbuf,"qprint%s %s",p,f);  /* ENH - options must be specified */
  1268.     zsyscmd(zmbuf);                    /* by user as /<option>/<option>.. */
  1269.     return(0);
  1270. }
  1271. #endif /* NOFRILLS */
  1272.  
  1273. /*  Z R E N A M E  -- changes the name of old to new  */
  1274. /* 
  1275.  Returns -1 on failure, 0 on success
  1276. */
  1277. zrename(old,new) char *old, *new; {
  1278.     int     ac0,ac1,ac2,result;
  1279.  
  1280.     ac0 = (char *) old;
  1281.     ac1 = (char *) new;
  1282.     ac2 = 0;
  1283.  
  1284.     result = sys($RENAME,&ac0,&ac1,&ac2);
  1285.     if (result > 0) {
  1286.         debug(F110," rename fails",old,result); /* last arg is zero in unix */
  1287.         return(-1);                             /* - using result may not   */
  1288.     }                                           /* work                     */
  1289.     if (result < 0) {
  1290.         debug (F110," negative return from rename system call",old,result);
  1291.         return(-1);
  1292.     }
  1293.     return(0); 
  1294. }
  1295.  
  1296. /*  Z F C D A T  --  Get file creation date */
  1297. /*
  1298.   Call with pointer to filename.
  1299.   On success, returns pointer to creation date in yyyymmdd hh:mm:ss format.
  1300.   On failure, returns pointer to null string.
  1301. */
  1302. static char datbuf[40];
  1303.  
  1304. /* static */                            /* (===OS2 change===) */
  1305. char *
  1306. zfcdat(name) char *name; {
  1307.  
  1308. #ifdef TIMESTAMP
  1309.  
  1310.     P_FSTAT fs_pak;                             /* for AOS/VS only */
  1311.     int year,month,day,hours,minutes,seconds;
  1312.     int     error;
  1313.     int     ac0,ac1,ac2;                /* registers for fstat system call */
  1314.     long    date;
  1315.  
  1316.     ac0 = name;                         /* ptr to filename */
  1317.     ac1 = 0;                            /* resolve links */
  1318.     ac2 = (P_FSTAT *) &fs_pak;
  1319.     error = sys ($FSTAT, &ac0, &ac1, &ac2); /* get modification date */
  1320.     if (error != 0) {
  1321.         debug(F110,"zfcdat stat failed",name,error); 
  1322.         return("");
  1323.     }                                  
  1324.                                         
  1325.     date = fs_pak.stmh.long_time;       /* last modification date in seconds */
  1326.     error = dg_date(fs_pak.stmh.short_time[_DATE],&day,&month,&year);
  1327.     if (error != 0) {
  1328.         debug(F110,"zfcdat call to dg_date() failed","",error);
  1329.         return("");
  1330.     }
  1331.     error = dg_time(fs_pak.stmh.short_time[_TIME],&hours,&minutes,&seconds);
  1332.     if (error != 0) {
  1333.         debug(F110,"zfcdat call to dg_time() failed","",error);
  1334.         return("");
  1335.     }
  1336.     if (year < 100)
  1337.       year += 1900;
  1338.     if (year < 0 || year > 2100) {          /* Make sure year is ok */
  1339.         debug(F110,"zfcdat date failed",name,0);
  1340.         return("");
  1341.     }
  1342.     if (month  < 1 || month  > 12)
  1343.       return("");
  1344.     if (day < 0 || day > 31)
  1345.       return("");
  1346.     if (hours < 0 || hours > 23)
  1347.       return("");
  1348.     if (minutes  < 0 || minutes  > 59)
  1349.       return("");
  1350.     if (seconds < 0 || seconds  > 59)
  1351.       return("");
  1352.     sprintf(datbuf,"%04d%02d%02d %02d:%02d:%02d",year,month,day,hours,
  1353.                                                  minutes,seconds);
  1354.     debug(F111,"zfcdat",datbuf,strlen(datbuf));
  1355.     return(datbuf);
  1356.  
  1357. #else
  1358.     return ("");
  1359. #endif /* timestamp */
  1360. }
  1361.  
  1362. /* Z S T I M E  --  Set creation date for incoming file */
  1363. /*
  1364.  Call with:
  1365.  f  = pointer to name of existing file.
  1366.  yy = pointer to a Kermit file attribute structure in which yy->date.val
  1367.       is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00.
  1368.  x  = is a function code: 0 means to set the file's creation date as given.
  1369.       1 means compare the given date with the file creation date.
  1370.  Returns:
  1371.  -1 on any kind of error.
  1372.   0 if x is 0 and the file date was set successfully.
  1373.   0 if x is 1 and date from attribute structure <= file creation date.
  1374.   1 if x is 1 and date from attribute structure > file creation date.
  1375. */
  1376.  
  1377. int
  1378. zstime(f,yy,x) char *f; struct zattr *yy; int x; {
  1379.     int r = -1;                         /* return code */
  1380.     int     error;
  1381.     long tm;                            /* UNIX scalar form of date/time */
  1382.     char myname[] = "zstime()";
  1383.     long validate();
  1384.     int     vs_existing_datetime, vs_incoming_datetime;
  1385.     char    existing_kdate[20], *incoming_kdate = yy->date.val;
  1386.     void    convert_kdate_to_aosvs(),convert_aosvs_to_kdate();
  1387.  
  1388.     if ((x != 0) && (x != 1)) {
  1389.         debug(F111,"Bad argument",yy->date.val,yy->date.len);
  1390.         return (-1);
  1391.     }
  1392.     tm = validate(yy,myname);                  /* tm not used unless error */
  1393.     if (tm == -1) {
  1394.         debug(F111,"zstime fails on date check",incoming_kdate,yy->date.len);
  1395.         return (-1);
  1396.     }
  1397.     if ( error = fs_get_modification_date (f,&vs_existing_datetime) ) {
  1398.         debug(F110,"Can't stat file:",f,0);
  1399.         return(-1);
  1400.     }
  1401.     debug(F111,"zstime FSTAT of existing file okay",f,vs_existing_datetime);
  1402.     convert_kdate_to_aosvs(incoming_kdate,&vs_incoming_datetime);
  1403.     debug(F111,"zstime date of incoming file","",vs_incoming_datetime);
  1404.  
  1405.     /* ENH - modified for VS - we set the creation date in zopeno() */
  1406.     /* because the only straightforward way of setting it in VS is on the  */
  1407.     /* create; so, if x is 0 here we don't do anything                     */
  1408.  
  1409.     switch (x) {                        /* Execute desired function */
  1410.       case 0:                           /* Set the creation date of the file */
  1411.         debug(F110,"VS creation date intentionally not set in zstime(): ",f,0);
  1412.         r = 0;
  1413.         break;
  1414.       case 1:                           /* Compare the dates */
  1415.         convert_aosvs_to_kdate(existing_kdate,vs_existing_datetime);
  1416.         debug(F111,"zstime compare existing file date",existing_kdate,0);
  1417.         debug(F111,"zstime compare incoming file date",incoming_kdate,0);
  1418.         if (vs_incoming_datetime < vs_existing_datetime) r = 1; else r = 0;
  1419.         break;
  1420.       default:                          /* Error */
  1421.         r = -1;
  1422.     }
  1423.     return (r);
  1424. }
  1425.  
  1426.         
  1427. /*  Z S A T T R  --  fills in Kermit file attribute structure */
  1428. /*
  1429.  Returns 0 on success or -1 on falure.  Any string members that are null
  1430.  should be ignored by the caller; likewise, any numeric members that are
  1431.  -1 should be ignored.  (Actually, there is no error return right now.)
  1432. */
  1433. int
  1434. zsattr(xx) struct zattr *xx; {
  1435.     long k;
  1436.  
  1437.     if (iflen > 0)
  1438.         k = iflen % 1024L;                  /* file length in k bytes */
  1439.     else k = 0L;
  1440.     if (k != 0L) k = 1L;
  1441.     xx->lengthk = (iflen / 1024L) + k;
  1442.     xx->type.len = 0;                   /* file type can't be filled in here */
  1443.     xx->type.val = "";
  1444.     if (*nambuf) {
  1445.         xx->date.val = zfcdat(nambuf);  /* File creation (mod. in VS) date */
  1446.         xx->date.len = (int)strlen(xx->date.val);
  1447.     } else {
  1448.         xx->date.len = 0;
  1449.         xx->date.val = "";
  1450.     }
  1451.     xx->creator.len = 0;                /* File creator */
  1452.     xx->creator.val = "";
  1453.     xx->account.len = 0;                /* File account */
  1454.     xx->account.val = "";
  1455.     xx->area.len = 0;                   /* File area */
  1456.     xx->area.val = "";
  1457.     xx->password.len = 0;               /* Area password */
  1458.     xx->password.val = "";
  1459.     xx->blksize = -1L;                  /* File blocksize */
  1460.     xx->xaccess.len = 0;        /* File access */
  1461.     xx->xaccess.val = "";
  1462.     xx->encoding.len = 0;               /* Transfer syntax */
  1463.     xx->encoding.val = 0;
  1464.     xx->disp.len = 0;                   /* Disposition upon arrival */
  1465.     xx->disp.val = "";
  1466.     xx->lprotect.len = 0;               /* Local protection */
  1467.     xx->lprotect.val = "";
  1468.     xx->gprotect.len = 0;               /* Generic protection */
  1469.     xx->gprotect.val = "";
  1470.     xx->systemid.len = 2;               /* System ID */
  1471.     xx->systemid.val = "F3";
  1472.     xx->recfm.len = 0;                  /* Record format */
  1473.     xx->recfm.val = "";
  1474.     xx->sysparam.len = 0;               /* System-dependent parameters */
  1475.     xx->sysparam.val = "";
  1476.     xx->length = iflen;                 /* Length */
  1477.     return(0);
  1478. }
  1479.  
  1480.  
  1481.  
  1482. /* Directory Functions for Unix, written by Jeff Damens, CUCCA, 1984. */
  1483.  
  1484. /*
  1485.  * The path structure is used to represent the name to match.
  1486.  * Each slash-separated segment of the name is kept in one
  1487.  * such structure, and they are linked together, to make
  1488.  * traversing the name easier.
  1489.  */
  1490.  
  1491. struct path {
  1492.               char npart[MAXNAMLEN];    /* name part of path segment */
  1493.               struct path *fwd;         /* forward ptr */
  1494.             };
  1495.  
  1496. #define SSPACE 10000                    /* size of string-generating buffer */
  1497. static char sspace[SSPACE];             /* buffer to generate names in */
  1498.  
  1499. static char *freeptr,**resptr;          /* copies of caller's arguments */
  1500. static int remlen;                      /* remaining length in caller's array*/
  1501. static int numfnd;                      /* number of matches found */
  1502.  
  1503. /*
  1504.  * splitpath:
  1505.  *  takes a string and splits the slash-separated portions into
  1506.  *  a list of path structures.  Returns the head of the list.  The
  1507.  *  structures are allocated by malloc, so they must be freed.
  1508.  *  Splitpath is used internally by the filename generator.
  1509.  *
  1510.  * Input: A string.
  1511.  * Returns: A linked list of the slash-separated segments of the input.
  1512.  */
  1513.  
  1514. struct path * 
  1515. splitpath(p) char *p; {
  1516.     struct path *head,*cur,*prv;
  1517.     int i;
  1518.     head = prv = NULL;
  1519.  
  1520. debug(F110,"splitpath p",p,0);
  1521.  
  1522.     if (*p == '/') p++;            /* Skip leading slash */
  1523.     while (*p != '\0') {
  1524.     cur = (struct path *) malloc(sizeof (struct path));
  1525.     debug(F101,"splitpath malloc","",cur);
  1526.     if (cur == NULL) {
  1527.         debug(F100,"splitpath malloc failure","",0);
  1528.         return((struct path *)NULL);
  1529.     }
  1530.     cur -> fwd = NULL;
  1531.     if (head == NULL)
  1532.       head = cur;
  1533.     else
  1534.       prv -> fwd = cur;        /* Link into chain */
  1535.     prv = cur;
  1536.     for (i=0; i < MAXNAMLEN && *p != '/' && *p != '\0'; i++) {
  1537.         cur -> npart[i] = *p++;
  1538.     }
  1539.     cur -> npart[i] = '\0';      /* end this segment */
  1540.     if (i >= MAXNAMLEN)
  1541.       while (*p != '/' && *p != '\0') p++;
  1542.     if (*p == '/')
  1543.       p++;
  1544.     }
  1545.     return(head);
  1546. }
  1547.  
  1548. /*  F G E N  --  finds filenames that match string  */
  1549. /*
  1550.  * fgen:
  1551.  *  This is the actual name generator.  It is passed a string,
  1552.  *  possibly containing wildcards, and an array of character pointers.
  1553.  *  It finds all the matching filenames and stores them into the array.
  1554.  *  The returned strings are allocated from a static buffer local to
  1555.  *  this module (so the caller doesn't have to worry about deallocating
  1556.  *  them); this means that successive calls to fgen will wipe out
  1557.  *  the results of previous calls.  This isn't a problem here
  1558.  *  because we process one wildcard string at a time.
  1559.  *
  1560.  * Input: a wildcard string, an array to write names to, the
  1561.  *        length of the array.
  1562.  * Returns: the number of matches.  The array is filled with filenames
  1563.  *          that matched the pattern.  If there wasn't enough room in the
  1564.  
  1565.  *          array, -1 is returned.
  1566.  * By: Jeff Damens, CUCCA, 1984.
  1567.  */
  1568.  
  1569. fgen(pat,resarry,len) char *pat,*resarry[]; int len; {
  1570.  
  1571. /* Victor Johannsen helped out with this addition to Kermit.  The use of
  1572.  * sys_gnfn() is the proper way to solve this problem.  But, I would prefer
  1573.  * not building up the static string space.  This can be a problem when a
  1574.  * long, full-qualified directory name pattern is used, since this directory
  1575.  * name would be pre-pended to each file name.  
  1576.  *   A later fix would actually not build a list, but call sys_gnfn() within
  1577.  * znext(), and update the counters appropriately.  Several systems support
  1578.  * this model, which seems much more flexible and not at all space-limited.
  1579.  *   -- Phil Julian, 30 April 1987
  1580.  */
  1581.    P_GNFN          packet;                 /* packet for gnfn call */
  1582.    char            prefix[256];
  1583.    char            dirname[256];
  1584.    int             chan;                   /* Channel for open the DIR */
  1585.    FILE            *Ftemp;
  1586.    char            *DIRptr;                /* DIR name */
  1587.    int             i,off;
  1588.    int             got_dir = 0;
  1589.    char            *pattern,*cp;
  1590.  
  1591.    /* If a directory prefix is passed in, and possibly some wild cards, we
  1592.     * will need to parse off the directory to open.  We do not assume that
  1593.     * directories can be descended ad infinitum.
  1594.     */
  1595.    i = strlen(pat); zero(prefix,min(i+1,256));
  1596.    for (cp = pat+i-1; i-- >= 0; cp--) {
  1597.        if ((*cp == ':') || (*cp == '^') || (*cp == '=') || (*cp == '@')) {
  1598.        memcpy(prefix,pat,off = i+1);
  1599.        memcpy(dirname,prefix,off-1), dirname[off-1] = 0;
  1600.        DIRptr = dirname, pattern = cp+1;
  1601.        got_dir = 1; 
  1602.        /* Parse ^ for moving up a directory */
  1603.        if (*pat == '^') {
  1604.            /* From the tail of the current dir, back up to : */
  1605.            char *kp,*dp;
  1606.            int pos = 0;
  1607.            i = strlen(dp = getdir()) - 1;
  1608.            for (kp = pat; *kp == '^'; kp++,i--,pos++) 
  1609.          for (;dp[i] != ':';i--);
  1610.            i++;
  1611.            if (strlen(DIRptr)) {    /* Less ^s */
  1612.            memcpy(prefix,DIRptr+pos,strlen(DIRptr)-pos);
  1613.            prefix[strlen(DIRptr)-pos] = 0; /* Terminate */
  1614.            } else
  1615.          prefix[0] = 0;
  1616.            memcpy(dirname,dp,i);    /* The ^'d DIR */
  1617.            off -= pos;
  1618.            if (pos = strlen(prefix)) {
  1619.            memcpy(dirname+i,":",1);        /* Dir separator */
  1620.            memcpy(dirname+i+1,prefix,pos); /* Rest of dir name */
  1621.            }
  1622.            dirname[i+off] = 0;    /* Null terminate */
  1623.            memcpy(prefix,dirname,off = strlen(dirname));
  1624.            prefix[off] = ':'; prefix[++off] = 0;
  1625.        }
  1626.        break;
  1627.        }
  1628.    }
  1629.    if (got_dir == 0) {
  1630.        DIRptr = getdir();
  1631.        pattern = pat;
  1632.    }
  1633.    if ((Ftemp = fopen(DIRptr,"r")) == NULL)
  1634.      return(0);
  1635.    chan = fchannel(Ftemp);
  1636.  
  1637.    numfnd = 0;
  1638.    freeptr = sspace;
  1639.    resptr = resarry;
  1640.    remlen = len;
  1641.  
  1642.    packet.nfky = 0;
  1643.    packet.nftp = pattern;
  1644.    packet.nfnm = nambuf;
  1645.    
  1646.    while (i = !sys_gnfn(chan,&packet)) {
  1647.        if (got_dir == 0) {
  1648.        addresult( nambuf );
  1649.        } else {
  1650.        memcpy(prefix+off,nambuf,strlen(nambuf)+1);
  1651.        addresult( prefix );
  1652.        }
  1653.    }
  1654.    fclose(Ftemp);
  1655.    return(numfnd);            /* Return the number of matches */
  1656. }
  1657.  
  1658. /*  T R A V E R S E  -- searches directory for matches  */
  1659. /* traverse:
  1660.  *  Walks the directory tree looking for matches to its arguments.
  1661.  *  The algorithm is, briefly:
  1662.  *   If the current pattern segment contains no wildcards, that
  1663.  *   segment is added to what we already have.  If the name so far
  1664.  *   exists, we call ourselves recursively with the next segment
  1665.  *   in the pattern string; otherwise, we just return.
  1666.  *
  1667.  *   If the current pattern segment contains wildcards, we open the name
  1668.  *   we've accumulated so far (assuming it is really a directory), then read
  1669.  
  1670.  *   each filename in it, and, if it matches the wildcard pattern segment, add
  1671.  *   that filename to what we have so far and call ourselves recursively on the
  1672.  *   next segment.
  1673.  *
  1674.  *   Finally, when no more pattern segments remain, we add what's accumulated
  1675.  *   so far to the result array and increment the number of matches.
  1676.  *
  1677.  * Input: a pattern path list (as generated by splitpath), a string
  1678.  *        pointer that points to what we've traversed so far (this
  1679.  *        can be initialized to "/" to start the search at the root
  1680.  *        directory, or to "./" to start the search at the current
  1681.  *        directory), and a string pointer to the end of the string
  1682.  *        in the previous argument.
  1683.  * Returns: nothing.
  1684.  */
  1685. traverse(pl,sofar,endcur) struct path *pl; char *sofar,*endcur; {
  1686.     int fd;
  1687.     struct direct dir_entry;
  1688.     struct direct *dirbuf = &dir_entry;
  1689.     struct stat statbuf;
  1690.     if (pl == NULL) {
  1691.     *--endcur = '\0';        /* End string, overwrite trailing / */
  1692.     addresult(sofar);
  1693.     return;
  1694.     }
  1695.     if (!iswild(pl -> npart)) {
  1696.     strcpy(endcur,pl -> npart);
  1697.     endcur += (int)strlen(pl -> npart);
  1698.     *endcur = '\0';            /* End current string */
  1699.     if (stat(sofar,&statbuf) == 0) { /* If current piece exists */
  1700.         *endcur++ = DIRSEP;        /* append directory separator */
  1701.         *endcur = '\0';
  1702.         traverse(pl -> fwd,sofar,endcur);
  1703.     }
  1704.     return;
  1705.     }
  1706.     /* segment contains wildcards, have to search directory */
  1707.     *endcur = '\0';            /* End current string */
  1708.     if (stat(sofar,&statbuf) == -1)    /* Doesn't exist, forget it */
  1709.       return;
  1710.     if ((statbuf.st_mode & S_IFDIR) == 0) /* Not a directory, skip */
  1711.       return;
  1712.     if ((fd = open(sofar,O_RDONLY)) < 0) /* Can't open, forget it */
  1713.       return;
  1714.     while ( read(fd,dirbuf,sizeof dir_entry) ) {
  1715.     /* Get null-terminated copy */
  1716.     strncpy(nambuf,dirbuf->d_name,MAXNAMLEN);
  1717.     nambuf[MAXNAMLEN] = '\0';
  1718.     if (dirbuf->d_ino != 0 && match(pl -> npart,nambuf)) {
  1719.         char *eos;
  1720.         strcpy(endcur,nambuf);
  1721.         eos = endcur + strlen(nambuf);
  1722.         *eos++ = DIRSEP;        /* End this segment */
  1723.         traverse(pl -> fwd,sofar,eos);
  1724.     }
  1725.     }
  1726.     close(fd);
  1727. }
  1728.  
  1729. /*  A D D R E S U L T  --  Adds result string to result array  */
  1730. /*
  1731.  * addresult:
  1732.  *  Adds a result string to the result array.  Increments the number
  1733.  *  of matches found, copies the found string into our string
  1734.  *  buffer, and puts a pointer to the buffer into the caller's result
  1735.  *  array.  Our free buffer pointer is updated.  If there is no
  1736.  *  more room in the caller's array, the number of matches is set to -1.
  1737.  * Input: a result string.
  1738.  * Returns: nothing.
  1739.  
  1740.  */
  1741. static VOID 
  1742. addresult(str) char *str; {
  1743.     int l;
  1744.     debug(F111,"addresult",str,remlen);
  1745.     if (xdironly && !isdir(str))
  1746.       return;
  1747.     if (strncmp(str,"./",2) == 0) str += 2;
  1748.     if (--remlen < 0) {
  1749.     numfnd = -1;
  1750.     return;
  1751.     }
  1752.     l = strlen(str) + 1;        /* size this will take up */
  1753.     if ((freeptr + l) > &sspace[SSPACE]) {
  1754.     numfnd = -1;            /* do not record if not enough space */
  1755.     return;
  1756.     }
  1757.     strcpy(freeptr,str);
  1758.     *resptr++ = freeptr;
  1759.     freeptr += l;
  1760.     numfnd++;
  1761. }
  1762.  
  1763. /*  Z S Y S C M D  --  Supposed to execute a "system" command.  */
  1764. /*
  1765.  Code copied verbatim from zshcmd for now
  1766. */
  1767. int
  1768. zsyscmd(s) char *s; {
  1769.  
  1770.     if (priv_chk()) return(1);
  1771.     if (*s == '\0')                     /* Interactive shell requested? */
  1772.  
  1773. #ifdef mvux
  1774.         system("/bin/sh ");
  1775. #else
  1776.         system("x :cli prefix Kermit_Baby:");
  1777. #endif /* mvux */
  1778.  
  1779.     else                                /* Otherwise, */
  1780.         system(s);                      /* Best for aos/vs?? */
  1781.     return (0);                         /* Assume no errors */
  1782. }
  1783.  
  1784.  
  1785. /*  Z S H C M D  --  Start a CLI */
  1786. int
  1787. zshcmd(s) char *s; {                    /* ENH - new function */
  1788.  
  1789.     if (priv_chk()) return(1);
  1790.     if (*s == '\0')                     /* Interactive shell requested? */
  1791.  
  1792. #ifdef mvux
  1793.         system("/bin/sh ");
  1794. #else
  1795.         system("x :cli prefix Kermit_CLI:");
  1796. #endif /* mvux */
  1797.  
  1798.     else                                /* Otherwise, */
  1799.         system(s);                      /* Best for aos/vs?? */
  1800.     return (0);                         /* Assume no errors */
  1801. }
  1802.  
  1803. /*  I S W I L D  --  Check if filespec is "wild"  */
  1804. /*
  1805.   Returns 0 if it is a single file, 1 if it contains wildcard characters.
  1806.   Note:  must match the algorithm used by match(), hence no [a-z], etc
  1807. */  
  1808. int
  1809. iswild(filespec) char *filespec; {
  1810.     char c; int x; char *p;
  1811.     while ((c = *filespec++) != '\0') {
  1812.     if (c == '+'  ||        /* Match any character string */
  1813.         c == '*'  ||        /* Match any character but .  */
  1814.         c == '-'  ||        /* Match any string up to .   */
  1815.         c == '\\' ||        /* Except ... */
  1816.         c == '#'            /* Inferior directories */
  1817.         )
  1818.       return(1);
  1819.     }
  1820.     return(0);
  1821. }
  1822. /*  M A T C H  --  see if pattern matches string  */
  1823. /*
  1824.  * match:
  1825.  *  pattern matcher.  Takes a string and a pattern possibly containing
  1826.  *  the wildcard characters '*' and '?'.  Returns true if the pattern
  1827.  *  matches the string, false otherwise.
  1828.  * by: Jeff Damens, CUCCA
  1829.  
  1830.  *
  1831.  * Input: a string and a wildcard pattern.
  1832.  * Returns: 1 if match, 0 if no match.
  1833.  */
  1834.  
  1835. match(pattern,string) char *pattern,*string; {
  1836.     char *psave,*ssave;                 /* back up pointers for failure */
  1837.     psave = ssave = NULL;
  1838.     while (1) {
  1839.         for (; *pattern == *string; pattern++,string++)  /* skip first */
  1840.             if (*string == '\0') return(1);     /* end of strings, succeed */
  1841.         if (*string != '\0' && *pattern == '?') {
  1842.             pattern++;                  /* '?', let it match */
  1843.             string++;
  1844.         } else if (*pattern == '*') {   /* '*' ... */
  1845.             psave = ++pattern;          /* remember where we saw it */
  1846.             ssave = string;             /* let it match 0 chars */
  1847.         } else if (ssave != NULL && *ssave != '\0') {   /* if not at end  */
  1848.                                         /* ...have seen a star */
  1849.             string = ++ssave;           /* skip 1 char from string */
  1850.             pattern = psave;            /* and back up pattern */
  1851.         } else return(0);               /* otherwise just fail */
  1852.     }
  1853. }
  1854.  
  1855. /* C H E C K _ K E R M I T _ D A T E  --  see if Kermit date is valid */
  1856. /*
  1857.  This function is new in the VS version of Kermit only.  It replaces code
  1858.  that was previously in-line in zstime() that checks the validity of the
  1859.  date found in an argument passed in, and if it's valid, converts it to a
  1860.  scalar, stored in tm, which is returned.  It takes a second argument, which
  1861.  is the name of the calling function.  This is there so that entries to 
  1862.  log will report the accurate Kermit function.  It returns the value of
  1863.  tm if the date is valid and a -1 if not.                       --ENH
  1864. */
  1865. long
  1866. validate (yy,caller) struct zattr *yy; char *caller; {
  1867.     int r = -1;                         /* return code */
  1868.     long tm, days;
  1869.     int i, n, isleapyear;
  1870.                    /*       J  F  M  A   M   J   J   A   S   O   N   D   */
  1871.                    /*      31 28 31 30  31  30  31  31  30  31  30  31   */
  1872.     static
  1873.     int monthdays [13] = {  0,0,31,59,90,120,151,181,212,243,273,304,334 };
  1874.     char s[5];
  1875.     struct stat sb;
  1876.  
  1877.     debug(F111,caller," calling validate()",0);
  1878.     if ((yy->date.len == 0)
  1879.         || (yy->date.len != 17)
  1880.         || (yy->date.val[8] != ' ')
  1881.         || (yy->date.val[11] != ':')
  1882.         || (yy->date.val[14] != ':') ) {
  1883.         debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1884.         return(-1);
  1885.     }
  1886.     debug(F111,"Date check 1",yy->date.val,yy->date.len);
  1887.     for(i = 0; i < 8; i++) {
  1888.         if (!isdigit(yy->date.val[i])) {
  1889.             debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1890.             return(-1);
  1891.         }
  1892.     }
  1893.     debug(F111,"Date check 2",yy->date.val,yy->date.len);
  1894.     i++;
  1895.  
  1896.     for (; i < 16; i += 3) {
  1897.         if ((!isdigit(yy->date.val[i])) || (!isdigit(yy->date.val[i + 1]))) {
  1898.             debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1899.             return(-1);
  1900.         }
  1901.     }
  1902.     debug(F111,"Date check 3",yy->date.val,yy->date.len);
  1903.     debug(F100,"So far so good","",0);
  1904.  
  1905.     s[4] = '\0';
  1906.     for (i = 0; i < 4; i++)     /* Fix the year */
  1907.       s[i] = yy->date.val[i];
  1908.  
  1909.     debug(F110,"year",s,0);
  1910.  
  1911.     n = atoi(s);
  1912.  
  1913.     debug(F111,"year",s,n);
  1914.  
  1915. /* Previous year's leap days. This won't work after year 2100, */
  1916. /* I don't care about that! */
  1917.  
  1918.     isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
  1919.     days = (long) (n - 1970) * 365;
  1920.     days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
  1921.  
  1922.     s[2] = '\0';
  1923.  
  1924.     for (i = 4 ; i < 16; i += 2) {
  1925.         s[0] = yy->date.val[i];
  1926.         s[1] = yy->date.val[i + 1];
  1927.         n = atoi(s);
  1928.         debug(F110,"zstime entering switch",s,0);
  1929.         switch (i) {
  1930.           case 4:                       /* MM: month */
  1931.             if ((n < 1 ) || ( n > 12)) {
  1932.                 debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1933.                 return(-1);
  1934.             }
  1935.             days += monthdays [n];
  1936.             if (isleapyear && n > 2)
  1937.               ++days;
  1938.             continue;
  1939.  
  1940.           case 6:                       /* DD: day */
  1941.             if ((n < 1 ) || ( n > 31)) {
  1942.                 debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1943.                 return(-1);
  1944.             }
  1945.             tm = (days + n - 1) * 24L * 60L * 60L;
  1946.             i++;                        /* Skip the space */
  1947.             continue;
  1948.  
  1949.           case 9:                       /* hh: hour */
  1950.             if ((n < 0 ) || ( n > 23)) {
  1951.                 debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1952.                 return(-1);
  1953.             }
  1954.             tm += n * 60L * 60L;
  1955.             i++;                        /* Skip the colon */
  1956.             continue;
  1957.  
  1958.           case 12:                      /* mm: minute */
  1959.             if ((n < 0 ) || ( n > 59)) {
  1960.                 debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1961.                 return(-1);
  1962.             }
  1963.             tm += n * 60L;
  1964.             i++;                        /* Skip the colon */
  1965.             continue;
  1966.  
  1967.           case 15:                      /* ss: second */
  1968.             if ((n < 0 ) || ( n > 59)) {
  1969.                 debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
  1970.                 return(-1);
  1971.             }
  1972.             tm += n;
  1973.         }
  1974.  
  1975.         if (localtime(&tm)->tm_isdst)
  1976.           tm -= 60L * 60L;              /* Adjust for daylight savings time */
  1977.     }
  1978.  
  1979.     debug(F111,"Attribute creation date ok ",yy->date.val,yy->date.len);
  1980.  
  1981.     return(tm);
  1982.  
  1983. /*  F I L E _ E X I S T S  -- returns zero if filename already exists  */
  1984. /*
  1985.  Takes a pointer to a filename as an argument and determines whether that
  1986.  file exists.  Returns 1 if it does, 0 if not, -1 on other error.
  1987. */
  1988. int
  1989. file_exists(name) char *name; {
  1990.  
  1991.     P_FSTAT fs_pak;
  1992.     int ac0,ac1,ac2,error;
  1993.  
  1994.     ac0 = name;
  1995.     ac1 = 0;                                    /* resolve links */
  1996.     ac2 = (P_FSTAT *) &fs_pak;
  1997.     error = sys($FSTAT,&ac0,&ac1,&ac2);
  1998.     if (error == ERFDE)                         /* file doesn't exist */
  1999.         return (0);
  2000.     if (error == 0)                             /* file does exist */
  2001.         return (1);
  2002.     else                                        /* some other error */
  2003.         return (-1);
  2004. }
  2005.  
  2006.  
  2007. /*  S E T _ C R E A T I O N _ D A T E  --  set creation date on VS file  */
  2008. /*
  2009.  Takes a pointer to a filename and a timestamp in Kermit format and sets
  2010.  the creation date/time of the file -- returns 0 on success, -1 on failure
  2011. */                                      /* Contributed by:               */
  2012. int                                     /* Larry McCoskery, Data General */
  2013. set_creation_date( filename, date_time )
  2014.     char *  filename;
  2015.     char *  date_time;
  2016. {
  2017.     int     ac0,ac1,ac2,error;
  2018.     int     vs_datetime;                   /* VS scalar */
  2019.  
  2020.     P_CREATE    crepak;
  2021.     P_CTIM      timepak;
  2022.     void    convert_kdate_to_aosvs();
  2023.  
  2024.     /*
  2025.      *  First, convert the silly date.  EH assures me that it's ok
  2026.      */
  2027.  
  2028.     convert_kdate_to_aosvs ( date_time, &vs_datetime);
  2029.  
  2030.     /*
  2031.      *  Build the time packet. Set TCR, TLM, and TLA to specified date
  2032.      */
  2033.  
  2034.     timepak.tcth.long_time =
  2035.     timepak.tath.long_time =
  2036.     timepak.tmth.long_time = vs_datetime;
  2037.  
  2038.     /*
  2039.      *  Build the create packet
  2040.      */
  2041.  
  2042.     crepak.cftyp_format =   0;     /* record format */
  2043.     crepak.cftyp_entry  =   $FUDF; /* entry type */
  2044.     crepak.ccps         =   0;     /* file control parameters */
  2045.     crepak.ctim         =   &timepak;   /* address of time block */
  2046.     crepak.cacp         =   -1;           /* address of ACL */
  2047.     crepak.cdeh         =   0;     /* reserved */
  2048.     crepak.cdel         =   -1;    /* file element size */
  2049.     crepak.cmil         =   -1;    /* maximum index levels */
  2050.     crepak.cmrs         =   0;     /* reserved */
  2051.  
  2052.     /*
  2053.      *  Create it
  2054.      */
  2055.  
  2056.     ac0 = (char *) filename;
  2057.     ac1 = 0;        /* reserved */
  2058.     ac2 = (P_CREATE *) &crepak;
  2059.  
  2060.     error = sys( $CREATE, &ac0, &ac1, &ac2 );
  2061.  
  2062.     if( error != NULL ) {
  2063.         debug(F111, "can't create file - VS error",filename,error);
  2064.         error = -1;                         /* unix style "hide the error" */
  2065.     }
  2066.     else debug(F111,"file created",filename,0);
  2067.     return  error;
  2068. }
  2069.  
  2070. void
  2071. convert_kdate_to_aosvs( kermit_date, vs_scalar )
  2072.     char *  kermit_date;
  2073.     int  *  vs_scalar;
  2074. {
  2075.     int     month, day, year, hour, minute, second;
  2076.     int     date, time;
  2077.     char    c;
  2078.     short * sp = vs_scalar;
  2079.  
  2080.     sscanf( kermit_date,"%4d%2d%2d%c%2d%c%2d%c%2d",
  2081.         &year,&month,&day,&c,&hour,&c,&minute,&c,&second );
  2082.  
  2083.     convert_date_to_dg( day, month, year, &date );
  2084.     convert_time_to_dg( hour, minute, second, &time );
  2085.  
  2086.     sp[0] = (short) date;
  2087.     sp[1] = (short) time;
  2088. }
  2089.  
  2090. void
  2091. convert_aosvs_to_kdate( string, aosvs_scalar )
  2092.     char *  string;
  2093.     int     aosvs_scalar;
  2094. {
  2095.     int     day,month,year,hours,minutes,seconds;
  2096.     short * sp = &aosvs_scalar;
  2097.  
  2098.     dg_date( (int) sp[0], &day, &month, &year );
  2099.     dg_time( (int) sp[1], &hours, &minutes, &seconds );
  2100.  
  2101.     sprintf( string, "%4d%02d%02d %02d:%02d:%02d",
  2102.         year,month,day,hours,minutes,seconds );
  2103. }
  2104.  
  2105. int
  2106. convert_date_to_dg( day, month, year, scalar )
  2107.     int     day,month,year,*scalar;
  2108. {
  2109.     int     ac0,ac1,ac2,error;
  2110.  
  2111.     ac0 = day;
  2112.     ac1 = month;
  2113.     ac2 = year - 1900;
  2114.  
  2115.     error = sys( $FDAY, &ac0, &ac1, &ac2 );
  2116.  
  2117.     *scalar = ac0;
  2118.  
  2119.     return error;
  2120. }
  2121.  
  2122. int
  2123. convert_time_to_dg( hours, mins, secs, scalar )
  2124.     int     hours, mins, secs, *scalar;
  2125. {
  2126.     int     ac0,ac1,ac2,error; 
  2127.  
  2128.     ac0 = secs;
  2129.     ac1 = mins;
  2130.     ac2 = hours;
  2131.  
  2132.     error = sys( $FTOD, &ac0, &ac1, &ac2 );
  2133.  
  2134.     *scalar = ac0;
  2135.  
  2136.     return  error;
  2137. }
  2138.  
  2139. int
  2140. dg_date( scalar, day, month, year )
  2141.     int     scalar, *day, *month, *year;
  2142. {
  2143.     int     ac0,ac1,ac2,error;
  2144.  
  2145.     ac0 = scalar;
  2146.     ac1 = ac2 = 0;
  2147.     error = sys( $CDAY, &ac0, &ac1, &ac2 );
  2148.  
  2149.     *day = ac0;
  2150.     *month = ac1;
  2151.     *year = ac2 + 1900;
  2152.  
  2153.     return error;
  2154. }
  2155.  
  2156. int
  2157. dg_time( scalar, hours, mins, secs )
  2158.     int     scalar, *hours, *mins, *secs;
  2159. {
  2160.     int     ac0,ac1,ac2,error;
  2161.  
  2162.     ac0 = scalar;
  2163.     ac1 = ac2 = 0;
  2164.     error = sys( $CTOD, &ac0, &ac1, &ac2 );
  2165.  
  2166.     *hours = ac2;
  2167.     *mins  = ac1;
  2168.     *secs  = ac0;
  2169.  
  2170.     return error;
  2171. }
  2172.  
  2173. fs_get_modification_date( filename, date )
  2174.     char *  filename;
  2175.     int  *  date;
  2176. {
  2177.     P_FSTAT fs_pak;
  2178.     int ac0,ac1,ac2,error;
  2179.  
  2180.     ac0 = filename;
  2181.     ac1 = 0;                                    /* resolve links */
  2182.     ac2 = (P_FSTAT *) &fs_pak;
  2183.     error = sys($FSTAT,&ac0,&ac1,&ac2);
  2184.  
  2185.     *date = fs_pak.stmh.long_time;
  2186.  
  2187.     return  error;
  2188. }
  2189.  
  2190. int
  2191. get_dir(dirbuf)
  2192. char *dirbuf;
  2193. {
  2194.     int ac0,ac1,ac2,error;
  2195.  
  2196.     ac0 = (char *) "=";
  2197.     ac1 = (char *) dirbuf;
  2198.     ac2 = MAXPATH+1;
  2199.  
  2200.     error = sys($GNAME,&ac0,&ac1,&ac2);
  2201.     if (error == NULL)
  2202.         *(dirbuf+ac2) = '\000';        /* null terminator */
  2203.  
  2204.     return(error);
  2205. }    
  2206.  
  2207. /* Z F S E E K  --  Position input file pointer */
  2208. /*
  2209.    Call with:
  2210.     Long int, 0-based, indicating desired position.
  2211.    Returns:
  2212.     0 on success.
  2213.    -1 on failure.
  2214. */
  2215. #ifndef NORESEND
  2216. int
  2217. #ifdef CK_ANSIC
  2218. zfseek(long pos)
  2219. #else
  2220. zfseek(pos) long pos;
  2221. #endif /* CK_ANSIC */
  2222. /* zfseek */ {
  2223.     debug(F101,"zfseek","",pos);
  2224.     return(fseek(fp[ZIFILE], pos, 0));
  2225. }
  2226. #endif /* NORESEND */
  2227.  
  2228. static struct stat ckdfstat;
  2229.  
  2230. int
  2231. isdir(s) char *s; {
  2232.     int x; long y;
  2233.     P_FSTAT buf;
  2234.     int ac0,ac2;
  2235.     char * temp = NULL;
  2236.  
  2237.     if (!s) return(0);
  2238.  
  2239.     debug(F110,"isdir s",s,0);
  2240.     if (!*s) return(0);
  2241.     if (ISDIRSEP(*s) && *(s+1) == NUL)
  2242.       return(1);
  2243.     if (!strcmp(s,".."))
  2244.       return(1);
  2245.  
  2246. #ifdef COMMENT
  2247.     /* stat() can crash or loop */
  2248.     x = stat(s,&ckdfstat);
  2249.     if (x == -1) {
  2250.     debug(F101,"isdir errno","",errno);
  2251.     return(0);
  2252.     }
  2253.     x = (ckdfstat.st_mode & S_IFDIR);
  2254.     return(x ? 1 : 0);
  2255. #else
  2256.     /* Because ?fstat looks in searchlist */
  2257.     temp = alloc(strlen(s)+2);
  2258.     if ((*s != '^') && (*s != '@') && (*s != ':') && (*s != '=')) {
  2259.         strcpy(temp+1,s);
  2260.         *temp = '=';
  2261.     } else
  2262.       strcpy (temp,s);
  2263.     ac0 = (int) temp;
  2264.     ac2 = (int) &buf;
  2265.     x = sys_fstat(ac0,0,ac2);
  2266.     free(temp);
  2267.     if (x != 0) {
  2268.         debug(F111,"isdir sys_fstat fails",s,x);
  2269.         return(0);
  2270.     }
  2271.     x = buf.styp_type;                  /* File-format field */
  2272.     debug(F111,"isdir sys_fstat type",s,buf.styp_type);
  2273.     return(((x >= $LDIR) && (x <= $HDIR)) ? 1 : 0);
  2274. #endif /* COMMENT */
  2275. }
  2276.  
  2277. struct zfnfp *
  2278. zfnqfp(fname, buflen, buf)  char * fname; int buflen; char * buf; {
  2279.     int x = 0, y = 0;
  2280.     char * xp;
  2281.     static struct zfnfp fnfp;
  2282.  
  2283.     if (!fname)
  2284.       return(NULL);
  2285.  
  2286. debug(F110,"zfnqfp fname",fname,0);
  2287.  
  2288.     /* initialize the data structure */
  2289.     fnfp.len = buflen;
  2290.     fnfp.fpath = buf;
  2291.     fnfp.fname = NULL;
  2292.  
  2293.  
  2294.  
  2295.     if (*fname == DIRSEP) {        /* Pathname is absolute */
  2296.     strncpy(buf,fname,buflen);
  2297.     x = strlen(buf);
  2298.     y = 0;
  2299.     } else {                /* Pathname is relative */
  2300.     char * p;
  2301.     if (p = zgtdir()) {        /* So get current directory */
  2302.         strncpy(buf,p,buflen);
  2303.         x = strlen(buf);        
  2304.         if (buf[x] != DIRSEP)
  2305.           buf[x++] = DIRSEP;
  2306.         buflen -= x;        /* How much room left in buffer */
  2307.         if ((y = (int)strlen(fname)) > buflen) /* If enough room... */
  2308.           return(NULL);
  2309.         strncpy(buf+x,fname,buflen); /* ... append the filename */
  2310.     } else {
  2311.         return(NULL);
  2312.     }
  2313.     }
  2314.     for (x = x + y - 1; x > -1; x--)    /* Find where the filename starts */
  2315.       if (buf[x] == DIRSEP) {        /* There is guaranteed to be one */
  2316.       fnfp.fname = buf + x;        /* Got it, set pointer */
  2317.       fnfp.len = (int) strlen(buf);
  2318.       return(&fnfp);        /* and return. */
  2319.       }
  2320.     return(NULL);
  2321. }
  2322.  
  2323. /* Z M K D I R  --  Create directory(s) if necessary */
  2324. /*
  2325.    Call with:
  2326.     A pointer to a file specification that might contain directory
  2327.     information.  The filename is expected to be included.
  2328.     If the file specification does not include any directory separators,
  2329.     then it is assumed to be a plain file.
  2330.     If one or more directories are included in the file specification,
  2331.     this routine tries to create them if they don't already exist.
  2332.    Returns:
  2333.     0 on success, i.e. the directory was created
  2334.    -1 on failure to create the directory
  2335. */
  2336. int
  2337. zmkdir(path) char *path; {
  2338.     char *xp, *tp, c;
  2339.     int x;
  2340.  
  2341.     x = strlen(path);
  2342.     debug(F111,"zmkdir",path,x);
  2343.     if (x < 1 || x > MAXPATH)        /* Check length */
  2344.       return(-1);
  2345.     if (!(tp = malloc(x+1)))        /* Make a temporary copy */
  2346.       return(-1);
  2347.     strcpy(tp,path);
  2348.     xp = tp;
  2349.     if (ISDIRSEP(*xp))            /* Don't create root directory! */
  2350.       xp++;
  2351.  
  2352.     /* Go thru filespec from left to right... */
  2353.  
  2354.     for (; *xp; xp++) {            /* Create parts that don't exist */
  2355.     if (!ISDIRSEP(*xp))        /* Find next directory separator */
  2356.       continue;
  2357.     c = *xp;            /* Got one. */
  2358.     *xp = NUL;            /* Make this the end of the string. */
  2359.     if (!isdir(tp)) {        /* This directory exists already? */
  2360.         debug(F110,"zmkdir making",tp,0);
  2361.         x = mkdir(tp,0777);
  2362.         if (x < 0) {
  2363.         debug(F101,"zmkdir failed, errno","",errno);
  2364.         free(tp);        /* Free temporary buffer. */
  2365.         tp = NULL;
  2366.         return(-1);        /* Return failure code. */
  2367.         }
  2368.     }
  2369.     *xp = c;            /* Replace the separator. */
  2370.     }
  2371.     free(tp);                /* Free temporary buffer. */
  2372.     return(0);                /* Return success code. */
  2373. }
  2374.