home *** CD-ROM | disk | FTP | other *** search
/ The UNIX CD Bookshelf / OREILLY_TUCB_UNIX_CD.iso / upt / examples / SOURCES / DELETE / PART04.Z / PART04 / pattern.c
Encoding:
C/C++ Source or Header  |  1998-07-24  |  25.2 KB  |  1,008 lines

  1. /*
  2.  * $Source: /afs/athena.mit.edu/astaff/project/delete/src/RCS/pattern.c,v $
  3.  * $Author: jik $
  4.  *
  5.  * This program is part of a package including delete, undelete,
  6.  * lsdel, expunge and purge.  The software suite is meant as a
  7.  * replacement for rm which allows for file recovery.
  8.  * 
  9.  * Copyright (c) 1989 by the Massachusetts Institute of Technology.
  10.  * For copying and distribution information, see the file "mit-copyright.h."
  11.  */
  12.  
  13. #if (!defined(lint) && !defined(SABER))
  14.      static char rcsid_pattern_c[] = "$Header: /afs/athena.mit.edu/astaff/project/delete/src/RCS/pattern.c,v 1.21 91/02/20 17:27:03 jik Exp $";
  15. #endif
  16.  
  17. #include <stdio.h>
  18. #include <sys/types.h>
  19. #include <sys/dir.h>
  20. #include <sys/param.h>
  21. #ifdef SYSV
  22. #include <string.h>
  23. #define index strchr
  24. #define rindex strrchr
  25. #else
  26. #include <strings.h>
  27. #endif /* SYSV */
  28. #include <errno.h>
  29. #include <com_err.h>
  30. #include "pattern.h"
  31. #include "util.h"
  32. #include "directories.h"
  33. #include "undelete.h"
  34. #include "shell_regexp.h"
  35. #include "mit-copyright.h"
  36. #include "delete_errs.h"
  37. #include "errors.h"
  38. #include "stack.h"
  39.  
  40. extern char *realloc();
  41. extern int errno;
  42. extern char *whoami;
  43.  
  44. void free_list();
  45.  
  46.  
  47. /*
  48.  * add_arrays() takes pointers to two arrays of char **'s and their
  49.  * lengths, merges the two into the first by realloc'ing the first and
  50.  * then free's the second's memory usage.
  51.  */  
  52. int add_arrays(array1, num1, array2, num2)
  53. char ***array1, ***array2;
  54. int *num1, *num2;
  55. {
  56.      int counter;
  57.      
  58.      *array1 = (char **) realloc((char *) *array1, (unsigned)
  59.                  (sizeof(char *) * (*num1 + *num2)));
  60.      if (! *array1) {
  61.       set_error(errno);
  62.       error("realloc");
  63.       return error_code;
  64.      }
  65.      for (counter = *num1; counter < *num1 + *num2; counter++)
  66.       *(*array1 + counter) = *(*array2 + counter - *num1);
  67.      free ((char *) *array2);
  68.      *num1 += *num2;
  69.      return 0;
  70. }
  71.  
  72.  
  73.  
  74.  
  75.  
  76.  
  77. /*
  78.  * Add a string to a list of strings.
  79.  */
  80. int add_str(strs, num, str)
  81. char ***strs;
  82. int num;
  83. char *str;
  84. {
  85.      char **ary;
  86.  
  87.      ary = *strs = (char **) realloc((char *) *strs, (unsigned)
  88.                      (sizeof(char *) * (num + 1)));
  89.      if (! *strs) {
  90.       set_error(errno);
  91.       error("realloc");
  92.       return error_code;
  93.      }
  94.      ary[num] = Malloc((unsigned) (strlen(str) + 1));
  95.      if (! ary[num]) {
  96.       set_error(errno);
  97.       error("Malloc");
  98.       return error_code;
  99.      }
  100.      (void) strcpy(ary[num], str);
  101.      return 0;
  102. }
  103.  
  104.  
  105.  
  106.  
  107.  
  108. /*
  109.  * Find_matches will behave unpredictably if you try to use it to find
  110.  * very strange combinations of file types, for example only searching
  111.  * for undeleted files in the top-level directory, while searching
  112.  * recursively for deleted files.  Basically, there are some conflicts
  113.  * between options that I don't list here because I don't think I'll
  114.  * ever need to use those combinations.
  115.  */
  116. /*
  117.  * Function: find_matches(char *name, int *num_found, char ***found,
  118.  *                 int options)
  119.  *
  120.  * Requires: name points to a NULL-terminated string, representing a
  121.  *   filename pattern with regular filename characters, path
  122.  *   separators and shell wildcard characters []*?; num_found points
  123.  *   to a valid int memory storage location; found points to a valid
  124.  *   char ** memory storage location.
  125.  *
  126.  * Effects: Returns a list of all the files in the file hierarchy that
  127.  *   match the options specified in options and that match name.
  128.  *   Options are:
  129.  *
  130.  *   FIND_UNDELETED search for and return undeleted files
  131.  * 
  132.  *   FIND_DELETED search for and return deleted files
  133.  *
  134.  *   FIND_CONTENTS means that if matches are directories (or links to
  135.  *     directories), the contents of the directory should be matched
  136.  *     in addition to the directory itself
  137.  * 
  138.  *   RECURS_FIND_DELETED to search all undeleted subdirectories
  139.  *     recursively of matched directories looking for deleted files
  140.  *
  141.  *   RECURS_FIND_UNDELETED to search all undeleted subdirectories
  142.  *     recursively of matched directories looking for undeleted files
  143.  * 
  144.  *   RECURS_DELETED to recursively return all contents of deleted
  145.  *     directories in addition to the directories themselves
  146.  *   
  147.  *   FOLLW_LINKS to pursue symlinks to directories and continue down
  148.  *     the referenced directories when searching recursively (if the
  149.  *     initial string is an undeleted symlink it is always traversed;
  150.  *     deleted symlinks are never traversed)
  151.  *   
  152.  *   FOLLW_MOUNTPOINTS to traverse mount points when searching
  153.  *     recursively (if the initial string is a mountpoint it is always
  154.  *     traversed)
  155.  *
  156.  *   FIND_DOTFILES forces the system to recognize dot files instead of
  157.  *     discarding them when looking for files
  158.  *
  159.  *   If the first character of name is '/', the search is conducted
  160.  *   absolutely from the root of the hierarchy; else, it is conducted
  161.  *   relative to the current working directory.  The number of
  162.  *   matching files is returned in *num_found, and a list of file
  163.  *   names is returned in *found.  If there are no errors, the return
  164.  *   value is 0; else the return value represents the error code of
  165.  *   the error which occurred.  No matter how many file names are
  166.  *   returned, the memory location addressed in *found is a valid
  167.  *   pointer according to Malloc() and can be released using free()
  168.  *   safely.  However, if an error value is returned, the caller
  169.  *   should not attempt to use the values stored in *num_found or
  170.  *   *found.
  171.  *
  172.  * Modifies: *num_found, *found.
  173.  */
  174. int find_matches(name, num_found, found, options)
  175. char *name;
  176. int *num_found;
  177. char ***found;
  178. int options;
  179. {
  180.      char     **matched_files, **return_files, **recurs_files;
  181.      int     num_matched_files = 0, num_return_files = 0,
  182.                 num_recurs_files = 0;
  183.      int    retval;
  184.      int    i;
  185. #ifdef DEBUG
  186.      int    j;
  187. #endif
  188.      int    match_options = 0;
  189.  
  190. #ifdef DEBUG
  191.      fprintf(stderr, "Entering find_matches, name = %s, options = %d.\n",
  192.          name, options);
  193. #endif
  194.      
  195.      match_options = options & (FIND_DELETED | FIND_UNDELETED);
  196.      if (options & (RECURS_FIND_DELETED | RECURS_FIND_UNDELETED |
  197.             FIND_CONTENTS))
  198.       match_options |= FIND_UNDELETED;
  199.      
  200.      if (! match_options) {
  201.       set_error(PAT_NO_FILES_REQUESTED);
  202.       error("find_matches");
  203.       return error_code;
  204.      }
  205.      
  206.      retval = do_match(name, &num_matched_files, &matched_files,
  207.                match_options & FIND_UNDELETED,
  208.                match_options & FIND_DELETED);
  209.      if (retval) {
  210.       error(name);
  211.       return retval;
  212.      }
  213.      if (num_matched_files == 0) {
  214.       *num_found = num_matched_files;
  215.       *found = matched_files;
  216. #ifdef DEBUG
  217.       fprintf(stderr, "No matches found, returning.\n");
  218. #endif
  219.       return 0;
  220.      }
  221.  
  222. #ifdef DEBUG
  223.      fprintf(stderr, "The following matches were found:\n");
  224.      for (i = 0; i < num_matched_files; i++)
  225.       fprintf(stderr, "  %s\n", matched_files[i]);
  226. #endif
  227.      
  228.      if (options & RECURS) {
  229.       return_files = (char **) Malloc(0);
  230.       if (! return_files) {
  231.            set_error(errno);
  232.            error("Malloc");
  233.            return error_code;
  234.       }
  235.       num_return_files = 0;
  236.  
  237.       for (i = 0; i < num_matched_files; i++) {
  238.  
  239.            retval = do_recurs(matched_files[i], &num_recurs_files,
  240.                   &recurs_files, options);
  241.            if (retval) {
  242.             error(matched_files[i]);
  243.             return retval;
  244.            }
  245.  
  246.            if (num_recurs_files) {
  247.             retval = add_arrays(&return_files, &num_return_files,
  248.                     &recurs_files, &num_recurs_files);
  249.             if (retval) {
  250.              error("add_arrays");
  251.              return retval;
  252.             }
  253. #ifdef DEBUG
  254.             fprintf(stderr,
  255.                 "Just added the following to return_files:\n");
  256.             for (j = num_return_files - num_recurs_files;
  257.              j < num_return_files; j++)
  258.              fprintf(stderr, "  %s\n", return_files[j]);
  259. #endif
  260.            }
  261.            
  262.            if (is_deleted(lastpart(matched_files[i]))) {
  263.             if (options & FIND_DELETED) {
  264.              retval = add_str(&return_files, num_return_files,
  265.                       matched_files[i]);
  266.              if (retval) {
  267.                   error("add_str");
  268.                   return retval;
  269.              }
  270.              num_return_files++;
  271. #ifdef DEBUG
  272.              fprintf(stderr, "Just added %s to return_files.\n",
  273.                  return_files[num_return_files-1]);
  274. #endif
  275.             }
  276.            }
  277.            else if (options & FIND_UNDELETED) {
  278.             retval = add_str(&return_files, num_return_files,
  279.                      matched_files[i]);
  280.             if (retval) {
  281.              error("add_str");
  282.              return retval;
  283.             }
  284.             num_return_files++;
  285. #ifdef DEBUG
  286.             fprintf(stderr, "Just added %s to return_files.\n",
  287.                 return_files[num_return_files-1]);
  288. #endif
  289.            }
  290.       }
  291.       free_list(matched_files, num_matched_files);
  292.       *num_found = num_return_files;
  293.       *found = return_files;
  294.      }
  295.      else {
  296.       *num_found = num_matched_files;
  297.       *found = matched_files;
  298.      }
  299.  
  300.      return 0;
  301. }
  302.  
  303.  
  304.  
  305.  
  306.       
  307.       
  308.             
  309.            
  310. #define string_push(str)\
  311.       strsize = strlen(str);\
  312.       retval = push(str, strsize);\
  313.       if (! retval)\
  314.            retval |= push(&strsize, sizeof(int));\
  315.       if (retval) {\
  316.            error("push");\
  317.            (void) popall();\
  318.            return retval;\
  319.       }
  320. #define string_pop(str)\
  321.       retval = pop(&strsize, sizeof(int));\
  322.       if (! retval)\
  323.            retval = pop(str, strsize);\
  324.       if (! retval)\
  325.            str[strsize] = '\0'
  326.       
  327.       
  328.  
  329.  
  330.  
  331.  
  332. /*
  333.  * Function: do_match(char *name, int *num_found, char ***found,
  334.  *               Boolean match_undeleted, Boolean match_deleted)
  335.  *
  336.  * Requires: name points to a NULL-terminated string, representing a
  337.  *   filename pattern with regular filename characters, path
  338.  *   separators and shell wildcard characters []*?; num_found points
  339.  *   to a valid int memory storage location; found points to a valid
  340.  *   char ** memory storage location.
  341.  *
  342.  * Effects: Returns a list of all the files in the file hierarchy that
  343.  *   match name.  If match_undeleted is true, will return undeleted
  344.  *   files that match; if match_deleted is true, will return
  345.  *   deleted_files that match.  If the first character of name is '/',
  346.  *   the search is conducted absolutely from the root of the
  347.  *   hierarchy; else, it is conducted relative to the current working
  348.  *   directory.  The number of matching files is returned in
  349.  *   *num_found, and a list of file names is returned in *found.  If
  350.  *   there are no errors, the return value is 0; else the return value
  351.  *   represents the error code of the error which occurred.  No matter
  352.  *   how many file names are returned, the memory location addressed
  353.  *   in *found is a valid pointer according to Malloc() and can be
  354.  *   released using free() safely.  However, if an error value is
  355.  *   returned, the caller should not attempt to use the values stored
  356.  *   in *num_found or *found.
  357.  *
  358.  * Modifies: *num_found, *found.
  359.  *
  360.  * Algorithm:
  361.  *
  362.  * start:
  363.  *   base = "" or "/",
  364.  *   name = name or name + 1
  365.  *   initialze found and num_found
  366.  *   dirp = Opendir(base)
  367.  *   first = firstpart(name, rest) (assigns rest as side-effect)
  368.  *   if (! *first) {
  369.  *     add string to list if appropriate
  370.  *     return
  371.  * 
  372.  * loop:
  373.  *   dp = readdir(dirp)
  374.  *   if (! dp) goto updir
  375.  *   compare dp->d_name to first -- match?
  376.  *     yes - goto downdir
  377.  *     no - are we looking for deleted and is dp->d_name deleted?
  378.  *       yes - compare undeleted dp->d_name to first -- match?
  379.  *         yes - goto downdir
  380.  *         no - goto loop
  381.  *       no - goto loop
  382.  *
  383.  * downdir:
  384.  *   save dirp, rest, first and base on stack
  385.  *   first = firstpart(rest, rest)
  386.  *   base = dp->d_name appended to base
  387.  *   is first an empty string?
  388.  *      yes - put back dirp, rest, first, base
  389.  *            goto loop
  390.  *   try to open dir base - opens?
  391.  *      yes - goto loop
  392.  *      no - is the error ENOTDIR?
  393.  *           yes - don't worry about it
  394.  *            no - report the error
  395.  *          restore dirp, rest, first, base from stack
  396.  *           goto loop
  397.  *
  398.  * updir:
  399.  *   close dirp
  400.  *   restore base, rest, first from stack
  401.  *   STACK_EMPTY?
  402.  *     yes - return from procedure with results
  403.  *   restore dirp from stack
  404.  *   goto loop
  405.  */
  406. int do_match(name, num_found, found, match_undeleted, match_deleted)
  407. char *name;
  408. int *num_found;
  409. char ***found;
  410. Boolean match_undeleted, match_deleted;
  411. {
  412.      char base[MAXPATHLEN];
  413.      struct direct *dp;
  414.      DIR *dirp;
  415.      char first[MAXNAMLEN], rest[MAXPATHLEN];
  416.      int retval;
  417.      int strsize;
  418.      struct stat statbuf;
  419. #ifdef PATTERN_DEBUG
  420.      int j;
  421. #endif
  422.      
  423. #ifdef DEBUG
  424.      printf("do_match: looking for %s\n", name);
  425. #endif
  426.  
  427.      /* start: */
  428.      
  429.      if (*name == '/') {
  430.       *base = '/';
  431.       *(base + 1) = '\0';
  432.       name++;
  433.      }
  434.      else 
  435.       *base = '\0';
  436.  
  437.      *found = (char **) Malloc(0);
  438.      if (! *found) {
  439.       set_error(errno);
  440.       error("Malloc");
  441. #ifdef PATTERN_DEBUG
  442.       fprintf(stderr, "do_match: return 1.\n");
  443. #endif
  444.       return error_code;
  445.      }
  446.      *num_found = 0;
  447.      
  448.      dirp = Opendir(base);
  449.      if (! dirp) {
  450.       set_error(errno);
  451.       error(base);
  452. #ifdef PATTERN_DEBUG
  453.       fprintf(stderr, "do_match: return 2.\n");
  454. #endif
  455.       return error_code;
  456.      }
  457.      (void) strcpy(first, firstpart(name, rest));
  458.      if ((! *first) && (match_undeleted)) {
  459.       retval = add_str(found, *num_found, base);
  460.       if (retval) {
  461.            error("add_str");
  462.            (void) popall();
  463. #ifdef PATTERN_DEBUG
  464.            fprintf(stderr, "do_match: return 3.\n");
  465. #endif
  466.            return retval;
  467.       }
  468.       (*num_found)++;
  469. #ifdef PATTERN_DEBUG
  470.       fprintf(stderr, "do_match: return 4.\n");
  471. #endif
  472.       return 0;
  473.      }
  474.      
  475.      while (1) {
  476.       dp = readdir(dirp);
  477.       if (! dp) goto updir;
  478.  
  479.       retval = reg_cmp(first, dp->d_name);
  480. #ifdef PATTERN_DEBUG
  481.     fprintf(stderr, "do_match: comparing %s to %s returns %d.\n",
  482.         first, dp->d_name, retval);
  483. #endif
  484.       if (retval < 0) {
  485.            error("reg_cmp");
  486.            goto updir;
  487.       }
  488.  
  489.       if (retval == REGEXP_MATCH) goto downdir;
  490.  
  491.       if (is_deleted(dp->d_name) && match_deleted) {
  492.            retval = reg_cmp(first, &dp->d_name[2]);
  493. #ifdef PATTERN_DEBUG
  494.            fprintf(stderr,
  495.                "do_match: deleted compare of %s to %s returns %d.\n",
  496.                first, &dp->d_name[2], retval);
  497. #endif
  498.            if (retval < 0) {
  499.             error("reg_cmp");
  500.             goto updir;
  501.            }
  502.  
  503.            if (retval == REGEXP_MATCH)
  504.             goto downdir;
  505.            else
  506.             continue;
  507.       }
  508.       else
  509.            continue;
  510.  
  511.      downdir:
  512. #ifdef PATTERN_DEBUG
  513.       fprintf(stderr, "do_match: downdir\n");
  514. #endif
  515.       retval = push(&dirp, sizeof(DIR *));
  516.       if (retval) {
  517.            error("push");
  518.            (void) popall();
  519. #ifdef PATTERN_DEBUG
  520.            fprintf(stderr, "do_match: return 5.\n");
  521. #endif
  522.            return retval;
  523.       }
  524. #ifdef PATTERN_DEBUG
  525.       fprintf(stderr, "do_match: pushing %s, %s, %s\n", first, rest, base);
  526. #endif
  527.       string_push(first);
  528.       string_push(rest);
  529.       string_push(base);
  530.       (void) strcpy(base, append(base, dp->d_name));
  531.       (void) strcpy(first, firstpart(rest, rest));
  532.       if (! *first) {
  533.            if (is_deleted(lastpart(base))) {
  534.             if (match_deleted) {
  535.              retval = add_str(found, *num_found, base);
  536.              if (retval) {
  537.                   error("add_str");
  538.                   (void) popall();
  539. #ifdef PATTERN_DEBUG
  540.                   fprintf(stderr, "do_match: return 6.\n");
  541. #endif
  542.                   return retval;
  543.              }
  544.              (*num_found)++;
  545.             }
  546.            }
  547.            else if (match_undeleted) {
  548.             retval = add_str(found, *num_found, base);
  549.             if (retval) {
  550.              error("add_str");
  551.              (void) popall();
  552. #ifdef PATTERN_DEBUG
  553.              fprintf(stderr, "do_match: return 7.\n");
  554. #endif
  555.              return retval;
  556.             }
  557.             (*num_found)++;
  558.            }
  559.            string_pop(base);
  560.            string_pop(rest);
  561.            string_pop(first);
  562. #ifdef PATTERN_DEBUG
  563.            fprintf(stderr, "do_match: popped %s, %s, %s\n", first,
  564.                rest, base);
  565. #endif
  566.            (void) pop(&dirp, sizeof(DIR *));
  567.            continue;
  568.       }
  569.  
  570.       if (! stat(base, &statbuf)) {
  571.            if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
  572.             dirp = Opendir(base);
  573.       }
  574.       else {
  575.            dirp = NULL;
  576.       }
  577.       if (! dirp) {
  578.            set_error(errno);
  579.            error(base);
  580.            string_pop(base);
  581.            string_pop(rest);
  582.            string_pop(first);
  583. #ifdef PATTERN_DEBUG
  584.            fprintf(stderr, "do_match: popped %s, %s, %s\n", first,
  585.                rest, base);
  586. #endif
  587.            (void) pop(&dirp, sizeof(DIR *));
  588.            continue;
  589.       }
  590.       else 
  591.            continue;
  592.  
  593.      updir:
  594. #ifdef PATTERN_DEBUG
  595.       fprintf(stderr, "do_match: updir\n");
  596. #endif
  597.       closedir(dirp);
  598.       string_pop(base);
  599. #ifdef PATTERN_DEBUG
  600.       fprintf(stderr, "do_match: popped %s\n", base);
  601. #endif
  602.       if (retval) {
  603.            if (retval != STACK_EMPTY) {
  604.             error("pop");
  605.             (void) popall();
  606. #ifdef PATTERN_DEBUG
  607.             fprintf(stderr, "do_match: return 8.\n");
  608. #endif
  609.             return retval;
  610.            }
  611. #ifdef PATTERN_DEBUG
  612.            fprintf(stderr, "Returning %d word%s from do_match:\n",
  613.                *num_found,
  614.                *num_found == 1 ? "" : "s");
  615.            for (j = 0; j < *num_found; j++)
  616.             fprintf(stderr, "\t%s\n", (*found)[j]);
  617.            fprintf(stderr, "do_match: return 9.\n");
  618. #endif
  619.            return 0;
  620.       }
  621.       string_pop(rest);
  622.       string_pop(first);
  623. #ifdef PATTERN_DEBUG
  624.       fprintf(stderr, "do_match: popped %s, %s\n", rest, first);
  625. #endif
  626.       retval = pop(&dirp, sizeof(DIR *));
  627.       if (retval) {
  628.            error("pop");
  629.            (void) popall();
  630. #ifdef PATTERN_DEBUG
  631.            fprintf(stderr, "do_match: return 10.\n");
  632. #endif
  633.            return retval;
  634.       }
  635.       continue;
  636.      }
  637. }
  638.  
  639.  
  640.  
  641.  
  642.  
  643.  
  644. /*
  645.  * Function: do_recurs(char *name, int *num_found, char ***found,
  646.  *                int options)
  647.  *
  648.  * Requires: name points to a NULL-terminated string, representing a
  649.  *   filename; points to a valid int memory storage location; found
  650.  *   points to a valid char ** memory storage location.
  651.  *
  652.  * Effects: Returns a list of all the files in the file hierarchy that
  653.  *   are underneath the specified file, governed by the options set in
  654.  *   options.  Options are as described in the find_matches() description.
  655.  *   RECURS_FIND_DELETED and RECURS_DELETED imply FIND_DELETED.
  656.  *   RECURS_FIND_UNDELETED implies FIND_UNDELETED.
  657.  *
  658.  * Modifies: *num_found, *found.
  659.  *
  660.  * Algorithm:
  661.  *
  662.  * start:
  663.  *   initialze found and num_found
  664.  *   strcopy(base, name)
  665.  *   check if we just opened a deleted symlink and return if we did
  666.  *   dirp = Opendir(base)
  667.  *   check RECURS options and set FIND options as appropriate
  668.  * 
  669.  * loop:
  670.  *   dp = readdir(dirp)
  671.  *   if (! dp) goto updir
  672.  *   is dp deleted?
  673.  *     yes - is FIND_DELETED set?
  674.  *             yes - add to list
  675.  *                   is RECURS_DELETED set?
  676.  *                   yes - goto downdir
  677.  *                     no - goto loop
  678.  *            no - goto loop
  679.  *     no - is FIND_UNDELETED set?
  680.  *            yes - is the file a dotfile?
  681.  *               yes - is FIND_DOTFILES set?
  682.  *                   yes - add to list
  683.  *                 goto loop
  684.  *               no - add to list
  685.  *                  are RECURS_FIND_DELETED and FIND_DELETED set?
  686.  *               yes - goto downdir
  687.  *                  is RECURS_FIND_UNDELETED set?
  688.  *               yes - goto downdir
  689.  *               no - goto loop
  690.  *           no - goto loop
  691.  *             
  692.  * downdir:
  693.  *   save dirp, base on stack
  694.  *   base = dp->d_name appended to base
  695.  *   try to open base -- opens?
  696.  *     yes - is FOLLW_LINKS set?
  697.  *             yes - is it deleted?
  698.  *              yes - is it a link?
  699.  *                yes - close the directory
  700.  *                  restore base and dirp
  701.  *                  goto loop
  702.  *             no - is it a link?
  703.  *                     yes - close the directory
  704.  *                  restore base and dirp
  705.  *                  goto loop
  706.  *           is FOLLW_MOUNTPOINTS set?
  707.  *             no - is it a mountpoint?
  708.  *                  yes - close the directory
  709.  *                           restore base and dirp
  710.  *                           goto loop
  711.  *     no - is the error ENOTDIR?
  712.  *           yes - don't worry about it
  713.  *           no - report the error
  714.  *         restore base and dirp
  715.  *          goto loop
  716.  * 
  717.  * updir:
  718.  *   close dirp
  719.  *   restore base from stack
  720.  *   STACK_EMPTY?
  721.  *     yes - return from procedure with results
  722.  *   restore dirp from stack
  723.  *   goto loop
  724.  */
  725. int do_recurs(name, num_found, found, options)
  726. char *name;
  727. int *num_found;
  728. char ***found;
  729. int options;
  730. {
  731.      char base[MAXPATHLEN];
  732.      struct direct *dp;
  733.      DIR *dirp;
  734.      int retval;
  735.      int strsize;
  736.      struct stat statbuf;
  737.      int use_stat;
  738.  
  739. #ifdef DEBUG
  740.      fprintf(stderr, "do_recurs: opening %s\n", name);
  741. #endif
  742.  
  743.      /* start: */
  744.      
  745.      *found = (char **) Malloc(0);
  746.      if (! *found) {
  747.       set_error(errno);
  748.       error("Malloc");
  749.       return error_code;
  750.      }
  751.      *num_found = 0;
  752.      strcpy(base, name);
  753.  
  754.      if (lstat(base, &statbuf)) {
  755.       set_error(errno);
  756.       error(base);
  757.       return error_code;
  758.      }
  759.      
  760.      if (is_link(base, &statbuf)) {
  761.       /* Never follow deleted symlinks */
  762.       if (is_deleted(lastpart(base))) {
  763.            return 0;
  764.       }
  765.       if (stat(base, &statbuf)) {
  766.            if (errno == ENOENT) {
  767.             extern int readlink();
  768.             char pathbuf[MAXPATHLEN];
  769.             int cc;
  770.             
  771.             /* What the link is pointing to does not exist; */
  772.             /* this is a warning, not an error.            */
  773.             set_warning(errno);
  774.             cc = readlink(base, pathbuf, MAXPATHLEN);
  775.             if (cc > 0) {
  776.              char error_buf[2*MAXPATHLEN+20];
  777.  
  778.              pathbuf[(cc == MAXPATHLEN) ? (cc - 1) : cc] = '\0';
  779.              sprintf(error_buf, "%s (pointed to by %s)", pathbuf,
  780.                  base);
  781.              error(error_buf);
  782.             }
  783.             else {
  784.              error(base);
  785.             }
  786.  
  787.             return 0;
  788.            }
  789.            else {
  790.             set_error(errno);
  791.             error(base);
  792.             return error_code;
  793.            }
  794.       }
  795.      }
  796.  
  797.      if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
  798.       return 0;
  799.      
  800.      dirp = Opendir(base);
  801.      if (! dirp) {
  802. #ifdef DEBUG
  803.       fprintf(stderr, "Couldn't open %s.\n", base);
  804. #endif
  805.       set_error(errno);
  806.       error(base);
  807.       return error_code;
  808.      }
  809.  
  810.      if (options & (RECURS_FIND_DELETED | RECURS_DELETED))
  811.       options |= FIND_DELETED;
  812.      if (options & RECURS_FIND_UNDELETED)
  813.       options |= FIND_UNDELETED;
  814.      
  815.      while (1) {
  816.       dp = readdir(dirp);
  817.       if (! dp) goto updir;
  818.  
  819.       if (is_deleted(dp->d_name)) {
  820.            if (options & FIND_DELETED) {
  821.             retval = add_str(found, *num_found,
  822.                      append(base, dp->d_name));
  823.             if (retval) {
  824.              error("add_str");
  825.              (void) popall();
  826.              return retval;
  827.             }
  828.             (*num_found)++;
  829.             if (options & RECURS_DELETED)
  830.              goto downdir;
  831.             else
  832.              continue;
  833.            }
  834.            else
  835.             continue;
  836.       }
  837.  
  838.       if (options & FIND_UNDELETED) {
  839.            if (is_dotfile(dp->d_name)) {
  840.             if (options & FIND_DOTFILES) {
  841.              retval = add_str(found, *num_found,
  842.                       append(base, dp->d_name));
  843.              if (retval) {
  844.                   error("add_str");
  845.                   (void) popall();
  846.                   return retval;
  847.              }
  848.             }
  849.             continue;
  850.            }
  851.            else {
  852.             retval = add_str(found, *num_found,
  853.                      append(base, dp->d_name));
  854.             if (retval) {
  855.              error("add_str");
  856.              (void) popall();
  857.              return retval;
  858.             }
  859.             (*num_found)++;
  860.            }
  861.       }
  862.  
  863.       if (! is_dotfile(dp->d_name)) {
  864.            if (options & RECURS_FIND_DELETED)
  865.             goto downdir;
  866.            if (options & RECURS_FIND_UNDELETED)
  867.             goto downdir;
  868.       }
  869.       
  870.       continue;
  871.       
  872.            
  873.      downdir:
  874.       retval = push(&dirp, sizeof(DIR *));
  875.       if (retval) {
  876.            error("push");
  877.            (void) popall();
  878.            return retval;
  879.       }
  880.       string_push(base);
  881.       (void) strcpy(base, append(base, dp->d_name));
  882.  
  883.       /*
  884.        * Originally, I did an Opendir() right at the start and
  885.        * then only checked things if the Opendir resulted in an
  886.        * error.  However, this is inefficient, because the
  887.        * Opendir() procedure works by first calling open() on the
  888.        * file, and *then* calling fstat on the file descriptor
  889.        * that is returned.  since most of the time we will be
  890.        * trying to open things that are not directory, it is much
  891.        * more effecient to do the stat first here and to do the
  892.        * Opendir only if the stat results are satisfactory.
  893.        */
  894.       use_stat = (options & FOLLW_LINKS) && (! is_deleted(lastpart(base)));
  895.       if (use_stat)
  896.            retval = stat(base, &statbuf);
  897.       else
  898.            retval = lstat(base, &statbuf);
  899.       if (retval == -1) {
  900.            set_error(errno);
  901.            error(base);
  902.            string_pop(base);
  903.            (void) pop(&dirp, sizeof(DIR *));
  904.            continue;
  905.       }
  906.       /* It's not a directory, so punt it and continue. */
  907.       if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
  908.            string_pop(base);
  909.            (void) pop(&dirp, sizeof(DIR *));
  910.            continue;
  911.       }
  912.  
  913.       /* Actually try to open it. */
  914.       dirp = Opendir(base);
  915.       if (! dirp) {
  916.            set_error(errno);
  917.            error(base);
  918.            string_pop(base);
  919.            (void) pop(&dirp, sizeof(DIR *));
  920.            continue;
  921.       }
  922.       
  923.       if (! (options & FOLLW_MOUNTPOINTS)) {
  924.            if (is_mountpoint(base, use_stat ? (struct stat *) NULL :
  925.                  &statbuf)) {
  926.             closedir(dirp);
  927.             set_warning(PAT_IS_MOUNT);
  928.             error(base);
  929.             string_pop(base);
  930.             (void) pop(&dirp, sizeof(DIR *));
  931.             continue;
  932.            }
  933. #ifdef DEBUG
  934.            else {
  935.             fprintf(stderr,
  936.                 "do_recurs: %s isn't a mountpoint, following.\n",
  937.                 base);
  938.            }
  939. #endif
  940.       }
  941. #ifdef DEBUG
  942.       printf("do_recurs: opening %s\n", base);
  943. #endif
  944.       continue;
  945.       
  946.      updir:
  947.       closedir(dirp);
  948.       string_pop(base);
  949.       if (retval) {
  950.            if (retval != STACK_EMPTY) {
  951.             error("pop");
  952.             (void) popall();
  953.             return retval;
  954.            }
  955.            return 0;
  956.       }
  957.       retval = pop(&dirp, sizeof(DIR *));
  958.       if (retval) {
  959.            error("pop");
  960.            (void) popall();
  961.            return retval;
  962.       }
  963.       continue;
  964.      }
  965. }
  966.  
  967.  
  968. void free_list(list, num)
  969. char **list;
  970. int num;
  971. {
  972.      int i;
  973.  
  974.      for (i = 0; i < num; i++)
  975.       free(list[i]);
  976.  
  977.      free((char *) list);
  978. }
  979.  
  980.  
  981.  
  982.  
  983.  
  984.  
  985. /*
  986.  * returns true if the filename has no globbing wildcards in it.  That
  987.  * means no non-quoted question marks, asterisks, or open square
  988.  * braces.  Assumes a null-terminated string, and a valid globbing
  989.  */
  990. int no_wildcards(name)
  991. char *name;
  992. {
  993.      do {
  994.       switch (*name) {
  995.       case '\\':
  996.            name++;
  997.            break;
  998.       case '?':
  999.            return(0);
  1000.       case '*':
  1001.            return(0);
  1002.       case '[':
  1003.            return(0);
  1004.       }
  1005.      } while (*++name);
  1006.      return(1);
  1007. }
  1008.