| 1 | /*- |
| 2 | * Copyright (c) 1990 The Regents of the University of California. |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * This code is derived from software contributed to Berkeley by |
| 6 | * Cimarron D. Taylor of the University of California, Berkeley. |
| 7 | * |
| 8 | * %sccs.include.redist.c% |
| 9 | */ |
| 10 | |
| 11 | #ifndef lint |
| 12 | char copyright[] = |
| 13 | "@(#) Copyright (c) 1990 The Regents of the University of California.\n\ |
| 14 | All rights reserved.\n"; |
| 15 | #endif /* not lint */ |
| 16 | |
| 17 | #ifndef lint |
| 18 | static char sccsid[] = "@(#)find.c 4.33 (Berkeley) %G%"; |
| 19 | #endif /* not lint */ |
| 20 | |
| 21 | #include <sys/types.h> |
| 22 | #include <sys/stat.h> |
| 23 | #include <fts.h> |
| 24 | #include <stdio.h> |
| 25 | #include <string.h> |
| 26 | #include <errno.h> |
| 27 | #include "find.h" |
| 28 | |
| 29 | FTS *tree; /* pointer to top of FTS hierarchy */ |
| 30 | time_t now; /* time find was run */ |
| 31 | int ftsoptions; /* options passed to ftsopen() */ |
| 32 | int deprecated; /* old or new syntax */ |
| 33 | int depth; /* set by -depth option */ |
| 34 | int output_specified; /* one of -print, -ok or -exec was specified */ |
| 35 | |
| 36 | main(argc, argv) |
| 37 | int argc; |
| 38 | char **argv; |
| 39 | { |
| 40 | PLAN *plan; |
| 41 | char **p, **paths; |
| 42 | PLAN *find_formplan(); |
| 43 | time_t time(); |
| 44 | |
| 45 | (void)time(&now); /* initialize the time-of-day */ |
| 46 | |
| 47 | if (argc < 2) |
| 48 | usage(); |
| 49 | |
| 50 | paths = argv; |
| 51 | ftsoptions = FTS_NOSTAT|FTS_PHYSICAL; |
| 52 | |
| 53 | /* |
| 54 | * if arguments start with an option, treat it like new syntax; |
| 55 | * otherwise, if has a "-option" anywhere (which isn't an argument |
| 56 | * to another command) treat it as old syntax. |
| 57 | */ |
| 58 | if (argv[1][0] != '-') |
| 59 | for (p = argv + 1; *p; ++p) { |
| 60 | if (!strcmp(*p, "exec") || !strcmp(*p, "ok")) { |
| 61 | while (p[1] && strcmp(*++p, ";")); |
| 62 | continue; |
| 63 | } |
| 64 | if (**p == '-') { |
| 65 | deprecated = 1; |
| 66 | oldsyntax(&argv); |
| 67 | break; |
| 68 | } |
| 69 | } |
| 70 | if (!deprecated) |
| 71 | newsyntax(argc, &argv); |
| 72 | |
| 73 | plan = find_formplan(argv); /* execution plan */ |
| 74 | find_execute(plan, paths); |
| 75 | } |
| 76 | |
| 77 | /* |
| 78 | * find_formplan -- |
| 79 | * process the command line and create a "plan" corresponding to the |
| 80 | * command arguments. |
| 81 | */ |
| 82 | PLAN * |
| 83 | find_formplan(argv) |
| 84 | char **argv; |
| 85 | { |
| 86 | PLAN *plan, *tail, *new; |
| 87 | PLAN *c_print(), *find_create(), *find_squish_not(), *find_squish_or(); |
| 88 | PLAN *find_squish_paren(); |
| 89 | |
| 90 | /* |
| 91 | * for each argument in the command line, determine what kind of node |
| 92 | * it is, create the appropriate node type and add the new plan node |
| 93 | * to the end of the existing plan. The resulting plan is a linked |
| 94 | * list of plan nodes. For example, the string: |
| 95 | * |
| 96 | * % find . -name foo -newer bar -print |
| 97 | * |
| 98 | * results in the plan: |
| 99 | * |
| 100 | * [-name foo]--> [-newer bar]--> [-print] |
| 101 | * |
| 102 | * in this diagram, `[-name foo]' represents the plan node generated |
| 103 | * by c_name() with an argument of foo and `-->' represents the |
| 104 | * plan->next pointer. |
| 105 | */ |
| 106 | for (plan = NULL; *argv;) { |
| 107 | if (!(new = find_create(&argv))) |
| 108 | continue; |
| 109 | if (plan == NULL) |
| 110 | tail = plan = new; |
| 111 | else { |
| 112 | tail->next = new; |
| 113 | tail = new; |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | /* |
| 118 | * if the user didn't specify one of -print, -ok or -exec, then -print |
| 119 | * is assumed so we add a -print node on the end. It is possible that |
| 120 | * the user might want the -print someplace else on the command line, |
| 121 | * but there's no way to know that. |
| 122 | */ |
| 123 | if (!output_specified) { |
| 124 | new = c_print(); |
| 125 | if (plan == NULL) |
| 126 | tail = plan = new; |
| 127 | else { |
| 128 | tail->next = new; |
| 129 | tail = new; |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | /* |
| 134 | * the command line has been completely processed into a search plan |
| 135 | * except for the (, ), !, and -o operators. Rearrange the plan so |
| 136 | * that the portions of the plan which are affected by the operators |
| 137 | * are moved into operator nodes themselves. For example: |
| 138 | * |
| 139 | * [!]--> [-name foo]--> [-print] |
| 140 | * |
| 141 | * becomes |
| 142 | * |
| 143 | * [! [-name foo] ]--> [-print] |
| 144 | * |
| 145 | * and |
| 146 | * |
| 147 | * [(]--> [-depth]--> [-name foo]--> [)]--> [-print] |
| 148 | * |
| 149 | * becomes |
| 150 | * |
| 151 | * [expr [-depth]-->[-name foo] ]--> [-print] |
| 152 | * |
| 153 | * operators are handled in order of precedence. |
| 154 | */ |
| 155 | |
| 156 | plan = find_squish_paren(plan); /* ()'s */ |
| 157 | plan = find_squish_not(plan); /* !'s */ |
| 158 | plan = find_squish_or(plan); /* -o's */ |
| 159 | return(plan); |
| 160 | } |
| 161 | |
| 162 | /* |
| 163 | * find_execute -- |
| 164 | * take a search plan and an array of search paths and executes the plan |
| 165 | * over all FTSENT's returned for the given search paths. |
| 166 | */ |
| 167 | find_execute(plan, paths) |
| 168 | PLAN *plan; /* search plan */ |
| 169 | char **paths; /* array of pathnames to traverse */ |
| 170 | { |
| 171 | FTSENT *entry; /* current fts entry */ |
| 172 | PLAN *p; |
| 173 | |
| 174 | if (!(tree = ftsopen(paths, ftsoptions, NULL))) { |
| 175 | (void)fprintf(stderr, "find: ftsopen: %s.\n", strerror(errno)); |
| 176 | exit(1); |
| 177 | } |
| 178 | while (entry = ftsread(tree)) { |
| 179 | switch(entry->fts_info) { |
| 180 | case FTS_DNR: |
| 181 | (void)fprintf(stderr, |
| 182 | "find: %s: unable to read.\n", entry->fts_path); |
| 183 | continue; |
| 184 | case FTS_DNX: |
| 185 | (void)fprintf(stderr, |
| 186 | "find: %s: unable to search.\n", entry->fts_path); |
| 187 | continue; |
| 188 | case FTS_ERR: |
| 189 | (void)fprintf(stderr, |
| 190 | "find: %s: %s.\n", entry->fts_path, |
| 191 | strerror(errno)); |
| 192 | continue; |
| 193 | case FTS_D: |
| 194 | if (depth) |
| 195 | continue; |
| 196 | break; |
| 197 | case FTS_DC: |
| 198 | (void)fprintf(stderr, |
| 199 | "find: directory cycle: %s.\n", entry->fts_path); |
| 200 | continue; |
| 201 | case FTS_DP: |
| 202 | if (!depth) |
| 203 | continue; |
| 204 | break; |
| 205 | case FTS_NS: |
| 206 | if (!(ftsoptions & FTS_NOSTAT)) { |
| 207 | (void)fprintf(stderr, |
| 208 | "find: can't stat: %s.\n", entry->fts_path); |
| 209 | continue; |
| 210 | } |
| 211 | break; |
| 212 | } |
| 213 | |
| 214 | /* |
| 215 | * call all the functions in the execution plan until one is |
| 216 | * false or all have been executed. This is where we do all |
| 217 | * the work specified by the user on the command line. |
| 218 | */ |
| 219 | for (p = plan; p && (p->eval)(p, entry); p = p->next); |
| 220 | } |
| 221 | (void)ftsclose(tree); |
| 222 | } |