1 /* parser.c -- convert the command line args into an expression tree.
2 Copyright (C) 1990, 91, 92, 93, 94, 2000, 2001, 2003, 2004, 2005 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"
41 # define _(Text) gettext (Text)
46 # define N_(String) gettext_noop (String)
48 /* See locate.c for explanation as to why not use (String) */
49 # define N_(String) String
52 #if !defined (isascii) || defined (STDC_HEADERS)
59 #define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
60 #define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
69 static boolean parse_amin
PARAMS((char *argv
[], int *arg_ptr
));
70 static boolean parse_and
PARAMS((char *argv
[], int *arg_ptr
));
71 static boolean parse_anewer
PARAMS((char *argv
[], int *arg_ptr
));
72 static boolean parse_atime
PARAMS((char *argv
[], int *arg_ptr
));
73 boolean parse_close
PARAMS((char *argv
[], int *arg_ptr
));
74 static boolean parse_cmin
PARAMS((char *argv
[], int *arg_ptr
));
75 static boolean parse_cnewer
PARAMS((char *argv
[], int *arg_ptr
));
76 static boolean parse_comma
PARAMS((char *argv
[], int *arg_ptr
));
77 static boolean parse_ctime
PARAMS((char *argv
[], int *arg_ptr
));
78 static boolean parse_daystart
PARAMS((char *argv
[], int *arg_ptr
));
79 static boolean parse_delete
PARAMS((char *argv
[], int *arg_ptr
));
80 static boolean parse_d
PARAMS((char *argv
[], int *arg_ptr
));
81 static boolean parse_depth
PARAMS((char *argv
[], int *arg_ptr
));
82 static boolean parse_empty
PARAMS((char *argv
[], int *arg_ptr
));
83 static boolean parse_exec
PARAMS((char *argv
[], int *arg_ptr
));
84 static boolean parse_execdir
PARAMS((char *argv
[], int *arg_ptr
));
85 static boolean parse_false
PARAMS((char *argv
[], int *arg_ptr
));
86 static boolean parse_fls
PARAMS((char *argv
[], int *arg_ptr
));
87 static boolean parse_fprintf
PARAMS((char *argv
[], int *arg_ptr
));
88 static boolean parse_follow
PARAMS((char *argv
[], int *arg_ptr
));
89 static boolean parse_fprint
PARAMS((char *argv
[], int *arg_ptr
));
90 static boolean parse_fprint0
PARAMS((char *argv
[], int *arg_ptr
));
91 static boolean parse_fstype
PARAMS((char *argv
[], int *arg_ptr
));
92 static boolean parse_gid
PARAMS((char *argv
[], int *arg_ptr
));
93 static boolean parse_group
PARAMS((char *argv
[], int *arg_ptr
));
94 static boolean parse_help
PARAMS((char *argv
[], int *arg_ptr
));
95 static boolean parse_ilname
PARAMS((char *argv
[], int *arg_ptr
));
96 static boolean parse_iname
PARAMS((char *argv
[], int *arg_ptr
));
97 static boolean parse_inum
PARAMS((char *argv
[], int *arg_ptr
));
98 static boolean parse_ipath
PARAMS((char *argv
[], int *arg_ptr
));
99 static boolean parse_iregex
PARAMS((char *argv
[], int *arg_ptr
));
100 static boolean parse_iwholename
PARAMS((char *argv
[], int *arg_ptr
));
101 static boolean parse_links
PARAMS((char *argv
[], int *arg_ptr
));
102 static boolean parse_lname
PARAMS((char *argv
[], int *arg_ptr
));
103 static boolean parse_ls
PARAMS((char *argv
[], int *arg_ptr
));
104 static boolean parse_maxdepth
PARAMS((char *argv
[], int *arg_ptr
));
105 static boolean parse_mindepth
PARAMS((char *argv
[], int *arg_ptr
));
106 static boolean parse_mmin
PARAMS((char *argv
[], int *arg_ptr
));
107 static boolean parse_mtime
PARAMS((char *argv
[], int *arg_ptr
));
108 static boolean parse_name
PARAMS((char *argv
[], int *arg_ptr
));
109 static boolean parse_negate
PARAMS((char *argv
[], int *arg_ptr
));
110 static boolean parse_newer
PARAMS((char *argv
[], int *arg_ptr
));
111 static boolean parse_noleaf
PARAMS((char *argv
[], int *arg_ptr
));
112 static boolean parse_nogroup
PARAMS((char *argv
[], int *arg_ptr
));
113 static boolean parse_nouser
PARAMS((char *argv
[], int *arg_ptr
));
114 static boolean parse_nowarn
PARAMS((char *argv
[], int *arg_ptr
));
115 static boolean parse_ok
PARAMS((char *argv
[], int *arg_ptr
));
116 static boolean parse_okdir
PARAMS((char *argv
[], int *arg_ptr
));
117 boolean parse_open
PARAMS((char *argv
[], int *arg_ptr
));
118 static boolean parse_or
PARAMS((char *argv
[], int *arg_ptr
));
119 static boolean parse_path
PARAMS((char *argv
[], int *arg_ptr
));
120 static boolean parse_perm
PARAMS((char *argv
[], int *arg_ptr
));
121 boolean parse_print
PARAMS((char *argv
[], int *arg_ptr
));
122 static boolean parse_print0
PARAMS((char *argv
[], int *arg_ptr
));
123 static boolean parse_printf
PARAMS((char *argv
[], int *arg_ptr
));
124 static boolean parse_prune
PARAMS((char *argv
[], int *arg_ptr
));
125 static boolean parse_regex
PARAMS((char *argv
[], int *arg_ptr
));
126 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
, boolean ignore_case
));
127 static boolean parse_samefile
PARAMS((char *argv
[], int *arg_ptr
));
128 static boolean parse_size
PARAMS((char *argv
[], int *arg_ptr
));
129 static boolean parse_true
PARAMS((char *argv
[], int *arg_ptr
));
130 static boolean parse_type
PARAMS((char *argv
[], int *arg_ptr
));
131 static boolean parse_uid
PARAMS((char *argv
[], int *arg_ptr
));
132 static boolean parse_used
PARAMS((char *argv
[], int *arg_ptr
));
133 static boolean parse_user
PARAMS((char *argv
[], int *arg_ptr
));
134 static boolean parse_version
PARAMS((char *argv
[], int *arg_ptr
));
135 static boolean parse_wholename
PARAMS((char *argv
[], int *arg_ptr
));
136 static boolean parse_xdev
PARAMS((char *argv
[], int *arg_ptr
));
137 static boolean parse_ignore_race
PARAMS((char *argv
[], int *arg_ptr
));
138 static boolean parse_noignore_race
PARAMS((char *argv
[], int *arg_ptr
));
139 static boolean parse_warn
PARAMS((char *argv
[], int *arg_ptr
));
140 static boolean parse_xtype
PARAMS((char *argv
[], int *arg_ptr
));
141 static boolean parse_quit
PARAMS((char *argv
[], int *arg_ptr
));
143 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
, boolean ignore_case
));
144 static boolean insert_type
PARAMS((char *argv
[], int *arg_ptr
, boolean (*which_pred
)()));
145 static boolean insert_fprintf
PARAMS((FILE *fp
, boolean (*func
)(), char *argv
[], int *arg_ptr
));
146 static struct segment
**make_segment
PARAMS((struct segment
**segment
, char *format
, int len
, int kind
));
147 static boolean insert_exec_ok
PARAMS((const char *action
, boolean (*func
)(), char *argv
[], int *arg_ptr
));
148 static boolean get_num_days
PARAMS((char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
));
149 static boolean insert_time
PARAMS((char *argv
[], int *arg_ptr
, PFB pred
));
150 static boolean get_num
PARAMS((char *str
, uintmax_t *num
, enum comparison_type
*comp_type
));
151 static boolean insert_num
PARAMS((char *argv
[], int *arg_ptr
, PFB pred
));
152 static FILE *open_output_file
PARAMS((char *path
));
155 char *find_pred_name
PARAMS((PFB pred_func
));
162 ARG_OPTION
, /* regular options like -maxdepth */
163 ARG_POSITIONAL_OPTION
, /* options whose position is important (-follow) */
164 ARG_TEST
, /* a like -name */
165 ARG_PUNCTUATION
, /* like -o or ( */
166 ARG_ACTION
/* like -print */
177 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
178 If they are in some Unix versions of find, they are marked `Unix'. */
180 static struct parser_table
const parse_table
[] =
182 {ARG_PUNCTUATION
, "!", parse_negate
},
183 {ARG_PUNCTUATION
, "not", parse_negate
}, /* GNU */
184 {ARG_PUNCTUATION
, "(", parse_open
},
185 {ARG_PUNCTUATION
, ")", parse_close
},
186 {ARG_PUNCTUATION
, ",", parse_comma
}, /* GNU */
187 {ARG_PUNCTUATION
, "a", parse_and
},
188 {ARG_TEST
, "amin", parse_amin
}, /* GNU */
189 {ARG_PUNCTUATION
, "and", parse_and
}, /* GNU */
190 {ARG_TEST
, "anewer", parse_anewer
}, /* GNU */
191 {ARG_TEST
, "atime", parse_atime
},
192 {ARG_TEST
, "cmin", parse_cmin
}, /* GNU */
193 {ARG_TEST
, "cnewer", parse_cnewer
}, /* GNU */
194 #ifdef UNIMPLEMENTED_UNIX
195 /* It's pretty ugly for find to know about archive formats.
196 Plus what it could do with cpio archives is very limited.
197 Better to leave it out. */
198 {ARG_UNIMPLEMENTED
, "cpio", parse_cpio
}, /* Unix */
200 {ARG_TEST
, "ctime", parse_ctime
},
201 {ARG_POSITIONAL_OPTION
, "daystart", parse_daystart
}, /* GNU */
202 {ARG_ACTION
, "delete", parse_delete
}, /* GNU, Mac OS, FreeBSD */
203 {ARG_OPTION
, "d", parse_d
}, /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
204 {ARG_OPTION
, "depth", parse_depth
},
205 {ARG_TEST
, "empty", parse_empty
}, /* GNU */
206 {ARG_ACTION
, "exec", parse_exec
},
207 {ARG_ACTION
, "execdir", parse_execdir
}, /* *BSD, GNU */
208 {ARG_TEST
, "false", parse_false
}, /* GNU */
209 {ARG_ACTION
, "fls", parse_fls
}, /* GNU */
210 {ARG_POSITIONAL_OPTION
, "follow", parse_follow
}, /* GNU, Unix */
211 {ARG_ACTION
, "fprint", parse_fprint
}, /* GNU */
212 {ARG_ACTION
, "fprint0", parse_fprint0
}, /* GNU */
213 {ARG_ACTION
, "fprintf", parse_fprintf
}, /* GNU */
214 {ARG_TEST
, "fstype", parse_fstype
}, /* GNU, Unix */
215 {ARG_TEST
, "gid", parse_gid
}, /* GNU */
216 {ARG_TEST
, "group", parse_group
},
217 {ARG_TEST
, "help", parse_help
}, /* GNU */
218 {ARG_TEST
, "-help", parse_help
}, /* GNU */
219 {ARG_OPTION
, "ignore_readdir_race", parse_ignore_race
}, /* GNU */
220 {ARG_TEST
, "ilname", parse_ilname
}, /* GNU */
221 {ARG_TEST
, "iname", parse_iname
}, /* GNU */
222 {ARG_TEST
, "inum", parse_inum
}, /* GNU, Unix */
223 {ARG_TEST
, "ipath", parse_ipath
}, /* GNU, deprecated in favour of iwholename */
224 {ARG_TEST
, "iregex", parse_iregex
}, /* GNU */
225 {ARG_TEST
, "iwholename", parse_iwholename
}, /* GNU */
226 {ARG_TEST
, "links", parse_links
},
227 {ARG_TEST
, "lname", parse_lname
}, /* GNU */
228 {ARG_ACTION
, "ls", parse_ls
}, /* GNU, Unix */
229 {ARG_OPTION
, "maxdepth", parse_maxdepth
}, /* GNU */
230 {ARG_OPTION
, "mindepth", parse_mindepth
}, /* GNU */
231 {ARG_TEST
, "mmin", parse_mmin
}, /* GNU */
232 {ARG_OPTION
, "mount", parse_xdev
}, /* Unix */
233 {ARG_TEST
, "mtime", parse_mtime
},
234 {ARG_TEST
, "name", parse_name
},
235 #ifdef UNIMPLEMENTED_UNIX
236 {ARG_UNIMPLEMENTED
, "ncpio", parse_ncpio
}, /* Unix */
238 {ARG_TEST
, "newer", parse_newer
},
239 {ARG_OPTION
, "noleaf", parse_noleaf
}, /* GNU */
240 {ARG_TEST
, "nogroup", parse_nogroup
},
241 {ARG_TEST
, "nouser", parse_nouser
},
242 {ARG_OPTION
, "noignore_readdir_race", parse_noignore_race
},/* GNU */
243 {ARG_OPTION
, "nowarn", parse_nowarn
}, /* GNU */
244 {ARG_PUNCTUATION
, "o", parse_or
},
245 {ARG_PUNCTUATION
, "or", parse_or
}, /* GNU */
246 {ARG_ACTION
, "ok", parse_ok
},
247 {ARG_ACTION
, "okdir", parse_okdir
}, /* GNU (-execdir is BSD) */
248 {ARG_TEST
, "path", parse_path
}, /* GNU, HP-UX, GNU prefers wholename */
249 {ARG_TEST
, "perm", parse_perm
},
250 {ARG_ACTION
, "print", parse_print
},
251 {ARG_ACTION
, "print0", parse_print0
}, /* GNU */
252 {ARG_ACTION
, "printf", parse_printf
}, /* GNU */
253 {ARG_TEST
, "prune", parse_prune
},
254 {ARG_ACTION
, "quit", parse_quit
}, /* GNU */
255 {ARG_TEST
, "regex", parse_regex
}, /* GNU */
256 {ARG_TEST
, "samefile", parse_samefile
}, /* GNU */
257 {ARG_TEST
, "size", parse_size
},
258 {ARG_TEST
, "true", parse_true
}, /* GNU */
259 {ARG_TEST
, "type", parse_type
},
260 {ARG_TEST
, "uid", parse_uid
}, /* GNU */
261 {ARG_TEST
, "used", parse_used
}, /* GNU */
262 {ARG_TEST
, "user", parse_user
},
263 {ARG_TEST
, "version", parse_version
}, /* GNU */
264 {ARG_TEST
, "-version", parse_version
}, /* GNU */
265 {ARG_OPTION
, "warn", parse_warn
}, /* GNU */
266 {ARG_TEST
, "wholename", parse_wholename
}, /* GNU, replaces -path */
267 {ARG_OPTION
, "xdev", parse_xdev
},
268 {ARG_TEST
, "xtype", parse_xtype
}, /* GNU */
273 static const char *first_nonoption_arg
= NULL
;
275 /* Return a pointer to the parser function to invoke for predicate
277 Return NULL if SEARCH_NAME is not a valid predicate name. */
280 find_parser (char *search_name
)
283 const char *original_arg
= search_name
;
285 if (*search_name
== '-')
287 for (i
= 0; parse_table
[i
].parser_name
!= 0; i
++)
289 if (strcmp (parse_table
[i
].parser_name
, search_name
) == 0)
291 /* If this is an option, but we have already had a
292 * non-option argument, the user may be under the
293 * impression that the behaviour of the option
294 * argument is conditional on some preceding
295 * tests. This might typically be the case with,
296 * for example, -maxdepth.
298 * The options -daystart and -follow are exempt
299 * from this treatment, since their positioning
300 * in the command line does have an effect on
301 * subsequent tests but not previous ones. That
302 * might be intentional on the part of the user.
304 if (parse_table
[i
].type
!= ARG_POSITIONAL_OPTION
)
306 /* Something other than -follow/-daystart.
307 * If this is an option, check if it followed
308 * a non-option and if so, issue a warning.
310 if (parse_table
[i
].type
== ARG_OPTION
)
312 if ((first_nonoption_arg
!= NULL
)
313 && options
.warnings
)
315 /* option which folows a non-option */
317 _("warning: you have specified the %s "
318 "option after a non-option argument %s, "
319 "but options are not positional (%s affects "
320 "tests specified before it as well as those "
321 "specified after it). Please specify options "
322 "before other arguments.\n"),
330 /* Not an option or a positional option,
331 * so remember we've seen it in order to
332 * use it in a possible future warning message.
334 if (first_nonoption_arg
== NULL
)
336 first_nonoption_arg
= original_arg
;
341 return (parse_table
[i
].parser_func
);
347 /* The parsers are responsible to continue scanning ARGV for
348 their arguments. Each parser knows what is and isn't
351 ARGV is the argument array.
352 *ARG_PTR is the index to start at in ARGV,
353 updated to point beyond the last element consumed.
355 The predicate structure is updated with the new information. */
358 parse_amin (char **argv
, int *arg_ptr
)
360 struct predicate
*our_pred
;
362 enum comparison_type c_type
;
365 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
367 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
369 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
370 our_pred
= insert_primary (pred_amin
);
371 our_pred
->args
.info
.kind
= c_type
;
372 our_pred
->args
.info
.negative
= t
< 0;
373 our_pred
->args
.info
.l_val
= t
;
379 parse_and (char **argv
, int *arg_ptr
)
381 struct predicate
*our_pred
;
386 our_pred
= get_new_pred ();
387 our_pred
->pred_func
= pred_and
;
389 our_pred
->p_name
= find_pred_name (pred_and
);
391 our_pred
->p_type
= BI_OP
;
392 our_pred
->p_prec
= AND_PREC
;
393 our_pred
->need_stat
= our_pred
->need_type
= false;
398 parse_anewer (char **argv
, int *arg_ptr
)
400 struct predicate
*our_pred
;
401 struct stat stat_newer
;
403 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
405 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
406 error (1, errno
, "%s", argv
[*arg_ptr
]);
407 our_pred
= insert_primary (pred_anewer
);
408 our_pred
->args
.time
= stat_newer
.st_mtime
;
414 parse_atime (char **argv
, int *arg_ptr
)
416 return (insert_time (argv
, arg_ptr
, pred_atime
));
420 parse_close (char **argv
, int *arg_ptr
)
422 struct predicate
*our_pred
;
427 our_pred
= get_new_pred ();
428 our_pred
->pred_func
= pred_close
;
430 our_pred
->p_name
= find_pred_name (pred_close
);
432 our_pred
->p_type
= CLOSE_PAREN
;
433 our_pred
->p_prec
= NO_PREC
;
434 our_pred
->need_stat
= our_pred
->need_type
= false;
439 parse_cmin (char **argv
, int *arg_ptr
)
441 struct predicate
*our_pred
;
443 enum comparison_type c_type
;
446 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
448 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
450 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
451 our_pred
= insert_primary (pred_cmin
);
452 our_pred
->args
.info
.kind
= c_type
;
453 our_pred
->args
.info
.negative
= t
< 0;
454 our_pred
->args
.info
.l_val
= t
;
460 parse_cnewer (char **argv
, int *arg_ptr
)
462 struct predicate
*our_pred
;
463 struct stat stat_newer
;
465 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
467 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
468 error (1, errno
, "%s", argv
[*arg_ptr
]);
469 our_pred
= insert_primary (pred_cnewer
);
470 our_pred
->args
.time
= stat_newer
.st_mtime
;
476 parse_comma (char **argv
, int *arg_ptr
)
478 struct predicate
*our_pred
;
483 our_pred
= get_new_pred ();
484 our_pred
->pred_func
= pred_comma
;
486 our_pred
->p_name
= find_pred_name (pred_comma
);
488 our_pred
->p_type
= BI_OP
;
489 our_pred
->p_prec
= COMMA_PREC
;
490 our_pred
->need_stat
= our_pred
->need_type
= false;
495 parse_ctime (char **argv
, int *arg_ptr
)
497 return (insert_time (argv
, arg_ptr
, pred_ctime
));
501 parse_daystart (char **argv
, int *arg_ptr
)
508 if (options
.full_days
== false)
510 options
.cur_day_start
+= DAYSECS
;
511 local
= localtime (&options
.cur_day_start
);
512 options
.cur_day_start
-= (local
513 ? (local
->tm_sec
+ local
->tm_min
* 60
514 + local
->tm_hour
* 3600)
515 : options
.cur_day_start
% DAYSECS
);
516 options
.full_days
= true;
522 parse_delete (argv
, arg_ptr
)
526 struct predicate
*our_pred
;
530 our_pred
= insert_primary (pred_delete
);
531 our_pred
->side_effects
= true;
532 our_pred
->no_default_print
= true;
533 /* -delete implies -depth */
534 options
.do_dir_first
= false;
539 parse_depth (char **argv
, int *arg_ptr
)
544 options
.do_dir_first
= false;
549 parse_d (char **argv
, int *arg_ptr
)
554 if (options
.warnings
)
557 _("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
559 return parse_depth(argv
, arg_ptr
);
563 parse_empty (char **argv
, int *arg_ptr
)
568 insert_primary (pred_empty
);
573 parse_exec (char **argv
, int *arg_ptr
)
575 return (insert_exec_ok ("-exec", pred_exec
, argv
, arg_ptr
));
579 parse_execdir (char **argv
, int *arg_ptr
)
581 return (insert_exec_ok ("-execdir", pred_execdir
, argv
, arg_ptr
));
585 parse_false (char **argv
, int *arg_ptr
)
587 struct predicate
*our_pred
;
592 our_pred
= insert_primary (pred_false
);
593 our_pred
->need_stat
= our_pred
->need_type
= false;
598 parse_fls (char **argv
, int *arg_ptr
)
600 struct predicate
*our_pred
;
602 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
604 our_pred
= insert_primary (pred_fls
);
605 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
606 our_pred
->side_effects
= true;
607 our_pred
->no_default_print
= true;
613 parse_fprintf (char **argv
, int *arg_ptr
)
617 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
619 if (argv
[*arg_ptr
+ 1] == NULL
)
621 /* Ensure we get "missing arg" message, not "invalid arg". */
625 fp
= open_output_file (argv
[*arg_ptr
]);
627 return (insert_fprintf (fp
, pred_fprintf
, argv
, arg_ptr
));
631 parse_follow (char **argv
, int *arg_ptr
)
636 set_follow_state(SYMLINK_ALWAYS_DEREF
);
641 parse_fprint (char **argv
, int *arg_ptr
)
643 struct predicate
*our_pred
;
645 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
647 our_pred
= insert_primary (pred_fprint
);
648 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
649 our_pred
->side_effects
= true;
650 our_pred
->no_default_print
= true;
651 our_pred
->need_stat
= our_pred
->need_type
= false;
657 parse_fprint0 (char **argv
, int *arg_ptr
)
659 struct predicate
*our_pred
;
661 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
663 our_pred
= insert_primary (pred_fprint0
);
664 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
665 our_pred
->side_effects
= true;
666 our_pred
->no_default_print
= true;
667 our_pred
->need_stat
= our_pred
->need_type
= false;
673 parse_fstype (char **argv
, int *arg_ptr
)
675 struct predicate
*our_pred
;
677 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
679 our_pred
= insert_primary (pred_fstype
);
680 our_pred
->args
.str
= argv
[*arg_ptr
];
686 parse_gid (char **argv
, int *arg_ptr
)
688 return (insert_num (argv
, arg_ptr
, pred_gid
));
692 parse_group (char **argv
, int *arg_ptr
)
694 struct group
*cur_gr
;
695 struct predicate
*our_pred
;
699 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
701 cur_gr
= getgrnam (argv
[*arg_ptr
]);
704 gid
= cur_gr
->gr_gid
;
707 gid_len
= strspn (argv
[*arg_ptr
], "0123456789");
708 if ((gid_len
== 0) || (argv
[*arg_ptr
][gid_len
] != '\0'))
710 gid
= atoi (argv
[*arg_ptr
]);
712 our_pred
= insert_primary (pred_group
);
713 our_pred
->args
.gid
= gid
;
719 parse_help (char **argv
, int *arg_ptr
)
725 Usage: %s [path...] [expression]\n"), program_name
);
727 default path is the current directory; default expression is -print\n\
728 expression may consist of: operators, options, tests, and actions:\n"));
730 operators (decreasing precedence; -and is implicit where no others are given):\n\
731 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
732 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
734 positional options (always true): -daystart -follow\n\
735 normal options (always true, specified before other expressions):\n\
736 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
737 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
739 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
740 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
741 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
742 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
744 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
745 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
746 -used N -user NAME -xtype [bcdpfls]\n"));
748 actions: -exec COMMAND ; -fprint FILE -fprint0 FILE -fprintf FILE FORMAT\n\
749 -fls FILE -ok COMMAND ; -print -print0 -printf FORMAT -prune -ls -delete\n\
751 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
752 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
753 email to <bug-findutils@gnu.org>."));
758 parse_ilname (char **argv
, int *arg_ptr
)
760 struct predicate
*our_pred
;
762 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
764 our_pred
= insert_primary (pred_ilname
);
765 our_pred
->args
.str
= argv
[*arg_ptr
];
771 /* sanity check the fnmatch() function to make sure
772 * it really is the GNU version.
775 fnmatch_sanitycheck()
777 /* fprintf(stderr, "Performing find sanity check..."); */
778 if (0 != fnmatch("foo", "foo", 0)
779 || 0 == fnmatch("Foo", "foo", 0)
780 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD
))
782 error (1, 0, _("sanity check of the fnmatch() library function failed."));
783 /* fprintf(stderr, "FAILED\n"); */
787 /* fprintf(stderr, "OK\n"); */
793 check_name_arg(const char *pred
, const char *arg
)
795 if (strchr(arg
, '/'))
797 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'."),
800 return true; /* allow it anyway */
806 parse_iname (char **argv
, int *arg_ptr
)
808 struct predicate
*our_pred
;
810 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
812 if (!check_name_arg("-iname", argv
[*arg_ptr
]))
815 fnmatch_sanitycheck();
817 our_pred
= insert_primary (pred_iname
);
818 our_pred
->need_stat
= our_pred
->need_type
= false;
819 our_pred
->args
.str
= argv
[*arg_ptr
];
825 parse_inum (char **argv
, int *arg_ptr
)
827 return (insert_num (argv
, arg_ptr
, pred_inum
));
830 /* -ipath is deprecated (at RMS's request) in favour of
831 * -iwholename. See the node "GNU Manuals" in standards.texi
832 * for the rationale for this (basically, GNU prefers the use
833 * of the phrase "file name" to "path name"
836 parse_ipath (char **argv
, int *arg_ptr
)
839 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
841 return parse_iwholename(argv
, arg_ptr
);
845 parse_iwholename (char **argv
, int *arg_ptr
)
847 struct predicate
*our_pred
;
849 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
852 fnmatch_sanitycheck();
854 our_pred
= insert_primary (pred_ipath
);
855 our_pred
->need_stat
= our_pred
->need_type
= false;
856 our_pred
->args
.str
= argv
[*arg_ptr
];
862 parse_iregex (char **argv
, int *arg_ptr
)
864 return insert_regex (argv
, arg_ptr
, true);
868 parse_links (char **argv
, int *arg_ptr
)
870 return (insert_num (argv
, arg_ptr
, pred_links
));
874 parse_lname (char **argv
, int *arg_ptr
)
876 struct predicate
*our_pred
;
881 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
884 fnmatch_sanitycheck();
886 our_pred
= insert_primary (pred_lname
);
887 our_pred
->args
.str
= argv
[*arg_ptr
];
893 parse_ls (char **argv
, int *arg_ptr
)
895 struct predicate
*our_pred
;
900 our_pred
= insert_primary (pred_ls
);
901 our_pred
->side_effects
= true;
902 our_pred
->no_default_print
= true;
907 parse_maxdepth (char **argv
, int *arg_ptr
)
911 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
913 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
914 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
916 options
.maxdepth
= atoi (argv
[*arg_ptr
]);
917 if (options
.maxdepth
< 0)
924 parse_mindepth (char **argv
, int *arg_ptr
)
928 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
930 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
931 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
933 options
.mindepth
= atoi (argv
[*arg_ptr
]);
934 if (options
.mindepth
< 0)
941 parse_mmin (char **argv
, int *arg_ptr
)
943 struct predicate
*our_pred
;
945 enum comparison_type c_type
;
948 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
950 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
952 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
953 our_pred
= insert_primary (pred_mmin
);
954 our_pred
->args
.info
.kind
= c_type
;
955 our_pred
->args
.info
.negative
= t
< 0;
956 our_pred
->args
.info
.l_val
= t
;
962 parse_mtime (char **argv
, int *arg_ptr
)
964 return (insert_time (argv
, arg_ptr
, pred_mtime
));
968 parse_name (char **argv
, int *arg_ptr
)
970 struct predicate
*our_pred
;
975 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
977 if (!check_name_arg("-name", argv
[*arg_ptr
]))
979 fnmatch_sanitycheck();
981 our_pred
= insert_primary (pred_name
);
982 our_pred
->need_stat
= our_pred
->need_type
= false;
983 our_pred
->args
.str
= argv
[*arg_ptr
];
989 parse_negate (char **argv
, int *arg_ptr
)
991 struct predicate
*our_pred
;
996 our_pred
= get_new_pred_chk_op ();
997 our_pred
->pred_func
= pred_negate
;
999 our_pred
->p_name
= find_pred_name (pred_negate
);
1001 our_pred
->p_type
= UNI_OP
;
1002 our_pred
->p_prec
= NEGATE_PREC
;
1003 our_pred
->need_stat
= our_pred
->need_type
= false;
1008 parse_newer (char **argv
, int *arg_ptr
)
1010 struct predicate
*our_pred
;
1011 struct stat stat_newer
;
1016 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1018 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
1019 error (1, errno
, "%s", argv
[*arg_ptr
]);
1020 our_pred
= insert_primary (pred_newer
);
1021 our_pred
->args
.time
= stat_newer
.st_mtime
;
1027 parse_noleaf (char **argv
, int *arg_ptr
)
1032 options
.no_leaf_check
= true;
1037 /* Arbitrary amount by which to increase size
1038 of `uid_unused' and `gid_unused'. */
1039 #define ALLOC_STEP 2048
1041 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1042 char *uid_unused
= NULL
;
1044 /* Number of elements in `uid_unused'. */
1045 unsigned uid_allocated
;
1047 /* Similar for GIDs and group entries. */
1048 char *gid_unused
= NULL
;
1049 unsigned gid_allocated
;
1053 parse_nogroup (char **argv
, int *arg_ptr
)
1055 struct predicate
*our_pred
;
1060 our_pred
= insert_primary (pred_nogroup
);
1062 if (gid_unused
== NULL
)
1066 gid_allocated
= ALLOC_STEP
;
1067 gid_unused
= xmalloc (gid_allocated
);
1068 memset (gid_unused
, 1, gid_allocated
);
1070 while ((gr
= getgrent ()) != NULL
)
1072 if ((unsigned) gr
->gr_gid
>= gid_allocated
)
1074 unsigned new_allocated
= (unsigned) gr
->gr_gid
+ ALLOC_STEP
;
1075 gid_unused
= xrealloc (gid_unused
, new_allocated
);
1076 memset (gid_unused
+ gid_allocated
, 1,
1077 new_allocated
- gid_allocated
);
1078 gid_allocated
= new_allocated
;
1080 gid_unused
[(unsigned) gr
->gr_gid
] = 0;
1089 parse_nouser (char **argv
, int *arg_ptr
)
1091 struct predicate
*our_pred
;
1096 our_pred
= insert_primary (pred_nouser
);
1098 if (uid_unused
== NULL
)
1102 uid_allocated
= ALLOC_STEP
;
1103 uid_unused
= xmalloc (uid_allocated
);
1104 memset (uid_unused
, 1, uid_allocated
);
1106 while ((pw
= getpwent ()) != NULL
)
1108 if ((unsigned) pw
->pw_uid
>= uid_allocated
)
1110 unsigned new_allocated
= (unsigned) pw
->pw_uid
+ ALLOC_STEP
;
1111 uid_unused
= xrealloc (uid_unused
, new_allocated
);
1112 memset (uid_unused
+ uid_allocated
, 1,
1113 new_allocated
- uid_allocated
);
1114 uid_allocated
= new_allocated
;
1116 uid_unused
[(unsigned) pw
->pw_uid
] = 0;
1125 parse_nowarn (char **argv
, int *arg_ptr
)
1130 options
.warnings
= false;
1135 parse_ok (char **argv
, int *arg_ptr
)
1137 return (insert_exec_ok ("-ok", pred_ok
, argv
, arg_ptr
));
1141 parse_okdir (char **argv
, int *arg_ptr
)
1143 return (insert_exec_ok ("-okdir", pred_okdir
, argv
, arg_ptr
));
1147 parse_open (char **argv
, int *arg_ptr
)
1149 struct predicate
*our_pred
;
1154 our_pred
= get_new_pred_chk_op ();
1155 our_pred
->pred_func
= pred_open
;
1157 our_pred
->p_name
= find_pred_name (pred_open
);
1159 our_pred
->p_type
= OPEN_PAREN
;
1160 our_pred
->p_prec
= NO_PREC
;
1161 our_pred
->need_stat
= our_pred
->need_type
= false;
1166 parse_or (char **argv
, int *arg_ptr
)
1168 struct predicate
*our_pred
;
1173 our_pred
= get_new_pred ();
1174 our_pred
->pred_func
= pred_or
;
1176 our_pred
->p_name
= find_pred_name (pred_or
);
1178 our_pred
->p_type
= BI_OP
;
1179 our_pred
->p_prec
= OR_PREC
;
1180 our_pred
->need_stat
= our_pred
->need_type
= false;
1184 /* -path is deprecated (at RMS's request) in favour of
1185 * -iwholename. See the node "GNU Manuals" in standards.texi
1186 * for the rationale for this (basically, GNU prefers the use
1187 * of the phrase "file name" to "path name".
1189 * We do not issue a warning that this usage is deprecated
1190 * since HPUX find supports this predicate also.
1193 parse_path (char **argv
, int *arg_ptr
)
1195 return parse_wholename(argv
, arg_ptr
);
1199 parse_wholename (char **argv
, int *arg_ptr
)
1201 struct predicate
*our_pred
;
1203 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1205 our_pred
= insert_primary (pred_path
);
1206 our_pred
->need_stat
= our_pred
->need_type
= false;
1207 our_pred
->args
.str
= argv
[*arg_ptr
];
1213 parse_perm (char **argv
, int *arg_ptr
)
1217 enum permissions_type kind
= PERM_EXACT
;
1218 struct mode_change
*change
= NULL
;
1219 struct predicate
*our_pred
;
1221 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1224 switch (argv
[*arg_ptr
][0])
1228 kind
= PERM_AT_LEAST
;
1232 change
= mode_compile (argv
[*arg_ptr
]);
1235 /* Most likely the caller is an old script that is still
1236 * using the obsolete GNU syntax '-perm +MODE'. This old
1237 * syntax was withdrawn in favor of '-perm /MODE' because
1238 * it is incompatible with POSIX in some cases, but we
1239 * still support uses of it that are not incompatible with
1247 /* This is a POSIX-compatible usage */
1253 case '/': /* GNU extension */
1259 /* For example, '-perm 0644', which is valid and matches
1260 * only files whose mode is exactly 0644.
1262 * We do nothing here, because mode_start and kind are already
1270 change
= mode_compile (argv
[*arg_ptr
] + mode_start
);
1272 error (1, 0, _("invalid mode `%s'"), argv
[*arg_ptr
]);
1274 perm_val
= mode_adjust (0, change
, 0);
1277 our_pred
= insert_primary (pred_perm
);
1279 switch (argv
[*arg_ptr
][0])
1282 our_pred
->args
.perm
.kind
= PERM_AT_LEAST
;
1285 our_pred
->args
.perm
.kind
= PERM_ANY
;
1288 our_pred
->args
.perm
.kind
= PERM_EXACT
;
1291 our_pred
->args
.perm
.val
= perm_val
& MODE_ALL
;
1297 parse_print (char **argv
, int *arg_ptr
)
1299 struct predicate
*our_pred
;
1304 our_pred
= insert_primary (pred_print
);
1305 /* -print has the side effect of printing. This prevents us
1306 from doing undesired multiple printing when the user has
1307 already specified -print. */
1308 our_pred
->side_effects
= true;
1309 our_pred
->no_default_print
= true;
1310 our_pred
->need_stat
= our_pred
->need_type
= false;
1315 parse_print0 (char **argv
, int *arg_ptr
)
1317 struct predicate
*our_pred
;
1322 our_pred
= insert_primary (pred_print0
);
1323 /* -print0 has the side effect of printing. This prevents us
1324 from doing undesired multiple printing when the user has
1325 already specified -print0. */
1326 our_pred
->side_effects
= true;
1327 our_pred
->no_default_print
= true;
1328 our_pred
->need_stat
= our_pred
->need_type
= false;
1333 parse_printf (char **argv
, int *arg_ptr
)
1335 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1337 return (insert_fprintf (stdout
, pred_fprintf
, argv
, arg_ptr
));
1341 parse_prune (char **argv
, int *arg_ptr
)
1343 struct predicate
*our_pred
;
1348 our_pred
= insert_primary (pred_prune
);
1349 our_pred
->need_stat
= our_pred
->need_type
= false;
1350 /* -prune has a side effect that it does not descend into
1351 the current directory. */
1352 our_pred
->side_effects
= true;
1357 parse_quit (char **argv
, int *arg_ptr
)
1359 struct predicate
*our_pred
= insert_primary (pred_quit
);
1362 our_pred
->need_stat
= our_pred
->need_type
= false;
1368 parse_regex (char **argv
, int *arg_ptr
)
1370 return insert_regex (argv
, arg_ptr
, false);
1374 insert_regex (char **argv
, int *arg_ptr
, boolean ignore_case
)
1376 struct predicate
*our_pred
;
1377 struct re_pattern_buffer
*re
;
1378 const char *error_message
;
1380 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1382 our_pred
= insert_primary (pred_regex
);
1383 our_pred
->need_stat
= our_pred
->need_type
= false;
1384 re
= (struct re_pattern_buffer
*)
1385 xmalloc (sizeof (struct re_pattern_buffer
));
1386 our_pred
->args
.regex
= re
;
1387 re
->allocated
= 100;
1388 re
->buffer
= (unsigned char *) xmalloc (re
->allocated
);
1391 int options
= RE_SYNTAX_POSIX_BASIC
;
1393 options
|= RE_ICASE
;
1395 re_set_syntax(options
);
1396 re
->syntax
= options
;
1397 re
->translate
= NULL
;
1399 error_message
= re_compile_pattern (argv
[*arg_ptr
], strlen (argv
[*arg_ptr
]),
1402 error (1, 0, "%s", error_message
);
1408 parse_size (char **argv
, int *arg_ptr
)
1410 struct predicate
*our_pred
;
1412 enum comparison_type c_type
;
1416 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1418 len
= strlen (argv
[*arg_ptr
]);
1420 error (1, 0, _("invalid null argument to -size"));
1421 switch (argv
[*arg_ptr
][len
- 1])
1425 argv
[*arg_ptr
][len
- 1] = '\0';
1430 argv
[*arg_ptr
][len
- 1] = '\0';
1435 argv
[*arg_ptr
][len
- 1] = '\0';
1438 case 'M': /* Megabytes */
1439 blksize
= 1024*1024;
1440 argv
[*arg_ptr
][len
- 1] = '\0';
1443 case 'G': /* Gigabytes */
1444 blksize
= 1024*1024*1024;
1445 argv
[*arg_ptr
][len
- 1] = '\0';
1450 argv
[*arg_ptr
][len
- 1] = '\0';
1466 error (1, 0, _("invalid -size type `%c'"), argv
[*arg_ptr
][len
- 1]);
1468 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
1470 our_pred
= insert_primary (pred_size
);
1471 our_pred
->args
.size
.kind
= c_type
;
1472 our_pred
->args
.size
.blocksize
= blksize
;
1473 our_pred
->args
.size
.size
= num
;
1480 parse_samefile (char **argv
, int *arg_ptr
)
1482 struct predicate
*our_pred
;
1485 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1487 if ((*options
.xstat
) (argv
[*arg_ptr
], &st
))
1488 error (1, errno
, "%s", argv
[*arg_ptr
]);
1490 our_pred
= insert_primary (pred_samefile
);
1491 our_pred
->args
.fileid
.ino
= st
.st_ino
;
1492 our_pred
->args
.fileid
.dev
= st
.st_dev
;
1493 our_pred
->need_type
= false;
1494 our_pred
->need_stat
= true;
1501 parse_true (char **argv
, int *arg_ptr
)
1503 struct predicate
*our_pred
;
1508 our_pred
= insert_primary (pred_true
);
1509 our_pred
->need_stat
= our_pred
->need_type
= false;
1514 parse_type (char **argv
, int *arg_ptr
)
1516 return insert_type (argv
, arg_ptr
, pred_type
);
1520 parse_uid (char **argv
, int *arg_ptr
)
1522 return (insert_num (argv
, arg_ptr
, pred_uid
));
1526 parse_used (char **argv
, int *arg_ptr
)
1528 struct predicate
*our_pred
;
1530 enum comparison_type c_type
;
1533 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1535 if (!get_num (argv
[*arg_ptr
], &num_days
, &c_type
))
1537 t
= num_days
* DAYSECS
;
1538 our_pred
= insert_primary (pred_used
);
1539 our_pred
->args
.info
.kind
= c_type
;
1540 our_pred
->args
.info
.negative
= t
< 0;
1541 our_pred
->args
.info
.l_val
= t
;
1547 parse_user (char **argv
, int *arg_ptr
)
1549 struct passwd
*cur_pwd
;
1550 struct predicate
*our_pred
;
1554 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1556 cur_pwd
= getpwnam (argv
[*arg_ptr
]);
1558 if (cur_pwd
!= NULL
)
1559 uid
= cur_pwd
->pw_uid
;
1562 uid_len
= strspn (argv
[*arg_ptr
], "0123456789");
1563 if ((uid_len
== 0) || (argv
[*arg_ptr
][uid_len
] != '\0'))
1565 uid
= atoi (argv
[*arg_ptr
]);
1567 our_pred
= insert_primary (pred_user
);
1568 our_pred
->args
.uid
= uid
;
1574 parse_version (char **argv
, int *arg_ptr
)
1576 extern char *version_string
;
1583 printf (_("GNU find version %s\n"), version_string
);
1584 printf (_("Features enabled: "));
1587 printf("CACHE_IDS ");
1595 printf("DEBUG_STAT ");
1598 #if defined(USE_STRUCT_DIRENT_D_TYPE) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
1602 #if defined(O_NOFOLLOW)
1603 printf("O_NOFOLLOW(%s) ",
1604 (options
.open_nofollow_available
? "enabled" : "disabled"));
1610 /* For the moment, leave this as English in case someone wants
1611 to parse these strings. */
1620 parse_xdev (char **argv
, int *arg_ptr
)
1624 options
.stay_on_filesystem
= true;
1629 parse_ignore_race (char **argv
, int *arg_ptr
)
1633 options
.ignore_readdir_race
= true;
1638 parse_noignore_race (char **argv
, int *arg_ptr
)
1642 options
.ignore_readdir_race
= false;
1647 parse_warn (char **argv
, int *arg_ptr
)
1651 options
.warnings
= true;
1656 parse_xtype (char **argv
, int *arg_ptr
)
1660 return insert_type (argv
, arg_ptr
, pred_xtype
);
1664 insert_type (char **argv
, int *arg_ptr
, boolean (*which_pred
) (/* ??? */))
1667 struct predicate
*our_pred
;
1669 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
)
1670 || (strlen (argv
[*arg_ptr
]) != 1))
1672 switch (argv
[*arg_ptr
][0])
1674 case 'b': /* block special */
1675 type_cell
= S_IFBLK
;
1677 case 'c': /* character special */
1678 type_cell
= S_IFCHR
;
1680 case 'd': /* directory */
1681 type_cell
= S_IFDIR
;
1683 case 'f': /* regular file */
1684 type_cell
= S_IFREG
;
1687 case 'l': /* symbolic link */
1688 type_cell
= S_IFLNK
;
1692 case 'p': /* pipe */
1693 type_cell
= S_IFIFO
;
1697 case 's': /* socket */
1698 type_cell
= S_IFSOCK
;
1702 case 'D': /* Solaris door */
1703 type_cell
= S_IFDOOR
;
1706 default: /* None of the above ... nuke 'em. */
1709 our_pred
= insert_primary (which_pred
);
1711 /* Figure out if we will need to stat the file, because if we don't
1712 * need to follow symlinks, we can avoid a stat call by using
1713 * struct dirent.d_type.
1715 if (which_pred
== pred_xtype
)
1717 our_pred
->need_stat
= true;
1718 our_pred
->need_type
= false;
1722 our_pred
->need_stat
= false; /* struct dirent is enough */
1723 our_pred
->need_type
= true;
1725 our_pred
->args
.type
= type_cell
;
1726 (*arg_ptr
)++; /* Move on to next argument. */
1730 /* If true, we've determined that the current fprintf predicate
1731 uses stat information. */
1732 static boolean fprintf_stat_needed
;
1735 insert_fprintf (FILE *fp
, boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
1737 char *format
; /* Beginning of unprocessed format string. */
1738 register char *scan
; /* Current address in scanning `format'. */
1739 register char *scan2
; /* Address inside of element being scanned. */
1740 struct segment
**segmentp
; /* Address of current segment. */
1741 struct predicate
*our_pred
;
1743 format
= argv
[(*arg_ptr
)++];
1745 fprintf_stat_needed
= false; /* Might be overridden later. */
1746 our_pred
= insert_primary (func
);
1747 our_pred
->side_effects
= true;
1748 our_pred
->no_default_print
= true;
1749 our_pred
->args
.printf_vec
.stream
= fp
;
1750 segmentp
= &our_pred
->args
.printf_vec
.segment
;
1753 for (scan
= format
; *scan
; scan
++)
1758 if (*scan2
>= '0' && *scan2
<= '7')
1762 for (i
= n
= 0; i
< 3 && (*scan2
>= '0' && *scan2
<= '7');
1764 n
= 8 * n
+ *scan2
- '0';
1779 make_segment (segmentp
, format
, scan
- format
, KIND_STOP
);
1780 our_pred
->need_stat
= fprintf_stat_needed
;
1798 /* *scan = '\\'; * it already is */
1802 _("warning: unrecognized escape `\\%c'"), *scan2
);
1807 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1809 format
= scan2
+ 1; /* Move past the escape. */
1810 scan
= scan2
; /* Incremented immediately by `for'. */
1812 else if (*scan
== '%')
1816 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1822 /* Scan past flags, width and precision, to verify kind. */
1823 for (scan2
= scan
; *++scan2
&& strchr ("-+ #", *scan2
);)
1825 while (ISDIGIT (*scan2
))
1828 for (scan2
++; ISDIGIT (*scan2
); scan2
++)
1830 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2
))
1832 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1837 else if (strchr ("ACT", *scan2
) && scan2
[1])
1839 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1840 *scan2
| (scan2
[1] << 8));
1847 /* An unrecognized % escape. Print the char after the %. */
1848 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1850 segmentp
= make_segment (segmentp
, format
, scan
- format
,
1859 make_segment (segmentp
, format
, scan
- format
, KIND_PLAIN
);
1860 our_pred
->need_type
= false;
1861 our_pred
->need_stat
= fprintf_stat_needed
;
1865 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1866 from the text in FORMAT, which has length LEN.
1867 Return the address of the `next' pointer of the new segment. */
1869 static struct segment
**
1870 make_segment (struct segment
**segment
, char *format
, int len
, int kind
)
1874 *segment
= (struct segment
*) xmalloc (sizeof (struct segment
));
1876 (*segment
)->kind
= kind
;
1877 (*segment
)->next
= NULL
;
1878 (*segment
)->text_len
= len
;
1880 fmt
= (*segment
)->text
= xmalloc (len
+ sizeof "d");
1881 strncpy (fmt
, format
, len
);
1884 switch (kind
& 0xff)
1886 case KIND_PLAIN
: /* Plain text string, no % conversion. */
1887 case KIND_STOP
: /* Terminate argument, no newline. */
1890 case 'a': /* atime in `ctime' format */
1891 case 'A': /* atime in user-specified strftime format */
1892 case 'c': /* ctime in `ctime' format */
1893 case 'C': /* ctime in user-specified strftime format */
1894 case 'F': /* filesystem type */
1895 case 'g': /* group name */
1896 case 'i': /* inode number */
1897 case 'l': /* object of symlink */
1898 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
1899 case 's': /* size in bytes */
1900 case 't': /* mtime in `ctime' format */
1901 case 'T': /* mtime in user-specified strftime format */
1902 case 'u': /* user name */
1903 case 'y': /* file type */
1904 case 'Y': /* symlink pointed file type */
1905 fprintf_stat_needed
= true;
1907 case 'f': /* basename of path */
1908 case 'h': /* leading directories part of path */
1909 case 'H': /* ARGV element file was found under */
1910 case 'p': /* pathname */
1911 case 'P': /* pathname with ARGV element stripped */
1915 /* Numeric items that one might expect to honour
1916 * #, 0, + flags but which do not.
1918 case 'G': /* GID number */
1919 case 'U': /* UID number */
1920 case 'b': /* size in 512-byte blocks */
1921 case 'D': /* Filesystem device on which the file exits */
1922 case 'k': /* size in 1K blocks */
1923 case 'n': /* number of links */
1924 fprintf_stat_needed
= true;
1928 /* Numeric items that DO honour #, 0, + flags.
1930 case 'd': /* depth in search tree (0 = ARGV element) */
1934 case 'm': /* mode as octal number (perms only) */
1936 fprintf_stat_needed
= true;
1941 return (&(*segment
)->next
);
1945 check_path_safety(const char *action
)
1947 const char *path
= getenv("PATH");
1949 s
= next_element(path
, 1);
1950 while ((s
= next_element ((char *) NULL
, 1)) != NULL
)
1952 if (0 == strcmp(s
, "."))
1954 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)"),
1961 /* handles both exec and ok predicate */
1962 #if defined(NEW_EXEC)
1963 /* handles both exec and ok predicate */
1965 new_insert_exec_ok (const char *action
,
1966 boolean (*func
) (/* ??? */),
1967 char **argv
, int *arg_ptr
)
1969 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
1970 int i
; /* Index into cmd args */
1971 int saw_braces
; /* True if previous arg was '{}'. */
1972 boolean allow_plus
; /* True if + is a valid terminator */
1973 int brace_count
; /* Number of instances of {}. */
1975 struct predicate
*our_pred
;
1976 struct exec_val
*execp
; /* Pointer for efficiency. */
1978 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1981 our_pred
= insert_primary (func
);
1982 our_pred
->side_effects
= true;
1983 our_pred
->no_default_print
= true;
1984 execp
= &our_pred
->args
.exec_vec
;
1986 if ((func
!= pred_okdir
) && (func
!= pred_ok
))
1991 if ((func
== pred_execdir
) || (func
== pred_okdir
))
1993 options
.ignore_readdir_race
= false;
1994 check_path_safety(action
);
1995 execp
->use_current_dir
= true;
1999 execp
->use_current_dir
= false;
2002 our_pred
->args
.exec_vec
.multiple
= 0;
2004 /* Count the number of args with path replacements, up until the ';'.
2005 * Also figure out if the command is terminated by ";" or by "+".
2008 for (end
= start
, saw_braces
=0, brace_count
=0;
2010 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2013 /* For -exec and -execdir, "{} +" can terminate the command. */
2015 && argv
[end
][0] == '+' && argv
[end
][1] == 0
2018 our_pred
->args
.exec_vec
.multiple
= 1;
2023 if (strstr (argv
[end
], "{}"))
2028 if (0 == end
&& (func
== pred_execdir
|| func
== pred_okdir
))
2030 /* The POSIX standard says that {} replacement should
2031 * occur even in the utility name. This is insecure
2032 * since it means we will be executing a command whose
2033 * name is chosen according to whatever find finds in
2034 * the filesystem. That can be influenced by an
2035 * attacker. Hence for -execdir and -okdir this is not
2036 * allowed. We can specify this as those options are
2037 * not defined by POSIX.
2039 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
2044 /* Fail if no command given or no semicolon found. */
2045 if ((end
== start
) || (argv
[end
] == NULL
))
2052 if (our_pred
->args
.exec_vec
.multiple
&& brace_count
> 1)
2056 if (func
== pred_execdir
)
2062 _("Only one instance of {} is supported with -exec%s ... +"),
2066 /* execp->ctl = xmalloc(sizeof struct buildcmd_control); */
2067 bc_init_controlinfo(&execp
->ctl
);
2068 execp
->ctl
.exec_callback
= launch
;
2070 if (our_pred
->args
.exec_vec
.multiple
)
2072 /* "+" terminator, so we can just append our arguments after the
2073 * command and initial arguments.
2075 execp
->replace_vec
= NULL
;
2076 execp
->ctl
.replace_pat
= NULL
;
2077 execp
->ctl
.rplen
= 0;
2078 execp
->ctl
.lines_per_exec
= 0; /* no limit */
2079 execp
->ctl
.args_per_exec
= 0; /* no limit */
2081 /* remember how many arguments there are */
2082 execp
->ctl
.initial_argc
= (end
-start
) - 1;
2084 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
2085 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
2087 /* Gather the initial arguments. Skip the {}. */
2088 for (i
=start
; i
<end
-1; ++i
)
2090 bc_push_arg(&execp
->ctl
, &execp
->state
,
2091 argv
[i
], strlen(argv
[i
])+1,
2098 /* Semicolon terminator - more than one {} is supported, so we
2099 * have to do brace-replacement.
2101 execp
->num_args
= end
- start
;
2103 execp
->ctl
.replace_pat
= "{}";
2104 execp
->ctl
.rplen
= strlen(execp
->ctl
.replace_pat
);
2105 execp
->ctl
.lines_per_exec
= 0; /* no limit */
2106 execp
->ctl
.args_per_exec
= 0; /* no limit */
2107 execp
->replace_vec
= xmalloc(sizeof(char*)*execp
->num_args
);
2110 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2111 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
2113 /* Remember the (pre-replacement) arguments for later. */
2114 for (i
=0; i
<execp
->num_args
; ++i
)
2116 execp
->replace_vec
[i
] = argv
[i
+start
];
2120 if (argv
[end
] == NULL
)
2128 /* handles both exec and ok predicate */
2130 old_insert_exec_ok (boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
2132 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
2133 int num_paths
; /* Number of args with path replacements. */
2134 int path_pos
; /* Index in array of path replacements. */
2135 int vec_pos
; /* Index in array of args. */
2136 struct predicate
*our_pred
;
2137 struct exec_val
*execp
; /* Pointer for efficiency. */
2139 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2142 /* Count the number of args with path replacements, up until the ';'. */
2144 for (end
= start
, num_paths
= 0;
2146 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2148 if (strstr (argv
[end
], "{}"))
2150 /* Fail if no command given or no semicolon found. */
2151 if ((end
== start
) || (argv
[end
] == NULL
))
2157 our_pred
= insert_primary (func
);
2158 our_pred
->side_effects
= true;
2159 our_pred
->no_default_print
= true;
2160 execp
= &our_pred
->args
.exec_vec
;
2161 execp
->usercontext
= our_pred
;
2162 execp
->use_current_dir
= false;
2164 (struct path_arg
*) xmalloc (sizeof (struct path_arg
) * (num_paths
+ 1));
2165 execp
->vec
= (char **) xmalloc (sizeof (char *) * (end
- start
+ 1));
2166 /* Record the positions of all args, and the args with path replacements. */
2167 for (end
= start
, path_pos
= vec_pos
= 0;
2169 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2174 execp
->paths
[path_pos
].count
= 0;
2175 for (p
= argv
[end
]; *p
; ++p
)
2176 if (p
[0] == '{' && p
[1] == '}')
2178 execp
->paths
[path_pos
].count
++;
2181 if (execp
->paths
[path_pos
].count
)
2183 execp
->paths
[path_pos
].offset
= vec_pos
;
2184 execp
->paths
[path_pos
].origarg
= argv
[end
];
2187 execp
->vec
[vec_pos
++] = argv
[end
];
2189 execp
->paths
[path_pos
].offset
= -1;
2190 execp
->vec
[vec_pos
] = NULL
;
2192 if (argv
[end
] == NULL
)
2203 insert_exec_ok (const char *action
,
2204 boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
2206 #if defined(NEW_EXEC)
2207 return new_insert_exec_ok(action
, func
, argv
, arg_ptr
);
2209 return old_insert_exec_ok(func
, argv
, arg_ptr
);
2215 /* Get a number of days and comparison type.
2216 STR is the ASCII representation.
2217 Set *NUM_DAYS to the number of days, taken as being from
2218 the current moment (or possibly midnight). Thus the sense of the
2219 comparison type appears to be reversed.
2220 Set *COMP_TYPE to the kind of comparison that is requested.
2222 Return true if all okay, false if input error.
2224 Used by -atime, -ctime and -mtime (parsers) to
2225 get the appropriate information for a time predicate processor. */
2228 get_num_days (char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
)
2230 boolean r
= get_num (str
, num_days
, comp_type
);
2234 case COMP_LT
: *comp_type
= COMP_GT
; break;
2235 case COMP_GT
: *comp_type
= COMP_LT
; break;
2241 /* Insert a time predicate PRED.
2242 ARGV is a pointer to the argument array.
2243 ARG_PTR is a pointer to an index into the array, incremented if
2246 Return true if input is valid, false if not.
2248 A new predicate node is assigned, along with an argument node
2249 obtained with malloc.
2251 Used by -atime, -ctime, and -mtime parsers. */
2254 insert_time (char **argv
, int *arg_ptr
, PFB pred
)
2256 struct predicate
*our_pred
;
2258 enum comparison_type c_type
;
2261 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2263 if (!get_num_days (argv
[*arg_ptr
], &num_days
, &c_type
))
2266 /* Figure out the timestamp value we are looking for. */
2267 t
= ( options
.cur_day_start
- num_days
* DAYSECS
2268 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2272 /* We introduce a scope in which 'val' can be declared, for the
2273 * benefit of compilers that are really C89 compilers
2274 * which support intmax_t because config.h #defines it
2276 intmax_t val
= ( (intmax_t)options
.cur_day_start
- num_days
* DAYSECS
2277 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2280 /* Check for possibility of an overflow */
2281 if ( (intmax_t)t
!= val
)
2283 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv
[*arg_ptr
]);
2287 our_pred
= insert_primary (pred
);
2288 our_pred
->args
.info
.kind
= c_type
;
2289 our_pred
->args
.info
.negative
= t
< 0;
2290 our_pred
->args
.info
.l_val
= t
;
2293 fprintf (stderr
, "inserting %s\n", our_pred
->p_name
);
2294 fprintf (stderr
, " type: %s %s ",
2295 (c_type
== COMP_GT
) ? "gt" :
2296 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2297 (c_type
== COMP_GT
) ? " >" :
2298 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? ">=" : " ?")));
2299 t
= our_pred
->args
.info
.l_val
;
2300 fprintf (stderr
, "%ju %s", (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2301 if (c_type
== COMP_EQ
)
2303 t
= our_pred
->args
.info
.l_val
+= DAYSECS
;
2304 fprintf (stderr
, " < %ju %s",
2305 (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2306 our_pred
->args
.info
.l_val
-= DAYSECS
;
2312 /* Get a number with comparision information.
2313 The sense of the comparision information is 'normal'; that is,
2314 '+' looks for a count > than the number and '-' less than.
2316 STR is the ASCII representation of the number.
2317 Set *NUM to the number.
2318 Set *COMP_TYPE to the kind of comparison that is requested.
2320 Return true if all okay, false if input error. */
2323 get_num (char *str
, uintmax_t *num
, enum comparison_type
*comp_type
)
2330 *comp_type
= COMP_GT
;
2334 *comp_type
= COMP_LT
;
2338 *comp_type
= COMP_EQ
;
2342 return xstrtoumax (str
, NULL
, 10, num
, "") == LONGINT_OK
;
2345 /* Insert a number predicate.
2346 ARGV is a pointer to the argument array.
2347 *ARG_PTR is an index into ARGV, incremented if all went well.
2348 *PRED is the predicate processor to insert.
2350 Return true if input is valid, false if error.
2352 A new predicate node is assigned, along with an argument node
2353 obtained with malloc.
2355 Used by -inum and -links parsers. */
2358 insert_num (char **argv
, int *arg_ptr
, PFB pred
)
2360 struct predicate
*our_pred
;
2362 enum comparison_type c_type
;
2364 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2366 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
2368 our_pred
= insert_primary (pred
);
2369 our_pred
->args
.info
.kind
= c_type
;
2370 our_pred
->args
.info
.l_val
= num
;
2373 fprintf (stderr
, "inserting %s\n", our_pred
->p_name
);
2374 fprintf (stderr
, " type: %s %s ",
2375 (c_type
== COMP_GT
) ? "gt" :
2376 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2377 (c_type
== COMP_GT
) ? " >" :
2378 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? " =" : " ?")));
2379 fprintf (stderr
, "%ju\n", our_pred
->args
.info
.l_val
);
2385 open_output_file (char *path
)
2389 if (!strcmp (path
, "/dev/stderr"))
2391 else if (!strcmp (path
, "/dev/stdout"))
2393 f
= fopen (path
, "w");
2395 error (1, errno
, "%s", path
);