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