Annotation of src/usr.bin/find/function.c, Revision 1.18
1.18 ! deraadt 1: /* $OpenBSD: function.c,v 1.17 1999/12/04 22:42:32 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.18 ! deraadt 41: static char rcsid[] = "$OpenBSD: function.c,v 1.17 1999/12/04 22:42:32 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) \
1.17 millert 137: ++((p)->sec_data);
1.1 deraadt 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 +
1.17 millert 153: 60 - 1) / 60, plan->sec_data);
1.11 deraadt 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);
1.17 millert 165: new->sec_data = find_parsenum(new, "-amin", arg, NULL);
1.11 deraadt 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 +
1.17 millert 183: SECSPERDAY - 1) / SECSPERDAY, plan->sec_data);
1.1 deraadt 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);
1.17 millert 195: new->sec_data = find_parsenum(new, "-atime", arg, NULL);
1.1 deraadt 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 +
1.17 millert 214: 60 - 1) / 60, plan->sec_data);
1.11 deraadt 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);
1.17 millert 226: new->sec_data = find_parsenum(new, "-cmin", arg, NULL);
1.11 deraadt 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 +
1.17 millert 244: SECSPERDAY - 1) / SECSPERDAY, plan->sec_data);
1.1 deraadt 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);
1.17 millert 256: new->sec_data = find_parsenum(new, "-ctime", arg, NULL);
1.1 deraadt 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)) {
1.16 millert 456: (void) close(fd);
1.15 millert 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");
1.16 millert 487: (void) close(fd);
1.15 millert 488: return (0);
489: }
1.16 millert 490: (void) close(fd);
1.15 millert 491: }
492:
1.8 millert 493: pid = waitpid(pid, &status, 0);
494: return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
495: }
496:
497: /*
498: * c_execdir --
499: * build three parallel arrays, one with pointers to the strings passed
500: * on the command line, one with (possibly duplicated) pointers to the
501: * argv array, and one with integer values that are lengths of the
502: * strings, but also flags meaning that the string has to be massaged.
503: */
504: PLAN *
505: c_execdir(argvp)
506: char ***argvp;
507: {
508: PLAN *new; /* node returned */
509: register int cnt;
510: register char **argv, **ap, *p;
511:
512: ftsoptions &= ~FTS_NOSTAT;
513: isoutput = 1;
514:
515: new = palloc(N_EXECDIR, f_execdir);
516:
517: for (ap = argv = *argvp;; ++ap) {
518: if (!*ap)
519: errx(1,
520: "-execdir: no terminating \";\"");
1.1 deraadt 521: if (**ap == ';')
522: break;
523: }
524:
525: cnt = ap - *argvp + 1;
526: new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
527: new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
528: new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
529:
530: for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
531: new->e_orig[cnt] = *argv;
532: for (p = *argv; *p; ++p)
533: if (p[0] == '{' && p[1] == '}') {
534: new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
535: new->e_len[cnt] = MAXPATHLEN;
536: break;
537: }
538: if (!*p) {
539: new->e_argv[cnt] = *argv;
540: new->e_len[cnt] = 0;
541: }
542: }
543: new->e_argv[cnt] = new->e_orig[cnt] = NULL;
544:
545: *argvp = argv + 1;
546: return (new);
547: }
548:
549: /*
550: * -follow functions --
551: *
552: * Always true, causes symbolic links to be followed on a global
553: * basis.
554: */
555: PLAN *
556: c_follow()
557: {
558: ftsoptions &= ~FTS_PHYSICAL;
559: ftsoptions |= FTS_LOGICAL;
560:
561: return (palloc(N_FOLLOW, f_always_true));
562: }
563:
564: /*
565: * -fstype functions --
566: *
567: * True if the file is of a certain type.
568: */
569: int
570: f_fstype(plan, entry)
571: PLAN *plan;
572: FTSENT *entry;
573: {
574: static dev_t curdev; /* need a guaranteed illegal dev value */
575: static int first = 1;
576: struct statfs sb;
577: static short val;
578: static char fstype[MFSNAMELEN];
579: char *p, save[2];
580:
581: /* Only check when we cross mount point. */
582: if (first || curdev != entry->fts_statp->st_dev) {
583: curdev = entry->fts_statp->st_dev;
584:
585: /*
586: * Statfs follows symlinks; find wants the link's file system,
587: * not where it points.
588: */
589: if (entry->fts_info == FTS_SL ||
590: entry->fts_info == FTS_SLNONE) {
1.9 millert 591: if ((p = strrchr(entry->fts_accpath, '/')))
1.1 deraadt 592: ++p;
593: else
594: p = entry->fts_accpath;
595: save[0] = p[0];
596: p[0] = '.';
597: save[1] = p[1];
598: p[1] = '\0';
599:
600: } else
601: p = NULL;
602:
603: if (statfs(entry->fts_accpath, &sb))
604: err(1, "%s", entry->fts_accpath);
605:
606: if (p) {
607: p[0] = save[0];
608: p[1] = save[1];
609: }
610:
611: first = 0;
612:
613: /*
614: * Further tests may need both of these values, so
615: * always copy both of them.
616: */
617: val = sb.f_flags;
618: strncpy(fstype, sb.f_fstypename, MFSNAMELEN);
619: }
620: switch (plan->flags) {
621: case F_MTFLAG:
622: return (val & plan->mt_data);
623: case F_MTTYPE:
624: return (strncmp(fstype, plan->c_data, MFSNAMELEN) == 0);
625: default:
626: abort();
627: }
628: }
629:
630: PLAN *
631: c_fstype(arg)
632: char *arg;
633: {
634: register PLAN *new;
635:
636: ftsoptions &= ~FTS_NOSTAT;
637:
638: new = palloc(N_FSTYPE, f_fstype);
639: switch (*arg) {
640: case 'l':
641: if (!strcmp(arg, "local")) {
642: new->flags = F_MTFLAG;
643: new->mt_data = MNT_LOCAL;
644: return (new);
645: }
646: break;
647: case 'r':
648: if (!strcmp(arg, "rdonly")) {
649: new->flags = F_MTFLAG;
650: new->mt_data = MNT_RDONLY;
651: return (new);
652: }
653: break;
654: }
655:
656: new->flags = F_MTTYPE;
657: new->c_data = arg;
658: return (new);
659: }
660:
661: /*
662: * -group gname functions --
663: *
664: * True if the file belongs to the group gname. If gname is numeric and
665: * an equivalent of the getgrnam() function does not return a valid group
666: * name, gname is taken as a group ID.
667: */
668: int
669: f_group(plan, entry)
670: PLAN *plan;
671: FTSENT *entry;
672: {
673: return (entry->fts_statp->st_gid == plan->g_data);
674: }
675:
676: PLAN *
677: c_group(gname)
678: char *gname;
679: {
680: PLAN *new;
681: struct group *g;
682: gid_t gid;
683:
684: ftsoptions &= ~FTS_NOSTAT;
685:
686: g = getgrnam(gname);
687: if (g == NULL) {
688: gid = atoi(gname);
689: if (gid == 0 && gname[0] != '0')
690: errx(1, "-group: %s: no such group", gname);
691: } else
692: gid = g->gr_gid;
693:
694: new = palloc(N_GROUP, f_group);
695: new->g_data = gid;
696: return (new);
697: }
698:
699: /*
700: * -inum n functions --
701: *
702: * True if the file has inode # n.
703: */
704: int
705: f_inum(plan, entry)
706: PLAN *plan;
707: FTSENT *entry;
708: {
709: COMPARE(entry->fts_statp->st_ino, plan->i_data);
710: }
711:
712: PLAN *
713: c_inum(arg)
714: char *arg;
715: {
716: PLAN *new;
717:
718: ftsoptions &= ~FTS_NOSTAT;
719:
720: new = palloc(N_INUM, f_inum);
721: new->i_data = find_parsenum(new, "-inum", arg, NULL);
722: return (new);
723: }
724:
725: /*
726: * -links n functions --
727: *
728: * True if the file has n links.
729: */
730: int
731: f_links(plan, entry)
732: PLAN *plan;
733: FTSENT *entry;
734: {
735: COMPARE(entry->fts_statp->st_nlink, plan->l_data);
736: }
737:
738: PLAN *
739: c_links(arg)
740: char *arg;
741: {
742: PLAN *new;
743:
744: ftsoptions &= ~FTS_NOSTAT;
745:
746: new = palloc(N_LINKS, f_links);
747: new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
748: return (new);
749: }
750:
751: /*
752: * -ls functions --
753: *
754: * Always true - prints the current entry to stdout in "ls" format.
755: */
756: int
757: f_ls(plan, entry)
758: PLAN *plan;
759: FTSENT *entry;
760: {
761: printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
762: return (1);
763: }
764:
765: PLAN *
766: c_ls()
767: {
768: ftsoptions &= ~FTS_NOSTAT;
769: isoutput = 1;
770:
771: return (palloc(N_LS, f_ls));
1.5 tholo 772: }
773:
774: /*
775: * - maxdepth n functions --
776: *
777: * True if the current search depth is less than or equal to the
778: * maximum depth specified
779: */
780: int
1.6 tholo 781: f_maxdepth(plan, entry)
1.5 tholo 782: PLAN *plan;
783: FTSENT *entry;
784: {
785:
1.6 tholo 786: if (entry->fts_level >= plan->max_data)
1.5 tholo 787: fts_set(tree, entry, FTS_SKIP);
1.6 tholo 788: return (entry->fts_level <= plan->max_data);
1.5 tholo 789: }
790:
791: PLAN *
1.6 tholo 792: c_maxdepth(arg)
1.5 tholo 793: char *arg;
794: {
795: PLAN *new;
796:
1.6 tholo 797: new = palloc(N_MAXDEPTH, f_maxdepth);
798: new->max_data = atoi(arg);
799: return (new);
800: }
801:
802: /*
803: * - mindepth n functions --
804: *
805: * True if the current search depth is greater than or equal to the
806: * minimum depth specified
807: */
808: int
809: f_mindepth(plan, entry)
810: PLAN *plan;
811: FTSENT *entry;
812: {
813:
814: return (entry->fts_level >= plan->min_data);
815: }
816:
817: PLAN *
818: c_mindepth(arg)
819: char *arg;
820: {
821: PLAN *new;
822:
823: new = palloc(N_MINDEPTH, f_mindepth);
824: new->min_data = atoi(arg);
1.5 tholo 825: return (new);
1.1 deraadt 826: }
827:
828: /*
829: * -mtime n functions --
830: *
831: * True if the difference between the file modification time and the
832: * current time is n 24 hour periods.
833: */
834: int
835: f_mtime(plan, entry)
836: PLAN *plan;
837: FTSENT *entry;
838: {
839:
840: COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
1.17 millert 841: SECSPERDAY, plan->sec_data);
1.1 deraadt 842: }
843:
844: PLAN *
845: c_mtime(arg)
846: char *arg;
847: {
848: PLAN *new;
849:
850: ftsoptions &= ~FTS_NOSTAT;
851:
852: new = palloc(N_MTIME, f_mtime);
1.17 millert 853: new->sec_data = find_parsenum(new, "-mtime", arg, NULL);
1.1 deraadt 854: TIME_CORRECT(new, N_MTIME);
1.11 deraadt 855: return (new);
856: }
857:
858: /*
859: * -mmin n functions --
860: *
861: * True if the difference between the file modification time and the
862: * current time is n min periods.
863: */
864: int
865: f_mmin(plan, entry)
866: PLAN *plan;
867: FTSENT *entry;
868: {
869: extern time_t now;
870:
871: COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) /
1.17 millert 872: 60, plan->sec_data);
1.11 deraadt 873: }
874:
875: PLAN *
876: c_mmin(arg)
877: char *arg;
878: {
879: PLAN *new;
880:
881: ftsoptions &= ~FTS_NOSTAT;
882:
883: new = palloc(N_MMIN, f_mmin);
1.17 millert 884: new->sec_data = find_parsenum(new, "-mmin", arg, NULL);
1.11 deraadt 885: TIME_CORRECT(new, N_MMIN);
1.1 deraadt 886: return (new);
887: }
888:
889: /*
890: * -name functions --
891: *
892: * True if the basename of the filename being examined
893: * matches pattern using Pattern Matching Notation S3.14
894: */
895: int
896: f_name(plan, entry)
897: PLAN *plan;
898: FTSENT *entry;
899: {
900: return (!fnmatch(plan->c_data, entry->fts_name, 0));
901: }
902:
903: PLAN *
904: c_name(pattern)
905: char *pattern;
906: {
907: PLAN *new;
908:
909: new = palloc(N_NAME, f_name);
1.18 ! deraadt 910: new->c_data = pattern;
! 911: return (new);
! 912: }
! 913:
! 914: /*
! 915: * -iname functions --
! 916: *
! 917: * Similar to -name, but does case insensitive matching
! 918: *
! 919: */
! 920: int
! 921: f_iname(plan, entry)
! 922: PLAN *plan;
! 923: FTSENT *entry;
! 924: {
! 925: return (!fnmatch(plan->c_data, entry->fts_name, FNM_CASEFOLD));
! 926: }
! 927:
! 928: PLAN *
! 929: c_iname(pattern)
! 930: char *pattern;
! 931: {
! 932: PLAN *new;
! 933:
! 934: new = palloc(N_INAME, f_iname);
1.1 deraadt 935: new->c_data = pattern;
936: return (new);
937: }
938:
939: /*
940: * -newer file functions --
941: *
942: * True if the current file has been modified more recently
943: * then the modification time of the file named by the pathname
944: * file.
945: */
946: int
947: f_newer(plan, entry)
948: PLAN *plan;
949: FTSENT *entry;
950: {
1.17 millert 951:
952: return (entry->fts_statp->st_mtimespec.tv_sec > plan->t_data.tv_sec ||
953: (entry->fts_statp->st_mtimespec.tv_sec == plan->t_data.tv_sec &&
954: entry->fts_statp->st_mtimespec.tv_nsec > plan->t_data.tv_nsec));
1.1 deraadt 955: }
956:
957: PLAN *
958: c_newer(filename)
959: char *filename;
960: {
961: PLAN *new;
962: struct stat sb;
963:
964: ftsoptions &= ~FTS_NOSTAT;
965:
966: if (stat(filename, &sb))
967: err(1, "%s", filename);
968: new = palloc(N_NEWER, f_newer);
1.17 millert 969: memcpy(&new->t_data, &sb.st_mtimespec, sizeof(struct timespec));
970: return (new);
971: }
972:
973: /*
974: * -anewer file functions --
975: *
976: * True if the current file has been accessed more recently
977: * then the access time of the file named by the pathname
978: * file.
979: */
980: int
981: f_anewer(plan, entry)
982: PLAN *plan;
983: FTSENT *entry;
984: {
985:
986: return (entry->fts_statp->st_atimespec.tv_sec > plan->t_data.tv_sec ||
987: (entry->fts_statp->st_atimespec.tv_sec == plan->t_data.tv_sec &&
988: entry->fts_statp->st_atimespec.tv_nsec > plan->t_data.tv_nsec));
989: }
990:
991: PLAN *
992: c_anewer(filename)
993: char *filename;
994: {
995: PLAN *new;
996: struct stat sb;
997:
998: ftsoptions &= ~FTS_NOSTAT;
999:
1000: if (stat(filename, &sb))
1001: err(1, "%s", filename);
1002: new = palloc(N_NEWER, f_newer);
1003: memcpy(&new->t_data, &sb.st_atimespec, sizeof(struct timespec));
1004: return (new);
1005: }
1006:
1007: /*
1008: * -cnewer file functions --
1009: *
1010: * True if the current file has been changed more recently
1011: * then the inode change time of the file named by the pathname
1012: * file.
1013: */
1014: int
1015: f_cnewer(plan, entry)
1016: PLAN *plan;
1017: FTSENT *entry;
1018: {
1019:
1020: return (entry->fts_statp->st_ctimespec.tv_sec > plan->t_data.tv_sec ||
1021: (entry->fts_statp->st_ctimespec.tv_sec == plan->t_data.tv_sec &&
1022: entry->fts_statp->st_ctimespec.tv_nsec > plan->t_data.tv_nsec));
1023: }
1024:
1025: PLAN *
1026: c_cnewer(filename)
1027: char *filename;
1028: {
1029: PLAN *new;
1030: struct stat sb;
1031:
1032: ftsoptions &= ~FTS_NOSTAT;
1033:
1034: if (stat(filename, &sb))
1035: err(1, "%s", filename);
1036: new = palloc(N_NEWER, f_newer);
1037: memcpy(&new->t_data, &sb.st_ctimespec, sizeof(struct timespec));
1.1 deraadt 1038: return (new);
1039: }
1040:
1041: /*
1042: * -nogroup functions --
1043: *
1044: * True if file belongs to a user ID for which the equivalent
1045: * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
1046: */
1047: int
1048: f_nogroup(plan, entry)
1049: PLAN *plan;
1050: FTSENT *entry;
1051: {
1052: char *group_from_gid();
1053:
1054: return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
1055: }
1056:
1057: PLAN *
1058: c_nogroup()
1059: {
1060: ftsoptions &= ~FTS_NOSTAT;
1061:
1062: return (palloc(N_NOGROUP, f_nogroup));
1063: }
1064:
1065: /*
1066: * -nouser functions --
1067: *
1068: * True if file belongs to a user ID for which the equivalent
1069: * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
1070: */
1071: int
1072: f_nouser(plan, entry)
1073: PLAN *plan;
1074: FTSENT *entry;
1075: {
1076: char *user_from_uid();
1077:
1078: return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
1079: }
1080:
1081: PLAN *
1082: c_nouser()
1083: {
1084: ftsoptions &= ~FTS_NOSTAT;
1085:
1086: return (palloc(N_NOUSER, f_nouser));
1087: }
1088:
1089: /*
1090: * -path functions --
1091: *
1092: * True if the path of the filename being examined
1093: * matches pattern using Pattern Matching Notation S3.14
1094: */
1095: int
1096: f_path(plan, entry)
1097: PLAN *plan;
1098: FTSENT *entry;
1099: {
1100: return (!fnmatch(plan->c_data, entry->fts_path, 0));
1101: }
1102:
1103: PLAN *
1104: c_path(pattern)
1105: char *pattern;
1106: {
1107: PLAN *new;
1108:
1109: new = palloc(N_NAME, f_path);
1110: new->c_data = pattern;
1111: return (new);
1112: }
1113:
1114: /*
1115: * -perm functions --
1116: *
1117: * The mode argument is used to represent file mode bits. If it starts
1118: * with a leading digit, it's treated as an octal mode, otherwise as a
1119: * symbolic mode.
1120: */
1121: int
1122: f_perm(plan, entry)
1123: PLAN *plan;
1124: FTSENT *entry;
1125: {
1126: mode_t mode;
1127:
1128: mode = entry->fts_statp->st_mode &
1129: (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
1130: if (plan->flags == F_ATLEAST)
1131: return ((plan->m_data | mode) == mode);
1132: else
1133: return (mode == plan->m_data);
1134: /* NOTREACHED */
1135: }
1136:
1137: PLAN *
1138: c_perm(perm)
1139: char *perm;
1140: {
1141: PLAN *new;
1142: mode_t *set;
1143:
1144: ftsoptions &= ~FTS_NOSTAT;
1145:
1146: new = palloc(N_PERM, f_perm);
1147:
1148: if (*perm == '-') {
1149: new->flags = F_ATLEAST;
1150: ++perm;
1151: }
1152:
1153: if ((set = setmode(perm)) == NULL)
1154: err(1, "-perm: %s: illegal mode string", perm);
1155:
1156: new->m_data = getmode(set, 0);
1.12 deraadt 1157: free(set);
1.1 deraadt 1158: return (new);
1159: }
1160:
1161: /*
1162: * -print functions --
1163: *
1164: * Always true, causes the current pathame to be written to
1165: * standard output.
1166: */
1167: int
1168: f_print(plan, entry)
1169: PLAN *plan;
1170: FTSENT *entry;
1171: {
1172: (void)printf("%s\n", entry->fts_path);
1173: return(1);
1174: }
1175:
1176: /* ARGSUSED */
1.9 millert 1177: int
1.1 deraadt 1178: f_print0(plan, entry)
1179: PLAN *plan;
1180: FTSENT *entry;
1181: {
1182: (void)fputs(entry->fts_path, stdout);
1183: (void)fputc('\0', stdout);
1184: return(1);
1185: }
1186:
1187: PLAN *
1188: c_print()
1189: {
1190: isoutput = 1;
1191:
1192: return(palloc(N_PRINT, f_print));
1193: }
1194:
1195: PLAN *
1196: c_print0()
1197: {
1198: isoutput = 1;
1199:
1200: return(palloc(N_PRINT0, f_print0));
1201: }
1202:
1203: /*
1204: * -prune functions --
1205: *
1206: * Prune a portion of the hierarchy.
1207: */
1208: int
1209: f_prune(plan, entry)
1210: PLAN *plan;
1211: FTSENT *entry;
1212: {
1213:
1214: if (fts_set(tree, entry, FTS_SKIP))
1215: err(1, "%s", entry->fts_path);
1216: return (1);
1217: }
1218:
1219: PLAN *
1220: c_prune()
1221: {
1222: return (palloc(N_PRUNE, f_prune));
1223: }
1224:
1225: /*
1226: * -size n[c] functions --
1227: *
1228: * True if the file size in bytes, divided by an implementation defined
1229: * value and rounded up to the next integer, is n. If n is followed by
1230: * a c, the size is in bytes.
1231: */
1232: #define FIND_SIZE 512
1233: static int divsize = 1;
1234:
1235: int
1236: f_size(plan, entry)
1237: PLAN *plan;
1238: FTSENT *entry;
1239: {
1240: off_t size;
1241:
1242: size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
1243: FIND_SIZE : entry->fts_statp->st_size;
1244: COMPARE(size, plan->o_data);
1245: }
1246:
1247: PLAN *
1248: c_size(arg)
1249: char *arg;
1250: {
1251: PLAN *new;
1252: char endch;
1253:
1254: ftsoptions &= ~FTS_NOSTAT;
1255:
1256: new = palloc(N_SIZE, f_size);
1257: endch = 'c';
1258: new->o_data = find_parsenum(new, "-size", arg, &endch);
1259: if (endch == 'c')
1260: divsize = 0;
1261: return (new);
1262: }
1263:
1264: /*
1265: * -type c functions --
1266: *
1267: * True if the type of the file is c, where c is b, c, d, p, or f for
1268: * block special file, character special file, directory, FIFO, or
1269: * regular file, respectively.
1270: */
1271: int
1272: f_type(plan, entry)
1273: PLAN *plan;
1274: FTSENT *entry;
1275: {
1276: return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
1277: }
1278:
1279: PLAN *
1280: c_type(typestring)
1281: char *typestring;
1282: {
1283: PLAN *new;
1284: mode_t mask;
1285:
1286: ftsoptions &= ~FTS_NOSTAT;
1287:
1288: switch (typestring[0]) {
1.2 deraadt 1289: #ifdef S_IFWHT
1.3 deraadt 1290: case 'W':
1291: mask = S_IFWHT;
1.13 espie 1292: if ((ftsoptions & FTS_WHITEOUT) == 0)
1293: warnx("-type W without -W is a no-op");
1.3 deraadt 1294: break;
1.2 deraadt 1295: #endif
1.1 deraadt 1296: case 'b':
1297: mask = S_IFBLK;
1298: break;
1299: case 'c':
1300: mask = S_IFCHR;
1301: break;
1302: case 'd':
1303: mask = S_IFDIR;
1304: break;
1305: case 'f':
1306: mask = S_IFREG;
1307: break;
1308: case 'l':
1309: mask = S_IFLNK;
1310: break;
1311: case 'p':
1312: mask = S_IFIFO;
1313: break;
1314: case 's':
1315: mask = S_IFSOCK;
1316: break;
1317: default:
1318: errx(1, "-type: %s: unknown type", typestring);
1319: }
1320:
1321: new = palloc(N_TYPE, f_type);
1322: new->m_data = mask;
1323: return (new);
1324: }
1325:
1326: /*
1327: * -user uname functions --
1328: *
1329: * True if the file belongs to the user uname. If uname is numeric and
1330: * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1331: * return a valid user name, uname is taken as a user ID.
1332: */
1333: int
1334: f_user(plan, entry)
1335: PLAN *plan;
1336: FTSENT *entry;
1337: {
1338: return (entry->fts_statp->st_uid == plan->u_data);
1339: }
1340:
1341: PLAN *
1342: c_user(username)
1343: char *username;
1344: {
1345: PLAN *new;
1346: struct passwd *p;
1347: uid_t uid;
1348:
1349: ftsoptions &= ~FTS_NOSTAT;
1350:
1351: p = getpwnam(username);
1352: if (p == NULL) {
1353: uid = atoi(username);
1354: if (uid == 0 && username[0] != '0')
1355: errx(1, "-user: %s: no such user", username);
1356: } else
1357: uid = p->pw_uid;
1358:
1359: new = palloc(N_USER, f_user);
1360: new->u_data = uid;
1361: return (new);
1362: }
1363:
1364: /*
1365: * -xdev functions --
1366: *
1367: * Always true, causes find not to decend past directories that have a
1368: * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1369: */
1370: PLAN *
1371: c_xdev()
1372: {
1373: ftsoptions |= FTS_XDEV;
1374:
1375: return (palloc(N_XDEV, f_always_true));
1376: }
1377:
1378: /*
1379: * ( expression ) functions --
1380: *
1381: * True if expression is true.
1382: */
1383: int
1384: f_expr(plan, entry)
1385: PLAN *plan;
1386: FTSENT *entry;
1387: {
1388: register PLAN *p;
1389: register int state;
1390:
1391: for (p = plan->p_data[0];
1392: p && (state = (p->eval)(p, entry)); p = p->next);
1393: return (state);
1394: }
1395:
1396: /*
1397: * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
1398: * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1399: * to a N_EXPR node containing the expression and the ')' node is discarded.
1400: */
1401: PLAN *
1402: c_openparen()
1403: {
1404: return (palloc(N_OPENPAREN, (int (*)())-1));
1405: }
1406:
1407: PLAN *
1408: c_closeparen()
1409: {
1410: return (palloc(N_CLOSEPAREN, (int (*)())-1));
1411: }
1412:
1413: /*
1414: * ! expression functions --
1415: *
1416: * Negation of a primary; the unary NOT operator.
1417: */
1418: int
1419: f_not(plan, entry)
1420: PLAN *plan;
1421: FTSENT *entry;
1422: {
1423: register PLAN *p;
1424: register int state;
1425:
1426: for (p = plan->p_data[0];
1427: p && (state = (p->eval)(p, entry)); p = p->next);
1428: return (!state);
1429: }
1430:
1431: PLAN *
1432: c_not()
1433: {
1434: return (palloc(N_NOT, f_not));
1435: }
1436:
1437: /*
1438: * expression -o expression functions --
1439: *
1440: * Alternation of primaries; the OR operator. The second expression is
1441: * not evaluated if the first expression is true.
1442: */
1443: int
1444: f_or(plan, entry)
1445: PLAN *plan;
1446: FTSENT *entry;
1447: {
1448: register PLAN *p;
1449: register int state;
1450:
1451: for (p = plan->p_data[0];
1452: p && (state = (p->eval)(p, entry)); p = p->next);
1453:
1454: if (state)
1455: return (1);
1456:
1457: for (p = plan->p_data[1];
1458: p && (state = (p->eval)(p, entry)); p = p->next);
1459: return (state);
1460: }
1461:
1462: PLAN *
1463: c_or()
1464: {
1465: return (palloc(N_OR, f_or));
1466: }
1467:
1468: static PLAN *
1469: palloc(t, f)
1470: enum ntype t;
1471: int (*f) __P((PLAN *, FTSENT *));
1472: {
1473: PLAN *new;
1474:
1.17 millert 1475: if ((new = calloc(1, sizeof(PLAN)))) {
1.1 deraadt 1476: new->type = t;
1477: new->eval = f;
1478: return (new);
1479: }
1480: err(1, NULL);
1481: /* NOTREACHED */
1482: }