1 /* parser.c -- convert the command line args into an expression tree.
2 Copyright (C) 1990, 91, 92, 93, 94, 2000, 2001, 2003, 2004 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
26 #include "../gnulib/lib/modechange.h"
28 #include "../gnulib/lib/xstrtol.h"
29 #include "../gnulib/lib/xalloc.h"
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"); */
794 parse_iname (char **argv
, int *arg_ptr
)
796 struct predicate
*our_pred
;
798 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
801 fnmatch_sanitycheck();
803 our_pred
= insert_primary (pred_iname
);
804 our_pred
->need_stat
= our_pred
->need_type
= false;
805 our_pred
->args
.str
= argv
[*arg_ptr
];
811 parse_inum (char **argv
, int *arg_ptr
)
813 return (insert_num (argv
, arg_ptr
, pred_inum
));
816 /* -ipath is deprecated (at RMS's request) in favour of
817 * -iwholename. See the node "GNU Manuals" in standards.texi
818 * for the rationale for this (basically, GNU prefers the use
819 * of the phrase "file name" to "path name"
822 parse_ipath (char **argv
, int *arg_ptr
)
825 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
827 return parse_iwholename(argv
, arg_ptr
);
831 parse_iwholename (char **argv
, int *arg_ptr
)
833 struct predicate
*our_pred
;
835 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
838 fnmatch_sanitycheck();
840 our_pred
= insert_primary (pred_ipath
);
841 our_pred
->need_stat
= our_pred
->need_type
= false;
842 our_pred
->args
.str
= argv
[*arg_ptr
];
848 parse_iregex (char **argv
, int *arg_ptr
)
850 return insert_regex (argv
, arg_ptr
, true);
854 parse_links (char **argv
, int *arg_ptr
)
856 return (insert_num (argv
, arg_ptr
, pred_links
));
860 parse_lname (char **argv
, int *arg_ptr
)
862 struct predicate
*our_pred
;
867 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
870 fnmatch_sanitycheck();
872 our_pred
= insert_primary (pred_lname
);
873 our_pred
->args
.str
= argv
[*arg_ptr
];
879 parse_ls (char **argv
, int *arg_ptr
)
881 struct predicate
*our_pred
;
886 our_pred
= insert_primary (pred_ls
);
887 our_pred
->side_effects
= true;
888 our_pred
->no_default_print
= true;
893 parse_maxdepth (char **argv
, int *arg_ptr
)
897 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
899 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
900 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
902 options
.maxdepth
= atoi (argv
[*arg_ptr
]);
903 if (options
.maxdepth
< 0)
910 parse_mindepth (char **argv
, int *arg_ptr
)
914 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
916 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
917 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
919 options
.mindepth
= atoi (argv
[*arg_ptr
]);
920 if (options
.mindepth
< 0)
927 parse_mmin (char **argv
, int *arg_ptr
)
929 struct predicate
*our_pred
;
931 enum comparison_type c_type
;
934 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
936 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
938 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
939 our_pred
= insert_primary (pred_mmin
);
940 our_pred
->args
.info
.kind
= c_type
;
941 our_pred
->args
.info
.negative
= t
< 0;
942 our_pred
->args
.info
.l_val
= t
;
948 parse_mtime (char **argv
, int *arg_ptr
)
950 return (insert_time (argv
, arg_ptr
, pred_mtime
));
954 parse_name (char **argv
, int *arg_ptr
)
956 struct predicate
*our_pred
;
961 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
963 our_pred
= insert_primary (pred_name
);
964 our_pred
->need_stat
= our_pred
->need_type
= false;
965 our_pred
->args
.str
= argv
[*arg_ptr
];
971 parse_negate (char **argv
, int *arg_ptr
)
973 struct predicate
*our_pred
;
978 our_pred
= get_new_pred_chk_op ();
979 our_pred
->pred_func
= pred_negate
;
981 our_pred
->p_name
= find_pred_name (pred_negate
);
983 our_pred
->p_type
= UNI_OP
;
984 our_pred
->p_prec
= NEGATE_PREC
;
985 our_pred
->need_stat
= our_pred
->need_type
= false;
990 parse_newer (char **argv
, int *arg_ptr
)
992 struct predicate
*our_pred
;
993 struct stat stat_newer
;
998 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1000 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
1001 error (1, errno
, "%s", argv
[*arg_ptr
]);
1002 our_pred
= insert_primary (pred_newer
);
1003 our_pred
->args
.time
= stat_newer
.st_mtime
;
1009 parse_noleaf (char **argv
, int *arg_ptr
)
1014 options
.no_leaf_check
= true;
1019 /* Arbitrary amount by which to increase size
1020 of `uid_unused' and `gid_unused'. */
1021 #define ALLOC_STEP 2048
1023 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1024 char *uid_unused
= NULL
;
1026 /* Number of elements in `uid_unused'. */
1027 unsigned uid_allocated
;
1029 /* Similar for GIDs and group entries. */
1030 char *gid_unused
= NULL
;
1031 unsigned gid_allocated
;
1035 parse_nogroup (char **argv
, int *arg_ptr
)
1037 struct predicate
*our_pred
;
1042 our_pred
= insert_primary (pred_nogroup
);
1044 if (gid_unused
== NULL
)
1048 gid_allocated
= ALLOC_STEP
;
1049 gid_unused
= xmalloc (gid_allocated
);
1050 memset (gid_unused
, 1, gid_allocated
);
1052 while ((gr
= getgrent ()) != NULL
)
1054 if ((unsigned) gr
->gr_gid
>= gid_allocated
)
1056 unsigned new_allocated
= (unsigned) gr
->gr_gid
+ ALLOC_STEP
;
1057 gid_unused
= xrealloc (gid_unused
, new_allocated
);
1058 memset (gid_unused
+ gid_allocated
, 1,
1059 new_allocated
- gid_allocated
);
1060 gid_allocated
= new_allocated
;
1062 gid_unused
[(unsigned) gr
->gr_gid
] = 0;
1071 parse_nouser (char **argv
, int *arg_ptr
)
1073 struct predicate
*our_pred
;
1078 our_pred
= insert_primary (pred_nouser
);
1080 if (uid_unused
== NULL
)
1084 uid_allocated
= ALLOC_STEP
;
1085 uid_unused
= xmalloc (uid_allocated
);
1086 memset (uid_unused
, 1, uid_allocated
);
1088 while ((pw
= getpwent ()) != NULL
)
1090 if ((unsigned) pw
->pw_uid
>= uid_allocated
)
1092 unsigned new_allocated
= (unsigned) pw
->pw_uid
+ ALLOC_STEP
;
1093 uid_unused
= xrealloc (uid_unused
, new_allocated
);
1094 memset (uid_unused
+ uid_allocated
, 1,
1095 new_allocated
- uid_allocated
);
1096 uid_allocated
= new_allocated
;
1098 uid_unused
[(unsigned) pw
->pw_uid
] = 0;
1107 parse_nowarn (char **argv
, int *arg_ptr
)
1112 options
.warnings
= false;
1117 parse_ok (char **argv
, int *arg_ptr
)
1119 return (insert_exec_ok ("-ok", pred_ok
, argv
, arg_ptr
));
1123 parse_okdir (char **argv
, int *arg_ptr
)
1125 return (insert_exec_ok ("-okdir", pred_okdir
, argv
, arg_ptr
));
1129 parse_open (char **argv
, int *arg_ptr
)
1131 struct predicate
*our_pred
;
1136 our_pred
= get_new_pred_chk_op ();
1137 our_pred
->pred_func
= pred_open
;
1139 our_pred
->p_name
= find_pred_name (pred_open
);
1141 our_pred
->p_type
= OPEN_PAREN
;
1142 our_pred
->p_prec
= NO_PREC
;
1143 our_pred
->need_stat
= our_pred
->need_type
= false;
1148 parse_or (char **argv
, int *arg_ptr
)
1150 struct predicate
*our_pred
;
1155 our_pred
= get_new_pred ();
1156 our_pred
->pred_func
= pred_or
;
1158 our_pred
->p_name
= find_pred_name (pred_or
);
1160 our_pred
->p_type
= BI_OP
;
1161 our_pred
->p_prec
= OR_PREC
;
1162 our_pred
->need_stat
= our_pred
->need_type
= false;
1166 /* -path is deprecated (at RMS's request) in favour of
1167 * -iwholename. See the node "GNU Manuals" in standards.texi
1168 * for the rationale for this (basically, GNU prefers the use
1169 * of the phrase "file name" to "path name".
1171 * We do not issue a warning that this usage is deprecated
1172 * since HPUX find supports this predicate also.
1175 parse_path (char **argv
, int *arg_ptr
)
1177 return parse_wholename(argv
, arg_ptr
);
1181 parse_wholename (char **argv
, int *arg_ptr
)
1183 struct predicate
*our_pred
;
1185 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1187 our_pred
= insert_primary (pred_path
);
1188 our_pred
->need_stat
= our_pred
->need_type
= false;
1189 our_pred
->args
.str
= argv
[*arg_ptr
];
1195 parse_perm (char **argv
, int *arg_ptr
)
1199 struct mode_change
*change
;
1200 struct predicate
*our_pred
;
1202 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1205 switch (argv
[*arg_ptr
][0])
1216 change
= mode_compile (argv
[*arg_ptr
] + mode_start
, MODE_MASK_PLUS
);
1217 if (change
== MODE_INVALID
)
1218 error (1, 0, _("invalid mode `%s'"), argv
[*arg_ptr
]);
1219 else if (change
== MODE_MEMORY_EXHAUSTED
)
1220 error (1, 0, _("virtual memory exhausted"));
1221 perm_val
= mode_adjust (0, change
);
1224 our_pred
= insert_primary (pred_perm
);
1226 switch (argv
[*arg_ptr
][0])
1229 our_pred
->args
.perm
.kind
= PERM_AT_LEAST
;
1232 our_pred
->args
.perm
.kind
= PERM_ANY
;
1235 our_pred
->args
.perm
.kind
= PERM_EXACT
;
1238 our_pred
->args
.perm
.val
= perm_val
& MODE_ALL
;
1244 parse_print (char **argv
, int *arg_ptr
)
1246 struct predicate
*our_pred
;
1251 our_pred
= insert_primary (pred_print
);
1252 /* -print has the side effect of printing. This prevents us
1253 from doing undesired multiple printing when the user has
1254 already specified -print. */
1255 our_pred
->side_effects
= true;
1256 our_pred
->no_default_print
= true;
1257 our_pred
->need_stat
= our_pred
->need_type
= false;
1262 parse_print0 (char **argv
, int *arg_ptr
)
1264 struct predicate
*our_pred
;
1269 our_pred
= insert_primary (pred_print0
);
1270 /* -print0 has the side effect of printing. This prevents us
1271 from doing undesired multiple printing when the user has
1272 already specified -print0. */
1273 our_pred
->side_effects
= true;
1274 our_pred
->no_default_print
= true;
1275 our_pred
->need_stat
= our_pred
->need_type
= false;
1280 parse_printf (char **argv
, int *arg_ptr
)
1282 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1284 return (insert_fprintf (stdout
, pred_fprintf
, argv
, arg_ptr
));
1288 parse_prune (char **argv
, int *arg_ptr
)
1290 struct predicate
*our_pred
;
1295 our_pred
= insert_primary (pred_prune
);
1296 our_pred
->need_stat
= our_pred
->need_type
= false;
1297 /* -prune has a side effect that it does not descend into
1298 the current directory. */
1299 our_pred
->side_effects
= true;
1304 parse_quit (char **argv
, int *arg_ptr
)
1306 struct predicate
*our_pred
= insert_primary (pred_quit
);
1309 our_pred
->need_stat
= our_pred
->need_type
= false;
1315 parse_regex (char **argv
, int *arg_ptr
)
1317 return insert_regex (argv
, arg_ptr
, false);
1321 insert_regex (char **argv
, int *arg_ptr
, boolean ignore_case
)
1323 struct predicate
*our_pred
;
1324 struct re_pattern_buffer
*re
;
1325 const char *error_message
;
1327 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1329 our_pred
= insert_primary (pred_regex
);
1330 our_pred
->need_stat
= our_pred
->need_type
= false;
1331 re
= (struct re_pattern_buffer
*)
1332 xmalloc (sizeof (struct re_pattern_buffer
));
1333 our_pred
->args
.regex
= re
;
1334 re
->allocated
= 100;
1335 re
->buffer
= (unsigned char *) xmalloc (re
->allocated
);
1340 re_syntax_options
|= RE_ICASE
;
1344 re_syntax_options
&= ~RE_ICASE
;
1346 re
->translate
= NULL
;
1348 error_message
= re_compile_pattern (argv
[*arg_ptr
], strlen (argv
[*arg_ptr
]),
1351 error (1, 0, "%s", error_message
);
1357 parse_size (char **argv
, int *arg_ptr
)
1359 struct predicate
*our_pred
;
1361 enum comparison_type c_type
;
1365 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1367 len
= strlen (argv
[*arg_ptr
]);
1369 error (1, 0, _("invalid null argument to -size"));
1370 switch (argv
[*arg_ptr
][len
- 1])
1374 argv
[*arg_ptr
][len
- 1] = '\0';
1379 argv
[*arg_ptr
][len
- 1] = '\0';
1384 argv
[*arg_ptr
][len
- 1] = '\0';
1387 case 'M': /* Megabytes */
1388 blksize
= 1024*1024;
1389 argv
[*arg_ptr
][len
- 1] = '\0';
1392 case 'G': /* Gigabytes */
1393 blksize
= 1024*1024*1024;
1394 argv
[*arg_ptr
][len
- 1] = '\0';
1399 argv
[*arg_ptr
][len
- 1] = '\0';
1415 error (1, 0, _("invalid -size type `%c'"), argv
[*arg_ptr
][len
- 1]);
1417 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
1419 our_pred
= insert_primary (pred_size
);
1420 our_pred
->args
.size
.kind
= c_type
;
1421 our_pred
->args
.size
.blocksize
= blksize
;
1422 our_pred
->args
.size
.size
= num
;
1429 parse_samefile (char **argv
, int *arg_ptr
)
1431 struct predicate
*our_pred
;
1434 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1436 if ((*options
.xstat
) (argv
[*arg_ptr
], &st
))
1437 error (1, errno
, "%s", argv
[*arg_ptr
]);
1439 our_pred
= insert_primary (pred_samefile
);
1440 our_pred
->args
.fileid
.ino
= st
.st_ino
;
1441 our_pred
->args
.fileid
.dev
= st
.st_dev
;
1442 our_pred
->need_type
= false;
1443 our_pred
->need_stat
= true;
1450 parse_true (char **argv
, int *arg_ptr
)
1452 struct predicate
*our_pred
;
1457 our_pred
= insert_primary (pred_true
);
1458 our_pred
->need_stat
= our_pred
->need_type
= false;
1463 parse_type (char **argv
, int *arg_ptr
)
1465 return insert_type (argv
, arg_ptr
, pred_type
);
1469 parse_uid (char **argv
, int *arg_ptr
)
1471 return (insert_num (argv
, arg_ptr
, pred_uid
));
1475 parse_used (char **argv
, int *arg_ptr
)
1477 struct predicate
*our_pred
;
1479 enum comparison_type c_type
;
1482 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1484 if (!get_num (argv
[*arg_ptr
], &num_days
, &c_type
))
1486 t
= num_days
* DAYSECS
;
1487 our_pred
= insert_primary (pred_used
);
1488 our_pred
->args
.info
.kind
= c_type
;
1489 our_pred
->args
.info
.negative
= t
< 0;
1490 our_pred
->args
.info
.l_val
= t
;
1496 parse_user (char **argv
, int *arg_ptr
)
1498 struct passwd
*cur_pwd
;
1499 struct predicate
*our_pred
;
1503 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1505 cur_pwd
= getpwnam (argv
[*arg_ptr
]);
1507 if (cur_pwd
!= NULL
)
1508 uid
= cur_pwd
->pw_uid
;
1511 uid_len
= strspn (argv
[*arg_ptr
], "0123456789");
1512 if ((uid_len
== 0) || (argv
[*arg_ptr
][uid_len
] != '\0'))
1514 uid
= atoi (argv
[*arg_ptr
]);
1516 our_pred
= insert_primary (pred_user
);
1517 our_pred
->args
.uid
= uid
;
1523 parse_version (char **argv
, int *arg_ptr
)
1525 extern char *version_string
;
1532 printf (_("GNU find version %s\n"), version_string
);
1533 printf (_("Features enabled: "));
1536 printf("CACHE_IDS ");
1544 printf("DEBUG_STAT ");
1547 #if defined(USE_STRUCT_DIRENT_D_TYPE) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
1551 #if defined(O_NOFOLLOW)
1552 printf("O_NOFOLLOW(%s) ",
1553 (options
.open_nofollow_available
? "enabled" : "disabled"));
1559 /* For the moment, leave this as English in case someone wants
1560 to parse these strings. */
1569 parse_xdev (char **argv
, int *arg_ptr
)
1573 options
.stay_on_filesystem
= true;
1578 parse_ignore_race (char **argv
, int *arg_ptr
)
1582 options
.ignore_readdir_race
= true;
1587 parse_noignore_race (char **argv
, int *arg_ptr
)
1591 options
.ignore_readdir_race
= false;
1596 parse_warn (char **argv
, int *arg_ptr
)
1600 options
.warnings
= true;
1605 parse_xtype (char **argv
, int *arg_ptr
)
1609 return insert_type (argv
, arg_ptr
, pred_xtype
);
1613 insert_type (char **argv
, int *arg_ptr
, boolean (*which_pred
) (/* ??? */))
1616 struct predicate
*our_pred
;
1618 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
)
1619 || (strlen (argv
[*arg_ptr
]) != 1))
1621 switch (argv
[*arg_ptr
][0])
1623 case 'b': /* block special */
1624 type_cell
= S_IFBLK
;
1626 case 'c': /* character special */
1627 type_cell
= S_IFCHR
;
1629 case 'd': /* directory */
1630 type_cell
= S_IFDIR
;
1632 case 'f': /* regular file */
1633 type_cell
= S_IFREG
;
1636 case 'l': /* symbolic link */
1637 type_cell
= S_IFLNK
;
1641 case 'p': /* pipe */
1642 type_cell
= S_IFIFO
;
1646 case 's': /* socket */
1647 type_cell
= S_IFSOCK
;
1651 case 'D': /* Solaris door */
1652 type_cell
= S_IFDOOR
;
1655 default: /* None of the above ... nuke 'em. */
1658 our_pred
= insert_primary (which_pred
);
1660 /* Figure out if we will need to stat the file, because if we don't
1661 * need to follow symlinks, we can avoid a stat call by using
1662 * struct dirent.d_type.
1664 if (which_pred
== pred_xtype
)
1666 our_pred
->need_stat
= true;
1667 our_pred
->need_type
= false;
1671 our_pred
->need_stat
= false; /* struct dirent is enough */
1672 our_pred
->need_type
= true;
1674 our_pred
->args
.type
= type_cell
;
1675 (*arg_ptr
)++; /* Move on to next argument. */
1679 /* If true, we've determined that the current fprintf predicate
1680 uses stat information. */
1681 static boolean fprintf_stat_needed
;
1684 insert_fprintf (FILE *fp
, boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
1686 char *format
; /* Beginning of unprocessed format string. */
1687 register char *scan
; /* Current address in scanning `format'. */
1688 register char *scan2
; /* Address inside of element being scanned. */
1689 struct segment
**segmentp
; /* Address of current segment. */
1690 struct predicate
*our_pred
;
1692 format
= argv
[(*arg_ptr
)++];
1694 fprintf_stat_needed
= false; /* Might be overridden later. */
1695 our_pred
= insert_primary (func
);
1696 our_pred
->side_effects
= true;
1697 our_pred
->no_default_print
= true;
1698 our_pred
->args
.printf_vec
.stream
= fp
;
1699 segmentp
= &our_pred
->args
.printf_vec
.segment
;
1702 for (scan
= format
; *scan
; scan
++)
1707 if (*scan2
>= '0' && *scan2
<= '7')
1711 for (i
= n
= 0; i
< 3 && (*scan2
>= '0' && *scan2
<= '7');
1713 n
= 8 * n
+ *scan2
- '0';
1728 make_segment (segmentp
, format
, scan
- format
, KIND_STOP
);
1729 our_pred
->need_stat
= fprintf_stat_needed
;
1747 /* *scan = '\\'; * it already is */
1751 _("warning: unrecognized escape `\\%c'"), *scan2
);
1756 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1758 format
= scan2
+ 1; /* Move past the escape. */
1759 scan
= scan2
; /* Incremented immediately by `for'. */
1761 else if (*scan
== '%')
1765 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1771 /* Scan past flags, width and precision, to verify kind. */
1772 for (scan2
= scan
; *++scan2
&& strchr ("-+ #", *scan2
);)
1774 while (ISDIGIT (*scan2
))
1777 for (scan2
++; ISDIGIT (*scan2
); scan2
++)
1779 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2
))
1781 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1786 else if (strchr ("ACT", *scan2
) && scan2
[1])
1788 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1789 *scan2
| (scan2
[1] << 8));
1796 /* An unrecognized % escape. Print the char after the %. */
1797 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1799 segmentp
= make_segment (segmentp
, format
, scan
- format
,
1808 make_segment (segmentp
, format
, scan
- format
, KIND_PLAIN
);
1809 our_pred
->need_type
= false;
1810 our_pred
->need_stat
= fprintf_stat_needed
;
1814 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1815 from the text in FORMAT, which has length LEN.
1816 Return the address of the `next' pointer of the new segment. */
1818 static struct segment
**
1819 make_segment (struct segment
**segment
, char *format
, int len
, int kind
)
1823 *segment
= (struct segment
*) xmalloc (sizeof (struct segment
));
1825 (*segment
)->kind
= kind
;
1826 (*segment
)->next
= NULL
;
1827 (*segment
)->text_len
= len
;
1829 fmt
= (*segment
)->text
= xmalloc (len
+ sizeof "d");
1830 strncpy (fmt
, format
, len
);
1833 switch (kind
& 0xff)
1835 case KIND_PLAIN
: /* Plain text string, no % conversion. */
1836 case KIND_STOP
: /* Terminate argument, no newline. */
1839 case 'a': /* atime in `ctime' format */
1840 case 'A': /* atime in user-specified strftime format */
1841 case 'c': /* ctime in `ctime' format */
1842 case 'C': /* ctime in user-specified strftime format */
1843 case 'F': /* filesystem type */
1844 case 'g': /* group name */
1845 case 'i': /* inode number */
1846 case 'l': /* object of symlink */
1847 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
1848 case 's': /* size in bytes */
1849 case 't': /* mtime in `ctime' format */
1850 case 'T': /* mtime in user-specified strftime format */
1851 case 'u': /* user name */
1852 case 'y': /* file type */
1853 case 'Y': /* symlink pointed file type */
1854 fprintf_stat_needed
= true;
1856 case 'f': /* basename of path */
1857 case 'h': /* leading directories part of path */
1858 case 'H': /* ARGV element file was found under */
1859 case 'p': /* pathname */
1860 case 'P': /* pathname with ARGV element stripped */
1864 /* Numeric items that one might expect to honour
1865 * #, 0, + flags but which do not.
1867 case 'G': /* GID number */
1868 case 'U': /* UID number */
1869 case 'b': /* size in 512-byte blocks */
1870 case 'D': /* Filesystem device on which the file exits */
1871 case 'k': /* size in 1K blocks */
1872 case 'n': /* number of links */
1873 fprintf_stat_needed
= true;
1877 /* Numeric items that DO honour #, 0, + flags.
1879 case 'd': /* depth in search tree (0 = ARGV element) */
1883 case 'm': /* mode as octal number (perms only) */
1885 fprintf_stat_needed
= true;
1890 return (&(*segment
)->next
);
1894 check_path_safety(const char *action
)
1896 const char *path
= getenv("PATH");
1898 s
= next_element(path
, 1);
1899 while ((s
= next_element ((char *) NULL
, 1)) != NULL
)
1901 if (0 == strcmp(s
, "."))
1903 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)"),
1910 /* handles both exec and ok predicate */
1911 #if defined(NEW_EXEC)
1912 /* handles both exec and ok predicate */
1914 new_insert_exec_ok (const char *action
,
1915 boolean (*func
) (/* ??? */),
1916 char **argv
, int *arg_ptr
)
1918 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
1919 int i
; /* Index into cmd args */
1920 int saw_braces
; /* True if previous arg was '{}'. */
1921 boolean allow_plus
; /* True if + is a valid terminator */
1922 int brace_count
; /* Number of instances of {}. */
1924 struct predicate
*our_pred
;
1925 struct exec_val
*execp
; /* Pointer for efficiency. */
1927 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1930 our_pred
= insert_primary (func
);
1931 our_pred
->side_effects
= true;
1932 our_pred
->no_default_print
= true;
1933 execp
= &our_pred
->args
.exec_vec
;
1935 if ((func
!= pred_okdir
) && (func
!= pred_ok
))
1940 if ((func
== pred_execdir
) || (func
== pred_okdir
))
1942 options
.ignore_readdir_race
= false;
1943 check_path_safety(action
);
1944 execp
->use_current_dir
= true;
1948 execp
->use_current_dir
= false;
1951 our_pred
->args
.exec_vec
.multiple
= 0;
1953 /* Count the number of args with path replacements, up until the ';'.
1954 * Also figure out if the command is terminated by ";" or by "+".
1957 for (end
= start
, saw_braces
=0, brace_count
=0;
1959 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
1962 /* For -exec and -execdir, "{} +" can terminate the command. */
1964 && argv
[end
][0] == '+' && argv
[end
][1] == 0
1967 our_pred
->args
.exec_vec
.multiple
= 1;
1972 if (strstr (argv
[end
], "{}"))
1977 if (0 == end
&& (func
== pred_execdir
|| func
== pred_okdir
))
1979 /* The POSIX standard says that {} replacement should
1980 * occur even in the utility name. This is insecure
1981 * since it means we will be executing a command whose
1982 * name is chosen according to whatever find finds in
1983 * the filesystem. That can be influenced by an
1984 * attacker. Hence for -execdir and -okdir this is not
1985 * allowed. We can specify this as those options are
1986 * not defined by POSIX.
1988 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
1993 /* Fail if no command given or no semicolon found. */
1994 if ((end
== start
) || (argv
[end
] == NULL
))
2001 if (our_pred
->args
.exec_vec
.multiple
&& brace_count
> 1)
2005 if (func
== pred_execdir
)
2011 _("Only one instance of {} is supported with -exec%s ... +"),
2015 /* execp->ctl = xmalloc(sizeof struct buildcmd_control); */
2016 bc_init_controlinfo(&execp
->ctl
);
2017 execp
->ctl
.exec_callback
= launch
;
2019 if (our_pred
->args
.exec_vec
.multiple
)
2021 /* "+" terminator, so we can just append our arguments after the
2022 * command and initial arguments.
2024 execp
->replace_vec
= NULL
;
2025 execp
->ctl
.replace_pat
= NULL
;
2026 execp
->ctl
.rplen
= 0;
2027 execp
->ctl
.lines_per_exec
= 0; /* no limit */
2028 execp
->ctl
.args_per_exec
= 0; /* no limit */
2030 /* remember how many arguments there are */
2031 execp
->ctl
.initial_argc
= (end
-start
) - 1;
2033 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
2034 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
2036 /* Gather the initial arguments. Skip the {}. */
2037 for (i
=start
; i
<end
-1; ++i
)
2039 bc_push_arg(&execp
->ctl
, &execp
->state
,
2040 argv
[i
], strlen(argv
[i
])+1,
2047 /* Semicolon terminator - more than one {} is supported, so we
2048 * have to do brace-replacement.
2050 execp
->num_args
= end
- start
;
2052 execp
->ctl
.replace_pat
= "{}";
2053 execp
->ctl
.rplen
= strlen(execp
->ctl
.replace_pat
);
2054 execp
->ctl
.lines_per_exec
= 0; /* no limit */
2055 execp
->ctl
.args_per_exec
= 0; /* no limit */
2056 execp
->replace_vec
= xmalloc(sizeof(char*)*execp
->num_args
);
2059 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2060 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
2062 /* Remember the (pre-replacement) arguments for later. */
2063 for (i
=0; i
<execp
->num_args
; ++i
)
2065 execp
->replace_vec
[i
] = argv
[i
+start
];
2069 if (argv
[end
] == NULL
)
2077 /* handles both exec and ok predicate */
2079 old_insert_exec_ok (boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
2081 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
2082 int num_paths
; /* Number of args with path replacements. */
2083 int path_pos
; /* Index in array of path replacements. */
2084 int vec_pos
; /* Index in array of args. */
2085 struct predicate
*our_pred
;
2086 struct exec_val
*execp
; /* Pointer for efficiency. */
2088 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2091 /* Count the number of args with path replacements, up until the ';'. */
2093 for (end
= start
, num_paths
= 0;
2095 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2097 if (strstr (argv
[end
], "{}"))
2099 /* Fail if no command given or no semicolon found. */
2100 if ((end
== start
) || (argv
[end
] == NULL
))
2106 our_pred
= insert_primary (func
);
2107 our_pred
->side_effects
= true;
2108 our_pred
->no_default_print
= true;
2109 execp
= &our_pred
->args
.exec_vec
;
2110 execp
->usercontext
= our_pred
;
2111 execp
->use_current_dir
= false;
2113 (struct path_arg
*) xmalloc (sizeof (struct path_arg
) * (num_paths
+ 1));
2114 execp
->vec
= (char **) xmalloc (sizeof (char *) * (end
- start
+ 1));
2115 /* Record the positions of all args, and the args with path replacements. */
2116 for (end
= start
, path_pos
= vec_pos
= 0;
2118 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2123 execp
->paths
[path_pos
].count
= 0;
2124 for (p
= argv
[end
]; *p
; ++p
)
2125 if (p
[0] == '{' && p
[1] == '}')
2127 execp
->paths
[path_pos
].count
++;
2130 if (execp
->paths
[path_pos
].count
)
2132 execp
->paths
[path_pos
].offset
= vec_pos
;
2133 execp
->paths
[path_pos
].origarg
= argv
[end
];
2136 execp
->vec
[vec_pos
++] = argv
[end
];
2138 execp
->paths
[path_pos
].offset
= -1;
2139 execp
->vec
[vec_pos
] = NULL
;
2141 if (argv
[end
] == NULL
)
2152 insert_exec_ok (const char *action
,
2153 boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
2155 #if defined(NEW_EXEC)
2156 return new_insert_exec_ok(action
, func
, argv
, arg_ptr
);
2158 return old_insert_exec_ok(func
, argv
, arg_ptr
);
2164 /* Get a number of days and comparison type.
2165 STR is the ASCII representation.
2166 Set *NUM_DAYS to the number of days, taken as being from
2167 the current moment (or possibly midnight). Thus the sense of the
2168 comparison type appears to be reversed.
2169 Set *COMP_TYPE to the kind of comparison that is requested.
2171 Return true if all okay, false if input error.
2173 Used by -atime, -ctime and -mtime (parsers) to
2174 get the appropriate information for a time predicate processor. */
2177 get_num_days (char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
)
2179 boolean r
= get_num (str
, num_days
, comp_type
);
2183 case COMP_LT
: *comp_type
= COMP_GT
; break;
2184 case COMP_GT
: *comp_type
= COMP_LT
; break;
2190 /* Insert a time predicate PRED.
2191 ARGV is a pointer to the argument array.
2192 ARG_PTR is a pointer to an index into the array, incremented if
2195 Return true if input is valid, false if not.
2197 A new predicate node is assigned, along with an argument node
2198 obtained with malloc.
2200 Used by -atime, -ctime, and -mtime parsers. */
2203 insert_time (char **argv
, int *arg_ptr
, PFB pred
)
2205 struct predicate
*our_pred
;
2207 enum comparison_type c_type
;
2210 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2212 if (!get_num_days (argv
[*arg_ptr
], &num_days
, &c_type
))
2215 /* Figure out the timestamp value we are looking for. */
2216 t
= ( options
.cur_day_start
- num_days
* DAYSECS
2217 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2221 /* We introduce a scope in which 'val' can be declared, for the
2222 * benefit of compilers that are really C89 compilers
2223 * which support intmax_t because config.h #defines it
2225 intmax_t val
= ( (intmax_t)options
.cur_day_start
- num_days
* DAYSECS
2226 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2229 /* Check for possibility of an overflow */
2230 if ( (intmax_t)t
!= val
)
2232 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv
[*arg_ptr
]);
2236 our_pred
= insert_primary (pred
);
2237 our_pred
->args
.info
.kind
= c_type
;
2238 our_pred
->args
.info
.negative
= t
< 0;
2239 our_pred
->args
.info
.l_val
= t
;
2242 fprintf (stderr
, _("inserting %s\n"), our_pred
->p_name
);
2243 fprintf (stderr
, _(" type: %s %s "),
2244 (c_type
== COMP_GT
) ? "gt" :
2245 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2246 (c_type
== COMP_GT
) ? " >" :
2247 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? ">=" : " ?")));
2248 t
= our_pred
->args
.info
.l_val
;
2249 fprintf (stderr
, "%ju %s", (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2250 if (c_type
== COMP_EQ
)
2252 t
= our_pred
->args
.info
.l_val
+= DAYSECS
;
2255 (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2256 our_pred
->args
.info
.l_val
-= DAYSECS
;
2262 /* Get a number with comparision information.
2263 The sense of the comparision information is 'normal'; that is,
2264 '+' looks for a count > than the number and '-' less than.
2266 STR is the ASCII representation of the number.
2267 Set *NUM to the number.
2268 Set *COMP_TYPE to the kind of comparison that is requested.
2270 Return true if all okay, false if input error. */
2273 get_num (char *str
, uintmax_t *num
, enum comparison_type
*comp_type
)
2280 *comp_type
= COMP_GT
;
2284 *comp_type
= COMP_LT
;
2288 *comp_type
= COMP_EQ
;
2292 return xstrtoumax (str
, NULL
, 10, num
, "") == LONGINT_OK
;
2295 /* Insert a number predicate.
2296 ARGV is a pointer to the argument array.
2297 *ARG_PTR is an index into ARGV, incremented if all went well.
2298 *PRED is the predicate processor to insert.
2300 Return true if input is valid, false if error.
2302 A new predicate node is assigned, along with an argument node
2303 obtained with malloc.
2305 Used by -inum and -links parsers. */
2308 insert_num (char **argv
, int *arg_ptr
, PFB pred
)
2310 struct predicate
*our_pred
;
2312 enum comparison_type c_type
;
2314 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2316 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
2318 our_pred
= insert_primary (pred
);
2319 our_pred
->args
.info
.kind
= c_type
;
2320 our_pred
->args
.info
.l_val
= num
;
2323 fprintf (stderr
, _("inserting %s\n"), our_pred
->p_name
);
2324 fprintf (stderr
, _(" type: %s %s "),
2325 (c_type
== COMP_GT
) ? "gt" :
2326 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2327 (c_type
== COMP_GT
) ? " >" :
2328 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? " =" : " ?")));
2329 fprintf (stderr
, "%ju\n", our_pred
->args
.info
.l_val
);
2335 open_output_file (char *path
)
2339 if (!strcmp (path
, "/dev/stderr"))
2341 else if (!strcmp (path
, "/dev/stdout"))
2343 f
= fopen (path
, "w");
2345 error (1, errno
, "%s", path
);