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