1 /* parser.c -- convert the command line args into an expression tree.
2 Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2001, 2003,
3 2004, 2005 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
27 #include "modechange.h"
43 /* We need <unistd.h> for isatty(). */
50 # define _(Text) gettext (Text)
55 # define N_(String) gettext_noop (String)
57 /* See locate.c for explanation as to why not use (String) */
58 # define N_(String) String
61 #if !defined (isascii) || defined (STDC_HEADERS)
68 #define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
69 #define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
78 static boolean parse_amin
PARAMS((char *argv
[], int *arg_ptr
));
79 static boolean parse_and
PARAMS((char *argv
[], int *arg_ptr
));
80 static boolean parse_anewer
PARAMS((char *argv
[], int *arg_ptr
));
81 static boolean parse_atime
PARAMS((char *argv
[], int *arg_ptr
));
82 boolean parse_close
PARAMS((char *argv
[], int *arg_ptr
));
83 static boolean parse_cmin
PARAMS((char *argv
[], int *arg_ptr
));
84 static boolean parse_cnewer
PARAMS((char *argv
[], int *arg_ptr
));
85 static boolean parse_comma
PARAMS((char *argv
[], int *arg_ptr
));
86 static boolean parse_ctime
PARAMS((char *argv
[], int *arg_ptr
));
87 static boolean parse_daystart
PARAMS((char *argv
[], int *arg_ptr
));
88 static boolean parse_delete
PARAMS((char *argv
[], int *arg_ptr
));
89 static boolean parse_d
PARAMS((char *argv
[], int *arg_ptr
));
90 static boolean parse_depth
PARAMS((char *argv
[], int *arg_ptr
));
91 static boolean parse_empty
PARAMS((char *argv
[], int *arg_ptr
));
92 static boolean parse_exec
PARAMS((char *argv
[], int *arg_ptr
));
93 static boolean parse_execdir
PARAMS((char *argv
[], int *arg_ptr
));
94 static boolean parse_false
PARAMS((char *argv
[], int *arg_ptr
));
95 static boolean parse_fls
PARAMS((char *argv
[], int *arg_ptr
));
96 static boolean parse_fprintf
PARAMS((char *argv
[], int *arg_ptr
));
97 static boolean parse_follow
PARAMS((char *argv
[], int *arg_ptr
));
98 static boolean parse_fprint
PARAMS((char *argv
[], int *arg_ptr
));
99 static boolean parse_fprint0
PARAMS((char *argv
[], int *arg_ptr
));
100 static boolean parse_fstype
PARAMS((char *argv
[], int *arg_ptr
));
101 static boolean parse_gid
PARAMS((char *argv
[], int *arg_ptr
));
102 static boolean parse_group
PARAMS((char *argv
[], int *arg_ptr
));
103 static boolean parse_help
PARAMS((char *argv
[], int *arg_ptr
));
104 static boolean parse_ilname
PARAMS((char *argv
[], int *arg_ptr
));
105 static boolean parse_iname
PARAMS((char *argv
[], int *arg_ptr
));
106 static boolean parse_inum
PARAMS((char *argv
[], int *arg_ptr
));
107 static boolean parse_ipath
PARAMS((char *argv
[], int *arg_ptr
));
108 static boolean parse_iregex
PARAMS((char *argv
[], int *arg_ptr
));
109 static boolean parse_iwholename
PARAMS((char *argv
[], int *arg_ptr
));
110 static boolean parse_links
PARAMS((char *argv
[], int *arg_ptr
));
111 static boolean parse_lname
PARAMS((char *argv
[], int *arg_ptr
));
112 static boolean parse_ls
PARAMS((char *argv
[], int *arg_ptr
));
113 static boolean parse_maxdepth
PARAMS((char *argv
[], int *arg_ptr
));
114 static boolean parse_mindepth
PARAMS((char *argv
[], int *arg_ptr
));
115 static boolean parse_mmin
PARAMS((char *argv
[], int *arg_ptr
));
116 static boolean parse_mtime
PARAMS((char *argv
[], int *arg_ptr
));
117 static boolean parse_name
PARAMS((char *argv
[], int *arg_ptr
));
118 static boolean parse_negate
PARAMS((char *argv
[], int *arg_ptr
));
119 static boolean parse_newer
PARAMS((char *argv
[], int *arg_ptr
));
120 static boolean parse_noleaf
PARAMS((char *argv
[], int *arg_ptr
));
121 static boolean parse_nogroup
PARAMS((char *argv
[], int *arg_ptr
));
122 static boolean parse_nouser
PARAMS((char *argv
[], int *arg_ptr
));
123 static boolean parse_nowarn
PARAMS((char *argv
[], int *arg_ptr
));
124 static boolean parse_ok
PARAMS((char *argv
[], int *arg_ptr
));
125 static boolean parse_okdir
PARAMS((char *argv
[], int *arg_ptr
));
126 boolean parse_open
PARAMS((char *argv
[], int *arg_ptr
));
127 static boolean parse_or
PARAMS((char *argv
[], int *arg_ptr
));
128 static boolean parse_path
PARAMS((char *argv
[], int *arg_ptr
));
129 static boolean parse_perm
PARAMS((char *argv
[], int *arg_ptr
));
130 boolean parse_print
PARAMS((char *argv
[], int *arg_ptr
));
131 static boolean parse_print0
PARAMS((char *argv
[], int *arg_ptr
));
132 static boolean parse_printf
PARAMS((char *argv
[], int *arg_ptr
));
133 static boolean parse_prune
PARAMS((char *argv
[], int *arg_ptr
));
134 static boolean parse_regex
PARAMS((char *argv
[], int *arg_ptr
));
135 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
, boolean ignore_case
));
136 static boolean parse_samefile
PARAMS((char *argv
[], int *arg_ptr
));
137 static boolean parse_size
PARAMS((char *argv
[], int *arg_ptr
));
138 static boolean parse_true
PARAMS((char *argv
[], int *arg_ptr
));
139 static boolean parse_type
PARAMS((char *argv
[], int *arg_ptr
));
140 static boolean parse_uid
PARAMS((char *argv
[], int *arg_ptr
));
141 static boolean parse_used
PARAMS((char *argv
[], int *arg_ptr
));
142 static boolean parse_user
PARAMS((char *argv
[], int *arg_ptr
));
143 static boolean parse_version
PARAMS((char *argv
[], int *arg_ptr
));
144 static boolean parse_wholename
PARAMS((char *argv
[], int *arg_ptr
));
145 static boolean parse_xdev
PARAMS((char *argv
[], int *arg_ptr
));
146 static boolean parse_ignore_race
PARAMS((char *argv
[], int *arg_ptr
));
147 static boolean parse_noignore_race
PARAMS((char *argv
[], int *arg_ptr
));
148 static boolean parse_warn
PARAMS((char *argv
[], int *arg_ptr
));
149 static boolean parse_xtype
PARAMS((char *argv
[], int *arg_ptr
));
150 static boolean parse_quit
PARAMS((char *argv
[], int *arg_ptr
));
152 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
, boolean ignore_case
));
153 static boolean insert_type
PARAMS((char *argv
[], int *arg_ptr
, PRED_FUNC which_pred
));
154 static boolean insert_fprintf
PARAMS((FILE *fp
, PRED_FUNC func
, char *argv
[], int *arg_ptr
));
155 static struct segment
**make_segment
PARAMS((struct segment
**segment
, char *format
, int len
, int kind
));
156 static boolean insert_exec_ok
PARAMS((const char *action
, PRED_FUNC func
, char *argv
[], int *arg_ptr
));
157 static boolean get_num_days
PARAMS((char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
));
158 static boolean insert_time
PARAMS((char *argv
[], int *arg_ptr
, PRED_FUNC pred
));
159 static boolean get_num
PARAMS((char *str
, uintmax_t *num
, enum comparison_type
*comp_type
));
160 static boolean insert_num
PARAMS((char *argv
[], int *arg_ptr
, PRED_FUNC pred
));
161 static FILE *open_output_file
PARAMS((char *path
));
162 static boolean
stream_is_tty(FILE *fp
);
165 char *find_pred_name
PARAMS((PRED_FUNC pred_func
));
172 ARG_OPTION
, /* regular options like -maxdepth */
173 ARG_POSITIONAL_OPTION
, /* options whose position is important (-follow) */
174 ARG_TEST
, /* a like -name */
175 ARG_PUNCTUATION
, /* like -o or ( */
176 ARG_ACTION
/* like -print */
184 PARSE_FUNC parser_func
;
187 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
188 If they are in some Unix versions of find, they are marked `Unix'. */
190 static struct parser_table
const parse_table
[] =
192 {ARG_PUNCTUATION
, "!", parse_negate
},
193 {ARG_PUNCTUATION
, "not", parse_negate
}, /* GNU */
194 {ARG_PUNCTUATION
, "(", parse_open
},
195 {ARG_PUNCTUATION
, ")", parse_close
},
196 {ARG_PUNCTUATION
, ",", parse_comma
}, /* GNU */
197 {ARG_PUNCTUATION
, "a", parse_and
},
198 {ARG_TEST
, "amin", parse_amin
}, /* GNU */
199 {ARG_PUNCTUATION
, "and", parse_and
}, /* GNU */
200 {ARG_TEST
, "anewer", parse_anewer
}, /* GNU */
201 {ARG_TEST
, "atime", parse_atime
},
202 {ARG_TEST
, "cmin", parse_cmin
}, /* GNU */
203 {ARG_TEST
, "cnewer", parse_cnewer
}, /* GNU */
204 #ifdef UNIMPLEMENTED_UNIX
205 /* It's pretty ugly for find to know about archive formats.
206 Plus what it could do with cpio archives is very limited.
207 Better to leave it out. */
208 {ARG_UNIMPLEMENTED
, "cpio", parse_cpio
}, /* Unix */
210 {ARG_TEST
, "ctime", parse_ctime
},
211 {ARG_POSITIONAL_OPTION
, "daystart", parse_daystart
}, /* GNU */
212 {ARG_ACTION
, "delete", parse_delete
}, /* GNU, Mac OS, FreeBSD */
213 {ARG_OPTION
, "d", parse_d
}, /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
214 {ARG_OPTION
, "depth", parse_depth
},
215 {ARG_TEST
, "empty", parse_empty
}, /* GNU */
216 {ARG_ACTION
, "exec", parse_exec
},
217 {ARG_ACTION
, "execdir", parse_execdir
}, /* *BSD, GNU */
218 {ARG_TEST
, "false", parse_false
}, /* GNU */
219 {ARG_ACTION
, "fls", parse_fls
}, /* GNU */
220 {ARG_POSITIONAL_OPTION
, "follow", parse_follow
}, /* GNU, Unix */
221 {ARG_ACTION
, "fprint", parse_fprint
}, /* GNU */
222 {ARG_ACTION
, "fprint0", parse_fprint0
}, /* GNU */
223 {ARG_ACTION
, "fprintf", parse_fprintf
}, /* GNU */
224 {ARG_TEST
, "fstype", parse_fstype
}, /* GNU, Unix */
225 {ARG_TEST
, "gid", parse_gid
}, /* GNU */
226 {ARG_TEST
, "group", parse_group
},
227 {ARG_TEST
, "help", parse_help
}, /* GNU */
228 {ARG_TEST
, "-help", parse_help
}, /* GNU */
229 {ARG_OPTION
, "ignore_readdir_race", parse_ignore_race
}, /* GNU */
230 {ARG_TEST
, "ilname", parse_ilname
}, /* GNU */
231 {ARG_TEST
, "iname", parse_iname
}, /* GNU */
232 {ARG_TEST
, "inum", parse_inum
}, /* GNU, Unix */
233 {ARG_TEST
, "ipath", parse_ipath
}, /* GNU, deprecated in favour of iwholename */
234 {ARG_TEST
, "iregex", parse_iregex
}, /* GNU */
235 {ARG_TEST
, "iwholename", parse_iwholename
}, /* GNU */
236 {ARG_TEST
, "links", parse_links
},
237 {ARG_TEST
, "lname", parse_lname
}, /* GNU */
238 {ARG_ACTION
, "ls", parse_ls
}, /* GNU, Unix */
239 {ARG_OPTION
, "maxdepth", parse_maxdepth
}, /* GNU */
240 {ARG_OPTION
, "mindepth", parse_mindepth
}, /* GNU */
241 {ARG_TEST
, "mmin", parse_mmin
}, /* GNU */
242 {ARG_OPTION
, "mount", parse_xdev
}, /* Unix */
243 {ARG_TEST
, "mtime", parse_mtime
},
244 {ARG_TEST
, "name", parse_name
},
245 #ifdef UNIMPLEMENTED_UNIX
246 {ARG_UNIMPLEMENTED
, "ncpio", parse_ncpio
}, /* Unix */
248 {ARG_TEST
, "newer", parse_newer
},
249 {ARG_OPTION
, "noleaf", parse_noleaf
}, /* GNU */
250 {ARG_TEST
, "nogroup", parse_nogroup
},
251 {ARG_TEST
, "nouser", parse_nouser
},
252 {ARG_OPTION
, "noignore_readdir_race", parse_noignore_race
},/* GNU */
253 {ARG_OPTION
, "nowarn", parse_nowarn
}, /* GNU */
254 {ARG_PUNCTUATION
, "o", parse_or
},
255 {ARG_PUNCTUATION
, "or", parse_or
}, /* GNU */
256 {ARG_ACTION
, "ok", parse_ok
},
257 {ARG_ACTION
, "okdir", parse_okdir
}, /* GNU (-execdir is BSD) */
258 {ARG_TEST
, "path", parse_path
}, /* GNU, HP-UX, GNU prefers wholename */
259 {ARG_TEST
, "perm", parse_perm
},
260 {ARG_ACTION
, "print", parse_print
},
261 {ARG_ACTION
, "print0", parse_print0
}, /* GNU */
262 {ARG_ACTION
, "printf", parse_printf
}, /* GNU */
263 {ARG_TEST
, "prune", parse_prune
},
264 {ARG_ACTION
, "quit", parse_quit
}, /* GNU */
265 {ARG_TEST
, "regex", parse_regex
}, /* GNU */
266 {ARG_TEST
, "samefile", parse_samefile
}, /* GNU */
267 {ARG_TEST
, "size", parse_size
},
268 {ARG_TEST
, "true", parse_true
}, /* GNU */
269 {ARG_TEST
, "type", parse_type
},
270 {ARG_TEST
, "uid", parse_uid
}, /* GNU */
271 {ARG_TEST
, "used", parse_used
}, /* GNU */
272 {ARG_TEST
, "user", parse_user
},
273 {ARG_TEST
, "version", parse_version
}, /* GNU */
274 {ARG_TEST
, "-version", parse_version
}, /* GNU */
275 {ARG_OPTION
, "warn", parse_warn
}, /* GNU */
276 {ARG_TEST
, "wholename", parse_wholename
}, /* GNU, replaces -path */
277 {ARG_OPTION
, "xdev", parse_xdev
},
278 {ARG_TEST
, "xtype", parse_xtype
}, /* GNU */
283 static const char *first_nonoption_arg
= NULL
;
285 /* Return a pointer to the parser function to invoke for predicate
287 Return NULL if SEARCH_NAME is not a valid predicate name. */
290 find_parser (char *search_name
)
293 const char *original_arg
= search_name
;
295 if (*search_name
== '-')
297 for (i
= 0; parse_table
[i
].parser_name
!= 0; i
++)
299 if (strcmp (parse_table
[i
].parser_name
, search_name
) == 0)
301 /* If this is an option, but we have already had a
302 * non-option argument, the user may be under the
303 * impression that the behaviour of the option
304 * argument is conditional on some preceding
305 * tests. This might typically be the case with,
306 * for example, -maxdepth.
308 * The options -daystart and -follow are exempt
309 * from this treatment, since their positioning
310 * in the command line does have an effect on
311 * subsequent tests but not previous ones. That
312 * might be intentional on the part of the user.
314 if (parse_table
[i
].type
!= ARG_POSITIONAL_OPTION
)
316 /* Something other than -follow/-daystart.
317 * If this is an option, check if it followed
318 * a non-option and if so, issue a warning.
320 if (parse_table
[i
].type
== ARG_OPTION
)
322 if ((first_nonoption_arg
!= NULL
)
323 && options
.warnings
)
325 /* option which folows a non-option */
327 _("warning: you have specified the %s "
328 "option after a non-option argument %s, "
329 "but options are not positional (%s affects "
330 "tests specified before it as well as those "
331 "specified after it). Please specify options "
332 "before other arguments.\n"),
340 /* Not an option or a positional option,
341 * so remember we've seen it in order to
342 * use it in a possible future warning message.
344 if (first_nonoption_arg
== NULL
)
346 first_nonoption_arg
= original_arg
;
351 return (parse_table
[i
].parser_func
);
357 /* The parsers are responsible to continue scanning ARGV for
358 their arguments. Each parser knows what is and isn't
361 ARGV is the argument array.
362 *ARG_PTR is the index to start at in ARGV,
363 updated to point beyond the last element consumed.
365 The predicate structure is updated with the new information. */
368 parse_amin (char **argv
, int *arg_ptr
)
370 struct predicate
*our_pred
;
372 enum comparison_type c_type
;
375 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
377 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
379 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
380 our_pred
= insert_primary (pred_amin
);
381 our_pred
->args
.info
.kind
= c_type
;
382 our_pred
->args
.info
.negative
= t
< 0;
383 our_pred
->args
.info
.l_val
= t
;
389 parse_and (char **argv
, int *arg_ptr
)
391 struct predicate
*our_pred
;
396 our_pred
= get_new_pred ();
397 our_pred
->pred_func
= pred_and
;
399 our_pred
->p_name
= find_pred_name (pred_and
);
401 our_pred
->p_type
= BI_OP
;
402 our_pred
->p_prec
= AND_PREC
;
403 our_pred
->need_stat
= our_pred
->need_type
= false;
408 parse_anewer (char **argv
, int *arg_ptr
)
410 struct predicate
*our_pred
;
411 struct stat stat_newer
;
413 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
415 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
416 error (1, errno
, "%s", argv
[*arg_ptr
]);
417 our_pred
= insert_primary (pred_anewer
);
418 our_pred
->args
.time
= stat_newer
.st_mtime
;
424 parse_atime (char **argv
, int *arg_ptr
)
426 return (insert_time (argv
, arg_ptr
, pred_atime
));
430 parse_close (char **argv
, int *arg_ptr
)
432 struct predicate
*our_pred
;
437 our_pred
= get_new_pred ();
438 our_pred
->pred_func
= pred_close
;
440 our_pred
->p_name
= find_pred_name (pred_close
);
442 our_pred
->p_type
= CLOSE_PAREN
;
443 our_pred
->p_prec
= NO_PREC
;
444 our_pred
->need_stat
= our_pred
->need_type
= false;
449 parse_cmin (char **argv
, int *arg_ptr
)
451 struct predicate
*our_pred
;
453 enum comparison_type c_type
;
456 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
458 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
460 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
461 our_pred
= insert_primary (pred_cmin
);
462 our_pred
->args
.info
.kind
= c_type
;
463 our_pred
->args
.info
.negative
= t
< 0;
464 our_pred
->args
.info
.l_val
= t
;
470 parse_cnewer (char **argv
, int *arg_ptr
)
472 struct predicate
*our_pred
;
473 struct stat stat_newer
;
475 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
477 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
478 error (1, errno
, "%s", argv
[*arg_ptr
]);
479 our_pred
= insert_primary (pred_cnewer
);
480 our_pred
->args
.time
= stat_newer
.st_mtime
;
486 parse_comma (char **argv
, int *arg_ptr
)
488 struct predicate
*our_pred
;
493 our_pred
= get_new_pred ();
494 our_pred
->pred_func
= pred_comma
;
496 our_pred
->p_name
= find_pred_name (pred_comma
);
498 our_pred
->p_type
= BI_OP
;
499 our_pred
->p_prec
= COMMA_PREC
;
500 our_pred
->need_stat
= our_pred
->need_type
= false;
505 parse_ctime (char **argv
, int *arg_ptr
)
507 return (insert_time (argv
, arg_ptr
, pred_ctime
));
511 parse_daystart (char **argv
, int *arg_ptr
)
518 if (options
.full_days
== false)
520 options
.cur_day_start
+= DAYSECS
;
521 local
= localtime (&options
.cur_day_start
);
522 options
.cur_day_start
-= (local
523 ? (local
->tm_sec
+ local
->tm_min
* 60
524 + local
->tm_hour
* 3600)
525 : options
.cur_day_start
% DAYSECS
);
526 options
.full_days
= true;
532 parse_delete ( char *argv
[], int *arg_ptr
)
534 struct predicate
*our_pred
;
538 our_pred
= insert_primary (pred_delete
);
539 our_pred
->side_effects
= true;
540 our_pred
->no_default_print
= true;
541 /* -delete implies -depth */
542 options
.do_dir_first
= false;
547 parse_depth (char **argv
, int *arg_ptr
)
552 options
.do_dir_first
= false;
557 parse_d (char **argv
, int *arg_ptr
)
562 if (options
.warnings
)
565 _("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
567 return parse_depth(argv
, arg_ptr
);
571 parse_empty (char **argv
, int *arg_ptr
)
576 insert_primary (pred_empty
);
581 parse_exec (char **argv
, int *arg_ptr
)
583 return (insert_exec_ok ("-exec", pred_exec
, argv
, arg_ptr
));
587 parse_execdir (char **argv
, int *arg_ptr
)
589 return (insert_exec_ok ("-execdir", pred_execdir
, argv
, arg_ptr
));
593 parse_false (char **argv
, int *arg_ptr
)
595 struct predicate
*our_pred
;
600 our_pred
= insert_primary (pred_false
);
601 our_pred
->need_stat
= our_pred
->need_type
= false;
606 parse_fls (char **argv
, int *arg_ptr
)
608 struct predicate
*our_pred
;
610 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
612 our_pred
= insert_primary (pred_fls
);
613 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
614 our_pred
->side_effects
= true;
615 our_pred
->no_default_print
= true;
621 parse_fprintf (char **argv
, int *arg_ptr
)
625 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
627 if (argv
[*arg_ptr
+ 1] == NULL
)
629 /* Ensure we get "missing arg" message, not "invalid arg". */
633 fp
= open_output_file (argv
[*arg_ptr
]);
635 return (insert_fprintf (fp
, pred_fprintf
, argv
, arg_ptr
));
639 parse_follow (char **argv
, int *arg_ptr
)
644 set_follow_state(SYMLINK_ALWAYS_DEREF
);
649 parse_fprint (char **argv
, int *arg_ptr
)
651 struct predicate
*our_pred
;
653 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
655 our_pred
= insert_primary (pred_fprint
);
656 our_pred
->args
.printf_vec
.segment
= NULL
;
657 our_pred
->args
.printf_vec
.stream
= open_output_file (argv
[*arg_ptr
]);
658 our_pred
->args
.printf_vec
.dest_is_tty
= stream_is_tty(our_pred
->args
.printf_vec
.stream
);
659 our_pred
->args
.printf_vec
.quote_opts
= clone_quoting_options (NULL
);
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_fprint0 (char **argv
, int *arg_ptr
)
670 struct predicate
*our_pred
;
672 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
674 our_pred
= insert_primary (pred_fprint0
);
675 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
676 our_pred
->side_effects
= true;
677 our_pred
->no_default_print
= true;
678 our_pred
->need_stat
= our_pred
->need_type
= false;
684 parse_fstype (char **argv
, int *arg_ptr
)
686 struct predicate
*our_pred
;
688 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
690 our_pred
= insert_primary (pred_fstype
);
691 our_pred
->args
.str
= argv
[*arg_ptr
];
697 parse_gid (char **argv
, int *arg_ptr
)
699 return (insert_num (argv
, arg_ptr
, pred_gid
));
703 parse_group (char **argv
, int *arg_ptr
)
705 struct group
*cur_gr
;
706 struct predicate
*our_pred
;
710 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
712 cur_gr
= getgrnam (argv
[*arg_ptr
]);
715 gid
= cur_gr
->gr_gid
;
718 gid_len
= strspn (argv
[*arg_ptr
], "0123456789");
719 if ((gid_len
== 0) || (argv
[*arg_ptr
][gid_len
] != '\0'))
721 gid
= atoi (argv
[*arg_ptr
]);
723 our_pred
= insert_primary (pred_group
);
724 our_pred
->args
.gid
= gid
;
730 parse_help (char **argv
, int *arg_ptr
)
736 Usage: %s [path...] [expression]\n"), program_name
);
738 default path is the current directory; default expression is -print\n\
739 expression may consist of: operators, options, tests, and actions:\n"));
741 operators (decreasing precedence; -and is implicit where no others are given):\n\
742 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
743 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
745 positional options (always true): -daystart -follow\n\
746 normal options (always true, specified before other expressions):\n\
747 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
748 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
750 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
751 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
752 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
753 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
755 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
756 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
757 -used N -user NAME -xtype [bcdpfls]\n"));
759 actions: -exec COMMAND ; -fprint FILE -fprint0 FILE -fprintf FILE FORMAT\n\
760 -fls FILE -ok COMMAND ; -print -print0 -printf FORMAT -prune -ls -delete\n\
762 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
763 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
764 email to <bug-findutils@gnu.org>."));
769 parse_ilname (char **argv
, int *arg_ptr
)
771 struct predicate
*our_pred
;
773 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
775 our_pred
= insert_primary (pred_ilname
);
776 our_pred
->args
.str
= argv
[*arg_ptr
];
782 /* sanity check the fnmatch() function to make sure
783 * it really is the GNU version.
786 fnmatch_sanitycheck(void)
788 /* fprintf(stderr, "Performing find sanity check..."); */
789 if (0 != fnmatch("foo", "foo", 0)
790 || 0 == fnmatch("Foo", "foo", 0)
791 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD
))
793 error (1, 0, _("sanity check of the fnmatch() library function failed."));
794 /* fprintf(stderr, "FAILED\n"); */
798 /* fprintf(stderr, "OK\n"); */
804 check_name_arg(const char *pred
, const char *arg
)
806 if (strchr(arg
, '/'))
808 error(0, 0,_("warning: Unix filenames usually don't contain slashes (though pathnames do). That means that '%s %s' will probably evaluate to false all the time on this system. You might find the '-wholename' test more useful, or perhaps '-samefile'. Alternatively, if you are using GNU grep, you could use 'find ... -print0 | grep -FzZ %s'."),
811 return true; /* allow it anyway */
817 parse_iname (char **argv
, int *arg_ptr
)
819 struct predicate
*our_pred
;
821 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
823 if (!check_name_arg("-iname", argv
[*arg_ptr
]))
826 fnmatch_sanitycheck();
828 our_pred
= insert_primary (pred_iname
);
829 our_pred
->need_stat
= our_pred
->need_type
= false;
830 our_pred
->args
.str
= argv
[*arg_ptr
];
836 parse_inum (char **argv
, int *arg_ptr
)
838 return (insert_num (argv
, arg_ptr
, pred_inum
));
841 /* -ipath is deprecated (at RMS's request) in favour of
842 * -iwholename. See the node "GNU Manuals" in standards.texi
843 * for the rationale for this (basically, GNU prefers the use
844 * of the phrase "file name" to "path name"
847 parse_ipath (char **argv
, int *arg_ptr
)
850 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
852 return parse_iwholename(argv
, arg_ptr
);
856 parse_iwholename (char **argv
, int *arg_ptr
)
858 struct predicate
*our_pred
;
860 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
863 fnmatch_sanitycheck();
865 our_pred
= insert_primary (pred_ipath
);
866 our_pred
->need_stat
= our_pred
->need_type
= false;
867 our_pred
->args
.str
= argv
[*arg_ptr
];
873 parse_iregex (char **argv
, int *arg_ptr
)
875 return insert_regex (argv
, arg_ptr
, true);
879 parse_links (char **argv
, int *arg_ptr
)
881 return (insert_num (argv
, arg_ptr
, pred_links
));
885 parse_lname (char **argv
, int *arg_ptr
)
887 struct predicate
*our_pred
;
892 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
895 fnmatch_sanitycheck();
897 our_pred
= insert_primary (pred_lname
);
898 our_pred
->args
.str
= argv
[*arg_ptr
];
904 parse_ls (char **argv
, int *arg_ptr
)
906 struct predicate
*our_pred
;
911 our_pred
= insert_primary (pred_ls
);
912 our_pred
->side_effects
= true;
913 our_pred
->no_default_print
= true;
918 parse_maxdepth (char **argv
, int *arg_ptr
)
922 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
924 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
925 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
927 options
.maxdepth
= atoi (argv
[*arg_ptr
]);
928 if (options
.maxdepth
< 0)
935 parse_mindepth (char **argv
, int *arg_ptr
)
939 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
941 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
942 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
944 options
.mindepth
= atoi (argv
[*arg_ptr
]);
945 if (options
.mindepth
< 0)
952 parse_mmin (char **argv
, int *arg_ptr
)
954 struct predicate
*our_pred
;
956 enum comparison_type c_type
;
959 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
961 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
963 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
964 our_pred
= insert_primary (pred_mmin
);
965 our_pred
->args
.info
.kind
= c_type
;
966 our_pred
->args
.info
.negative
= t
< 0;
967 our_pred
->args
.info
.l_val
= t
;
973 parse_mtime (char **argv
, int *arg_ptr
)
975 return (insert_time (argv
, arg_ptr
, pred_mtime
));
979 parse_name (char **argv
, int *arg_ptr
)
981 struct predicate
*our_pred
;
986 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
988 if (!check_name_arg("-name", argv
[*arg_ptr
]))
990 fnmatch_sanitycheck();
992 our_pred
= insert_primary (pred_name
);
993 our_pred
->need_stat
= our_pred
->need_type
= false;
994 our_pred
->args
.str
= argv
[*arg_ptr
];
1000 parse_negate (char **argv
, int *arg_ptr
)
1002 struct predicate
*our_pred
;
1007 our_pred
= get_new_pred_chk_op ();
1008 our_pred
->pred_func
= pred_negate
;
1010 our_pred
->p_name
= find_pred_name (pred_negate
);
1012 our_pred
->p_type
= UNI_OP
;
1013 our_pred
->p_prec
= NEGATE_PREC
;
1014 our_pred
->need_stat
= our_pred
->need_type
= false;
1019 parse_newer (char **argv
, int *arg_ptr
)
1021 struct predicate
*our_pred
;
1022 struct stat stat_newer
;
1027 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1029 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
1030 error (1, errno
, "%s", argv
[*arg_ptr
]);
1031 our_pred
= insert_primary (pred_newer
);
1032 our_pred
->args
.time
= stat_newer
.st_mtime
;
1038 parse_noleaf (char **argv
, int *arg_ptr
)
1043 options
.no_leaf_check
= true;
1048 /* Arbitrary amount by which to increase size
1049 of `uid_unused' and `gid_unused'. */
1050 #define ALLOC_STEP 2048
1052 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1053 char *uid_unused
= NULL
;
1055 /* Number of elements in `uid_unused'. */
1056 unsigned uid_allocated
;
1058 /* Similar for GIDs and group entries. */
1059 char *gid_unused
= NULL
;
1060 unsigned gid_allocated
;
1064 parse_nogroup (char **argv
, int *arg_ptr
)
1066 struct predicate
*our_pred
;
1071 our_pred
= insert_primary (pred_nogroup
);
1073 if (gid_unused
== NULL
)
1077 gid_allocated
= ALLOC_STEP
;
1078 gid_unused
= xmalloc (gid_allocated
);
1079 memset (gid_unused
, 1, gid_allocated
);
1081 while ((gr
= getgrent ()) != NULL
)
1083 if ((unsigned) gr
->gr_gid
>= gid_allocated
)
1085 unsigned new_allocated
= (unsigned) gr
->gr_gid
+ ALLOC_STEP
;
1086 gid_unused
= xrealloc (gid_unused
, new_allocated
);
1087 memset (gid_unused
+ gid_allocated
, 1,
1088 new_allocated
- gid_allocated
);
1089 gid_allocated
= new_allocated
;
1091 gid_unused
[(unsigned) gr
->gr_gid
] = 0;
1100 parse_nouser (char **argv
, int *arg_ptr
)
1102 struct predicate
*our_pred
;
1107 our_pred
= insert_primary (pred_nouser
);
1109 if (uid_unused
== NULL
)
1113 uid_allocated
= ALLOC_STEP
;
1114 uid_unused
= xmalloc (uid_allocated
);
1115 memset (uid_unused
, 1, uid_allocated
);
1117 while ((pw
= getpwent ()) != NULL
)
1119 if ((unsigned) pw
->pw_uid
>= uid_allocated
)
1121 unsigned new_allocated
= (unsigned) pw
->pw_uid
+ ALLOC_STEP
;
1122 uid_unused
= xrealloc (uid_unused
, new_allocated
);
1123 memset (uid_unused
+ uid_allocated
, 1,
1124 new_allocated
- uid_allocated
);
1125 uid_allocated
= new_allocated
;
1127 uid_unused
[(unsigned) pw
->pw_uid
] = 0;
1136 parse_nowarn (char **argv
, int *arg_ptr
)
1141 options
.warnings
= false;
1146 parse_ok (char **argv
, int *arg_ptr
)
1148 return (insert_exec_ok ("-ok", pred_ok
, argv
, arg_ptr
));
1152 parse_okdir (char **argv
, int *arg_ptr
)
1154 return (insert_exec_ok ("-okdir", pred_okdir
, argv
, arg_ptr
));
1158 parse_open (char **argv
, int *arg_ptr
)
1160 struct predicate
*our_pred
;
1165 our_pred
= get_new_pred_chk_op ();
1166 our_pred
->pred_func
= pred_open
;
1168 our_pred
->p_name
= find_pred_name (pred_open
);
1170 our_pred
->p_type
= OPEN_PAREN
;
1171 our_pred
->p_prec
= NO_PREC
;
1172 our_pred
->need_stat
= our_pred
->need_type
= false;
1177 parse_or (char **argv
, int *arg_ptr
)
1179 struct predicate
*our_pred
;
1184 our_pred
= get_new_pred ();
1185 our_pred
->pred_func
= pred_or
;
1187 our_pred
->p_name
= find_pred_name (pred_or
);
1189 our_pred
->p_type
= BI_OP
;
1190 our_pred
->p_prec
= OR_PREC
;
1191 our_pred
->need_stat
= our_pred
->need_type
= false;
1195 /* -path is deprecated (at RMS's request) in favour of
1196 * -iwholename. See the node "GNU Manuals" in standards.texi
1197 * for the rationale for this (basically, GNU prefers the use
1198 * of the phrase "file name" to "path name".
1200 * We do not issue a warning that this usage is deprecated
1201 * since HPUX find supports this predicate also.
1204 parse_path (char **argv
, int *arg_ptr
)
1206 return parse_wholename(argv
, arg_ptr
);
1210 parse_wholename (char **argv
, int *arg_ptr
)
1212 struct predicate
*our_pred
;
1214 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1216 our_pred
= insert_primary (pred_path
);
1217 our_pred
->need_stat
= our_pred
->need_type
= false;
1218 our_pred
->args
.str
= argv
[*arg_ptr
];
1224 parse_perm (char **argv
, int *arg_ptr
)
1228 enum permissions_type kind
= PERM_EXACT
;
1229 struct mode_change
*change
= NULL
;
1230 struct predicate
*our_pred
;
1232 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1235 switch (argv
[*arg_ptr
][0])
1239 kind
= PERM_AT_LEAST
;
1243 change
= mode_compile (argv
[*arg_ptr
]);
1246 /* Most likely the caller is an old script that is still
1247 * using the obsolete GNU syntax '-perm +MODE'. This old
1248 * syntax was withdrawn in favor of '-perm /MODE' because
1249 * it is incompatible with POSIX in some cases, but we
1250 * still support uses of it that are not incompatible with
1258 /* This is a POSIX-compatible usage */
1264 case '/': /* GNU extension */
1270 /* For example, '-perm 0644', which is valid and matches
1271 * only files whose mode is exactly 0644.
1273 * We do nothing here, because mode_start and kind are already
1281 change
= mode_compile (argv
[*arg_ptr
] + mode_start
);
1283 error (1, 0, _("invalid mode `%s'"), argv
[*arg_ptr
]);
1285 perm_val
= mode_adjust (0, change
, 0);
1288 our_pred
= insert_primary (pred_perm
);
1290 switch (argv
[*arg_ptr
][0])
1293 our_pred
->args
.perm
.kind
= PERM_AT_LEAST
;
1296 our_pred
->args
.perm
.kind
= PERM_ANY
;
1299 our_pred
->args
.perm
.kind
= PERM_EXACT
;
1302 our_pred
->args
.perm
.val
= perm_val
& MODE_ALL
;
1308 parse_print (char **argv
, int *arg_ptr
)
1310 struct predicate
*our_pred
;
1315 our_pred
= insert_primary (pred_print
);
1316 /* -print has the side effect of printing. This prevents us
1317 from doing undesired multiple printing when the user has
1318 already specified -print. */
1319 our_pred
->side_effects
= true;
1320 our_pred
->no_default_print
= true;
1321 our_pred
->need_stat
= our_pred
->need_type
= false;
1322 our_pred
->args
.printf_vec
.segment
= NULL
;
1323 our_pred
->args
.printf_vec
.stream
= stdout
;
1324 our_pred
->args
.printf_vec
.dest_is_tty
= stream_is_tty(stdout
);
1325 our_pred
->args
.printf_vec
.quote_opts
= clone_quoting_options (NULL
);
1331 parse_print0 (char **argv
, int *arg_ptr
)
1333 struct predicate
*our_pred
;
1338 our_pred
= insert_primary (pred_print0
);
1339 /* -print0 has the side effect of printing. This prevents us
1340 from doing undesired multiple printing when the user has
1341 already specified -print0. */
1342 our_pred
->side_effects
= true;
1343 our_pred
->no_default_print
= true;
1344 our_pred
->need_stat
= our_pred
->need_type
= false;
1349 parse_printf (char **argv
, int *arg_ptr
)
1351 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1353 return (insert_fprintf (stdout
, pred_fprintf
, argv
, arg_ptr
));
1357 parse_prune (char **argv
, int *arg_ptr
)
1359 struct predicate
*our_pred
;
1364 our_pred
= insert_primary (pred_prune
);
1365 our_pred
->need_stat
= our_pred
->need_type
= false;
1366 /* -prune has a side effect that it does not descend into
1367 the current directory. */
1368 our_pred
->side_effects
= true;
1373 parse_quit (char **argv
, int *arg_ptr
)
1375 struct predicate
*our_pred
= insert_primary (pred_quit
);
1378 our_pred
->need_stat
= our_pred
->need_type
= false;
1384 parse_regex (char **argv
, int *arg_ptr
)
1386 return insert_regex (argv
, arg_ptr
, false);
1390 insert_regex (char **argv
, int *arg_ptr
, boolean ignore_case
)
1392 struct predicate
*our_pred
;
1393 struct re_pattern_buffer
*re
;
1394 const char *error_message
;
1395 int opt
= RE_SYNTAX_POSIX_BASIC
;
1397 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1399 our_pred
= insert_primary (pred_regex
);
1400 our_pred
->need_stat
= our_pred
->need_type
= false;
1401 re
= (struct re_pattern_buffer
*)
1402 xmalloc (sizeof (struct re_pattern_buffer
));
1403 our_pred
->args
.regex
= re
;
1404 re
->allocated
= 100;
1405 re
->buffer
= (unsigned char *) xmalloc (re
->allocated
);
1413 re
->translate
= NULL
;
1415 error_message
= re_compile_pattern (argv
[*arg_ptr
], strlen (argv
[*arg_ptr
]),
1418 error (1, 0, "%s", error_message
);
1424 parse_size (char **argv
, int *arg_ptr
)
1426 struct predicate
*our_pred
;
1428 enum comparison_type c_type
;
1432 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1434 len
= strlen (argv
[*arg_ptr
]);
1436 error (1, 0, _("invalid null argument to -size"));
1437 switch (argv
[*arg_ptr
][len
- 1])
1441 argv
[*arg_ptr
][len
- 1] = '\0';
1446 argv
[*arg_ptr
][len
- 1] = '\0';
1451 argv
[*arg_ptr
][len
- 1] = '\0';
1454 case 'M': /* Megabytes */
1455 blksize
= 1024*1024;
1456 argv
[*arg_ptr
][len
- 1] = '\0';
1459 case 'G': /* Gigabytes */
1460 blksize
= 1024*1024*1024;
1461 argv
[*arg_ptr
][len
- 1] = '\0';
1466 argv
[*arg_ptr
][len
- 1] = '\0';
1482 error (1, 0, _("invalid -size type `%c'"), argv
[*arg_ptr
][len
- 1]);
1484 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
1486 our_pred
= insert_primary (pred_size
);
1487 our_pred
->args
.size
.kind
= c_type
;
1488 our_pred
->args
.size
.blocksize
= blksize
;
1489 our_pred
->args
.size
.size
= num
;
1496 parse_samefile (char **argv
, int *arg_ptr
)
1498 struct predicate
*our_pred
;
1501 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1503 if ((*options
.xstat
) (argv
[*arg_ptr
], &st
))
1504 error (1, errno
, "%s", argv
[*arg_ptr
]);
1506 our_pred
= insert_primary (pred_samefile
);
1507 our_pred
->args
.fileid
.ino
= st
.st_ino
;
1508 our_pred
->args
.fileid
.dev
= st
.st_dev
;
1509 our_pred
->need_type
= false;
1510 our_pred
->need_stat
= true;
1517 parse_true (char **argv
, int *arg_ptr
)
1519 struct predicate
*our_pred
;
1524 our_pred
= insert_primary (pred_true
);
1525 our_pred
->need_stat
= our_pred
->need_type
= false;
1530 parse_type (char **argv
, int *arg_ptr
)
1532 return insert_type (argv
, arg_ptr
, pred_type
);
1536 parse_uid (char **argv
, int *arg_ptr
)
1538 return (insert_num (argv
, arg_ptr
, pred_uid
));
1542 parse_used (char **argv
, int *arg_ptr
)
1544 struct predicate
*our_pred
;
1546 enum comparison_type c_type
;
1549 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1551 if (!get_num (argv
[*arg_ptr
], &num_days
, &c_type
))
1553 t
= num_days
* DAYSECS
;
1554 our_pred
= insert_primary (pred_used
);
1555 our_pred
->args
.info
.kind
= c_type
;
1556 our_pred
->args
.info
.negative
= t
< 0;
1557 our_pred
->args
.info
.l_val
= t
;
1563 parse_user (char **argv
, int *arg_ptr
)
1565 struct passwd
*cur_pwd
;
1566 struct predicate
*our_pred
;
1570 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1572 cur_pwd
= getpwnam (argv
[*arg_ptr
]);
1574 if (cur_pwd
!= NULL
)
1575 uid
= cur_pwd
->pw_uid
;
1578 uid_len
= strspn (argv
[*arg_ptr
], "0123456789");
1579 if ((uid_len
== 0) || (argv
[*arg_ptr
][uid_len
] != '\0'))
1581 uid
= atoi (argv
[*arg_ptr
]);
1583 our_pred
= insert_primary (pred_user
);
1584 our_pred
->args
.uid
= uid
;
1590 parse_version (char **argv
, int *arg_ptr
)
1592 extern char *version_string
;
1599 printf (_("GNU find version %s\n"), version_string
);
1600 printf (_("Features enabled: "));
1603 printf("CACHE_IDS ");
1611 printf("DEBUG_STAT ");
1614 #if defined(USE_STRUCT_DIRENT_D_TYPE) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
1618 #if defined(O_NOFOLLOW)
1619 printf("O_NOFOLLOW(%s) ",
1620 (options
.open_nofollow_available
? "enabled" : "disabled"));
1626 /* For the moment, leave this as English in case someone wants
1627 to parse these strings. */
1636 parse_xdev (char **argv
, int *arg_ptr
)
1640 options
.stay_on_filesystem
= true;
1645 parse_ignore_race (char **argv
, int *arg_ptr
)
1649 options
.ignore_readdir_race
= true;
1654 parse_noignore_race (char **argv
, int *arg_ptr
)
1658 options
.ignore_readdir_race
= false;
1663 parse_warn (char **argv
, int *arg_ptr
)
1667 options
.warnings
= true;
1672 parse_xtype (char **argv
, int *arg_ptr
)
1676 return insert_type (argv
, arg_ptr
, pred_xtype
);
1680 insert_type (char **argv
, int *arg_ptr
, PRED_FUNC which_pred
)
1683 struct predicate
*our_pred
;
1685 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
)
1686 || (strlen (argv
[*arg_ptr
]) != 1))
1688 switch (argv
[*arg_ptr
][0])
1690 case 'b': /* block special */
1691 type_cell
= S_IFBLK
;
1693 case 'c': /* character special */
1694 type_cell
= S_IFCHR
;
1696 case 'd': /* directory */
1697 type_cell
= S_IFDIR
;
1699 case 'f': /* regular file */
1700 type_cell
= S_IFREG
;
1703 case 'l': /* symbolic link */
1704 type_cell
= S_IFLNK
;
1708 case 'p': /* pipe */
1709 type_cell
= S_IFIFO
;
1713 case 's': /* socket */
1714 type_cell
= S_IFSOCK
;
1718 case 'D': /* Solaris door */
1719 type_cell
= S_IFDOOR
;
1722 default: /* None of the above ... nuke 'em. */
1725 our_pred
= insert_primary (which_pred
);
1727 /* Figure out if we will need to stat the file, because if we don't
1728 * need to follow symlinks, we can avoid a stat call by using
1729 * struct dirent.d_type.
1731 if (which_pred
== pred_xtype
)
1733 our_pred
->need_stat
= true;
1734 our_pred
->need_type
= false;
1738 our_pred
->need_stat
= false; /* struct dirent is enough */
1739 our_pred
->need_type
= true;
1741 our_pred
->args
.type
= type_cell
;
1742 (*arg_ptr
)++; /* Move on to next argument. */
1747 /* Return true if the file accessed via FP is a terminal.
1750 stream_is_tty(FILE *fp
)
1752 int fd
= fileno(fp
);
1755 return false; /* not a valid stream */
1759 return isatty(fd
) ? true : false;
1766 /* If true, we've determined that the current fprintf predicate
1767 uses stat information. */
1768 static boolean fprintf_stat_needed
;
1771 insert_fprintf (FILE *fp
, PRED_FUNC func
, char **argv
, int *arg_ptr
)
1773 char *format
; /* Beginning of unprocessed format string. */
1774 register char *scan
; /* Current address in scanning `format'. */
1775 register char *scan2
; /* Address inside of element being scanned. */
1776 struct segment
**segmentp
; /* Address of current segment. */
1777 struct predicate
*our_pred
;
1779 format
= argv
[(*arg_ptr
)++];
1781 fprintf_stat_needed
= false; /* Might be overridden later. */
1782 our_pred
= insert_primary (func
);
1783 our_pred
->side_effects
= true;
1784 our_pred
->no_default_print
= true;
1785 our_pred
->args
.printf_vec
.stream
= fp
;
1786 our_pred
->args
.printf_vec
.dest_is_tty
= stream_is_tty(fp
);
1787 our_pred
->args
.printf_vec
.quote_opts
= clone_quoting_options (NULL
);
1788 segmentp
= &our_pred
->args
.printf_vec
.segment
;
1791 for (scan
= format
; *scan
; scan
++)
1796 if (*scan2
>= '0' && *scan2
<= '7')
1800 for (i
= n
= 0; i
< 3 && (*scan2
>= '0' && *scan2
<= '7');
1802 n
= 8 * n
+ *scan2
- '0';
1817 make_segment (segmentp
, format
, scan
- format
, KIND_STOP
);
1818 our_pred
->need_stat
= fprintf_stat_needed
;
1836 /* *scan = '\\'; * it already is */
1840 _("warning: unrecognized escape `\\%c'"), *scan2
);
1845 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1847 format
= scan2
+ 1; /* Move past the escape. */
1848 scan
= scan2
; /* Incremented immediately by `for'. */
1850 else if (*scan
== '%')
1854 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1860 /* Scan past flags, width and precision, to verify kind. */
1861 for (scan2
= scan
; *++scan2
&& strchr ("-+ #", *scan2
);)
1863 while (ISDIGIT (*scan2
))
1866 for (scan2
++; ISDIGIT (*scan2
); scan2
++)
1868 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2
))
1870 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1875 else if (strchr ("ACT", *scan2
) && scan2
[1])
1877 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1878 *scan2
| (scan2
[1] << 8));
1885 /* An unrecognized % escape. Print the char after the %. */
1886 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1888 segmentp
= make_segment (segmentp
, format
, scan
- format
,
1897 make_segment (segmentp
, format
, scan
- format
, KIND_PLAIN
);
1898 our_pred
->need_type
= false;
1899 our_pred
->need_stat
= fprintf_stat_needed
;
1903 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1904 from the text in FORMAT, which has length LEN.
1905 Return the address of the `next' pointer of the new segment. */
1907 static struct segment
**
1908 make_segment (struct segment
**segment
, char *format
, int len
, int kind
)
1912 *segment
= (struct segment
*) xmalloc (sizeof (struct segment
));
1914 (*segment
)->kind
= kind
;
1915 (*segment
)->next
= NULL
;
1916 (*segment
)->text_len
= len
;
1918 fmt
= (*segment
)->text
= xmalloc (len
+ sizeof "d");
1919 strncpy (fmt
, format
, len
);
1922 switch (kind
& 0xff)
1924 case KIND_PLAIN
: /* Plain text string, no % conversion. */
1925 case KIND_STOP
: /* Terminate argument, no newline. */
1928 case 'a': /* atime in `ctime' format */
1929 case 'A': /* atime in user-specified strftime format */
1930 case 'c': /* ctime in `ctime' format */
1931 case 'C': /* ctime in user-specified strftime format */
1932 case 'F': /* filesystem type */
1933 case 'g': /* group name */
1934 case 'i': /* inode number */
1935 case 'l': /* object of symlink */
1936 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
1937 case 's': /* size in bytes */
1938 case 't': /* mtime in `ctime' format */
1939 case 'T': /* mtime in user-specified strftime format */
1940 case 'u': /* user name */
1941 case 'y': /* file type */
1942 case 'Y': /* symlink pointed file type */
1943 fprintf_stat_needed
= true;
1945 case 'f': /* basename of path */
1946 case 'h': /* leading directories part of path */
1947 case 'H': /* ARGV element file was found under */
1948 case 'p': /* pathname */
1949 case 'P': /* pathname with ARGV element stripped */
1953 /* Numeric items that one might expect to honour
1954 * #, 0, + flags but which do not.
1956 case 'G': /* GID number */
1957 case 'U': /* UID number */
1958 case 'b': /* size in 512-byte blocks */
1959 case 'D': /* Filesystem device on which the file exits */
1960 case 'k': /* size in 1K blocks */
1961 case 'n': /* number of links */
1962 fprintf_stat_needed
= true;
1966 /* Numeric items that DO honour #, 0, + flags.
1968 case 'd': /* depth in search tree (0 = ARGV element) */
1972 case 'm': /* mode as octal number (perms only) */
1974 fprintf_stat_needed
= true;
1979 return (&(*segment
)->next
);
1983 check_path_safety(const char *action
)
1985 const char *path
= getenv("PATH");
1987 s
= next_element(path
, 1);
1988 while ((s
= next_element ((char *) NULL
, 1)) != NULL
)
1990 if (0 == strcmp(s
, "."))
1992 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)"),
1999 /* handles both exec and ok predicate */
2000 #if defined(NEW_EXEC)
2001 /* handles both exec and ok predicate */
2003 new_insert_exec_ok (const char *action
,
2008 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
2009 int i
; /* Index into cmd args */
2010 int saw_braces
; /* True if previous arg was '{}'. */
2011 boolean allow_plus
; /* True if + is a valid terminator */
2012 int brace_count
; /* Number of instances of {}. */
2014 struct predicate
*our_pred
;
2015 struct exec_val
*execp
; /* Pointer for efficiency. */
2017 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2020 our_pred
= insert_primary (func
);
2021 our_pred
->side_effects
= true;
2022 our_pred
->no_default_print
= true;
2023 execp
= &our_pred
->args
.exec_vec
;
2025 if ((func
!= pred_okdir
) && (func
!= pred_ok
))
2030 if ((func
== pred_execdir
) || (func
== pred_okdir
))
2032 options
.ignore_readdir_race
= false;
2033 check_path_safety(action
);
2034 execp
->use_current_dir
= true;
2038 execp
->use_current_dir
= false;
2041 our_pred
->args
.exec_vec
.multiple
= 0;
2043 /* Count the number of args with path replacements, up until the ';'.
2044 * Also figure out if the command is terminated by ";" or by "+".
2047 for (end
= start
, saw_braces
=0, brace_count
=0;
2049 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2052 /* For -exec and -execdir, "{} +" can terminate the command. */
2054 && argv
[end
][0] == '+' && argv
[end
][1] == 0
2057 our_pred
->args
.exec_vec
.multiple
= 1;
2062 if (strstr (argv
[end
], "{}"))
2067 if (0 == end
&& (func
== pred_execdir
|| func
== pred_okdir
))
2069 /* The POSIX standard says that {} replacement should
2070 * occur even in the utility name. This is insecure
2071 * since it means we will be executing a command whose
2072 * name is chosen according to whatever find finds in
2073 * the filesystem. That can be influenced by an
2074 * attacker. Hence for -execdir and -okdir this is not
2075 * allowed. We can specify this as those options are
2076 * not defined by POSIX.
2078 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
2083 /* Fail if no command given or no semicolon found. */
2084 if ((end
== start
) || (argv
[end
] == NULL
))
2091 if (our_pred
->args
.exec_vec
.multiple
&& brace_count
> 1)
2095 if (func
== pred_execdir
)
2101 _("Only one instance of {} is supported with -exec%s ... +"),
2105 /* execp->ctl = xmalloc(sizeof struct buildcmd_control); */
2106 bc_init_controlinfo(&execp
->ctl
);
2107 execp
->ctl
.exec_callback
= launch
;
2109 if (our_pred
->args
.exec_vec
.multiple
)
2111 /* "+" terminator, so we can just append our arguments after the
2112 * command and initial arguments.
2114 execp
->replace_vec
= NULL
;
2115 execp
->ctl
.replace_pat
= NULL
;
2116 execp
->ctl
.rplen
= 0;
2117 execp
->ctl
.lines_per_exec
= 0; /* no limit */
2118 execp
->ctl
.args_per_exec
= 0; /* no limit */
2120 /* remember how many arguments there are */
2121 execp
->ctl
.initial_argc
= (end
-start
) - 1;
2123 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
2124 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
2126 /* Gather the initial arguments. Skip the {}. */
2127 for (i
=start
; i
<end
-1; ++i
)
2129 bc_push_arg(&execp
->ctl
, &execp
->state
,
2130 argv
[i
], strlen(argv
[i
])+1,
2137 /* Semicolon terminator - more than one {} is supported, so we
2138 * have to do brace-replacement.
2140 execp
->num_args
= end
- start
;
2142 execp
->ctl
.replace_pat
= "{}";
2143 execp
->ctl
.rplen
= strlen(execp
->ctl
.replace_pat
);
2144 execp
->ctl
.lines_per_exec
= 0; /* no limit */
2145 execp
->ctl
.args_per_exec
= 0; /* no limit */
2146 execp
->replace_vec
= xmalloc(sizeof(char*)*execp
->num_args
);
2149 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2150 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
2152 /* Remember the (pre-replacement) arguments for later. */
2153 for (i
=0; i
<execp
->num_args
; ++i
)
2155 execp
->replace_vec
[i
] = argv
[i
+start
];
2159 if (argv
[end
] == NULL
)
2167 /* handles both exec and ok predicate */
2169 old_insert_exec_ok (boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
2171 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
2172 int num_paths
; /* Number of args with path replacements. */
2173 int path_pos
; /* Index in array of path replacements. */
2174 int vec_pos
; /* Index in array of args. */
2175 struct predicate
*our_pred
;
2176 struct exec_val
*execp
; /* Pointer for efficiency. */
2178 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2181 /* Count the number of args with path replacements, up until the ';'. */
2183 for (end
= start
, num_paths
= 0;
2185 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2187 if (strstr (argv
[end
], "{}"))
2189 /* Fail if no command given or no semicolon found. */
2190 if ((end
== start
) || (argv
[end
] == NULL
))
2196 our_pred
= insert_primary (func
);
2197 our_pred
->side_effects
= true;
2198 our_pred
->no_default_print
= true;
2199 execp
= &our_pred
->args
.exec_vec
;
2200 execp
->usercontext
= our_pred
;
2201 execp
->use_current_dir
= false;
2203 (struct path_arg
*) xmalloc (sizeof (struct path_arg
) * (num_paths
+ 1));
2204 execp
->vec
= (char **) xmalloc (sizeof (char *) * (end
- start
+ 1));
2205 /* Record the positions of all args, and the args with path replacements. */
2206 for (end
= start
, path_pos
= vec_pos
= 0;
2208 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2213 execp
->paths
[path_pos
].count
= 0;
2214 for (p
= argv
[end
]; *p
; ++p
)
2215 if (p
[0] == '{' && p
[1] == '}')
2217 execp
->paths
[path_pos
].count
++;
2220 if (execp
->paths
[path_pos
].count
)
2222 execp
->paths
[path_pos
].offset
= vec_pos
;
2223 execp
->paths
[path_pos
].origarg
= argv
[end
];
2226 execp
->vec
[vec_pos
++] = argv
[end
];
2228 execp
->paths
[path_pos
].offset
= -1;
2229 execp
->vec
[vec_pos
] = NULL
;
2231 if (argv
[end
] == NULL
)
2242 insert_exec_ok (const char *action
, PRED_FUNC func
, char **argv
, int *arg_ptr
)
2244 #if defined(NEW_EXEC)
2245 return new_insert_exec_ok(action
, func
, argv
, arg_ptr
);
2247 return old_insert_exec_ok(func
, argv
, arg_ptr
);
2253 /* Get a number of days and comparison type.
2254 STR is the ASCII representation.
2255 Set *NUM_DAYS to the number of days, taken as being from
2256 the current moment (or possibly midnight). Thus the sense of the
2257 comparison type appears to be reversed.
2258 Set *COMP_TYPE to the kind of comparison that is requested.
2260 Return true if all okay, false if input error.
2262 Used by -atime, -ctime and -mtime (parsers) to
2263 get the appropriate information for a time predicate processor. */
2266 get_num_days (char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
)
2268 boolean r
= get_num (str
, num_days
, comp_type
);
2272 case COMP_LT
: *comp_type
= COMP_GT
; break;
2273 case COMP_GT
: *comp_type
= COMP_LT
; break;
2279 /* Insert a time predicate PRED.
2280 ARGV is a pointer to the argument array.
2281 ARG_PTR is a pointer to an index into the array, incremented if
2284 Return true if input is valid, false if not.
2286 A new predicate node is assigned, along with an argument node
2287 obtained with malloc.
2289 Used by -atime, -ctime, and -mtime parsers. */
2292 insert_time (char **argv
, int *arg_ptr
, PRED_FUNC pred
)
2294 struct predicate
*our_pred
;
2296 enum comparison_type c_type
;
2299 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2301 if (!get_num_days (argv
[*arg_ptr
], &num_days
, &c_type
))
2304 /* Figure out the timestamp value we are looking for. */
2305 t
= ( options
.cur_day_start
- num_days
* DAYSECS
2306 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2310 /* We introduce a scope in which 'val' can be declared, for the
2311 * benefit of compilers that are really C89 compilers
2312 * which support intmax_t because config.h #defines it
2314 intmax_t val
= ( (intmax_t)options
.cur_day_start
- num_days
* DAYSECS
2315 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2318 /* Check for possibility of an overflow */
2319 if ( (intmax_t)t
!= val
)
2321 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv
[*arg_ptr
]);
2325 our_pred
= insert_primary (pred
);
2326 our_pred
->args
.info
.kind
= c_type
;
2327 our_pred
->args
.info
.negative
= t
< 0;
2328 our_pred
->args
.info
.l_val
= t
;
2331 fprintf (stderr
, "inserting %s\n", our_pred
->p_name
);
2332 fprintf (stderr
, " type: %s %s ",
2333 (c_type
== COMP_GT
) ? "gt" :
2334 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2335 (c_type
== COMP_GT
) ? " >" :
2336 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? ">=" : " ?")));
2337 t
= our_pred
->args
.info
.l_val
;
2338 fprintf (stderr
, "%ju %s", (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2339 if (c_type
== COMP_EQ
)
2341 t
= our_pred
->args
.info
.l_val
+= DAYSECS
;
2342 fprintf (stderr
, " < %ju %s",
2343 (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2344 our_pred
->args
.info
.l_val
-= DAYSECS
;
2350 /* Get a number with comparision information.
2351 The sense of the comparision information is 'normal'; that is,
2352 '+' looks for a count > than the number and '-' less than.
2354 STR is the ASCII representation of the number.
2355 Set *NUM to the number.
2356 Set *COMP_TYPE to the kind of comparison that is requested.
2358 Return true if all okay, false if input error. */
2361 get_num (char *str
, uintmax_t *num
, enum comparison_type
*comp_type
)
2368 *comp_type
= COMP_GT
;
2372 *comp_type
= COMP_LT
;
2376 *comp_type
= COMP_EQ
;
2380 return xstrtoumax (str
, NULL
, 10, num
, "") == LONGINT_OK
;
2383 /* Insert a number predicate.
2384 ARGV is a pointer to the argument array.
2385 *ARG_PTR is an index into ARGV, incremented if all went well.
2386 *PRED is the predicate processor to insert.
2388 Return true if input is valid, false if error.
2390 A new predicate node is assigned, along with an argument node
2391 obtained with malloc.
2393 Used by -inum and -links parsers. */
2396 insert_num (char **argv
, int *arg_ptr
, PRED_FUNC pred
)
2398 struct predicate
*our_pred
;
2400 enum comparison_type c_type
;
2402 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2404 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
2406 our_pred
= insert_primary (pred
);
2407 our_pred
->args
.info
.kind
= c_type
;
2408 our_pred
->args
.info
.l_val
= num
;
2411 fprintf (stderr
, "inserting %s\n", our_pred
->p_name
);
2412 fprintf (stderr
, " type: %s %s ",
2413 (c_type
== COMP_GT
) ? "gt" :
2414 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2415 (c_type
== COMP_GT
) ? " >" :
2416 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? " =" : " ?")));
2417 fprintf (stderr
, "%ju\n", our_pred
->args
.info
.l_val
);
2423 open_output_file (char *path
)
2427 if (!strcmp (path
, "/dev/stderr"))
2429 else if (!strcmp (path
, "/dev/stdout"))
2431 f
= fopen (path
, "w");
2433 error (1, errno
, "%s", path
);