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