This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / usr.bin / find / function.c
CommitLineData
15637ed4 1/*-
6b479535
NW
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
6b479535 38static char sccsid[] = "@(#)function.c 8.1 (Berkeley) 6/6/93";
15637ed4
RG
39#endif /* not lint */
40
41#include <sys/param.h>
4e97a0eb 42#include <sys/ucred.h>
15637ed4
RG
43#include <sys/stat.h>
44#include <sys/wait.h>
45#include <sys/mount.h>
6b479535
NW
46
47#include <err.h>
15637ed4 48#include <errno.h>
6b479535
NW
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>
6b479535
NW
56#include <tzfile.h>
57#include <unistd.h>
58
15637ed4
RG
59#include "find.h"
60
6b479535
NW
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
6b479535 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 */
6b479535
NW
80static long
81find_parsenum(plan, option, vp, endch)
15637ed4 82 PLAN *plan;
6b479535 83 char *option, *vp, *endch;
15637ed4
RG
84{
85 long value;
6b479535 86 char *endchar, *str; /* Pointer to character ending conversion. */
15637ed4 87
6b479535
NW
88 /* Determine comparison from leading + or -. */
89 str = vp;
90 switch (*str) {
15637ed4
RG
91 case '+':
92 ++str;
4e97a0eb 93 plan->flags = F_GREATER;
15637ed4
RG
94 break;
95 case '-':
96 ++str;
4e97a0eb 97 plan->flags = F_LESSTHAN;
15637ed4
RG
98 break;
99 default:
4e97a0eb 100 plan->flags = F_EQUAL;
15637ed4
RG
101 break;
102 }
103
104 /*
6b479535 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);
6b479535
NW
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];
6b479535 116 return (value);
15637ed4
RG
117}
118
6b479535
NW
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 */
6b479535 135int
15637ed4
RG
136f_atime(plan, entry)
137 PLAN *plan;
138 FTSENT *entry;
139{
140 extern time_t now;
141
4e97a0eb 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);
6b479535
NW
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 */
6b479535 165int
15637ed4
RG
166f_ctime(plan, entry)
167 PLAN *plan;
168 FTSENT *entry;
169{
170 extern time_t now;
171
4e97a0eb 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);
6b479535
NW
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 */
6b479535 197int
15637ed4
RG
198f_always_true(plan, entry)
199 PLAN *plan;
200 FTSENT *entry;
201{
6b479535 202 return (1);
15637ed4
RG
203}
204
205PLAN *
206c_depth()
207{
208 isdepth = 1;
209
6b479535 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 */
6b479535 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
4e97a0eb 240 if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv))
6b479535 241 return (0);
15637ed4 242
6b479535 243 switch (pid = vfork()) {
15637ed4 244 case -1:
6b479535 245 err(1, "fork");
15637ed4
RG
246 /* NOTREACHED */
247 case 0:
248 if (fchdir(dotfd)) {
6b479535 249 warn("chdir");
15637ed4
RG
250 _exit(1);
251 }
252 execvp(plan->e_argv[0], plan->e_argv);
6b479535 253 warn("%s", plan->e_argv[0]);
15637ed4
RG
254 _exit(1);
255 }
256 pid = waitpid(pid, &status, 0);
6b479535 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);
4e97a0eb
NW
279 if (isok)
280 new->flags = F_NEEDOK;
15637ed4
RG
281
282 for (ap = argv = *argvp;; ++ap) {
283 if (!*ap)
6b479535
NW
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;
6b479535 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
6b479535 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 */
6b479535 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;
4e97a0eb
NW
341 struct statfs sb;
342 static short val;
15637ed4
RG
343 char *p, save[2];
344
4e97a0eb
NW
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) {
6b479535 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))
6b479535 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;
6b479535 376 switch (plan->flags) {
4e97a0eb
NW
377 case F_MTFLAG:
378 val = sb.f_flags;
379 break;
380 case F_MTTYPE:
381 val = sb.f_type;
382 break;
6b479535
NW
383 default:
384 abort();
4e97a0eb
NW
385 }
386 }
387 switch(plan->flags) {
388 case F_MTFLAG:
6b479535 389 return (val & plan->mt_data);
4e97a0eb 390 case F_MTTYPE:
6b479535
NW
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);
6b479535 406 switch (*arg) {
dc00eac9
RG
407 case 'i':
408 if (!strcmp(arg, "isofs")) {
409 new->flags = F_MTTYPE;
410 new->mt_data = MOUNT_ISOFS;
411 return (new);
412 }
413 break;
15637ed4
RG
414 case 'l':
415 if (!strcmp(arg, "local")) {
4e97a0eb
NW
416 new->flags = F_MTFLAG;
417 new->mt_data = MNT_LOCAL;
6b479535 418 return (new);
15637ed4
RG
419 }
420 break;
421 case 'm':
422 if (!strcmp(arg, "mfs")) {
4e97a0eb
NW
423 new->flags = F_MTTYPE;
424 new->mt_data = MOUNT_MFS;
6b479535 425 return (new);
15637ed4
RG
426 }
427 break;
428 case 'n':
429 if (!strcmp(arg, "nfs")) {
4e97a0eb
NW
430 new->flags = F_MTTYPE;
431 new->mt_data = MOUNT_NFS;
6b479535 432 return (new);
4e97a0eb
NW
433 }
434 break;
6b479535 435#ifdef MOUNT_PC
4e97a0eb
NW
436 case 'p':
437 if (!strcmp(arg, "pc")) {
438 new->flags = F_MTTYPE;
439 new->mt_data = MOUNT_PC;
6b479535 440 return (new);
15637ed4
RG
441 }
442 break;
4e97a0eb 443#endif
4ca3ede5
C
444 case 'r':
445 if (!strcmp(arg, "rdonly")) {
4e97a0eb
NW
446 new->flags = F_MTFLAG;
447 new->mt_data = MNT_RDONLY;
6b479535 448 return (new);
4ca3ede5
C
449 }
450 break;
15637ed4
RG
451 case 'u':
452 if (!strcmp(arg, "ufs")) {
4e97a0eb
NW
453 new->flags = F_MTTYPE;
454 new->mt_data = MOUNT_UFS;
6b479535 455 return (new);
15637ed4
RG
456 }
457 break;
458 }
6b479535 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 */
6b479535 470int
15637ed4
RG
471f_group(plan, entry)
472 PLAN *plan;
473 FTSENT *entry;
474{
6b479535 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')
6b479535 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;
6b479535 498 return (new);
15637ed4
RG
499}
500
501/*
502 * -inum n functions --
503 *
504 * True if the file has inode # n.
505 */
6b479535 506int
15637ed4
RG
507f_inum(plan, entry)
508 PLAN *plan;
509 FTSENT *entry;
510{
4e97a0eb 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);
6b479535
NW
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 */
6b479535 532int
15637ed4
RG
533f_links(plan, entry)
534 PLAN *plan;
535 FTSENT *entry;
536{
4e97a0eb 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);
6b479535
NW
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 */
6b479535 558int
15637ed4
RG
559f_ls(plan, entry)
560 PLAN *plan;
561 FTSENT *entry;
562{
4e97a0eb 563 printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
6b479535 564 return (1);
15637ed4
RG
565}
566
567PLAN *
568c_ls()
569{
570 ftsoptions &= ~FTS_NOSTAT;
571 isoutput = 1;
572
6b479535 573 return (palloc(N_LS, f_ls));
15637ed4
RG
574}
575
4e97a0eb
NW
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 */
6b479535 582int
4e97a0eb
NW
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);
6b479535
NW
602 new->t_data = find_parsenum(new, "-mtime", arg, NULL);
603 TIME_CORRECT(new, N_MTIME);
604 return (new);
4e97a0eb
NW
605}
606
15637ed4
RG
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 */
6b479535 613int
15637ed4
RG
614f_name(plan, entry)
615 PLAN *plan;
616 FTSENT *entry;
617{
6b479535 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;
6b479535 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 */
6b479535 639int
15637ed4
RG
640f_newer(plan, entry)
641 PLAN *plan;
642 FTSENT *entry;
643{
6b479535 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))
6b479535 657 err(1, "%s", filename);
15637ed4
RG
658 new = palloc(N_NEWER, f_newer);
659 new->t_data = sb.st_mtime;
6b479535 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 */
6b479535 669int
15637ed4
RG
670f_nogroup(plan, entry)
671 PLAN *plan;
672 FTSENT *entry;
673{
674 char *group_from_gid();
675
27f8b345 676 return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
15637ed4
RG
677}
678
679PLAN *
680c_nogroup()
681{
682 ftsoptions &= ~FTS_NOSTAT;
683
6b479535 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 */
6b479535 693int
15637ed4
RG
694f_nouser(plan, entry)
695 PLAN *plan;
696 FTSENT *entry;
697{
698 char *user_from_uid();
699
27f8b345 700 return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
15637ed4
RG
701}
702
703PLAN *
704c_nouser()
705{
706 ftsoptions &= ~FTS_NOSTAT;
707
6b479535 708 return (palloc(N_NOUSER, f_nouser));
15637ed4
RG
709}
710
4e97a0eb
NW
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 */
6b479535 717int
4e97a0eb
NW
718f_path(plan, entry)
719 PLAN *plan;
720 FTSENT *entry;
721{
6b479535 722 return (!fnmatch(plan->c_data, entry->fts_path, 0));
4e97a0eb
NW
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;
6b479535 733 return (new);
4e97a0eb
NW
734}
735
15637ed4
RG
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 */
6b479535 743int
15637ed4
RG
744f_perm(plan, entry)
745 PLAN *plan;
746 FTSENT *entry;
747{
748 mode_t mode;
749
4e97a0eb 750 mode = entry->fts_statp->st_mode &
15637ed4 751 (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
4e97a0eb 752 if (plan->flags == F_ATLEAST)
6b479535 753 return ((plan->m_data | mode) == mode);
15637ed4 754 else
6b479535 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 == '-') {
4e97a0eb 771 new->flags = F_ATLEAST;
15637ed4
RG
772 ++perm;
773 }
774
775 if ((set = setmode(perm)) == NULL)
6b479535 776 err(1, "-perm: %s: illegal mode string", perm);
15637ed4
RG
777
778 new->m_data = getmode(set, 0);
6b479535 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 */
6b479535 788int
15637ed4
RG
789f_print(plan, entry)
790 PLAN *plan;
791 FTSENT *entry;
792{
a558e4ae
GW
793 if (plan->flags & F_PRINTF) {
794 printf(plan->c_data, entry->fts_path);
795 } else {
796 fputs(entry->fts_path, stdout);
797 }
798
799 if (plan->flags & F_PRINT0) {
800 fputc('\0', stdout);
801 } else {
802 fputc('\n', stdout);
803 }
6b479535 804 return (1);
15637ed4
RG
805}
806
807PLAN *
808c_print()
809{
810 isoutput = 1;
811
6b479535 812 return (palloc(N_PRINT, f_print));
15637ed4 813}
a558e4ae
GW
814
815PLAN *
816c_print0()
817{
818 PLAN *rv = palloc(N_PRINT, f_print);
819 rv->flags = F_PRINT0;
820 isoutput = 1;
821 return rv;
822}
823
824PLAN *
825c_printf(arg)
826 char *arg;
827{
828 PLAN *rv = palloc(N_PRINT, f_print);
829 rv->flags = F_PRINTF;
830 rv->c_data = arg;
831 isoutput = 1;
832 return rv;
833}
834
15637ed4
RG
835
836/*
837 * -prune functions --
838 *
839 * Prune a portion of the hierarchy.
840 */
6b479535 841int
15637ed4
RG
842f_prune(plan, entry)
843 PLAN *plan;
844 FTSENT *entry;
845{
846 extern FTS *tree;
847
848 if (fts_set(tree, entry, FTS_SKIP))
6b479535
NW
849 err(1, "%s", entry->fts_path);
850 return (1);
15637ed4
RG
851}
852
853PLAN *
854c_prune()
855{
6b479535 856 return (palloc(N_PRUNE, f_prune));
15637ed4
RG
857}
858
859/*
860 * -size n[c] functions --
861 *
862 * True if the file size in bytes, divided by an implementation defined
863 * value and rounded up to the next integer, is n. If n is followed by
864 * a c, the size is in bytes.
865 */
866#define FIND_SIZE 512
867static int divsize = 1;
868
6b479535 869int
15637ed4
RG
870f_size(plan, entry)
871 PLAN *plan;
872 FTSENT *entry;
873{
874 off_t size;
875
4e97a0eb
NW
876 size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
877 FIND_SIZE : entry->fts_statp->st_size;
15637ed4
RG
878 COMPARE(size, plan->o_data);
879}
880
881PLAN *
882c_size(arg)
883 char *arg;
884{
885 PLAN *new;
886 char endch;
887
888 ftsoptions &= ~FTS_NOSTAT;
889
890 new = palloc(N_SIZE, f_size);
6b479535 891 endch = 'c';
15637ed4
RG
892 new->o_data = find_parsenum(new, "-size", arg, &endch);
893 if (endch == 'c')
894 divsize = 0;
6b479535 895 return (new);
15637ed4
RG
896}
897
898/*
899 * -type c functions --
900 *
901 * True if the type of the file is c, where c is b, c, d, p, or f for
902 * block special file, character special file, directory, FIFO, or
903 * regular file, respectively.
904 */
6b479535 905int
15637ed4
RG
906f_type(plan, entry)
907 PLAN *plan;
908 FTSENT *entry;
909{
6b479535 910 return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
15637ed4
RG
911}
912
913PLAN *
914c_type(typestring)
915 char *typestring;
916{
917 PLAN *new;
918 mode_t mask;
919
920 ftsoptions &= ~FTS_NOSTAT;
921
922 switch (typestring[0]) {
923 case 'b':
924 mask = S_IFBLK;
925 break;
926 case 'c':
927 mask = S_IFCHR;
928 break;
929 case 'd':
930 mask = S_IFDIR;
931 break;
932 case 'f':
933 mask = S_IFREG;
934 break;
935 case 'l':
936 mask = S_IFLNK;
937 break;
938 case 'p':
939 mask = S_IFIFO;
940 break;
941 case 's':
942 mask = S_IFSOCK;
943 break;
944 default:
6b479535 945 errx(1, "-type: %s: unknown type", typestring);
15637ed4
RG
946 }
947
948 new = palloc(N_TYPE, f_type);
949 new->m_data = mask;
6b479535 950 return (new);
15637ed4
RG
951}
952
953/*
954 * -user uname functions --
955 *
956 * True if the file belongs to the user uname. If uname is numeric and
957 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
958 * return a valid user name, uname is taken as a user ID.
959 */
6b479535 960int
15637ed4
RG
961f_user(plan, entry)
962 PLAN *plan;
963 FTSENT *entry;
964{
6b479535 965 return (entry->fts_statp->st_uid == plan->u_data);
15637ed4
RG
966}
967
968PLAN *
969c_user(username)
970 char *username;
971{
972 PLAN *new;
973 struct passwd *p;
974 uid_t uid;
975
976 ftsoptions &= ~FTS_NOSTAT;
977
978 p = getpwnam(username);
979 if (p == NULL) {
980 uid = atoi(username);
981 if (uid == 0 && username[0] != '0')
6b479535 982 errx(1, "-user: %s: no such user", username);
15637ed4
RG
983 } else
984 uid = p->pw_uid;
985
986 new = palloc(N_USER, f_user);
987 new->u_data = uid;
6b479535 988 return (new);
15637ed4
RG
989}
990
991/*
992 * -xdev functions --
993 *
994 * Always true, causes find not to decend past directories that have a
995 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
996 */
997PLAN *
998c_xdev()
999{
1000 ftsoptions |= FTS_XDEV;
1001
6b479535 1002 return (palloc(N_XDEV, f_always_true));
15637ed4
RG
1003}
1004
1005/*
1006 * ( expression ) functions --
1007 *
1008 * True if expression is true.
1009 */
6b479535 1010int
15637ed4
RG
1011f_expr(plan, entry)
1012 PLAN *plan;
1013 FTSENT *entry;
1014{
1015 register PLAN *p;
1016 register int state;
1017
1018 for (p = plan->p_data[0];
1019 p && (state = (p->eval)(p, entry)); p = p->next);
6b479535 1020 return (state);
15637ed4
RG
1021}
1022
1023/*
1024 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
1025 * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1026 * to a N_EXPR node containing the expression and the ')' node is discarded.
1027 */
1028PLAN *
1029c_openparen()
1030{
6b479535 1031 return (palloc(N_OPENPAREN, (int (*)())-1));
15637ed4
RG
1032}
1033
1034PLAN *
1035c_closeparen()
1036{
6b479535 1037 return (palloc(N_CLOSEPAREN, (int (*)())-1));
15637ed4
RG
1038}
1039
15637ed4
RG
1040/*
1041 * ! expression functions --
1042 *
1043 * Negation of a primary; the unary NOT operator.
1044 */
6b479535 1045int
15637ed4
RG
1046f_not(plan, entry)
1047 PLAN *plan;
1048 FTSENT *entry;
1049{
1050 register PLAN *p;
1051 register int state;
1052
1053 for (p = plan->p_data[0];
1054 p && (state = (p->eval)(p, entry)); p = p->next);
6b479535 1055 return (!state);
15637ed4
RG
1056}
1057
1058PLAN *
1059c_not()
1060{
6b479535 1061 return (palloc(N_NOT, f_not));
15637ed4
RG
1062}
1063
1064/*
1065 * expression -o expression functions --
1066 *
1067 * Alternation of primaries; the OR operator. The second expression is
1068 * not evaluated if the first expression is true.
1069 */
6b479535 1070int
15637ed4
RG
1071f_or(plan, entry)
1072 PLAN *plan;
1073 FTSENT *entry;
1074{
1075 register PLAN *p;
1076 register int state;
1077
1078 for (p = plan->p_data[0];
1079 p && (state = (p->eval)(p, entry)); p = p->next);
1080
1081 if (state)
6b479535 1082 return (1);
15637ed4
RG
1083
1084 for (p = plan->p_data[1];
1085 p && (state = (p->eval)(p, entry)); p = p->next);
6b479535 1086 return (state);
15637ed4
RG
1087}
1088
1089PLAN *
1090c_or()
1091{
6b479535 1092 return (palloc(N_OR, f_or));
15637ed4
RG
1093}
1094
1095static PLAN *
1096palloc(t, f)
1097 enum ntype t;
6b479535 1098 int (*f) __P((PLAN *, FTSENT *));
15637ed4
RG
1099{
1100 PLAN *new;
1101
1102 if (new = malloc(sizeof(PLAN))) {
1103 new->type = t;
1104 new->eval = f;
1105 new->flags = 0;
1106 new->next = NULL;
6b479535 1107 return (new);
15637ed4 1108 }
6b479535 1109 err(1, NULL);
15637ed4
RG
1110 /* NOTREACHED */
1111}