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) 09/11/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 <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 } 223