1 /* parser.c -- convert the command line args into an expression tree.
2 Copyright (C) 1990, 91, 92, 93, 94, 2000, 2001, 2003, 2004 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)
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
26 #include "../gnulib/lib/modechange.h"
28 #include "../gnulib/lib/xstrtol.h"
29 #include "../gnulib/lib/xalloc.h"
36 # define _(Text) gettext (Text)
41 # define N_(String) gettext_noop (String)
43 /* See locate.c for explanation as to why not use (String) */
44 # define N_(String) String
47 #if !defined (isascii) || defined (STDC_HEADERS)
54 #define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
55 #define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
64 static boolean parse_amin
PARAMS((char *argv
[], int *arg_ptr
));
65 static boolean parse_and
PARAMS((char *argv
[], int *arg_ptr
));
66 static boolean parse_anewer
PARAMS((char *argv
[], int *arg_ptr
));
67 static boolean parse_atime
PARAMS((char *argv
[], int *arg_ptr
));
68 boolean parse_close
PARAMS((char *argv
[], int *arg_ptr
));
69 static boolean parse_cmin
PARAMS((char *argv
[], int *arg_ptr
));
70 static boolean parse_cnewer
PARAMS((char *argv
[], int *arg_ptr
));
71 static boolean parse_comma
PARAMS((char *argv
[], int *arg_ptr
));
72 static boolean parse_ctime
PARAMS((char *argv
[], int *arg_ptr
));
73 static boolean parse_daystart
PARAMS((char *argv
[], int *arg_ptr
));
74 static boolean parse_delete
PARAMS((char *argv
[], int *arg_ptr
));
75 static boolean parse_d
PARAMS((char *argv
[], int *arg_ptr
));
76 static boolean parse_depth
PARAMS((char *argv
[], int *arg_ptr
));
77 static boolean parse_empty
PARAMS((char *argv
[], int *arg_ptr
));
78 static boolean parse_exec
PARAMS((char *argv
[], int *arg_ptr
));
79 static boolean parse_execdir
PARAMS((char *argv
[], int *arg_ptr
));
80 static boolean parse_false
PARAMS((char *argv
[], int *arg_ptr
));
81 static boolean parse_fls
PARAMS((char *argv
[], int *arg_ptr
));
82 static boolean parse_fprintf
PARAMS((char *argv
[], int *arg_ptr
));
83 static boolean parse_follow
PARAMS((char *argv
[], int *arg_ptr
));
84 static boolean parse_fprint
PARAMS((char *argv
[], int *arg_ptr
));
85 static boolean parse_fprint0
PARAMS((char *argv
[], int *arg_ptr
));
86 static boolean parse_fstype
PARAMS((char *argv
[], int *arg_ptr
));
87 static boolean parse_gid
PARAMS((char *argv
[], int *arg_ptr
));
88 static boolean parse_group
PARAMS((char *argv
[], int *arg_ptr
));
89 static boolean parse_help
PARAMS((char *argv
[], int *arg_ptr
));
90 static boolean parse_ilname
PARAMS((char *argv
[], int *arg_ptr
));
91 static boolean parse_iname
PARAMS((char *argv
[], int *arg_ptr
));
92 static boolean parse_inum
PARAMS((char *argv
[], int *arg_ptr
));
93 static boolean parse_ipath
PARAMS((char *argv
[], int *arg_ptr
));
94 static boolean parse_iregex
PARAMS((char *argv
[], int *arg_ptr
));
95 static boolean parse_iwholename
PARAMS((char *argv
[], int *arg_ptr
));
96 static boolean parse_links
PARAMS((char *argv
[], int *arg_ptr
));
97 static boolean parse_lname
PARAMS((char *argv
[], int *arg_ptr
));
98 static boolean parse_ls
PARAMS((char *argv
[], int *arg_ptr
));
99 static boolean parse_maxdepth
PARAMS((char *argv
[], int *arg_ptr
));
100 static boolean parse_mindepth
PARAMS((char *argv
[], int *arg_ptr
));
101 static boolean parse_mmin
PARAMS((char *argv
[], int *arg_ptr
));
102 static boolean parse_mtime
PARAMS((char *argv
[], int *arg_ptr
));
103 static boolean parse_name
PARAMS((char *argv
[], int *arg_ptr
));
104 static boolean parse_negate
PARAMS((char *argv
[], int *arg_ptr
));
105 static boolean parse_newer
PARAMS((char *argv
[], int *arg_ptr
));
106 static boolean parse_noleaf
PARAMS((char *argv
[], int *arg_ptr
));
107 static boolean parse_nogroup
PARAMS((char *argv
[], int *arg_ptr
));
108 static boolean parse_nouser
PARAMS((char *argv
[], int *arg_ptr
));
109 static boolean parse_nowarn
PARAMS((char *argv
[], int *arg_ptr
));
110 static boolean parse_ok
PARAMS((char *argv
[], int *arg_ptr
));
111 static boolean parse_okdir
PARAMS((char *argv
[], int *arg_ptr
));
112 boolean parse_open
PARAMS((char *argv
[], int *arg_ptr
));
113 static boolean parse_or
PARAMS((char *argv
[], int *arg_ptr
));
114 static boolean parse_path
PARAMS((char *argv
[], int *arg_ptr
));
115 static boolean parse_perm
PARAMS((char *argv
[], int *arg_ptr
));
116 boolean parse_print
PARAMS((char *argv
[], int *arg_ptr
));
117 static boolean parse_print0
PARAMS((char *argv
[], int *arg_ptr
));
118 static boolean parse_printf
PARAMS((char *argv
[], int *arg_ptr
));
119 static boolean parse_prune
PARAMS((char *argv
[], int *arg_ptr
));
120 static boolean parse_regex
PARAMS((char *argv
[], int *arg_ptr
));
121 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
, boolean ignore_case
));
122 static boolean parse_samefile
PARAMS((char *argv
[], int *arg_ptr
));
123 static boolean parse_size
PARAMS((char *argv
[], int *arg_ptr
));
124 static boolean parse_true
PARAMS((char *argv
[], int *arg_ptr
));
125 static boolean parse_type
PARAMS((char *argv
[], int *arg_ptr
));
126 static boolean parse_uid
PARAMS((char *argv
[], int *arg_ptr
));
127 static boolean parse_used
PARAMS((char *argv
[], int *arg_ptr
));
128 static boolean parse_user
PARAMS((char *argv
[], int *arg_ptr
));
129 static boolean parse_version
PARAMS((char *argv
[], int *arg_ptr
));
130 static boolean parse_wholename
PARAMS((char *argv
[], int *arg_ptr
));
131 static boolean parse_xdev
PARAMS((char *argv
[], int *arg_ptr
));
132 static boolean parse_ignore_race
PARAMS((char *argv
[], int *arg_ptr
));
133 static boolean parse_noignore_race
PARAMS((char *argv
[], int *arg_ptr
));
134 static boolean parse_warn
PARAMS((char *argv
[], int *arg_ptr
));
135 static boolean parse_xtype
PARAMS((char *argv
[], int *arg_ptr
));
136 static boolean parse_quit
PARAMS((char *argv
[], int *arg_ptr
));
138 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
, boolean ignore_case
));
139 static boolean insert_type
PARAMS((char *argv
[], int *arg_ptr
, boolean (*which_pred
)()));
140 static boolean insert_fprintf
PARAMS((FILE *fp
, boolean (*func
)(), char *argv
[], int *arg_ptr
));
141 static struct segment
**make_segment
PARAMS((struct segment
**segment
, char *format
, int len
, int kind
));
142 static boolean insert_exec_ok
PARAMS((const char *action
, boolean (*func
)(), char *argv
[], int *arg_ptr
));
143 static boolean get_num_days
PARAMS((char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
));
144 static boolean insert_time
PARAMS((char *argv
[], int *arg_ptr
, PFB pred
));
145 static boolean get_num
PARAMS((char *str
, uintmax_t *num
, enum comparison_type
*comp_type
));
146 static boolean insert_num
PARAMS((char *argv
[], int *arg_ptr
, PFB pred
));
147 static FILE *open_output_file
PARAMS((char *path
));
150 char *find_pred_name
PARAMS((PFB pred_func
));
157 ARG_OPTION
, /* regular options like -maxdepth */
158 ARG_POSITIONAL_OPTION
, /* options whose position is important (-follow) */
159 ARG_TEST
, /* a like -name */
160 ARG_PUNCTUATION
, /* like -o or ( */
161 ARG_ACTION
/* like -print */
172 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
173 If they are in some Unix versions of find, they are marked `Unix'. */
175 static struct parser_table
const parse_table
[] =
177 {ARG_PUNCTUATION
, "!", parse_negate
},
178 {ARG_PUNCTUATION
, "not", parse_negate
}, /* GNU */
179 {ARG_PUNCTUATION
, "(", parse_open
},
180 {ARG_PUNCTUATION
, ")", parse_close
},
181 {ARG_PUNCTUATION
, ",", parse_comma
}, /* GNU */
182 {ARG_PUNCTUATION
, "a", parse_and
},
183 {ARG_TEST
, "amin", parse_amin
}, /* GNU */
184 {ARG_PUNCTUATION
, "and", parse_and
}, /* GNU */
185 {ARG_TEST
, "anewer", parse_anewer
}, /* GNU */
186 {ARG_TEST
, "atime", parse_atime
},
187 {ARG_TEST
, "cmin", parse_cmin
}, /* GNU */
188 {ARG_TEST
, "cnewer", parse_cnewer
}, /* GNU */
189 #ifdef UNIMPLEMENTED_UNIX
190 /* It's pretty ugly for find to know about archive formats.
191 Plus what it could do with cpio archives is very limited.
192 Better to leave it out. */
193 {ARG_UNIMPLEMENTED
, "cpio", parse_cpio
}, /* Unix */
195 {ARG_TEST
, "ctime", parse_ctime
},
196 {ARG_POSITIONAL_OPTION
, "daystart", parse_daystart
}, /* GNU */
197 {ARG_ACTION
, "delete", parse_delete
}, /* GNU, Mac OS, FreeBSD */
198 {ARG_OPTION
, "d", parse_d
}, /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
199 {ARG_OPTION
, "depth", parse_depth
},
200 {ARG_TEST
, "empty", parse_empty
}, /* GNU */
201 {ARG_ACTION
, "exec", parse_exec
},
202 {ARG_ACTION
, "execdir", parse_execdir
}, /* *BSD, GNU */
203 {ARG_TEST
, "false", parse_false
}, /* GNU */
204 {ARG_ACTION
, "fls", parse_fls
}, /* GNU */
205 {ARG_POSITIONAL_OPTION
, "follow", parse_follow
}, /* GNU, Unix */
206 {ARG_ACTION
, "fprint", parse_fprint
}, /* GNU */
207 {ARG_ACTION
, "fprint0", parse_fprint0
}, /* GNU */
208 {ARG_ACTION
, "fprintf", parse_fprintf
}, /* GNU */
209 {ARG_TEST
, "fstype", parse_fstype
}, /* GNU, Unix */
210 {ARG_TEST
, "gid", parse_gid
}, /* GNU */
211 {ARG_TEST
, "group", parse_group
},
212 {ARG_TEST
, "help", parse_help
}, /* GNU */
213 {ARG_TEST
, "-help", parse_help
}, /* GNU */
214 {ARG_OPTION
, "ignore_readdir_race", parse_ignore_race
}, /* GNU */
215 {ARG_TEST
, "ilname", parse_ilname
}, /* GNU */
216 {ARG_TEST
, "iname", parse_iname
}, /* GNU */
217 {ARG_TEST
, "inum", parse_inum
}, /* GNU, Unix */
218 {ARG_TEST
, "ipath", parse_ipath
}, /* GNU, deprecated in favour of iwholename */
219 {ARG_TEST
, "iregex", parse_iregex
}, /* GNU */
220 {ARG_TEST
, "iwholename", parse_iwholename
}, /* GNU */
221 {ARG_TEST
, "links", parse_links
},
222 {ARG_TEST
, "lname", parse_lname
}, /* GNU */
223 {ARG_ACTION
, "ls", parse_ls
}, /* GNU, Unix */
224 {ARG_OPTION
, "maxdepth", parse_maxdepth
}, /* GNU */
225 {ARG_OPTION
, "mindepth", parse_mindepth
}, /* GNU */
226 {ARG_TEST
, "mmin", parse_mmin
}, /* GNU */
227 {ARG_OPTION
, "mount", parse_xdev
}, /* Unix */
228 {ARG_TEST
, "mtime", parse_mtime
},
229 {ARG_TEST
, "name", parse_name
},
230 #ifdef UNIMPLEMENTED_UNIX
231 {ARG_UNIMPLEMENTED
, "ncpio", parse_ncpio
}, /* Unix */
233 {ARG_TEST
, "newer", parse_newer
},
234 {ARG_OPTION
, "noleaf", parse_noleaf
}, /* GNU */
235 {ARG_TEST
, "nogroup", parse_nogroup
},
236 {ARG_TEST
, "nouser", parse_nouser
},
237 {ARG_OPTION
, "noignore_readdir_race", parse_noignore_race
},/* GNU */
238 {ARG_OPTION
, "nowarn", parse_nowarn
}, /* GNU */
239 {ARG_PUNCTUATION
, "o", parse_or
},
240 {ARG_PUNCTUATION
, "or", parse_or
}, /* GNU */
241 {ARG_ACTION
, "ok", parse_ok
},
242 {ARG_ACTION
, "okdir", parse_okdir
}, /* GNU (-execdir is BSD) */
243 {ARG_TEST
, "path", parse_path
}, /* GNU, HP-UX, GNU prefers wholename */
244 {ARG_TEST
, "perm", parse_perm
},
245 {ARG_ACTION
, "print", parse_print
},
246 {ARG_ACTION
, "print0", parse_print0
}, /* GNU */
247 {ARG_ACTION
, "printf", parse_printf
}, /* GNU */
248 {ARG_TEST
, "prune", parse_prune
},
249 {ARG_ACTION
, "quit", parse_quit
}, /* GNU */
250 {ARG_TEST
, "regex", parse_regex
}, /* GNU */
251 {ARG_TEST
, "samefile", parse_samefile
}, /* GNU */
252 {ARG_TEST
, "size", parse_size
},
253 {ARG_TEST
, "true", parse_true
}, /* GNU */
254 {ARG_TEST
, "type", parse_type
},
255 {ARG_TEST
, "uid", parse_uid
}, /* GNU */
256 {ARG_TEST
, "used", parse_used
}, /* GNU */
257 {ARG_TEST
, "user", parse_user
},
258 {ARG_TEST
, "version", parse_version
}, /* GNU */
259 {ARG_TEST
, "-version", parse_version
}, /* GNU */
260 {ARG_OPTION
, "warn", parse_warn
}, /* GNU */
261 {ARG_TEST
, "wholename", parse_wholename
}, /* GNU, replaces -path */
262 {ARG_OPTION
, "xdev", parse_xdev
},
263 {ARG_TEST
, "xtype", parse_xtype
}, /* GNU */
268 static const char *first_nonoption_arg
= NULL
;
270 /* Return a pointer to the parser function to invoke for predicate
272 Return NULL if SEARCH_NAME is not a valid predicate name. */
275 find_parser (char *search_name
)
278 const char *original_arg
= search_name
;
280 if (*search_name
== '-')
282 for (i
= 0; parse_table
[i
].parser_name
!= 0; i
++)
284 if (strcmp (parse_table
[i
].parser_name
, search_name
) == 0)
286 /* If this is an option, but we have already had a
287 * non-option argument, the user may be under the
288 * impression that the behaviour of the option
289 * argument is conditional on some preceding
290 * tests. This might typically be the case with,
291 * for example, -maxdepth.
293 * The options -daystart and -follow are exempt
294 * from this treatment, since their positioning
295 * in the command line does have an effect on
296 * subsequent tests but not previous ones. That
297 * might be intentional on the part of the user.
299 if (parse_table
[i
].type
!= ARG_POSITIONAL_OPTION
)
301 /* Something other than -follow/-daystart.
302 * If this is an option, check if it followed
303 * a non-option and if so, issue a warning.
305 if (parse_table
[i
].type
== ARG_OPTION
)
307 if ((first_nonoption_arg
!= NULL
)
308 && options
.warnings
)
310 /* option which folows a non-option */
312 _("warning: you have specified the %s "
313 "option after a non-option argument %s, "
314 "but options are not positional (%s affects "
315 "tests specified before it as well as those "
316 "specified after it). Please specify options "
317 "before other arguments.\n"),
325 /* Not an option or a positional option,
326 * so remember we've seen it in order to
327 * use it in a possible future warning message.
329 if (first_nonoption_arg
== NULL
)
331 first_nonoption_arg
= original_arg
;
336 return (parse_table
[i
].parser_func
);
342 /* The parsers are responsible to continue scanning ARGV for
343 their arguments. Each parser knows what is and isn't
346 ARGV is the argument array.
347 *ARG_PTR is the index to start at in ARGV,
348 updated to point beyond the last element consumed.
350 The predicate structure is updated with the new information. */
353 parse_amin (char **argv
, int *arg_ptr
)
355 struct predicate
*our_pred
;
357 enum comparison_type c_type
;
360 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
362 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
364 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
365 our_pred
= insert_primary (pred_amin
);
366 our_pred
->args
.info
.kind
= c_type
;
367 our_pred
->args
.info
.negative
= t
< 0;
368 our_pred
->args
.info
.l_val
= t
;
374 parse_and (char **argv
, int *arg_ptr
)
376 struct predicate
*our_pred
;
381 our_pred
= get_new_pred ();
382 our_pred
->pred_func
= pred_and
;
384 our_pred
->p_name
= find_pred_name (pred_and
);
386 our_pred
->p_type
= BI_OP
;
387 our_pred
->p_prec
= AND_PREC
;
388 our_pred
->need_stat
= our_pred
->need_type
= false;
393 parse_anewer (char **argv
, int *arg_ptr
)
395 struct predicate
*our_pred
;
396 struct stat stat_newer
;
398 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
400 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
401 error (1, errno
, "%s", argv
[*arg_ptr
]);
402 our_pred
= insert_primary (pred_anewer
);
403 our_pred
->args
.time
= stat_newer
.st_mtime
;
409 parse_atime (char **argv
, int *arg_ptr
)
411 return (insert_time (argv
, arg_ptr
, pred_atime
));
415 parse_close (char **argv
, int *arg_ptr
)
417 struct predicate
*our_pred
;
422 our_pred
= get_new_pred ();
423 our_pred
->pred_func
= pred_close
;
425 our_pred
->p_name
= find_pred_name (pred_close
);
427 our_pred
->p_type
= CLOSE_PAREN
;
428 our_pred
->p_prec
= NO_PREC
;
429 our_pred
->need_stat
= our_pred
->need_type
= false;
434 parse_cmin (char **argv
, int *arg_ptr
)
436 struct predicate
*our_pred
;
438 enum comparison_type c_type
;
441 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
443 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
445 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
446 our_pred
= insert_primary (pred_cmin
);
447 our_pred
->args
.info
.kind
= c_type
;
448 our_pred
->args
.info
.negative
= t
< 0;
449 our_pred
->args
.info
.l_val
= t
;
455 parse_cnewer (char **argv
, int *arg_ptr
)
457 struct predicate
*our_pred
;
458 struct stat stat_newer
;
460 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
462 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
463 error (1, errno
, "%s", argv
[*arg_ptr
]);
464 our_pred
= insert_primary (pred_cnewer
);
465 our_pred
->args
.time
= stat_newer
.st_mtime
;
471 parse_comma (char **argv
, int *arg_ptr
)
473 struct predicate
*our_pred
;
478 our_pred
= get_new_pred ();
479 our_pred
->pred_func
= pred_comma
;
481 our_pred
->p_name
= find_pred_name (pred_comma
);
483 our_pred
->p_type
= BI_OP
;
484 our_pred
->p_prec
= COMMA_PREC
;
485 our_pred
->need_stat
= our_pred
->need_type
= false;
490 parse_ctime (char **argv
, int *arg_ptr
)
492 return (insert_time (argv
, arg_ptr
, pred_ctime
));
496 parse_daystart (char **argv
, int *arg_ptr
)
503 if (options
.full_days
== false)
505 options
.cur_day_start
+= DAYSECS
;
506 local
= localtime (&options
.cur_day_start
);
507 options
.cur_day_start
-= (local
508 ? (local
->tm_sec
+ local
->tm_min
* 60
509 + local
->tm_hour
* 3600)
510 : options
.cur_day_start
% DAYSECS
);
511 options
.full_days
= true;
517 parse_delete (argv
, arg_ptr
)
521 struct predicate
*our_pred
;
525 our_pred
= insert_primary (pred_delete
);
526 our_pred
->side_effects
= true;
527 our_pred
->no_default_print
= true;
528 /* -delete implies -depth */
529 options
.do_dir_first
= false;
534 parse_depth (char **argv
, int *arg_ptr
)
539 options
.do_dir_first
= false;
544 parse_d (char **argv
, int *arg_ptr
)
549 if (options
.warnings
)
552 _("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
554 return parse_depth(argv
, arg_ptr
);
558 parse_empty (char **argv
, int *arg_ptr
)
563 insert_primary (pred_empty
);
568 parse_exec (char **argv
, int *arg_ptr
)
570 return (insert_exec_ok ("-exec", pred_exec
, argv
, arg_ptr
));
574 parse_execdir (char **argv
, int *arg_ptr
)
576 return (insert_exec_ok ("-execdir", pred_execdir
, argv
, arg_ptr
));
580 parse_false (char **argv
, int *arg_ptr
)
582 struct predicate
*our_pred
;
587 our_pred
= insert_primary (pred_false
);
588 our_pred
->need_stat
= our_pred
->need_type
= false;
593 parse_fls (char **argv
, int *arg_ptr
)
595 struct predicate
*our_pred
;
597 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
599 our_pred
= insert_primary (pred_fls
);
600 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
601 our_pred
->side_effects
= true;
602 our_pred
->no_default_print
= true;
608 parse_fprintf (char **argv
, int *arg_ptr
)
612 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
614 if (argv
[*arg_ptr
+ 1] == NULL
)
616 /* Ensure we get "missing arg" message, not "invalid arg". */
620 fp
= open_output_file (argv
[*arg_ptr
]);
622 return (insert_fprintf (fp
, pred_fprintf
, argv
, arg_ptr
));
626 parse_follow (char **argv
, int *arg_ptr
)
631 set_follow_state(SYMLINK_ALWAYS_DEREF
);
636 parse_fprint (char **argv
, int *arg_ptr
)
638 struct predicate
*our_pred
;
640 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
642 our_pred
= insert_primary (pred_fprint
);
643 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
644 our_pred
->side_effects
= true;
645 our_pred
->no_default_print
= true;
646 our_pred
->need_stat
= our_pred
->need_type
= false;
652 parse_fprint0 (char **argv
, int *arg_ptr
)
654 struct predicate
*our_pred
;
656 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
658 our_pred
= insert_primary (pred_fprint0
);
659 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
660 our_pred
->side_effects
= true;
661 our_pred
->no_default_print
= true;
662 our_pred
->need_stat
= our_pred
->need_type
= false;
668 parse_fstype (char **argv
, int *arg_ptr
)
670 struct predicate
*our_pred
;
672 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
674 our_pred
= insert_primary (pred_fstype
);
675 our_pred
->args
.str
= argv
[*arg_ptr
];
681 parse_gid (char **argv
, int *arg_ptr
)
683 return (insert_num (argv
, arg_ptr
, pred_gid
));
687 parse_group (char **argv
, int *arg_ptr
)
689 struct group
*cur_gr
;
690 struct predicate
*our_pred
;
694 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
696 cur_gr
= getgrnam (argv
[*arg_ptr
]);
699 gid
= cur_gr
->gr_gid
;
702 gid_len
= strspn (argv
[*arg_ptr
], "0123456789");
703 if ((gid_len
== 0) || (argv
[*arg_ptr
][gid_len
] != '\0'))
705 gid
= atoi (argv
[*arg_ptr
]);
707 our_pred
= insert_primary (pred_group
);
708 our_pred
->args
.gid
= gid
;
714 parse_help (char **argv
, int *arg_ptr
)
720 Usage: %s [path...] [expression]\n"), program_name
);
722 default path is the current directory; default expression is -print\n\
723 expression may consist of: operators, options, tests, and actions:\n"));
725 operators (decreasing precedence; -and is implicit where no others are given):\n\
726 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
727 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
729 positional options (always true): -daystart -follow\n\
730 normal options (always true, specified before other expressions):\n\
731 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
732 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
734 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
735 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
736 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
737 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
739 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
740 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
741 -used N -user NAME -xtype [bcdpfls]\n"));
743 actions: -exec COMMAND ; -fprint FILE -fprint0 FILE -fprintf FILE FORMAT\n\
744 -fls FILE -ok COMMAND ; -print -print0 -printf FORMAT -prune -ls -delete\n\
746 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
747 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
748 email to <bug-findutils@gnu.org>."));
753 parse_ilname (char **argv
, int *arg_ptr
)
755 struct predicate
*our_pred
;
757 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
759 our_pred
= insert_primary (pred_ilname
);
760 our_pred
->args
.str
= argv
[*arg_ptr
];
766 /* sanity check the fnmatch() function to make sure
767 * it really is the GNU version.
770 fnmatch_sanitycheck()
772 /* fprintf(stderr, "Performing find sanity check..."); */
773 if (0 != fnmatch("foo", "foo", 0)
774 || 0 == fnmatch("Foo", "foo", 0)
775 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD
))
777 error (1, 0, _("sanity check of the fnmatch() library function failed."));
778 /* fprintf(stderr, "FAILED\n"); */
782 /* fprintf(stderr, "OK\n"); */
789 parse_iname (char **argv
, int *arg_ptr
)
791 struct predicate
*our_pred
;
793 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
796 fnmatch_sanitycheck();
798 our_pred
= insert_primary (pred_iname
);
799 our_pred
->need_stat
= our_pred
->need_type
= false;
800 our_pred
->args
.str
= argv
[*arg_ptr
];
806 parse_inum (char **argv
, int *arg_ptr
)
808 return (insert_num (argv
, arg_ptr
, pred_inum
));
811 /* -ipath is deprecated (at RMS's request) in favour of
812 * -iwholename. See the node "GNU Manuals" in standards.texi
813 * for the rationale for this (basically, GNU prefers the use
814 * of the phrase "file name" to "path name"
817 parse_ipath (char **argv
, int *arg_ptr
)
820 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
822 return parse_iwholename(argv
, arg_ptr
);
826 parse_iwholename (char **argv
, int *arg_ptr
)
828 struct predicate
*our_pred
;
830 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
833 fnmatch_sanitycheck();
835 our_pred
= insert_primary (pred_ipath
);
836 our_pred
->need_stat
= our_pred
->need_type
= false;
837 our_pred
->args
.str
= argv
[*arg_ptr
];
843 parse_iregex (char **argv
, int *arg_ptr
)
845 return insert_regex (argv
, arg_ptr
, true);
849 parse_links (char **argv
, int *arg_ptr
)
851 return (insert_num (argv
, arg_ptr
, pred_links
));
855 parse_lname (char **argv
, int *arg_ptr
)
857 struct predicate
*our_pred
;
862 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
865 fnmatch_sanitycheck();
867 our_pred
= insert_primary (pred_lname
);
868 our_pred
->args
.str
= argv
[*arg_ptr
];
874 parse_ls (char **argv
, int *arg_ptr
)
876 struct predicate
*our_pred
;
881 our_pred
= insert_primary (pred_ls
);
882 our_pred
->side_effects
= true;
883 our_pred
->no_default_print
= true;
888 parse_maxdepth (char **argv
, int *arg_ptr
)
892 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
894 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
895 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
897 options
.maxdepth
= atoi (argv
[*arg_ptr
]);
898 if (options
.maxdepth
< 0)
905 parse_mindepth (char **argv
, int *arg_ptr
)
909 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
911 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
912 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
914 options
.mindepth
= atoi (argv
[*arg_ptr
]);
915 if (options
.mindepth
< 0)
922 parse_mmin (char **argv
, int *arg_ptr
)
924 struct predicate
*our_pred
;
926 enum comparison_type c_type
;
929 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
931 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
933 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
934 our_pred
= insert_primary (pred_mmin
);
935 our_pred
->args
.info
.kind
= c_type
;
936 our_pred
->args
.info
.negative
= t
< 0;
937 our_pred
->args
.info
.l_val
= t
;
943 parse_mtime (char **argv
, int *arg_ptr
)
945 return (insert_time (argv
, arg_ptr
, pred_mtime
));
949 parse_name (char **argv
, int *arg_ptr
)
951 struct predicate
*our_pred
;
956 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
958 our_pred
= insert_primary (pred_name
);
959 our_pred
->need_stat
= our_pred
->need_type
= false;
960 our_pred
->args
.str
= argv
[*arg_ptr
];
966 parse_negate (char **argv
, int *arg_ptr
)
968 struct predicate
*our_pred
;
973 our_pred
= get_new_pred_chk_op ();
974 our_pred
->pred_func
= pred_negate
;
976 our_pred
->p_name
= find_pred_name (pred_negate
);
978 our_pred
->p_type
= UNI_OP
;
979 our_pred
->p_prec
= NEGATE_PREC
;
980 our_pred
->need_stat
= our_pred
->need_type
= false;
985 parse_newer (char **argv
, int *arg_ptr
)
987 struct predicate
*our_pred
;
988 struct stat stat_newer
;
993 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
995 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
996 error (1, errno
, "%s", argv
[*arg_ptr
]);
997 our_pred
= insert_primary (pred_newer
);
998 our_pred
->args
.time
= stat_newer
.st_mtime
;
1004 parse_noleaf (char **argv
, int *arg_ptr
)
1009 options
.no_leaf_check
= true;
1014 /* Arbitrary amount by which to increase size
1015 of `uid_unused' and `gid_unused'. */
1016 #define ALLOC_STEP 2048
1018 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1019 char *uid_unused
= NULL
;
1021 /* Number of elements in `uid_unused'. */
1022 unsigned uid_allocated
;
1024 /* Similar for GIDs and group entries. */
1025 char *gid_unused
= NULL
;
1026 unsigned gid_allocated
;
1030 parse_nogroup (char **argv
, int *arg_ptr
)
1032 struct predicate
*our_pred
;
1037 our_pred
= insert_primary (pred_nogroup
);
1039 if (gid_unused
== NULL
)
1043 gid_allocated
= ALLOC_STEP
;
1044 gid_unused
= xmalloc (gid_allocated
);
1045 memset (gid_unused
, 1, gid_allocated
);
1047 while ((gr
= getgrent ()) != NULL
)
1049 if ((unsigned) gr
->gr_gid
>= gid_allocated
)
1051 unsigned new_allocated
= (unsigned) gr
->gr_gid
+ ALLOC_STEP
;
1052 gid_unused
= xrealloc (gid_unused
, new_allocated
);
1053 memset (gid_unused
+ gid_allocated
, 1,
1054 new_allocated
- gid_allocated
);
1055 gid_allocated
= new_allocated
;
1057 gid_unused
[(unsigned) gr
->gr_gid
] = 0;
1066 parse_nouser (char **argv
, int *arg_ptr
)
1068 struct predicate
*our_pred
;
1073 our_pred
= insert_primary (pred_nouser
);
1075 if (uid_unused
== NULL
)
1079 uid_allocated
= ALLOC_STEP
;
1080 uid_unused
= xmalloc (uid_allocated
);
1081 memset (uid_unused
, 1, uid_allocated
);
1083 while ((pw
= getpwent ()) != NULL
)
1085 if ((unsigned) pw
->pw_uid
>= uid_allocated
)
1087 unsigned new_allocated
= (unsigned) pw
->pw_uid
+ ALLOC_STEP
;
1088 uid_unused
= xrealloc (uid_unused
, new_allocated
);
1089 memset (uid_unused
+ uid_allocated
, 1,
1090 new_allocated
- uid_allocated
);
1091 uid_allocated
= new_allocated
;
1093 uid_unused
[(unsigned) pw
->pw_uid
] = 0;
1102 parse_nowarn (char **argv
, int *arg_ptr
)
1107 options
.warnings
= false;
1112 parse_ok (char **argv
, int *arg_ptr
)
1114 return (insert_exec_ok ("-ok", pred_ok
, argv
, arg_ptr
));
1118 parse_okdir (char **argv
, int *arg_ptr
)
1120 return (insert_exec_ok ("-okdir", pred_okdir
, argv
, arg_ptr
));
1124 parse_open (char **argv
, int *arg_ptr
)
1126 struct predicate
*our_pred
;
1131 our_pred
= get_new_pred_chk_op ();
1132 our_pred
->pred_func
= pred_open
;
1134 our_pred
->p_name
= find_pred_name (pred_open
);
1136 our_pred
->p_type
= OPEN_PAREN
;
1137 our_pred
->p_prec
= NO_PREC
;
1138 our_pred
->need_stat
= our_pred
->need_type
= false;
1143 parse_or (char **argv
, int *arg_ptr
)
1145 struct predicate
*our_pred
;
1150 our_pred
= get_new_pred ();
1151 our_pred
->pred_func
= pred_or
;
1153 our_pred
->p_name
= find_pred_name (pred_or
);
1155 our_pred
->p_type
= BI_OP
;
1156 our_pred
->p_prec
= OR_PREC
;
1157 our_pred
->need_stat
= our_pred
->need_type
= false;
1161 /* -path is deprecated (at RMS's request) in favour of
1162 * -iwholename. See the node "GNU Manuals" in standards.texi
1163 * for the rationale for this (basically, GNU prefers the use
1164 * of the phrase "file name" to "path name".
1166 * We do not issue a warning that this usage is deprecated
1167 * since HPUX find supports this predicate also.
1170 parse_path (char **argv
, int *arg_ptr
)
1172 return parse_wholename(argv
, arg_ptr
);
1176 parse_wholename (char **argv
, int *arg_ptr
)
1178 struct predicate
*our_pred
;
1180 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1182 our_pred
= insert_primary (pred_path
);
1183 our_pred
->need_stat
= our_pred
->need_type
= false;
1184 our_pred
->args
.str
= argv
[*arg_ptr
];
1190 parse_perm (char **argv
, int *arg_ptr
)
1194 struct mode_change
*change
;
1195 struct predicate
*our_pred
;
1197 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1200 switch (argv
[*arg_ptr
][0])
1211 change
= mode_compile (argv
[*arg_ptr
] + mode_start
, MODE_MASK_PLUS
);
1212 if (change
== MODE_INVALID
)
1213 error (1, 0, _("invalid mode `%s'"), argv
[*arg_ptr
]);
1214 else if (change
== MODE_MEMORY_EXHAUSTED
)
1215 error (1, 0, _("virtual memory exhausted"));
1216 perm_val
= mode_adjust (0, change
);
1219 our_pred
= insert_primary (pred_perm
);
1221 switch (argv
[*arg_ptr
][0])
1224 our_pred
->args
.perm
.kind
= PERM_AT_LEAST
;
1227 our_pred
->args
.perm
.kind
= PERM_ANY
;
1230 our_pred
->args
.perm
.kind
= PERM_EXACT
;
1233 our_pred
->args
.perm
.val
= perm_val
& MODE_ALL
;
1239 parse_print (char **argv
, int *arg_ptr
)
1241 struct predicate
*our_pred
;
1246 our_pred
= insert_primary (pred_print
);
1247 /* -print has the side effect of printing. This prevents us
1248 from doing undesired multiple printing when the user has
1249 already specified -print. */
1250 our_pred
->side_effects
= true;
1251 our_pred
->no_default_print
= true;
1252 our_pred
->need_stat
= our_pred
->need_type
= false;
1257 parse_print0 (char **argv
, int *arg_ptr
)
1259 struct predicate
*our_pred
;
1264 our_pred
= insert_primary (pred_print0
);
1265 /* -print0 has the side effect of printing. This prevents us
1266 from doing undesired multiple printing when the user has
1267 already specified -print0. */
1268 our_pred
->side_effects
= true;
1269 our_pred
->no_default_print
= true;
1270 our_pred
->need_stat
= our_pred
->need_type
= false;
1275 parse_printf (char **argv
, int *arg_ptr
)
1277 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1279 return (insert_fprintf (stdout
, pred_fprintf
, argv
, arg_ptr
));
1283 parse_prune (char **argv
, int *arg_ptr
)
1285 struct predicate
*our_pred
;
1290 our_pred
= insert_primary (pred_prune
);
1291 our_pred
->need_stat
= our_pred
->need_type
= false;
1292 /* -prune has a side effect that it does not descend into
1293 the current directory. */
1294 our_pred
->side_effects
= true;
1299 parse_quit (char **argv
, int *arg_ptr
)
1301 struct predicate
*our_pred
= insert_primary (pred_quit
);
1304 our_pred
->need_stat
= our_pred
->need_type
= false;
1310 parse_regex (char **argv
, int *arg_ptr
)
1312 return insert_regex (argv
, arg_ptr
, false);
1316 insert_regex (char **argv
, int *arg_ptr
, boolean ignore_case
)
1318 struct predicate
*our_pred
;
1319 struct re_pattern_buffer
*re
;
1320 const char *error_message
;
1322 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1324 our_pred
= insert_primary (pred_regex
);
1325 our_pred
->need_stat
= our_pred
->need_type
= false;
1326 re
= (struct re_pattern_buffer
*)
1327 xmalloc (sizeof (struct re_pattern_buffer
));
1328 our_pred
->args
.regex
= re
;
1329 re
->allocated
= 100;
1330 re
->buffer
= (unsigned char *) xmalloc (re
->allocated
);
1335 re_syntax_options
|= RE_ICASE
;
1339 re_syntax_options
&= ~RE_ICASE
;
1341 re
->translate
= NULL
;
1343 error_message
= re_compile_pattern (argv
[*arg_ptr
], strlen (argv
[*arg_ptr
]),
1346 error (1, 0, "%s", error_message
);
1352 parse_size (char **argv
, int *arg_ptr
)
1354 struct predicate
*our_pred
;
1356 enum comparison_type c_type
;
1360 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1362 len
= strlen (argv
[*arg_ptr
]);
1364 error (1, 0, _("invalid null argument to -size"));
1365 switch (argv
[*arg_ptr
][len
- 1])
1369 argv
[*arg_ptr
][len
- 1] = '\0';
1374 argv
[*arg_ptr
][len
- 1] = '\0';
1379 argv
[*arg_ptr
][len
- 1] = '\0';
1382 case 'M': /* Megabytes */
1383 blksize
= 1024*1024;
1384 argv
[*arg_ptr
][len
- 1] = '\0';
1387 case 'G': /* Gigabytes */
1388 blksize
= 1024*1024*1024;
1389 argv
[*arg_ptr
][len
- 1] = '\0';
1394 argv
[*arg_ptr
][len
- 1] = '\0';
1410 error (1, 0, _("invalid -size type `%c'"), argv
[*arg_ptr
][len
- 1]);
1412 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
1414 our_pred
= insert_primary (pred_size
);
1415 our_pred
->args
.size
.kind
= c_type
;
1416 our_pred
->args
.size
.blocksize
= blksize
;
1417 our_pred
->args
.size
.size
= num
;
1424 parse_samefile (char **argv
, int *arg_ptr
)
1426 struct predicate
*our_pred
;
1429 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1431 if ((*options
.xstat
) (argv
[*arg_ptr
], &st
))
1432 error (1, errno
, "%s", argv
[*arg_ptr
]);
1434 our_pred
= insert_primary (pred_samefile
);
1435 our_pred
->args
.fileid
.ino
= st
.st_ino
;
1436 our_pred
->args
.fileid
.dev
= st
.st_dev
;
1437 our_pred
->need_type
= false;
1438 our_pred
->need_stat
= true;
1445 parse_true (char **argv
, int *arg_ptr
)
1447 struct predicate
*our_pred
;
1452 our_pred
= insert_primary (pred_true
);
1453 our_pred
->need_stat
= our_pred
->need_type
= false;
1458 parse_type (char **argv
, int *arg_ptr
)
1460 return insert_type (argv
, arg_ptr
, pred_type
);
1464 parse_uid (char **argv
, int *arg_ptr
)
1466 return (insert_num (argv
, arg_ptr
, pred_uid
));
1470 parse_used (char **argv
, int *arg_ptr
)
1472 struct predicate
*our_pred
;
1474 enum comparison_type c_type
;
1477 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1479 if (!get_num (argv
[*arg_ptr
], &num_days
, &c_type
))
1481 t
= num_days
* DAYSECS
;
1482 our_pred
= insert_primary (pred_used
);
1483 our_pred
->args
.info
.kind
= c_type
;
1484 our_pred
->args
.info
.negative
= t
< 0;
1485 our_pred
->args
.info
.l_val
= t
;
1491 parse_user (char **argv
, int *arg_ptr
)
1493 struct passwd
*cur_pwd
;
1494 struct predicate
*our_pred
;
1498 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1500 cur_pwd
= getpwnam (argv
[*arg_ptr
]);
1502 if (cur_pwd
!= NULL
)
1503 uid
= cur_pwd
->pw_uid
;
1506 uid_len
= strspn (argv
[*arg_ptr
], "0123456789");
1507 if ((uid_len
== 0) || (argv
[*arg_ptr
][uid_len
] != '\0'))
1509 uid
= atoi (argv
[*arg_ptr
]);
1511 our_pred
= insert_primary (pred_user
);
1512 our_pred
->args
.uid
= uid
;
1518 parse_version (char **argv
, int *arg_ptr
)
1520 extern char *version_string
;
1525 printf (_("GNU find version %s\n"), version_string
);
1530 parse_xdev (char **argv
, int *arg_ptr
)
1534 options
.stay_on_filesystem
= true;
1539 parse_ignore_race (char **argv
, int *arg_ptr
)
1543 options
.ignore_readdir_race
= true;
1548 parse_noignore_race (char **argv
, int *arg_ptr
)
1552 options
.ignore_readdir_race
= false;
1557 parse_warn (char **argv
, int *arg_ptr
)
1561 options
.warnings
= true;
1566 parse_xtype (char **argv
, int *arg_ptr
)
1570 return insert_type (argv
, arg_ptr
, pred_xtype
);
1574 insert_type (char **argv
, int *arg_ptr
, boolean (*which_pred
) (/* ??? */))
1577 struct predicate
*our_pred
;
1579 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
)
1580 || (strlen (argv
[*arg_ptr
]) != 1))
1582 switch (argv
[*arg_ptr
][0])
1584 case 'b': /* block special */
1585 type_cell
= S_IFBLK
;
1587 case 'c': /* character special */
1588 type_cell
= S_IFCHR
;
1590 case 'd': /* directory */
1591 type_cell
= S_IFDIR
;
1593 case 'f': /* regular file */
1594 type_cell
= S_IFREG
;
1597 case 'l': /* symbolic link */
1598 type_cell
= S_IFLNK
;
1602 case 'p': /* pipe */
1603 type_cell
= S_IFIFO
;
1607 case 's': /* socket */
1608 type_cell
= S_IFSOCK
;
1612 case 'D': /* Solaris door */
1613 type_cell
= S_IFDOOR
;
1616 default: /* None of the above ... nuke 'em. */
1619 our_pred
= insert_primary (which_pred
);
1621 /* Figure out if we will need to stat the file, because if we don't
1622 * need to follow symlinks, we can avoid a stat call by using
1623 * struct dirent.d_type.
1625 if (which_pred
== pred_xtype
)
1627 our_pred
->need_stat
= true;
1628 our_pred
->need_type
= false;
1632 our_pred
->need_stat
= false; /* struct dirent is enough */
1633 our_pred
->need_type
= true;
1635 our_pred
->args
.type
= type_cell
;
1636 (*arg_ptr
)++; /* Move on to next argument. */
1640 /* If true, we've determined that the current fprintf predicate
1641 uses stat information. */
1642 static boolean fprintf_stat_needed
;
1645 insert_fprintf (FILE *fp
, boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
1647 char *format
; /* Beginning of unprocessed format string. */
1648 register char *scan
; /* Current address in scanning `format'. */
1649 register char *scan2
; /* Address inside of element being scanned. */
1650 struct segment
**segmentp
; /* Address of current segment. */
1651 struct predicate
*our_pred
;
1653 format
= argv
[(*arg_ptr
)++];
1655 fprintf_stat_needed
= false; /* Might be overridden later. */
1656 our_pred
= insert_primary (func
);
1657 our_pred
->side_effects
= true;
1658 our_pred
->no_default_print
= true;
1659 our_pred
->args
.printf_vec
.stream
= fp
;
1660 segmentp
= &our_pred
->args
.printf_vec
.segment
;
1663 for (scan
= format
; *scan
; scan
++)
1668 if (*scan2
>= '0' && *scan2
<= '7')
1672 for (i
= n
= 0; i
< 3 && (*scan2
>= '0' && *scan2
<= '7');
1674 n
= 8 * n
+ *scan2
- '0';
1689 make_segment (segmentp
, format
, scan
- format
, KIND_STOP
);
1690 our_pred
->need_stat
= fprintf_stat_needed
;
1708 /* *scan = '\\'; * it already is */
1712 _("warning: unrecognized escape `\\%c'"), *scan2
);
1717 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1719 format
= scan2
+ 1; /* Move past the escape. */
1720 scan
= scan2
; /* Incremented immediately by `for'. */
1722 else if (*scan
== '%')
1726 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1732 /* Scan past flags, width and precision, to verify kind. */
1733 for (scan2
= scan
; *++scan2
&& strchr ("-+ #", *scan2
);)
1735 while (ISDIGIT (*scan2
))
1738 for (scan2
++; ISDIGIT (*scan2
); scan2
++)
1740 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2
))
1742 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1747 else if (strchr ("ACT", *scan2
) && scan2
[1])
1749 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1750 *scan2
| (scan2
[1] << 8));
1757 /* An unrecognized % escape. Print the char after the %. */
1758 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1760 segmentp
= make_segment (segmentp
, format
, scan
- format
,
1769 make_segment (segmentp
, format
, scan
- format
, KIND_PLAIN
);
1770 our_pred
->need_type
= false;
1771 our_pred
->need_stat
= fprintf_stat_needed
;
1775 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1776 from the text in FORMAT, which has length LEN.
1777 Return the address of the `next' pointer of the new segment. */
1779 static struct segment
**
1780 make_segment (struct segment
**segment
, char *format
, int len
, int kind
)
1784 *segment
= (struct segment
*) xmalloc (sizeof (struct segment
));
1786 (*segment
)->kind
= kind
;
1787 (*segment
)->next
= NULL
;
1788 (*segment
)->text_len
= len
;
1790 fmt
= (*segment
)->text
= xmalloc (len
+ sizeof "d");
1791 strncpy (fmt
, format
, len
);
1794 switch (kind
& 0xff)
1796 case KIND_PLAIN
: /* Plain text string, no % conversion. */
1797 case KIND_STOP
: /* Terminate argument, no newline. */
1800 case 'a': /* atime in `ctime' format */
1801 case 'A': /* atime in user-specified strftime format */
1802 case 'c': /* ctime in `ctime' format */
1803 case 'C': /* ctime in user-specified strftime format */
1804 case 'F': /* filesystem type */
1805 case 'g': /* group name */
1806 case 'i': /* inode number */
1807 case 'l': /* object of symlink */
1808 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
1809 case 's': /* size in bytes */
1810 case 't': /* mtime in `ctime' format */
1811 case 'T': /* mtime in user-specified strftime format */
1812 case 'u': /* user name */
1813 case 'y': /* file type */
1814 case 'Y': /* symlink pointed file type */
1815 fprintf_stat_needed
= true;
1817 case 'f': /* basename of path */
1818 case 'h': /* leading directories part of path */
1819 case 'H': /* ARGV element file was found under */
1820 case 'p': /* pathname */
1821 case 'P': /* pathname with ARGV element stripped */
1825 /* Numeric items that one might expect to honour
1826 * #, 0, + flags but which do not.
1828 case 'G': /* GID number */
1829 case 'U': /* UID number */
1830 case 'b': /* size in 512-byte blocks */
1831 case 'D': /* Filesystem device on which the file exits */
1832 case 'k': /* size in 1K blocks */
1833 case 'n': /* number of links */
1834 fprintf_stat_needed
= true;
1838 /* Numeric items that DO honour #, 0, + flags.
1840 case 'd': /* depth in search tree (0 = ARGV element) */
1844 case 'm': /* mode as octal number (perms only) */
1846 fprintf_stat_needed
= true;
1851 return (&(*segment
)->next
);
1855 check_path_safety(const char *action
)
1857 const char *path
= getenv("PATH");
1859 s
= next_element(path
, 1);
1860 while ((s
= next_element ((char *) NULL
, 1)) != NULL
)
1862 if (0 == strcmp(s
, "."))
1864 error(1, 0, _("The current directory is included in the PATH environment variable, which is insecure in combination with the %s action of find. Please remove the current directory from your $PATH (that is, remove \".\" or leading or trailing colons)"),
1871 /* handles both exec and ok predicate */
1872 #if defined(NEW_EXEC)
1873 /* handles both exec and ok predicate */
1875 new_insert_exec_ok (const char *action
,
1876 boolean (*func
) (/* ??? */),
1877 char **argv
, int *arg_ptr
)
1879 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
1880 int i
; /* Index into cmd args */
1881 int saw_braces
; /* True if previous arg was '{}'. */
1882 boolean allow_plus
; /* True if + is a valid terminator */
1883 int brace_count
; /* Number of instances of {}. */
1887 struct predicate
*our_pred
;
1888 struct exec_val
*execp
; /* Pointer for efficiency. */
1890 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1893 our_pred
= insert_primary (func
);
1894 our_pred
->side_effects
= true;
1895 our_pred
->no_default_print
= true;
1896 execp
= &our_pred
->args
.exec_vec
;
1898 if ((func
!= pred_okdir
) && (func
!= pred_ok
))
1903 if ((func
== pred_execdir
) || (func
== pred_okdir
))
1905 check_path_safety(action
);
1906 execp
->use_current_dir
= true;
1910 execp
->use_current_dir
= false;
1913 our_pred
->args
.exec_vec
.multiple
= 0;
1915 /* Count the number of args with path replacements, up until the ';'.
1916 * Also figure out if the command is terminated by ";" or by "+".
1919 for (end
= start
, saw_braces
=0, brace_count
=0;
1921 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
1924 /* For -exec and -execdir, "{} +" can terminate the command. */
1926 && argv
[end
][0] == '+' && argv
[end
][1] == 0
1929 our_pred
->args
.exec_vec
.multiple
= 1;
1934 if (strstr (argv
[end
], "{}"))
1939 if (0 == end
&& (func
== pred_execdir
|| func
== pred_okdir
))
1941 /* The POSIX standard says that {} replacement should
1942 * occur even in the utility name. This is insecure
1943 * since it means we will be executing a command whose
1944 * name is chosen according to whatever find finds in
1945 * the filesystem. That can be influenced by an
1946 * attacker. Hence for -execdir and -okdir this is not
1947 * allowed. We can specify this as those options are
1948 * not defined by POSIX.
1950 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
1955 /* Fail if no command given or no semicolon found. */
1956 if ((end
== start
) || (argv
[end
] == NULL
))
1963 if (our_pred
->args
.exec_vec
.multiple
&& brace_count
> 1)
1967 if (func
== pred_execdir
)
1973 _("Only one instance of {} is supported with -exec%s ... +"),
1977 /* execp->ctl = xmalloc(sizeof struct buildcmd_control); */
1978 bc_init_controlinfo(&execp
->ctl
);
1979 execp
->ctl
.exec_callback
= launch
;
1981 if (our_pred
->args
.exec_vec
.multiple
)
1983 /* "+" terminator, so we can just append our arguments after the
1984 * command and initial arguments.
1986 execp
->replace_vec
= NULL
;
1987 execp
->ctl
.replace_pat
= NULL
;
1988 execp
->ctl
.rplen
= 0;
1989 execp
->ctl
.lines_per_exec
= 0; /* no limit */
1990 execp
->ctl
.args_per_exec
= 0; /* no limit */
1992 /* remember how many arguments there are */
1993 execp
->ctl
.initial_argc
= (end
-start
) - 1;
1995 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
1996 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
1998 /* Gather the initial arguments. Skip the {}. */
1999 for (i
=start
; i
<end
-1; ++i
)
2001 bc_push_arg(&execp
->ctl
, &execp
->state
,
2002 argv
[i
], strlen(argv
[i
])+1,
2009 /* Semicolon terminator - more than one {} is supported, so we
2010 * have to do brace-replacement.
2012 execp
->num_args
= end
- start
;
2014 execp
->ctl
.replace_pat
= "{}";
2015 execp
->ctl
.rplen
= strlen(execp
->ctl
.replace_pat
);
2016 execp
->ctl
.lines_per_exec
= 0; /* no limit */
2017 execp
->ctl
.args_per_exec
= 0; /* no limit */
2018 execp
->replace_vec
= xmalloc(sizeof(char*)*execp
->num_args
);
2021 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2022 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
2024 /* Remember the (pre-replacement) arguments for later. */
2025 for (i
=0; i
<execp
->num_args
; ++i
)
2027 execp
->replace_vec
[i
] = argv
[i
+start
];
2031 if (argv
[end
] == NULL
)
2039 /* handles both exec and ok predicate */
2041 old_insert_exec_ok (boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
2043 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
2044 int num_paths
; /* Number of args with path replacements. */
2045 int path_pos
; /* Index in array of path replacements. */
2046 int vec_pos
; /* Index in array of args. */
2047 struct predicate
*our_pred
;
2048 struct exec_val
*execp
; /* Pointer for efficiency. */
2050 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2053 /* Count the number of args with path replacements, up until the ';'. */
2055 for (end
= start
, num_paths
= 0;
2057 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2059 if (strstr (argv
[end
], "{}"))
2061 /* Fail if no command given or no semicolon found. */
2062 if ((end
== start
) || (argv
[end
] == NULL
))
2068 our_pred
= insert_primary (func
);
2069 our_pred
->side_effects
= true;
2070 our_pred
->no_default_print
= true;
2071 execp
= &our_pred
->args
.exec_vec
;
2072 execp
->usercontext
= our_pred
;
2073 execp
->use_current_dir
= false;
2075 (struct path_arg
*) xmalloc (sizeof (struct path_arg
) * (num_paths
+ 1));
2076 execp
->vec
= (char **) xmalloc (sizeof (char *) * (end
- start
+ 1));
2077 /* Record the positions of all args, and the args with path replacements. */
2078 for (end
= start
, path_pos
= vec_pos
= 0;
2080 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2085 execp
->paths
[path_pos
].count
= 0;
2086 for (p
= argv
[end
]; *p
; ++p
)
2087 if (p
[0] == '{' && p
[1] == '}')
2089 execp
->paths
[path_pos
].count
++;
2092 if (execp
->paths
[path_pos
].count
)
2094 execp
->paths
[path_pos
].offset
= vec_pos
;
2095 execp
->paths
[path_pos
].origarg
= argv
[end
];
2098 execp
->vec
[vec_pos
++] = argv
[end
];
2100 execp
->paths
[path_pos
].offset
= -1;
2101 execp
->vec
[vec_pos
] = NULL
;
2103 if (argv
[end
] == NULL
)
2114 insert_exec_ok (const char *action
,
2115 boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
2117 #if defined(NEW_EXEC)
2118 return new_insert_exec_ok(action
, func
, argv
, arg_ptr
);
2120 return old_insert_exec_ok(func
, argv
, arg_ptr
);
2126 /* Get a number of days and comparison type.
2127 STR is the ASCII representation.
2128 Set *NUM_DAYS to the number of days, taken as being from
2129 the current moment (or possibly midnight). Thus the sense of the
2130 comparison type appears to be reversed.
2131 Set *COMP_TYPE to the kind of comparison that is requested.
2133 Return true if all okay, false if input error.
2135 Used by -atime, -ctime and -mtime (parsers) to
2136 get the appropriate information for a time predicate processor. */
2139 get_num_days (char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
)
2141 boolean r
= get_num (str
, num_days
, comp_type
);
2145 case COMP_LT
: *comp_type
= COMP_GT
; break;
2146 case COMP_GT
: *comp_type
= COMP_LT
; break;
2152 /* Insert a time predicate PRED.
2153 ARGV is a pointer to the argument array.
2154 ARG_PTR is a pointer to an index into the array, incremented if
2157 Return true if input is valid, false if not.
2159 A new predicate node is assigned, along with an argument node
2160 obtained with malloc.
2162 Used by -atime, -ctime, and -mtime parsers. */
2165 insert_time (char **argv
, int *arg_ptr
, PFB pred
)
2167 struct predicate
*our_pred
;
2169 enum comparison_type c_type
;
2172 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2174 if (!get_num_days (argv
[*arg_ptr
], &num_days
, &c_type
))
2177 /* Figure out the timestamp value we are looking for. */
2178 t
= ( options
.cur_day_start
- num_days
* DAYSECS
2179 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2183 /* We introduce a scope in which 'val' can be declared, for the
2184 * benefit of compilers that are really C89 compilers
2185 * which support intmax_t because config.h #defines it
2187 intmax_t val
= ( (intmax_t)options
.cur_day_start
- num_days
* DAYSECS
2188 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2191 /* Check for possibility of an overflow */
2192 if ( (intmax_t)t
!= val
)
2194 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv
[*arg_ptr
]);
2198 our_pred
= insert_primary (pred
);
2199 our_pred
->args
.info
.kind
= c_type
;
2200 our_pred
->args
.info
.negative
= t
< 0;
2201 our_pred
->args
.info
.l_val
= t
;
2204 fprintf (stderr
, _("inserting %s\n"), our_pred
->p_name
);
2205 fprintf (stderr
, _(" type: %s %s "),
2206 (c_type
== COMP_GT
) ? "gt" :
2207 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2208 (c_type
== COMP_GT
) ? " >" :
2209 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? ">=" : " ?")));
2210 t
= our_pred
->args
.info
.l_val
;
2211 fprintf (stderr
, "%ju %s", (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2212 if (c_type
== COMP_EQ
)
2214 t
= our_pred
->args
.info
.l_val
+= DAYSECS
;
2217 (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2218 our_pred
->args
.info
.l_val
-= DAYSECS
;
2224 /* Get a number with comparision information.
2225 The sense of the comparision information is 'normal'; that is,
2226 '+' looks for a count > than the number and '-' less than.
2228 STR is the ASCII representation of the number.
2229 Set *NUM to the number.
2230 Set *COMP_TYPE to the kind of comparison that is requested.
2232 Return true if all okay, false if input error. */
2235 get_num (char *str
, uintmax_t *num
, enum comparison_type
*comp_type
)
2242 *comp_type
= COMP_GT
;
2246 *comp_type
= COMP_LT
;
2250 *comp_type
= COMP_EQ
;
2254 return xstrtoumax (str
, NULL
, 10, num
, "") == LONGINT_OK
;
2257 /* Insert a number predicate.
2258 ARGV is a pointer to the argument array.
2259 *ARG_PTR is an index into ARGV, incremented if all went well.
2260 *PRED is the predicate processor to insert.
2262 Return true if input is valid, false if error.
2264 A new predicate node is assigned, along with an argument node
2265 obtained with malloc.
2267 Used by -inum and -links parsers. */
2270 insert_num (char **argv
, int *arg_ptr
, PFB pred
)
2272 struct predicate
*our_pred
;
2274 enum comparison_type c_type
;
2276 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2278 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
2280 our_pred
= insert_primary (pred
);
2281 our_pred
->args
.info
.kind
= c_type
;
2282 our_pred
->args
.info
.l_val
= num
;
2285 fprintf (stderr
, _("inserting %s\n"), our_pred
->p_name
);
2286 fprintf (stderr
, _(" type: %s %s "),
2287 (c_type
== COMP_GT
) ? "gt" :
2288 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2289 (c_type
== COMP_GT
) ? " >" :
2290 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? " =" : " ?")));
2291 fprintf (stderr
, "%ju\n", our_pred
->args
.info
.l_val
);
2297 open_output_file (char *path
)
2301 if (!strcmp (path
, "/dev/stderr"))
2303 else if (!strcmp (path
, "/dev/stdout"))
2305 f
= fopen (path
, "w");
2307 error (1, errno
, "%s", path
);