home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / ckc190.zip / ckdfio.c < prev    next >
C/C++ Source or Header  |  1994-06-25  |  74KB  |  2,196 lines

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