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