]> git.saurik.com Git - apple/shell_cmds.git/blame - find/function.c
shell_cmds-34.tar.gz
[apple/shell_cmds.git] / find / function.c
CommitLineData
44bd5ea7
A
1/*-
2 * Copyright (c) 1990, 1993
3 * The Regents of the University of California. 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
44bd5ea7
A
37#ifndef lint
38#if 0
c0fcf4e1 39static const char sccsid[] = "@(#)function.c 8.10 (Berkeley) 5/4/95";
44bd5ea7 40#else
c0fcf4e1
A
41static const char rcsid[] =
42 "$FreeBSD: src/usr.bin/find/function.c,v 1.22.2.9 2001/09/19 09:44:24 ru Exp $";
44bd5ea7
A
43#endif
44#endif /* not lint */
45
46#include <sys/param.h>
47#include <sys/ucred.h>
48#include <sys/stat.h>
49#include <sys/wait.h>
50#include <sys/mount.h>
c0fcf4e1 51#include <sys/timeb.h>
44bd5ea7 52
c0fcf4e1 53#include <dirent.h>
44bd5ea7
A
54#include <err.h>
55#include <errno.h>
56#include <fnmatch.h>
57#include <fts.h>
58#include <grp.h>
59#include <pwd.h>
c0fcf4e1 60#include <regex.h>
44bd5ea7
A
61#include <stdio.h>
62#include <stdlib.h>
63#include <string.h>
44bd5ea7
A
64#include <unistd.h>
65
66#include "find.h"
67
c0fcf4e1
A
68time_t get_date __P((char *date, struct timeb *now));
69
44bd5ea7 70#define COMPARE(a, b) { \
c0fcf4e1 71 switch (plan->flags & F_ELG_MASK) { \
44bd5ea7
A
72 case F_EQUAL: \
73 return (a == b); \
74 case F_LESSTHAN: \
75 return (a < b); \
76 case F_GREATER: \
77 return (a > b); \
78 default: \
79 abort(); \
80 } \
81}
82
c0fcf4e1
A
83static PLAN *
84palloc(option)
85 OPTION *option;
86{
87 PLAN *new;
88
89 if ((new = malloc(sizeof(PLAN))) == NULL)
90 err(1, NULL);
91 new->execute = option->execute;
92 new->flags = option->flags;
93 new->next = NULL;
94 return new;
95}
44bd5ea7
A
96
97/*
98 * find_parsenum --
99 * Parse a string of the form [+-]# and return the value.
100 */
c0fcf4e1 101static long long
44bd5ea7
A
102find_parsenum(plan, option, vp, endch)
103 PLAN *plan;
104 char *option, *vp, *endch;
105{
c0fcf4e1 106 long long value;
44bd5ea7 107 char *endchar, *str; /* Pointer to character ending conversion. */
c0fcf4e1 108
44bd5ea7
A
109 /* Determine comparison from leading + or -. */
110 str = vp;
111 switch (*str) {
112 case '+':
113 ++str;
c0fcf4e1 114 plan->flags |= F_GREATER;
44bd5ea7
A
115 break;
116 case '-':
117 ++str;
c0fcf4e1 118 plan->flags |= F_LESSTHAN;
44bd5ea7
A
119 break;
120 default:
c0fcf4e1 121 plan->flags |= F_EQUAL;
44bd5ea7
A
122 break;
123 }
c0fcf4e1 124
44bd5ea7 125 /*
c0fcf4e1 126 * Convert the string with strtoq(). Note, if strtoq() returns zero
44bd5ea7
A
127 * and endchar points to the beginning of the string we know we have
128 * a syntax error.
129 */
c0fcf4e1 130 value = strtoq(str, &endchar, 10);
44bd5ea7
A
131 if (value == 0 && endchar == str)
132 errx(1, "%s: %s: illegal numeric value", option, vp);
133 if (endchar[0] && (endch == NULL || endchar[0] != *endch))
134 errx(1, "%s: %s: illegal trailing character", option, vp);
135 if (endch)
136 *endch = endchar[0];
c0fcf4e1 137 return value;
44bd5ea7
A
138}
139
c0fcf4e1
A
140/*
141 * nextarg --
142 * Check that another argument still exists, return a pointer to it,
143 * and increment the argument vector pointer.
144 */
145static char *
146nextarg(option, argvp)
147 OPTION *option;
148 char ***argvp;
149{
150 char *arg;
151
152 if ((arg = **argvp) == 0)
153 errx(1, "%s: requires additional arguments", option->name);
154 (*argvp)++;
155 return arg;
156} /* nextarg() */
157
44bd5ea7
A
158/*
159 * The value of n for the inode times (atime, ctime, and mtime) is a range,
160 * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with
161 * -n, such that "-mtime -1" would be less than 0 days, which isn't what the
162 * user wanted. Correct so that -1 is "less than 1".
163 */
c0fcf4e1
A
164#define TIME_CORRECT(p) \
165 if (((p)->flags & F_ELG_MASK) == F_LESSTHAN) \
44bd5ea7
A
166 ++((p)->t_data);
167
168/*
c0fcf4e1 169 * -[acm]min n functions --
44bd5ea7 170 *
c0fcf4e1
A
171 * True if the difference between the
172 * file access time (-amin)
173 * last change of file status information (-cmin)
174 * file modification time (-mmin)
175 * and the current time is n min periods.
44bd5ea7
A
176 */
177int
c0fcf4e1 178f_Xmin(plan, entry)
44bd5ea7
A
179 PLAN *plan;
180 FTSENT *entry;
181{
182 extern time_t now;
183
c0fcf4e1
A
184 if (plan->flags & F_TIME_C) {
185 COMPARE((now - entry->fts_statp->st_ctime +
186 60 - 1) / 60, plan->t_data);
187 } else if (plan->flags & F_TIME_A) {
188 COMPARE((now - entry->fts_statp->st_atime +
189 60 - 1) / 60, plan->t_data);
190 } else {
191 COMPARE((now - entry->fts_statp->st_mtime +
192 60 - 1) / 60, plan->t_data);
193 }
44bd5ea7 194}
c0fcf4e1 195
44bd5ea7 196PLAN *
c0fcf4e1
A
197c_Xmin(option, argvp)
198 OPTION *option;
44bd5ea7 199 char ***argvp;
44bd5ea7 200{
c0fcf4e1 201 char *nmins;
44bd5ea7
A
202 PLAN *new;
203
c0fcf4e1 204 nmins = nextarg(option, argvp);
44bd5ea7
A
205 ftsoptions &= ~FTS_NOSTAT;
206
c0fcf4e1
A
207 new = palloc(option);
208 new->t_data = find_parsenum(new, option->name, nmins, NULL);
209 TIME_CORRECT(new);
210 return new;
44bd5ea7 211}
c0fcf4e1 212
44bd5ea7 213/*
c0fcf4e1 214 * -[acm]time n functions --
44bd5ea7 215 *
c0fcf4e1
A
216 * True if the difference between the
217 * file access time (-atime)
218 * last change of file status information (-ctime)
219 * file modification time (-mtime)
220 * and the current time is n 24 hour periods.
44bd5ea7 221 */
c0fcf4e1 222
44bd5ea7 223int
c0fcf4e1 224f_Xtime(plan, entry)
44bd5ea7
A
225 PLAN *plan;
226 FTSENT *entry;
227{
228 extern time_t now;
229
c0fcf4e1
A
230 if (plan->flags & F_TIME_C) {
231 COMPARE((now - entry->fts_statp->st_ctime +
232 86400 - 1) / 86400, plan->t_data);
233 } else if (plan->flags & F_TIME_A) {
234 COMPARE((now - entry->fts_statp->st_atime +
235 86400 - 1) / 86400, plan->t_data);
236 } else {
237 COMPARE((now - entry->fts_statp->st_mtime +
238 86400 - 1) / 86400, plan->t_data);
239 }
44bd5ea7 240}
c0fcf4e1 241
44bd5ea7 242PLAN *
c0fcf4e1
A
243c_Xtime(option, argvp)
244 OPTION *option;
44bd5ea7 245 char ***argvp;
44bd5ea7 246{
c0fcf4e1 247 char *ndays;
44bd5ea7
A
248 PLAN *new;
249
c0fcf4e1 250 ndays = nextarg(option, argvp);
44bd5ea7
A
251 ftsoptions &= ~FTS_NOSTAT;
252
c0fcf4e1
A
253 new = palloc(option);
254 new->t_data = find_parsenum(new, option->name, ndays, NULL);
255 TIME_CORRECT(new);
256 return new;
44bd5ea7
A
257}
258
c0fcf4e1
A
259/*
260 * -maxdepth/-mindepth n functions --
261 *
262 * Does the same as -prune if the level of the current file is
263 * greater/less than the specified maximum/minimum depth.
264 *
265 * Note that -maxdepth and -mindepth are handled specially in
266 * find_execute() so their f_* functions are set to f_always_true().
267 */
268PLAN *
269c_mXXdepth(option, argvp)
270 OPTION *option;
271 char ***argvp;
272{
273 char *dstr;
274 PLAN *new;
275
276 dstr = nextarg(option, argvp);
277 if (dstr[0] == '-')
278 /* all other errors handled by find_parsenum() */
279 errx(1, "%s: %s: value must be positive", option->name, dstr);
280
281 new = palloc(option);
282 if (option->flags & F_MAXDEPTH)
283 maxdepth = find_parsenum(new, option->name, dstr, NULL);
284 else
285 mindepth = find_parsenum(new, option->name, dstr, NULL);
286 return new;
287}
288
289/*
290 * -delete functions --
291 *
292 * True always. Makes its best shot and continues on regardless.
293 */
294int
295f_delete(plan, entry)
296 PLAN *plan;
297 FTSENT *entry;
298{
299 /* ignore these from fts */
300 if (strcmp(entry->fts_accpath, ".") == 0 ||
301 strcmp(entry->fts_accpath, "..") == 0)
302 return 1;
303
304 /* sanity check */
305 if (isdepth == 0 || /* depth off */
306 (ftsoptions & FTS_NOSTAT) || /* not stat()ing */
307 !(ftsoptions & FTS_PHYSICAL) || /* physical off */
308 (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */
309 errx(1, "-delete: insecure options got turned on");
310
311 /* Potentially unsafe - do not accept relative paths whatsoever */
312 if (strchr(entry->fts_accpath, '/') != NULL)
313 errx(1, "-delete: %s: relative path potentially not safe",
314 entry->fts_accpath);
315
316 /* Turn off user immutable bits if running as root */
317 if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
318 !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
319 geteuid() == 0)
320 chflags(entry->fts_accpath,
321 entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
322
323 /* rmdir directories, unlink everything else */
324 if (S_ISDIR(entry->fts_statp->st_mode)) {
325 if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY)
326 warn("-delete: rmdir(%s)", entry->fts_path);
327 } else {
328 if (unlink(entry->fts_accpath) < 0)
329 warn("-delete: unlink(%s)", entry->fts_path);
330 }
331
332 /* "succeed" */
333 return 1;
334}
335
336PLAN *
337c_delete(option, argvp)
338 OPTION *option;
339 char ***argvp;
340{
341
342 ftsoptions &= ~FTS_NOSTAT; /* no optimise */
343 ftsoptions |= FTS_PHYSICAL; /* disable -follow */
344 ftsoptions &= ~FTS_LOGICAL; /* disable -follow */
345 isoutput = 1; /* possible output */
346 isdepth = 1; /* -depth implied */
347
348 return palloc(option);
349}
350
351
44bd5ea7
A
352/*
353 * -depth functions --
354 *
355 * Always true, causes descent of the directory hierarchy to be done
356 * so that all entries in a directory are acted on before the directory
357 * itself.
358 */
359int
360f_always_true(plan, entry)
361 PLAN *plan;
362 FTSENT *entry;
363{
c0fcf4e1 364 return 1;
44bd5ea7 365}
c0fcf4e1 366
44bd5ea7 367PLAN *
c0fcf4e1
A
368c_depth(option, argvp)
369 OPTION *option;
44bd5ea7 370 char ***argvp;
44bd5ea7
A
371{
372 isdepth = 1;
373
c0fcf4e1
A
374 return palloc(option);
375}
376
377/*
378 * -empty functions --
379 *
380 * True if the file or directory is empty
381 */
382int
383f_empty(plan, entry)
384 PLAN *plan;
385 FTSENT *entry;
386{
387 if (S_ISREG(entry->fts_statp->st_mode) &&
388 entry->fts_statp->st_size == 0)
389 return 1;
390 if (S_ISDIR(entry->fts_statp->st_mode)) {
391 struct dirent *dp;
392 int empty;
393 DIR *dir;
394
395 empty = 1;
396 dir = opendir(entry->fts_accpath);
397 if (dir == NULL)
398 err(1, "%s", entry->fts_accpath);
399 for (dp = readdir(dir); dp; dp = readdir(dir))
400 if (dp->d_name[0] != '.' ||
401 (dp->d_name[1] != '\0' &&
402 (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) {
403 empty = 0;
404 break;
405 }
406 closedir(dir);
407 return empty;
408 }
409 return 0;
410}
411
412PLAN *
413c_empty(option, argvp)
414 OPTION *option;
415 char ***argvp;
416{
417 ftsoptions &= ~FTS_NOSTAT;
418
419 return palloc(option);
44bd5ea7 420}
c0fcf4e1 421
44bd5ea7 422/*
c0fcf4e1 423 * [-exec | -execdir | -ok] utility [arg ... ] ; functions --
44bd5ea7
A
424 *
425 * True if the executed utility returns a zero value as exit status.
426 * The end of the primary expression is delimited by a semicolon. If
c0fcf4e1
A
427 * "{}" occurs anywhere, it gets replaced by the current pathname,
428 * or, in the case of -execdir, the current basename (filename
429 * without leading directory prefix). For -exec and -ok,
430 * the current directory for the execution of utility is the same as
431 * the current directory when the find utility was started, whereas
432 * for -execdir, it is the directory the file resides in.
44bd5ea7 433 *
c0fcf4e1
A
434 * The primary -ok differs from -exec in that it requests affirmation
435 * of the user before executing the utility.
44bd5ea7
A
436 */
437int
438f_exec(plan, entry)
c0fcf4e1 439 register PLAN *plan;
44bd5ea7
A
440 FTSENT *entry;
441{
442 extern int dotfd;
c0fcf4e1 443 register int cnt;
44bd5ea7
A
444 pid_t pid;
445 int status;
c0fcf4e1
A
446 char *file;
447
448 /* XXX - if file/dir ends in '/' this will not work -- can it? */
449 if ((plan->flags & F_EXECDIR) && \
450 (file = strrchr(entry->fts_path, '/')))
451 file++;
452 else
453 file = entry->fts_path;
44bd5ea7
A
454
455 for (cnt = 0; plan->e_argv[cnt]; ++cnt)
456 if (plan->e_len[cnt])
457 brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
c0fcf4e1 458 file, plan->e_len[cnt]);
44bd5ea7 459
c0fcf4e1
A
460 if ((plan->flags & F_NEEDOK) && !queryuser(plan->e_argv))
461 return 0;
44bd5ea7 462
c0fcf4e1 463 /* make sure find output is interspersed correctly with subprocesses */
44bd5ea7
A
464 fflush(stdout);
465 fflush(stderr);
466
c0fcf4e1 467 switch (pid = fork()) {
44bd5ea7
A
468 case -1:
469 err(1, "fork");
470 /* NOTREACHED */
471 case 0:
c0fcf4e1
A
472 /* change dir back from where we started */
473 if (!(plan->flags & F_EXECDIR) && fchdir(dotfd)) {
44bd5ea7
A
474 warn("chdir");
475 _exit(1);
476 }
477 execvp(plan->e_argv[0], plan->e_argv);
478 warn("%s", plan->e_argv[0]);
479 _exit(1);
480 }
481 pid = waitpid(pid, &status, 0);
482 return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
483}
c0fcf4e1 484
44bd5ea7 485/*
c0fcf4e1 486 * c_exec, c_execdir, c_ok --
44bd5ea7
A
487 * build three parallel arrays, one with pointers to the strings passed
488 * on the command line, one with (possibly duplicated) pointers to the
489 * argv array, and one with integer values that are lengths of the
490 * strings, but also flags meaning that the string has to be massaged.
491 */
492PLAN *
c0fcf4e1
A
493c_exec(option, argvp)
494 OPTION *option;
44bd5ea7 495 char ***argvp;
44bd5ea7
A
496{
497 PLAN *new; /* node returned */
c0fcf4e1
A
498 register int cnt;
499 register char **argv, **ap, *p;
44bd5ea7 500
c0fcf4e1
A
501 /* XXX - was in c_execdir, but seems unnecessary!?
502 ftsoptions &= ~FTS_NOSTAT;
503 */
44bd5ea7 504 isoutput = 1;
c0fcf4e1
A
505
506 /* XXX - this is a change from the previous coding */
507 new = palloc(option);
44bd5ea7
A
508
509 for (ap = argv = *argvp;; ++ap) {
510 if (!*ap)
511 errx(1,
c0fcf4e1 512 "%s: no terminating \";\"", option->name);
44bd5ea7
A
513 if (**ap == ';')
514 break;
515 }
516
517 cnt = ap - *argvp + 1;
518 new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
519 new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
520 new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
521
522 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
523 new->e_orig[cnt] = *argv;
524 for (p = *argv; *p; ++p)
525 if (p[0] == '{' && p[1] == '}') {
526 new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
527 new->e_len[cnt] = MAXPATHLEN;
528 break;
529 }
530 if (!*p) {
531 new->e_argv[cnt] = *argv;
532 new->e_len[cnt] = 0;
533 }
534 }
535 new->e_argv[cnt] = new->e_orig[cnt] = NULL;
536
537 *argvp = argv + 1;
c0fcf4e1
A
538 return new;
539}
540
541int
542f_flags(plan, entry)
543 PLAN *plan;
544 FTSENT *entry;
545{
546 u_long flags;
547
548 flags = entry->fts_statp->st_flags;
549 if (plan->flags & F_ATLEAST)
550 return (flags | plan->fl_flags) == flags &&
551 !(flags & plan->fl_notflags);
552 else if (plan->flags & F_ANY)
553 return (flags & plan->fl_flags) ||
554 (flags | plan->fl_notflags) != flags;
555 else
556 return flags == plan->fl_flags &&
557 !(plan->fl_flags & plan->fl_notflags);
558}
559
560PLAN *
561c_flags(option, argvp)
562 OPTION *option;
563 char ***argvp;
564{
565 char *flags_str;
566 PLAN *new;
567 u_long flags, notflags;
568
569 flags_str = nextarg(option, argvp);
570 ftsoptions &= ~FTS_NOSTAT;
571
572 new = palloc(option);
573
574 if (*flags_str == '-') {
575 new->flags |= F_ATLEAST;
576 flags_str++;
577 } else if (*flags_str == '+') {
578 new->flags |= F_ANY;
579 flags_str++;
580 }
581 if (strtofflags(&flags_str, &flags, &notflags) == 1)
582 errx(1, "%s: %s: illegal flags string", option->name, flags_str);
583
584 new->fl_flags = flags;
585 new->fl_notflags = notflags;
586 return new;
44bd5ea7 587}
c0fcf4e1 588
44bd5ea7
A
589/*
590 * -follow functions --
591 *
592 * Always true, causes symbolic links to be followed on a global
593 * basis.
594 */
595PLAN *
c0fcf4e1
A
596c_follow(option, argvp)
597 OPTION *option;
44bd5ea7 598 char ***argvp;
44bd5ea7
A
599{
600 ftsoptions &= ~FTS_PHYSICAL;
601 ftsoptions |= FTS_LOGICAL;
602
c0fcf4e1 603 return palloc(option);
44bd5ea7 604}
c0fcf4e1 605
44bd5ea7
A
606/*
607 * -fstype functions --
608 *
609 * True if the file is of a certain type.
610 */
611int
612f_fstype(plan, entry)
613 PLAN *plan;
614 FTSENT *entry;
615{
616 static dev_t curdev; /* need a guaranteed illegal dev value */
617 static int first = 1;
618 struct statfs sb;
c0fcf4e1 619 static int val_type, val_flags;
44bd5ea7
A
620 char *p, save[2];
621
622 /* Only check when we cross mount point. */
623 if (first || curdev != entry->fts_statp->st_dev) {
624 curdev = entry->fts_statp->st_dev;
625
626 /*
627 * Statfs follows symlinks; find wants the link's file system,
628 * not where it points.
629 */
630 if (entry->fts_info == FTS_SL ||
631 entry->fts_info == FTS_SLNONE) {
632 if ((p = strrchr(entry->fts_accpath, '/')) != NULL)
633 ++p;
634 else
635 p = entry->fts_accpath;
636 save[0] = p[0];
637 p[0] = '.';
638 save[1] = p[1];
639 p[1] = '\0';
c0fcf4e1 640 } else
44bd5ea7
A
641 p = NULL;
642
643 if (statfs(entry->fts_accpath, &sb))
644 err(1, "%s", entry->fts_accpath);
645
646 if (p) {
647 p[0] = save[0];
648 p[1] = save[1];
649 }
650
651 first = 0;
652
653 /*
654 * Further tests may need both of these values, so
655 * always copy both of them.
656 */
c0fcf4e1
A
657 val_flags = sb.f_flags;
658 val_type = sb.f_type;
44bd5ea7 659 }
c0fcf4e1 660 switch (plan->flags & F_MTMASK) {
44bd5ea7 661 case F_MTFLAG:
c0fcf4e1 662 return (val_flags & plan->mt_data) != 0;
44bd5ea7 663 case F_MTTYPE:
c0fcf4e1 664 return (val_type == plan->mt_data);
44bd5ea7
A
665 default:
666 abort();
667 }
668}
c0fcf4e1
A
669
670#if !defined(__NetBSD__)
44bd5ea7 671PLAN *
c0fcf4e1
A
672c_fstype(option, argvp)
673 OPTION *option;
44bd5ea7 674 char ***argvp;
44bd5ea7 675{
c0fcf4e1
A
676 char *fsname;
677 register PLAN *new;
678 struct vfsconf vfc;
679
680 fsname = nextarg(option, argvp);
44bd5ea7 681 ftsoptions &= ~FTS_NOSTAT;
44bd5ea7 682
c0fcf4e1
A
683 new = palloc(option);
684
685 /*
686 * Check first for a filesystem name.
687 */
688 if (getvfsbyname(fsname, &vfc) == 0) {
689 new->flags |= F_MTTYPE;
690 new->mt_data = vfc.vfc_typenum;
691 return new;
692 }
693
694 switch (*fsname) {
44bd5ea7 695 case 'l':
c0fcf4e1
A
696 if (!strcmp(fsname, "local")) {
697 new->flags |= F_MTFLAG;
44bd5ea7 698 new->mt_data = MNT_LOCAL;
c0fcf4e1 699 return new;
44bd5ea7
A
700 }
701 break;
702 case 'r':
c0fcf4e1
A
703 if (!strcmp(fsname, "rdonly")) {
704 new->flags |= F_MTFLAG;
44bd5ea7 705 new->mt_data = MNT_RDONLY;
c0fcf4e1 706 return new;
44bd5ea7
A
707 }
708 break;
709 }
710
c0fcf4e1
A
711 errx(1, "%s: unknown file type", fsname);
712 /* NOTREACHED */
44bd5ea7 713}
c0fcf4e1
A
714#endif /* __NetBSD__ */
715
44bd5ea7
A
716/*
717 * -group gname functions --
718 *
719 * True if the file belongs to the group gname. If gname is numeric and
720 * an equivalent of the getgrnam() function does not return a valid group
721 * name, gname is taken as a group ID.
722 */
723int
724f_group(plan, entry)
725 PLAN *plan;
726 FTSENT *entry;
727{
c0fcf4e1 728 return entry->fts_statp->st_gid == plan->g_data;
44bd5ea7 729}
c0fcf4e1 730
44bd5ea7 731PLAN *
c0fcf4e1
A
732c_group(option, argvp)
733 OPTION *option;
44bd5ea7 734 char ***argvp;
44bd5ea7 735{
c0fcf4e1 736 char *gname;
44bd5ea7
A
737 PLAN *new;
738 struct group *g;
739 gid_t gid;
c0fcf4e1
A
740
741 gname = nextarg(option, argvp);
44bd5ea7
A
742 ftsoptions &= ~FTS_NOSTAT;
743
744 g = getgrnam(gname);
745 if (g == NULL) {
746 gid = atoi(gname);
747 if (gid == 0 && gname[0] != '0')
c0fcf4e1 748 errx(1, "%s: %s: no such group", option->name, gname);
44bd5ea7
A
749 } else
750 gid = g->gr_gid;
c0fcf4e1
A
751
752 new = palloc(option);
44bd5ea7 753 new->g_data = gid;
c0fcf4e1 754 return new;
44bd5ea7
A
755}
756
757/*
758 * -inum n functions --
759 *
760 * True if the file has inode # n.
761 */
762int
763f_inum(plan, entry)
764 PLAN *plan;
765 FTSENT *entry;
766{
767 COMPARE(entry->fts_statp->st_ino, plan->i_data);
768}
c0fcf4e1 769
44bd5ea7 770PLAN *
c0fcf4e1
A
771c_inum(option, argvp)
772 OPTION *option;
44bd5ea7 773 char ***argvp;
44bd5ea7 774{
c0fcf4e1 775 char *inum_str;
44bd5ea7 776 PLAN *new;
c0fcf4e1
A
777
778 inum_str = nextarg(option, argvp);
44bd5ea7 779 ftsoptions &= ~FTS_NOSTAT;
c0fcf4e1
A
780
781 new = palloc(option);
782 new->i_data = find_parsenum(new, option->name, inum_str, NULL);
783 return new;
44bd5ea7 784}
c0fcf4e1 785
44bd5ea7
A
786/*
787 * -links n functions --
788 *
789 * True if the file has n links.
790 */
791int
792f_links(plan, entry)
793 PLAN *plan;
794 FTSENT *entry;
795{
796 COMPARE(entry->fts_statp->st_nlink, plan->l_data);
797}
c0fcf4e1 798
44bd5ea7 799PLAN *
c0fcf4e1
A
800c_links(option, argvp)
801 OPTION *option;
44bd5ea7 802 char ***argvp;
44bd5ea7 803{
c0fcf4e1 804 char *nlinks;
44bd5ea7 805 PLAN *new;
c0fcf4e1
A
806
807 nlinks = nextarg(option, argvp);
44bd5ea7 808 ftsoptions &= ~FTS_NOSTAT;
c0fcf4e1
A
809
810 new = palloc(option);
811 new->l_data = (nlink_t)find_parsenum(new, option->name, nlinks, NULL);
812 return new;
44bd5ea7 813}
c0fcf4e1 814
44bd5ea7
A
815/*
816 * -ls functions --
817 *
818 * Always true - prints the current entry to stdout in "ls" format.
819 */
820int
821f_ls(plan, entry)
822 PLAN *plan;
823 FTSENT *entry;
824{
825 printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
c0fcf4e1 826 return 1;
44bd5ea7
A
827}
828
44bd5ea7 829PLAN *
c0fcf4e1
A
830c_ls(option, argvp)
831 OPTION *option;
44bd5ea7 832 char ***argvp;
44bd5ea7 833{
44bd5ea7 834 ftsoptions &= ~FTS_NOSTAT;
c0fcf4e1 835 isoutput = 1;
44bd5ea7 836
c0fcf4e1 837 return palloc(option);
44bd5ea7
A
838}
839
840/*
841 * -name functions --
842 *
843 * True if the basename of the filename being examined
844 * matches pattern using Pattern Matching Notation S3.14
845 */
846int
847f_name(plan, entry)
848 PLAN *plan;
849 FTSENT *entry;
850{
c0fcf4e1
A
851 return !fnmatch(plan->c_data, entry->fts_name,
852 plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0);
44bd5ea7 853}
c0fcf4e1 854
44bd5ea7 855PLAN *
c0fcf4e1
A
856c_name(option, argvp)
857 OPTION *option;
44bd5ea7 858 char ***argvp;
44bd5ea7 859{
c0fcf4e1 860 char *pattern;
44bd5ea7
A
861 PLAN *new;
862
c0fcf4e1
A
863 pattern = nextarg(option, argvp);
864 new = palloc(option);
44bd5ea7 865 new->c_data = pattern;
c0fcf4e1 866 return new;
44bd5ea7 867}
c0fcf4e1 868
44bd5ea7
A
869/*
870 * -newer file functions --
871 *
872 * True if the current file has been modified more recently
873 * then the modification time of the file named by the pathname
874 * file.
875 */
876int
877f_newer(plan, entry)
878 PLAN *plan;
879 FTSENT *entry;
880{
c0fcf4e1
A
881 if (plan->flags & F_TIME_C)
882 return entry->fts_statp->st_ctime > plan->t_data;
883 else if (plan->flags & F_TIME_A)
884 return entry->fts_statp->st_atime > plan->t_data;
885 else
886 return entry->fts_statp->st_mtime > plan->t_data;
44bd5ea7 887}
c0fcf4e1 888
44bd5ea7 889PLAN *
c0fcf4e1
A
890c_newer(option, argvp)
891 OPTION *option;
44bd5ea7 892 char ***argvp;
44bd5ea7 893{
c0fcf4e1 894 char *fn_or_tspec;
44bd5ea7
A
895 PLAN *new;
896 struct stat sb;
c0fcf4e1
A
897
898 fn_or_tspec = nextarg(option, argvp);
44bd5ea7
A
899 ftsoptions &= ~FTS_NOSTAT;
900
c0fcf4e1
A
901 new = palloc(option);
902 /* compare against what */
903 if (option->flags & F_TIME2_T) {
904 new->t_data = get_date(fn_or_tspec, (struct timeb *) 0);
905 if (new->t_data == (time_t) -1)
906 errx(1, "Can't parse date/time: %s", fn_or_tspec);
907 } else {
908 if (stat(fn_or_tspec, &sb))
909 err(1, "%s", fn_or_tspec);
910 if (option->flags & F_TIME2_C)
911 new->t_data = sb.st_ctime;
912 else if (option->flags & F_TIME2_A)
913 new->t_data = sb.st_atime;
914 else
915 new->t_data = sb.st_mtime;
916 }
917 return new;
44bd5ea7 918}
c0fcf4e1 919
44bd5ea7
A
920/*
921 * -nogroup functions --
922 *
923 * True if file belongs to a user ID for which the equivalent
924 * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
925 */
926int
927f_nogroup(plan, entry)
928 PLAN *plan;
929 FTSENT *entry;
930{
c0fcf4e1 931 return group_from_gid(entry->fts_statp->st_gid, 1) == NULL;
44bd5ea7 932}
c0fcf4e1 933
44bd5ea7 934PLAN *
c0fcf4e1
A
935c_nogroup(option, argvp)
936 OPTION *option;
44bd5ea7 937 char ***argvp;
44bd5ea7
A
938{
939 ftsoptions &= ~FTS_NOSTAT;
940
c0fcf4e1 941 return palloc(option);
44bd5ea7 942}
c0fcf4e1 943
44bd5ea7
A
944/*
945 * -nouser functions --
946 *
947 * True if file belongs to a user ID for which the equivalent
948 * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
949 */
950int
951f_nouser(plan, entry)
952 PLAN *plan;
953 FTSENT *entry;
954{
c0fcf4e1 955 return user_from_uid(entry->fts_statp->st_uid, 1) == NULL;
44bd5ea7 956}
c0fcf4e1 957
44bd5ea7 958PLAN *
c0fcf4e1
A
959c_nouser(option, argvp)
960 OPTION *option;
44bd5ea7 961 char ***argvp;
44bd5ea7
A
962{
963 ftsoptions &= ~FTS_NOSTAT;
964
c0fcf4e1 965 return palloc(option);
44bd5ea7 966}
c0fcf4e1 967
44bd5ea7
A
968/*
969 * -path functions --
970 *
971 * True if the path of the filename being examined
972 * matches pattern using Pattern Matching Notation S3.14
973 */
974int
975f_path(plan, entry)
976 PLAN *plan;
977 FTSENT *entry;
978{
c0fcf4e1
A
979 return !fnmatch(plan->c_data, entry->fts_path,
980 plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0);
44bd5ea7 981}
44bd5ea7 982
c0fcf4e1
A
983/* c_path is the same as c_name */
984
44bd5ea7
A
985/*
986 * -perm functions --
987 *
988 * The mode argument is used to represent file mode bits. If it starts
989 * with a leading digit, it's treated as an octal mode, otherwise as a
990 * symbolic mode.
991 */
992int
993f_perm(plan, entry)
994 PLAN *plan;
995 FTSENT *entry;
996{
997 mode_t mode;
998
999 mode = entry->fts_statp->st_mode &
1000 (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
c0fcf4e1
A
1001 if (plan->flags & F_ATLEAST)
1002 return (plan->m_data | mode) == mode;
1003 else if (plan->flags & F_ANY)
1004 return (mode & plan->m_data);
44bd5ea7 1005 else
c0fcf4e1 1006 return mode == plan->m_data;
44bd5ea7
A
1007 /* NOTREACHED */
1008}
c0fcf4e1 1009
44bd5ea7 1010PLAN *
c0fcf4e1
A
1011c_perm(option, argvp)
1012 OPTION *option;
44bd5ea7 1013 char ***argvp;
44bd5ea7 1014{
c0fcf4e1 1015 char *perm;
44bd5ea7
A
1016 PLAN *new;
1017 mode_t *set;
1018
c0fcf4e1 1019 perm = nextarg(option, argvp);
44bd5ea7
A
1020 ftsoptions &= ~FTS_NOSTAT;
1021
c0fcf4e1 1022 new = palloc(option);
44bd5ea7
A
1023
1024 if (*perm == '-') {
c0fcf4e1
A
1025 new->flags |= F_ATLEAST;
1026 ++perm;
1027 } else if (*perm == '+') {
1028 new->flags |= F_ANY;
44bd5ea7
A
1029 ++perm;
1030 }
1031
1032 if ((set = setmode(perm)) == NULL)
c0fcf4e1 1033 errx(1, "%s: %s: illegal mode string", option->name, perm);
44bd5ea7
A
1034
1035 new->m_data = getmode(set, 0);
c0fcf4e1
A
1036 free(set);
1037 return new;
44bd5ea7 1038}
c0fcf4e1 1039
44bd5ea7
A
1040/*
1041 * -print functions --
1042 *
1043 * Always true, causes the current pathame to be written to
1044 * standard output.
1045 */
1046int
1047f_print(plan, entry)
1048 PLAN *plan;
1049 FTSENT *entry;
1050{
c0fcf4e1
A
1051 (void)puts(entry->fts_path);
1052 return 1;
44bd5ea7
A
1053}
1054
44bd5ea7 1055PLAN *
c0fcf4e1
A
1056c_print(option, argvp)
1057 OPTION *option;
44bd5ea7 1058 char ***argvp;
44bd5ea7
A
1059{
1060 isoutput = 1;
1061
c0fcf4e1 1062 return palloc(option);
44bd5ea7
A
1063}
1064
c0fcf4e1
A
1065/*
1066 * -print0 functions --
1067 *
1068 * Always true, causes the current pathame to be written to
1069 * standard output followed by a NUL character
1070 */
1071int
1072f_print0(plan, entry)
1073 PLAN *plan;
1074 FTSENT *entry;
44bd5ea7 1075{
c0fcf4e1
A
1076 fputs(entry->fts_path, stdout);
1077 fputc('\0', stdout);
1078 return 1;
44bd5ea7 1079}
c0fcf4e1
A
1080
1081/* c_print0 is the same as c_print */
1082
44bd5ea7
A
1083/*
1084 * -prune functions --
1085 *
1086 * Prune a portion of the hierarchy.
1087 */
1088int
1089f_prune(plan, entry)
1090 PLAN *plan;
1091 FTSENT *entry;
1092{
1093 extern FTS *tree;
1094
1095 if (fts_set(tree, entry, FTS_SKIP))
1096 err(1, "%s", entry->fts_path);
c0fcf4e1 1097 return 1;
44bd5ea7 1098}
c0fcf4e1
A
1099
1100/* c_prune == c_simple */
1101
1102/*
1103 * -regex functions --
1104 *
1105 * True if the whole path of the file matches pattern using
1106 * regular expression.
1107 */
1108int
1109f_regex(plan, entry)
1110 PLAN *plan;
1111 FTSENT *entry;
1112{
1113 char *str;
1114 size_t len;
1115 regex_t *pre;
1116 regmatch_t pmatch;
1117 int errcode;
1118 char errbuf[LINE_MAX];
1119 int matched;
1120
1121 pre = plan->re_data;
1122 str = entry->fts_path;
1123 len = strlen(str);
1124 matched = 0;
1125
1126 pmatch.rm_so = 0;
1127 pmatch.rm_eo = len;
1128
1129 errcode = regexec(pre, str, 1, &pmatch, REG_STARTEND);
1130
1131 if (errcode != 0 && errcode != REG_NOMATCH) {
1132 regerror(errcode, pre, errbuf, sizeof errbuf);
1133 errx(1, "%s: %s",
1134 plan->flags & F_IGNCASE ? "-iregex" : "-regex", errbuf);
1135 }
1136
1137 if (errcode == 0 && pmatch.rm_so == 0 && pmatch.rm_eo == len)
1138 matched = 1;
1139
1140 return matched;
1141}
1142
44bd5ea7 1143PLAN *
c0fcf4e1
A
1144c_regex(option, argvp)
1145 OPTION *option;
44bd5ea7 1146 char ***argvp;
44bd5ea7 1147{
c0fcf4e1
A
1148 PLAN *new;
1149 char *pattern;
1150 regex_t *pre;
1151 int errcode;
1152 char errbuf[LINE_MAX];
1153
1154 if ((pre = malloc(sizeof(regex_t))) == NULL)
1155 err(1, NULL);
1156
1157 pattern = nextarg(option, argvp);
1158
1159 if ((errcode = regcomp(pre, pattern,
1160 regexp_flags | (option->flags & F_IGNCASE ? REG_ICASE : 0))) != 0) {
1161 regerror(errcode, pre, errbuf, sizeof errbuf);
1162 errx(1, "%s: %s: %s",
1163 option->flags & F_IGNCASE ? "-iregex" : "-regex",
1164 pattern, errbuf);
1165 }
1166
1167 new = palloc(option);
1168 new->re_data = pre;
1169
1170 return new;
1171}
1172
1173/* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or */
1174
1175PLAN *
1176c_simple(option, argvp)
1177 OPTION *option;
1178 char ***argvp;
1179{
1180 return palloc(option);
44bd5ea7 1181}
c0fcf4e1 1182
44bd5ea7
A
1183/*
1184 * -size n[c] functions --
1185 *
1186 * True if the file size in bytes, divided by an implementation defined
1187 * value and rounded up to the next integer, is n. If n is followed by
1188 * a c, the size is in bytes.
1189 */
1190#define FIND_SIZE 512
1191static int divsize = 1;
1192
1193int
1194f_size(plan, entry)
1195 PLAN *plan;
1196 FTSENT *entry;
1197{
1198 off_t size;
1199
1200 size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
1201 FIND_SIZE : entry->fts_statp->st_size;
1202 COMPARE(size, plan->o_data);
1203}
c0fcf4e1 1204
44bd5ea7 1205PLAN *
c0fcf4e1
A
1206c_size(option, argvp)
1207 OPTION *option;
44bd5ea7 1208 char ***argvp;
44bd5ea7 1209{
c0fcf4e1 1210 char *size_str;
44bd5ea7
A
1211 PLAN *new;
1212 char endch;
c0fcf4e1
A
1213
1214 size_str = nextarg(option, argvp);
44bd5ea7
A
1215 ftsoptions &= ~FTS_NOSTAT;
1216
c0fcf4e1 1217 new = palloc(option);
44bd5ea7 1218 endch = 'c';
c0fcf4e1 1219 new->o_data = find_parsenum(new, option->name, size_str, &endch);
44bd5ea7
A
1220 if (endch == 'c')
1221 divsize = 0;
c0fcf4e1 1222 return new;
44bd5ea7 1223}
c0fcf4e1 1224
44bd5ea7
A
1225/*
1226 * -type c functions --
1227 *
1228 * True if the type of the file is c, where c is b, c, d, p, f or w
1229 * for block special file, character special file, directory, FIFO,
1230 * regular file or whiteout respectively.
1231 */
1232int
1233f_type(plan, entry)
1234 PLAN *plan;
1235 FTSENT *entry;
1236{
c0fcf4e1 1237 return (entry->fts_statp->st_mode & S_IFMT) == plan->m_data;
44bd5ea7 1238}
c0fcf4e1 1239
44bd5ea7 1240PLAN *
c0fcf4e1
A
1241c_type(option, argvp)
1242 OPTION *option;
44bd5ea7 1243 char ***argvp;
44bd5ea7 1244{
c0fcf4e1 1245 char *typestring;
44bd5ea7 1246 PLAN *new;
c0fcf4e1
A
1247 mode_t mask;
1248
1249 typestring = nextarg(option, argvp);
44bd5ea7
A
1250 ftsoptions &= ~FTS_NOSTAT;
1251
1252 switch (typestring[0]) {
44bd5ea7
A
1253 case 'b':
1254 mask = S_IFBLK;
1255 break;
1256 case 'c':
1257 mask = S_IFCHR;
1258 break;
1259 case 'd':
1260 mask = S_IFDIR;
1261 break;
1262 case 'f':
1263 mask = S_IFREG;
1264 break;
1265 case 'l':
1266 mask = S_IFLNK;
1267 break;
1268 case 'p':
1269 mask = S_IFIFO;
1270 break;
1271 case 's':
1272 mask = S_IFSOCK;
1273 break;
1274#ifdef FTS_WHITEOUT
1275 case 'w':
1276 mask = S_IFWHT;
1277 ftsoptions |= FTS_WHITEOUT;
1278 break;
1279#endif /* FTS_WHITEOUT */
1280 default:
c0fcf4e1 1281 errx(1, "%s: %s: unknown type", option->name, typestring);
44bd5ea7 1282 }
c0fcf4e1
A
1283
1284 new = palloc(option);
44bd5ea7 1285 new->m_data = mask;
c0fcf4e1 1286 return new;
44bd5ea7 1287}
c0fcf4e1 1288
44bd5ea7
A
1289/*
1290 * -user uname functions --
1291 *
1292 * True if the file belongs to the user uname. If uname is numeric and
1293 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1294 * return a valid user name, uname is taken as a user ID.
1295 */
1296int
1297f_user(plan, entry)
1298 PLAN *plan;
1299 FTSENT *entry;
1300{
c0fcf4e1 1301 return entry->fts_statp->st_uid == plan->u_data;
44bd5ea7 1302}
c0fcf4e1 1303
44bd5ea7 1304PLAN *
c0fcf4e1
A
1305c_user(option, argvp)
1306 OPTION *option;
44bd5ea7 1307 char ***argvp;
44bd5ea7 1308{
c0fcf4e1 1309 char *username;
44bd5ea7
A
1310 PLAN *new;
1311 struct passwd *p;
1312 uid_t uid;
c0fcf4e1
A
1313
1314 username = nextarg(option, argvp);
44bd5ea7
A
1315 ftsoptions &= ~FTS_NOSTAT;
1316
1317 p = getpwnam(username);
1318 if (p == NULL) {
1319 uid = atoi(username);
1320 if (uid == 0 && username[0] != '0')
c0fcf4e1 1321 errx(1, "%s: %s: no such user", option->name, username);
44bd5ea7
A
1322 } else
1323 uid = p->pw_uid;
1324
c0fcf4e1 1325 new = palloc(option);
44bd5ea7 1326 new->u_data = uid;
c0fcf4e1 1327 return new;
44bd5ea7 1328}
c0fcf4e1 1329
44bd5ea7
A
1330/*
1331 * -xdev functions --
1332 *
1333 * Always true, causes find not to decend past directories that have a
1334 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1335 */
1336PLAN *
c0fcf4e1
A
1337c_xdev(option, argvp)
1338 OPTION *option;
44bd5ea7 1339 char ***argvp;
44bd5ea7
A
1340{
1341 ftsoptions |= FTS_XDEV;
1342
c0fcf4e1 1343 return palloc(option);
44bd5ea7
A
1344}
1345
1346/*
1347 * ( expression ) functions --
1348 *
1349 * True if expression is true.
1350 */
1351int
1352f_expr(plan, entry)
1353 PLAN *plan;
1354 FTSENT *entry;
1355{
c0fcf4e1
A
1356 register PLAN *p;
1357 register int state = 0;
44bd5ea7 1358
44bd5ea7 1359 for (p = plan->p_data[0];
c0fcf4e1
A
1360 p && (state = (p->execute)(p, entry)); p = p->next);
1361 return state;
44bd5ea7 1362}
c0fcf4e1 1363
44bd5ea7 1364/*
c0fcf4e1 1365 * f_openparen and f_closeparen nodes are temporary place markers. They are
44bd5ea7 1366 * eliminated during phase 2 of find_formplan() --- the '(' node is converted
c0fcf4e1
A
1367 * to a f_expr node containing the expression and the ')' node is discarded.
1368 * The functions themselves are only used as constants.
44bd5ea7 1369 */
c0fcf4e1
A
1370
1371int
1372f_openparen(plan, entry)
1373 PLAN *plan;
1374 FTSENT *entry;
1375{
1376 abort();
1377}
1378
1379int
1380f_closeparen(plan, entry)
1381 PLAN *plan;
1382 FTSENT *entry;
44bd5ea7 1383{
c0fcf4e1 1384 abort();
44bd5ea7 1385}
c0fcf4e1
A
1386
1387/* c_openparen == c_simple */
1388/* c_closeparen == c_simple */
1389
1390/*
1391 * AND operator. Since AND is implicit, no node is allocated.
1392 */
44bd5ea7 1393PLAN *
c0fcf4e1
A
1394c_and(option, argvp)
1395 OPTION *option;
44bd5ea7 1396 char ***argvp;
44bd5ea7 1397{
c0fcf4e1 1398 return NULL;
44bd5ea7 1399}
c0fcf4e1 1400
44bd5ea7
A
1401/*
1402 * ! expression functions --
1403 *
1404 * Negation of a primary; the unary NOT operator.
1405 */
1406int
1407f_not(plan, entry)
1408 PLAN *plan;
1409 FTSENT *entry;
1410{
c0fcf4e1
A
1411 register PLAN *p;
1412 register int state = 0;
44bd5ea7 1413
44bd5ea7 1414 for (p = plan->p_data[0];
c0fcf4e1
A
1415 p && (state = (p->execute)(p, entry)); p = p->next);
1416 return !state;
44bd5ea7 1417}
c0fcf4e1
A
1418
1419/* c_not == c_simple */
1420
44bd5ea7
A
1421/*
1422 * expression -o expression functions --
1423 *
1424 * Alternation of primaries; the OR operator. The second expression is
1425 * not evaluated if the first expression is true.
1426 */
1427int
1428f_or(plan, entry)
1429 PLAN *plan;
1430 FTSENT *entry;
1431{
c0fcf4e1
A
1432 register PLAN *p;
1433 register int state = 0;
44bd5ea7 1434
44bd5ea7 1435 for (p = plan->p_data[0];
c0fcf4e1 1436 p && (state = (p->execute)(p, entry)); p = p->next);
44bd5ea7
A
1437
1438 if (state)
c0fcf4e1 1439 return 1;
44bd5ea7
A
1440
1441 for (p = plan->p_data[1];
c0fcf4e1
A
1442 p && (state = (p->execute)(p, entry)); p = p->next);
1443 return state;
44bd5ea7
A
1444}
1445
c0fcf4e1 1446/* c_or == c_simple */