home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / f / find12as.zip / FIND.C < prev    next >
C/C++ Source or Header  |  1992-02-22  |  11KB  |  356 lines

  1. /* find -- search for files in a directory heirarchy
  2.    Copyright (C) 1987, 1990 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 1, 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@ai.mit.edu). */
  20.  
  21. /* MS-DOS port (c) 1990 by Thorsten Ohl, ohl@gnu.ai.mit.edu
  22.    This port is also distributed under the terms of the
  23.    GNU General Public License as published by the
  24.    Free Software Foundation.
  25.  
  26.    Please note that this file is not identical to the
  27.    original GNU release, you should have received this
  28.    code as patch to the official release.
  29.  
  30.    $Header: e:/gnu/find/RCS/find.c 1.2.0.3 90/09/23 16:09:38 tho Exp $
  31.  */
  32.  
  33. /* Usage: find path... [expression]
  34.  
  35.    Predicates:
  36.  
  37.    Numbers can be specified as
  38.    +n for greater than n, or
  39.    -n for less than n,
  40.    n for exactly n.
  41.  
  42.    If none of -print, -ls, -ok, -exec are given, -print is assumed.
  43.  
  44.    -atime n            file last accessed n*24 hours ago
  45.    -ctime n            file status last modified n*24 hours ago
  46.    -depth            true; process dir contents before dir itself
  47.    -exec cmd            exec cmd, true if 0 status returned
  48.    -fulldays            true; from day boundaries rather than from now
  49.    -fstype type            file is on a filesystem of type type
  50.    -group gname            file belongs to group gname (gid allowed)
  51.    -inum n            file has inode number n
  52.    -links n            file has n links
  53.    -ls                true; list current file in 'ls -li' format
  54.    -mtime n            file data last modified n*24 hours ago
  55.    -name pattern        base of path name matches glob pattern
  56.                 ('*' and '?' do not match '.' at start)
  57.    -newer file            modtime is more recent than file's
  58.    -nouser            no user corresponds to file's uid
  59.    -nogroup            no group corresponds to file's gid
  60.    -ok cmd            like exec but ask user first; false if not 'y'
  61.    -perm mode            perm bits are exactly mode (octal or symbol)
  62.    -perm -mode            perm bits mode are set (s,s,t checked)
  63.    -permmask mode        true; set significant bits mask for next -perm
  64.                 (allows testing for unset bits)
  65.    -print            true; print current full pathname
  66.    -prune            (no -depth) true; do not descend current dir
  67.                 (-depth) false; no effect
  68.    -regex pattern        path name matches regex pattern
  69.    -size n[c]            file has n blocks (or chars)
  70.    -type c            file type: b, c, d, p, f, l, s
  71.    -user uname            file is owned by uname (uid allowed)
  72.    -version            true; print find version number on stderr
  73.    -xdev            true; don't descend dirs with different st_dev
  74.  
  75.    Grouping operators (in order of decreasing precendence):
  76.  
  77.    ( expr )            force precedence
  78.    ! expr            true if expr is false
  79.    expr1 expr2            and (implied); expr2 not eval if expr1 false
  80.    expr1 -o expr2        or; expr2 not eval if expr1 true
  81.    expr1 -or expr2        same as -o
  82.  
  83.    find processes files by applying each predicate in turn until the
  84.    overall expression evaluates false.  The expression evaluation
  85.    continues until the outcome is known (left hand side false for and,
  86.    true for or).  Once this happens no more expressions are
  87.    evaluated and find moves on to the next pathname.
  88.  
  89.    Exits with status 0 if all files are processed successfully,
  90.    >0 if error occurrs. */
  91.  
  92. #include <sys/types.h>
  93. #include <stdio.h>
  94. #ifndef USG
  95. #include <strings.h>
  96. #else
  97. #include <string.h>
  98. #define index strchr
  99. #define rindex strrchr
  100. #endif
  101. #include <sys/stat.h>
  102. #ifndef S_IFLNK
  103. #define lstat stat
  104. #endif
  105.  
  106. #include "defs.h"
  107.  
  108. #define apply_predicate(pathname, stat_buf_ptr, node)    \
  109.   (*(node)->pred_func)((pathname), (stat_buf_ptr), (node))
  110.  
  111. char *savedir ();
  112. void error ();
  113.  
  114. /* Name this program was run with. */
  115. char *program_name;
  116.  
  117. /* All predicates for each path to process. */
  118. struct pred_struct *predicates;
  119.  
  120. /* The last predicate allocated. */
  121. struct pred_struct *last_pred;
  122.  
  123. /* The root of the evaluation tree. */
  124. struct pred_struct *eval_tree;
  125.  
  126. /* If true, process directory before contents.  True unless -depth given. */
  127. boolean do_dir_first;
  128.  
  129. /* Global permission mask for -perm. */
  130. unsigned long perm_mask;
  131.  
  132. /* Seconds between 00:00 1/1/70 and either one day before now
  133.    (the default), or the start of today (if -fulldays is given). */
  134. long cur_day_start;
  135.  
  136. /* If true, cur_day_start has been adjusted to the start of the day. */
  137. boolean full_days;
  138.  
  139. /* If true, don't cross filesystem boundaries. */
  140. boolean stay_on_filesystem;
  141.  
  142. /* If true, don't descend past current directory. */
  143. boolean stop_at_current_level;
  144.  
  145. /* Status value to return to system. */
  146. int exit_status;
  147.  
  148. void
  149. main (argc, argv)
  150.      int argc;
  151.      char *argv[];
  152. {
  153.   int i;
  154. #ifdef MSDOS
  155.   PARSE_FCT parse_function;
  156. #else
  157.   PFB parse_function;        /* Pointer to who is to do the parsing. */
  158. #endif
  159.   struct pred_struct *cur_pred;
  160.   char *predicate_name;        /* Name of predicate being parsed. */
  161.  
  162.   program_name = argv[0];
  163.  
  164.   predicates = NULL;
  165.   last_pred = NULL;
  166.   do_dir_first = true;
  167.   cur_day_start = time ((long *) 0) - DAYSECS;
  168.   full_days = false;
  169.   stay_on_filesystem = false;
  170.   exit_status = 0;
  171.  
  172. #ifdef    DEBUG
  173.   printf ("%ld %s", cur_day_start, ctime (&cur_day_start));
  174. #endif /* DEBUG */
  175.  
  176.   /* Find where in ARGV the predicates begin. */
  177.   for (i = 1; i < argc && index ("-!()", argv[i][0]) == 0; i++)
  178.     ;
  179.  
  180.   if (i == 1)
  181.     usage ("no paths specified");
  182.  
  183.   /* Enclose the expression in `( ... )' so a default -print will
  184.      apply to the whole expression. */
  185.   parse_open (argv, &argc);
  186.   /* Build the input order list. */
  187.   while (i < argc)
  188.     {
  189.       if (index ("-!()", argv[i][0]) == 0)
  190.     usage ("paths must precede expression");
  191.       predicate_name = argv[i];
  192.       parse_function = find_parser (predicate_name);
  193.       if (parse_function == NULL)
  194.     error (1, 0, "invalid predicate `%s'", predicate_name);
  195.       i++;
  196.       if (!(*parse_function) (argv, &i))
  197.     {
  198.       if (argv[i] == NULL)
  199.         error (1, 0, "missing argument to `%s'", predicate_name);
  200.       else
  201.         error (1, 0, "invalid argument to `%s'", predicate_name);
  202.     }
  203.     }
  204.   if (predicates->pred_next == NULL)
  205.     {
  206.       /* No predicates that are entered into the tree were given.
  207.      Remove the `(', because `( ) -print' is not a valid expression. */
  208.       free (predicates);
  209.       predicates = last_pred = NULL;
  210.     }
  211.   else
  212.     parse_close (argv, &argc);
  213.  
  214.   if (no_side_effects (predicates))
  215.     parse_print (argv, &argc);
  216.  
  217. #ifdef    DEBUG
  218.   print_list (predicates);
  219. #endif /* DEBUG */
  220.  
  221.   /* Done parsing the predicates.  Build the evaluation tree. */
  222.   cur_pred = predicates;
  223.   eval_tree = get_expr (&cur_pred, NO_PREC);
  224. #ifdef    DEBUG
  225.   print_tree (eval_tree, 0);
  226. #endif /* DEBUG */
  227.  
  228.   /* Process all of the input paths. */
  229.   for (i = 1; i < argc && index ("-!()", argv[i][0]) == 0; i++)
  230.     process_path (argv[i], true);
  231.  
  232.   exit (exit_status);
  233. }
  234.  
  235. /* Recursively descend path PATHNAME, applying the predicates.
  236.    If ROOT is true, PATHNAME is a command line argument, and
  237.    thus the root of a subtree. */
  238.  
  239. void
  240. process_path (pathname, root)
  241.      char *pathname;
  242.      boolean root;
  243. {
  244.   static dev_t root_dev;
  245.   struct stat stat_buf;
  246.   char *name_space;
  247.  
  248.   if (lstat (pathname, &stat_buf) != 0)
  249.     {
  250.       error (0, errno, "%s", pathname);
  251.       exit_status = 1;
  252.       return;
  253.     }
  254.  
  255.   if ((stat_buf.st_mode & S_IFMT) != S_IFDIR)
  256.     {
  257.       perm_mask = 07777;    /* Start fresh. */
  258.       apply_predicate (pathname, &stat_buf, eval_tree);
  259.       return;
  260.     }
  261.  
  262.   if (stay_on_filesystem)
  263.     {
  264.       if (root)
  265.     root_dev = stat_buf.st_dev;
  266.       else if (stat_buf.st_dev != root_dev)
  267.     return;
  268.     }
  269.  
  270.   stop_at_current_level = false;
  271.  
  272.   if (do_dir_first)
  273.     {
  274.       perm_mask = 07777;    /* Start fresh. */
  275.       apply_predicate (pathname, &stat_buf, eval_tree);
  276.     }
  277.  
  278.   if (stop_at_current_level == false)
  279.     {
  280.       errno = 0;
  281.       name_space = savedir (pathname, stat_buf.st_size);
  282.       if (name_space == NULL)
  283.     {
  284.       if (errno)
  285.         {
  286.           error (0, errno, "%s", pathname);
  287.           exit_status = 1;
  288.         }
  289.       else
  290.         error (1, 0, "virtual memory exhausted");
  291.     }
  292.       else
  293.     {
  294.       char *namep;        /* Current point in `name_space'. */
  295.       char *cur_path;    /* Full path of each file to process. */
  296.       unsigned cur_path_size; /* Bytes allocated for `cur_path'. */
  297.       unsigned file_len;    /* Length of each path to process. */
  298.       unsigned pathname_len; /* Length of `pathname' + 2. */
  299.  
  300.       if (!strcmp (pathname, "/"))
  301.         pathname_len = 2;    /* Won't add a slash to this. */
  302. #ifdef MSDOS
  303.       else if (!strcmp (pathname + 1, ":/"))
  304.         pathname_len = 4;    /* Won't add a slash to this either. */
  305. #endif /* MSDOS */
  306.       else
  307.         pathname_len = strlen (pathname) + 2; /* For '/' and '\0'. */
  308.       cur_path_size = 0;
  309.       cur_path = NULL;
  310.  
  311.       for (namep = name_space; *namep;
  312.            namep += file_len - pathname_len + 1)
  313.         {
  314.           file_len = pathname_len + strlen (namep);
  315.           if (file_len > cur_path_size)
  316.         {
  317.           while (file_len > cur_path_size)
  318.             cur_path_size += 1024;
  319.           if (cur_path)
  320.             free (cur_path);
  321.           cur_path = xmalloc (cur_path_size);
  322.           strcpy (cur_path, pathname);
  323.           cur_path[pathname_len - 2] = '/';
  324.         }
  325.           strcpy (cur_path + pathname_len - 1, namep);
  326.           process_path (cur_path, false);
  327.         }
  328.       if (cur_path)
  329.         free (cur_path);
  330.       free (name_space);
  331.     }
  332.     }
  333.  
  334.   if (do_dir_first == false)
  335.     {
  336.       perm_mask = 07777;    /* Start fresh. */
  337.       apply_predicate (pathname, &stat_buf, eval_tree);
  338.     }
  339. }
  340.  
  341. /* Return true if there are no side effects in any of the predicates in
  342.    predicate list PRED, false if there are any. */
  343.  
  344. boolean
  345. no_side_effects (pred)
  346.      struct pred_struct *pred;
  347. {
  348.   while (pred != NULL)
  349.     {
  350.       if (pred->side_effects)
  351.     return (false);
  352.       pred = pred->pred_next;
  353.     }
  354.   return (true);
  355. }
  356.