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.35 (Berkeley) 11/15/90"; 19 #endif /* not lint */ 20 21 #include <sys/param.h> 22 #include <sys/stat.h> 23 #include <sys/errno.h> 24 #include <fts.h> 25 #include <stdio.h> 26 #include "find.h" 27 #include <string.h> 28 #include <stdlib.h> 29 30 FTS *tree; /* pointer to top of FTS hierarchy */ 31 time_t now; /* time find was run */ 32 /* options for the ftsopen(3) call */ 33 int ftsoptions = FTS_NOSTAT|FTS_PHYSICAL; 34 int isdeprecated; /* using deprecated syntax */ 35 int isdepth; /* do directories on post-order visit */ 36 int isoutput; /* user specified output operator */ 37 int isrelative; /* can do -exec/ok on relative path */ 38 int isstopdnx; /* don't read unsearchable directories */ 39 40 main(argc, argv) 41 int argc; 42 char **argv; 43 { 44 PLAN *plan; 45 char **p, **paths; 46 PLAN *find_formplan(); 47 time_t time(); 48 void newsyntax(), oldsyntax(); 49 50 (void)time(&now); /* initialize the time-of-day */ 51 52 if (argc < 2) 53 usage(); 54 55 paths = argv; 56 57 /* 58 * if arguments start with an option, treat it like new syntax; 59 * otherwise, if has a "-option" anywhere (which isn't an argument 60 * to another command) treat it as old syntax. 61 */ 62 if (argv[1][0] != '-') 63 for (p = argv + 1; *p; ++p) { 64 if (!strcmp(*p, "exec") || !strcmp(*p, "ok")) { 65 while (p[1] && strcmp(*++p, ";")); 66 continue; 67 } 68 if (**p == '-') { 69 isdeprecated = 1; 70 oldsyntax(&argv); 71 break; 72 } 73 } 74 if (!isdeprecated) 75 newsyntax(argc, &argv); 76 77 plan = find_formplan(argv); /* execution plan */ 78 find_execute(plan, paths); 79 } 80 81 /* 82 * find_formplan -- 83 * process the command line and create a "plan" corresponding to the 84 * command arguments. 85 */ 86 PLAN * 87 find_formplan(argv) 88 char **argv; 89 { 90 PLAN *plan, *tail, *new; 91 PLAN *c_print(), *find_create(), *not_squish(), *or_squish(); 92 PLAN *paren_squish(); 93 94 /* 95 * for each argument in the command line, determine what kind of node 96 * it is, create the appropriate node type and add the new plan node 97 * to the end of the existing plan. The resulting plan is a linked 98 * list of plan nodes. For example, the string: 99 * 100 * % find . -name foo -newer bar -print 101 * 102 * results in the plan: 103 * 104 * [-name foo]--> [-newer bar]--> [-print] 105 * 106 * in this diagram, `[-name foo]' represents the plan node generated 107 * by c_name() with an argument of foo and `-->' represents the 108 * plan->next pointer. 109 */ 110 for (plan = NULL; *argv;) { 111 if (!(new = find_create(&argv))) 112 continue; 113 if (plan == NULL) 114 tail = plan = new; 115 else { 116 tail->next = new; 117 tail = new; 118 } 119 } 120 121 /* 122 * if the user didn't specify one of -print, -ok or -exec, then -print 123 * is assumed so we add a -print node on the end. It is possible that 124 * the user might want the -print someplace else on the command line, 125 * but there's no way to know that. 126 */ 127 if (!isoutput) { 128 new = c_print(); 129 if (plan == NULL) 130 tail = plan = new; 131 else { 132 tail->next = new; 133 tail = new; 134 } 135 } 136 137 /* 138 * the command line has been completely processed into a search plan 139 * except for the (, ), !, and -o operators. Rearrange the plan so 140 * that the portions of the plan which are affected by the operators 141 * are moved into operator nodes themselves. For example: 142 * 143 * [!]--> [-name foo]--> [-print] 144 * 145 * becomes 146 * 147 * [! [-name foo] ]--> [-print] 148 * 149 * and 150 * 151 * [(]--> [-depth]--> [-name foo]--> [)]--> [-print] 152 * 153 * becomes 154 * 155 * [expr [-depth]-->[-name foo] ]--> [-print] 156 * 157 * operators are handled in order of precedence. 158 */ 159 160 plan = paren_squish(plan); /* ()'s */ 161 plan = not_squish(plan); /* !'s */ 162 plan = or_squish(plan); /* -o's */ 163 return(plan); 164 } 165 166 /* 167 * find_execute -- 168 * take a search plan and an array of search paths and executes the plan 169 * over all FTSENT's returned for the given search paths. 170 */ 171 find_execute(plan, paths) 172 PLAN *plan; /* search plan */ 173 char **paths; /* array of pathnames to traverse */ 174 { 175 register FTSENT *entry; 176 PLAN *p; 177 178 /* 179 * If need stat info, might as well quit when the directory isn't 180 * searchable. 181 */ 182 if (!(ftsoptions & FTS_NOSTAT)) 183 isstopdnx = 1; 184 185 if (!(tree = fts_open(paths, ftsoptions, (int (*)())NULL))) { 186 (void)fprintf(stderr, "find: ftsopen: %s.\n", strerror(errno)); 187 exit(1); 188 } 189 190 while (entry = fts_read(tree)) { 191 switch(entry->fts_info) { 192 case FTS_DNR: 193 (void)fprintf(stderr, 194 "find: %s: unable to read.\n", entry->fts_path); 195 continue; 196 case FTS_DNX: { 197 /* 198 * If can't search the directory, but able to read it, 199 * and don't need stat information or to exec/ok the 200 * file, use the fts_children list. 201 */ 202 register char *t; 203 204 if (isstopdnx) 205 goto srcherr; 206 errno = 0; 207 entry = fts_children(tree); 208 if (errno) 209 goto srcherr; 210 for (t = entry->fts_path; *t; ++t); 211 *t = '/'; 212 for (; entry; entry = entry->fts_link) { 213 (void)bcopy(entry->fts_name, t + 1, 214 entry->fts_namelen + 1); 215 for (p = plan; p && (p->eval)(p, entry); 216 p = p->next); 217 } 218 continue; 219 220 srcherr: (void)fprintf(stderr, 221 "find: %s: unable to search.\n", entry->fts_path); 222 continue; 223 } 224 case FTS_ERR: 225 (void)fprintf(stderr, 226 "find: %s: %s.\n", entry->fts_path, 227 strerror(errno)); 228 continue; 229 case FTS_D: 230 if (isdepth) 231 continue; 232 break; 233 case FTS_DC: 234 (void)fprintf(stderr, 235 "find: directory cycle: %s.\n", entry->fts_path); 236 continue; 237 case FTS_DP: 238 if (!isdepth) 239 continue; 240 break; 241 case FTS_NS: 242 if (!(ftsoptions & FTS_NOSTAT)) { 243 (void)fprintf(stderr, 244 "find: can't stat: %s.\n", entry->fts_path); 245 continue; 246 } 247 break; 248 } 249 250 /* 251 * call all the functions in the execution plan until one is 252 * false or all have been executed. This is where we do all 253 * the work specified by the user on the command line. 254 */ 255 for (p = plan; p && (p->eval)(p, entry); p = p->next); 256 } 257 (void)fts_close(tree); 258 } 259