home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume22 / undel2 / part03 / pattern.c < prev   
Encoding:
C/C++ Source or Header  |  1990-06-07  |  22.7 KB  |  897 lines

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