Annotation of src/usr.bin/find/function.c, Revision 1.15
1.15 ! millert 1: /* $OpenBSD: function.c,v 1.14 1999/10/03 19:07:35 millert Exp $ */
1.4 deraadt 2:
1.1 deraadt 3: /*-
4: * Copyright (c) 1990, 1993
5: * The Regents of the University of California. All rights reserved.
6: *
7: * This code is derived from software contributed to Berkeley by
8: * Cimarron D. Taylor of the University of California, Berkeley.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: * 3. All advertising materials mentioning features or use of this software
19: * must display the following acknowledgement:
20: * This product includes software developed by the University of
21: * California, Berkeley and its contributors.
22: * 4. Neither the name of the University nor the names of its contributors
23: * may be used to endorse or promote products derived from this software
24: * without specific prior written permission.
25: *
26: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36: * SUCH DAMAGE.
37: */
38:
39: #ifndef lint
40: /*static char sccsid[] = "from: @(#)function.c 8.1 (Berkeley) 6/6/93";*/
1.15 ! millert 41: static char rcsid[] = "$OpenBSD: function.c,v 1.14 1999/10/03 19:07:35 millert Exp $";
1.1 deraadt 42: #endif /* not lint */
43:
44: #include <sys/param.h>
45: #include <sys/ucred.h>
46: #include <sys/stat.h>
47: #include <sys/wait.h>
48: #include <sys/mount.h>
49:
1.7 tholo 50: #include <dirent.h>
1.1 deraadt 51: #include <err.h>
52: #include <errno.h>
1.15 ! millert 53: #include <fcntl.h>
1.1 deraadt 54: #include <fnmatch.h>
55: #include <fts.h>
56: #include <grp.h>
1.10 millert 57: #include <libgen.h>
1.1 deraadt 58: #include <pwd.h>
59: #include <stdio.h>
60: #include <stdlib.h>
61: #include <string.h>
62: #include <tzfile.h>
63: #include <unistd.h>
64:
65: #include "find.h"
66:
67: #define COMPARE(a, b) { \
68: switch (plan->flags) { \
69: case F_EQUAL: \
70: return (a == b); \
71: case F_LESSTHAN: \
72: return (a < b); \
73: case F_GREATER: \
74: return (a > b); \
75: default: \
76: abort(); \
77: } \
78: }
79:
80: static PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *))));
81:
1.9 millert 82: extern int dotfd;
83: extern time_t now;
84: extern FTS *tree;
85:
1.1 deraadt 86: /*
87: * find_parsenum --
88: * Parse a string of the form [+-]# and return the value.
89: */
90: static long
91: find_parsenum(plan, option, vp, endch)
92: PLAN *plan;
93: char *option, *vp, *endch;
94: {
95: long value;
96: char *endchar, *str; /* Pointer to character ending conversion. */
97:
98: /* Determine comparison from leading + or -. */
99: str = vp;
100: switch (*str) {
101: case '+':
102: ++str;
103: plan->flags = F_GREATER;
104: break;
105: case '-':
106: ++str;
107: plan->flags = F_LESSTHAN;
108: break;
109: default:
110: plan->flags = F_EQUAL;
111: break;
112: }
113:
114: /*
115: * Convert the string with strtol(). Note, if strtol() returns zero
116: * and endchar points to the beginning of the string we know we have
117: * a syntax error.
118: */
119: value = strtol(str, &endchar, 10);
120: if (value == 0 && endchar == str)
121: errx(1, "%s: %s: illegal numeric value", option, vp);
122: if (endchar[0] && (endch == NULL || endchar[0] != *endch))
123: errx(1, "%s: %s: illegal trailing character", option, vp);
124: if (endch)
125: *endch = endchar[0];
126: return (value);
127: }
128:
129: /*
130: * The value of n for the inode times (atime, ctime, and mtime) is a range,
131: * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with
132: * -n, such that "-mtime -1" would be less than 0 days, which isn't what the
133: * user wanted. Correct so that -1 is "less than 1".
134: */
135: #define TIME_CORRECT(p, ttype) \
136: if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \
137: ++((p)->t_data);
138:
139: /*
1.11 deraadt 140: * -amin n functions --
141: *
142: * True if the difference between the file access time and the
143: * current time is n min periods.
144: */
145: int
146: f_amin(plan, entry)
147: PLAN *plan;
148: FTSENT *entry;
149: {
150: extern time_t now;
151:
152: COMPARE((now - entry->fts_statp->st_atime +
153: 60 - 1) / 60, plan->t_data);
154: }
155:
156: PLAN *
157: c_amin(arg)
158: char *arg;
159: {
160: PLAN *new;
161:
162: ftsoptions &= ~FTS_NOSTAT;
163:
164: new = palloc(N_AMIN, f_amin);
165: new->t_data = find_parsenum(new, "-amin", arg, NULL);
166: TIME_CORRECT(new, N_AMIN);
167: return (new);
168: }
169:
170: /*
1.1 deraadt 171: * -atime n functions --
172: *
173: * True if the difference between the file access time and the
174: * current time is n 24 hour periods.
175: */
176: int
177: f_atime(plan, entry)
178: PLAN *plan;
179: FTSENT *entry;
180: {
181:
182: COMPARE((now - entry->fts_statp->st_atime +
183: SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
184: }
185:
186: PLAN *
187: c_atime(arg)
188: char *arg;
189: {
190: PLAN *new;
191:
192: ftsoptions &= ~FTS_NOSTAT;
193:
194: new = palloc(N_ATIME, f_atime);
195: new->t_data = find_parsenum(new, "-atime", arg, NULL);
196: TIME_CORRECT(new, N_ATIME);
197: return (new);
198: }
1.11 deraadt 199:
200: /*
201: * -cmin n functions --
202: *
203: * True if the difference between the last change of file
204: * status information and the current time is n min periods.
205: */
206: int
207: f_cmin(plan, entry)
208: PLAN *plan;
209: FTSENT *entry;
210: {
211: extern time_t now;
212:
213: COMPARE((now - entry->fts_statp->st_ctime +
214: 60 - 1) / 60, plan->t_data);
215: }
216:
217: PLAN *
218: c_cmin(arg)
219: char *arg;
220: {
221: PLAN *new;
222:
223: ftsoptions &= ~FTS_NOSTAT;
224:
225: new = palloc(N_CMIN, f_cmin);
226: new->t_data = find_parsenum(new, "-cmin", arg, NULL);
227: TIME_CORRECT(new, N_CMIN);
228: return (new);
229: }
230:
1.1 deraadt 231: /*
232: * -ctime n functions --
233: *
234: * True if the difference between the last change of file
235: * status information and the current time is n 24 hour periods.
236: */
237: int
238: f_ctime(plan, entry)
239: PLAN *plan;
240: FTSENT *entry;
241: {
242:
243: COMPARE((now - entry->fts_statp->st_ctime +
244: SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
245: }
246:
247: PLAN *
248: c_ctime(arg)
249: char *arg;
250: {
251: PLAN *new;
252:
253: ftsoptions &= ~FTS_NOSTAT;
254:
255: new = palloc(N_CTIME, f_ctime);
256: new->t_data = find_parsenum(new, "-ctime", arg, NULL);
257: TIME_CORRECT(new, N_CTIME);
258: return (new);
259: }
260:
261: /*
262: * -depth functions --
263: *
264: * Always true, causes descent of the directory hierarchy to be done
265: * so that all entries in a directory are acted on before the directory
266: * itself.
267: */
268: int
269: f_always_true(plan, entry)
270: PLAN *plan;
271: FTSENT *entry;
272: {
273: return (1);
274: }
275:
276: PLAN *
277: c_depth()
278: {
279: isdepth = 1;
280:
281: return (palloc(N_DEPTH, f_always_true));
282: }
283:
1.7 tholo 284: /*
285: * -empty functions --
286: *
287: * True if the file or directory is empty
288: */
289: int
290: f_empty(plan, entry)
291: PLAN *plan;
292: FTSENT *entry;
293: {
294: if (S_ISREG(entry->fts_statp->st_mode) && entry->fts_statp->st_size == 0)
295: return (1);
296: if (S_ISDIR(entry->fts_statp->st_mode)) {
297: struct dirent *dp;
298: int empty;
299: DIR *dir;
300:
301: empty = 1;
302: dir = opendir(entry->fts_accpath);
303: if (dir == NULL)
304: err(1, "%s", entry->fts_accpath);
305: for (dp = readdir(dir); dp; dp = readdir(dir))
306: if (dp->d_name[0] != '.' ||
307: (dp->d_name[1] != '\0' &&
308: (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) {
309: empty = 0;
310: break;
311: }
312: closedir(dir);
313: return (empty);
314: }
315: return (0);
316: }
317:
318: PLAN *
319: c_empty()
320: {
321: ftsoptions &= ~FTS_NOSTAT;
322:
323: return (palloc(N_EMPTY, f_empty));
324: }
325:
1.1 deraadt 326: /*
327: * [-exec | -ok] utility [arg ... ] ; functions --
328: *
329: * True if the executed utility returns a zero value as exit status.
330: * The end of the primary expression is delimited by a semicolon. If
331: * "{}" occurs anywhere, it gets replaced by the current pathname.
332: * The current directory for the execution of utility is the same as
333: * the current directory when the find utility was started.
334: *
335: * The primary -ok is different in that it requests affirmation of the
336: * user before executing the utility.
337: */
338: int
339: f_exec(plan, entry)
340: register PLAN *plan;
341: FTSENT *entry;
342: {
343: register int cnt;
344: pid_t pid;
345: int status;
346:
347: for (cnt = 0; plan->e_argv[cnt]; ++cnt)
348: if (plan->e_len[cnt])
349: brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
350: entry->fts_path, plan->e_len[cnt]);
351:
352: if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv))
353: return (0);
354:
355: /* don't mix output of command with find output */
356: fflush(stdout);
357: fflush(stderr);
358:
359: switch (pid = vfork()) {
360: case -1:
361: err(1, "fork");
362: /* NOTREACHED */
363: case 0:
364: if (fchdir(dotfd)) {
365: warn("chdir");
366: _exit(1);
367: }
368: execvp(plan->e_argv[0], plan->e_argv);
369: warn("%s", plan->e_argv[0]);
370: _exit(1);
371: }
372: pid = waitpid(pid, &status, 0);
373: return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
374: }
375:
376: /*
377: * c_exec --
378: * build three parallel arrays, one with pointers to the strings passed
379: * on the command line, one with (possibly duplicated) pointers to the
380: * argv array, and one with integer values that are lengths of the
381: * strings, but also flags meaning that the string has to be massaged.
382: */
383: PLAN *
384: c_exec(argvp, isok)
385: char ***argvp;
386: int isok;
387: {
388: PLAN *new; /* node returned */
389: register int cnt;
390: register char **argv, **ap, *p;
391:
392: isoutput = 1;
393:
394: new = palloc(N_EXEC, f_exec);
395: if (isok)
396: new->flags = F_NEEDOK;
397:
398: for (ap = argv = *argvp;; ++ap) {
399: if (!*ap)
400: errx(1,
401: "%s: no terminating \";\"", isok ? "-ok" : "-exec");
1.8 millert 402: if (**ap == ';')
403: break;
404: }
405:
406: cnt = ap - *argvp + 1;
407: new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
408: new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
409: new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
410:
411: for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
412: new->e_orig[cnt] = *argv;
413: for (p = *argv; *p; ++p)
414: if (p[0] == '{' && p[1] == '}') {
415: new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
416: new->e_len[cnt] = MAXPATHLEN;
417: break;
418: }
419: if (!*p) {
420: new->e_argv[cnt] = *argv;
421: new->e_len[cnt] = 0;
422: }
423: }
424: new->e_argv[cnt] = new->e_orig[cnt] = NULL;
425:
426: *argvp = argv + 1;
427: return (new);
428: }
429:
430: /*
431: * -execdir utility [arg ... ] ; functions --
432: *
433: * True if the executed utility returns a zero value as exit status.
434: * The end of the primary expression is delimited by a semicolon. If
435: * "{}" occurs anywhere, it gets replaced by the unqualified pathname.
436: * The current directory for the execution of utility is the same as
437: * the directory where the file lives.
438: */
439: int
440: f_execdir(plan, entry)
441: register PLAN *plan;
442: FTSENT *entry;
443: {
444: register int cnt;
445: pid_t pid;
1.15 ! millert 446: int status, fd;
1.10 millert 447: char base[MAXPATHLEN];
1.8 millert 448:
1.15 ! millert 449: /* fts(3) does not chdir for the root level so we do it ourselves. */
! 450: if (entry->fts_level == FTS_ROOTLEVEL) {
! 451: if ((fd = open(".", O_RDONLY)) == -1) {
! 452: warn("cannot open \".\"");
! 453: return (0);
! 454: }
! 455: if (chdir(entry->fts_accpath)) {
! 456: warn("cannot chdir to %s", entry->fts_accpath);
! 457: return (0);
! 458: }
! 459: }
! 460:
1.10 millert 461: /* Substitute basename(path) for {} since cwd is it's parent dir */
462: (void)strncpy(base, basename(entry->fts_path), sizeof(base) - 1);
463: base[sizeof(base) - 1] = '\0';
1.8 millert 464: for (cnt = 0; plan->e_argv[cnt]; ++cnt)
465: if (plan->e_len[cnt])
466: brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
1.10 millert 467: base, plan->e_len[cnt]);
1.8 millert 468:
469: /* don't mix output of command with find output */
470: fflush(stdout);
471: fflush(stderr);
472:
473: switch (pid = vfork()) {
474: case -1:
475: err(1, "fork");
476: /* NOTREACHED */
477: case 0:
478: execvp(plan->e_argv[0], plan->e_argv);
479: warn("%s", plan->e_argv[0]);
480: _exit(1);
481: }
1.15 ! millert 482:
! 483: /* Undo the above... */
! 484: if (entry->fts_level == FTS_ROOTLEVEL) {
! 485: if (fchdir(fd) == -1) {
! 486: warn("unable to chdir back to starting directory");
! 487: return (0);
! 488: }
! 489: }
! 490:
1.8 millert 491: pid = waitpid(pid, &status, 0);
492: return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
493: }
494:
495: /*
496: * c_execdir --
497: * build three parallel arrays, one with pointers to the strings passed
498: * on the command line, one with (possibly duplicated) pointers to the
499: * argv array, and one with integer values that are lengths of the
500: * strings, but also flags meaning that the string has to be massaged.
501: */
502: PLAN *
503: c_execdir(argvp)
504: char ***argvp;
505: {
506: PLAN *new; /* node returned */
507: register int cnt;
508: register char **argv, **ap, *p;
509:
510: ftsoptions &= ~FTS_NOSTAT;
511: isoutput = 1;
512:
513: new = palloc(N_EXECDIR, f_execdir);
514:
515: for (ap = argv = *argvp;; ++ap) {
516: if (!*ap)
517: errx(1,
518: "-execdir: no terminating \";\"");
1.1 deraadt 519: if (**ap == ';')
520: break;
521: }
522:
523: cnt = ap - *argvp + 1;
524: new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
525: new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
526: new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
527:
528: for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
529: new->e_orig[cnt] = *argv;
530: for (p = *argv; *p; ++p)
531: if (p[0] == '{' && p[1] == '}') {
532: new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
533: new->e_len[cnt] = MAXPATHLEN;
534: break;
535: }
536: if (!*p) {
537: new->e_argv[cnt] = *argv;
538: new->e_len[cnt] = 0;
539: }
540: }
541: new->e_argv[cnt] = new->e_orig[cnt] = NULL;
542:
543: *argvp = argv + 1;
544: return (new);
545: }
546:
547: /*
548: * -follow functions --
549: *
550: * Always true, causes symbolic links to be followed on a global
551: * basis.
552: */
553: PLAN *
554: c_follow()
555: {
556: ftsoptions &= ~FTS_PHYSICAL;
557: ftsoptions |= FTS_LOGICAL;
558:
559: return (palloc(N_FOLLOW, f_always_true));
560: }
561:
562: /*
563: * -fstype functions --
564: *
565: * True if the file is of a certain type.
566: */
567: int
568: f_fstype(plan, entry)
569: PLAN *plan;
570: FTSENT *entry;
571: {
572: static dev_t curdev; /* need a guaranteed illegal dev value */
573: static int first = 1;
574: struct statfs sb;
575: static short val;
576: static char fstype[MFSNAMELEN];
577: char *p, save[2];
578:
579: /* Only check when we cross mount point. */
580: if (first || curdev != entry->fts_statp->st_dev) {
581: curdev = entry->fts_statp->st_dev;
582:
583: /*
584: * Statfs follows symlinks; find wants the link's file system,
585: * not where it points.
586: */
587: if (entry->fts_info == FTS_SL ||
588: entry->fts_info == FTS_SLNONE) {
1.9 millert 589: if ((p = strrchr(entry->fts_accpath, '/')))
1.1 deraadt 590: ++p;
591: else
592: p = entry->fts_accpath;
593: save[0] = p[0];
594: p[0] = '.';
595: save[1] = p[1];
596: p[1] = '\0';
597:
598: } else
599: p = NULL;
600:
601: if (statfs(entry->fts_accpath, &sb))
602: err(1, "%s", entry->fts_accpath);
603:
604: if (p) {
605: p[0] = save[0];
606: p[1] = save[1];
607: }
608:
609: first = 0;
610:
611: /*
612: * Further tests may need both of these values, so
613: * always copy both of them.
614: */
615: val = sb.f_flags;
616: strncpy(fstype, sb.f_fstypename, MFSNAMELEN);
617: }
618: switch (plan->flags) {
619: case F_MTFLAG:
620: return (val & plan->mt_data);
621: case F_MTTYPE:
622: return (strncmp(fstype, plan->c_data, MFSNAMELEN) == 0);
623: default:
624: abort();
625: }
626: }
627:
628: PLAN *
629: c_fstype(arg)
630: char *arg;
631: {
632: register PLAN *new;
633:
634: ftsoptions &= ~FTS_NOSTAT;
635:
636: new = palloc(N_FSTYPE, f_fstype);
637: switch (*arg) {
638: case 'l':
639: if (!strcmp(arg, "local")) {
640: new->flags = F_MTFLAG;
641: new->mt_data = MNT_LOCAL;
642: return (new);
643: }
644: break;
645: case 'r':
646: if (!strcmp(arg, "rdonly")) {
647: new->flags = F_MTFLAG;
648: new->mt_data = MNT_RDONLY;
649: return (new);
650: }
651: break;
652: }
653:
654: new->flags = F_MTTYPE;
655: new->c_data = arg;
656: return (new);
657: }
658:
659: /*
660: * -group gname functions --
661: *
662: * True if the file belongs to the group gname. If gname is numeric and
663: * an equivalent of the getgrnam() function does not return a valid group
664: * name, gname is taken as a group ID.
665: */
666: int
667: f_group(plan, entry)
668: PLAN *plan;
669: FTSENT *entry;
670: {
671: return (entry->fts_statp->st_gid == plan->g_data);
672: }
673:
674: PLAN *
675: c_group(gname)
676: char *gname;
677: {
678: PLAN *new;
679: struct group *g;
680: gid_t gid;
681:
682: ftsoptions &= ~FTS_NOSTAT;
683:
684: g = getgrnam(gname);
685: if (g == NULL) {
686: gid = atoi(gname);
687: if (gid == 0 && gname[0] != '0')
688: errx(1, "-group: %s: no such group", gname);
689: } else
690: gid = g->gr_gid;
691:
692: new = palloc(N_GROUP, f_group);
693: new->g_data = gid;
694: return (new);
695: }
696:
697: /*
698: * -inum n functions --
699: *
700: * True if the file has inode # n.
701: */
702: int
703: f_inum(plan, entry)
704: PLAN *plan;
705: FTSENT *entry;
706: {
707: COMPARE(entry->fts_statp->st_ino, plan->i_data);
708: }
709:
710: PLAN *
711: c_inum(arg)
712: char *arg;
713: {
714: PLAN *new;
715:
716: ftsoptions &= ~FTS_NOSTAT;
717:
718: new = palloc(N_INUM, f_inum);
719: new->i_data = find_parsenum(new, "-inum", arg, NULL);
720: return (new);
721: }
722:
723: /*
724: * -links n functions --
725: *
726: * True if the file has n links.
727: */
728: int
729: f_links(plan, entry)
730: PLAN *plan;
731: FTSENT *entry;
732: {
733: COMPARE(entry->fts_statp->st_nlink, plan->l_data);
734: }
735:
736: PLAN *
737: c_links(arg)
738: char *arg;
739: {
740: PLAN *new;
741:
742: ftsoptions &= ~FTS_NOSTAT;
743:
744: new = palloc(N_LINKS, f_links);
745: new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
746: return (new);
747: }
748:
749: /*
750: * -ls functions --
751: *
752: * Always true - prints the current entry to stdout in "ls" format.
753: */
754: int
755: f_ls(plan, entry)
756: PLAN *plan;
757: FTSENT *entry;
758: {
759: printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
760: return (1);
761: }
762:
763: PLAN *
764: c_ls()
765: {
766: ftsoptions &= ~FTS_NOSTAT;
767: isoutput = 1;
768:
769: return (palloc(N_LS, f_ls));
1.5 tholo 770: }
771:
772: /*
773: * - maxdepth n functions --
774: *
775: * True if the current search depth is less than or equal to the
776: * maximum depth specified
777: */
778: int
1.6 tholo 779: f_maxdepth(plan, entry)
1.5 tholo 780: PLAN *plan;
781: FTSENT *entry;
782: {
783:
1.6 tholo 784: if (entry->fts_level >= plan->max_data)
1.5 tholo 785: fts_set(tree, entry, FTS_SKIP);
1.6 tholo 786: return (entry->fts_level <= plan->max_data);
1.5 tholo 787: }
788:
789: PLAN *
1.6 tholo 790: c_maxdepth(arg)
1.5 tholo 791: char *arg;
792: {
793: PLAN *new;
794:
1.6 tholo 795: new = palloc(N_MAXDEPTH, f_maxdepth);
796: new->max_data = atoi(arg);
797: return (new);
798: }
799:
800: /*
801: * - mindepth n functions --
802: *
803: * True if the current search depth is greater than or equal to the
804: * minimum depth specified
805: */
806: int
807: f_mindepth(plan, entry)
808: PLAN *plan;
809: FTSENT *entry;
810: {
811:
812: return (entry->fts_level >= plan->min_data);
813: }
814:
815: PLAN *
816: c_mindepth(arg)
817: char *arg;
818: {
819: PLAN *new;
820:
821: new = palloc(N_MINDEPTH, f_mindepth);
822: new->min_data = atoi(arg);
1.5 tholo 823: return (new);
1.1 deraadt 824: }
825:
826: /*
827: * -mtime n functions --
828: *
829: * True if the difference between the file modification time and the
830: * current time is n 24 hour periods.
831: */
832: int
833: f_mtime(plan, entry)
834: PLAN *plan;
835: FTSENT *entry;
836: {
837:
838: COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
839: SECSPERDAY, plan->t_data);
840: }
841:
842: PLAN *
843: c_mtime(arg)
844: char *arg;
845: {
846: PLAN *new;
847:
848: ftsoptions &= ~FTS_NOSTAT;
849:
850: new = palloc(N_MTIME, f_mtime);
851: new->t_data = find_parsenum(new, "-mtime", arg, NULL);
852: TIME_CORRECT(new, N_MTIME);
1.11 deraadt 853: return (new);
854: }
855:
856: /*
857: * -mmin n functions --
858: *
859: * True if the difference between the file modification time and the
860: * current time is n min periods.
861: */
862: int
863: f_mmin(plan, entry)
864: PLAN *plan;
865: FTSENT *entry;
866: {
867: extern time_t now;
868:
869: COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) /
870: 60, plan->t_data);
871: }
872:
873: PLAN *
874: c_mmin(arg)
875: char *arg;
876: {
877: PLAN *new;
878:
879: ftsoptions &= ~FTS_NOSTAT;
880:
881: new = palloc(N_MMIN, f_mmin);
882: new->t_data = find_parsenum(new, "-mmin", arg, NULL);
883: TIME_CORRECT(new, N_MMIN);
1.1 deraadt 884: return (new);
885: }
886:
887: /*
888: * -name functions --
889: *
890: * True if the basename of the filename being examined
891: * matches pattern using Pattern Matching Notation S3.14
892: */
893: int
894: f_name(plan, entry)
895: PLAN *plan;
896: FTSENT *entry;
897: {
898: return (!fnmatch(plan->c_data, entry->fts_name, 0));
899: }
900:
901: PLAN *
902: c_name(pattern)
903: char *pattern;
904: {
905: PLAN *new;
906:
907: new = palloc(N_NAME, f_name);
908: new->c_data = pattern;
909: return (new);
910: }
911:
912: /*
913: * -newer file functions --
914: *
915: * True if the current file has been modified more recently
916: * then the modification time of the file named by the pathname
917: * file.
918: */
919: int
920: f_newer(plan, entry)
921: PLAN *plan;
922: FTSENT *entry;
923: {
924: return (entry->fts_statp->st_mtime > plan->t_data);
925: }
926:
927: PLAN *
928: c_newer(filename)
929: char *filename;
930: {
931: PLAN *new;
932: struct stat sb;
933:
934: ftsoptions &= ~FTS_NOSTAT;
935:
936: if (stat(filename, &sb))
937: err(1, "%s", filename);
938: new = palloc(N_NEWER, f_newer);
939: new->t_data = sb.st_mtime;
940: return (new);
941: }
942:
943: /*
944: * -nogroup functions --
945: *
946: * True if file belongs to a user ID for which the equivalent
947: * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
948: */
949: int
950: f_nogroup(plan, entry)
951: PLAN *plan;
952: FTSENT *entry;
953: {
954: char *group_from_gid();
955:
956: return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
957: }
958:
959: PLAN *
960: c_nogroup()
961: {
962: ftsoptions &= ~FTS_NOSTAT;
963:
964: return (palloc(N_NOGROUP, f_nogroup));
965: }
966:
967: /*
968: * -nouser functions --
969: *
970: * True if file belongs to a user ID for which the equivalent
971: * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
972: */
973: int
974: f_nouser(plan, entry)
975: PLAN *plan;
976: FTSENT *entry;
977: {
978: char *user_from_uid();
979:
980: return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
981: }
982:
983: PLAN *
984: c_nouser()
985: {
986: ftsoptions &= ~FTS_NOSTAT;
987:
988: return (palloc(N_NOUSER, f_nouser));
989: }
990:
991: /*
992: * -path functions --
993: *
994: * True if the path of the filename being examined
995: * matches pattern using Pattern Matching Notation S3.14
996: */
997: int
998: f_path(plan, entry)
999: PLAN *plan;
1000: FTSENT *entry;
1001: {
1002: return (!fnmatch(plan->c_data, entry->fts_path, 0));
1003: }
1004:
1005: PLAN *
1006: c_path(pattern)
1007: char *pattern;
1008: {
1009: PLAN *new;
1010:
1011: new = palloc(N_NAME, f_path);
1012: new->c_data = pattern;
1013: return (new);
1014: }
1015:
1016: /*
1017: * -perm functions --
1018: *
1019: * The mode argument is used to represent file mode bits. If it starts
1020: * with a leading digit, it's treated as an octal mode, otherwise as a
1021: * symbolic mode.
1022: */
1023: int
1024: f_perm(plan, entry)
1025: PLAN *plan;
1026: FTSENT *entry;
1027: {
1028: mode_t mode;
1029:
1030: mode = entry->fts_statp->st_mode &
1031: (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
1032: if (plan->flags == F_ATLEAST)
1033: return ((plan->m_data | mode) == mode);
1034: else
1035: return (mode == plan->m_data);
1036: /* NOTREACHED */
1037: }
1038:
1039: PLAN *
1040: c_perm(perm)
1041: char *perm;
1042: {
1043: PLAN *new;
1044: mode_t *set;
1045:
1046: ftsoptions &= ~FTS_NOSTAT;
1047:
1048: new = palloc(N_PERM, f_perm);
1049:
1050: if (*perm == '-') {
1051: new->flags = F_ATLEAST;
1052: ++perm;
1053: }
1054:
1055: if ((set = setmode(perm)) == NULL)
1056: err(1, "-perm: %s: illegal mode string", perm);
1057:
1058: new->m_data = getmode(set, 0);
1.12 deraadt 1059: free(set);
1.1 deraadt 1060: return (new);
1061: }
1062:
1063: /*
1064: * -print functions --
1065: *
1066: * Always true, causes the current pathame to be written to
1067: * standard output.
1068: */
1069: int
1070: f_print(plan, entry)
1071: PLAN *plan;
1072: FTSENT *entry;
1073: {
1074: (void)printf("%s\n", entry->fts_path);
1075: return(1);
1076: }
1077:
1078: /* ARGSUSED */
1.9 millert 1079: int
1.1 deraadt 1080: f_print0(plan, entry)
1081: PLAN *plan;
1082: FTSENT *entry;
1083: {
1084: (void)fputs(entry->fts_path, stdout);
1085: (void)fputc('\0', stdout);
1086: return(1);
1087: }
1088:
1089: PLAN *
1090: c_print()
1091: {
1092: isoutput = 1;
1093:
1094: return(palloc(N_PRINT, f_print));
1095: }
1096:
1097: PLAN *
1098: c_print0()
1099: {
1100: isoutput = 1;
1101:
1102: return(palloc(N_PRINT0, f_print0));
1103: }
1104:
1105: /*
1106: * -prune functions --
1107: *
1108: * Prune a portion of the hierarchy.
1109: */
1110: int
1111: f_prune(plan, entry)
1112: PLAN *plan;
1113: FTSENT *entry;
1114: {
1115:
1116: if (fts_set(tree, entry, FTS_SKIP))
1117: err(1, "%s", entry->fts_path);
1118: return (1);
1119: }
1120:
1121: PLAN *
1122: c_prune()
1123: {
1124: return (palloc(N_PRUNE, f_prune));
1125: }
1126:
1127: /*
1128: * -size n[c] functions --
1129: *
1130: * True if the file size in bytes, divided by an implementation defined
1131: * value and rounded up to the next integer, is n. If n is followed by
1132: * a c, the size is in bytes.
1133: */
1134: #define FIND_SIZE 512
1135: static int divsize = 1;
1136:
1137: int
1138: f_size(plan, entry)
1139: PLAN *plan;
1140: FTSENT *entry;
1141: {
1142: off_t size;
1143:
1144: size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
1145: FIND_SIZE : entry->fts_statp->st_size;
1146: COMPARE(size, plan->o_data);
1147: }
1148:
1149: PLAN *
1150: c_size(arg)
1151: char *arg;
1152: {
1153: PLAN *new;
1154: char endch;
1155:
1156: ftsoptions &= ~FTS_NOSTAT;
1157:
1158: new = palloc(N_SIZE, f_size);
1159: endch = 'c';
1160: new->o_data = find_parsenum(new, "-size", arg, &endch);
1161: if (endch == 'c')
1162: divsize = 0;
1163: return (new);
1164: }
1165:
1166: /*
1167: * -type c functions --
1168: *
1169: * True if the type of the file is c, where c is b, c, d, p, or f for
1170: * block special file, character special file, directory, FIFO, or
1171: * regular file, respectively.
1172: */
1173: int
1174: f_type(plan, entry)
1175: PLAN *plan;
1176: FTSENT *entry;
1177: {
1178: return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
1179: }
1180:
1181: PLAN *
1182: c_type(typestring)
1183: char *typestring;
1184: {
1185: PLAN *new;
1186: mode_t mask;
1187:
1188: ftsoptions &= ~FTS_NOSTAT;
1189:
1190: switch (typestring[0]) {
1.2 deraadt 1191: #ifdef S_IFWHT
1.3 deraadt 1192: case 'W':
1193: mask = S_IFWHT;
1.13 espie 1194: if ((ftsoptions & FTS_WHITEOUT) == 0)
1195: warnx("-type W without -W is a no-op");
1.3 deraadt 1196: break;
1.2 deraadt 1197: #endif
1.1 deraadt 1198: case 'b':
1199: mask = S_IFBLK;
1200: break;
1201: case 'c':
1202: mask = S_IFCHR;
1203: break;
1204: case 'd':
1205: mask = S_IFDIR;
1206: break;
1207: case 'f':
1208: mask = S_IFREG;
1209: break;
1210: case 'l':
1211: mask = S_IFLNK;
1212: break;
1213: case 'p':
1214: mask = S_IFIFO;
1215: break;
1216: case 's':
1217: mask = S_IFSOCK;
1218: break;
1219: default:
1220: errx(1, "-type: %s: unknown type", typestring);
1221: }
1222:
1223: new = palloc(N_TYPE, f_type);
1224: new->m_data = mask;
1225: return (new);
1226: }
1227:
1228: /*
1229: * -user uname functions --
1230: *
1231: * True if the file belongs to the user uname. If uname is numeric and
1232: * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1233: * return a valid user name, uname is taken as a user ID.
1234: */
1235: int
1236: f_user(plan, entry)
1237: PLAN *plan;
1238: FTSENT *entry;
1239: {
1240: return (entry->fts_statp->st_uid == plan->u_data);
1241: }
1242:
1243: PLAN *
1244: c_user(username)
1245: char *username;
1246: {
1247: PLAN *new;
1248: struct passwd *p;
1249: uid_t uid;
1250:
1251: ftsoptions &= ~FTS_NOSTAT;
1252:
1253: p = getpwnam(username);
1254: if (p == NULL) {
1255: uid = atoi(username);
1256: if (uid == 0 && username[0] != '0')
1257: errx(1, "-user: %s: no such user", username);
1258: } else
1259: uid = p->pw_uid;
1260:
1261: new = palloc(N_USER, f_user);
1262: new->u_data = uid;
1263: return (new);
1264: }
1265:
1266: /*
1267: * -xdev functions --
1268: *
1269: * Always true, causes find not to decend past directories that have a
1270: * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1271: */
1272: PLAN *
1273: c_xdev()
1274: {
1275: ftsoptions |= FTS_XDEV;
1276:
1277: return (palloc(N_XDEV, f_always_true));
1278: }
1279:
1280: /*
1281: * ( expression ) functions --
1282: *
1283: * True if expression is true.
1284: */
1285: int
1286: f_expr(plan, entry)
1287: PLAN *plan;
1288: FTSENT *entry;
1289: {
1290: register PLAN *p;
1291: register int state;
1292:
1293: for (p = plan->p_data[0];
1294: p && (state = (p->eval)(p, entry)); p = p->next);
1295: return (state);
1296: }
1297:
1298: /*
1299: * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
1300: * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1301: * to a N_EXPR node containing the expression and the ')' node is discarded.
1302: */
1303: PLAN *
1304: c_openparen()
1305: {
1306: return (palloc(N_OPENPAREN, (int (*)())-1));
1307: }
1308:
1309: PLAN *
1310: c_closeparen()
1311: {
1312: return (palloc(N_CLOSEPAREN, (int (*)())-1));
1313: }
1314:
1315: /*
1316: * ! expression functions --
1317: *
1318: * Negation of a primary; the unary NOT operator.
1319: */
1320: int
1321: f_not(plan, entry)
1322: PLAN *plan;
1323: FTSENT *entry;
1324: {
1325: register PLAN *p;
1326: register int state;
1327:
1328: for (p = plan->p_data[0];
1329: p && (state = (p->eval)(p, entry)); p = p->next);
1330: return (!state);
1331: }
1332:
1333: PLAN *
1334: c_not()
1335: {
1336: return (palloc(N_NOT, f_not));
1337: }
1338:
1339: /*
1340: * expression -o expression functions --
1341: *
1342: * Alternation of primaries; the OR operator. The second expression is
1343: * not evaluated if the first expression is true.
1344: */
1345: int
1346: f_or(plan, entry)
1347: PLAN *plan;
1348: FTSENT *entry;
1349: {
1350: register PLAN *p;
1351: register int state;
1352:
1353: for (p = plan->p_data[0];
1354: p && (state = (p->eval)(p, entry)); p = p->next);
1355:
1356: if (state)
1357: return (1);
1358:
1359: for (p = plan->p_data[1];
1360: p && (state = (p->eval)(p, entry)); p = p->next);
1361: return (state);
1362: }
1363:
1364: PLAN *
1365: c_or()
1366: {
1367: return (palloc(N_OR, f_or));
1368: }
1369:
1370: static PLAN *
1371: palloc(t, f)
1372: enum ntype t;
1373: int (*f) __P((PLAN *, FTSENT *));
1374: {
1375: PLAN *new;
1376:
1.9 millert 1377: if ((new = malloc(sizeof(PLAN)))) {
1.1 deraadt 1378: new->type = t;
1379: new->eval = f;
1380: new->flags = 0;
1381: new->next = NULL;
1382: return (new);
1383: }
1384: err(1, NULL);
1385: /* NOTREACHED */
1386: }