Work round an apparent compiler bug in HP-UX 11.23 for
[findutils.git] / find / parser.c
blob2e361b69c18eb699638086beea366a25bab2fa67
1 /* parser.c -- convert the command line args into an expression tree.
2 Copyright (C) 1990, 91, 92, 93, 94, 2000 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 9 Temple Place - Suite 330, Boston, MA 02111-1307,
17 USA.
20 #define _GNU_SOURCE
21 #include "defs.h"
22 #include <ctype.h>
23 #include <pwd.h>
24 #include <grp.h>
25 #include <fnmatch.h>
26 #include "modechange.h"
27 #include "modetype.h"
28 #include "xstrtol.h"
31 #if ENABLE_NLS
32 # include <libintl.h>
33 # define _(Text) gettext (Text)
34 #else
35 # define _(Text) Text
36 #endif
37 #ifdef gettext_noop
38 # define N_(String) gettext_noop (String)
39 #else
40 /* See locate.c for explanation as to why not use (String) */
41 # define N_(String) String
42 #endif
44 #if !defined (isascii) || defined (STDC_HEADERS)
45 #ifdef isascii
46 #undef isascii
47 #endif
48 #define isascii(c) 1
49 #endif
51 #define ISDIGIT(c) (isascii (c) && isdigit (c))
52 #define ISUPPER(c) (isascii (c) && isupper (c))
54 #ifndef HAVE_ENDGRENT
55 #define endgrent()
56 #endif
57 #ifndef HAVE_ENDPWENT
58 #define endpwent()
59 #endif
61 static boolean parse_amin PARAMS((char *argv[], int *arg_ptr));
62 static boolean parse_and PARAMS((char *argv[], int *arg_ptr));
63 static boolean parse_anewer PARAMS((char *argv[], int *arg_ptr));
64 static boolean parse_atime PARAMS((char *argv[], int *arg_ptr));
65 boolean parse_close PARAMS((char *argv[], int *arg_ptr));
66 static boolean parse_cmin PARAMS((char *argv[], int *arg_ptr));
67 static boolean parse_cnewer PARAMS((char *argv[], int *arg_ptr));
68 static boolean parse_comma PARAMS((char *argv[], int *arg_ptr));
69 static boolean parse_ctime PARAMS((char *argv[], int *arg_ptr));
70 static boolean parse_daystart PARAMS((char *argv[], int *arg_ptr));
71 static boolean parse_depth PARAMS((char *argv[], int *arg_ptr));
72 static boolean parse_empty PARAMS((char *argv[], int *arg_ptr));
73 static boolean parse_exec PARAMS((char *argv[], int *arg_ptr));
74 static boolean parse_false PARAMS((char *argv[], int *arg_ptr));
75 static boolean parse_fls PARAMS((char *argv[], int *arg_ptr));
76 static boolean parse_fprintf PARAMS((char *argv[], int *arg_ptr));
77 static boolean parse_follow PARAMS((char *argv[], int *arg_ptr));
78 static boolean parse_fprint PARAMS((char *argv[], int *arg_ptr));
79 static boolean parse_fprint0 PARAMS((char *argv[], int *arg_ptr));
80 static boolean parse_fstype PARAMS((char *argv[], int *arg_ptr));
81 static boolean parse_gid PARAMS((char *argv[], int *arg_ptr));
82 static boolean parse_group PARAMS((char *argv[], int *arg_ptr));
83 static boolean parse_help PARAMS((char *argv[], int *arg_ptr));
84 static boolean parse_ilname PARAMS((char *argv[], int *arg_ptr));
85 static boolean parse_iname PARAMS((char *argv[], int *arg_ptr));
86 static boolean parse_inum PARAMS((char *argv[], int *arg_ptr));
87 static boolean parse_ipath PARAMS((char *argv[], int *arg_ptr));
88 static boolean parse_iregex PARAMS((char *argv[], int *arg_ptr));
89 static boolean parse_iwholename PARAMS((char *argv[], int *arg_ptr));
90 static boolean parse_links PARAMS((char *argv[], int *arg_ptr));
91 static boolean parse_lname PARAMS((char *argv[], int *arg_ptr));
92 static boolean parse_ls PARAMS((char *argv[], int *arg_ptr));
93 static boolean parse_maxdepth PARAMS((char *argv[], int *arg_ptr));
94 static boolean parse_mindepth PARAMS((char *argv[], int *arg_ptr));
95 static boolean parse_mmin PARAMS((char *argv[], int *arg_ptr));
96 static boolean parse_mtime PARAMS((char *argv[], int *arg_ptr));
97 static boolean parse_name PARAMS((char *argv[], int *arg_ptr));
98 static boolean parse_negate PARAMS((char *argv[], int *arg_ptr));
99 static boolean parse_newer PARAMS((char *argv[], int *arg_ptr));
100 static boolean parse_noleaf PARAMS((char *argv[], int *arg_ptr));
101 static boolean parse_nogroup PARAMS((char *argv[], int *arg_ptr));
102 static boolean parse_nouser PARAMS((char *argv[], int *arg_ptr));
103 static boolean parse_ok PARAMS((char *argv[], int *arg_ptr));
104 boolean parse_open PARAMS((char *argv[], int *arg_ptr));
105 static boolean parse_or PARAMS((char *argv[], int *arg_ptr));
106 static boolean parse_path PARAMS((char *argv[], int *arg_ptr));
107 static boolean parse_perm PARAMS((char *argv[], int *arg_ptr));
108 boolean parse_print PARAMS((char *argv[], int *arg_ptr));
109 static boolean parse_print0 PARAMS((char *argv[], int *arg_ptr));
110 static boolean parse_printf PARAMS((char *argv[], int *arg_ptr));
111 static boolean parse_prune PARAMS((char *argv[], int *arg_ptr));
112 static boolean parse_regex PARAMS((char *argv[], int *arg_ptr));
113 static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, boolean ignore_case));
114 static boolean parse_size PARAMS((char *argv[], int *arg_ptr));
115 static boolean parse_true PARAMS((char *argv[], int *arg_ptr));
116 static boolean parse_type PARAMS((char *argv[], int *arg_ptr));
117 static boolean parse_uid PARAMS((char *argv[], int *arg_ptr));
118 static boolean parse_used PARAMS((char *argv[], int *arg_ptr));
119 static boolean parse_user PARAMS((char *argv[], int *arg_ptr));
120 static boolean parse_version PARAMS((char *argv[], int *arg_ptr));
121 static boolean parse_wholename PARAMS((char *argv[], int *arg_ptr));
122 static boolean parse_xdev PARAMS((char *argv[], int *arg_ptr));
123 static boolean parse_ignore_race PARAMS((char *argv[], int *arg_ptr));
124 static boolean parse_noignore_race PARAMS((char *argv[], int *arg_ptr));
125 static boolean parse_xtype PARAMS((char *argv[], int *arg_ptr));
127 static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, boolean ignore_case));
128 static boolean insert_type PARAMS((char *argv[], int *arg_ptr, boolean (*which_pred )()));
129 static boolean insert_fprintf PARAMS((FILE *fp, boolean (*func )(), char *argv[], int *arg_ptr));
130 static struct segment **make_segment PARAMS((struct segment **segment, char *format, int len, int kind));
131 static boolean insert_exec_ok PARAMS((boolean (*func )(), char *argv[], int *arg_ptr));
132 static boolean get_num_days PARAMS((char *str, uintmax_t *num_days, enum comparison_type *comp_type));
133 static boolean insert_time PARAMS((char *argv[], int *arg_ptr, PFB pred));
134 static boolean get_num PARAMS((char *str, uintmax_t *num, enum comparison_type *comp_type));
135 static boolean insert_num PARAMS((char *argv[], int *arg_ptr, PFB pred));
136 static FILE *open_output_file PARAMS((char *path));
138 #ifdef DEBUG
139 char *find_pred_name PARAMS((PFB pred_func));
140 #endif /* DEBUG */
142 struct parser_table
144 char *parser_name;
145 PFB parser_func;
148 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
149 If they are in some Unix versions of find, they are marked `Unix'. */
151 static struct parser_table const parse_table[] =
153 {"!", parse_negate},
154 {"not", parse_negate}, /* GNU */
155 {"(", parse_open},
156 {")", parse_close},
157 {",", parse_comma}, /* GNU */
158 {"a", parse_and},
159 {"amin", parse_amin}, /* GNU */
160 {"and", parse_and}, /* GNU */
161 {"anewer", parse_anewer}, /* GNU */
162 {"atime", parse_atime},
163 {"cmin", parse_cmin}, /* GNU */
164 {"cnewer", parse_cnewer}, /* GNU */
165 #ifdef UNIMPLEMENTED_UNIX
166 /* It's pretty ugly for find to know about archive formats.
167 Plus what it could do with cpio archives is very limited.
168 Better to leave it out. */
169 {"cpio", parse_cpio}, /* Unix */
170 #endif
171 {"ctime", parse_ctime},
172 {"daystart", parse_daystart}, /* GNU */
173 {"depth", parse_depth},
174 {"empty", parse_empty}, /* GNU */
175 {"exec", parse_exec},
176 {"false", parse_false}, /* GNU */
177 {"fls", parse_fls}, /* GNU */
178 {"follow", parse_follow}, /* GNU, Unix */
179 {"fprint", parse_fprint}, /* GNU */
180 {"fprint0", parse_fprint0}, /* GNU */
181 {"fprintf", parse_fprintf}, /* GNU */
182 {"fstype", parse_fstype}, /* GNU, Unix */
183 {"gid", parse_gid}, /* GNU */
184 {"group", parse_group},
185 {"help", parse_help}, /* GNU */
186 {"-help", parse_help}, /* GNU */
187 {"ignore_readdir_race", parse_ignore_race}, /* GNU */
188 {"ilname", parse_ilname}, /* GNU */
189 {"iname", parse_iname}, /* GNU */
190 {"inum", parse_inum}, /* GNU, Unix */
191 {"ipath", parse_ipath}, /* GNU, deprecated in favour of iwholename */
192 {"iregex", parse_iregex}, /* GNU */
193 {"iwholename", parse_iwholename}, /* GNU */
194 {"links", parse_links},
195 {"lname", parse_lname}, /* GNU */
196 {"ls", parse_ls}, /* GNU, Unix */
197 {"maxdepth", parse_maxdepth}, /* GNU */
198 {"mindepth", parse_mindepth}, /* GNU */
199 {"mmin", parse_mmin}, /* GNU */
200 {"mount", parse_xdev}, /* Unix */
201 {"mtime", parse_mtime},
202 {"name", parse_name},
203 #ifdef UNIMPLEMENTED_UNIX
204 {"ncpio", parse_ncpio}, /* Unix */
205 #endif
206 {"newer", parse_newer},
207 {"noleaf", parse_noleaf}, /* GNU */
208 {"nogroup", parse_nogroup},
209 {"nouser", parse_nouser},
210 {"noignore_readdir_race", parse_noignore_race}, /* GNU */
211 {"o", parse_or},
212 {"or", parse_or}, /* GNU */
213 {"ok", parse_ok},
214 {"path", parse_path}, /* GNU, HP-UX, GNU prefers wholename */
215 {"perm", parse_perm},
216 {"print", parse_print},
217 {"print0", parse_print0}, /* GNU */
218 {"printf", parse_printf}, /* GNU */
219 {"prune", parse_prune},
220 {"regex", parse_regex}, /* GNU */
221 {"size", parse_size},
222 {"true", parse_true}, /* GNU */
223 {"type", parse_type},
224 {"uid", parse_uid}, /* GNU */
225 {"used", parse_used}, /* GNU */
226 {"user", parse_user},
227 {"version", parse_version}, /* GNU */
228 {"-version", parse_version}, /* GNU */
229 {"wholename", parse_wholename}, /* GNU, replaces -path */
231 {"xdev", parse_xdev},
232 {"xtype", parse_xtype}, /* GNU */
233 {0, 0}
236 /* Return a pointer to the parser function to invoke for predicate
237 SEARCH_NAME.
238 Return NULL if SEARCH_NAME is not a valid predicate name. */
241 find_parser (char *search_name)
243 int i;
245 if (*search_name == '-')
246 search_name++;
247 for (i = 0; parse_table[i].parser_name != 0; i++)
248 if (strcmp (parse_table[i].parser_name, search_name) == 0)
249 return (parse_table[i].parser_func);
250 return (NULL);
253 /* The parsers are responsible to continue scanning ARGV for
254 their arguments. Each parser knows what is and isn't
255 allowed for itself.
257 ARGV is the argument array.
258 *ARG_PTR is the index to start at in ARGV,
259 updated to point beyond the last element consumed.
261 The predicate structure is updated with the new information. */
263 static boolean
264 parse_amin (char **argv, int *arg_ptr)
266 struct predicate *our_pred;
267 uintmax_t num;
268 enum comparison_type c_type;
269 time_t t;
271 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
272 return (false);
273 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
274 return (false);
275 t = cur_day_start + DAYSECS - num * 60;
276 our_pred = insert_primary (pred_amin);
277 our_pred->args.info.kind = c_type;
278 our_pred->args.info.negative = t < 0;
279 our_pred->args.info.l_val = t;
280 (*arg_ptr)++;
281 return (true);
284 static boolean
285 parse_and (char **argv, int *arg_ptr)
287 struct predicate *our_pred;
289 our_pred = get_new_pred ();
290 our_pred->pred_func = pred_and;
291 #ifdef DEBUG
292 our_pred->p_name = find_pred_name (pred_and);
293 #endif /* DEBUG */
294 our_pred->p_type = BI_OP;
295 our_pred->p_prec = AND_PREC;
296 our_pred->need_stat = false;
297 return (true);
300 static boolean
301 parse_anewer (char **argv, int *arg_ptr)
303 struct predicate *our_pred;
304 struct stat stat_newer;
306 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
307 return (false);
308 if ((*xstat) (argv[*arg_ptr], &stat_newer))
309 error (1, errno, "%s", argv[*arg_ptr]);
310 our_pred = insert_primary (pred_anewer);
311 our_pred->args.time = stat_newer.st_mtime;
312 (*arg_ptr)++;
313 return (true);
316 static boolean
317 parse_atime (char **argv, int *arg_ptr)
319 return (insert_time (argv, arg_ptr, pred_atime));
322 boolean
323 parse_close (char **argv, int *arg_ptr)
325 struct predicate *our_pred;
327 our_pred = get_new_pred ();
328 our_pred->pred_func = pred_close;
329 #ifdef DEBUG
330 our_pred->p_name = find_pred_name (pred_close);
331 #endif /* DEBUG */
332 our_pred->p_type = CLOSE_PAREN;
333 our_pred->p_prec = NO_PREC;
334 our_pred->need_stat = false;
335 return (true);
338 static boolean
339 parse_cmin (char **argv, int *arg_ptr)
341 struct predicate *our_pred;
342 uintmax_t num;
343 enum comparison_type c_type;
344 time_t t;
346 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
347 return (false);
348 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
349 return (false);
350 t = cur_day_start + DAYSECS - num * 60;
351 our_pred = insert_primary (pred_cmin);
352 our_pred->args.info.kind = c_type;
353 our_pred->args.info.negative = t < 0;
354 our_pred->args.info.l_val = t;
355 (*arg_ptr)++;
356 return (true);
359 static boolean
360 parse_cnewer (char **argv, int *arg_ptr)
362 struct predicate *our_pred;
363 struct stat stat_newer;
365 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
366 return (false);
367 if ((*xstat) (argv[*arg_ptr], &stat_newer))
368 error (1, errno, "%s", argv[*arg_ptr]);
369 our_pred = insert_primary (pred_cnewer);
370 our_pred->args.time = stat_newer.st_mtime;
371 (*arg_ptr)++;
372 return (true);
375 static boolean
376 parse_comma (char **argv, int *arg_ptr)
378 struct predicate *our_pred;
380 our_pred = get_new_pred ();
381 our_pred->pred_func = pred_comma;
382 #ifdef DEBUG
383 our_pred->p_name = find_pred_name (pred_comma);
384 #endif /* DEBUG */
385 our_pred->p_type = BI_OP;
386 our_pred->p_prec = COMMA_PREC;
387 our_pred->need_stat = false;
388 return (true);
391 static boolean
392 parse_ctime (char **argv, int *arg_ptr)
394 return (insert_time (argv, arg_ptr, pred_ctime));
397 static boolean
398 parse_daystart (char **argv, int *arg_ptr)
400 struct tm *local;
402 if (full_days == false)
404 cur_day_start += DAYSECS;
405 local = localtime (&cur_day_start);
406 cur_day_start -= (local
407 ? (local->tm_sec + local->tm_min * 60
408 + local->tm_hour * 3600)
409 : cur_day_start % DAYSECS);
410 full_days = true;
412 return (true);
415 static boolean
416 parse_depth (char **argv, int *arg_ptr)
418 do_dir_first = false;
419 return (true);
422 static boolean
423 parse_empty (char **argv, int *arg_ptr)
425 insert_primary (pred_empty);
426 return (true);
429 static boolean
430 parse_exec (char **argv, int *arg_ptr)
432 return (insert_exec_ok (pred_exec, argv, arg_ptr));
435 static boolean
436 parse_false (char **argv, int *arg_ptr)
438 struct predicate *our_pred;
440 our_pred = insert_primary (pred_false);
441 our_pred->need_stat = false;
442 return (true);
445 static boolean
446 parse_fls (char **argv, int *arg_ptr)
448 struct predicate *our_pred;
450 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
451 return (false);
452 our_pred = insert_primary (pred_fls);
453 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
454 our_pred->side_effects = true;
455 our_pred->no_default_print = true;
456 (*arg_ptr)++;
457 return (true);
460 static boolean
461 parse_fprintf (char **argv, int *arg_ptr)
463 FILE *fp;
465 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
466 return (false);
467 if (argv[*arg_ptr + 1] == NULL)
469 /* Ensure we get "missing arg" message, not "invalid arg". */
470 (*arg_ptr)++;
471 return (false);
473 fp = open_output_file (argv[*arg_ptr]);
474 (*arg_ptr)++;
475 return (insert_fprintf (fp, pred_fprintf, argv, arg_ptr));
478 static boolean
479 parse_follow (char **argv, int *arg_ptr)
481 dereference = true;
482 xstat = stat;
483 no_leaf_check = true;
484 return (true);
487 static boolean
488 parse_fprint (char **argv, int *arg_ptr)
490 struct predicate *our_pred;
492 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
493 return (false);
494 our_pred = insert_primary (pred_fprint);
495 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
496 our_pred->side_effects = true;
497 our_pred->no_default_print = true;
498 our_pred->need_stat = false;
499 (*arg_ptr)++;
500 return (true);
503 static boolean
504 parse_fprint0 (char **argv, int *arg_ptr)
506 struct predicate *our_pred;
508 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
509 return (false);
510 our_pred = insert_primary (pred_fprint0);
511 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
512 our_pred->side_effects = true;
513 our_pred->no_default_print = true;
514 our_pred->need_stat = false;
515 (*arg_ptr)++;
516 return (true);
519 static boolean
520 parse_fstype (char **argv, int *arg_ptr)
522 struct predicate *our_pred;
524 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
525 return (false);
526 our_pred = insert_primary (pred_fstype);
527 our_pred->args.str = argv[*arg_ptr];
528 (*arg_ptr)++;
529 return (true);
532 static boolean
533 parse_gid (char **argv, int *arg_ptr)
535 return (insert_num (argv, arg_ptr, pred_gid));
538 static boolean
539 parse_group (char **argv, int *arg_ptr)
541 struct group *cur_gr;
542 struct predicate *our_pred;
543 gid_t gid;
544 int gid_len;
546 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
547 return (false);
548 cur_gr = getgrnam (argv[*arg_ptr]);
549 endgrent ();
550 if (cur_gr != NULL)
551 gid = cur_gr->gr_gid;
552 else
554 gid_len = strspn (argv[*arg_ptr], "0123456789");
555 if ((gid_len == 0) || (argv[*arg_ptr][gid_len] != '\0'))
556 return (false);
557 gid = atoi (argv[*arg_ptr]);
559 our_pred = insert_primary (pred_group);
560 our_pred->args.gid = gid;
561 (*arg_ptr)++;
562 return (true);
565 static boolean
566 parse_help (char **argv, int *arg_ptr)
568 printf (_("\
569 Usage: %s [path...] [expression]\n"), program_name);
570 puts (_("\
571 default path is the current directory; default expression is -print\n\
572 expression may consist of:\n\
573 operators (decreasing precedence; -and is implicit where no others are given):\n\
574 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n"));
575 puts (_("\
576 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n\
577 options (always true): -daystart -depth -follow --help\n\
578 -maxdepth LEVELS -mindepth LEVELS -mount -noleaf --version -xdev\n\
579 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N"));
580 puts (_("\
581 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
582 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
583 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
584 puts (_("\
585 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
586 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
587 -used N -user NAME -xtype [bcdpfls]\n"));
588 puts (_("\
589 actions: -exec COMMAND ; -fprint FILE -fprint0 FILE -fprintf FILE FORMAT\n\
590 -ok COMMAND ; -print -print0 -printf FORMAT -prune -ls\n"));
591 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
592 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
593 email to <bug-findutils@gnu.org>."));
594 exit (0);
597 static boolean
598 parse_ilname (char **argv, int *arg_ptr)
600 struct predicate *our_pred;
602 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
603 return (false);
604 our_pred = insert_primary (pred_ilname);
605 our_pred->args.str = argv[*arg_ptr];
606 (*arg_ptr)++;
607 return (true);
611 /* sanity check the fnmatch() function to make sure
612 * it really is the GNU version.
614 static boolean
615 fnmatch_sanitycheck()
617 if (0 != fnmatch("foo", "foo", 0)
618 || 0 == fnmatch("Foo", "foo", 0)
619 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD))
621 error (1, 0, _("sanity check of the fnmatch() library function failed."));
622 return false;
625 return true;
630 static boolean
631 parse_iname (char **argv, int *arg_ptr)
633 struct predicate *our_pred;
635 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
636 return (false);
638 fnmatch_sanitycheck();
640 our_pred = insert_primary (pred_iname);
641 our_pred->need_stat = false;
642 our_pred->args.str = argv[*arg_ptr];
643 (*arg_ptr)++;
644 return (true);
647 static boolean
648 parse_inum (char **argv, int *arg_ptr)
650 return (insert_num (argv, arg_ptr, pred_inum));
653 /* -ipath is deprecated (at RMS's request) in favour of
654 * -iwholename. See the node "GNU Manuals" in standards.texi
655 * for the rationale for this (basically, GNU prefers the use
656 * of the phrase "file name" to "path name"
658 static boolean
659 parse_ipath (char **argv, int *arg_ptr)
661 error (0, 0,
662 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
664 return parse_iwholename(argv, arg_ptr);
667 static boolean
668 parse_iwholename (char **argv, int *arg_ptr)
670 struct predicate *our_pred;
672 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
673 return (false);
675 fnmatch_sanitycheck();
677 our_pred = insert_primary (pred_ipath);
678 our_pred->need_stat = false;
679 our_pred->args.str = argv[*arg_ptr];
680 (*arg_ptr)++;
681 return (true);
684 static boolean
685 parse_iregex (char **argv, int *arg_ptr)
687 return insert_regex (argv, arg_ptr, true);
690 static boolean
691 parse_links (char **argv, int *arg_ptr)
693 return (insert_num (argv, arg_ptr, pred_links));
696 static boolean
697 parse_lname (char **argv, int *arg_ptr)
699 struct predicate *our_pred;
701 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
702 return (false);
704 fnmatch_sanitycheck();
706 our_pred = insert_primary (pred_lname);
707 our_pred->args.str = argv[*arg_ptr];
708 (*arg_ptr)++;
709 return (true);
712 static boolean
713 parse_ls (char **argv, int *arg_ptr)
715 struct predicate *our_pred;
717 our_pred = insert_primary (pred_ls);
718 our_pred->side_effects = true;
719 our_pred->no_default_print = true;
720 return (true);
723 static boolean
724 parse_maxdepth (char **argv, int *arg_ptr)
726 int depth_len;
728 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
729 return (false);
730 depth_len = strspn (argv[*arg_ptr], "0123456789");
731 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
732 return (false);
733 maxdepth = atoi (argv[*arg_ptr]);
734 if (maxdepth < 0)
735 return (false);
736 (*arg_ptr)++;
737 return (true);
740 static boolean
741 parse_mindepth (char **argv, int *arg_ptr)
743 int depth_len;
745 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
746 return (false);
747 depth_len = strspn (argv[*arg_ptr], "0123456789");
748 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
749 return (false);
750 mindepth = atoi (argv[*arg_ptr]);
751 if (mindepth < 0)
752 return (false);
753 (*arg_ptr)++;
754 return (true);
757 static boolean
758 parse_mmin (char **argv, int *arg_ptr)
760 struct predicate *our_pred;
761 uintmax_t num;
762 enum comparison_type c_type;
763 time_t t;
765 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
766 return (false);
767 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
768 return (false);
769 t = cur_day_start + DAYSECS - num * 60;
770 our_pred = insert_primary (pred_mmin);
771 our_pred->args.info.kind = c_type;
772 our_pred->args.info.negative = t < 0;
773 our_pred->args.info.l_val = t;
774 (*arg_ptr)++;
775 return (true);
778 static boolean
779 parse_mtime (char **argv, int *arg_ptr)
781 return (insert_time (argv, arg_ptr, pred_mtime));
784 static boolean
785 parse_name (char **argv, int *arg_ptr)
787 struct predicate *our_pred;
789 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
790 return (false);
791 our_pred = insert_primary (pred_name);
792 our_pred->need_stat = false;
793 our_pred->args.str = argv[*arg_ptr];
794 (*arg_ptr)++;
795 return (true);
798 static boolean
799 parse_negate (char **argv, int *arg_ptr)
801 struct predicate *our_pred;
803 our_pred = get_new_pred_chk_op ();
804 our_pred->pred_func = pred_negate;
805 #ifdef DEBUG
806 our_pred->p_name = find_pred_name (pred_negate);
807 #endif /* DEBUG */
808 our_pred->p_type = UNI_OP;
809 our_pred->p_prec = NEGATE_PREC;
810 our_pred->need_stat = false;
811 return (true);
814 static boolean
815 parse_newer (char **argv, int *arg_ptr)
817 struct predicate *our_pred;
818 struct stat stat_newer;
820 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
821 return (false);
822 if ((*xstat) (argv[*arg_ptr], &stat_newer))
823 error (1, errno, "%s", argv[*arg_ptr]);
824 our_pred = insert_primary (pred_newer);
825 our_pred->args.time = stat_newer.st_mtime;
826 (*arg_ptr)++;
827 return (true);
830 static boolean
831 parse_noleaf (char **argv, int *arg_ptr)
833 no_leaf_check = true;
834 return true;
837 #ifdef CACHE_IDS
838 /* Arbitrary amount by which to increase size
839 of `uid_unused' and `gid_unused'. */
840 #define ALLOC_STEP 2048
842 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
843 char *uid_unused = NULL;
845 /* Number of elements in `uid_unused'. */
846 unsigned uid_allocated;
848 /* Similar for GIDs and group entries. */
849 char *gid_unused = NULL;
850 unsigned gid_allocated;
851 #endif
853 static boolean
854 parse_nogroup (char **argv, int *arg_ptr)
856 struct predicate *our_pred;
858 our_pred = insert_primary (pred_nogroup);
859 #ifdef CACHE_IDS
860 if (gid_unused == NULL)
862 struct group *gr;
864 gid_allocated = ALLOC_STEP;
865 gid_unused = xmalloc (gid_allocated);
866 memset (gid_unused, 1, gid_allocated);
867 setgrent ();
868 while ((gr = getgrent ()) != NULL)
870 if ((unsigned) gr->gr_gid >= gid_allocated)
872 unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP;
873 gid_unused = xrealloc (gid_unused, new_allocated);
874 memset (gid_unused + gid_allocated, 1,
875 new_allocated - gid_allocated);
876 gid_allocated = new_allocated;
878 gid_unused[(unsigned) gr->gr_gid] = 0;
880 endgrent ();
882 #endif
883 return (true);
886 static boolean
887 parse_nouser (char **argv, int *arg_ptr)
889 struct predicate *our_pred;
891 our_pred = insert_primary (pred_nouser);
892 #ifdef CACHE_IDS
893 if (uid_unused == NULL)
895 struct passwd *pw;
897 uid_allocated = ALLOC_STEP;
898 uid_unused = xmalloc (uid_allocated);
899 memset (uid_unused, 1, uid_allocated);
900 setpwent ();
901 while ((pw = getpwent ()) != NULL)
903 if ((unsigned) pw->pw_uid >= uid_allocated)
905 unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP;
906 uid_unused = xrealloc (uid_unused, new_allocated);
907 memset (uid_unused + uid_allocated, 1,
908 new_allocated - uid_allocated);
909 uid_allocated = new_allocated;
911 uid_unused[(unsigned) pw->pw_uid] = 0;
913 endpwent ();
915 #endif
916 return (true);
919 static boolean
920 parse_ok (char **argv, int *arg_ptr)
922 return (insert_exec_ok (pred_ok, argv, arg_ptr));
925 boolean
926 parse_open (char **argv, int *arg_ptr)
928 struct predicate *our_pred;
930 our_pred = get_new_pred_chk_op ();
931 our_pred->pred_func = pred_open;
932 #ifdef DEBUG
933 our_pred->p_name = find_pred_name (pred_open);
934 #endif /* DEBUG */
935 our_pred->p_type = OPEN_PAREN;
936 our_pred->p_prec = NO_PREC;
937 our_pred->need_stat = false;
938 return (true);
941 static boolean
942 parse_or (char **argv, int *arg_ptr)
944 struct predicate *our_pred;
946 our_pred = get_new_pred ();
947 our_pred->pred_func = pred_or;
948 #ifdef DEBUG
949 our_pred->p_name = find_pred_name (pred_or);
950 #endif /* DEBUG */
951 our_pred->p_type = BI_OP;
952 our_pred->p_prec = OR_PREC;
953 our_pred->need_stat = false;
954 return (true);
957 /* -path is deprecated (at RMS's request) in favour of
958 * -iwholename. See the node "GNU Manuals" in standards.texi
959 * for the rationale for this (basically, GNU prefers the use
960 * of the phrase "file name" to "path name".
962 * We do not issue a warning that this usage is deprecated
963 * since HPUX find supports this predicate also.
965 static boolean
966 parse_path (char **argv, int *arg_ptr)
968 return parse_wholename(argv, arg_ptr);
971 static boolean
972 parse_wholename (char **argv, int *arg_ptr)
974 struct predicate *our_pred;
976 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
977 return (false);
978 our_pred = insert_primary (pred_path);
979 our_pred->need_stat = false;
980 our_pred->args.str = argv[*arg_ptr];
981 (*arg_ptr)++;
982 return (true);
985 static boolean
986 parse_perm (char **argv, int *arg_ptr)
988 mode_t perm_val;
989 int mode_start = 0;
990 struct mode_change *change;
991 struct predicate *our_pred;
993 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
994 return (false);
996 switch (argv[*arg_ptr][0])
998 case '-':
999 case '+':
1000 mode_start = 1;
1001 break;
1002 default:
1003 /* empty */
1004 break;
1007 change = mode_compile (argv[*arg_ptr] + mode_start, MODE_MASK_PLUS);
1008 if (change == MODE_INVALID)
1009 error (1, 0, _("invalid mode `%s'"), argv[*arg_ptr]);
1010 else if (change == MODE_MEMORY_EXHAUSTED)
1011 error (1, 0, _("virtual memory exhausted"));
1012 perm_val = mode_adjust (0, change);
1013 mode_free (change);
1015 our_pred = insert_primary (pred_perm);
1017 switch (argv[*arg_ptr][0])
1019 case '-':
1020 our_pred->args.perm.kind = PERM_AT_LEAST;
1021 break;
1022 case '+':
1023 our_pred->args.perm.kind = PERM_ANY;
1024 break;
1025 default:
1026 our_pred->args.perm.kind = PERM_EXACT;
1027 break;
1029 our_pred->args.perm.val = perm_val & MODE_ALL;
1030 (*arg_ptr)++;
1031 return (true);
1034 boolean
1035 parse_print (char **argv, int *arg_ptr)
1037 struct predicate *our_pred;
1039 our_pred = insert_primary (pred_print);
1040 /* -print has the side effect of printing. This prevents us
1041 from doing undesired multiple printing when the user has
1042 already specified -print. */
1043 our_pred->side_effects = true;
1044 our_pred->no_default_print = true;
1045 our_pred->need_stat = false;
1046 return (true);
1049 static boolean
1050 parse_print0 (char **argv, int *arg_ptr)
1052 struct predicate *our_pred;
1054 our_pred = insert_primary (pred_print0);
1055 /* -print0 has the side effect of printing. This prevents us
1056 from doing undesired multiple printing when the user has
1057 already specified -print0. */
1058 our_pred->side_effects = true;
1059 our_pred->no_default_print = true;
1060 our_pred->need_stat = false;
1061 return (true);
1064 static boolean
1065 parse_printf (char **argv, int *arg_ptr)
1067 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1068 return (false);
1069 return (insert_fprintf (stdout, pred_fprintf, argv, arg_ptr));
1072 static boolean
1073 parse_prune (char **argv, int *arg_ptr)
1075 struct predicate *our_pred;
1077 our_pred = insert_primary (pred_prune);
1078 our_pred->need_stat = false;
1079 /* -prune has a side effect that it does not descend into
1080 the current directory. */
1081 our_pred->side_effects = true;
1082 return (true);
1085 static boolean
1086 parse_regex (char **argv, int *arg_ptr)
1088 return insert_regex (argv, arg_ptr, false);
1091 static boolean
1092 insert_regex (char **argv, int *arg_ptr, boolean ignore_case)
1094 struct predicate *our_pred;
1095 struct re_pattern_buffer *re;
1096 const char *error_message;
1098 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1099 return (false);
1100 our_pred = insert_primary (pred_regex);
1101 our_pred->need_stat = false;
1102 re = (struct re_pattern_buffer *)
1103 xmalloc (sizeof (struct re_pattern_buffer));
1104 our_pred->args.regex = re;
1105 re->allocated = 100;
1106 re->buffer = (unsigned char *) xmalloc (re->allocated);
1107 re->fastmap = NULL;
1109 if (ignore_case)
1111 unsigned i;
1113 re->translate = xmalloc (256);
1114 /* Map uppercase characters to corresponding lowercase ones. */
1115 for (i = 0; i < 256; i++)
1116 re->translate[i] = ISUPPER (i) ? tolower (i) : i;
1118 else
1119 re->translate = NULL;
1121 error_message = re_compile_pattern (argv[*arg_ptr], strlen (argv[*arg_ptr]),
1122 re);
1123 if (error_message)
1124 error (1, 0, "%s", error_message);
1125 (*arg_ptr)++;
1126 return (true);
1129 static boolean
1130 parse_size (char **argv, int *arg_ptr)
1132 struct predicate *our_pred;
1133 uintmax_t num;
1134 enum comparison_type c_type;
1135 int blksize = 512;
1136 int len;
1138 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1139 return (false);
1140 len = strlen (argv[*arg_ptr]);
1141 if (len == 0)
1142 error (1, 0, _("invalid null argument to -size"));
1143 switch (argv[*arg_ptr][len - 1])
1145 case 'b':
1146 blksize = 512;
1147 argv[*arg_ptr][len - 1] = '\0';
1148 break;
1150 case 'c':
1151 blksize = 1;
1152 argv[*arg_ptr][len - 1] = '\0';
1153 break;
1155 case 'k':
1156 blksize = 1024;
1157 argv[*arg_ptr][len - 1] = '\0';
1158 break;
1160 case 'M': /* Megabytes */
1161 blksize = 1024*1024;
1162 argv[*arg_ptr][len - 1] = '\0';
1163 break;
1165 case 'G': /* Gigabytes */
1166 blksize = 1024*1024*1024;
1167 argv[*arg_ptr][len - 1] = '\0';
1168 break;
1170 case 'w':
1171 blksize = 2;
1172 argv[*arg_ptr][len - 1] = '\0';
1173 break;
1175 case '0':
1176 case '1':
1177 case '2':
1178 case '3':
1179 case '4':
1180 case '5':
1181 case '6':
1182 case '7':
1183 case '8':
1184 case '9':
1185 break;
1187 default:
1188 error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]);
1190 if (!get_num (argv[*arg_ptr], &num, &c_type))
1191 return (false);
1192 our_pred = insert_primary (pred_size);
1193 our_pred->args.size.kind = c_type;
1194 our_pred->args.size.blocksize = blksize;
1195 our_pred->args.size.size = num;
1196 (*arg_ptr)++;
1197 return (true);
1200 static boolean
1201 parse_true (char **argv, int *arg_ptr)
1203 struct predicate *our_pred;
1205 our_pred = insert_primary (pred_true);
1206 our_pred->need_stat = false;
1207 return (true);
1210 static boolean
1211 parse_type (char **argv, int *arg_ptr)
1213 return insert_type (argv, arg_ptr, pred_type);
1216 static boolean
1217 parse_uid (char **argv, int *arg_ptr)
1219 return (insert_num (argv, arg_ptr, pred_uid));
1222 static boolean
1223 parse_used (char **argv, int *arg_ptr)
1225 struct predicate *our_pred;
1226 uintmax_t num_days;
1227 enum comparison_type c_type;
1228 time_t t;
1230 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1231 return (false);
1232 if (!get_num (argv[*arg_ptr], &num_days, &c_type))
1233 return (false);
1234 t = num_days * DAYSECS;
1235 our_pred = insert_primary (pred_used);
1236 our_pred->args.info.kind = c_type;
1237 our_pred->args.info.negative = t < 0;
1238 our_pred->args.info.l_val = t;
1239 (*arg_ptr)++;
1240 return (true);
1243 static boolean
1244 parse_user (char **argv, int *arg_ptr)
1246 struct passwd *cur_pwd;
1247 struct predicate *our_pred;
1248 uid_t uid;
1249 int uid_len;
1251 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1252 return (false);
1253 cur_pwd = getpwnam (argv[*arg_ptr]);
1254 endpwent ();
1255 if (cur_pwd != NULL)
1256 uid = cur_pwd->pw_uid;
1257 else
1259 uid_len = strspn (argv[*arg_ptr], "0123456789");
1260 if ((uid_len == 0) || (argv[*arg_ptr][uid_len] != '\0'))
1261 return (false);
1262 uid = atoi (argv[*arg_ptr]);
1264 our_pred = insert_primary (pred_user);
1265 our_pred->args.uid = uid;
1266 (*arg_ptr)++;
1267 return (true);
1270 static boolean
1271 parse_version (char **argv, int *arg_ptr)
1273 extern char *version_string;
1275 fflush (stderr);
1276 printf (_("GNU find version %s\n"), version_string);
1277 exit (0);
1280 static boolean
1281 parse_xdev (char **argv, int *arg_ptr)
1283 stay_on_filesystem = true;
1284 return true;
1287 static boolean
1288 parse_ignore_race (char **argv, int *arg_ptr)
1290 ignore_readdir_race = true;
1291 return true;
1294 static boolean
1295 parse_noignore_race (char **argv, int *arg_ptr)
1297 ignore_readdir_race = false;
1298 return true;
1301 static boolean
1302 parse_xtype (char **argv, int *arg_ptr)
1304 return insert_type (argv, arg_ptr, pred_xtype);
1307 static boolean
1308 insert_type (char **argv, int *arg_ptr, boolean (*which_pred) (/* ??? */))
1310 mode_t type_cell;
1311 struct predicate *our_pred;
1313 if ((argv == NULL) || (argv[*arg_ptr] == NULL)
1314 || (strlen (argv[*arg_ptr]) != 1))
1315 return (false);
1316 switch (argv[*arg_ptr][0])
1318 case 'b': /* block special */
1319 type_cell = S_IFBLK;
1320 break;
1321 case 'c': /* character special */
1322 type_cell = S_IFCHR;
1323 break;
1324 case 'd': /* directory */
1325 type_cell = S_IFDIR;
1326 break;
1327 case 'f': /* regular file */
1328 type_cell = S_IFREG;
1329 break;
1330 #ifdef S_IFLNK
1331 case 'l': /* symbolic link */
1332 type_cell = S_IFLNK;
1333 break;
1334 #endif
1335 #ifdef S_IFIFO
1336 case 'p': /* pipe */
1337 type_cell = S_IFIFO;
1338 break;
1339 #endif
1340 #ifdef S_IFSOCK
1341 case 's': /* socket */
1342 type_cell = S_IFSOCK;
1343 break;
1344 #endif
1345 #ifdef S_IFDOOR
1346 case 'D': /* Solaris door */
1347 type_cell = S_IFDOOR;
1348 break;
1349 #endif
1350 default: /* None of the above ... nuke 'em. */
1351 return (false);
1353 our_pred = insert_primary (which_pred);
1354 our_pred->args.type = type_cell;
1355 (*arg_ptr)++; /* Move on to next argument. */
1356 return (true);
1359 /* If true, we've determined that the current fprintf predicate
1360 uses stat information. */
1361 static boolean fprintf_stat_needed;
1363 static boolean
1364 insert_fprintf (FILE *fp, boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
1366 char *format; /* Beginning of unprocessed format string. */
1367 register char *scan; /* Current address in scanning `format'. */
1368 register char *scan2; /* Address inside of element being scanned. */
1369 struct segment **segmentp; /* Address of current segment. */
1370 struct predicate *our_pred;
1372 format = argv[(*arg_ptr)++];
1374 fprintf_stat_needed = false; /* Might be overridden later. */
1375 our_pred = insert_primary (func);
1376 our_pred->side_effects = true;
1377 our_pred->no_default_print = true;
1378 our_pred->args.printf_vec.stream = fp;
1379 segmentp = &our_pred->args.printf_vec.segment;
1380 *segmentp = NULL;
1382 for (scan = format; *scan; scan++)
1384 if (*scan == '\\')
1386 scan2 = scan + 1;
1387 if (*scan2 >= '0' && *scan2 <= '7')
1389 register int n, i;
1391 for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7');
1392 i++, scan2++)
1393 n = 8 * n + *scan2 - '0';
1394 scan2--;
1395 *scan = n;
1397 else
1399 switch (*scan2)
1401 case 'a':
1402 *scan = 7;
1403 break;
1404 case 'b':
1405 *scan = '\b';
1406 break;
1407 case 'c':
1408 make_segment (segmentp, format, scan - format, KIND_STOP);
1409 our_pred->need_stat = fprintf_stat_needed;
1410 return (true);
1411 case 'f':
1412 *scan = '\f';
1413 break;
1414 case 'n':
1415 *scan = '\n';
1416 break;
1417 case 'r':
1418 *scan = '\r';
1419 break;
1420 case 't':
1421 *scan = '\t';
1422 break;
1423 case 'v':
1424 *scan = '\v';
1425 break;
1426 case '\\':
1427 /* *scan = '\\'; * it already is */
1428 break;
1429 default:
1430 error (0, 0,
1431 _("warning: unrecognized escape `\\%c'"), *scan2);
1432 scan++;
1433 continue;
1436 segmentp = make_segment (segmentp, format, scan - format + 1,
1437 KIND_PLAIN);
1438 format = scan2 + 1; /* Move past the escape. */
1439 scan = scan2; /* Incremented immediately by `for'. */
1441 else if (*scan == '%')
1443 if (scan[1] == '%')
1445 segmentp = make_segment (segmentp, format, scan - format + 1,
1446 KIND_PLAIN);
1447 scan++;
1448 format = scan + 1;
1449 continue;
1451 /* Scan past flags, width and precision, to verify kind. */
1452 for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);)
1453 /* Do nothing. */ ;
1454 while (ISDIGIT (*scan2))
1455 scan2++;
1456 if (*scan2 == '.')
1457 for (scan2++; ISDIGIT (*scan2); scan2++)
1458 /* Do nothing. */ ;
1459 if (strchr ("abcdfFgGhHiklmnpPstuU", *scan2))
1461 segmentp = make_segment (segmentp, format, scan2 - format,
1462 (int) *scan2);
1463 scan = scan2;
1464 format = scan + 1;
1466 else if (strchr ("ACT", *scan2) && scan2[1])
1468 segmentp = make_segment (segmentp, format, scan2 - format,
1469 *scan2 | (scan2[1] << 8));
1470 scan = scan2 + 1;
1471 format = scan + 1;
1472 continue;
1474 else
1476 /* An unrecognized % escape. Print the char after the %. */
1477 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1478 *scan2);
1479 segmentp = make_segment (segmentp, format, scan - format,
1480 KIND_PLAIN);
1481 format = scan + 1;
1482 continue;
1487 if (scan > format)
1488 make_segment (segmentp, format, scan - format, KIND_PLAIN);
1489 our_pred->need_stat = fprintf_stat_needed;
1490 return (true);
1493 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1494 from the text in FORMAT, which has length LEN.
1495 Return the address of the `next' pointer of the new segment. */
1497 static struct segment **
1498 make_segment (struct segment **segment, char *format, int len, int kind)
1500 char *fmt;
1502 *segment = (struct segment *) xmalloc (sizeof (struct segment));
1504 (*segment)->kind = kind;
1505 (*segment)->next = NULL;
1506 (*segment)->text_len = len;
1508 fmt = (*segment)->text = xmalloc (len + sizeof "d");
1509 strncpy (fmt, format, len);
1510 fmt += len;
1512 switch (kind & 0xff)
1514 case KIND_PLAIN: /* Plain text string, no % conversion. */
1515 case KIND_STOP: /* Terminate argument, no newline. */
1516 break;
1518 case 'a': /* atime in `ctime' format */
1519 case 'A': /* atime in user-specified strftime format */
1520 case 'b': /* size in 512-byte blocks */
1521 case 'c': /* ctime in `ctime' format */
1522 case 'C': /* ctime in user-specified strftime format */
1523 case 'F': /* filesystem type */
1524 case 'G': /* GID number */
1525 case 'g': /* group name */
1526 case 'i': /* inode number */
1527 case 'k': /* size in 1K blocks */
1528 case 'l': /* object of symlink */
1529 case 'n': /* number of links */
1530 case 's': /* size in bytes */
1531 case 't': /* mtime in `ctime' format */
1532 case 'T': /* mtime in user-specified strftime format */
1533 case 'U': /* UID number */
1534 case 'u': /* user name */
1535 fprintf_stat_needed = true;
1536 /* FALLTHROUGH */
1537 case 'f': /* basename of path */
1538 case 'h': /* leading directories part of path */
1539 case 'H': /* ARGV element file was found under */
1540 case 'p': /* pathname */
1541 case 'P': /* pathname with ARGV element stripped */
1542 *fmt++ = 's';
1543 break;
1545 case 'd': /* depth in search tree (0 = ARGV element) */
1546 *fmt++ = 'd';
1547 break;
1549 case 'm': /* mode as octal number (perms only) */
1550 *fmt++ = 'o';
1551 fprintf_stat_needed = true;
1552 break;
1554 *fmt = '\0';
1556 return (&(*segment)->next);
1559 /* handles both exec and ok predicate */
1560 static boolean
1561 insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
1563 int start, end; /* Indexes in ARGV of start & end of cmd. */
1564 int num_paths; /* Number of args with path replacements. */
1565 int path_pos; /* Index in array of path replacements. */
1566 int vec_pos; /* Index in array of args. */
1567 struct predicate *our_pred;
1568 struct exec_val *execp; /* Pointer for efficiency. */
1570 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1571 return (false);
1573 /* Count the number of args with path replacements, up until the ';'. */
1574 start = *arg_ptr;
1575 for (end = start, num_paths = 0;
1576 (argv[end] != NULL)
1577 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
1578 end++)
1579 if (strstr (argv[end], "{}"))
1580 num_paths++;
1581 /* Fail if no command given or no semicolon found. */
1582 if ((end == start) || (argv[end] == NULL))
1584 *arg_ptr = end;
1585 return (false);
1588 our_pred = insert_primary (func);
1589 our_pred->side_effects = true;
1590 our_pred->no_default_print = true;
1591 execp = &our_pred->args.exec_vec;
1592 execp->paths =
1593 (struct path_arg *) xmalloc (sizeof (struct path_arg) * (num_paths + 1));
1594 execp->vec = (char **) xmalloc (sizeof (char *) * (end - start + 1));
1595 /* Record the positions of all args, and the args with path replacements. */
1596 for (end = start, path_pos = vec_pos = 0;
1597 (argv[end] != NULL)
1598 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
1599 end++)
1601 register char *p;
1603 execp->paths[path_pos].count = 0;
1604 for (p = argv[end]; *p; ++p)
1605 if (p[0] == '{' && p[1] == '}')
1607 execp->paths[path_pos].count++;
1608 ++p;
1610 if (execp->paths[path_pos].count)
1612 execp->paths[path_pos].offset = vec_pos;
1613 execp->paths[path_pos].origarg = argv[end];
1614 path_pos++;
1616 execp->vec[vec_pos++] = argv[end];
1618 execp->paths[path_pos].offset = -1;
1619 execp->vec[vec_pos] = NULL;
1621 if (argv[end] == NULL)
1622 *arg_ptr = end;
1623 else
1624 *arg_ptr = end + 1;
1625 return (true);
1628 /* Get a number of days and comparison type.
1629 STR is the ASCII representation.
1630 Set *NUM_DAYS to the number of days, taken as being from
1631 the current moment (or possibly midnight). Thus the sense of the
1632 comparison type appears to be reversed.
1633 Set *COMP_TYPE to the kind of comparison that is requested.
1635 Return true if all okay, false if input error.
1637 Used by -atime, -ctime and -mtime (parsers) to
1638 get the appropriate information for a time predicate processor. */
1640 static boolean
1641 get_num_days (char *str, uintmax_t *num_days, enum comparison_type *comp_type)
1643 boolean r = get_num (str, num_days, comp_type);
1644 if (r)
1645 switch (*comp_type)
1647 case COMP_LT: *comp_type = COMP_GT; break;
1648 case COMP_GT: *comp_type = COMP_LT; break;
1649 default: break;
1651 return r;
1654 /* Insert a time predicate PRED.
1655 ARGV is a pointer to the argument array.
1656 ARG_PTR is a pointer to an index into the array, incremented if
1657 all went well.
1659 Return true if input is valid, false if not.
1661 A new predicate node is assigned, along with an argument node
1662 obtained with malloc.
1664 Used by -atime, -ctime, and -mtime parsers. */
1666 static boolean
1667 insert_time (char **argv, int *arg_ptr, PFB pred)
1669 struct predicate *our_pred;
1670 uintmax_t num_days;
1671 enum comparison_type c_type;
1672 time_t t;
1674 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1675 return (false);
1676 if (!get_num_days (argv[*arg_ptr], &num_days, &c_type))
1677 return (false);
1679 /* Figure out the timestamp value we are looking for. */
1680 t = ( cur_day_start - num_days * DAYSECS
1681 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
1682 #if 1
1683 if (1)
1685 /* We introduce a scope in which 'val' can be declared, for the
1686 * benefit of compilers that are really C89 compilers
1687 * which support intmax_t because config.h #defines it
1689 intmax_t val = ( (intmax_t)cur_day_start - num_days * DAYSECS
1690 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
1691 t = val;
1693 /* Check for possibility of an overflow */
1694 if ( (intmax_t)t != val )
1696 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv[*arg_ptr]);
1699 #endif
1701 our_pred = insert_primary (pred);
1702 our_pred->args.info.kind = c_type;
1703 our_pred->args.info.negative = t < 0;
1704 our_pred->args.info.l_val = t;
1705 (*arg_ptr)++;
1706 #ifdef DEBUG
1707 printf (_("inserting %s\n"), our_pred->p_name);
1708 printf (_(" type: %s %s "),
1709 (c_type == COMP_GT) ? "gt" :
1710 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
1711 (c_type == COMP_GT) ? " >" :
1712 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? ">=" : " ?")));
1713 t = our_pred->args.info.l_val;
1714 printf ("%ju %s", (uintmax_t) our_pred->args.info.l_val, ctime (&t));
1715 if (c_type == COMP_EQ)
1717 t = our_pred->args.info.l_val += DAYSECS;
1718 printf (" < %ju %s",
1719 (uintmax_t) our_pred->args.info.l_val, ctime (&t));
1720 our_pred->args.info.l_val -= DAYSECS;
1722 #endif /* DEBUG */
1723 return (true);
1726 /* Get a number with comparision information.
1727 The sense of the comparision information is 'normal'; that is,
1728 '+' looks for a count > than the number and '-' less than.
1730 STR is the ASCII representation of the number.
1731 Set *NUM to the number.
1732 Set *COMP_TYPE to the kind of comparison that is requested.
1734 Return true if all okay, false if input error. */
1736 static boolean
1737 get_num (char *str, uintmax_t *num, enum comparison_type *comp_type)
1739 int len_num; /* Length of field. */
1741 if (str == NULL)
1742 return (false);
1743 switch (str[0])
1745 case '+':
1746 *comp_type = COMP_GT;
1747 str++;
1748 break;
1749 case '-':
1750 *comp_type = COMP_LT;
1751 str++;
1752 break;
1753 default:
1754 *comp_type = COMP_EQ;
1755 break;
1758 return xstrtoumax (str, NULL, 10, num, "") == LONGINT_OK;
1761 /* Insert a number predicate.
1762 ARGV is a pointer to the argument array.
1763 *ARG_PTR is an index into ARGV, incremented if all went well.
1764 *PRED is the predicate processor to insert.
1766 Return true if input is valid, false if error.
1768 A new predicate node is assigned, along with an argument node
1769 obtained with malloc.
1771 Used by -inum and -links parsers. */
1773 static boolean
1774 insert_num (char **argv, int *arg_ptr, PFB pred)
1776 struct predicate *our_pred;
1777 uintmax_t num;
1778 enum comparison_type c_type;
1780 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1781 return (false);
1782 if (!get_num (argv[*arg_ptr], &num, &c_type))
1783 return (false);
1784 our_pred = insert_primary (pred);
1785 our_pred->args.info.kind = c_type;
1786 our_pred->args.info.l_val = num;
1787 (*arg_ptr)++;
1788 #ifdef DEBUG
1789 printf (_("inserting %s\n"), our_pred->p_name);
1790 printf (_(" type: %s %s "),
1791 (c_type == COMP_GT) ? "gt" :
1792 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
1793 (c_type == COMP_GT) ? " >" :
1794 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?")));
1795 printf ("%ju\n", our_pred->args.info.l_val);
1796 #endif /* DEBUG */
1797 return (true);
1800 static FILE *
1801 open_output_file (char *path)
1803 FILE *f;
1805 if (!strcmp (path, "/dev/stderr"))
1806 return (stderr);
1807 else if (!strcmp (path, "/dev/stdout"))
1808 return (stdout);
1809 f = fopen (path, "w");
1810 if (f == NULL)
1811 error (1, errno, "%s", path);
1812 return (f);