home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / ckc072.zip / ckifio.c < prev    next >
C/C++ Source or Header  |  1989-06-19  |  22KB  |  747 lines

  1. char *ckzv = "Amiga file support, 4D(004) 24 Jan 88";
  2.  
  3. /* C K I F I O  --  Kermit file system support for the Amiga */
  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.  Modified for Amiga by Jack J. Rouse, The Software Distillery
  14. */
  15.  
  16. /* Includes */
  17.  
  18. #include "ckcker.h"        /* Kermit definitions */
  19. #include "ckcdeb.h"        /* Typedefs, formats for debug() */
  20. #include <stdio.h>        /* Unix Standard i/o */
  21. #include <ctype.h>
  22. #define MAXNAMLEN 30
  23.  
  24. char *ckzsys = " Amiga";
  25.  
  26. /* Definitions of some Amiga system commands */
  27.  
  28. char *DIRCMD = "list ";        /* For directory listing */
  29. char *DELCMD = "delete ";        /* For file deletion */
  30. char *TYPCMD = "type ";            /* For typing a file */
  31. char *PWDCMD = "cd ";            /* For saying where I am */
  32.  
  33. char *SPACMD = "info ";
  34.  
  35. char *SPACM2 = "info ";            /* should be space in specified directory */
  36.  
  37. char *WHOCMD = "status ";        /* Check process status */
  38.  
  39. #define MAXWLD 300
  40.  
  41. /*
  42.   Functions (n is one of the predefined file numbers from ckermi.h):
  43.  
  44.    zopeni(n,name)   -- Opens an existing file for input.
  45.    zopeno(n,name)   -- Opens a new file for output.
  46.    zclose(n)        -- Closes a file.
  47.    zchin(n,&c)      -- Gets the next character from an input file.
  48.    zsout(n,s)       -- Write a null-terminated string to output file, buffered.
  49.    zsoutl(n,s)      -- Like zsout, but appends a line terminator.
  50.    zsoutx(n,s,x)    -- Write x characters to output file, unbuffered.
  51.    zchout(n,c)      -- Add a character to an output file, unbuffered.
  52.    zchki(name)      -- Check if named file exists and is readable, return size.
  53.    zchko(name)      -- Check if named file can be created.
  54.    znewn(name,s)    -- Make a new unique file name based on the given name.
  55.    zdelet(name)     -- Delete the named file.
  56.    zxpand(string)   -- Expands the given wildcard string into a list of files.
  57.    znext(string)    -- Returns the next file from the list in "string".
  58.    zxcmd(cmd)       -- Execute the command in a lower fork.
  59.    zclosf()         -- Close input file associated with zxcmd()'s lower fork.
  60.    zrtol(n1,n2)     -- Convert remote filename into local form.
  61.    zltor(n1,n2)     -- Convert local filename into remote form.
  62.    zchdir(dirnam)   -- Change working directory.
  63.    zhome()          -- Return pointer to home directory name string.
  64.    zkself()         -- Kill self, log out own job (simply exits)
  65.  */
  66.  
  67. /* Declarations */
  68.  
  69. FILE *fp[ZNFILS] = {             /* File pointers */
  70.     NULL, NULL, NULL, NULL, NULL, NULL, NULL };
  71.  
  72. static int fcount;            /* Number of files in wild group */
  73. char *malloc(), *strcpy();        /* System functions */
  74. char *strrchr();
  75.  
  76. static char *mtchs[MAXWLD],        /* Matches found for filename */
  77.      **mtchptr;                /* Pointer to current match */
  78.  
  79. /* utility functions from ckiutl.c */
  80. extern int existobj();
  81. struct DirHandle    /* fake structure definition */
  82. {
  83.     int _foo_;
  84. };
  85. extern struct DirHandle *opendir();
  86. extern char *readdir();
  87. extern closedir();
  88.  
  89. /*  Z K S E L F  --  Kill Self: log out own job, if possible.  */
  90.  
  91. zkself() {                /* For "bye", but no guarantee! */
  92.     doexit(GOOD_EXIT);
  93. }
  94.  
  95. /*  Z O P E N I  --  Open an existing file for input. */
  96.  
  97. zopeni(n,name) int n; char *name; {
  98.     debug(F111," zopeni",name,n);
  99.     debug(F101,"  fp","",(int) fp[n]);
  100.     if (chkfn(n) != 0) return(0);
  101.     if (n == ZSYSFN) {            /* Input from a system function? */
  102.         debug(F110," invoking zxcmd",name,0);
  103.     return(zxcmd(name));        /* Try to fork the command */
  104.     }
  105.     if (n == ZSTDIO) {            /* Standard input? */
  106.     if (isatty(0)) {
  107.         ermsg("Terminal input not allowed");
  108.         debug(F110,"zopeni: attempts input from unredirected stdin","",0);
  109.         return(0);
  110.     }
  111.     fp[ZIFILE] = stdin;
  112.     return(1);
  113.     }
  114.     fp[n] = fopen(name,"r");        /* Real file. */
  115.     debug(F111," zopeni", name, (int) fp[n]);
  116.     if (fp[n] == NULL) perror("zopeni");
  117.     return((fp[n] != NULL) ? 1 : 0);
  118. }
  119.  
  120. /*  Z O P E N O  --  Open a new file for output.  */
  121.  
  122. zopeno(n,name) int n; char *name; {
  123.     debug(F111," zopeno",name,n);
  124.     if (chkfn(n) != 0) return(0);
  125.     if ((n == ZCTERM) || (n == ZSTDIO)) {   /* Terminal or standard output */
  126.     fp[ZOFILE] = stdout;
  127.     debug(F101," fp[]=stdout", "", (int) fp[n]);
  128.     return(1);
  129.     }
  130.     fp[n] = fopen(name,"w");        /* A real file, try to open */
  131.     if (fp[n] == NULL) {
  132.         perror("zopeno can't open");
  133.     } else {
  134.         if (n == ZDFILE) setbuf(fp[n],NULL); /* Debugging file unbuffered */
  135.     }
  136.     debug(F101, " fp[n]", "", (int) fp[n]);
  137.     return((fp[n] != NULL) ? 1 : 0);
  138. }
  139.  
  140. /*  Z C L O S E  --  Close the given file.  */
  141.  
  142. /*  Returns 0 if arg out of range, 1 if successful, -1 if close failed.  */
  143.  
  144. zclose(n) int n; {
  145.     int x;
  146.     if (chkfn(n) < 1) return(0);    /* Check range of n */
  147.     if ((n == ZIFILE) && fp[ZSYSFN]) {    /* If system function */
  148.         x = zclosf();            /* do it specially */
  149.     } else {
  150.         if ((fp[n] != stdout) && (fp[n] != stdin)) x = fclose(fp[n]);
  151.     fp[n] = NULL;
  152.     }
  153.     return((x == EOF) ? -1 : 1);
  154. }
  155.  
  156. /*  Z C H I N  --  Get a character from the input file.  */
  157.  
  158. /*  Returns -1 if EOF, 0 otherwise with character returned in argument  */
  159.  
  160. zchin(n,c) int n; char *c; {
  161.     int a;
  162.     if (chkfn(n) < 1) return(-1);
  163.     a = getc(fp[n]);
  164.     if (a == EOF) return(-1);
  165.     *c = a & 0377;
  166.     return(0);
  167. }
  168.  
  169. /*  Z S O U T  --  Write a string to the given file, buffered.  */
  170.  
  171. zsout(n,s) int n; char *s; {
  172.     if (chkfn(n) < 1) return(-1);
  173.     fputs(s,fp[n]);
  174.     return(0);
  175. }
  176.  
  177. /*  Z S O U T L  --  Write string to file, with line terminator, buffered  */
  178.  
  179. zsoutl(n,s) int n; char *s; {
  180.     if (chkfn(n) < 1) return(-1);
  181.     fputs(s,fp[n]);
  182.     fputs("\n",fp[n]);
  183.     return(0);
  184. }
  185.  
  186. /*  Z S O U T X  --  Write x characters to file, unbuffered.  */
  187.  
  188. zsoutx(n,s,x) int n, x; char *s; {
  189.     if (chkfn(n) < 1) return(-1);
  190.     return(write(fileno(fp[n]),s,x));
  191. }
  192.  
  193.  
  194. /*  Z C H O U T  --  Add a character to the given file.  */
  195.  
  196. /*  Should return 0 or greater on success, -1 on failure (e.g. disk full)  */
  197.  
  198. zchout(n,c) int n; CHAR c; {
  199.     if (chkfn(n) < 1) return(-1);
  200.     if (n == ZSFILE)
  201.         return(write(fileno(fp[n]),&c,1)); /* Use unbuffered for session log */
  202.     else {                /* Buffered for everything else */
  203.     if (putc(c,fp[n]) == EOF)    /* If true, maybe there was an error */
  204.         return(ferror(fp[n])?-1:0);    /* Check to make sure */
  205.     else                /* Otherwise... */
  206.         return(0);            /* There was no error. */
  207.     }
  208. }
  209.  
  210. /*  C H K F N  --  Internal function to verify file number is ok  */
  211.  
  212. /*
  213.  Returns:
  214.   -1: File number n is out of range
  215.    0: n is in range, but file is not open
  216.    1: n in range and file is open
  217. */
  218. chkfn(n) int n; {
  219.     switch (n) {
  220.     case ZCTERM:
  221.     case ZSTDIO:
  222.     case ZIFILE:
  223.     case ZOFILE:
  224.     case ZDFILE:
  225.     case ZTFILE:
  226.     case ZPFILE:
  227.     case ZSFILE:
  228.     case ZSYSFN: break;
  229.     default:
  230.         debug(F101,"chkfn: file number out of range","",n);
  231.         fprintf(stderr,"?File number out of range - %d\n",n);
  232.         return(-1);
  233.     }
  234.     return( (fp[n] == NULL) ? 0 : 1 );
  235. }
  236.  
  237. /*  Z C H K I  --  Check if input file exists and is readable  */
  238.  
  239. /*
  240.   Returns:
  241.    >= 0 if the file can be read (returns the size).
  242.      -1 if file doesn't exist or can't be accessed,
  243.      -2 if file exists but is not readable (e.g. a directory file).
  244.      -3 if file exists but protected against read access.
  245. */
  246. long
  247. zchki(name) char *name; {
  248.     long size, readstat();
  249.  
  250.     size = readstat(name);
  251.     debug(F111,"zchki file size",name,(int)size);
  252.     return(size);
  253. }
  254.  
  255. /*  Z C H K O  --  Check if output file can be created  */
  256.  
  257. /*
  258.  Returns -1 if write permission for the file would be denied, 0 otherwise.
  259. */
  260. zchko(name) char *name; {
  261.     int rc = writestat(name);
  262.  
  263. #ifdef DEBUG
  264.     if (rc < 0)
  265.     debug(F111,"zchko access failed:",name,NULL);
  266.     else
  267.     debug(F111,"zchko access ok:",name,NULL);
  268. #endif
  269.     return(rc);
  270. }
  271.  
  272. /*  Z D E L E T  --  Delete the named file.  */
  273.  
  274. zdelet(name) char *name; {
  275.     unlink(name);
  276. }
  277.  
  278.  
  279. /*  Z R T O L  --  Convert remote filename into local form  */
  280.  
  281. /*  For AMIGA, this means changing uppercase letters to lowercase.  */
  282.  
  283. zrtol(name,name2) char *name, *name2; {
  284.     for ( ; *name != '\0'; name++) {
  285.         *name2++ = isupper(*name) ? tolower(*name) : *name;
  286.     }
  287.     *name2 = '\0';
  288.     debug(F110,"zrtol:",name2,0);
  289. }
  290.  
  291.  
  292. /*  Z L T O R  --  Local TO Remote */
  293.  
  294. /*  Convert filename from local format to common (remote) form.  */
  295.  
  296. zltor(name,name2) char *name, *name2; {
  297.     char work[100], *cp, *pp;
  298.     int dc = 0;
  299.  
  300.     debug(F110,"zltor",name,0);
  301.     pp = work;
  302.     if ((cp = strrchr(name, ':')) == NULL)
  303.         cp = name;
  304.     else
  305.         ++cp;
  306.  
  307.     for (; *cp != '\0'; cp++) {    /* strip path name */
  308.         if (*cp == '/') {
  309.         dc = 0;
  310.         pp = work;
  311.     }
  312.     else if (islower(*cp)) *pp++ = toupper(*cp); /* Uppercase letters */
  313.     else if (*cp == '~') *pp++ = 'X';    /* Change tilde to 'X' */
  314.     else if (*cp == '#') *pp++ = 'X';    /* Change number sign to 'X' */
  315.     else if ((*cp == '.') && (++dc > 1)) *pp++ = 'X'; /* & extra dots */
  316.     else *pp++ = *cp;
  317.     }
  318.     *pp = '\0';                /* Tie it off. */
  319.     cp = name2;                /* If nothing before dot, */
  320.     if (*work == '.') *cp++ = 'X';    /* insert 'X' */
  321.     strcpy(cp,work);
  322.     debug(F110," name2",name2,0);
  323. }    
  324.  
  325. /*  Z H O M E  --  Return pointer to user's home directory  */
  326.  
  327. /* we return "s:", which is where startup scripts are found */
  328. char *
  329. zhome() {
  330.     return("s:");        /* very approximately */
  331. }
  332.  
  333. /*  Z C H D I R  --  Change directory  */
  334.  
  335. zchdir(dirnam) char *dirnam; {
  336.     return((chdir(dirnam) == 0) ? 1 : 0);
  337. }
  338.  
  339. /*  Z G T D I R  --  Return pointer to user's current directory  */
  340.  
  341. char *
  342. zgtdir() {
  343.     return("");
  344. }
  345.  
  346. /*  Z X C M D -- Run a system command so its output can be read like a file */
  347. zxcmd(comand) char *comand; {
  348.     FILE *f, *pipeopen();
  349.  
  350.     if ((f = pipeopen(comand)) == NULL) return(0);
  351.     fp[ZIFILE] = f;            /* open a stream for it */
  352.     fp[ZSYSFN] = fp[ZIFILE];    /* Remember. */
  353.     return(1);
  354. }
  355.  
  356. /*  Z C L O S F  - wait for the child fork to terminate and close the pipe. */
  357.  
  358. zclosf() {
  359.     pipeclose(fp[ZIFILE]);
  360.     fp[ZIFILE] = fp[ZSYSFN] = NULL;
  361.     return(1);
  362. }
  363.  
  364. /*  Z X P A N D  --  Expand a wildcard string into an array of strings  */
  365. /*
  366.   Returns the number of files that match fn1, with data structures set up
  367.   so that first file (if any) will be returned by the next znext() call.
  368. */
  369. zxpand(fn) char *fn; {
  370.     fcount = fgen(fn,mtchs,MAXWLD);    /* Look up the file. */
  371.     if (fcount > 0) {
  372.     mtchptr = mtchs;        /* Save pointer for next. */
  373.     }
  374.     debug(F111,"zxpand",mtchs[0],fcount);
  375.     return(fcount);
  376. }
  377.  
  378.  
  379. /*  Z N E X T  --  Get name of next file from list created by zxpand(). */
  380. /*
  381.  Returns >0 if there's another file, with its name copied into the arg string,
  382.  or 0 if no more files in list.
  383. */
  384. znext(fn) char *fn; {
  385.     if (fcount-- > 0) strcpy(fn,*mtchptr++);
  386.     else *fn = '\0';
  387.     debug(F111,"znext",fn,fcount+1);
  388.     return(fcount+1);
  389. }
  390.  
  391.  
  392. /*  Z N E W N  --  Make a new name for the given file  */
  393.  
  394. znewn(fn,s) char *fn, **s; {
  395.     static char buf[100];
  396.     char *bp;
  397.     int len = 0, d;
  398. #ifdef MAXNAMLEN
  399.     int maxlen = MAXNAMLEN;
  400. #else
  401.     int maxlen = 14;
  402. #endif
  403.  
  404.     bp = buf;
  405.     while (*fn) {            /* Copy name into buf */
  406.     *bp++ = *fn++;
  407.     len++;
  408.     }
  409.     if (len > maxlen-3) bp -= 3;    /* Don't let it get too long */
  410.  
  411.     /* 
  412.      * On the Amiga, it takes much less time to determine
  413.      * if a given file exists than to read all the file names in
  414.      * a directory (or even just names with a certain prefix).
  415.      */
  416.     d = 0;
  417.     do {
  418.     sprintf(bp, "~%d", ++d);
  419.     } while (zchki(buf) != -1 && d < 100);
  420.  
  421.     *s = buf;
  422. }
  423.  
  424. /* Directory Functions for Unix, written by Jeff Damens, CUCCA, 1984. */
  425.  
  426. /*
  427.  * The path structure is used to represent the name to match.
  428.  * Each slash-separated segment of the name is kept in one
  429.  * such structure, and they are linked together, to make
  430.  * traversing the name easier.
  431.  */
  432.  
  433. struct path {
  434.               char npart[MAXNAMLEN];    /* name part of path segment */
  435.               struct path *fwd;        /* forward ptr */
  436.             };
  437.  
  438. #define SSPACE 4000            /* size of string-generating buffer */
  439.  
  440. static char sspace[SSPACE];             /* buffer to generate names in */
  441. static char *freeptr,**resptr;             /* copies of caller's arguments */
  442. static int remlen;                      /* remaining length in caller's array*/
  443. static int numfnd;                      /* number of matches found */
  444.  
  445. /*
  446.  * splitpath:
  447.  *  takes a string and splits the slash-separated portions into
  448.  *  a list of path structures.  Returns the head of the list.  The
  449.  *  structures are allocated by malloc, so they must be freed.
  450.  *  Splitpath is used internally by the filename generator.
  451.  *
  452.  * Input: A string.
  453.  * Returns: A linked list of the slash-separated segments of the input.
  454.  */
  455.  
  456. struct path *
  457. splitpath(p)
  458. char *p;
  459. {
  460.  struct path *head,*cur,*prv;
  461.  int i;
  462.  head = prv = NULL;
  463.  if (*p == '/') p++;            /* skip leading slash */
  464.  while (*p != '\0')
  465.  {
  466.    cur = (struct path *) malloc(sizeof (struct path));
  467.    debug(F101,"splitpath malloc","",cur);
  468.    if (cur == NULL) fatal("malloc fails in splitpath()");
  469.    cur -> fwd = NULL;
  470.    if (head == NULL) head = cur;
  471.    else prv -> fwd = cur;       /* link into chain */
  472.    prv = cur;
  473.    for (i=0; i < MAXNAMLEN && *p != '/' && *p != '\0'; i++)
  474.      cur -> npart[i] = *p++;
  475.    cur -> npart[i] = '\0';      /* end this segment */
  476.    if (i >= MAXNAMLEN) while (*p != '/' && *p != '\0') p++;
  477.    if (*p == '/') p++;
  478.  }
  479.  return(head);
  480. }
  481.  
  482. /*
  483.  * fgen:
  484.  *  This is the actual name generator.  It is passed a string,
  485.  *  possibly containing wildcards, and an array of character pointers.
  486.  *  It finds all the matching filenames and stores them into the array.
  487.  *  The returned strings are allocated from a static buffer local to
  488.  *  this module (so the caller doesn't have to worry about deallocating
  489.  *  them); this means that successive calls to fgen will wipe out
  490.  *  the results of previous calls.  This isn't a problem here
  491.  *  because we process one wildcard string at a time.
  492.  *
  493.  * Input: a wildcard string, an array to write names to, the
  494.  *        length of the array.
  495.  * Returns: the number of matches.  The array is filled with filenames
  496.  *          that matched the pattern.  If there wasn't enough room in the
  497.  *        array, -1 is returned.
  498.  * By: Jeff Damens, CUCCA, 1984.
  499.  */
  500.  
  501. fgen(pat,resarry,len)
  502. char *pat,*resarry[];
  503. int len;
  504. {
  505.  struct path *head;
  506.  char scratch[100],*sptr;
  507.  char *tail;
  508.  
  509.  if ((tail = strrchr(pat, ':')) == NULL) /* locate unit name */
  510.   tail = pat;                /* no unit name */
  511.  else
  512.   ++tail;                /* eat ':' */
  513.  while (*tail == '/')            /* eat parent path slashes */
  514.   ++tail;
  515.  sptr = scratch;            /* init buffer correctly */
  516.  while (pat < tail)
  517.   *sptr++ = *pat++;
  518.  head = splitpath(pat);
  519.  numfnd = 0;                            /* none found yet */
  520.  freeptr = sspace;            /* this is where matches are copied */
  521.  resptr = resarry;            /* static copies of these so*/
  522.  remlen = len;                /* recursive calls can alter them */
  523.  traverse(head,scratch,sptr);        /* go walk the directory tree */
  524.  for (; head != NULL; head = head -> fwd)
  525.    free(head);                /* return the path segments */
  526.  return(numfnd);            /* and return the number of matches */
  527. }
  528.  
  529. /* traverse:
  530.  *  Walks the directory tree looking for matches to its arguments.
  531.  *  The algorithm is, briefly:
  532.  *   If the current pattern segment contains no wildcards, that
  533.  *   segment is added to what we already have.  If the name so far
  534.  *   exists, we call ourselves recursively with the next segment
  535.  *   in the pattern string; otherwise, we just return.
  536.  *
  537.  *   If the current pattern segment contains wildcards, we open the name
  538.  *   we've accumulated so far (assuming it is really a directory), then read 
  539.  *   each filename in it, and, if it matches the wildcard pattern segment, add
  540.  *   that filename to what we have so far and call ourselves recursively on the
  541.  *   next segment.
  542.  *
  543.  *   Finally, when no more pattern segments remain, we add what's accumulated
  544.  *   so far to the result array and increment the number of matches.
  545.  *
  546.  * Input: a pattern path list (as generated by splitpath), a string
  547.  *      pointer that points to what we've traversed so far (this
  548.  *      can be initialized to "/" to start the search at the root
  549.  *      directory, or to "./" to start the search at the current
  550.  *      directory), and a string pointer to the end of the string
  551.  *      in the previous argument.
  552.  * Returns: nothing.
  553.  */
  554. traverse(pl,sofar,endcur)
  555. struct path *pl;
  556. char *sofar,*endcur;
  557. {
  558.  struct DirHandle *fd;
  559.  char *fname;
  560.  
  561.  if (pl == NULL)
  562.  {
  563.   *--endcur = '\0';                    /* end string, overwrite trailing / */
  564.   addresult(sofar);
  565.   return;
  566.  }
  567.  if (!iswild(pl -> npart))
  568.  {
  569.   strcpy(endcur,pl -> npart);
  570.   endcur += strlen(pl -> npart);
  571.   *endcur = '\0';                         /* end current string */
  572.   if (existobj(sofar))            /* if current piece exists */
  573.   {
  574.       *endcur++ = '/';            /* add slash to end */
  575.       *endcur = '\0';            /* and end the string */
  576.       traverse(pl -> fwd,sofar,endcur);
  577.   }
  578.   return;
  579.  }
  580. /* cont'd... */
  581.  
  582. /*...traverse, cont'd */
  583.  
  584. /* segment contains wildcards, have to search directory */
  585.  *endcur = '\0';                            /* end current string */
  586.  if ((fd = opendir(sofar)) == NULL) return;      /* can't open, forget it */
  587.  while (fname = readdir(fd))
  588. {
  589.   if (match(pl -> npart,fname)) {
  590.     char *eos;
  591.     strcpy(endcur,fname);
  592.     eos = endcur + strlen(fname);
  593.     *eos = '/';                    /* end this segment */
  594.     traverse(pl -> fwd,sofar,eos+1);
  595.   }
  596. }
  597.  closedir(fd);
  598. }
  599.  
  600. /*
  601.  * addresult:
  602.  *  Adds a result string to the result array.  Increments the number
  603.  *  of matches found, copies the found string into our string
  604.  *  buffer, and puts a pointer to the buffer into the caller's result
  605.  *  array.  Our free buffer pointer is updated.  If there is no
  606.  *  more room in the caller's array, the number of matches is set to -1.
  607.  * Input: a result string.
  608.  * Returns: nothing.
  609.  */
  610.  
  611. addresult(str)
  612. char *str;
  613. {
  614.  int l;
  615.  if (--remlen < 0) {
  616.   numfnd = -1;
  617.   return;
  618.  }
  619.  l = strlen(str) + 1;            /* size this will take up */
  620.  if ((freeptr + l) > &sspace[SSPACE]) {
  621.     numfnd = -1;            /* do not record if not enough space */
  622.     return;
  623.   }
  624.  strcpy(freeptr,str);
  625.  *resptr++ = freeptr;
  626.  freeptr += l;
  627.  numfnd++;
  628. }
  629.  
  630. iswild(str)
  631. char *str;
  632. {
  633.  char c;
  634.  while ((c = *str++) != '\0')
  635.    if (c == '*' || c == '?') return(1);
  636.  return(0);
  637. }
  638.  
  639. #ifdef OLDMATCH
  640. /*
  641.  * match:
  642.  *  pattern matcher.  Takes a string and a pattern possibly containing
  643.  *  the wildcard characters '*' and '?'.  Returns true if the pattern
  644.  *  matches the string, false otherwise.
  645.  * by: Jeff Damens, CUCCA
  646.  *
  647.  * Input: a string and a wildcard pattern.
  648.  * Returns: 1 if match, 0 if no match.
  649.  */
  650.  
  651. match(pattern,string)
  652. register char *pattern,*string;
  653. {
  654.     char *psave,*ssave;            /* back up pointers for failure */
  655.     psave = ssave = NULL;
  656.     while (1) {
  657.     for (;
  658.          tolower(*pattern) == tolower(*string);
  659.          pattern++,string++)  /* skip first */
  660.         if (*string == '\0') return(1);    /* end of strings, succeed */
  661.     if (*string != '\0' && *pattern == '?') {
  662.         pattern++;            /* '?', let it match */
  663.         string++;
  664.     } else if (*pattern == '*') {    /* '*' ... */
  665.         psave = ++pattern;        /* remember where we saw it */
  666.         ssave = string;        /* let it match 0 chars */
  667.     } else if (ssave != NULL && *ssave != '\0') {    /* if not at end  */
  668.                       /* ...have seen a star */
  669.         string = ++ssave;        /* skip 1 char from string */
  670.         pattern = psave;        /* and back up pattern */
  671.     } else return(0);        /* otherwise just fail */
  672.     }
  673. }
  674. #else
  675. /*
  676.  * match -- match wildcard pattern to string
  677.  *    allows multiple '*'s and works without backtracking
  678.  *    upper and lower case considered equivalent
  679.  *    written by Jack Rouse
  680.  *    working without backtracking is cute, but is this usually going
  681.  *       to be the most efficient method?
  682.  */
  683. match(pattern, target)
  684. register char *pattern, *target;
  685. {
  686.     int link[MAXNAMLEN];        /* list of matches to try in pattern */
  687.     register int first, last;    /* first and last items in list */
  688.     register int here, next;    /* current and next list items */
  689.     char lowch;            /* current target character */
  690.  
  691.     /* start out trying to match at first position */
  692.     first = last = 0;
  693.     link[0] = -1;
  694.  
  695.     /* go through the target */
  696.     for (; *target; ++target)
  697.     {
  698.         /* get lowercase target character */
  699.         lowch = tolower(*target);
  700.  
  701.         /* go through all positions this round and build next round */
  702.         last = -1;
  703.         for (here = first; here >= 0; here = next)
  704.         {
  705.             next = link[here];
  706.             switch (pattern[here])
  707.             {
  708.             case '*':
  709.                 /* try match at here+1 this round */
  710.                 /*!!!check needed only if "**" allowed? */
  711.                 if (next != here + 1)
  712.                 {
  713.                     link[here + 1] = next;
  714.                     next = here + 1;
  715.                 }
  716.                 /* retry match at here next round */
  717.                 break;
  718.             default:
  719.                 if (tolower(pattern[here]) != lowch)
  720.                     continue;
  721.                 /* matched, fall through */
  722.             case '?':
  723.                 /* try match at here+1 next round */
  724.                 ++here;
  725.                 break;
  726.             }
  727.             /* try match at here value next round */
  728.             if (last < 0)
  729.                 first = here;
  730.             else
  731.                 link[last] = here;
  732.             last = here;
  733.         }
  734.         /* if no positions left, match failed */
  735.         if (last == -1) return(0);
  736.         /* terminate list */
  737.         link[last] = -1;
  738.     }
  739.  
  740.     /* at end of target, skip empty matches */
  741.     while (pattern[last] == '*')
  742.         ++last;
  743.  
  744.     return(pattern[last] == '\0');
  745. }
  746. #endif
  747.