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