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