home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / gnu / find-3.8-src.lha / src / amiga / find-3.8 / find / find.c < prev    next >
C/C++ Source or Header  |  1993-03-26  |  11KB  |  403 lines

  1. /* find -- search for files in a directory hierarchy
  2.    Copyright (C) 1987, 1990, 1991 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* GNU find was written by Eric Decker (cire@cisco.com),
  19.    with enhancements by David MacKenzie (djm@gnu.ai.mit.edu),
  20.    Jay Plett (jay@silence.princeton.nj.us),
  21.    and Tim Wood (axolotl!tim@toad.com).
  22.    The idea for -print0 and xargs -0 came from
  23.    Dan Bernstein (brnstnd@kramden.acf.nyu.edu).  */
  24.  
  25. #include <sys/types.h>
  26. #include <sys/stat.h>
  27. #include <stdio.h>
  28. #include "defs.h"
  29. #include "modetype.h"
  30.  
  31. #ifndef S_IFLNK
  32. #define lstat stat
  33. #endif
  34.  
  35. int lstat ();
  36. int stat ();
  37.  
  38. #define apply_predicate(pathname, stat_buf_ptr, node)    \
  39.   (*(node)->pred_func)((pathname), (stat_buf_ptr), (node))
  40.  
  41. boolean mark_stat ();
  42. boolean opt_expr ();
  43. boolean parse_open ();
  44. boolean parse_close ();
  45. char *savedir ();
  46. void error ();
  47.  
  48. static void scan_directory ();
  49. static int process_path ();
  50.  
  51. /* Name this program was run with. */
  52. char *program_name;
  53.  
  54. /* All predicates for each path to process. */
  55. struct predicate *predicates;
  56.  
  57. /* The last predicate allocated. */
  58. struct predicate *last_pred;
  59.  
  60. /* The root of the evaluation tree. */
  61. static struct predicate *eval_tree;
  62.  
  63. /* If true, process directory before contents.  True unless -depth given. */
  64. boolean do_dir_first;
  65.  
  66. /* If >=0, don't descend more than this many levels of subdirectories. */
  67. int maxdepth;
  68.  
  69. /* If >=0, don't process files above this level. */
  70. int mindepth;
  71.  
  72. /* Current depth; 0 means current path is a command line arg. */
  73. int curdepth;
  74.  
  75. /* Seconds between 00:00 1/1/70 and either one day before now
  76.    (the default), or the start of today (if -daystart is given). */
  77. time_t cur_day_start;
  78.  
  79. /* If true, cur_day_start has been adjusted to the start of the day. */
  80. boolean full_days;
  81.  
  82. /* If true, do not assume that files in directories with nlink == 2
  83.    are non-directories. */
  84. boolean no_leaf_check;
  85.  
  86. /* If true, don't cross filesystem boundaries. */
  87. boolean stay_on_filesystem;
  88.  
  89. /* If true, don't descend past current directory.
  90.    Can be set by -prune, -maxdepth, and -xdev. */
  91. boolean stop_at_current_level;
  92.  
  93. /* If true, we have called stat on the current path. */
  94. boolean have_stat;
  95.  
  96. /* Status value to return to system. */
  97. int exit_status;
  98.  
  99. /* Length of current path. */
  100. int path_length;
  101.  
  102. /* Pointer to the function used to stat files. */
  103. int (*xstat) ();
  104.  
  105. #ifdef DEBUG_STAT
  106. static int
  107. debug_stat (file, bufp)
  108.      char *file;
  109.      struct stat *bufp;
  110. {
  111.   fprintf (stderr, "debug_stat (%s)\n", file);
  112.   return lstat (file, bufp);
  113. }
  114. #endif /* DEBUG_STAT */
  115.  
  116. void
  117. main (argc, argv)
  118.      int argc;
  119.      char *argv[];
  120. {
  121.   int i;
  122.   PFB parse_function;        /* Pointer to who is to do the parsing. */
  123.   struct predicate *cur_pred;
  124.   char *predicate_name;        /* Name of predicate being parsed. */
  125.  
  126.   program_name = argv[0];
  127.  
  128.   predicates = NULL;
  129.   last_pred = NULL;
  130.   do_dir_first = true;
  131.   maxdepth = mindepth = -1;
  132.   cur_day_start = time ((time_t *) 0) - DAYSECS;
  133.   full_days = false;
  134.   no_leaf_check = false;
  135.   stay_on_filesystem = false;
  136.   exit_status = 0;
  137. #ifdef DEBUG_STAT
  138.   xstat = debug_stat;
  139. #else /* !DEBUG_STAT */
  140.   xstat = lstat;
  141. #endif /* !DEBUG_STAT */
  142.  
  143. #ifdef DEBUG
  144.   printf ("cur_day_start = %s", ctime (&cur_day_start));
  145. #endif /* DEBUG */
  146.  
  147.   /* Find where in ARGV the predicates begin. */
  148.   for (i = 1; i < argc && index ("-!(),", argv[i][0]) == NULL; i++)
  149.     /* Do nothing. */ ;
  150.  
  151.   /* Enclose the expression in `( ... )' so a default -print will
  152.      apply to the whole expression. */
  153.   parse_open (argv, &argc);
  154.   /* Build the input order list. */
  155.   while (i < argc)
  156.     {
  157.       if (index ("-!(),", argv[i][0]) == NULL)
  158.     usage ("paths must precede expression");
  159.       predicate_name = argv[i];
  160.       parse_function = find_parser (predicate_name);
  161.       if (parse_function == NULL)
  162.     error (1, 0, "invalid predicate `%s'", predicate_name);
  163.       i++;
  164.       if (!(*parse_function) (argv, &i))
  165.     {
  166.       if (argv[i] == NULL)
  167.         error (1, 0, "missing argument to `%s'", predicate_name);
  168.       else
  169.         error (1, 0, "invalid argument to `%s'", predicate_name);
  170.     }
  171.     }
  172.   if (predicates->pred_next == NULL)
  173.     {
  174.       /* No predicates that do something other than set a global variable
  175.      were given; remove the unneeded initial `(' and add `-print'. */
  176.       cur_pred = predicates;
  177.       predicates = last_pred = predicates->pred_next;
  178.       free ((char *) cur_pred);
  179.       parse_print (argv, &argc);
  180.     }
  181.   else if (!no_side_effects (predicates->pred_next))
  182.     {
  183.       /* One or more predicates that produce output were given;
  184.      remove the unneeded initial `('. */
  185.       cur_pred = predicates;
  186.       predicates = predicates->pred_next;
  187.       free ((char *) cur_pred);
  188.     }
  189.   else
  190.     {
  191.       /* `( user-supplied-expression ) -print'. */
  192.       parse_close (argv, &argc);
  193.       parse_print (argv, &argc);
  194.     }
  195.  
  196. #ifdef    DEBUG
  197.   printf ("Predicate List:\n");
  198.   print_list (predicates);
  199. #endif /* DEBUG */
  200.  
  201.   /* Done parsing the predicates.  Build the evaluation tree. */
  202.   cur_pred = predicates;
  203.   eval_tree = get_expr (&cur_pred, NO_PREC);
  204. #ifdef    DEBUG
  205.   printf ("Eval Tree:\n");
  206.   print_tree (eval_tree, 0);
  207. #endif /* DEBUG */
  208.  
  209.   /* Rearrange the eval tree in optimal-predicate order. */
  210.   opt_expr (&eval_tree);
  211.  
  212.   /* Determine the point, if any, at which to stat the file. */
  213.   mark_stat (eval_tree);
  214.  
  215. #ifdef DEBUG
  216.   printf ("Optimized Eval Tree:\n");
  217.   print_tree (eval_tree, 0);
  218. #endif /* DEBUG */
  219.  
  220.   /* If no paths given, default to ".". */
  221.   for (i = 1; i < argc && index ("-!(),", argv[i][0]) == NULL; i++)
  222.     {
  223.       curdepth = 0;
  224.       path_length = strlen (argv[i]);
  225.       process_path (argv[i], false);
  226.     }
  227.   if (i == 1)
  228.     {
  229.       curdepth = 0;
  230.       path_length = 1;
  231.       process_path (".", false);
  232.     }
  233.  
  234.   exit (exit_status);
  235. }
  236.  
  237. /* Recursively descend path PATHNAME, applying the predicates.
  238.    LEAF is nonzero if PATHNAME is in a directory that has no
  239.    unexamined subdirectories, and therefore it is not a directory.
  240.    This allows us to avoid stat as long as possible for leaf files.
  241.  
  242.    Return nonzero iff PATHNAME is a directory. */
  243.  
  244. static int
  245. process_path (pathname, leaf)
  246.      char *pathname;
  247.      boolean leaf;
  248. {
  249.   struct stat stat_buf;
  250.   int pathlen;            /* Length of PATHNAME. */
  251.   static dev_t root_dev;    /* Device ID of current argument pathname. */
  252.  
  253.   pathlen = strlen (pathname);
  254.  
  255.   /* Assume non-directory initially. */
  256.   stat_buf.st_mode = 0;
  257.  
  258.   if (leaf)
  259.     have_stat = false;
  260.   else
  261.     {
  262.       if ((*xstat) (pathname, &stat_buf) != 0)
  263.     {
  264.       fflush (stdout);
  265.       error (0, errno, "%s", pathname);
  266.       exit_status = 1;
  267.       return 0;
  268.     }
  269.       have_stat = true;
  270.     }
  271.  
  272.   if (!S_ISDIR (stat_buf.st_mode))
  273.     {
  274.       if (curdepth >= mindepth)
  275.     apply_predicate (pathname, &stat_buf, eval_tree);
  276.       return 0;
  277.     }
  278.  
  279.   stop_at_current_level = maxdepth >= 0 && curdepth >= maxdepth;
  280.  
  281.   if (stay_on_filesystem)
  282.     {
  283.       if (curdepth == 0)
  284.     root_dev = stat_buf.st_dev;
  285.       else if (stat_buf.st_dev != root_dev)
  286.     stop_at_current_level = true;
  287.     }
  288.  
  289.   if (do_dir_first && curdepth >= mindepth)
  290.     apply_predicate (pathname, &stat_buf, eval_tree);
  291.  
  292.   if (stop_at_current_level == false)
  293.     /* Scan directory on disk. */
  294.     scan_directory (pathname, pathlen, &stat_buf);
  295.  
  296.   if (do_dir_first == false && curdepth >= mindepth)
  297.     apply_predicate (pathname, &stat_buf, eval_tree);
  298.  
  299.   return 1;
  300. }
  301.  
  302. /* Scan directory PATHNAME and recurse through process_path for each entry.
  303.    PATHLEN is the length of PATHNAME.
  304.    STATP is the results of *xstat on it. */
  305.  
  306. static void
  307. scan_directory (pathname, pathlen, statp)
  308.      char *pathname;
  309.      int pathlen;
  310.      struct stat *statp;
  311. {
  312.   char *name_space;        /* Names of files in PATHNAME. */
  313.   int subdirs_left;        /* Number of unexamined subdirs in PATHNAME. */
  314.  
  315.   subdirs_left = statp->st_nlink - 2; /* Account for name and ".". */
  316.  
  317.   errno = 0;
  318.   /* On some systems (VAX 4.3BSD+NFS), NFS mount points have st_size < 0.  */
  319.   name_space = savedir (pathname, statp->st_size > 0 ? statp->st_size : 512);
  320.   if (name_space == NULL)
  321.     {
  322.       if (errno)
  323.     {
  324.       fflush (stdout);
  325.       error (0, errno, "%s", pathname);
  326.       exit_status = 1;
  327.     }
  328.       else
  329.     {
  330.       fflush (stdout);
  331.       error (1, 0, "virtual memory exhausted");
  332.     }
  333.     }
  334.   else
  335.     {
  336.       register char *namep;    /* Current point in `name_space'. */
  337.       char *cur_path;        /* Full path of each file to process. */
  338.       unsigned cur_path_size;    /* Bytes allocated for `cur_path'. */
  339.       register unsigned file_len; /* Length of each path to process. */
  340.       register unsigned pathname_len; /* PATHLEN plus trailing '/'. */
  341.  
  342.       if (pathname[pathlen - 1] == '/')
  343.     pathname_len = pathlen + 1; /* For '\0'; already have '/'. */
  344.       else
  345.     pathname_len = pathlen + 2; /* For '/' and '\0'. */
  346.       cur_path_size = 0;
  347.       cur_path = NULL;
  348.  
  349.       for (namep = name_space; *namep; namep += file_len - pathname_len + 1)
  350.     {
  351.       /* Append this directory entry's name to the path being searched. */
  352.       file_len = pathname_len + strlen (namep);
  353.       if (file_len > cur_path_size)
  354.         {
  355.           while (file_len > cur_path_size)
  356.         cur_path_size += 1024;
  357.           if (cur_path)
  358.         free (cur_path);
  359.           cur_path = xmalloc (cur_path_size);
  360.           strcpy (cur_path, pathname);
  361.           cur_path[pathname_len - 2] = '/';
  362.         }
  363.       strcpy (cur_path + pathname_len - 1, namep);
  364.  
  365.       curdepth++;
  366.       if (!no_leaf_check)
  367.         /* Normal case optimization.
  368.            On normal Unix filesystems, a directory that has no
  369.            subdirectories has two links: its name, and ".".  Any
  370.            additional links are to the ".." entries of its
  371.            subdirectories.  Once we have processed as many
  372.            subdirectories as there are additional links, we know
  373.            that the rest of the entries are non-directories --
  374.            in other words, leaf files. */
  375.         subdirs_left -= process_path (cur_path, subdirs_left == 0);
  376.       else
  377.         /* There might be weird (NFS?) filesystems mounted,
  378.            which don't have Unix-like directory link counts. */
  379.         process_path (cur_path, false);
  380.       curdepth--;
  381.     }
  382.       if (cur_path)
  383.     free (cur_path);
  384.       free (name_space);
  385.     }
  386. }
  387.  
  388. /* Return true if there are no side effects in any of the predicates in
  389.    predicate list PRED, false if there are any. */
  390.  
  391. boolean
  392. no_side_effects (pred)
  393.      struct predicate *pred;
  394. {
  395.   while (pred != NULL)
  396.     {
  397.       if (pred->side_effects)
  398.     return (false);
  399.       pred = pred->pred_next;
  400.     }
  401.   return (true);
  402. }
  403.