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

  1. char *ckzv = "Data General file support, 4C(034) 24 Jan 88";
  2.  
  3. /* C K U F I O  --  Kermit file system support for Unix systems */
  4.  
  5. /*
  6.  Author: Frank da Cruz (SY.FDC@CU20B),
  7.  Columbia University Center for Computing Activities, January 1985.
  8.  Copyright (C) 1985, Trustees of Columbia University in the City of New York.
  9.  Permission is granted to any individual or institution to use, copy, or
  10.  redistribute this software so long as it is not sold for profit, provided this
  11.  copyright notice is retained.
  12.   
  13.  This module was adapted to the Data General computers by:
  14.  
  15.     Phil Julian, SAS Institute, Inc., Box 8000, Cary, NC 27512-8000
  16.   
  17.  Acknowledgements for addtional help:
  18.  
  19.     For using sys_gnfn() in fgen(), instead of my kludge:
  20.         Victor Johansen, Micro_rel, 2343 W 10th Place, Tempe AZ 85281
  21.  
  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.     
  26. */
  27. /* Includes */
  28.  
  29. #include "ckcker.h"            /* Kermit definitions */
  30. #include "ckcdeb.h"            /* Typedefs, debug formats, etc */
  31. #include <ctype.h>            /* Character types */
  32. #include <stdio.h>            /* Standard i/o */
  33.  
  34. #ifdef datageneral
  35. /* The DG compiler version 3.21 has a bug in the get*id() functions, which
  36.  * cause the program to go into an infinite loop.  These functions should
  37.  * return -1 unless the system is in a UNIX environment, so I made the
  38.  * appropriate kludges.   -- Phil Julian, 8 April 87
  39.  */
  40. #ifndef dgux
  41. #define getgid() -1
  42. #define getuid() -1
  43. #define geteuid() -1
  44. #endif dgux
  45. #include <memory.h>
  46. #include <dglib.h>
  47. #include <sys_calls.h>
  48. #include <packets:process.h>
  49. #include <packets/filestatus.h>         /* Used for ?GNFN */
  50. #include <paru.h>
  51. #include <bit.h>
  52. #define fork() vfork()
  53. int wildcarlb;                          /* Wild card ^ or # */
  54.  
  55. #else  
  56. #include <sys/types.h>            /* Data types */
  57. #include <pwd.h>            /* Password file for shell name */
  58. #endif datageneral
  59.  
  60. #include <sys/dir.h>            /* Directory structure */
  61. #include <sys/stat.h>            /* File status */
  62.  
  63. /* Berkeley Unix Version 4.x */
  64. /* 4.1bsd support added by Charles E Brooks, EDN-VAX */
  65.  
  66. #ifdef BSD4
  67. #ifdef MAXNAMLEN
  68. #define BSD42
  69. char *ckzsys = " 4.2 BSD";
  70. #else
  71. #ifdef FT17
  72. #define BSD41
  73. char *ckzsys = " For:Pro Fortune 1.7";
  74. #else
  75. #define BSD41
  76. char *ckzsys = " 4.1 BSD";
  77. #endif
  78. #endif
  79. #endif
  80.  
  81. /* 2.9bsd support contributed by Bradley Smith, UCLA */
  82. #ifdef BSD29
  83. char *ckzsys = " 2.9 BSD";
  84. #endif
  85.  
  86. /* Version 7 Unix  */
  87. #ifdef V7
  88. char *ckzsys = " Version 7 Unix";
  89. #endif
  90.  
  91. /* Datageneral support contributed by Phil Julian, SAS Institute, Inc. */
  92. #ifdef dgux
  93. char *ckzsys = " Data General DG/UX";
  94. #else 
  95. #ifdef datageneral
  96. char *ckzsys = " Data General AOS/VS";
  97. #endif
  98. #endif
  99.  
  100. /* DEC Professional-300 series with Venturcom Venix v1 */
  101. #ifdef PROVX1
  102. char *ckzsys = " DEC Pro-3xx/Venix v1";
  103. #endif
  104.  
  105.  
  106. /* NCR Tower support contributed by John Bray, Auburn, AL. */
  107. /* Tower OS is like Sys III but with BSD features -- mostly follows BSD. */
  108. #ifdef TOWER1
  109. char *ckzsys = " NCR Tower 1632, OS 1.02";
  110. #endif
  111.  
  112. /* Sys III/V, Xenix, PC/IX,... support by Herm Fischer, Litton Data Systems */
  113. #ifdef UXIII
  114. #ifdef XENIX
  115. char *ckzsys = " Xenix/286";
  116. #else
  117. #ifdef PCIX
  118. char *ckzsys = " PC/IX";
  119. #else
  120. #ifdef ISIII
  121. char *ckzsys = " Interactive Systems Corp, System III";
  122. #else
  123. #ifndef datageneral
  124. char *ckzsys = " AT&T System III/System V";
  125. #endif
  126. #endif
  127. #endif
  128. #endif
  129. #endif
  130.  
  131. /* Definitions of some Unix system commands */
  132.  
  133. #ifdef datageneral
  134. /* Definitions of system commands for AOS/VS */
  135.  
  136. char *DIRCMD = "filestatus/sort/assortment ";    /* For directory listing */
  137. char *DELCMD = "delete/v ";                /* For file deletion */
  138. char *TYPCMD = "type ";                /* For typing a file */
  139. char *PWDCMD = "directory ";            /* For saying where I am */
  140. /* Space/quota of home, not current directory.  Note that 31 characters is
  141.  * the longest name length, and so the home directory name cannot be
  142.  * longer.
  143.  */
  144. char *SPACMD = "space :udd:                               ";
  145. char *SPACM2 = "space ";        /* For space in specified directory */
  146. char *WHOCMD = "who ";
  147.  
  148. #else
  149. char *DIRCMD = "ls -l ";        /* For directory listing */
  150. char *DELCMD = "rm -f ";        /* For file deletion */
  151. char *TYPCMD = "cat ";            /* For typing a file */
  152. char *PWDCMD = "pwd ";            /* For saying where I am */
  153.  
  154. #ifdef BSD4
  155. char *SPACMD = "pwd ; quota ; df .";    /* Space/quota of current directory */
  156. #else
  157. char *SPACMD = "df ";
  158. #endif
  159.  
  160. char *SPACM2 = "df ";            /* For space in specified directory */
  161.  
  162. #ifdef BSD4
  163. char *WHOCMD = "finger ";        /* For seeing who's logged in */
  164. #else
  165. char *WHOCMD = "who ";            /* For seeing who's logged in */
  166. #endif
  167. #endif
  168.  
  169.  
  170. /*
  171.   Functions (n is one of the predefined file numbers from ckermi.h):
  172.  
  173.    zopeni(n,name)   -- Opens an existing file for input.
  174.    zopeno(n,name)   -- Opens a new file for output.
  175.    zclose(n)        -- Closes a file.
  176.    zchin(n,&c)      -- Gets the next character from an input file.
  177.    zsout(n,s)       -- Write a null-terminated string to output file, buffered.
  178.    zsoutl(n,s)      -- Like zsout, but appends a line terminator.
  179.    zsoutx(n,s,x)    -- Write x characters to output file, unbuffered.
  180.    zchout(n,c)      -- Add a character to an output file, unbuffered.
  181.    zchki(name)      -- Check if named file exists and is readable, return size.
  182.    zchko(name)      -- Check if named file can be created.
  183.    znewn(name,s)    -- Make a new unique file name based on the given name.
  184.    zdelet(name)     -- Delete the named file.
  185.    zxpand(string)   -- Expands the given wildcard string into a list of files.
  186.    znext(string)    -- Returns the next file from the list in "string".
  187.    zxcmd(cmd)       -- Execute the command in a lower fork.
  188.    zclosf()         -- Close input file associated with zxcmd()'s lower fork.
  189.    zrtol(n1,n2)     -- Convert remote filename into local form.
  190.    zltor(n1,n2)     -- Convert local filename into remote form.
  191.    zchdir(dirnam)   -- Change working directory.
  192.    zhome()          -- Return pointer to home directory name string.
  193.    zkself()         -- Kill self, log out own job.
  194.  */
  195.  
  196. #ifdef aegis
  197. #include "/sys/ins/base.ins.c"
  198. #include "/sys/ins/error.ins.c"
  199. #include "/sys/ins/pgm.ins.c"
  200. #endif
  201.  
  202. #ifdef FT17
  203. #define PROVX1
  204. #endif
  205. #ifndef PROVX1
  206. #ifndef aegis
  207. #ifndef datageneral
  208. #include <sys/file.h>            /* File access */
  209. #endif
  210. #endif
  211. #endif
  212. #ifdef FT17
  213. #undef PROVX1
  214. #endif
  215.  
  216. /* Some systems define these in include files, others don't... */
  217. #ifndef R_OK
  218. #define R_OK 4                /* For access */
  219. #endif
  220.  
  221. #ifndef W_OK
  222. #define W_OK 2
  223.  
  224. #endif
  225.  
  226. #ifdef PROVX1
  227. #define MAXNAMLEN DIRSIZ        /* Max file name length */
  228. #endif
  229.  
  230. #ifdef UXIII
  231. #include <fcntl.h>
  232. #define MAXNAMLEN DIRSIZ
  233. #endif
  234.  
  235. #ifndef O_RDONLY
  236. #define O_RDONLY 000
  237. #endif
  238.  
  239. #ifndef MAXNAMLEN
  240. #define MAXNAMLEN 14            /* If still not defined... */
  241. #endif
  242.  
  243. #ifdef PROVX1
  244. #define MAXWLD 50            /* Maximum wildcard filenames */
  245. #else
  246. #ifdef BSD29
  247. #define MAXWLD 50            /* Maximum wildcard filenames */
  248. #else
  249. #define MAXWLD 500
  250. #endif
  251. #endif
  252.  
  253. /* Declarations */
  254.  
  255. FILE *fp[ZNFILS] = {             /* File pointers */
  256.     NULL, NULL, NULL, NULL, NULL, NULL, NULL };
  257.  
  258. static int pid;                    /* pid of child fork */
  259. #ifdef SASMOD                /* For remote Kermit command */
  260. static int savout, saverr;        /* saved stdout and stderr streams */
  261. #endif
  262. static int fcount;            /* Number of files in wild group */
  263. static char nambuf[MAXNAMLEN+1];    /* Buffer for a filename */
  264. char *malloc(), *getenv(), *strcpy();    /* System functions */
  265. extern errno;                /* System error code */
  266.  
  267. static char *mtchs[MAXWLD],        /* Matches found for filename */
  268.  
  269.      **mtchptr;                /* Pointer to current match */
  270.  
  271. /*  Z K S E L F  --  Kill Self: log out own job, if possible.  */
  272.  
  273. zkself() {                /* For "bye", but no guarantee! */
  274. #ifdef aegis
  275.     return(kill(0,9));
  276. #else
  277. #ifdef PROVX1
  278.     return(kill(0,9));
  279. #else
  280. #ifdef V7
  281.     return(kill(0,9));
  282. #else
  283. #ifdef TOWER1
  284.     return(kill(0,9));
  285. #else
  286. #ifdef FT17
  287.     return(kill(0,9));
  288. #else
  289. #ifdef datageneral
  290.     /* sys_term works better than kill() on the DG, but does not log off. */
  291.     char *msg ="bye ";
  292.     int ac2;
  293.     ac2 = (int) msg;
  294.     return(sys_term(getpid(),0,ac2));
  295. #else
  296.     return(kill(getppid(),1));
  297. #endif
  298. #endif
  299. #endif
  300. #endif
  301. #endif
  302. #endif
  303. }
  304.  
  305. /*  Z O P E N I  --  Open an existing file for input. */
  306.  
  307. zopeni(n,name) int n; char *name; {
  308.     debug(F111," zopeni",name,n);
  309.     debug(F101,"  fp","",(int) fp[n]);
  310.     if (chkfn(n) != 0) return(0);
  311.     if (n == ZSYSFN) {            /* Input from a system function? */
  312.         debug(F110," invoking zxcmd",name,0);
  313.     return(zxcmd(name));        /* Try to fork the command */
  314.     }
  315.     if (n == ZSTDIO) {            /* Standard input? */
  316.     if (isatty(0)) {
  317.         ermsg("Terminal input not allowed");
  318.         debug(F110,"zopeni: attempts input from unredirected stdin","",0);
  319.         return(0);
  320.     }
  321.     fp[ZIFILE] = stdin;
  322.     return(1);
  323.     }
  324.     fp[n] = fopen(name,"r");        /* Real file. */
  325.     debug(F111," zopeni", name, (int) fp[n]);
  326.     if (fp[n] == NULL) perror("zopeni");
  327.     return((fp[n] != NULL) ? 1 : 0);
  328. }
  329.  
  330. /*  Z O P E N O  --  Open a new file for output.  */
  331.  
  332. zopeno(n,name) int n; char *name; {
  333.  
  334.     debug(F111," zopeno",name,n);
  335.     if (chkfn(n) != 0) return(0);
  336.     if ((n == ZCTERM) || (n == ZSTDIO)) {   /* Terminal or standard output */
  337.     fp[ZOFILE] = stdout;
  338.     debug(F101," fp[]=stdout", "", (int) fp[n]);
  339.     return(1);
  340.     }
  341.     fp[n] = fopen(name,"w");        /* A real file, try to open */
  342.     if (fp[n] == NULL) {
  343.         perror("zopeno can't open");
  344.     } else {
  345.     chown(name, getuid(), getgid());     /* In case set[gu]id */
  346.         if (n == ZDFILE) setbuf(fp[n],NULL); /* Debugging file unbuffered */
  347.     }
  348.     debug(F101, " fp[n]", "", (int) fp[n]);
  349.     return((fp[n] != NULL) ? 1 : 0);
  350. }
  351.  
  352. /*  Z C L O S E  --  Close the given file.  */
  353.  
  354. /*  Returns 0 if arg out of range, 1 if successful, -1 if close failed.  */
  355.  
  356. zclose(n) int n; {
  357.     int x;
  358.     if (chkfn(n) < 1) return(0);    /* Check range of n */
  359.     if ((n == ZIFILE) && fp[ZSYSFN]) {    /* If system function */
  360.         x = zclosf();            /* do it specially */
  361.     } else {
  362.         if ((fp[n] != stdout) && (fp[n] != stdin)) x = fclose(fp[n]);
  363.     fp[n] = NULL;
  364.     }
  365.     return((x == EOF) ? -1 : 1);
  366. }
  367.  
  368. /*  Z C H I N  --  Get a character from the input file.  */
  369.  
  370. /*  Returns -1 if EOF, 0 otherwise with character returned in argument  */
  371.  
  372. zchin(n,c) int n; char *c; {
  373.     int a;
  374.     if (chkfn(n) < 1) return(-1);
  375.     a = getc(fp[n]);
  376.     if (a == EOF) return(-1);
  377.     *c = a & 0377;
  378.     return(0);
  379. }
  380.  
  381. /*  Z S O U T  --  Write a string to the given file, buffered.  */
  382.  
  383. zsout(n,s) int n; char *s; {
  384.     if (chkfn(n) < 1) return(-1);
  385.     fputs(s,fp[n]);
  386.     return(0);
  387. }
  388.  
  389.  
  390. /*  Z S O U T L  --  Write string to file, with line terminator, buffered  */
  391.  
  392. zsoutl(n,s) int n; char *s; {
  393.     if (chkfn(n) < 1) return(-1);
  394.     fputs(s,fp[n]);
  395.  
  396.     fputs("\n",fp[n]);
  397.     return(0);
  398. }
  399.  
  400. /*  Z S O U T X  --  Write x characters to file, unbuffered.  */
  401.  
  402. zsoutx(n,s,x) int n, x; char *s; {
  403.     if (chkfn(n) < 1) return(-1);
  404. /*  return(write(fp[n]->_file,s,x));  */
  405.     return(write(fileno(fp[n]),s,x));
  406. }
  407.  
  408.  
  409. /*  Z C H O U T  --  Add a character to the given file.  */
  410.  
  411. /*  Should return 0 or greater on success, -1 on failure (e.g. disk full)  */
  412.  
  413. zchout(n,c) int n; char c; {
  414.     if (chkfn(n) < 1) return(-1);
  415.     if (n == ZSFILE)
  416. /*        return(write(fp[n]->_file,&c,1));  */
  417.         return(write(fileno(fp[n]),&c,1)); /* Use unbuffered for session log */
  418.     else {                /* Buffered for everything else */
  419.     if (putc(c,fp[n]) == EOF)    /* If true, maybe there was an error */
  420.         return(ferror(fp[n])?-1:0);    /* Check to make sure */
  421.     else                /* Otherwise... */
  422.         return(0);            /* There was no error. */
  423.     }
  424. }
  425.  
  426.  
  427. /*  C H K F N  --  Internal function to verify file number is ok  */
  428.  
  429. /*
  430.  Returns:
  431.   -1: File number n is out of range
  432.    0: n is in range, but file is not open
  433.  
  434.    1: n in range and file is open
  435. */
  436. chkfn(n) int n; {
  437.     switch (n) {
  438.     case ZCTERM:
  439.     case ZSTDIO:
  440.     case ZIFILE:
  441.     case ZOFILE:
  442.     case ZDFILE:
  443.     case ZTFILE:
  444.     case ZPFILE:
  445.     case ZSFILE:
  446.     case ZSYSFN: break;
  447.     default:
  448.         debug(F101,"chkfn: file number out of range","",n);
  449.         fprintf(stderr,"?File number out of range - %d\n",n);
  450.         return(-1);
  451.     }
  452.     return( (fp[n] == NULL) ? 0 : 1 );
  453. }
  454.  
  455. /*  Z C H K I  --  Check if input file exists and is readable  */
  456.  
  457. /*
  458.   Returns:
  459.    >= 0 if the file can be read (returns the size).
  460.      -1 if file doesn't exist or can't be accessed,
  461.      -2 if file exists but is not readable (e.g. a directory file).
  462.      -3 if file exists but protected against read access.
  463. */
  464. /*
  465.  For Berkeley Unix, a file must be of type "regular" to be readable.
  466.  Directory files, special files, and symbolic links are not readable.
  467. */
  468. long
  469. zchki(name) char *name; {
  470. #ifdef datageneral
  471.     /* For some reason, the DG croaks intermittently with a call to stat(),
  472.      * and goes into an infinite loop.  This routine needs to see if the
  473.      * file is okay to look at and access, so I will just call ?fstat to
  474.      * do that.  The only files that cannot be accessed by normal i/o routines
  475.      * are directories.  Other files are fair game.
  476.      */
  477.     int x; long y;            
  478.     P_FSTAT buf;        /* struct stat buf; */  
  479.     int ac0,ac2;
  480.  
  481.     /* x = stat(name,&buf); */
  482.     ac0 = (int) name;  ac2 = (int) &buf;
  483.     x = sys_fstat(ac0,0,ac2);
  484.     
  485.     if (x != 0) {
  486.     debug(F111,"zchki sys_fstat fails",name,ac0);
  487.     return(-1);
  488.     }
  489.  
  490.  
  491.     x = buf.styp_type;                  /* Isolate file format field */
  492.  
  493.     if ((x >= $LDIR) && (x <= $HDIR)) {
  494.     debug(F111,"zchki skipping DIR type:",name,x);
  495.     return(-2);
  496.     }
  497. #else
  498.     struct stat buf;
  499.     int x; long y;            
  500.  
  501.     x = stat(name,&buf);
  502.     if (x < 0) {
  503.     debug(F111,"zchki stat fails",name,errno);
  504.     return(-1);
  505.     }
  506.     x = buf.st_mode & S_IFMT;        /* Isolate file format field */
  507.  
  508.     if ((x != 0) && (x != S_IFREG)) {
  509.     debug(F111,"zchki skipping:",name,x);
  510.     return(-2);
  511.     }
  512. #endif datageneral
  513.  
  514.     debug(F111,"zchki stat ok:",name,x);
  515.  
  516.     if ((x = access(name,R_OK)) < 0) {     /* Is the file accessible? */
  517.     debug(F111," access failed:",name,x); /* No */
  518.         return(-3);            
  519.     } else {
  520. #ifdef datageneral
  521.     y = buf.sefm;
  522. #else
  523.     y = buf.st_size;
  524. #endif
  525.     debug(F111," access ok:",name,(int) y); /* Yes */
  526.     return( (y > -1) ? y : 0 );
  527.     }
  528. }
  529.  
  530. /*  Z C H K O  --  Check if output file can be created  */
  531.  
  532. /*
  533.  Returns -1 if write permission for the file would be denied, 0 otherwise.
  534. */
  535. zchko(name) char *name; {
  536.     int i, x;
  537.     char s[50], *sp;    
  538.  
  539.     sp = s;                /* Make a copy, get length */
  540.     x = 0;
  541.     while ((*sp++ = *name++) != '\0')
  542.         x++;
  543.     if (x == 0) return(-1);        /* If no filename, fail. */
  544.  
  545.     debug(F101," length","",x);
  546.     for (i = x; i > 0; i--)        /* Strip filename. */
  547.     if (s[i-1] == '/') break;
  548.  
  549.     debug(F101," i","",i);
  550.     if (i == 0)                /* If no path, use current directory */
  551.         strcpy(s,"./");            
  552.     else                /* Otherwise, use given one. */
  553.         s[i] = '\0';
  554.  
  555.     x = access(s,W_OK);            /* Check access of path. */
  556.     if (x < 0) {
  557.     debug(F111,"zchko access failed:",s,errno);
  558.     return(-1);
  559.     } else {
  560.     debug(F111,"zchko access ok:",s,x);
  561.     return(0);
  562.     }
  563. }
  564.  
  565. /*  Z D E L E T  --  Delete the named file.  */
  566.  
  567. zdelet(name) char *name; {
  568.     unlink(name);
  569. }
  570.  
  571.  
  572. /*  Z R T O L  --  Convert remote filename into local form  */
  573.  
  574. /*  For UNIX, this means changing uppercase letters to lowercase.  */
  575.  
  576. zrtol(name,name2) char *name, *name2; {
  577.     for ( ; *name != '\0'; name++) {
  578.         *name2++ = isupper(*name) ? tolower(*name) : *name;
  579.     }
  580.     *name2 = '\0';
  581.     debug(F110,"zrtol:",name2,0);
  582. }
  583.  
  584.  
  585. /*  Z L T O R  --  Local TO Remote */
  586.  
  587. /*  Convert filename from local format to common (remote) form.  */
  588.  
  589. zltor(name,name2) char *name, *name2; {
  590.     char work[100], *cp, *pp;
  591.     int dc = 0;
  592. #ifdef aegis
  593.     char *getenv(), *index(), *namechars;
  594.     int tilde = 0, bslash = 0;
  595.  
  596.     if ((namechars = getenv("NAMECHARS")) != NULL) {
  597.         if (index(namechars, '~' ) != NULL) tilde  = '~';
  598.         if (index(namechars, '\\') != NULL) bslash = '\\';
  599.     } else {
  600.         tilde = '~';
  601.         bslash = '\\';
  602.     }
  603. #endif
  604.  
  605.     debug(F110,"zltor",name,0);
  606.     pp = work;
  607. #ifdef aegis
  608.     cp = name;
  609.     if (tilde && *cp == tilde)
  610.         ++cp;
  611.     for (; *cp != '\0'; cp++) {    /* strip path name */
  612.         if (*cp == '/' || *cp == bslash) {
  613. #else
  614. #ifdef datageneral
  615.     /* Strip off the DG directory prefix */
  616.     for (cp=name; *cp != '\0'; cp++) {
  617.         if (*cp == ':') {
  618. #else
  619.     for (cp = name; *cp != '\0'; cp++) {    /* strip path name */
  620.         if (*cp == '/') {
  621. #endif
  622. #endif
  623.         dc = 0;
  624.         pp = work;
  625.     }
  626.     else if (islower(*cp)) *pp++ = toupper(*cp); /* Uppercase letters */
  627.     else if (*cp == '~') *pp++ = 'X';    /* Change tilde to 'X' */
  628.     else if (*cp == '#') *pp++ = 'X';    /* Change number sign to 'X' */
  629.     else if ((*cp == '.') && (++dc > 1)) *pp++ = 'X'; /* & extra dots */
  630.     else *pp++ = *cp;
  631.     }
  632.     *pp = '\0';                /* Tie it off. */
  633.     cp = name2;                /* If nothing before dot, */
  634.     if (*work == '.') *cp++ = 'X';    /* insert 'X' */
  635.     strcpy(cp,work);
  636.     debug(F110," name2",name2,0);
  637. }
  638.  
  639.  
  640. /*  Z C H D I R  --  Change directory  */
  641.  
  642. zchdir(dirnam) char *dirnam; {
  643.     char *hd;
  644.     if (*dirnam == '\0') hd = getenv("HOME");
  645.     else hd = dirnam;
  646.     return((chdir(hd) == 0) ? 1 : 0);
  647. }
  648.  
  649. /*  Z H O M E  --  Return pointer to user's home directory  */
  650.  
  651. char *
  652. zhome() {
  653.     return(getenv("HOME"));
  654. }
  655.  
  656. /*  Z G T D I R  --  Return pointer to user's current directory  */
  657.  
  658. char *
  659. zgtdir() {
  660.     char *getcwd();
  661.     char cwdbuf[100];
  662.     char *buf;
  663.     buf = cwdbuf;
  664.     return(getcwd(buf,100));
  665. }
  666.  
  667. #ifdef datageneral
  668. /*********************************************************************
  669.            P I P E _ C A L L
  670.  
  671.    Split off the arguments from a command to the system, and
  672.    pass the command and arguments to the system routine.
  673.    Return the name of the temp file which stored the results of the
  674.    command.
  675.    This routine assumes that the "l=" option is available on all
  676.    commands, which is true of AOS/VS CLI.  Other DG systems may need
  677.    another method, if the l= option is not available.
  678.  
  679.    Returns 0 if the system() function succeeds (>1 status)
  680.            1 if the system() function fails.
  681.  
  682.    I would have deleted this routine, except that I use it in zxpand()
  683.    if a # is used as a wild card character.  I used to depend on this
  684.    routine for zxcmd() as well as zxpand(), but I now use sys_proc and
  685.    sys_gnfn() respectively.
  686.    
  687. *********************************************************************/
  688.  
  689. int pipe_call(command,output_fname)
  690.    char  *command,output_fname[];
  691. {
  692.    /* Look for the first semi-colon, if any, and strip up to that point, plus 
  693.       any blanks following it.  Because the system() command refers to
  694.       the initial working directory, a "dir xxx;" is usually pre-pended. 
  695.    */
  696.    char  *arguments, syscommand[512], locommand[512];
  697.    char *insert,*fname,*cp,*tempname;
  698.    FILE *dummy;
  699.    
  700.    strcpy(locommand,command);
  701.    /* Find the first ;, if any */
  702.    for (insert=locommand; ((*insert) && (*insert!=';')); insert++);
  703.    if (*insert) arguments = insert+1;
  704.    else arguments = locommand;        /* No ; found */
  705.    /* Skip leading spaces */
  706.    for (; ((*arguments) && isspace(*arguments)); arguments++);
  707.    /* Find the end of the command to CLI */
  708.    for (; ((*arguments) && (*arguments!=',') && !isspace(*arguments));
  709.         arguments++);
  710.    /* If *arguments is NULL, there are no parameters to the command.
  711.       Otherwise, null-terminate command and address the arguments */
  712.    if (*arguments) *arguments++ = '\0';
  713.  
  714.    /* create the @OUTPUT file name -- a unique file name */
  715.    fname = getenv("HOME");  
  716.    for (cp=fname; *cp; cp++) if (*cp == '/') *cp=':';
  717.    strcpy(syscommand,fname);   strcat(syscommand,":?kermit_pipe_123456");
  718.    fname = &syscommand[0];
  719.    mktemp(fname);
  720.    strcpy(output_fname,fname);
  721.  
  722.    /* Create the file to establish its file type first */
  723.    close(dummy=open(output_fname,O_WRONLY));
  724.  
  725.    /* Append an L= (listing =) switch.  All CLI commands have this. */
  726.    sprintf(syscommand,"%s/l=%s %s",locommand,output_fname,arguments);
  727.  
  728.    /* now execute the command */
  729.    if (system(syscommand) > 1) {
  730.        perror("system");
  731.        return(1);
  732.    }
  733.    return(0);
  734. }
  735. #endif
  736.  
  737.  
  738. /*  Z X C M D -- Run a system command so its output can be read like a file */
  739.  
  740. zxcmd(comand) char *comand; {
  741. #ifdef datageneral
  742. /******** Start of Rick Lamb's addition to Kermit history. ****************/
  743. #include <paru.h>
  744. /* I modified Rick's code to use a tempname type of file.  -- Phil Julian. */
  745. /* Can open and start a task in AOS using their dg_open command
  746.  * and a system call "sys_proc" in "start_cli". R.H.Lamb 12/86 
  747.  */
  748.         FILE *piped;
  749.         char temp[132];
  750.         char *tempfile,*cp,pipename[256];
  751.         int i;
  752.          
  753.         /* Create a unique pipe file in the users home directory, because
  754.          * the actual working directory could be where the user has no
  755.          * write privelages, or two users may collide in the same directory.
  756.          */
  757.     tempfile = getenv("HOME");  
  758.     for (cp = tempfile; *cp; cp++) if (*cp == '/') *cp=':';
  759.         /* Make sure and copy the NULL also */
  760.     memcpy (pipename, tempfile, strlen(tempfile)+1);
  761.     strcat (pipename,":?kermit_pipe_123456");
  762.     mktemp(pipename);
  763.  
  764. /* The command interpreter for AOS is "cli.pr"
  765.  * for MV/UX its :bin:sh:pr 
  766.  */
  767.         if ((piped=dg_open(pipename,$ICRF+$OFIN+$OFCR+$RTDY,$FPIP))==NULL) {
  768.             perror("Trouble creating a pipe in zxcmd\n"); 
  769.             fprintf(stderr,"Pipe filename was [%s]\n",pipename);
  770.             return(0);
  771.         }
  772. #ifdef mvux
  773.         /* I think Rick's comment above may be wrong:  "sh:pr" ?? */
  774.         strcpy(temp,":bin:sh.pr,"); strcpy(&temp[11],comand);
  775. #else
  776.         strcpy(temp,":cli.pr,"); strcpy(&temp[8],comand);
  777. #endif
  778.         if (start_cli(temp,pipename))
  779.                 {perror("Can't execute command in zxcmd\n"); return(0);}
  780.         fp[ZIFILE]=piped;
  781.         fp[ZSYSFN]=fp[ZIFILE];
  782.         return(1);
  783.  
  784. #else datageneral       /* old unix stuff  */
  785.     int pipes[2];
  786.     if (pipe(pipes) != 0) return(0);    /* can't make pipe, fail */
  787.     if ((pid = fork()) == 0) {        /* child */
  788.  
  789. /*#if BSD4*/        /* Code from Dave Tweten@AMES-NAS */
  790.             /* readapted to use getpwuid to find login shell */
  791.             /*   -- H. Fischer */
  792.     char *shpath, *shname, *shptr;    /* to find desired shell */
  793.     struct passwd *p;
  794.     extern struct passwd * getpwuid();
  795.     extern int getuid();
  796.     char *defShel = "/bin/sh";    /* default shell */
  797. /*#endif*/
  798.  
  799.     close(pipes[0]);        /* close input side of pipe */
  800.     close(0);            /* close stdin */
  801.     if (open("/dev/null",0) < 0) return(0);    /* replace input by null */
  802.  
  803. #ifndef UXIII
  804.     dup2(pipes[1],1);        /* replace stdout & stderr */
  805.     dup2(pipes[1],2);        /* by the pipe */
  806. #else
  807.     close(1);            /* simulate dup2 */
  808.     if (dup(pipes[1]) != 1 )
  809.         conol("trouble duping stdout in routine zxcmd\n");
  810.     close(2);            /* simulate dup2 */
  811.     if (dup(pipes[1]) != 2 )
  812.         conol("trouble duping stderr in routine zxcmd\n");
  813. #endif
  814.  
  815.     close(pipes[1]);        /* get rid of this copy of the pipe */
  816.  
  817. /****     shptr = shname = shpath = getenv("SHELL");  /* What shell? */
  818.     p = getpwuid( getuid() );    /* get login data */
  819.     if ( p == (struct passwd *) NULL || !*(p->pw_shell) ) shpath = defShel;
  820.       else shpath = p->pw_shell;
  821.     shptr = shname = shpath;
  822.     while (*shptr != '\0') if (*shptr++ == dsep) shname = shptr;
  823.     debug(F100,"zxcmd...","",0);
  824.     debug(F110,shpath,shname,0);
  825.     execl(shpath,shname,"-c",comand,(char *)NULL); /* Execute the command */
  826.  
  827. /****    execl("/bin/sh","sh","-c",comand,(char *)NULL); /* Execute the command */
  828.  
  829.     exit(0);            /* just punt if it didnt work */
  830.     } else if (pid == -1) {
  831.     debug(F100,"zxcmd fork failure","",0);
  832.     return(0);
  833.     }
  834.     close(pipes[1]);            /* don't need the output side */
  835.     fp[ZIFILE] = fdopen(pipes[0],"r");    /* open a stream for it */
  836.     fp[ZSYSFN] = fp[ZIFILE];        /* Remember. */
  837.     return(1);
  838. #endif datageneral
  839. }
  840.  
  841. #ifdef SASMOD
  842. /* For remote Kermit command */
  843. /*  Z X L O G  --  redirect stderr and stdout for logging. */
  844.  
  845. zxlog() {
  846.     FILE *tmpf, *tmpfile();
  847.  
  848.     if (chkfn(ZSYSFN) != 0) return(0);
  849.     /* Unix magic to redirect stdout and stderr to temporary file */
  850.     fflush(stdout); fflush(stderr);    /* synchronize */
  851.     if ((tmpf = tmpfile()) == NULL) return(0);
  852.     if ((savout = dup(1)) < 0 || (saverr = dup(2)) < 0) return(0);
  853.     dup2(fileno(tmpf), 1); dup2(fileno(tmpf), 2);
  854.     fp[ZSYSFN] = tmpf;
  855.     return(1);
  856. }
  857.  
  858.  
  859. /*  Z X U N L O G  --  restore stderr and stdout from logging. */
  860.  
  861. zxunlog() {
  862.     /* restore stdout and stderr */
  863.     fflush(stdout); fflush(stderr);    /* synchronize */
  864.     dup2(savout, 1); close(savout);
  865.     dup2(saverr, 2); close(saverr);
  866.  
  867.     /* rewind to start of temporary file */
  868.     rewind(fp[ZSYSFN]);
  869.  
  870.     fp[ZIFILE] = fp[ZSYSFN];
  871.     pid = 0;
  872.     return(1);
  873. }
  874. #endif SASMOD
  875.  
  876. /*  Z C L O S F  - wait for the child fork to terminate and close the pipe. */
  877.  
  878. zclosf() {
  879.     int wstat;
  880.     fclose(fp[ZIFILE]);
  881.     fp[ZIFILE] = fp[ZSYSFN] = NULL;
  882. #ifndef datageneral
  883.     while ((wstat = wait(0)) != pid && wstat != -1) ;
  884. #endif 
  885.     return(1);
  886. }
  887.  
  888.  
  889. #ifdef datageneral
  890.  
  891. /******** More of Rick Lamb's addition to Kermit history. */
  892.  
  893. /* S T A R T _ C L I - starts a command as another concurrent task.
  894.  * The command is equivalent to the CLI command of
  895.  * "proc/def/output=pipename :cli command" 
  896.  */
  897. start_cli(command,pipename)
  898. char *command,*pipename;
  899. {       
  900.     P_PROC packet;
  901.         P_ISEND message;
  902.         int len,pid,err;
  903.         short int *string = (short int *)command;
  904.         
  905.         debug(F110,"Start_cli: command = ",command,0);
  906.         len = strlen(command);
  907.         command[len] = '\0';        /* Null terminate the string */
  908.  
  909.     message.isfl = (short) 0;    /* System flags */
  910.     message.iufl = (short) $RFCF;    /* User flags */
  911.     message.idph = (long) 0;    /* Destination port number */
  912.     message.iopn = (short) 0;    /* Local origin port number */
  913.     message.ilth = (short) (len / 2 + 1);     /* Length (in words) of message */
  914.     message.iptr = string;        /* Pointer to message buffer */
  915.  
  916.     packet.pflg = (short) 0;    /* Flags for process creation */
  917.     packet.ppri = (short) -1;    /* Process priority -- was 3 */
  918.     packet.psnm = ":cli.pr";     /* Byte pointer to program name */
  919.     packet.pipc = &message;        /* Pointer to initial msg. or -1 */
  920.     packet.pnm  = (char *) -1;      /* Byte ptr to process name or -1 */
  921.     packet.pmem = (long) -1;    /* Maximum memory pages or -1 */
  922.     packet.pdir = (char *) 0;    /* Byte ptr to initial dir. or -1/0 */
  923.     packet.pcon = (char *) 0;    /* Byte ptr to console name or -1/0 */
  924.     packet.pcal = (short) -1;    /* Max concurrent system calls or -1 */
  925.     packet.pwss = (short) -1;    /* Max working set size or -1 */
  926.     packet.punm = -1;        /* Byte ptr to username or -1 */
  927.     /* Note that $PVPC (unlimited sons) causes privelage violations
  928.      * for users that are not royally endowed.  Anyway, following are
  929.      * the privileges bits.  -- Phil Julian
  930.      */
  931.     packet.pprv = (short) ( /* $PVPC+ */ $PVWS+$PVEX+$PVIP); 
  932.     packet.ppcr = (short) -1;    /* Maximum sons or -1 */
  933.     packet.pwmi = (short) -1;    /* Working set minimum or -1 */
  934.                                     /* reserved */
  935.     packet.pifp = "@Null";      /* Byte ptr to @INPUT  or -1/0 */
  936.     packet.pofp = pipename;     /* Byte ptr to @OUTPUT or -1/0 */
  937.     packet.plfp = (char *) 0;    /* Byte ptr to @LIST   or -1/0 */
  938.     packet.pdfp = (char *) 0;    /* Byte ptr to @DATA   or -1/0 */
  939.     packet.smch = (_ulong) -1;    /* Max CPU time or -1 */
  940.  
  941.         if (err = sys_proc(&packet,&pid)) {
  942.             perror("Start_cli: sys_proc ");
  943.             fprintf(stderr,"Start_cli: Error in sys_proc = %#o\n",err);
  944.             return(1);
  945.         }
  946.         else
  947.         return(0);
  948. }
  949. /******** End of Rick Lamb's addition to Kermit history. */
  950. #endif datageneral
  951.  
  952.  
  953. /*  Z X P A N D  --  Expand a wildcard string into an array of strings  */
  954. /*
  955.   Returns the number of files that match fn1, with data structures set up
  956.   so that first file (if any) will be returned by the next znext() call.
  957. */
  958. zxpand(fn) char *fn; {
  959. #ifdef datageneral
  960.    /* Victor Johansen's code requires no change in zxpand(). 
  961.     * However, it is difficult to expand wild card strings that
  962.     * contain some ^ or #.  For a #, you must check the file type for
  963.     * being a directory, then open up the directory, and keep searching
  964.     * for sub-directories, etc.  However, an simple kludge can be used
  965.     * in this case, which saves me the programming effort.  Anyone
  966.     * using the # should pay an execution time penalty for using it.
  967.     * So when a string contains the #, we use the original kludge that
  968.     * I used before getting Victor's code.  Victor Johansen's code is more 
  969.     * efficient because it uses sys_gnfn(), but I have to do more work
  970.     * in some cases.  With the ^'s, the directory name must be parsed 
  971.     * and adjusted, before opening the correct directory.
  972.     */
  973.    char tempname[256],command[256];
  974.    int pipe_call();
  975.    FILE *sysout;
  976.    char *curptr, *saveptr, buffer[257], *pos, *end;
  977.    int n;
  978.  
  979.    wildcarlb = 0;
  980.    for (curptr = fn; *curptr; curptr++) 
  981.         if (*curptr == '#') { wildcarlb = 0; goto nonvictor; }
  982.    wildcarlb = 1; goto victor;
  983.  
  984. nonvictor:           
  985.    curptr = command;
  986.    sprintf(curptr, "filestatus/cpl=16/nheader %s", fn);
  987.    curptr = tempname;
  988.    pipe_call(command,curptr);
  989.  
  990.    /* Read the file of filenames, and parse out a universal name */
  991.    sysout = fopen(tempname,"r");
  992.    for (fcount=0; n=dg_fgets(buffer,256,sysout); )
  993.       {
  994.         curptr = (char *) malloc(min(256,strlen(buffer)));
  995.         mtchs[fcount]=curptr;
  996.         /* delete leading spaces, leading directory name, and and trailing LF */
  997.         if (iscntrl(*(pos = &buffer[strlen(buffer)-1])))
  998.                *pos-- = '\0';
  999.         /* First char will be =, if working dir, or : or @ if not.
  1000.            Delete the =, but keep others.
  1001.         */
  1002.         for (pos=buffer; *pos; pos++) {
  1003.              if (*pos == '=') break;
  1004.          if ((*pos == ':') || (*pos == '@'))  { pos--; break; }
  1005.     }
  1006.         strcpy(mtchs[fcount],pos+1);
  1007.         fcount++;
  1008.        }
  1009.    fclose(sysout);
  1010.    zdelet(tempname);
  1011.  
  1012. victor:
  1013.    if (wildcarlb)
  1014.         fcount = fgen(fn,mtchs,MAXWLD);    /* Look up the file. */
  1015.  
  1016. #else
  1017.     fcount = fgen(fn,mtchs,MAXWLD);    /* Look up the file. */
  1018. #endif datageneral
  1019.  
  1020.     if (fcount > 0) {
  1021.     mtchptr = mtchs;        /* Save pointer for next. */
  1022.     }
  1023.     debug(F111,"zxpand",mtchs[0],fcount);
  1024.     return(fcount);
  1025. }
  1026.  
  1027.  
  1028.  
  1029. /*  Z N E X T  --  Get name of next file from list created by zxpand(). */
  1030. /*
  1031.  Returns >0 if there's another file, with its name copied into the arg string,
  1032.  or 0 if no more files in list.
  1033. */
  1034. znext(fn) char *fn; {
  1035.     if (fcount-- > 0)
  1036.  
  1037. #ifdef datageneral
  1038.        {
  1039.           /* Victor Johansen's code requires no change in znext(), but my
  1040.            * code does.  The flag, wildcardlb is 1, if Victor's code is
  1041.            * in effect.
  1042.            */
  1043.           strcpy(fn,*mtchptr++);
  1044.           if (wildcarlb == 0)  /* My old code: Phil Julian */ 
  1045.                free (*(mtchptr-1));
  1046.        }
  1047. #else 
  1048.        strcpy(fn,*mtchptr++);
  1049. #endif datageneral
  1050.     else *fn = '\0';
  1051.     debug(F111,"znext",fn,fcount+1);
  1052.     return(fcount+1);
  1053. }
  1054.  
  1055.  
  1056. /*  Z N E W N  --  Make a new name for the given file  */
  1057.  
  1058. znewn(fn,s) char *fn, **s; {
  1059. #ifdef BSD4
  1060.     static char buf[256];
  1061. #else
  1062.     static char buf[100];
  1063. #endif
  1064.     char *bp, *xp;
  1065.     int len = 0, n = 0, d = 0, t, i, power = 1;
  1066. #ifdef MAXNAMLEN
  1067.     int max = MAXNAMLEN;
  1068. #else
  1069.  
  1070.     int max = 14;
  1071. #endif
  1072.     bp = buf;
  1073.     while (*fn) {            /* Copy name into buf */
  1074.     *bp++ = *fn++;
  1075.     len++;
  1076.     }
  1077.     if (len > max-2) {             /* Don't let it get too long */
  1078.     bp = buf + max-2;
  1079.     len = max - 2;
  1080.     }
  1081.     
  1082.     for (i = 1; i < 4; i++) {        /* Try up to 999 times */
  1083.     power *= 10;
  1084.     *bp++ = '*';            /* Put a star on the end */
  1085.     *bp-- = '\0';
  1086.     
  1087.     n = zxpand(buf);        /* Expand the resulting wild name */
  1088.  
  1089.     while (n-- > 0) {        /* Find any existing name~d files */
  1090.         xp = *mtchptr++;
  1091.         xp += len;
  1092.         if (*xp == '~') {
  1093.         t = atoi(xp+1);
  1094.         if (t > d) d = t;    /* Get maximum d */
  1095.         }
  1096.     }
  1097.     if (d < power-1) {
  1098.         sprintf(bp,"~%d",d+1);    /* Make name~(d+1) */
  1099.         *s = buf;
  1100.         return;
  1101.     }
  1102.     bp--; len--;
  1103.     }
  1104. /* If we ever get here, we'll overwrite the xxx~100 file... */
  1105. }
  1106.  
  1107. /* Directory Functions for Unix, written by Jeff Damens, CUCCA, 1984. */
  1108.  
  1109. /*
  1110.  * The path structure is used to represent the name to match.
  1111.  * Each slash-separated segment of the name is kept in one
  1112.  * such structure, and they are linked together, to make
  1113.  * traversing the name easier.
  1114.  */
  1115.  
  1116. struct path {
  1117.               char npart[MAXNAMLEN];    /* name part of path segment */
  1118.               struct path *fwd;        /* forward ptr */
  1119.             };
  1120.  
  1121. #ifdef PROVX1
  1122. #define SSPACE 500
  1123. #else
  1124. #ifdef BSD29
  1125. #define SSPACE 500
  1126. #else
  1127. #ifdef aegis
  1128. #define SSPACE 10000            /* size of string-generating buffer */
  1129. #else
  1130. #ifdef datageneral
  1131. #define SSPACE 10000            /* size of string-generating buffer */
  1132. #else
  1133. #define SSPACE 2000            /* size of string-generating buffer */
  1134. #endif
  1135. #endif
  1136. #endif
  1137. #endif
  1138. static char sspace[SSPACE];             /* buffer to generate names in */
  1139.  
  1140. static char *freeptr,**resptr;             /* copies of caller's arguments */
  1141. static int remlen;                      /* remaining length in caller's array*/
  1142. static int numfnd;                      /* number of matches found */
  1143.  
  1144. /*
  1145.  * splitpath:
  1146.  *  takes a string and splits the slash-separated portions into
  1147.  *  a list of path structures.  Returns the head of the list.  The
  1148.  *  structures are allocated by malloc, so they must be freed.
  1149.  *  Splitpath is used internally by the filename generator.
  1150.  *
  1151.  * Input: A string.
  1152.  * Returns: A linked list of the slash-separated segments of the input.
  1153.  */
  1154.  
  1155. struct path *
  1156. #ifdef aegis
  1157. splitpath(p, bslash)
  1158. char *p;
  1159. int bslash;
  1160. #else
  1161. splitpath(p)
  1162. char *p;
  1163. #endif
  1164. {
  1165.  struct path *head,*cur,*prv;
  1166.  int i;
  1167.  head = prv = NULL;
  1168.  if (*p == '/') p++;            /* skip leading slash */
  1169.  while (*p != '\0')
  1170.  {
  1171.    cur = (struct path *) malloc(sizeof (struct path));
  1172.    debug(F101,"splitpath malloc","",cur);
  1173.    if (cur == NULL) fatal("malloc fails in splitpath()");
  1174.    cur -> fwd = NULL;
  1175.    if (head == NULL) head = cur;
  1176.    else prv -> fwd = cur;       /* link into chain */
  1177.    prv = cur;
  1178. #ifdef aegis
  1179.    /* treat backslash as "../" */
  1180.    if (bslash && *p == bslash) {
  1181.      strcpy(cur->npart, "..");
  1182.      ++p;
  1183.    } else {
  1184.      for (i=0; i < MAXNAMLEN && *p && *p != '/' && *p != bslash; i++)
  1185.        cur -> npart[i] = *p++;
  1186.      cur -> npart[i] = '\0';      /* end this segment */
  1187.      if (i >= MAXNAMLEN) while (*p && *p != '/' && *p != bslash) p++;
  1188.    }
  1189.    if (*p == '/') p++;
  1190. #else
  1191.    for (i=0; i < MAXNAMLEN && *p != '/' && *p != '\0'; i++)
  1192.      cur -> npart[i] = *p++;
  1193.    cur -> npart[i] = '\0';      /* end this segment */
  1194.    if (i >= MAXNAMLEN) while (*p != '/' && *p != '\0') p++;
  1195.    if (*p == '/') p++;
  1196. #endif
  1197.  }
  1198.  return(head);
  1199. }
  1200.  
  1201. /*
  1202.  * fgen:
  1203.  *  This is the actual name generator.  It is passed a string,
  1204.  *  possibly containing wildcards, and an array of character pointers.
  1205.  *  It finds all the matching filenames and stores them into the array.
  1206.  *  The returned strings are allocated from a static buffer local to
  1207.  *  this module (so the caller doesn't have to worry about deallocating
  1208.  *  them); this means that successive calls to fgen will wipe out
  1209.  *  the results of previous calls.  This isn't a problem here
  1210.  *  because we process one wildcard string at a time.
  1211.  *
  1212.  * Input: a wildcard string, an array to write names to, the
  1213.  *        length of the array.
  1214.  * Returns: the number of matches.  The array is filled with filenames
  1215.  *          that matched the pattern.  If there wasn't enough room in the
  1216.  
  1217.  *        array, -1 is returned.
  1218.  * By: Jeff Damens, CUCCA, 1984.
  1219.  */
  1220.  
  1221. fgen(pat,resarry,len)
  1222. char *pat,*resarry[];
  1223. int len;
  1224. {
  1225. #ifdef datageneral
  1226. /* Victor Johannsen helped out with this addition to Kermit.  The use of
  1227.  * sys_gnfn() is the proper way to solve this problem.  But, I would prefer
  1228.  * not building up the static string space.  This can be a problem when a
  1229.  * long, full-qualified directory name pattern is used, since this directory
  1230.  * name would be pre-pended to each file name.  
  1231.  *   A later fix would actually not build a list, but call sys_gnfn() within
  1232.  * znext(), and update the counters appropriately.  Several systems support
  1233.  * this model, which seems much more flexible and not at all space-limited.
  1234.  *   -- Phil Julian, 30 April 1987
  1235.  */
  1236.    P_GNFN          packet;                 /* packet for gnfn call */
  1237.    char            prefix[256];
  1238.    char           dirname[256];
  1239.    int             chan;                   /* Channel for open the DIR */
  1240.    FILE            *Ftemp;
  1241.    char            *DIRptr;                /* DIR name */
  1242.    int             i,off;
  1243.    int             got_dir = 0;
  1244.    char            *pattern,*cp;
  1245.  
  1246.    /* If a directory prefix is passed in, and possibly some wild cards, we
  1247.     * will need to parse off the directory to open.  We do not assume that
  1248.     * directories can be descended ad infinitum.
  1249.     */
  1250.    i = strlen(pat); zero(prefix,min(i+1,256));
  1251.    for (cp = pat+i-1; i-- >= 0; cp--)
  1252.         if ((*cp == ':') || (*cp == '^') || (*cp == '=') || (*cp == '@')) {
  1253.              memcpy(prefix,pat,off = i+1);
  1254.              memcpy(dirname,prefix,off-1), dirname[off-1] = 0;
  1255.              DIRptr = dirname, pattern = cp+1;
  1256.              got_dir = 1; 
  1257.              /* Parse ^ for moving up a directory */
  1258.              if (*pat == '^') {
  1259.                   /* From the tail of the current dir, back up to : */
  1260.                   char *kp,*dp;
  1261.                   int pos = 0;
  1262.                   
  1263.                   i = strlen(dp = getdir()) - 1;
  1264.                   for (kp = pat; *kp == '^'; kp++,i--,pos++) 
  1265.                        for (;dp[i] != ':';i--);
  1266.                   i++;
  1267.                   if (strlen(DIRptr)) {                /* Less ^s */
  1268.                        memcpy(prefix,DIRptr+pos,strlen(DIRptr)-pos);
  1269.                        prefix[strlen(DIRptr)-pos] = 0; /* Terminate */
  1270.                   } else prefix[0] = 0;
  1271.                   memcpy(dirname,dp,i);                /* The ^'d DIR */
  1272.                   off -= pos;
  1273.                   if (pos = strlen(prefix)) {
  1274.                        memcpy(dirname+i,":",1);        /* Dir separator */
  1275.                        memcpy(dirname+i+1,prefix,pos); /* Rest of dir name */
  1276.                   }
  1277.                   dirname[i+off] = 0;                  /* Null terminate */
  1278.                   memcpy(prefix,dirname,off = strlen(dirname));
  1279.                   prefix[off] = ':'; prefix[++off] = 0;
  1280.              }
  1281.              break;
  1282.         }
  1283.  
  1284.    if (got_dir == 0) {
  1285.         DIRptr = getdir();
  1286.         pattern = pat;
  1287.    }
  1288.  
  1289.    if ( (Ftemp = fopen(DIRptr,"r")) == NULL) return(0);
  1290.    chan = fchannel(Ftemp);
  1291.  
  1292.    numfnd = 0;
  1293.    freeptr = sspace;
  1294.    resptr = resarry;
  1295.    remlen = len;
  1296.  
  1297.    packet.nfky = 0;
  1298.    packet.nftp = pattern;
  1299.    packet.nfnm = nambuf;
  1300.    
  1301.    while (i = !sys_gnfn(chan,&packet)) {
  1302.          if (got_dir == 0) addresult( nambuf );
  1303.          else {
  1304.              memcpy(prefix+off,nambuf,strlen(nambuf)+1);
  1305.              addresult( prefix );
  1306.          }
  1307.    }
  1308.  
  1309.    fclose(Ftemp);
  1310.  
  1311. #else datageneral
  1312.  struct path *head;
  1313.  char scratch[100],*sptr;
  1314. #ifdef aegis
  1315.  char *getenv(), *index(), *namechars;
  1316.  int tilde = 0, bslash = 0, bquote = 0;
  1317.  
  1318.  if ((namechars = getenv("NAMECHARS")) != NULL) {
  1319.  
  1320.   if (index(namechars, '~' ) != NULL) tilde  = '~';
  1321.   if (index(namechars, '\\') != NULL) bslash = '\\';
  1322.   if (index(namechars, '`' ) != NULL) bquote = '`';
  1323.  }
  1324.  else { tilde = '~'; bslash = '\\'; bquote = '`'; }
  1325.  
  1326.  sptr = scratch;
  1327.  /* copy "`node_data", etc. anchors */
  1328.  if (bquote && *pat == bquote)
  1329.   while (*pat && *pat != '/' && *pat != bslash)
  1330.    *sptr++ = *pat++;
  1331.  else if (tilde && *pat == tilde)
  1332.   *sptr++ = *pat++;
  1333.  while (*pat == '/')
  1334.   *sptr++ = *pat++;
  1335.  if (sptr == scratch)
  1336.  {
  1337.   strcpy(scratch,"./");
  1338.   sptr = scratch+2;
  1339.  }                    /* init buffer correctly */
  1340.  head = splitpath(pat, bslash);
  1341. #else
  1342.  head = splitpath(pat);
  1343.  if (*pat == '/')
  1344.  {
  1345.   scratch[0] = '/';
  1346.   sptr = scratch+1;
  1347.  }
  1348.  else
  1349.  {
  1350.   strcpy(scratch,"./");
  1351.   sptr = scratch+2;
  1352.  }                    /* init buffer correctly */
  1353. #endif
  1354.  numfnd = 0;                            /* none found yet */
  1355.  freeptr = sspace;            /* this is where matches are copied */
  1356.  resptr = resarry;            /* static copies of these so*/
  1357.  remlen = len;                /* recursive calls can alter them */
  1358.  traverse(head,scratch,sptr);        /* go walk the directory tree */
  1359.  for (; head != NULL; head = head -> fwd)
  1360.    free(head);                /* return the path segments */
  1361. #endif datageneral
  1362.  return(numfnd);            /* and return the number of matches */
  1363. }
  1364.  
  1365. /* traverse:
  1366.  *  Walks the directory tree looking for matches to its arguments.
  1367.  *  The algorithm is, briefly:
  1368.  *   If the current pattern segment contains no wildcards, that
  1369.  *   segment is added to what we already have.  If the name so far
  1370.  *   exists, we call ourselves recursively with the next segment
  1371.  *   in the pattern string; otherwise, we just return.
  1372.  *
  1373.  *   If the current pattern segment contains wildcards, we open the name
  1374.  *   we've accumulated so far (assuming it is really a directory), then read
  1375.  
  1376.  *   each filename in it, and, if it matches the wildcard pattern segment, add
  1377.  *   that filename to what we have so far and call ourselves recursively on the
  1378.  *   next segment.
  1379.  *
  1380.  *   Finally, when no more pattern segments remain, we add what's accumulated
  1381.  *   so far to the result array and increment the number of matches.
  1382.  *
  1383.  * Input: a pattern path list (as generated by splitpath), a string
  1384.  *      pointer that points to what we've traversed so far (this
  1385.  *      can be initialized to "/" to start the search at the root
  1386.  *      directory, or to "./" to start the search at the current
  1387.  *      directory), and a string pointer to the end of the string
  1388.  *      in the previous argument.
  1389.  * Returns: nothing.
  1390.  */
  1391. traverse(pl,sofar,endcur)
  1392. struct path *pl;
  1393. char *sofar,*endcur;
  1394. {
  1395. #ifdef BSD42
  1396.  DIR *fd, *opendir();
  1397.  struct direct *dirbuf;
  1398. #else
  1399. #ifdef BSD29
  1400.  DIR *fd, *opendir();
  1401.  struct direct *dirbuf;
  1402. #else
  1403.  int fd;
  1404.  struct direct dir_entry;
  1405.  struct direct *dirbuf = &dir_entry;
  1406. #endif
  1407. #endif
  1408.  struct stat statbuf;
  1409.  if (pl == NULL)
  1410.  {
  1411.   *--endcur = '\0';                    /* end string, overwrite trailing / */
  1412.   addresult(sofar);
  1413.   return;
  1414.  }
  1415.  if (!iswild(pl -> npart))
  1416.  {
  1417.   strcpy(endcur,pl -> npart);
  1418.   endcur += strlen(pl -> npart);
  1419.   *endcur = '\0';                         /* end current string */
  1420.   if (stat(sofar,&statbuf) == 0)    /* if current piece exists */
  1421.   {
  1422.       *endcur++ = '/';                  /* add slash to end */
  1423.       *endcur = '\0';            /* and end the string */
  1424.       traverse(pl -> fwd,sofar,endcur);
  1425.   }
  1426.   return;
  1427.  }
  1428. /* cont'd... */
  1429.  
  1430. /*...traverse, cont'd */
  1431.  
  1432. /* segment contains wildcards, have to search directory */
  1433.  *endcur = '\0';                            /* end current string */
  1434.  if (stat(sofar,&statbuf) == -1) return;       /* doesn't exist, forget it */
  1435.  if ((statbuf.st_mode & S_IFDIR) == 0) return;  /* not a directory, skip */
  1436. #ifdef BSD42            /* ==BSD4 */
  1437.  if ((fd = opendir(sofar)) == NULL) return;      /* can't open, forget it */
  1438.  while (dirbuf = readdir(fd))
  1439. #else
  1440. #ifdef BSD29            /* ==BSD29 */
  1441.  if ((fd = opendir(sofar)) == NULL) return;      /* can't open, forget it */
  1442.  while (dirbuf = readdir(fd))
  1443. #else
  1444.  
  1445.  if ((fd = open(sofar,O_RDONLY)) < 0) return;      /* can't open, forget it */
  1446.  while ( read(fd,dirbuf,sizeof dir_entry) )
  1447. #endif
  1448. #endif
  1449. {
  1450.   strncpy(nambuf,dirbuf->d_name,MAXNAMLEN); /* Get a null terminated copy!!! */
  1451.  
  1452.   nambuf[MAXNAMLEN] = '\0';
  1453.   if (dirbuf->d_ino != 0 && match(pl -> npart,nambuf)) {
  1454.     char *eos;
  1455.     strcpy(endcur,nambuf);
  1456.     eos = endcur + strlen(nambuf);
  1457.     *eos = '/';                    /* end this segment */
  1458.     traverse(pl -> fwd,sofar,eos+1);
  1459.   }
  1460. }
  1461. #ifdef BSD42            /* ==BSD4 */
  1462.  closedir(fd);
  1463. #else
  1464. #ifdef BSD29
  1465.  closedir(fd);
  1466. #else
  1467.  close(fd);
  1468. #endif
  1469. #endif
  1470. }
  1471.  
  1472. /*
  1473.  * addresult:
  1474.  *  Adds a result string to the result array.  Increments the number
  1475.  *  of matches found, copies the found string into our string
  1476.  *  buffer, and puts a pointer to the buffer into the caller's result
  1477.  *  array.  Our free buffer pointer is updated.  If there is no
  1478.  *  more room in the caller's array, the number of matches is set to -1.
  1479.  * Input: a result string.
  1480.  * Returns: nothing.
  1481.  */
  1482.  
  1483. addresult(str)
  1484. char *str;
  1485. {
  1486.  int l;
  1487.  if (strncmp(str,"./",2) == 0) str += 2;
  1488.  if (--remlen < 0) {
  1489.   numfnd = -1;
  1490.   return;
  1491.  }
  1492.  l = strlen(str) + 1;            /* size this will take up */
  1493.  if ((freeptr + l) > &sspace[SSPACE]) {
  1494.     numfnd = -1;            /* do not record if not enough space */
  1495.     return;
  1496.   }
  1497.  strcpy(freeptr,str);
  1498.  *resptr++ = freeptr;
  1499.  freeptr += l;
  1500.  numfnd++;
  1501. }
  1502.  
  1503. iswild(str)
  1504. char *str;
  1505. {
  1506.  char c;
  1507.  while ((c = *str++) != '\0')
  1508.    if (c == '*' || c == '?') return(1);
  1509.  return(0);
  1510. }
  1511.  
  1512. /*
  1513.  * match:
  1514.  *  pattern matcher.  Takes a string and a pattern possibly containing
  1515.  *  the wildcard characters '*' and '?'.  Returns true if the pattern
  1516.  *  matches the string, false otherwise.
  1517.  * by: Jeff Damens, CUCCA
  1518.  
  1519.  *
  1520.  * Input: a string and a wildcard pattern.
  1521.  * Returns: 1 if match, 0 if no match.
  1522.  */
  1523.  
  1524. match(pattern,string) char *pattern,*string; {
  1525.     char *psave,*ssave;            /* back up pointers for failure */
  1526.     psave = ssave = NULL;
  1527.     while (1) {
  1528.     for (; *pattern == *string; pattern++,string++)  /* skip first */
  1529.         if (*string == '\0') return(1);    /* end of strings, succeed */
  1530.     if (*string != '\0' && *pattern == '?') {
  1531.         pattern++;            /* '?', let it match */
  1532.         string++;
  1533.     } else if (*pattern == '*') {    /* '*' ... */
  1534.         psave = ++pattern;        /* remember where we saw it */
  1535.         ssave = string;        /* let it match 0 chars */
  1536.     } else if (ssave != NULL && *ssave != '\0') {    /* if not at end  */
  1537.                       /* ...have seen a star */
  1538.         string = ++ssave;        /* skip 1 char from string */
  1539.         pattern = psave;        /* and back up pattern */
  1540.     } else return(0);        /* otherwise just fail */
  1541.     }
  1542. }
  1543.