home *** CD-ROM | disk | FTP | other *** search
- /* find -- search for files in a directory heirarchy
- Copyright (C) 1987, 1990 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /* GNU find was written by Eric Decker (cire@cisco.com),
- with enhancements by David MacKenzie (djm@ai.mit.edu). */
-
- /* MS-DOS port (c) 1990 by Thorsten Ohl, ohl@gnu.ai.mit.edu
- This port is also distributed under the terms of the
- GNU General Public License as published by the
- Free Software Foundation.
-
- Please note that this file is not identical to the
- original GNU release, you should have received this
- code as patch to the official release.
-
- $Header: e:/gnu/find/RCS/find.c 1.2.0.3 90/09/23 16:09:38 tho Exp $
- */
-
- /* Usage: find path... [expression]
-
- Predicates:
-
- Numbers can be specified as
- +n for greater than n, or
- -n for less than n,
- n for exactly n.
-
- If none of -print, -ls, -ok, -exec are given, -print is assumed.
-
- -atime n file last accessed n*24 hours ago
- -ctime n file status last modified n*24 hours ago
- -depth true; process dir contents before dir itself
- -exec cmd exec cmd, true if 0 status returned
- -fulldays true; from day boundaries rather than from now
- -fstype type file is on a filesystem of type type
- -group gname file belongs to group gname (gid allowed)
- -inum n file has inode number n
- -links n file has n links
- -ls true; list current file in 'ls -li' format
- -mtime n file data last modified n*24 hours ago
- -name pattern base of path name matches glob pattern
- ('*' and '?' do not match '.' at start)
- -newer file modtime is more recent than file's
- -nouser no user corresponds to file's uid
- -nogroup no group corresponds to file's gid
- -ok cmd like exec but ask user first; false if not 'y'
- -perm mode perm bits are exactly mode (octal or symbol)
- -perm -mode perm bits mode are set (s,s,t checked)
- -permmask mode true; set significant bits mask for next -perm
- (allows testing for unset bits)
- -print true; print current full pathname
- -prune (no -depth) true; do not descend current dir
- (-depth) false; no effect
- -regex pattern path name matches regex pattern
- -size n[c] file has n blocks (or chars)
- -type c file type: b, c, d, p, f, l, s
- -user uname file is owned by uname (uid allowed)
- -version true; print find version number on stderr
- -xdev true; don't descend dirs with different st_dev
-
- Grouping operators (in order of decreasing precendence):
-
- ( expr ) force precedence
- ! expr true if expr is false
- expr1 expr2 and (implied); expr2 not eval if expr1 false
- expr1 -o expr2 or; expr2 not eval if expr1 true
- expr1 -or expr2 same as -o
-
- find processes files by applying each predicate in turn until the
- overall expression evaluates false. The expression evaluation
- continues until the outcome is known (left hand side false for and,
- true for or). Once this happens no more expressions are
- evaluated and find moves on to the next pathname.
-
- Exits with status 0 if all files are processed successfully,
- >0 if error occurrs. */
-
- #include <sys/types.h>
- #include <stdio.h>
- #ifndef USG
- #include <strings.h>
- #else
- #include <string.h>
- #define index strchr
- #define rindex strrchr
- #endif
- #include <sys/stat.h>
- #ifndef S_IFLNK
- #define lstat stat
- #endif
-
- #include "defs.h"
-
- #define apply_predicate(pathname, stat_buf_ptr, node) \
- (*(node)->pred_func)((pathname), (stat_buf_ptr), (node))
-
- char *savedir ();
- void error ();
-
- /* Name this program was run with. */
- char *program_name;
-
- /* All predicates for each path to process. */
- struct pred_struct *predicates;
-
- /* The last predicate allocated. */
- struct pred_struct *last_pred;
-
- /* The root of the evaluation tree. */
- struct pred_struct *eval_tree;
-
- /* If true, process directory before contents. True unless -depth given. */
- boolean do_dir_first;
-
- /* Global permission mask for -perm. */
- unsigned long perm_mask;
-
- /* Seconds between 00:00 1/1/70 and either one day before now
- (the default), or the start of today (if -fulldays is given). */
- long cur_day_start;
-
- /* If true, cur_day_start has been adjusted to the start of the day. */
- boolean full_days;
-
- /* If true, don't cross filesystem boundaries. */
- boolean stay_on_filesystem;
-
- /* If true, don't descend past current directory. */
- boolean stop_at_current_level;
-
- /* Status value to return to system. */
- int exit_status;
-
- void
- main (argc, argv)
- int argc;
- char *argv[];
- {
- int i;
- #ifdef MSDOS
- PARSE_FCT parse_function;
- #else
- PFB parse_function; /* Pointer to who is to do the parsing. */
- #endif
- struct pred_struct *cur_pred;
- char *predicate_name; /* Name of predicate being parsed. */
-
- program_name = argv[0];
-
- predicates = NULL;
- last_pred = NULL;
- do_dir_first = true;
- cur_day_start = time ((long *) 0) - DAYSECS;
- full_days = false;
- stay_on_filesystem = false;
- exit_status = 0;
-
- #ifdef DEBUG
- printf ("%ld %s", cur_day_start, ctime (&cur_day_start));
- #endif /* DEBUG */
-
- /* Find where in ARGV the predicates begin. */
- for (i = 1; i < argc && index ("-!()", argv[i][0]) == 0; i++)
- ;
-
- if (i == 1)
- usage ("no paths specified");
-
- /* Enclose the expression in `( ... )' so a default -print will
- apply to the whole expression. */
- parse_open (argv, &argc);
- /* Build the input order list. */
- while (i < argc)
- {
- if (index ("-!()", argv[i][0]) == 0)
- usage ("paths must precede expression");
- predicate_name = argv[i];
- parse_function = find_parser (predicate_name);
- if (parse_function == NULL)
- error (1, 0, "invalid predicate `%s'", predicate_name);
- i++;
- if (!(*parse_function) (argv, &i))
- {
- if (argv[i] == NULL)
- error (1, 0, "missing argument to `%s'", predicate_name);
- else
- error (1, 0, "invalid argument to `%s'", predicate_name);
- }
- }
- if (predicates->pred_next == NULL)
- {
- /* No predicates that are entered into the tree were given.
- Remove the `(', because `( ) -print' is not a valid expression. */
- free (predicates);
- predicates = last_pred = NULL;
- }
- else
- parse_close (argv, &argc);
-
- if (no_side_effects (predicates))
- parse_print (argv, &argc);
-
- #ifdef DEBUG
- print_list (predicates);
- #endif /* DEBUG */
-
- /* Done parsing the predicates. Build the evaluation tree. */
- cur_pred = predicates;
- eval_tree = get_expr (&cur_pred, NO_PREC);
- #ifdef DEBUG
- print_tree (eval_tree, 0);
- #endif /* DEBUG */
-
- /* Process all of the input paths. */
- for (i = 1; i < argc && index ("-!()", argv[i][0]) == 0; i++)
- process_path (argv[i], true);
-
- exit (exit_status);
- }
-
- /* Recursively descend path PATHNAME, applying the predicates.
- If ROOT is true, PATHNAME is a command line argument, and
- thus the root of a subtree. */
-
- void
- process_path (pathname, root)
- char *pathname;
- boolean root;
- {
- static dev_t root_dev;
- struct stat stat_buf;
- char *name_space;
-
- if (lstat (pathname, &stat_buf) != 0)
- {
- error (0, errno, "%s", pathname);
- exit_status = 1;
- return;
- }
-
- if ((stat_buf.st_mode & S_IFMT) != S_IFDIR)
- {
- perm_mask = 07777; /* Start fresh. */
- apply_predicate (pathname, &stat_buf, eval_tree);
- return;
- }
-
- if (stay_on_filesystem)
- {
- if (root)
- root_dev = stat_buf.st_dev;
- else if (stat_buf.st_dev != root_dev)
- return;
- }
-
- stop_at_current_level = false;
-
- if (do_dir_first)
- {
- perm_mask = 07777; /* Start fresh. */
- apply_predicate (pathname, &stat_buf, eval_tree);
- }
-
- if (stop_at_current_level == false)
- {
- errno = 0;
- name_space = savedir (pathname, stat_buf.st_size);
- if (name_space == NULL)
- {
- if (errno)
- {
- error (0, errno, "%s", pathname);
- exit_status = 1;
- }
- else
- error (1, 0, "virtual memory exhausted");
- }
- else
- {
- char *namep; /* Current point in `name_space'. */
- char *cur_path; /* Full path of each file to process. */
- unsigned cur_path_size; /* Bytes allocated for `cur_path'. */
- unsigned file_len; /* Length of each path to process. */
- unsigned pathname_len; /* Length of `pathname' + 2. */
-
- if (!strcmp (pathname, "/"))
- pathname_len = 2; /* Won't add a slash to this. */
- #ifdef MSDOS
- else if (!strcmp (pathname + 1, ":/"))
- pathname_len = 4; /* Won't add a slash to this either. */
- #endif /* MSDOS */
- else
- pathname_len = strlen (pathname) + 2; /* For '/' and '\0'. */
- cur_path_size = 0;
- cur_path = NULL;
-
- for (namep = name_space; *namep;
- namep += file_len - pathname_len + 1)
- {
- file_len = pathname_len + strlen (namep);
- if (file_len > cur_path_size)
- {
- while (file_len > cur_path_size)
- cur_path_size += 1024;
- if (cur_path)
- free (cur_path);
- cur_path = xmalloc (cur_path_size);
- strcpy (cur_path, pathname);
- cur_path[pathname_len - 2] = '/';
- }
- strcpy (cur_path + pathname_len - 1, namep);
- process_path (cur_path, false);
- }
- if (cur_path)
- free (cur_path);
- free (name_space);
- }
- }
-
- if (do_dir_first == false)
- {
- perm_mask = 07777; /* Start fresh. */
- apply_predicate (pathname, &stat_buf, eval_tree);
- }
- }
-
- /* Return true if there are no side effects in any of the predicates in
- predicate list PRED, false if there are any. */
-
- boolean
- no_side_effects (pred)
- struct pred_struct *pred;
- {
- while (pred != NULL)
- {
- if (pred->side_effects)
- return (false);
- pred = pred->pred_next;
- }
- return (true);
- }
-