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