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