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 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37
38 #include "tweak.h"
39 #ifdef HAVE_UNISTD_H
40 #ifndef __hpux
41 #include <unistd.h>
42 #endif
43 #endif
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include "my-string.h"
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <sys/param.h>
50 #ifdef _POSIX_SOURCE
51 #include "limits.h"
52 #if !defined(MAXPATHLEN) && defined(PATH_MAX)
53 #define MAXPATHLEN PATH_MAX
54 #endif
55 #endif
56 #ifdef HAVE_TZFILE_H
57 #include <tzfile.h>
58 #endif
59 #include "common_def.h"
60 #include "client_def.h"
61 #include "c_extern.h"
62 #ifdef TIME_WITH_SYS_TIME
63 # include <sys/time.h>
64 # include <time.h>
65 #else
66 # ifdef HAVE_SYS_TIME_H
67 # include <sys/time.h>
68 # else
69 # include <time.h>
70 # endif
71 #endif
72
73 #ifdef HAVE_SYS_WAIT_H
74 #include <sys/wait.h>
75 #endif
76 #ifndef WEXITSTATUS
77 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
78 #endif
79 #ifndef WIFEXITED
80 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
81 #endif
82 #include "find.h"
83
84
85 #define FIND_EQUAL 0
86 #define FIND_LESSTHAN 1
87 #define FIND_GREATER 2
88
89 #define COMPARE(a, b) { \
90 switch(plan->flags) { \
91 case FIND_EQUAL: \
92 return(a == b); \
93 case FIND_LESSTHAN: \
94 return(a < b); \
95 case FIND_GREATER: \
96 return(a > b); \
97 } \
98 return(0); \
99 }
100
palloc(enum ntype t,int (* f)())101 static PLAN *palloc (enum ntype t, int (*f)())
102 {
103 PLAN *new;
104
105 new = (PLAN *) malloc(sizeof(PLAN));
106
107 if ( new ) {
108 new->type = t;
109 new->eval = f;
110 new->flags = 0;
111 new->next = NULL;
112 return(new);
113 }
114 perror("palloc");
115 exit(EX_OSERR);
116 }
117
118 extern int isoutput;
119 extern int process;
120
121 /*
122 * find_parsenum --
123 * Parse a string of the form [+-]# and return the value.
124 */
find_parsenum(PLAN * plan,const char * option,char * str,char * endch)125 static long find_parsenum (PLAN * plan, const char * option, char * str,
126 char * endch)
127 {
128 long value;
129 char *endchar; /* pointer to character ending conversion */
130
131 /* determine comparison from leading + or - */
132 switch(*str) {
133 case '+':
134 ++str;
135 plan->flags = FIND_GREATER;
136 break;
137 case '-':
138 ++str;
139 plan->flags = FIND_LESSTHAN;
140 break;
141 default:
142 plan->flags = FIND_EQUAL;
143 break;
144 }
145
146 /*
147 * convert the string with strtol(). Note, if strtol() returns zero
148 * and endchar points to the beginning of the string we know we have
149 * a syntax error.
150 */
151 value = strtol(str, &endchar, 10);
152 if ( (!value && endchar == str) || (endchar[0] &&
153 (!endch || endchar[0] != *endch))) {
154 fprintf(stderr,"%s: %s", option, "illegal numeric value");
155 exit(EX_USAGE);
156 }
157 if (endch)
158 *endch = endchar[0];
159 return(value);
160 }
161
162 /*
163 * -time n functions --
164 *
165 * True if the difference between the file time and the
166 * current time is n 24 hour periods.
167 *
168 */
169
170 extern time_t now;
find_time(PLAN * plan,struct stat * sbuf,char * path)171 static int find_time (PLAN * plan, struct stat * sbuf, char * path)
172 {
173
174 /* with FSP sbuf.st_atime=sbuf.st_ctime=sbuf.st_mtime */
175 COMPARE((now - sbuf->st_atime + SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
176 }
177
178
c_time(char * arg)179 PLAN * c_time (char * arg)
180 {
181 PLAN *new;
182
183 new = palloc(N_TIME, find_time);
184 new->t_data = find_parsenum(new, "-time", arg, NULL);
185 return(new);
186 }
187
188 /*
189 * brace_subst --
190 * Replace occurrences of {} in s1 with s2 and return the result string.
191 */
brace_subst(char * orig,char ** store,char * path,int len)192 static void brace_subst (char * orig, char ** store, char * path, int len)
193 {
194 register int plen;
195 register char ch, *p;
196
197 plen = strlen(path);
198 for (p = *store; (ch = *orig) ; ++orig)
199 if (ch == '{' && orig[1] == '}') {
200 while ((p - *store) + plen > len)
201 if (!(*store = (char *)realloc(*store, len *= 2))) {
202 perror("realloc");
203 client_done();
204 exit(EX_OSERR);
205 }
206 memmove(p,path,plen);
207 p += plen;
208 ++orig;
209 } else
210 *p++ = ch;
211 *p = '\0';
212 }
213
214 /*
215 * queryuser --
216 * print a message to standard error and then read input from standard
217 * input. If the input is 'y' then 1 is returned.
218 */
queryuser(char ** argv)219 static int queryuser (char ** argv)
220 {
221 int ch, first, nl;
222
223 fprintf(stderr, "\"%s", *argv);
224 while (*++argv) fprintf(stderr, " %s", *argv);
225 fprintf(stderr, "\"? ");
226 fflush(stderr);
227
228 first = ch = getchar();
229 for (nl = 0;;)
230 {
231 if (ch == '\n')
232 {
233 nl = 1;
234 break;
235 }
236 if (ch == EOF) break;
237 ch = getchar();
238 }
239
240 if (!nl) {
241 fprintf(stderr, "\n");
242 fflush(stderr);
243 }
244 return(first == 'y');
245 }
246
247 /*
248 * [-exec | -ok] utility [arg ... ] ; functions --
249 *
250 * True if the executed utility returns a zero value as exit status.
251 * The end of the primary expression is delimited by a semicolon. If
252 * "{}" occurs anywhere, it gets replaced by the current pathname.
253 * The current directory for the execution of utility is the same as
254 * the current directory when the find utility was started.
255 *
256 * The primary -ok is different in that it requests affirmation of the
257 * user before executing the utility.
258 */
find_exec(PLAN * plan,struct stat * sbuf,char * path)259 static int find_exec (PLAN * plan, struct stat * sbuf, char * path)
260 {
261 register int cnt;
262 pid_t pid;
263 #ifndef HAVE_UNION_WAIT
264 int status;
265 #else
266 union wait status;
267 #endif
268
269 for (cnt = 0; plan->e_argv[cnt]; ++cnt)
270 if (plan->e_len[cnt])
271 brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], path,
272 plan->e_len[cnt]);
273
274 if (plan->flags && !queryuser(plan->e_argv)) return(0);
275
276 switch(pid = fork())
277 {
278 case -1:
279 perror ("fork");
280 exit(EX_OSERR);
281 case 0:
282 execvp(plan->e_argv[0], plan->e_argv);
283 perror ("execvp");
284 exit(EX_OSERR);
285 }
286 pid = wait(&status);
287
288 return(pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
289 }
290
emalloc_ffind(unsigned int len)291 static char *emalloc_ffind (unsigned int len)
292 {
293 char *p;
294
295 if ( (p = (char *)malloc(len))) return((char *)p);
296 perror("malloc");
297 exit(EX_OSERR);
298 }
299
300 /*
301 * c_exec --
302 * build three parallel arrays, one with pointers to the strings passed
303 * on the command line, one with (possibly duplicated) pointers to the
304 * argv array, and one with integer values that are lengths of the
305 * strings, but also flags meaning that the string has to be massaged.
306 */
c_exec(char *** argvp,int isok)307 PLAN *c_exec (char *** argvp, int isok)
308 {
309 PLAN *new; /* node returned */
310 register int cnt;
311 register char **argv, **ap, *p;
312
313 isoutput = 1;
314
315 new = palloc(N_EXEC, find_exec);
316 new->flags = isok;
317
318 for (ap = argv = *argvp;; ++ap) {
319 if (!*ap) {
320 fprintf(stderr,"%s: no terminating", isok ? "-ok" : "-exec");
321 exit(EX_USAGE);
322 }
323 if (**ap == ';') break;
324 }
325
326 cnt = ap - *argvp + 1;
327 new->e_argv = (char **)emalloc_ffind((unsigned int)cnt * sizeof(char *));
328 new->e_orig = (char **)emalloc_ffind((unsigned int)cnt * sizeof(char *));
329 new->e_len = (int *)emalloc_ffind((unsigned int)cnt * sizeof(int));
330
331 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
332 new->e_orig[cnt] = *argv;
333 for (p = *argv; *p; ++p)
334 if (p[0] == '{' && p[1] == '}') {
335 new->e_argv[cnt] = (char *)emalloc_ffind((unsigned int)MAXPATHLEN);
336 new->e_len[cnt] = MAXPATHLEN;
337 break;
338 }
339 if (!*p) {
340 new->e_argv[cnt] = *argv;
341 new->e_len[cnt] = 0;
342 }
343 }
344 new->e_argv[cnt] = new->e_orig[cnt] = NULL;
345
346 *argvp = argv + 1;
347 return(new);
348 }
349
printtime_ffind(time_t ftime)350 static void printtime_ffind (time_t ftime)
351 {
352 int i;
353 char *longstring;
354
355 longstring = (char *)ctime(&ftime);
356 for (i = 4; i < 11; ++i) putchar(longstring[i]);
357
358 #define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY)
359
360 if (ftime + SIXMONTHS > time((time_t *)NULL))
361 for (i = 11; i < 16; ++i) putchar(longstring[i]);
362 else {
363 (void)putchar(' ');
364 for (i = 20; i < 24; ++i) putchar(longstring[i]);
365 }
366 putchar(' ');
367 }
368
369 #define BLK(A) (((A)+1023)/1024)
370
printlong_ffind(char * name,struct stat * sb)371 static void printlong_ffind (char * name, struct stat * sb)
372 {
373 const char *modep;
374
375 printf("%4ld ", (long)BLK(sb->st_size));
376 modep = ((S_IFDIR & sb->st_mode)) ? "drwxrwxrwx" : "-rw-rw-rw-" ;
377 printf("%s %3u %-*s %-*s ", modep, (unsigned int)sb->st_nlink, 8, "nobody", 8, "nobody");
378
379 printf("%8ld ", (long)sb->st_size);
380 printtime_ffind(sb->st_mtime);
381 printf("%s", name);
382 putchar('\n');
383 }
384
385 /*
386 * -ls functions --
387 *
388 * Always true - prints the current sbuf to stdout in "ls" format.
389 */
find_ls(PLAN * plan,struct stat * sbuf,char * path)390 static int find_ls (PLAN * plan, struct stat * sbuf, char * path)
391 {
392 printlong_ffind(path, sbuf);
393 return(1);
394 }
395
c_ls(void)396 PLAN *c_ls (void)
397 {
398 isoutput = 1;
399 return(palloc(N_LS, find_ls));
400 }
401
402 /*
403 * -name functions --
404 *
405 * True if the basename of the filename being examined
406 * matches pattern using Pattern Matching Notation S3.14
407 */
find_name(PLAN * plan,struct stat * sbuf,char * path)408 static int find_name (PLAN * plan, struct stat * sbuf, char * path)
409 {
410 register char *name;
411
412 /* extract filename */
413 for (name = path + strlen(path); name > path && *name != '/'; name--);
414 if (*name == '/') name++;
415 return(fnmatch(plan->c_data, name));
416 }
417
c_name(char * pattern)418 PLAN *c_name (char * pattern)
419 {
420 PLAN *new;
421
422 new = palloc(N_NAME, find_name);
423 new->c_data = pattern;
424 return(new);
425 }
426
427 /*
428 * -newer file functions --
429 *
430 * True if the current file has been modified more recently
431 * then the modification time of the file named by the pathname
432 * file.
433 */
find_newer(PLAN * plan,struct stat * sbuf,char * path)434 static int find_newer (PLAN * plan, struct stat * sbuf, char * path)
435 {
436 return(sbuf->st_mtime > plan->t_data);
437 }
438
c_newer(char * filename)439 PLAN *c_newer (char * filename)
440 {
441 PLAN *new;
442 struct stat sb;
443
444 if (stat(filename, &sb)) {
445 perror("stat");
446 exit(EX_NOINPUT);
447 }
448 new = palloc(N_NEWER, find_newer);
449 new->t_data = sb.st_mtime;
450 return(new);
451 }
452
453 /*
454 * -print functions --
455 *
456 * Always true, causes the current pathame to be written to
457 * standard output.
458 */
find_print(PLAN * plan,struct stat * sbuf,char * path)459 static int find_print (PLAN * plan, struct stat * sbuf, char * path)
460 {
461 (void)printf("%s\n", path);
462 return(1);
463 }
464
c_print(void)465 PLAN *c_print (void)
466 {
467 isoutput = 1;
468
469 return(palloc(N_PRINT, find_print));
470 }
471
472 /*
473 * -prune functions --
474 *
475 * Prune a portion of the hierarchy.
476 */
find_prune(PLAN * plan,struct stat * sbuf,char * path)477 static int find_prune (PLAN * plan, struct stat * sbuf, char * path)
478 {
479 process = -1;
480 return(1);
481 }
482
c_prune(void)483 PLAN *c_prune (void)
484 {
485 return(palloc(N_PRUNE, find_prune));
486 }
487
488 /*
489 * -size n[c] functions --
490 *
491 * True if the file size in bytes, divided by an implementation defined
492 * value and rounded up to the next integer, is n. If n is followed by
493 * a c, the size is in bytes.
494 */
495 #define FIND_SIZE 512
496 static int divsize = 1;
497
find_size(PLAN * plan,struct stat * sbuf,char * path)498 static int find_size (PLAN * plan, struct stat * sbuf, char * path)
499 {
500 off_t size;
501
502 size = divsize ? ((sbuf->st_size + FIND_SIZE - 1)/FIND_SIZE) : sbuf->st_size;
503 COMPARE(size, plan->o_data);
504 }
505
c_size(char * arg)506 PLAN *c_size (char * arg)
507 {
508 PLAN *new;
509 char endch='c';
510
511 new = palloc(N_SIZE, find_size);
512 new->o_data = find_parsenum(new, "-size", arg, &endch);
513 if (endch == 'c') divsize = 0;
514 return(new);
515 }
516
517 /*
518 * -type c functions --
519 *
520 * True if the type of the file is c, where c is d or f for
521 * directory or regular file, respectively.
522 */
find_type(PLAN * plan,struct stat * sbuf,char * path)523 static int find_type (PLAN * plan, struct stat * sbuf, char * path)
524 {
525 return((sbuf->st_mode & S_IFMT) == plan->m_data);
526 }
527
c_type(char * typestring)528 PLAN *c_type (char * typestring)
529 {
530 PLAN *new;
531 mode_t mask;
532
533 switch (typestring[0]) {
534 case 'd':
535 mask = S_IFDIR;
536 break;
537 case 'f':
538 mask = S_IFREG;
539 break;
540 default:
541 fprintf(stderr,"-type: unknown type");
542 exit(EX_USAGE);
543 }
544
545 new = palloc(N_TYPE, find_type);
546 new->m_data = mask;
547 return(new);
548 }
549
550 /*
551 * ( expression ) functions --
552 *
553 * True if expression is true.
554 */
find_expr(PLAN * plan,struct stat * sbuf,char * path)555 int find_expr (PLAN * plan, struct stat * sbuf, char * path)
556 {
557 register PLAN *p;
558 register int state;
559
560 for(p=plan->p_data[0]; p && (state=(p->eval)(p, sbuf, path)); p=p->next);
561 return(state);
562 }
563
564 /*
565 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
566 * eliminated during phase 2 of find_formplan() --- the '(' node is converted
567 * to a N_EXPR node containing the expression and the ')' node is discarded.
568 */
c_openparen(void)569 PLAN *c_openparen (void)
570 {
571 return(palloc(N_OPENPAREN, (int (*)())-1));
572 }
573
c_closeparen(void)574 PLAN *c_closeparen (void)
575 {
576 return(palloc(N_CLOSEPAREN, (int (*)())-1));
577 }
578
579
580 /*
581 * ! expression functions --
582 *
583 * Negation of a primary; the unary NOT operator.
584 */
find_not(PLAN * plan,struct stat * sbuf,char * path)585 static int find_not (PLAN * plan, struct stat * sbuf, char * path)
586 {
587 register PLAN *p;
588 register int state;
589
590 for(p=plan->p_data[0]; p && (state=(p->eval)(p, sbuf, path)); p=p->next);
591 return(!state);
592 }
593
c_not(void)594 PLAN *c_not (void)
595 {
596 return(palloc(N_NOT, find_not));
597 }
598
599 /*
600 * expression -o expression functions --
601 *
602 * Alternation of primaries; the OR operator. The second expression is
603 * not evaluated if the first expression is true.
604 */
find_or(PLAN * plan,struct stat * sbuf,char * path)605 static int find_or (PLAN * plan, struct stat * sbuf, char * path)
606 {
607 register PLAN *p;
608 register int state;
609
610 for(p=plan->p_data[0]; p && (state=(p->eval)(p, sbuf, path)); p=p->next);
611
612 if (state) return(1);
613
614 for(p=plan->p_data[1]; p && (state=(p->eval)(p, sbuf, path)); p=p->next);
615 return(state);
616 }
617
c_or(void)618 PLAN *c_or (void)
619 {
620 return(palloc(N_OR, find_or));
621 }
622