1 /* parser.c -- convert the command line args into an expression tree.
2 Copyright (C) 1990, 91, 92, 93, 94, 2000 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
22 #include "modechange.h"
28 # define _(Text) gettext (Text)
33 # define N_(String) gettext_noop (String)
35 # define N_(String) (String)
38 #if !defined (isascii) || defined (STDC_HEADERS)
45 #define ISDIGIT(c) (isascii (c) && isdigit (c))
46 #define ISUPPER(c) (isascii (c) && isupper (c))
55 static boolean parse_amin
PARAMS((char *argv
[], int *arg_ptr
));
56 static boolean parse_and
PARAMS((char *argv
[], int *arg_ptr
));
57 static boolean parse_anewer
PARAMS((char *argv
[], int *arg_ptr
));
58 static boolean parse_atime
PARAMS((char *argv
[], int *arg_ptr
));
59 boolean parse_close
PARAMS((char *argv
[], int *arg_ptr
));
60 static boolean parse_cmin
PARAMS((char *argv
[], int *arg_ptr
));
61 static boolean parse_cnewer
PARAMS((char *argv
[], int *arg_ptr
));
62 static boolean parse_comma
PARAMS((char *argv
[], int *arg_ptr
));
63 static boolean parse_ctime
PARAMS((char *argv
[], int *arg_ptr
));
64 static boolean parse_daystart
PARAMS((char *argv
[], int *arg_ptr
));
65 static boolean parse_depth
PARAMS((char *argv
[], int *arg_ptr
));
66 static boolean parse_empty
PARAMS((char *argv
[], int *arg_ptr
));
67 static boolean parse_exec
PARAMS((char *argv
[], int *arg_ptr
));
68 static boolean parse_false
PARAMS((char *argv
[], int *arg_ptr
));
69 static boolean parse_fls
PARAMS((char *argv
[], int *arg_ptr
));
70 static boolean parse_fprintf
PARAMS((char *argv
[], int *arg_ptr
));
71 static boolean parse_follow
PARAMS((char *argv
[], int *arg_ptr
));
72 static boolean parse_fprint
PARAMS((char *argv
[], int *arg_ptr
));
73 static boolean parse_fprint0
PARAMS((char *argv
[], int *arg_ptr
));
74 static boolean parse_fstype
PARAMS((char *argv
[], int *arg_ptr
));
75 static boolean parse_gid
PARAMS((char *argv
[], int *arg_ptr
));
76 static boolean parse_group
PARAMS((char *argv
[], int *arg_ptr
));
77 static boolean parse_help
PARAMS((char *argv
[], int *arg_ptr
));
78 static boolean parse_ilname
PARAMS((char *argv
[], int *arg_ptr
));
79 static boolean parse_iname
PARAMS((char *argv
[], int *arg_ptr
));
80 static boolean parse_inum
PARAMS((char *argv
[], int *arg_ptr
));
81 static boolean parse_ipath
PARAMS((char *argv
[], int *arg_ptr
));
82 static boolean parse_iregex
PARAMS((char *argv
[], int *arg_ptr
));
83 static boolean parse_links
PARAMS((char *argv
[], int *arg_ptr
));
84 static boolean parse_lname
PARAMS((char *argv
[], int *arg_ptr
));
85 static boolean parse_ls
PARAMS((char *argv
[], int *arg_ptr
));
86 static boolean parse_maxdepth
PARAMS((char *argv
[], int *arg_ptr
));
87 static boolean parse_mindepth
PARAMS((char *argv
[], int *arg_ptr
));
88 static boolean parse_mmin
PARAMS((char *argv
[], int *arg_ptr
));
89 static boolean parse_mtime
PARAMS((char *argv
[], int *arg_ptr
));
90 static boolean parse_name
PARAMS((char *argv
[], int *arg_ptr
));
91 static boolean parse_negate
PARAMS((char *argv
[], int *arg_ptr
));
92 static boolean parse_newer
PARAMS((char *argv
[], int *arg_ptr
));
93 static boolean parse_noleaf
PARAMS((char *argv
[], int *arg_ptr
));
94 static boolean parse_nogroup
PARAMS((char *argv
[], int *arg_ptr
));
95 static boolean parse_nouser
PARAMS((char *argv
[], int *arg_ptr
));
96 static boolean parse_ok
PARAMS((char *argv
[], int *arg_ptr
));
97 boolean parse_open
PARAMS((char *argv
[], int *arg_ptr
));
98 static boolean parse_or
PARAMS((char *argv
[], int *arg_ptr
));
99 static boolean parse_path
PARAMS((char *argv
[], int *arg_ptr
));
100 static boolean parse_perm
PARAMS((char *argv
[], int *arg_ptr
));
101 boolean parse_print
PARAMS((char *argv
[], int *arg_ptr
));
102 static boolean parse_print0
PARAMS((char *argv
[], int *arg_ptr
));
103 static boolean parse_printf
PARAMS((char *argv
[], int *arg_ptr
));
104 static boolean parse_prune
PARAMS((char *argv
[], int *arg_ptr
));
105 static boolean parse_regex
PARAMS((char *argv
[], int *arg_ptr
));
106 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
, boolean ignore_case
));
107 static boolean parse_size
PARAMS((char *argv
[], int *arg_ptr
));
108 static boolean parse_true
PARAMS((char *argv
[], int *arg_ptr
));
109 static boolean parse_type
PARAMS((char *argv
[], int *arg_ptr
));
110 static boolean parse_uid
PARAMS((char *argv
[], int *arg_ptr
));
111 static boolean parse_used
PARAMS((char *argv
[], int *arg_ptr
));
112 static boolean parse_user
PARAMS((char *argv
[], int *arg_ptr
));
113 static boolean parse_version
PARAMS((char *argv
[], int *arg_ptr
));
114 static boolean parse_xdev
PARAMS((char *argv
[], int *arg_ptr
));
115 static boolean parse_xtype
PARAMS((char *argv
[], int *arg_ptr
));
117 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
, boolean ignore_case
));
118 static boolean insert_type
PARAMS((char *argv
[], int *arg_ptr
, boolean (*which_pred
)()));
119 static boolean insert_fprintf
PARAMS((FILE *fp
, boolean (*func
)(), char *argv
[], int *arg_ptr
));
120 static struct segment
**make_segment
PARAMS((struct segment
**segment
, char *format
, int len
, int kind
));
121 static boolean insert_exec_ok
PARAMS((boolean (*func
)(), char *argv
[], int *arg_ptr
));
122 static boolean get_num_days
PARAMS((char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
));
123 static boolean insert_time
PARAMS((char *argv
[], int *arg_ptr
, PFB pred
));
124 static boolean get_num
PARAMS((char *str
, uintmax_t *num
, enum comparison_type
*comp_type
));
125 static boolean insert_num
PARAMS((char *argv
[], int *arg_ptr
, PFB pred
));
126 static FILE *open_output_file
PARAMS((char *path
));
129 char *find_pred_name
PARAMS((PFB pred_func
));
138 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
139 If they are in some Unix versions of find, they are marked `Unix'. */
141 static struct parser_table
const parse_table
[] =
144 {"not", parse_negate
}, /* GNU */
147 {",", parse_comma
}, /* GNU */
149 {"amin", parse_amin
}, /* GNU */
150 {"and", parse_and
}, /* GNU */
151 {"anewer", parse_anewer
}, /* GNU */
152 {"atime", parse_atime
},
153 {"cmin", parse_cmin
}, /* GNU */
154 {"cnewer", parse_cnewer
}, /* GNU */
155 #ifdef UNIMPLEMENTED_UNIX
156 /* It's pretty ugly for find to know about archive formats.
157 Plus what it could do with cpio archives is very limited.
158 Better to leave it out. */
159 {"cpio", parse_cpio
}, /* Unix */
161 {"ctime", parse_ctime
},
162 {"daystart", parse_daystart
}, /* GNU */
163 {"depth", parse_depth
},
164 {"empty", parse_empty
}, /* GNU */
165 {"exec", parse_exec
},
166 {"false", parse_false
}, /* GNU */
167 {"fls", parse_fls
}, /* GNU */
168 {"follow", parse_follow
}, /* GNU, Unix */
169 {"fprint", parse_fprint
}, /* GNU */
170 {"fprint0", parse_fprint0
}, /* GNU */
171 {"fprintf", parse_fprintf
}, /* GNU */
172 {"fstype", parse_fstype
}, /* GNU, Unix */
173 {"gid", parse_gid
}, /* GNU */
174 {"group", parse_group
},
175 {"help", parse_help
}, /* GNU */
176 {"-help", parse_help
}, /* GNU */
177 {"ilname", parse_ilname
}, /* GNU */
178 {"iname", parse_iname
}, /* GNU */
179 {"inum", parse_inum
}, /* GNU, Unix */
180 {"ipath", parse_ipath
}, /* GNU */
181 {"iregex", parse_iregex
}, /* GNU */
182 {"links", parse_links
},
183 {"lname", parse_lname
}, /* GNU */
184 {"ls", parse_ls
}, /* GNU, Unix */
185 {"maxdepth", parse_maxdepth
}, /* GNU */
186 {"mindepth", parse_mindepth
}, /* GNU */
187 {"mmin", parse_mmin
}, /* GNU */
188 {"mount", parse_xdev
}, /* Unix */
189 {"mtime", parse_mtime
},
190 {"name", parse_name
},
191 #ifdef UNIMPLEMENTED_UNIX
192 {"ncpio", parse_ncpio
}, /* Unix */
194 {"newer", parse_newer
},
195 {"noleaf", parse_noleaf
}, /* GNU */
196 {"nogroup", parse_nogroup
},
197 {"nouser", parse_nouser
},
199 {"or", parse_or
}, /* GNU */
201 {"path", parse_path
}, /* GNU, HP-UX */
202 {"perm", parse_perm
},
203 {"print", parse_print
},
204 {"print0", parse_print0
}, /* GNU */
205 {"printf", parse_printf
}, /* GNU */
206 {"prune", parse_prune
},
207 {"regex", parse_regex
}, /* GNU */
208 {"size", parse_size
},
209 {"true", parse_true
}, /* GNU */
210 {"type", parse_type
},
211 {"uid", parse_uid
}, /* GNU */
212 {"used", parse_used
}, /* GNU */
213 {"user", parse_user
},
214 {"version", parse_version
}, /* GNU */
215 {"-version", parse_version
}, /* GNU */
216 {"xdev", parse_xdev
},
217 {"xtype", parse_xtype
}, /* GNU */
221 /* Return a pointer to the parser function to invoke for predicate
223 Return NULL if SEARCH_NAME is not a valid predicate name. */
226 find_parser (char *search_name
)
230 if (*search_name
== '-')
232 for (i
= 0; parse_table
[i
].parser_name
!= 0; i
++)
233 if (strcmp (parse_table
[i
].parser_name
, search_name
) == 0)
234 return (parse_table
[i
].parser_func
);
238 /* The parsers are responsible to continue scanning ARGV for
239 their arguments. Each parser knows what is and isn't
242 ARGV is the argument array.
243 *ARG_PTR is the index to start at in ARGV,
244 updated to point beyond the last element consumed.
246 The predicate structure is updated with the new information. */
249 parse_amin (char **argv
, int *arg_ptr
)
251 struct predicate
*our_pred
;
253 enum comparison_type c_type
;
256 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
258 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
260 t
= cur_day_start
+ DAYSECS
- num
* 60;
261 our_pred
= insert_primary (pred_amin
);
262 our_pred
->args
.info
.kind
= c_type
;
263 our_pred
->args
.info
.negative
= t
< 0;
264 our_pred
->args
.info
.l_val
= t
;
270 parse_and (char **argv
, int *arg_ptr
)
272 struct predicate
*our_pred
;
274 our_pred
= get_new_pred ();
275 our_pred
->pred_func
= pred_and
;
277 our_pred
->p_name
= find_pred_name (pred_and
);
279 our_pred
->p_type
= BI_OP
;
280 our_pred
->p_prec
= AND_PREC
;
281 our_pred
->need_stat
= false;
286 parse_anewer (char **argv
, int *arg_ptr
)
288 struct predicate
*our_pred
;
289 struct stat stat_newer
;
291 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
293 if ((*xstat
) (argv
[*arg_ptr
], &stat_newer
))
294 error (1, errno
, "%s", argv
[*arg_ptr
]);
295 our_pred
= insert_primary (pred_anewer
);
296 our_pred
->args
.time
= stat_newer
.st_mtime
;
302 parse_atime (char **argv
, int *arg_ptr
)
304 return (insert_time (argv
, arg_ptr
, pred_atime
));
308 parse_close (char **argv
, int *arg_ptr
)
310 struct predicate
*our_pred
;
312 our_pred
= get_new_pred ();
313 our_pred
->pred_func
= pred_close
;
315 our_pred
->p_name
= find_pred_name (pred_close
);
317 our_pred
->p_type
= CLOSE_PAREN
;
318 our_pred
->p_prec
= NO_PREC
;
319 our_pred
->need_stat
= false;
324 parse_cmin (char **argv
, int *arg_ptr
)
326 struct predicate
*our_pred
;
328 enum comparison_type c_type
;
331 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
333 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
335 t
= cur_day_start
+ DAYSECS
- num
* 60;
336 our_pred
= insert_primary (pred_cmin
);
337 our_pred
->args
.info
.kind
= c_type
;
338 our_pred
->args
.info
.negative
= t
< 0;
339 our_pred
->args
.info
.l_val
= t
;
345 parse_cnewer (char **argv
, int *arg_ptr
)
347 struct predicate
*our_pred
;
348 struct stat stat_newer
;
350 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
352 if ((*xstat
) (argv
[*arg_ptr
], &stat_newer
))
353 error (1, errno
, "%s", argv
[*arg_ptr
]);
354 our_pred
= insert_primary (pred_cnewer
);
355 our_pred
->args
.time
= stat_newer
.st_mtime
;
361 parse_comma (char **argv
, int *arg_ptr
)
363 struct predicate
*our_pred
;
365 our_pred
= get_new_pred ();
366 our_pred
->pred_func
= pred_comma
;
368 our_pred
->p_name
= find_pred_name (pred_comma
);
370 our_pred
->p_type
= BI_OP
;
371 our_pred
->p_prec
= COMMA_PREC
;
372 our_pred
->need_stat
= false;
377 parse_ctime (char **argv
, int *arg_ptr
)
379 return (insert_time (argv
, arg_ptr
, pred_ctime
));
383 parse_daystart (char **argv
, int *arg_ptr
)
387 if (full_days
== false)
389 cur_day_start
+= DAYSECS
;
390 local
= localtime (&cur_day_start
);
391 cur_day_start
-= (local
392 ? (local
->tm_sec
+ local
->tm_min
* 60
393 + local
->tm_hour
* 3600)
394 : cur_day_start
% DAYSECS
);
401 parse_depth (char **argv
, int *arg_ptr
)
403 do_dir_first
= false;
408 parse_empty (char **argv
, int *arg_ptr
)
410 insert_primary (pred_empty
);
415 parse_exec (char **argv
, int *arg_ptr
)
417 return (insert_exec_ok (pred_exec
, argv
, arg_ptr
));
421 parse_false (char **argv
, int *arg_ptr
)
423 struct predicate
*our_pred
;
425 our_pred
= insert_primary (pred_false
);
426 our_pred
->need_stat
= false;
431 parse_fls (char **argv
, int *arg_ptr
)
433 struct predicate
*our_pred
;
435 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
437 our_pred
= insert_primary (pred_fls
);
438 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
439 our_pred
->side_effects
= true;
445 parse_fprintf (char **argv
, int *arg_ptr
)
449 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
451 if (argv
[*arg_ptr
+ 1] == NULL
)
453 /* Ensure we get "missing arg" message, not "invalid arg". */
457 fp
= open_output_file (argv
[*arg_ptr
]);
459 return (insert_fprintf (fp
, pred_fprintf
, argv
, arg_ptr
));
463 parse_follow (char **argv
, int *arg_ptr
)
467 no_leaf_check
= true;
472 parse_fprint (char **argv
, int *arg_ptr
)
474 struct predicate
*our_pred
;
476 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
478 our_pred
= insert_primary (pred_fprint
);
479 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
480 our_pred
->side_effects
= true;
481 our_pred
->need_stat
= false;
487 parse_fprint0 (char **argv
, int *arg_ptr
)
489 struct predicate
*our_pred
;
491 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
493 our_pred
= insert_primary (pred_fprint0
);
494 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
495 our_pred
->side_effects
= true;
496 our_pred
->need_stat
= false;
502 parse_fstype (char **argv
, int *arg_ptr
)
504 struct predicate
*our_pred
;
506 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
508 our_pred
= insert_primary (pred_fstype
);
509 our_pred
->args
.str
= argv
[*arg_ptr
];
515 parse_gid (char **argv
, int *arg_ptr
)
517 return (insert_num (argv
, arg_ptr
, pred_gid
));
521 parse_group (char **argv
, int *arg_ptr
)
523 struct group
*cur_gr
;
524 struct predicate
*our_pred
;
528 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
530 cur_gr
= getgrnam (argv
[*arg_ptr
]);
533 gid
= cur_gr
->gr_gid
;
536 gid_len
= strspn (argv
[*arg_ptr
], "0123456789");
537 if ((gid_len
== 0) || (argv
[*arg_ptr
][gid_len
] != '\0'))
539 gid
= atoi (argv
[*arg_ptr
]);
541 our_pred
= insert_primary (pred_group
);
542 our_pred
->args
.gid
= gid
;
548 parse_help (char **argv
, int *arg_ptr
)
551 Usage: %s [path...] [expression]\n"), program_name
);
553 default path is the current directory; default expression is -print\n\
554 expression may consist of:\n\
555 operators (decreasing precedence; -and is implicit where no others are given):\n\
556 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n"));
558 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n\
559 options (always true): -daystart -depth -follow --help\n\
560 -maxdepth LEVELS -mindepth LEVELS -mount -noleaf --version -xdev\n\
561 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n"));
563 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
564 -ilname PATTERN -iname PATTERN -inum N -ipath PATTERN -iregex PATTERN\n\
565 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE\n"));
567 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
568 -size N[bckw] -true -type [bcdpfls] -uid N -used N -user NAME\n\
569 -xtype [bcdpfls]\n"));
571 actions: -exec COMMAND ; -fprint FILE -fprint0 FILE -fprintf FILE FORMAT\n\
572 -ok COMMAND ; -print -print0 -printf FORMAT -prune -ls\n"));
577 parse_ilname (char **argv
, int *arg_ptr
)
579 struct predicate
*our_pred
;
581 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
583 our_pred
= insert_primary (pred_ilname
);
584 our_pred
->args
.str
= argv
[*arg_ptr
];
590 parse_iname (char **argv
, int *arg_ptr
)
592 struct predicate
*our_pred
;
594 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
596 our_pred
= insert_primary (pred_iname
);
597 our_pred
->need_stat
= false;
598 our_pred
->args
.str
= argv
[*arg_ptr
];
604 parse_inum (char **argv
, int *arg_ptr
)
606 return (insert_num (argv
, arg_ptr
, pred_inum
));
610 parse_ipath (char **argv
, int *arg_ptr
)
612 struct predicate
*our_pred
;
614 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
616 our_pred
= insert_primary (pred_ipath
);
617 our_pred
->need_stat
= false;
618 our_pred
->args
.str
= argv
[*arg_ptr
];
624 parse_iregex (char **argv
, int *arg_ptr
)
626 return insert_regex (argv
, arg_ptr
, true);
630 parse_links (char **argv
, int *arg_ptr
)
632 return (insert_num (argv
, arg_ptr
, pred_links
));
636 parse_lname (char **argv
, int *arg_ptr
)
638 struct predicate
*our_pred
;
640 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
642 our_pred
= insert_primary (pred_lname
);
643 our_pred
->args
.str
= argv
[*arg_ptr
];
649 parse_ls (char **argv
, int *arg_ptr
)
651 struct predicate
*our_pred
;
653 our_pred
= insert_primary (pred_ls
);
654 our_pred
->side_effects
= true;
659 parse_maxdepth (char **argv
, int *arg_ptr
)
663 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
665 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
666 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
668 maxdepth
= atoi (argv
[*arg_ptr
]);
676 parse_mindepth (char **argv
, int *arg_ptr
)
680 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
682 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
683 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
685 mindepth
= atoi (argv
[*arg_ptr
]);
693 parse_mmin (char **argv
, int *arg_ptr
)
695 struct predicate
*our_pred
;
697 enum comparison_type c_type
;
700 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
702 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
704 t
= cur_day_start
+ DAYSECS
- num
* 60;
705 our_pred
= insert_primary (pred_mmin
);
706 our_pred
->args
.info
.kind
= c_type
;
707 our_pred
->args
.info
.negative
= t
< 0;
708 our_pred
->args
.info
.l_val
= t
;
714 parse_mtime (char **argv
, int *arg_ptr
)
716 return (insert_time (argv
, arg_ptr
, pred_mtime
));
720 parse_name (char **argv
, int *arg_ptr
)
722 struct predicate
*our_pred
;
724 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
726 our_pred
= insert_primary (pred_name
);
727 our_pred
->need_stat
= false;
728 our_pred
->args
.str
= argv
[*arg_ptr
];
734 parse_negate (char **argv
, int *arg_ptr
)
736 struct predicate
*our_pred
;
738 our_pred
= get_new_pred_chk_op ();
739 our_pred
->pred_func
= pred_negate
;
741 our_pred
->p_name
= find_pred_name (pred_negate
);
743 our_pred
->p_type
= UNI_OP
;
744 our_pred
->p_prec
= NEGATE_PREC
;
745 our_pred
->need_stat
= false;
750 parse_newer (char **argv
, int *arg_ptr
)
752 struct predicate
*our_pred
;
753 struct stat stat_newer
;
755 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
757 if ((*xstat
) (argv
[*arg_ptr
], &stat_newer
))
758 error (1, errno
, "%s", argv
[*arg_ptr
]);
759 our_pred
= insert_primary (pred_newer
);
760 our_pred
->args
.time
= stat_newer
.st_mtime
;
766 parse_noleaf (char **argv
, int *arg_ptr
)
768 no_leaf_check
= true;
773 /* Arbitrary amount by which to increase size
774 of `uid_unused' and `gid_unused'. */
775 #define ALLOC_STEP 2048
777 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
778 char *uid_unused
= NULL
;
780 /* Number of elements in `uid_unused'. */
781 unsigned uid_allocated
;
783 /* Similar for GIDs and group entries. */
784 char *gid_unused
= NULL
;
785 unsigned gid_allocated
;
789 parse_nogroup (char **argv
, int *arg_ptr
)
791 struct predicate
*our_pred
;
793 our_pred
= insert_primary (pred_nogroup
);
795 if (gid_unused
== NULL
)
799 gid_allocated
= ALLOC_STEP
;
800 gid_unused
= xmalloc (gid_allocated
);
801 memset (gid_unused
, 1, gid_allocated
);
803 while ((gr
= getgrent ()) != NULL
)
805 if ((unsigned) gr
->gr_gid
>= gid_allocated
)
807 unsigned new_allocated
= (unsigned) gr
->gr_gid
+ ALLOC_STEP
;
808 gid_unused
= xrealloc (gid_unused
, new_allocated
);
809 memset (gid_unused
+ gid_allocated
, 1,
810 new_allocated
- gid_allocated
);
811 gid_allocated
= new_allocated
;
813 gid_unused
[(unsigned) gr
->gr_gid
] = 0;
822 parse_nouser (char **argv
, int *arg_ptr
)
824 struct predicate
*our_pred
;
826 our_pred
= insert_primary (pred_nouser
);
828 if (uid_unused
== NULL
)
832 uid_allocated
= ALLOC_STEP
;
833 uid_unused
= xmalloc (uid_allocated
);
834 memset (uid_unused
, 1, uid_allocated
);
836 while ((pw
= getpwent ()) != NULL
)
838 if ((unsigned) pw
->pw_uid
>= uid_allocated
)
840 unsigned new_allocated
= (unsigned) pw
->pw_uid
+ ALLOC_STEP
;
841 uid_unused
= xrealloc (uid_unused
, new_allocated
);
842 memset (uid_unused
+ uid_allocated
, 1,
843 new_allocated
- uid_allocated
);
844 uid_allocated
= new_allocated
;
846 uid_unused
[(unsigned) pw
->pw_uid
] = 0;
855 parse_ok (char **argv
, int *arg_ptr
)
857 return (insert_exec_ok (pred_ok
, argv
, arg_ptr
));
861 parse_open (char **argv
, int *arg_ptr
)
863 struct predicate
*our_pred
;
865 our_pred
= get_new_pred_chk_op ();
866 our_pred
->pred_func
= pred_open
;
868 our_pred
->p_name
= find_pred_name (pred_open
);
870 our_pred
->p_type
= OPEN_PAREN
;
871 our_pred
->p_prec
= NO_PREC
;
872 our_pred
->need_stat
= false;
877 parse_or (char **argv
, int *arg_ptr
)
879 struct predicate
*our_pred
;
881 our_pred
= get_new_pred ();
882 our_pred
->pred_func
= pred_or
;
884 our_pred
->p_name
= find_pred_name (pred_or
);
886 our_pred
->p_type
= BI_OP
;
887 our_pred
->p_prec
= OR_PREC
;
888 our_pred
->need_stat
= false;
893 parse_path (char **argv
, int *arg_ptr
)
895 struct predicate
*our_pred
;
897 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
899 our_pred
= insert_primary (pred_path
);
900 our_pred
->need_stat
= false;
901 our_pred
->args
.str
= argv
[*arg_ptr
];
907 parse_perm (char **argv
, int *arg_ptr
)
911 struct mode_change
*change
;
912 struct predicate
*our_pred
;
914 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
917 switch (argv
[*arg_ptr
][0])
928 change
= mode_compile (argv
[*arg_ptr
] + mode_start
, MODE_MASK_PLUS
);
929 if (change
== MODE_INVALID
)
930 error (1, 0, _("invalid mode `%s'"), argv
[*arg_ptr
]);
931 else if (change
== MODE_MEMORY_EXHAUSTED
)
932 error (1, 0, _("virtual memory exhausted"));
933 perm_val
= mode_adjust (0, change
);
936 our_pred
= insert_primary (pred_perm
);
938 switch (argv
[*arg_ptr
][0])
941 our_pred
->args
.perm
.kind
= PERM_AT_LEAST
;
944 our_pred
->args
.perm
.kind
= PERM_ANY
;
947 our_pred
->args
.perm
.kind
= PERM_EXACT
;
950 our_pred
->args
.perm
.val
= perm_val
& MODE_ALL
;
956 parse_print (char **argv
, int *arg_ptr
)
958 struct predicate
*our_pred
;
960 our_pred
= insert_primary (pred_print
);
961 /* -print has the side effect of printing. This prevents us
962 from doing undesired multiple printing when the user has
963 already specified -print. */
964 our_pred
->side_effects
= true;
965 our_pred
->need_stat
= false;
970 parse_print0 (char **argv
, int *arg_ptr
)
972 struct predicate
*our_pred
;
974 our_pred
= insert_primary (pred_print0
);
975 /* -print0 has the side effect of printing. This prevents us
976 from doing undesired multiple printing when the user has
977 already specified -print0. */
978 our_pred
->side_effects
= true;
979 our_pred
->need_stat
= false;
984 parse_printf (char **argv
, int *arg_ptr
)
986 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
988 return (insert_fprintf (stdout
, pred_fprintf
, argv
, arg_ptr
));
992 parse_prune (char **argv
, int *arg_ptr
)
994 struct predicate
*our_pred
;
996 our_pred
= insert_primary (pred_prune
);
997 our_pred
->need_stat
= false;
998 /* -prune has a side effect that it does not descend into
999 the current directory. */
1000 our_pred
->side_effects
= true;
1005 parse_regex (char **argv
, int *arg_ptr
)
1007 return insert_regex (argv
, arg_ptr
, false);
1011 insert_regex (char **argv
, int *arg_ptr
, boolean ignore_case
)
1013 struct predicate
*our_pred
;
1014 struct re_pattern_buffer
*re
;
1015 const char *error_message
;
1017 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1019 our_pred
= insert_primary (pred_regex
);
1020 our_pred
->need_stat
= false;
1021 re
= (struct re_pattern_buffer
*)
1022 xmalloc (sizeof (struct re_pattern_buffer
));
1023 our_pred
->args
.regex
= re
;
1024 re
->allocated
= 100;
1025 re
->buffer
= (unsigned char *) xmalloc (re
->allocated
);
1032 re
->translate
= xmalloc (256);
1033 /* Map uppercase characters to corresponding lowercase ones. */
1034 for (i
= 0; i
< 256; i
++)
1035 re
->translate
[i
] = ISUPPER (i
) ? tolower (i
) : i
;
1038 re
->translate
= NULL
;
1040 error_message
= re_compile_pattern (argv
[*arg_ptr
], strlen (argv
[*arg_ptr
]),
1043 error (1, 0, "%s", error_message
);
1049 parse_size (char **argv
, int *arg_ptr
)
1051 struct predicate
*our_pred
;
1053 enum comparison_type c_type
;
1057 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1059 len
= strlen (argv
[*arg_ptr
]);
1061 error (1, 0, _("invalid null argument to -size"));
1062 switch (argv
[*arg_ptr
][len
- 1])
1066 argv
[*arg_ptr
][len
- 1] = '\0';
1071 argv
[*arg_ptr
][len
- 1] = '\0';
1076 argv
[*arg_ptr
][len
- 1] = '\0';
1081 argv
[*arg_ptr
][len
- 1] = '\0';
1097 error (1, 0, _("invalid -size type `%c'"), argv
[*arg_ptr
][len
- 1]);
1099 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
1101 our_pred
= insert_primary (pred_size
);
1102 our_pred
->args
.size
.kind
= c_type
;
1103 our_pred
->args
.size
.blocksize
= blksize
;
1104 our_pred
->args
.size
.size
= num
;
1110 parse_true (char **argv
, int *arg_ptr
)
1112 struct predicate
*our_pred
;
1114 our_pred
= insert_primary (pred_true
);
1115 our_pred
->need_stat
= false;
1120 parse_type (char **argv
, int *arg_ptr
)
1122 return insert_type (argv
, arg_ptr
, pred_type
);
1126 parse_uid (char **argv
, int *arg_ptr
)
1128 return (insert_num (argv
, arg_ptr
, pred_uid
));
1132 parse_used (char **argv
, int *arg_ptr
)
1134 struct predicate
*our_pred
;
1136 enum comparison_type c_type
;
1139 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1141 if (!get_num (argv
[*arg_ptr
], &num_days
, &c_type
))
1143 t
= num_days
* DAYSECS
;
1144 our_pred
= insert_primary (pred_used
);
1145 our_pred
->args
.info
.kind
= c_type
;
1146 our_pred
->args
.info
.negative
= t
< 0;
1147 our_pred
->args
.info
.l_val
= t
;
1153 parse_user (char **argv
, int *arg_ptr
)
1155 struct passwd
*cur_pwd
;
1156 struct predicate
*our_pred
;
1160 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1162 cur_pwd
= getpwnam (argv
[*arg_ptr
]);
1164 if (cur_pwd
!= NULL
)
1165 uid
= cur_pwd
->pw_uid
;
1168 uid_len
= strspn (argv
[*arg_ptr
], "0123456789");
1169 if ((uid_len
== 0) || (argv
[*arg_ptr
][uid_len
] != '\0'))
1171 uid
= atoi (argv
[*arg_ptr
]);
1173 our_pred
= insert_primary (pred_user
);
1174 our_pred
->args
.uid
= uid
;
1180 parse_version (char **argv
, int *arg_ptr
)
1182 extern char *version_string
;
1185 printf (_("GNU find version %s\n"), version_string
);
1190 parse_xdev (char **argv
, int *arg_ptr
)
1192 stay_on_filesystem
= true;
1197 parse_xtype (char **argv
, int *arg_ptr
)
1199 return insert_type (argv
, arg_ptr
, pred_xtype
);
1203 insert_type (char **argv
, int *arg_ptr
, boolean (*which_pred
) (/* ??? */))
1206 struct predicate
*our_pred
;
1208 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
)
1209 || (strlen (argv
[*arg_ptr
]) != 1))
1211 switch (argv
[*arg_ptr
][0])
1213 case 'b': /* block special */
1214 type_cell
= S_IFBLK
;
1216 case 'c': /* character special */
1217 type_cell
= S_IFCHR
;
1219 case 'd': /* directory */
1220 type_cell
= S_IFDIR
;
1222 case 'f': /* regular file */
1223 type_cell
= S_IFREG
;
1226 case 'l': /* symbolic link */
1227 type_cell
= S_IFLNK
;
1231 case 'p': /* pipe */
1232 type_cell
= S_IFIFO
;
1236 case 's': /* socket */
1237 type_cell
= S_IFSOCK
;
1240 default: /* None of the above ... nuke 'em. */
1243 our_pred
= insert_primary (which_pred
);
1244 our_pred
->args
.type
= type_cell
;
1245 (*arg_ptr
)++; /* Move on to next argument. */
1249 /* If true, we've determined that the current fprintf predicate
1250 uses stat information. */
1251 static boolean fprintf_stat_needed
;
1254 insert_fprintf (FILE *fp
, boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
1256 char *format
; /* Beginning of unprocessed format string. */
1257 register char *scan
; /* Current address in scanning `format'. */
1258 register char *scan2
; /* Address inside of element being scanned. */
1259 struct segment
**segmentp
; /* Address of current segment. */
1260 struct predicate
*our_pred
;
1262 format
= argv
[(*arg_ptr
)++];
1264 fprintf_stat_needed
= false; /* Might be overridden later. */
1265 our_pred
= insert_primary (func
);
1266 our_pred
->side_effects
= true;
1267 our_pred
->args
.printf_vec
.stream
= fp
;
1268 segmentp
= &our_pred
->args
.printf_vec
.segment
;
1271 for (scan
= format
; *scan
; scan
++)
1276 if (*scan2
>= '0' && *scan2
<= '7')
1280 for (i
= n
= 0; i
< 3 && (*scan2
>= '0' && *scan2
<= '7');
1282 n
= 8 * n
+ *scan2
- '0';
1297 make_segment (segmentp
, format
, scan
- format
, KIND_STOP
);
1298 our_pred
->need_stat
= fprintf_stat_needed
;
1316 /* *scan = '\\'; * it already is */
1320 _("warning: unrecognized escape `\\%c'"), *scan2
);
1325 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1327 format
= scan2
+ 1; /* Move past the escape. */
1328 scan
= scan2
; /* Incremented immediately by `for'. */
1330 else if (*scan
== '%')
1334 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1340 /* Scan past flags, width and precision, to verify kind. */
1341 for (scan2
= scan
; *++scan2
&& strchr ("-+ #", *scan2
);)
1343 while (ISDIGIT (*scan2
))
1346 for (scan2
++; ISDIGIT (*scan2
); scan2
++)
1348 if (strchr ("abcdfFgGhHiklmnpPstuU", *scan2
))
1350 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1355 else if (strchr ("ACT", *scan2
) && scan2
[1])
1357 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1358 *scan2
| (scan2
[1] << 8));
1365 /* An unrecognized % escape. Print the char after the %. */
1366 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1368 segmentp
= make_segment (segmentp
, format
, scan
- format
,
1377 make_segment (segmentp
, format
, scan
- format
, KIND_PLAIN
);
1378 our_pred
->need_stat
= fprintf_stat_needed
;
1382 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1383 from the text in FORMAT, which has length LEN.
1384 Return the address of the `next' pointer of the new segment. */
1386 static struct segment
**
1387 make_segment (struct segment
**segment
, char *format
, int len
, int kind
)
1391 *segment
= (struct segment
*) xmalloc (sizeof (struct segment
));
1393 (*segment
)->kind
= kind
;
1394 (*segment
)->next
= NULL
;
1395 (*segment
)->text_len
= len
;
1397 fmt
= (*segment
)->text
= xmalloc (len
+ sizeof "d");
1398 strncpy (fmt
, format
, len
);
1401 switch (kind
& 0xff)
1403 case KIND_PLAIN
: /* Plain text string, no % conversion. */
1404 case KIND_STOP
: /* Terminate argument, no newline. */
1407 case 'a': /* atime in `ctime' format */
1408 case 'A': /* atime in user-specified strftime format */
1409 case 'b': /* size in 512-byte blocks */
1410 case 'c': /* ctime in `ctime' format */
1411 case 'C': /* ctime in user-specified strftime format */
1412 case 'F': /* filesystem type */
1413 case 'G': /* GID number */
1414 case 'g': /* group name */
1415 case 'i': /* inode number */
1416 case 'k': /* size in 1K blocks */
1417 case 'l': /* object of symlink */
1418 case 'n': /* number of links */
1419 case 's': /* size in bytes */
1420 case 't': /* mtime in `ctime' format */
1421 case 'T': /* mtime in user-specified strftime format */
1422 case 'U': /* UID number */
1423 case 'u': /* user name */
1424 fprintf_stat_needed
= true;
1426 case 'f': /* basename of path */
1427 case 'h': /* leading directories part of path */
1428 case 'H': /* ARGV element file was found under */
1429 case 'p': /* pathname */
1430 case 'P': /* pathname with ARGV element stripped */
1434 case 'd': /* depth in search tree (0 = ARGV element) */
1438 case 'm': /* mode as octal number (perms only) */
1440 fprintf_stat_needed
= true;
1445 return (&(*segment
)->next
);
1449 insert_exec_ok (boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
1451 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
1452 int num_paths
; /* Number of args with path replacements. */
1453 int path_pos
; /* Index in array of path replacements. */
1454 int vec_pos
; /* Index in array of args. */
1455 struct predicate
*our_pred
;
1456 struct exec_val
*execp
; /* Pointer for efficiency. */
1458 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1461 /* Count the number of args with path replacements, up until the ';'. */
1463 for (end
= start
, num_paths
= 0;
1465 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
1467 if (strstr (argv
[end
], "{}"))
1469 /* Fail if no command given or no semicolon found. */
1470 if ((end
== start
) || (argv
[end
] == NULL
))
1476 our_pred
= insert_primary (func
);
1477 our_pred
->side_effects
= true;
1478 execp
= &our_pred
->args
.exec_vec
;
1480 (struct path_arg
*) xmalloc (sizeof (struct path_arg
) * (num_paths
+ 1));
1481 execp
->vec
= (char **) xmalloc (sizeof (char *) * (end
- start
+ 1));
1482 /* Record the positions of all args, and the args with path replacements. */
1483 for (end
= start
, path_pos
= vec_pos
= 0;
1485 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
1490 execp
->paths
[path_pos
].count
= 0;
1491 for (p
= argv
[end
]; *p
; ++p
)
1492 if (p
[0] == '{' && p
[1] == '}')
1494 execp
->paths
[path_pos
].count
++;
1497 if (execp
->paths
[path_pos
].count
)
1499 execp
->paths
[path_pos
].offset
= vec_pos
;
1500 execp
->paths
[path_pos
].origarg
= argv
[end
];
1503 execp
->vec
[vec_pos
++] = argv
[end
];
1505 execp
->paths
[path_pos
].offset
= -1;
1506 execp
->vec
[vec_pos
] = NULL
;
1508 if (argv
[end
] == NULL
)
1515 /* Get a number of days and comparison type.
1516 STR is the ASCII representation.
1517 Set *NUM_DAYS to the number of days, taken as being from
1518 the current moment (or possibly midnight). Thus the sense of the
1519 comparison type appears to be reversed.
1520 Set *COMP_TYPE to the kind of comparison that is requested.
1522 Return true if all okay, false if input error.
1524 Used by -atime, -ctime and -mtime (parsers) to
1525 get the appropriate information for a time predicate processor. */
1528 get_num_days (char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
)
1530 boolean r
= get_num (str
, num_days
, comp_type
);
1534 case COMP_LT
: *comp_type
= COMP_GT
; break;
1535 case COMP_GT
: *comp_type
= COMP_LT
; break;
1541 /* Insert a time predicate PRED.
1542 ARGV is a pointer to the argument array.
1543 ARG_PTR is a pointer to an index into the array, incremented if
1546 Return true if input is valid, false if not.
1548 A new predicate node is assigned, along with an argument node
1549 obtained with malloc.
1551 Used by -atime, -ctime, and -mtime parsers. */
1554 insert_time (char **argv
, int *arg_ptr
, PFB pred
)
1556 struct predicate
*our_pred
;
1558 enum comparison_type c_type
;
1561 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1563 if (!get_num_days (argv
[*arg_ptr
], &num_days
, &c_type
))
1565 t
= (cur_day_start
- num_days
* DAYSECS
1566 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
1567 our_pred
= insert_primary (pred
);
1568 our_pred
->args
.info
.kind
= c_type
;
1569 our_pred
->args
.info
.negative
= t
< 0;
1570 our_pred
->args
.info
.l_val
= t
;
1573 printf (_("inserting %s\n"), our_pred
->p_name
);
1574 printf (_(" type: %s %s "),
1575 (c_type
== COMP_GT
) ? "gt" :
1576 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
1577 (c_type
== COMP_GT
) ? " >" :
1578 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? ">=" : " ?")));
1579 t
= our_pred
->args
.info
.l_val
;
1580 printf ("%ju %s", (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
1581 if (c_type
== COMP_EQ
)
1583 t
= our_pred
->args
.info
.l_val
+= DAYSECS
;
1584 printf (" < %ju %s",
1585 (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
1586 our_pred
->args
.info
.l_val
-= DAYSECS
;
1592 /* Get a number with comparision information.
1593 The sense of the comparision information is 'normal'; that is,
1594 '+' looks for a count > than the number and '-' less than.
1596 STR is the ASCII representation of the number.
1597 Set *NUM to the number.
1598 Set *COMP_TYPE to the kind of comparison that is requested.
1600 Return true if all okay, false if input error. */
1603 get_num (char *str
, uintmax_t *num
, enum comparison_type
*comp_type
)
1605 int len_num
; /* Length of field. */
1612 *comp_type
= COMP_GT
;
1616 *comp_type
= COMP_LT
;
1620 *comp_type
= COMP_EQ
;
1624 return xstrtoumax (str
, NULL
, 10, num
, "") == LONGINT_OK
;
1627 /* Insert a number predicate.
1628 ARGV is a pointer to the argument array.
1629 *ARG_PTR is an index into ARGV, incremented if all went well.
1630 *PRED is the predicate processor to insert.
1632 Return true if input is valid, false if error.
1634 A new predicate node is assigned, along with an argument node
1635 obtained with malloc.
1637 Used by -inum and -links parsers. */
1640 insert_num (char **argv
, int *arg_ptr
, PFB pred
)
1642 struct predicate
*our_pred
;
1644 enum comparison_type c_type
;
1646 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1648 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
1650 our_pred
= insert_primary (pred
);
1651 our_pred
->args
.info
.kind
= c_type
;
1652 our_pred
->args
.info
.l_val
= num
;
1655 printf (_("inserting %s\n"), our_pred
->p_name
);
1656 printf (_(" type: %s %s "),
1657 (c_type
== COMP_GT
) ? "gt" :
1658 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
1659 (c_type
== COMP_GT
) ? " >" :
1660 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? " =" : " ?")));
1661 printf ("%ju\n", our_pred
->args
.info
.l_val
);
1667 open_output_file (char *path
)
1671 if (!strcmp (path
, "/dev/stderr"))
1673 else if (!strcmp (path
, "/dev/stdout"))
1675 f
= fopen (path
, "w");
1677 error (1, errno
, "%s", path
);