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,
25 #include "../gnulib/lib/modechange.h"
27 #include "../gnulib/lib/xstrtol.h"
28 #include "../gnulib/lib/xalloc.h"
33 # define _(Text) gettext (Text)
38 # define N_(String) gettext_noop (String)
40 /* See locate.c for explanation as to why not use (String) */
41 # define N_(String) String
44 #if !defined (isascii) || defined (STDC_HEADERS)
51 #define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
52 #define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
61 static boolean parse_amin
PARAMS((char *argv
[], int *arg_ptr
));
62 static boolean parse_and
PARAMS((char *argv
[], int *arg_ptr
));
63 static boolean parse_anewer
PARAMS((char *argv
[], int *arg_ptr
));
64 static boolean parse_atime
PARAMS((char *argv
[], int *arg_ptr
));
65 boolean parse_close
PARAMS((char *argv
[], int *arg_ptr
));
66 static boolean parse_cmin
PARAMS((char *argv
[], int *arg_ptr
));
67 static boolean parse_cnewer
PARAMS((char *argv
[], int *arg_ptr
));
68 static boolean parse_comma
PARAMS((char *argv
[], int *arg_ptr
));
69 static boolean parse_ctime
PARAMS((char *argv
[], int *arg_ptr
));
70 static boolean parse_daystart
PARAMS((char *argv
[], int *arg_ptr
));
71 static boolean parse_delete
PARAMS((char *argv
[], int *arg_ptr
));
72 static boolean parse_d
PARAMS((char *argv
[], int *arg_ptr
));
73 static boolean parse_depth
PARAMS((char *argv
[], int *arg_ptr
));
74 static boolean parse_empty
PARAMS((char *argv
[], int *arg_ptr
));
75 static boolean parse_exec
PARAMS((char *argv
[], int *arg_ptr
));
76 static boolean parse_false
PARAMS((char *argv
[], int *arg_ptr
));
77 static boolean parse_fls
PARAMS((char *argv
[], int *arg_ptr
));
78 static boolean parse_fprintf
PARAMS((char *argv
[], int *arg_ptr
));
79 static boolean parse_follow
PARAMS((char *argv
[], int *arg_ptr
));
80 static boolean parse_fprint
PARAMS((char *argv
[], int *arg_ptr
));
81 static boolean parse_fprint0
PARAMS((char *argv
[], int *arg_ptr
));
82 static boolean parse_fstype
PARAMS((char *argv
[], int *arg_ptr
));
83 static boolean parse_gid
PARAMS((char *argv
[], int *arg_ptr
));
84 static boolean parse_group
PARAMS((char *argv
[], int *arg_ptr
));
85 static boolean parse_help
PARAMS((char *argv
[], int *arg_ptr
));
86 static boolean parse_ilname
PARAMS((char *argv
[], int *arg_ptr
));
87 static boolean parse_iname
PARAMS((char *argv
[], int *arg_ptr
));
88 static boolean parse_inum
PARAMS((char *argv
[], int *arg_ptr
));
89 static boolean parse_ipath
PARAMS((char *argv
[], int *arg_ptr
));
90 static boolean parse_iregex
PARAMS((char *argv
[], int *arg_ptr
));
91 static boolean parse_iwholename
PARAMS((char *argv
[], int *arg_ptr
));
92 static boolean parse_links
PARAMS((char *argv
[], int *arg_ptr
));
93 static boolean parse_lname
PARAMS((char *argv
[], int *arg_ptr
));
94 static boolean parse_ls
PARAMS((char *argv
[], int *arg_ptr
));
95 static boolean parse_maxdepth
PARAMS((char *argv
[], int *arg_ptr
));
96 static boolean parse_mindepth
PARAMS((char *argv
[], int *arg_ptr
));
97 static boolean parse_mmin
PARAMS((char *argv
[], int *arg_ptr
));
98 static boolean parse_mtime
PARAMS((char *argv
[], int *arg_ptr
));
99 static boolean parse_name
PARAMS((char *argv
[], int *arg_ptr
));
100 static boolean parse_negate
PARAMS((char *argv
[], int *arg_ptr
));
101 static boolean parse_newer
PARAMS((char *argv
[], int *arg_ptr
));
102 static boolean parse_noleaf
PARAMS((char *argv
[], int *arg_ptr
));
103 static boolean parse_nogroup
PARAMS((char *argv
[], int *arg_ptr
));
104 static boolean parse_nouser
PARAMS((char *argv
[], int *arg_ptr
));
105 static boolean parse_nowarn
PARAMS((char *argv
[], int *arg_ptr
));
106 static boolean parse_ok
PARAMS((char *argv
[], int *arg_ptr
));
107 boolean parse_open
PARAMS((char *argv
[], int *arg_ptr
));
108 static boolean parse_or
PARAMS((char *argv
[], int *arg_ptr
));
109 static boolean parse_path
PARAMS((char *argv
[], int *arg_ptr
));
110 static boolean parse_perm
PARAMS((char *argv
[], int *arg_ptr
));
111 boolean parse_print
PARAMS((char *argv
[], int *arg_ptr
));
112 static boolean parse_print0
PARAMS((char *argv
[], int *arg_ptr
));
113 static boolean parse_printf
PARAMS((char *argv
[], int *arg_ptr
));
114 static boolean parse_prune
PARAMS((char *argv
[], int *arg_ptr
));
115 static boolean parse_regex
PARAMS((char *argv
[], int *arg_ptr
));
116 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
, boolean ignore_case
));
117 static boolean parse_samefile
PARAMS((char *argv
[], int *arg_ptr
));
118 static boolean parse_size
PARAMS((char *argv
[], int *arg_ptr
));
119 static boolean parse_true
PARAMS((char *argv
[], int *arg_ptr
));
120 static boolean parse_type
PARAMS((char *argv
[], int *arg_ptr
));
121 static boolean parse_uid
PARAMS((char *argv
[], int *arg_ptr
));
122 static boolean parse_used
PARAMS((char *argv
[], int *arg_ptr
));
123 static boolean parse_user
PARAMS((char *argv
[], int *arg_ptr
));
124 static boolean parse_version
PARAMS((char *argv
[], int *arg_ptr
));
125 static boolean parse_wholename
PARAMS((char *argv
[], int *arg_ptr
));
126 static boolean parse_xdev
PARAMS((char *argv
[], int *arg_ptr
));
127 static boolean parse_ignore_race
PARAMS((char *argv
[], int *arg_ptr
));
128 static boolean parse_noignore_race
PARAMS((char *argv
[], int *arg_ptr
));
129 static boolean parse_warn
PARAMS((char *argv
[], int *arg_ptr
));
130 static boolean parse_xtype
PARAMS((char *argv
[], int *arg_ptr
));
131 static boolean parse_quit
PARAMS((char *argv
[], int *arg_ptr
));
133 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
, boolean ignore_case
));
134 static boolean insert_type
PARAMS((char *argv
[], int *arg_ptr
, boolean (*which_pred
)()));
135 static boolean insert_fprintf
PARAMS((FILE *fp
, boolean (*func
)(), char *argv
[], int *arg_ptr
));
136 static struct segment
**make_segment
PARAMS((struct segment
**segment
, char *format
, int len
, int kind
));
137 static boolean insert_exec_ok
PARAMS((boolean (*func
)(), char *argv
[], int *arg_ptr
));
138 static boolean get_num_days
PARAMS((char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
));
139 static boolean insert_time
PARAMS((char *argv
[], int *arg_ptr
, PFB pred
));
140 static boolean get_num
PARAMS((char *str
, uintmax_t *num
, enum comparison_type
*comp_type
));
141 static boolean insert_num
PARAMS((char *argv
[], int *arg_ptr
, PFB pred
));
142 static FILE *open_output_file
PARAMS((char *path
));
145 char *find_pred_name
PARAMS((PFB pred_func
));
152 ARG_OPTION
, /* regular options like -maxdepth */
153 ARG_POSITIONAL_OPTION
, /* options whose position is important (-follow) */
154 ARG_TEST
, /* a like -name */
155 ARG_PUNCTUATION
, /* like -o or ( */
156 ARG_ACTION
/* like -print */
167 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
168 If they are in some Unix versions of find, they are marked `Unix'. */
170 static struct parser_table
const parse_table
[] =
172 {ARG_PUNCTUATION
, "!", parse_negate
},
173 {ARG_PUNCTUATION
, "not", parse_negate
}, /* GNU */
174 {ARG_PUNCTUATION
, "(", parse_open
},
175 {ARG_PUNCTUATION
, ")", parse_close
},
176 {ARG_PUNCTUATION
, ",", parse_comma
}, /* GNU */
177 {ARG_PUNCTUATION
, "a", parse_and
},
178 {ARG_TEST
, "amin", parse_amin
}, /* GNU */
179 {ARG_PUNCTUATION
, "and", parse_and
}, /* GNU */
180 {ARG_TEST
, "anewer", parse_anewer
}, /* GNU */
181 {ARG_TEST
, "atime", parse_atime
},
182 {ARG_TEST
, "cmin", parse_cmin
}, /* GNU */
183 {ARG_TEST
, "cnewer", parse_cnewer
}, /* GNU */
184 #ifdef UNIMPLEMENTED_UNIX
185 /* It's pretty ugly for find to know about archive formats.
186 Plus what it could do with cpio archives is very limited.
187 Better to leave it out. */
188 {ARG_UNIMPLEMENTED
, "cpio", parse_cpio
}, /* Unix */
190 {ARG_TEST
, "ctime", parse_ctime
},
191 {ARG_POSITIONAL_OPTION
, "daystart", parse_daystart
}, /* GNU */
192 {ARG_ACTION
, "delete", parse_delete
}, /* GNU, Mac OS, FreeBSD */
193 {ARG_OPTION
, "d", parse_d
}, /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
194 {ARG_OPTION
, "depth", parse_depth
},
195 {ARG_TEST
, "empty", parse_empty
}, /* GNU */
196 {ARG_ACTION
, "exec", parse_exec
},
197 {ARG_TEST
, "false", parse_false
}, /* GNU */
198 {ARG_ACTION
, "fls", parse_fls
}, /* GNU */
199 {ARG_POSITIONAL_OPTION
, "follow", parse_follow
}, /* GNU, Unix */
200 {ARG_ACTION
, "fprint", parse_fprint
}, /* GNU */
201 {ARG_ACTION
, "fprint0", parse_fprint0
}, /* GNU */
202 {ARG_ACTION
, "fprintf", parse_fprintf
}, /* GNU */
203 {ARG_TEST
, "fstype", parse_fstype
}, /* GNU, Unix */
204 {ARG_TEST
, "gid", parse_gid
}, /* GNU */
205 {ARG_TEST
, "group", parse_group
},
206 {ARG_TEST
, "help", parse_help
}, /* GNU */
207 {ARG_TEST
, "-help", parse_help
}, /* GNU */
208 {ARG_OPTION
, "ignore_readdir_race", parse_ignore_race
}, /* GNU */
209 {ARG_TEST
, "ilname", parse_ilname
}, /* GNU */
210 {ARG_TEST
, "iname", parse_iname
}, /* GNU */
211 {ARG_TEST
, "inum", parse_inum
}, /* GNU, Unix */
212 {ARG_TEST
, "ipath", parse_ipath
}, /* GNU, deprecated in favour of iwholename */
213 {ARG_TEST
, "iregex", parse_iregex
}, /* GNU */
214 {ARG_TEST
, "iwholename", parse_iwholename
}, /* GNU */
215 {ARG_TEST
, "links", parse_links
},
216 {ARG_TEST
, "lname", parse_lname
}, /* GNU */
217 {ARG_ACTION
, "ls", parse_ls
}, /* GNU, Unix */
218 {ARG_OPTION
, "maxdepth", parse_maxdepth
}, /* GNU */
219 {ARG_OPTION
, "mindepth", parse_mindepth
}, /* GNU */
220 {ARG_TEST
, "mmin", parse_mmin
}, /* GNU */
221 {ARG_OPTION
, "mount", parse_xdev
}, /* Unix */
222 {ARG_TEST
, "mtime", parse_mtime
},
223 {ARG_TEST
, "name", parse_name
},
224 #ifdef UNIMPLEMENTED_UNIX
225 {ARG_UNIMPLEMENTED
, "ncpio", parse_ncpio
}, /* Unix */
227 {ARG_TEST
, "newer", parse_newer
},
228 {ARG_OPTION
, "noleaf", parse_noleaf
}, /* GNU */
229 {ARG_TEST
, "nogroup", parse_nogroup
},
230 {ARG_TEST
, "nouser", parse_nouser
},
231 {ARG_OPTION
, "noignore_readdir_race", parse_noignore_race
},/* GNU */
232 {ARG_OPTION
, "nowarn", parse_nowarn
}, /* GNU */
233 {ARG_PUNCTUATION
, "o", parse_or
},
234 {ARG_PUNCTUATION
, "or", parse_or
}, /* GNU */
235 {ARG_ACTION
, "ok", parse_ok
},
236 {ARG_TEST
, "path", parse_path
}, /* GNU, HP-UX, GNU prefers wholename */
237 {ARG_TEST
, "perm", parse_perm
},
238 {ARG_ACTION
, "print", parse_print
},
239 {ARG_ACTION
, "print0", parse_print0
}, /* GNU */
240 {ARG_ACTION
, "printf", parse_printf
}, /* GNU */
241 {ARG_TEST
, "prune", parse_prune
},
242 {ARG_ACTION
, "quit", parse_quit
}, /* GNU */
243 {ARG_TEST
, "regex", parse_regex
}, /* GNU */
244 {ARG_TEST
, "samefile", parse_samefile
}, /* GNU */
245 {ARG_TEST
, "size", parse_size
},
246 {ARG_TEST
, "true", parse_true
}, /* GNU */
247 {ARG_TEST
, "type", parse_type
},
248 {ARG_TEST
, "uid", parse_uid
}, /* GNU */
249 {ARG_TEST
, "used", parse_used
}, /* GNU */
250 {ARG_TEST
, "user", parse_user
},
251 {ARG_TEST
, "version", parse_version
}, /* GNU */
252 {ARG_TEST
, "-version", parse_version
}, /* GNU */
253 {ARG_OPTION
, "warn", parse_warn
}, /* GNU */
254 {ARG_TEST
, "wholename", parse_wholename
}, /* GNU, replaces -path */
255 {ARG_OPTION
, "xdev", parse_xdev
},
256 {ARG_TEST
, "xtype", parse_xtype
}, /* GNU */
261 static const char *first_nonoption_arg
= NULL
;
263 /* Return a pointer to the parser function to invoke for predicate
265 Return NULL if SEARCH_NAME is not a valid predicate name. */
268 find_parser (char *search_name
)
271 const char *original_arg
= search_name
;
273 if (*search_name
== '-')
275 for (i
= 0; parse_table
[i
].parser_name
!= 0; i
++)
277 if (strcmp (parse_table
[i
].parser_name
, search_name
) == 0)
279 /* If this is an option, but we have already had a
280 * non-option argument, the user may be under the
281 * impression that the behaviour of the option
282 * argument is conditional on some preceding
283 * tests. This might typically be the case with,
284 * for example, -maxdepth.
286 * The options -daystart and -follow are exempt
287 * from this treatment, since their positioning
288 * in the command line does have an effect on
289 * subsequent tests but not previous ones. That
290 * might be intentional on the part of the user.
292 if (parse_table
[i
].type
!= ARG_POSITIONAL_OPTION
)
294 /* Something other than -follow/-daystart.
295 * If this is an option, check if it followed
296 * a non-option and if so, issue a warning.
298 if (parse_table
[i
].type
== ARG_OPTION
)
300 if ((first_nonoption_arg
!= NULL
)
301 && options
.warnings
)
303 /* option which folows a non-option */
305 _("warning: you have specified the %s "
306 "option after a non-option argument %s, "
307 "but options are not positional (%s affects "
308 "tests specified before it as well as those "
309 "specified after it). Please specify options "
310 "before other arguments.\n"),
318 /* Not an option or a positional option,
319 * so remember we've seen it in order to
320 * use it in a possible future warning message.
322 if (first_nonoption_arg
== NULL
)
324 first_nonoption_arg
= original_arg
;
329 return (parse_table
[i
].parser_func
);
335 /* The parsers are responsible to continue scanning ARGV for
336 their arguments. Each parser knows what is and isn't
339 ARGV is the argument array.
340 *ARG_PTR is the index to start at in ARGV,
341 updated to point beyond the last element consumed.
343 The predicate structure is updated with the new information. */
346 parse_amin (char **argv
, int *arg_ptr
)
348 struct predicate
*our_pred
;
350 enum comparison_type c_type
;
353 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
355 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
357 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
358 our_pred
= insert_primary (pred_amin
);
359 our_pred
->args
.info
.kind
= c_type
;
360 our_pred
->args
.info
.negative
= t
< 0;
361 our_pred
->args
.info
.l_val
= t
;
367 parse_and (char **argv
, int *arg_ptr
)
369 struct predicate
*our_pred
;
374 our_pred
= get_new_pred ();
375 our_pred
->pred_func
= pred_and
;
377 our_pred
->p_name
= find_pred_name (pred_and
);
379 our_pred
->p_type
= BI_OP
;
380 our_pred
->p_prec
= AND_PREC
;
381 our_pred
->need_stat
= false;
386 parse_anewer (char **argv
, int *arg_ptr
)
388 struct predicate
*our_pred
;
389 struct stat stat_newer
;
391 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
393 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
394 error (1, errno
, "%s", argv
[*arg_ptr
]);
395 our_pred
= insert_primary (pred_anewer
);
396 our_pred
->args
.time
= stat_newer
.st_mtime
;
402 parse_atime (char **argv
, int *arg_ptr
)
404 return (insert_time (argv
, arg_ptr
, pred_atime
));
408 parse_close (char **argv
, int *arg_ptr
)
410 struct predicate
*our_pred
;
415 our_pred
= get_new_pred ();
416 our_pred
->pred_func
= pred_close
;
418 our_pred
->p_name
= find_pred_name (pred_close
);
420 our_pred
->p_type
= CLOSE_PAREN
;
421 our_pred
->p_prec
= NO_PREC
;
422 our_pred
->need_stat
= false;
427 parse_cmin (char **argv
, int *arg_ptr
)
429 struct predicate
*our_pred
;
431 enum comparison_type c_type
;
434 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
436 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
438 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
439 our_pred
= insert_primary (pred_cmin
);
440 our_pred
->args
.info
.kind
= c_type
;
441 our_pred
->args
.info
.negative
= t
< 0;
442 our_pred
->args
.info
.l_val
= t
;
448 parse_cnewer (char **argv
, int *arg_ptr
)
450 struct predicate
*our_pred
;
451 struct stat stat_newer
;
453 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
455 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
456 error (1, errno
, "%s", argv
[*arg_ptr
]);
457 our_pred
= insert_primary (pred_cnewer
);
458 our_pred
->args
.time
= stat_newer
.st_mtime
;
464 parse_comma (char **argv
, int *arg_ptr
)
466 struct predicate
*our_pred
;
471 our_pred
= get_new_pred ();
472 our_pred
->pred_func
= pred_comma
;
474 our_pred
->p_name
= find_pred_name (pred_comma
);
476 our_pred
->p_type
= BI_OP
;
477 our_pred
->p_prec
= COMMA_PREC
;
478 our_pred
->need_stat
= false;
483 parse_ctime (char **argv
, int *arg_ptr
)
485 return (insert_time (argv
, arg_ptr
, pred_ctime
));
489 parse_daystart (char **argv
, int *arg_ptr
)
496 if (options
.full_days
== false)
498 options
.cur_day_start
+= DAYSECS
;
499 local
= localtime (&options
.cur_day_start
);
500 options
.cur_day_start
-= (local
501 ? (local
->tm_sec
+ local
->tm_min
* 60
502 + local
->tm_hour
* 3600)
503 : options
.cur_day_start
% DAYSECS
);
504 options
.full_days
= true;
510 parse_delete (argv
, arg_ptr
)
514 struct predicate
*our_pred
;
518 our_pred
= insert_primary (pred_delete
);
519 our_pred
->side_effects
= true;
520 our_pred
->no_default_print
= true;
521 /* -delete implies -depth */
522 options
.do_dir_first
= false;
527 parse_depth (char **argv
, int *arg_ptr
)
532 options
.do_dir_first
= false;
537 parse_d (char **argv
, int *arg_ptr
)
542 if (options
.warnings
)
545 _("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
547 return parse_depth(argv
, arg_ptr
);
551 parse_empty (char **argv
, int *arg_ptr
)
556 insert_primary (pred_empty
);
561 parse_exec (char **argv
, int *arg_ptr
)
563 return (insert_exec_ok (pred_exec
, argv
, arg_ptr
));
567 parse_false (char **argv
, int *arg_ptr
)
569 struct predicate
*our_pred
;
574 our_pred
= insert_primary (pred_false
);
575 our_pred
->need_stat
= false;
580 parse_fls (char **argv
, int *arg_ptr
)
582 struct predicate
*our_pred
;
584 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
586 our_pred
= insert_primary (pred_fls
);
587 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
588 our_pred
->side_effects
= true;
589 our_pred
->no_default_print
= true;
595 parse_fprintf (char **argv
, int *arg_ptr
)
599 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
601 if (argv
[*arg_ptr
+ 1] == NULL
)
603 /* Ensure we get "missing arg" message, not "invalid arg". */
607 fp
= open_output_file (argv
[*arg_ptr
]);
609 return (insert_fprintf (fp
, pred_fprintf
, argv
, arg_ptr
));
613 parse_follow (char **argv
, int *arg_ptr
)
618 set_follow_state(SYMLINK_ALWAYS_DEREF
);
623 parse_fprint (char **argv
, int *arg_ptr
)
625 struct predicate
*our_pred
;
627 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
629 our_pred
= insert_primary (pred_fprint
);
630 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
631 our_pred
->side_effects
= true;
632 our_pred
->no_default_print
= true;
633 our_pred
->need_stat
= false;
639 parse_fprint0 (char **argv
, int *arg_ptr
)
641 struct predicate
*our_pred
;
643 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
645 our_pred
= insert_primary (pred_fprint0
);
646 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
647 our_pred
->side_effects
= true;
648 our_pred
->no_default_print
= true;
649 our_pred
->need_stat
= false;
655 parse_fstype (char **argv
, int *arg_ptr
)
657 struct predicate
*our_pred
;
659 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
661 our_pred
= insert_primary (pred_fstype
);
662 our_pred
->args
.str
= argv
[*arg_ptr
];
668 parse_gid (char **argv
, int *arg_ptr
)
670 return (insert_num (argv
, arg_ptr
, pred_gid
));
674 parse_group (char **argv
, int *arg_ptr
)
676 struct group
*cur_gr
;
677 struct predicate
*our_pred
;
681 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
683 cur_gr
= getgrnam (argv
[*arg_ptr
]);
686 gid
= cur_gr
->gr_gid
;
689 gid_len
= strspn (argv
[*arg_ptr
], "0123456789");
690 if ((gid_len
== 0) || (argv
[*arg_ptr
][gid_len
] != '\0'))
692 gid
= atoi (argv
[*arg_ptr
]);
694 our_pred
= insert_primary (pred_group
);
695 our_pred
->args
.gid
= gid
;
701 parse_help (char **argv
, int *arg_ptr
)
707 Usage: %s [path...] [expression]\n"), program_name
);
709 default path is the current directory; default expression is -print\n\
710 expression may consist of: operators, options, tests, and actions:\n"));
712 operators (decreasing precedence; -and is implicit where no others are given):\n\
713 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
714 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
716 positional options (always true): -daystart -follow\n\
717 normal options (always true, specified before other expressions):\n\
718 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
719 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
721 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
722 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
723 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
724 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
726 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
727 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
728 -used N -user NAME -xtype [bcdpfls]\n"));
730 actions: -exec COMMAND ; -fprint FILE -fprint0 FILE -fprintf FILE FORMAT\n\
731 -fls FILE -ok COMMAND ; -print -print0 -printf FORMAT -prune -ls -delete\n\
733 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
734 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
735 email to <bug-findutils@gnu.org>."));
740 parse_ilname (char **argv
, int *arg_ptr
)
742 struct predicate
*our_pred
;
744 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
746 our_pred
= insert_primary (pred_ilname
);
747 our_pred
->args
.str
= argv
[*arg_ptr
];
753 /* sanity check the fnmatch() function to make sure
754 * it really is the GNU version.
757 fnmatch_sanitycheck()
759 /* fprintf(stderr, "Performing find sanity check..."); */
760 if (0 != fnmatch("foo", "foo", 0)
761 || 0 == fnmatch("Foo", "foo", 0)
762 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD
))
764 error (1, 0, _("sanity check of the fnmatch() library function failed."));
765 /* fprintf(stderr, "FAILED\n"); */
769 /* fprintf(stderr, "OK\n"); */
776 parse_iname (char **argv
, int *arg_ptr
)
778 struct predicate
*our_pred
;
780 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
783 fnmatch_sanitycheck();
785 our_pred
= insert_primary (pred_iname
);
786 our_pred
->need_stat
= false;
787 our_pred
->args
.str
= argv
[*arg_ptr
];
793 parse_inum (char **argv
, int *arg_ptr
)
795 return (insert_num (argv
, arg_ptr
, pred_inum
));
798 /* -ipath is deprecated (at RMS's request) in favour of
799 * -iwholename. See the node "GNU Manuals" in standards.texi
800 * for the rationale for this (basically, GNU prefers the use
801 * of the phrase "file name" to "path name"
804 parse_ipath (char **argv
, int *arg_ptr
)
807 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
809 return parse_iwholename(argv
, arg_ptr
);
813 parse_iwholename (char **argv
, int *arg_ptr
)
815 struct predicate
*our_pred
;
817 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
820 fnmatch_sanitycheck();
822 our_pred
= insert_primary (pred_ipath
);
823 our_pred
->need_stat
= false;
824 our_pred
->args
.str
= argv
[*arg_ptr
];
830 parse_iregex (char **argv
, int *arg_ptr
)
832 return insert_regex (argv
, arg_ptr
, true);
836 parse_links (char **argv
, int *arg_ptr
)
838 return (insert_num (argv
, arg_ptr
, pred_links
));
842 parse_lname (char **argv
, int *arg_ptr
)
844 struct predicate
*our_pred
;
849 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
852 fnmatch_sanitycheck();
854 our_pred
= insert_primary (pred_lname
);
855 our_pred
->args
.str
= argv
[*arg_ptr
];
861 parse_ls (char **argv
, int *arg_ptr
)
863 struct predicate
*our_pred
;
868 our_pred
= insert_primary (pred_ls
);
869 our_pred
->side_effects
= true;
870 our_pred
->no_default_print
= true;
875 parse_maxdepth (char **argv
, int *arg_ptr
)
879 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
881 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
882 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
884 options
.maxdepth
= atoi (argv
[*arg_ptr
]);
885 if (options
.maxdepth
< 0)
892 parse_mindepth (char **argv
, int *arg_ptr
)
896 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
898 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
899 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
901 options
.mindepth
= atoi (argv
[*arg_ptr
]);
902 if (options
.mindepth
< 0)
909 parse_mmin (char **argv
, int *arg_ptr
)
911 struct predicate
*our_pred
;
913 enum comparison_type c_type
;
916 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
918 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
920 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
921 our_pred
= insert_primary (pred_mmin
);
922 our_pred
->args
.info
.kind
= c_type
;
923 our_pred
->args
.info
.negative
= t
< 0;
924 our_pred
->args
.info
.l_val
= t
;
930 parse_mtime (char **argv
, int *arg_ptr
)
932 return (insert_time (argv
, arg_ptr
, pred_mtime
));
936 parse_name (char **argv
, int *arg_ptr
)
938 struct predicate
*our_pred
;
943 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
945 our_pred
= insert_primary (pred_name
);
946 our_pred
->need_stat
= false;
947 our_pred
->args
.str
= argv
[*arg_ptr
];
953 parse_negate (char **argv
, int *arg_ptr
)
955 struct predicate
*our_pred
;
960 our_pred
= get_new_pred_chk_op ();
961 our_pred
->pred_func
= pred_negate
;
963 our_pred
->p_name
= find_pred_name (pred_negate
);
965 our_pred
->p_type
= UNI_OP
;
966 our_pred
->p_prec
= NEGATE_PREC
;
967 our_pred
->need_stat
= false;
972 parse_newer (char **argv
, int *arg_ptr
)
974 struct predicate
*our_pred
;
975 struct stat stat_newer
;
980 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
982 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
983 error (1, errno
, "%s", argv
[*arg_ptr
]);
984 our_pred
= insert_primary (pred_newer
);
985 our_pred
->args
.time
= stat_newer
.st_mtime
;
991 parse_noleaf (char **argv
, int *arg_ptr
)
996 options
.no_leaf_check
= true;
1001 /* Arbitrary amount by which to increase size
1002 of `uid_unused' and `gid_unused'. */
1003 #define ALLOC_STEP 2048
1005 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1006 char *uid_unused
= NULL
;
1008 /* Number of elements in `uid_unused'. */
1009 unsigned uid_allocated
;
1011 /* Similar for GIDs and group entries. */
1012 char *gid_unused
= NULL
;
1013 unsigned gid_allocated
;
1017 parse_nogroup (char **argv
, int *arg_ptr
)
1019 struct predicate
*our_pred
;
1024 our_pred
= insert_primary (pred_nogroup
);
1026 if (gid_unused
== NULL
)
1030 gid_allocated
= ALLOC_STEP
;
1031 gid_unused
= xmalloc (gid_allocated
);
1032 memset (gid_unused
, 1, gid_allocated
);
1034 while ((gr
= getgrent ()) != NULL
)
1036 if ((unsigned) gr
->gr_gid
>= gid_allocated
)
1038 unsigned new_allocated
= (unsigned) gr
->gr_gid
+ ALLOC_STEP
;
1039 gid_unused
= xrealloc (gid_unused
, new_allocated
);
1040 memset (gid_unused
+ gid_allocated
, 1,
1041 new_allocated
- gid_allocated
);
1042 gid_allocated
= new_allocated
;
1044 gid_unused
[(unsigned) gr
->gr_gid
] = 0;
1053 parse_nouser (char **argv
, int *arg_ptr
)
1055 struct predicate
*our_pred
;
1060 our_pred
= insert_primary (pred_nouser
);
1062 if (uid_unused
== NULL
)
1066 uid_allocated
= ALLOC_STEP
;
1067 uid_unused
= xmalloc (uid_allocated
);
1068 memset (uid_unused
, 1, uid_allocated
);
1070 while ((pw
= getpwent ()) != NULL
)
1072 if ((unsigned) pw
->pw_uid
>= uid_allocated
)
1074 unsigned new_allocated
= (unsigned) pw
->pw_uid
+ ALLOC_STEP
;
1075 uid_unused
= xrealloc (uid_unused
, new_allocated
);
1076 memset (uid_unused
+ uid_allocated
, 1,
1077 new_allocated
- uid_allocated
);
1078 uid_allocated
= new_allocated
;
1080 uid_unused
[(unsigned) pw
->pw_uid
] = 0;
1089 parse_nowarn (char **argv
, int *arg_ptr
)
1094 options
.warnings
= false;
1099 parse_ok (char **argv
, int *arg_ptr
)
1101 return (insert_exec_ok (pred_ok
, argv
, arg_ptr
));
1105 parse_open (char **argv
, int *arg_ptr
)
1107 struct predicate
*our_pred
;
1112 our_pred
= get_new_pred_chk_op ();
1113 our_pred
->pred_func
= pred_open
;
1115 our_pred
->p_name
= find_pred_name (pred_open
);
1117 our_pred
->p_type
= OPEN_PAREN
;
1118 our_pred
->p_prec
= NO_PREC
;
1119 our_pred
->need_stat
= false;
1124 parse_or (char **argv
, int *arg_ptr
)
1126 struct predicate
*our_pred
;
1131 our_pred
= get_new_pred ();
1132 our_pred
->pred_func
= pred_or
;
1134 our_pred
->p_name
= find_pred_name (pred_or
);
1136 our_pred
->p_type
= BI_OP
;
1137 our_pred
->p_prec
= OR_PREC
;
1138 our_pred
->need_stat
= false;
1142 /* -path is deprecated (at RMS's request) in favour of
1143 * -iwholename. See the node "GNU Manuals" in standards.texi
1144 * for the rationale for this (basically, GNU prefers the use
1145 * of the phrase "file name" to "path name".
1147 * We do not issue a warning that this usage is deprecated
1148 * since HPUX find supports this predicate also.
1151 parse_path (char **argv
, int *arg_ptr
)
1153 return parse_wholename(argv
, arg_ptr
);
1157 parse_wholename (char **argv
, int *arg_ptr
)
1159 struct predicate
*our_pred
;
1161 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1163 our_pred
= insert_primary (pred_path
);
1164 our_pred
->need_stat
= false;
1165 our_pred
->args
.str
= argv
[*arg_ptr
];
1171 parse_perm (char **argv
, int *arg_ptr
)
1175 struct mode_change
*change
;
1176 struct predicate
*our_pred
;
1178 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1181 switch (argv
[*arg_ptr
][0])
1192 change
= mode_compile (argv
[*arg_ptr
] + mode_start
, MODE_MASK_PLUS
);
1193 if (change
== MODE_INVALID
)
1194 error (1, 0, _("invalid mode `%s'"), argv
[*arg_ptr
]);
1195 else if (change
== MODE_MEMORY_EXHAUSTED
)
1196 error (1, 0, _("virtual memory exhausted"));
1197 perm_val
= mode_adjust (0, change
);
1200 our_pred
= insert_primary (pred_perm
);
1202 switch (argv
[*arg_ptr
][0])
1205 our_pred
->args
.perm
.kind
= PERM_AT_LEAST
;
1208 our_pred
->args
.perm
.kind
= PERM_ANY
;
1211 our_pred
->args
.perm
.kind
= PERM_EXACT
;
1214 our_pred
->args
.perm
.val
= perm_val
& MODE_ALL
;
1220 parse_print (char **argv
, int *arg_ptr
)
1222 struct predicate
*our_pred
;
1227 our_pred
= insert_primary (pred_print
);
1228 /* -print has the side effect of printing. This prevents us
1229 from doing undesired multiple printing when the user has
1230 already specified -print. */
1231 our_pred
->side_effects
= true;
1232 our_pred
->no_default_print
= true;
1233 our_pred
->need_stat
= false;
1238 parse_print0 (char **argv
, int *arg_ptr
)
1240 struct predicate
*our_pred
;
1245 our_pred
= insert_primary (pred_print0
);
1246 /* -print0 has the side effect of printing. This prevents us
1247 from doing undesired multiple printing when the user has
1248 already specified -print0. */
1249 our_pred
->side_effects
= true;
1250 our_pred
->no_default_print
= true;
1251 our_pred
->need_stat
= false;
1256 parse_printf (char **argv
, int *arg_ptr
)
1258 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1260 return (insert_fprintf (stdout
, pred_fprintf
, argv
, arg_ptr
));
1264 parse_prune (char **argv
, int *arg_ptr
)
1266 struct predicate
*our_pred
;
1271 our_pred
= insert_primary (pred_prune
);
1272 our_pred
->need_stat
= false;
1273 /* -prune has a side effect that it does not descend into
1274 the current directory. */
1275 our_pred
->side_effects
= true;
1280 parse_quit (char **argv
, int *arg_ptr
)
1282 struct predicate
*our_pred
= insert_primary (pred_quit
);
1285 our_pred
->need_stat
= false;
1291 parse_regex (char **argv
, int *arg_ptr
)
1293 return insert_regex (argv
, arg_ptr
, false);
1297 insert_regex (char **argv
, int *arg_ptr
, boolean ignore_case
)
1299 struct predicate
*our_pred
;
1300 struct re_pattern_buffer
*re
;
1301 const char *error_message
;
1303 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1305 our_pred
= insert_primary (pred_regex
);
1306 our_pred
->need_stat
= false;
1307 re
= (struct re_pattern_buffer
*)
1308 xmalloc (sizeof (struct re_pattern_buffer
));
1309 our_pred
->args
.regex
= re
;
1310 re
->allocated
= 100;
1311 re
->buffer
= (unsigned char *) xmalloc (re
->allocated
);
1316 re_syntax_options
|= RE_ICASE
;
1320 re_syntax_options
&= ~RE_ICASE
;
1322 re
->translate
= NULL
;
1324 error_message
= re_compile_pattern (argv
[*arg_ptr
], strlen (argv
[*arg_ptr
]),
1327 error (1, 0, "%s", error_message
);
1333 parse_size (char **argv
, int *arg_ptr
)
1335 struct predicate
*our_pred
;
1337 enum comparison_type c_type
;
1341 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1343 len
= strlen (argv
[*arg_ptr
]);
1345 error (1, 0, _("invalid null argument to -size"));
1346 switch (argv
[*arg_ptr
][len
- 1])
1350 argv
[*arg_ptr
][len
- 1] = '\0';
1355 argv
[*arg_ptr
][len
- 1] = '\0';
1360 argv
[*arg_ptr
][len
- 1] = '\0';
1363 case 'M': /* Megabytes */
1364 blksize
= 1024*1024;
1365 argv
[*arg_ptr
][len
- 1] = '\0';
1368 case 'G': /* Gigabytes */
1369 blksize
= 1024*1024*1024;
1370 argv
[*arg_ptr
][len
- 1] = '\0';
1375 argv
[*arg_ptr
][len
- 1] = '\0';
1391 error (1, 0, _("invalid -size type `%c'"), argv
[*arg_ptr
][len
- 1]);
1393 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
1395 our_pred
= insert_primary (pred_size
);
1396 our_pred
->args
.size
.kind
= c_type
;
1397 our_pred
->args
.size
.blocksize
= blksize
;
1398 our_pred
->args
.size
.size
= num
;
1405 parse_samefile (char **argv
, int *arg_ptr
)
1407 struct predicate
*our_pred
;
1410 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1412 if ((*options
.xstat
) (argv
[*arg_ptr
], &st
))
1413 error (1, errno
, "%s", argv
[*arg_ptr
]);
1415 our_pred
= insert_primary (pred_samefile
);
1416 our_pred
->args
.fileid
.ino
= st
.st_ino
;
1417 our_pred
->args
.fileid
.dev
= st
.st_dev
;
1418 our_pred
->need_stat
= true;
1425 parse_true (char **argv
, int *arg_ptr
)
1427 struct predicate
*our_pred
;
1432 our_pred
= insert_primary (pred_true
);
1433 our_pred
->need_stat
= false;
1438 parse_type (char **argv
, int *arg_ptr
)
1440 return insert_type (argv
, arg_ptr
, pred_type
);
1444 parse_uid (char **argv
, int *arg_ptr
)
1446 return (insert_num (argv
, arg_ptr
, pred_uid
));
1450 parse_used (char **argv
, int *arg_ptr
)
1452 struct predicate
*our_pred
;
1454 enum comparison_type c_type
;
1457 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1459 if (!get_num (argv
[*arg_ptr
], &num_days
, &c_type
))
1461 t
= num_days
* DAYSECS
;
1462 our_pred
= insert_primary (pred_used
);
1463 our_pred
->args
.info
.kind
= c_type
;
1464 our_pred
->args
.info
.negative
= t
< 0;
1465 our_pred
->args
.info
.l_val
= t
;
1471 parse_user (char **argv
, int *arg_ptr
)
1473 struct passwd
*cur_pwd
;
1474 struct predicate
*our_pred
;
1478 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1480 cur_pwd
= getpwnam (argv
[*arg_ptr
]);
1482 if (cur_pwd
!= NULL
)
1483 uid
= cur_pwd
->pw_uid
;
1486 uid_len
= strspn (argv
[*arg_ptr
], "0123456789");
1487 if ((uid_len
== 0) || (argv
[*arg_ptr
][uid_len
] != '\0'))
1489 uid
= atoi (argv
[*arg_ptr
]);
1491 our_pred
= insert_primary (pred_user
);
1492 our_pred
->args
.uid
= uid
;
1498 parse_version (char **argv
, int *arg_ptr
)
1500 extern char *version_string
;
1505 printf (_("GNU find version %s\n"), version_string
);
1510 parse_xdev (char **argv
, int *arg_ptr
)
1514 options
.stay_on_filesystem
= true;
1519 parse_ignore_race (char **argv
, int *arg_ptr
)
1523 options
.ignore_readdir_race
= true;
1528 parse_noignore_race (char **argv
, int *arg_ptr
)
1532 options
.ignore_readdir_race
= false;
1537 parse_warn (char **argv
, int *arg_ptr
)
1541 options
.warnings
= true;
1546 parse_xtype (char **argv
, int *arg_ptr
)
1550 return insert_type (argv
, arg_ptr
, pred_xtype
);
1554 insert_type (char **argv
, int *arg_ptr
, boolean (*which_pred
) (/* ??? */))
1557 struct predicate
*our_pred
;
1559 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
)
1560 || (strlen (argv
[*arg_ptr
]) != 1))
1562 switch (argv
[*arg_ptr
][0])
1564 case 'b': /* block special */
1565 type_cell
= S_IFBLK
;
1567 case 'c': /* character special */
1568 type_cell
= S_IFCHR
;
1570 case 'd': /* directory */
1571 type_cell
= S_IFDIR
;
1573 case 'f': /* regular file */
1574 type_cell
= S_IFREG
;
1577 case 'l': /* symbolic link */
1578 type_cell
= S_IFLNK
;
1582 case 'p': /* pipe */
1583 type_cell
= S_IFIFO
;
1587 case 's': /* socket */
1588 type_cell
= S_IFSOCK
;
1592 case 'D': /* Solaris door */
1593 type_cell
= S_IFDOOR
;
1596 default: /* None of the above ... nuke 'em. */
1599 our_pred
= insert_primary (which_pred
);
1600 our_pred
->args
.type
= type_cell
;
1601 (*arg_ptr
)++; /* Move on to next argument. */
1605 /* If true, we've determined that the current fprintf predicate
1606 uses stat information. */
1607 static boolean fprintf_stat_needed
;
1610 insert_fprintf (FILE *fp
, boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
1612 char *format
; /* Beginning of unprocessed format string. */
1613 register char *scan
; /* Current address in scanning `format'. */
1614 register char *scan2
; /* Address inside of element being scanned. */
1615 struct segment
**segmentp
; /* Address of current segment. */
1616 struct predicate
*our_pred
;
1618 format
= argv
[(*arg_ptr
)++];
1620 fprintf_stat_needed
= false; /* Might be overridden later. */
1621 our_pred
= insert_primary (func
);
1622 our_pred
->side_effects
= true;
1623 our_pred
->no_default_print
= true;
1624 our_pred
->args
.printf_vec
.stream
= fp
;
1625 segmentp
= &our_pred
->args
.printf_vec
.segment
;
1628 for (scan
= format
; *scan
; scan
++)
1633 if (*scan2
>= '0' && *scan2
<= '7')
1637 for (i
= n
= 0; i
< 3 && (*scan2
>= '0' && *scan2
<= '7');
1639 n
= 8 * n
+ *scan2
- '0';
1654 make_segment (segmentp
, format
, scan
- format
, KIND_STOP
);
1655 our_pred
->need_stat
= fprintf_stat_needed
;
1673 /* *scan = '\\'; * it already is */
1677 _("warning: unrecognized escape `\\%c'"), *scan2
);
1682 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1684 format
= scan2
+ 1; /* Move past the escape. */
1685 scan
= scan2
; /* Incremented immediately by `for'. */
1687 else if (*scan
== '%')
1691 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1697 /* Scan past flags, width and precision, to verify kind. */
1698 for (scan2
= scan
; *++scan2
&& strchr ("-+ #", *scan2
);)
1700 while (ISDIGIT (*scan2
))
1703 for (scan2
++; ISDIGIT (*scan2
); scan2
++)
1705 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2
))
1707 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1712 else if (strchr ("ACT", *scan2
) && scan2
[1])
1714 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1715 *scan2
| (scan2
[1] << 8));
1722 /* An unrecognized % escape. Print the char after the %. */
1723 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1725 segmentp
= make_segment (segmentp
, format
, scan
- format
,
1734 make_segment (segmentp
, format
, scan
- format
, KIND_PLAIN
);
1735 our_pred
->need_stat
= fprintf_stat_needed
;
1739 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1740 from the text in FORMAT, which has length LEN.
1741 Return the address of the `next' pointer of the new segment. */
1743 static struct segment
**
1744 make_segment (struct segment
**segment
, char *format
, int len
, int kind
)
1748 *segment
= (struct segment
*) xmalloc (sizeof (struct segment
));
1750 (*segment
)->kind
= kind
;
1751 (*segment
)->next
= NULL
;
1752 (*segment
)->text_len
= len
;
1754 fmt
= (*segment
)->text
= xmalloc (len
+ sizeof "d");
1755 strncpy (fmt
, format
, len
);
1758 switch (kind
& 0xff)
1760 case KIND_PLAIN
: /* Plain text string, no % conversion. */
1761 case KIND_STOP
: /* Terminate argument, no newline. */
1764 case 'a': /* atime in `ctime' format */
1765 case 'A': /* atime in user-specified strftime format */
1766 case 'c': /* ctime in `ctime' format */
1767 case 'C': /* ctime in user-specified strftime format */
1768 case 'F': /* filesystem type */
1769 case 'g': /* group name */
1770 case 'i': /* inode number */
1771 case 'l': /* object of symlink */
1772 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
1773 case 's': /* size in bytes */
1774 case 't': /* mtime in `ctime' format */
1775 case 'T': /* mtime in user-specified strftime format */
1776 case 'u': /* user name */
1777 case 'y': /* file type */
1778 case 'Y': /* symlink pointed file type */
1779 fprintf_stat_needed
= true;
1781 case 'f': /* basename of path */
1782 case 'h': /* leading directories part of path */
1783 case 'H': /* ARGV element file was found under */
1784 case 'p': /* pathname */
1785 case 'P': /* pathname with ARGV element stripped */
1789 /* Numeric items that one might expect to honour
1790 * #, 0, + flags but which do not.
1792 case 'G': /* GID number */
1793 case 'U': /* UID number */
1794 case 'b': /* size in 512-byte blocks */
1795 case 'D': /* Filesystem device on which the file exits */
1796 case 'k': /* size in 1K blocks */
1797 case 'n': /* number of links */
1798 fprintf_stat_needed
= true;
1801 /* Numeric items that DO honour #, 0, + flags.
1803 case 'd': /* depth in search tree (0 = ARGV element) */
1807 case 'm': /* mode as octal number (perms only) */
1809 fprintf_stat_needed
= true;
1814 return (&(*segment
)->next
);
1817 /* handles both exec and ok predicate */
1819 insert_exec_ok (boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
1821 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
1822 int num_paths
; /* Number of args with path replacements. */
1823 int path_pos
; /* Index in array of path replacements. */
1824 int vec_pos
; /* Index in array of args. */
1825 struct predicate
*our_pred
;
1826 struct exec_val
*execp
; /* Pointer for efficiency. */
1828 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1831 /* Count the number of args with path replacements, up until the ';'. */
1833 for (end
= start
, num_paths
= 0;
1835 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
1837 if (strstr (argv
[end
], "{}"))
1839 /* Fail if no command given or no semicolon found. */
1840 if ((end
== start
) || (argv
[end
] == NULL
))
1846 our_pred
= insert_primary (func
);
1847 our_pred
->side_effects
= true;
1848 our_pred
->no_default_print
= true;
1849 execp
= &our_pred
->args
.exec_vec
;
1851 (struct path_arg
*) xmalloc (sizeof (struct path_arg
) * (num_paths
+ 1));
1852 execp
->vec
= (char **) xmalloc (sizeof (char *) * (end
- start
+ 1));
1853 /* Record the positions of all args, and the args with path replacements. */
1854 for (end
= start
, path_pos
= vec_pos
= 0;
1856 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
1861 execp
->paths
[path_pos
].count
= 0;
1862 for (p
= argv
[end
]; *p
; ++p
)
1863 if (p
[0] == '{' && p
[1] == '}')
1865 execp
->paths
[path_pos
].count
++;
1868 if (execp
->paths
[path_pos
].count
)
1870 execp
->paths
[path_pos
].offset
= vec_pos
;
1871 execp
->paths
[path_pos
].origarg
= argv
[end
];
1874 execp
->vec
[vec_pos
++] = argv
[end
];
1876 execp
->paths
[path_pos
].offset
= -1;
1877 execp
->vec
[vec_pos
] = NULL
;
1879 if (argv
[end
] == NULL
)
1886 /* Get a number of days and comparison type.
1887 STR is the ASCII representation.
1888 Set *NUM_DAYS to the number of days, taken as being from
1889 the current moment (or possibly midnight). Thus the sense of the
1890 comparison type appears to be reversed.
1891 Set *COMP_TYPE to the kind of comparison that is requested.
1893 Return true if all okay, false if input error.
1895 Used by -atime, -ctime and -mtime (parsers) to
1896 get the appropriate information for a time predicate processor. */
1899 get_num_days (char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
)
1901 boolean r
= get_num (str
, num_days
, comp_type
);
1905 case COMP_LT
: *comp_type
= COMP_GT
; break;
1906 case COMP_GT
: *comp_type
= COMP_LT
; break;
1912 /* Insert a time predicate PRED.
1913 ARGV is a pointer to the argument array.
1914 ARG_PTR is a pointer to an index into the array, incremented if
1917 Return true if input is valid, false if not.
1919 A new predicate node is assigned, along with an argument node
1920 obtained with malloc.
1922 Used by -atime, -ctime, and -mtime parsers. */
1925 insert_time (char **argv
, int *arg_ptr
, PFB pred
)
1927 struct predicate
*our_pred
;
1929 enum comparison_type c_type
;
1932 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1934 if (!get_num_days (argv
[*arg_ptr
], &num_days
, &c_type
))
1937 /* Figure out the timestamp value we are looking for. */
1938 t
= ( options
.cur_day_start
- num_days
* DAYSECS
1939 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
1943 /* We introduce a scope in which 'val' can be declared, for the
1944 * benefit of compilers that are really C89 compilers
1945 * which support intmax_t because config.h #defines it
1947 intmax_t val
= ( (intmax_t)options
.cur_day_start
- num_days
* DAYSECS
1948 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
1951 /* Check for possibility of an overflow */
1952 if ( (intmax_t)t
!= val
)
1954 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv
[*arg_ptr
]);
1958 our_pred
= insert_primary (pred
);
1959 our_pred
->args
.info
.kind
= c_type
;
1960 our_pred
->args
.info
.negative
= t
< 0;
1961 our_pred
->args
.info
.l_val
= t
;
1964 fprintf (stderr
, _("inserting %s\n"), our_pred
->p_name
);
1965 fprintf (stderr
, _(" type: %s %s "),
1966 (c_type
== COMP_GT
) ? "gt" :
1967 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
1968 (c_type
== COMP_GT
) ? " >" :
1969 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? ">=" : " ?")));
1970 t
= our_pred
->args
.info
.l_val
;
1971 fprintf (stderr
, "%ju %s", (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
1972 if (c_type
== COMP_EQ
)
1974 t
= our_pred
->args
.info
.l_val
+= DAYSECS
;
1977 (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
1978 our_pred
->args
.info
.l_val
-= DAYSECS
;
1984 /* Get a number with comparision information.
1985 The sense of the comparision information is 'normal'; that is,
1986 '+' looks for a count > than the number and '-' less than.
1988 STR is the ASCII representation of the number.
1989 Set *NUM to the number.
1990 Set *COMP_TYPE to the kind of comparison that is requested.
1992 Return true if all okay, false if input error. */
1995 get_num (char *str
, uintmax_t *num
, enum comparison_type
*comp_type
)
2002 *comp_type
= COMP_GT
;
2006 *comp_type
= COMP_LT
;
2010 *comp_type
= COMP_EQ
;
2014 return xstrtoumax (str
, NULL
, 10, num
, "") == LONGINT_OK
;
2017 /* Insert a number predicate.
2018 ARGV is a pointer to the argument array.
2019 *ARG_PTR is an index into ARGV, incremented if all went well.
2020 *PRED is the predicate processor to insert.
2022 Return true if input is valid, false if error.
2024 A new predicate node is assigned, along with an argument node
2025 obtained with malloc.
2027 Used by -inum and -links parsers. */
2030 insert_num (char **argv
, int *arg_ptr
, PFB pred
)
2032 struct predicate
*our_pred
;
2034 enum comparison_type c_type
;
2036 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2038 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
2040 our_pred
= insert_primary (pred
);
2041 our_pred
->args
.info
.kind
= c_type
;
2042 our_pred
->args
.info
.l_val
= num
;
2045 fprintf (stderr
, _("inserting %s\n"), our_pred
->p_name
);
2046 fprintf (stderr
, _(" type: %s %s "),
2047 (c_type
== COMP_GT
) ? "gt" :
2048 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2049 (c_type
== COMP_GT
) ? " >" :
2050 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? " =" : " ?")));
2051 fprintf (stderr
, "%ju\n", our_pred
->args
.info
.l_val
);
2057 open_output_file (char *path
)
2061 if (!strcmp (path
, "/dev/stderr"))
2063 else if (!strcmp (path
, "/dev/stdout"))
2065 f
= fopen (path
, "w");
2067 error (1, errno
, "%s", path
);