1 /* parser.c -- convert the command line args into an expression tree.
2 Copyright (C) 1990, 91, 92, 93, 94 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. */
19 #include <sys/types.h>
25 #include "modechange.h"
31 # define _(Text) gettext (Text)
36 # define N_(String) gettext_noop (String)
38 # define N_(String) (String)
41 #if !defined (isascii) || defined (STDC_HEADERS)
48 #define ISDIGIT(c) (isascii (c) && isdigit (c))
49 #define ISUPPER(c) (isascii (c) && isupper (c))
58 static boolean parse_amin
PARAMS((char *argv
[], int *arg_ptr
));
59 static boolean parse_and
PARAMS((char *argv
[], int *arg_ptr
));
60 static boolean parse_anewer
PARAMS((char *argv
[], int *arg_ptr
));
61 static boolean parse_atime
PARAMS((char *argv
[], int *arg_ptr
));
62 boolean parse_close
PARAMS((char *argv
[], int *arg_ptr
));
63 static boolean parse_cmin
PARAMS((char *argv
[], int *arg_ptr
));
64 static boolean parse_cnewer
PARAMS((char *argv
[], int *arg_ptr
));
65 static boolean parse_comma
PARAMS((char *argv
[], int *arg_ptr
));
66 static boolean parse_ctime
PARAMS((char *argv
[], int *arg_ptr
));
67 static boolean parse_daystart
PARAMS((char *argv
[], int *arg_ptr
));
68 static boolean parse_depth
PARAMS((char *argv
[], int *arg_ptr
));
69 static boolean parse_empty
PARAMS((char *argv
[], int *arg_ptr
));
70 static boolean parse_exec
PARAMS((char *argv
[], int *arg_ptr
));
71 static boolean parse_false
PARAMS((char *argv
[], int *arg_ptr
));
72 static boolean parse_fls
PARAMS((char *argv
[], int *arg_ptr
));
73 static boolean parse_fprintf
PARAMS((char *argv
[], int *arg_ptr
));
74 static boolean parse_follow
PARAMS((char *argv
[], int *arg_ptr
));
75 static boolean parse_fprint
PARAMS((char *argv
[], int *arg_ptr
));
76 static boolean parse_fprint0
PARAMS((char *argv
[], int *arg_ptr
));
77 static boolean parse_fstype
PARAMS((char *argv
[], int *arg_ptr
));
78 static boolean parse_gid
PARAMS((char *argv
[], int *arg_ptr
));
79 static boolean parse_group
PARAMS((char *argv
[], int *arg_ptr
));
80 static boolean parse_help
PARAMS((char *argv
[], int *arg_ptr
));
81 static boolean parse_ilname
PARAMS((char *argv
[], int *arg_ptr
));
82 static boolean parse_iname
PARAMS((char *argv
[], int *arg_ptr
));
83 static boolean parse_inum
PARAMS((char *argv
[], int *arg_ptr
));
84 static boolean parse_ipath
PARAMS((char *argv
[], int *arg_ptr
));
85 static boolean parse_iregex
PARAMS((char *argv
[], int *arg_ptr
));
86 static boolean parse_links
PARAMS((char *argv
[], int *arg_ptr
));
87 static boolean parse_lname
PARAMS((char *argv
[], int *arg_ptr
));
88 static boolean parse_ls
PARAMS((char *argv
[], int *arg_ptr
));
89 static boolean parse_maxdepth
PARAMS((char *argv
[], int *arg_ptr
));
90 static boolean parse_mindepth
PARAMS((char *argv
[], int *arg_ptr
));
91 static boolean parse_mmin
PARAMS((char *argv
[], int *arg_ptr
));
92 static boolean parse_mtime
PARAMS((char *argv
[], int *arg_ptr
));
93 static boolean parse_name
PARAMS((char *argv
[], int *arg_ptr
));
94 static boolean parse_negate
PARAMS((char *argv
[], int *arg_ptr
));
95 static boolean parse_newer
PARAMS((char *argv
[], int *arg_ptr
));
96 static boolean parse_noleaf
PARAMS((char *argv
[], int *arg_ptr
));
97 static boolean parse_nogroup
PARAMS((char *argv
[], int *arg_ptr
));
98 static boolean parse_nouser
PARAMS((char *argv
[], int *arg_ptr
));
99 static boolean parse_ok
PARAMS((char *argv
[], int *arg_ptr
));
100 boolean parse_open
PARAMS((char *argv
[], int *arg_ptr
));
101 static boolean parse_or
PARAMS((char *argv
[], int *arg_ptr
));
102 static boolean parse_path
PARAMS((char *argv
[], int *arg_ptr
));
103 static boolean parse_perm
PARAMS((char *argv
[], int *arg_ptr
));
104 boolean parse_print
PARAMS((char *argv
[], int *arg_ptr
));
105 static boolean parse_print0
PARAMS((char *argv
[], int *arg_ptr
));
106 static boolean parse_printf
PARAMS((char *argv
[], int *arg_ptr
));
107 static boolean parse_prune
PARAMS((char *argv
[], int *arg_ptr
));
108 static boolean parse_regex
PARAMS((char *argv
[], int *arg_ptr
));
109 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
, boolean ignore_case
));
110 static boolean parse_size
PARAMS((char *argv
[], int *arg_ptr
));
111 static boolean parse_true
PARAMS((char *argv
[], int *arg_ptr
));
112 static boolean parse_type
PARAMS((char *argv
[], int *arg_ptr
));
113 static boolean parse_uid
PARAMS((char *argv
[], int *arg_ptr
));
114 static boolean parse_used
PARAMS((char *argv
[], int *arg_ptr
));
115 static boolean parse_user
PARAMS((char *argv
[], int *arg_ptr
));
116 static boolean parse_version
PARAMS((char *argv
[], int *arg_ptr
));
117 static boolean parse_xdev
PARAMS((char *argv
[], int *arg_ptr
));
118 static boolean parse_xtype
PARAMS((char *argv
[], int *arg_ptr
));
120 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
, boolean ignore_case
));
121 static boolean insert_type
PARAMS((char *argv
[], int *arg_ptr
, boolean (*which_pred
)()));
122 static boolean insert_fprintf
PARAMS((FILE *fp
, boolean (*func
)(), char *argv
[], int *arg_ptr
));
123 static struct segment
**make_segment
PARAMS((struct segment
**segment
, char *format
, int len
, int kind
));
124 static boolean insert_exec_ok
PARAMS((boolean (*func
)(), char *argv
[], int *arg_ptr
));
125 static boolean get_num_days
PARAMS((char *str
, unsigned long *num_days
, enum comparison_type
*comp_type
));
126 static boolean insert_time
PARAMS((char *argv
[], int *arg_ptr
, PFB pred
));
127 static boolean get_num
PARAMS((char *str
, unsigned long *num
, enum comparison_type
*comp_type
));
128 static boolean insert_num
PARAMS((char *argv
[], int *arg_ptr
, PFB pred
));
129 static FILE *open_output_file
PARAMS((char *path
));
132 char *find_pred_name
PARAMS((PFB pred_func
));
141 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
142 If they are in some Unix versions of find, they are marked `Unix'. */
144 static struct parser_table
const parse_table
[] =
147 {"not", parse_negate
}, /* GNU */
150 {",", parse_comma
}, /* GNU */
152 {"amin", parse_amin
}, /* GNU */
153 {"and", parse_and
}, /* GNU */
154 {"anewer", parse_anewer
}, /* GNU */
155 {"atime", parse_atime
},
156 {"cmin", parse_cmin
}, /* GNU */
157 {"cnewer", parse_cnewer
}, /* GNU */
158 #ifdef UNIMPLEMENTED_UNIX
159 /* It's pretty ugly for find to know about archive formats.
160 Plus what it could do with cpio archives is very limited.
161 Better to leave it out. */
162 {"cpio", parse_cpio
}, /* Unix */
164 {"ctime", parse_ctime
},
165 {"daystart", parse_daystart
}, /* GNU */
166 {"depth", parse_depth
},
167 {"empty", parse_empty
}, /* GNU */
168 {"exec", parse_exec
},
169 {"false", parse_false
}, /* GNU */
170 {"fls", parse_fls
}, /* GNU */
171 {"follow", parse_follow
}, /* GNU, Unix */
172 {"fprint", parse_fprint
}, /* GNU */
173 {"fprint0", parse_fprint0
}, /* GNU */
174 {"fprintf", parse_fprintf
}, /* GNU */
175 {"fstype", parse_fstype
}, /* GNU, Unix */
176 {"gid", parse_gid
}, /* GNU */
177 {"group", parse_group
},
178 {"help", parse_help
}, /* GNU */
179 {"-help", parse_help
}, /* GNU */
180 {"ilname", parse_ilname
}, /* GNU */
181 {"iname", parse_iname
}, /* GNU */
182 {"inum", parse_inum
}, /* GNU, Unix */
183 {"ipath", parse_ipath
}, /* GNU */
184 {"iregex", parse_iregex
}, /* GNU */
185 {"links", parse_links
},
186 {"lname", parse_lname
}, /* GNU */
187 {"ls", parse_ls
}, /* GNU, Unix */
188 {"maxdepth", parse_maxdepth
}, /* GNU */
189 {"mindepth", parse_mindepth
}, /* GNU */
190 {"mmin", parse_mmin
}, /* GNU */
191 {"mount", parse_xdev
}, /* Unix */
192 {"mtime", parse_mtime
},
193 {"name", parse_name
},
194 #ifdef UNIMPLEMENTED_UNIX
195 {"ncpio", parse_ncpio
}, /* Unix */
197 {"newer", parse_newer
},
198 {"noleaf", parse_noleaf
}, /* GNU */
199 {"nogroup", parse_nogroup
},
200 {"nouser", parse_nouser
},
202 {"or", parse_or
}, /* GNU */
204 {"path", parse_path
}, /* GNU, HP-UX */
205 {"perm", parse_perm
},
206 {"print", parse_print
},
207 {"print0", parse_print0
}, /* GNU */
208 {"printf", parse_printf
}, /* GNU */
209 {"prune", parse_prune
},
210 {"regex", parse_regex
}, /* GNU */
211 {"size", parse_size
},
212 {"true", parse_true
}, /* GNU */
213 {"type", parse_type
},
214 {"uid", parse_uid
}, /* GNU */
215 {"used", parse_used
}, /* GNU */
216 {"user", parse_user
},
217 {"version", parse_version
}, /* GNU */
218 {"-version", parse_version
}, /* GNU */
219 {"xdev", parse_xdev
},
220 {"xtype", parse_xtype
}, /* GNU */
224 /* Return a pointer to the parser function to invoke for predicate
226 Return NULL if SEARCH_NAME is not a valid predicate name. */
229 find_parser (char *search_name
)
233 if (*search_name
== '-')
235 for (i
= 0; parse_table
[i
].parser_name
!= 0; i
++)
236 if (strcmp (parse_table
[i
].parser_name
, search_name
) == 0)
237 return (parse_table
[i
].parser_func
);
241 /* The parsers are responsible to continue scanning ARGV for
242 their arguments. Each parser knows what is and isn't
245 ARGV is the argument array.
246 *ARG_PTR is the index to start at in ARGV,
247 updated to point beyond the last element consumed.
249 The predicate structure is updated with the new information. */
252 parse_amin (char **argv
, int *arg_ptr
)
254 struct predicate
*our_pred
;
256 enum comparison_type c_type
;
258 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
260 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
262 our_pred
= insert_primary (pred_amin
);
263 our_pred
->args
.info
.kind
= c_type
;
264 our_pred
->args
.info
.l_val
= cur_day_start
+ DAYSECS
- num
* 60;
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
;
330 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
332 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
334 our_pred
= insert_primary (pred_cmin
);
335 our_pred
->args
.info
.kind
= c_type
;
336 our_pred
->args
.info
.l_val
= cur_day_start
+ DAYSECS
- num
* 60;
342 parse_cnewer (char **argv
, int *arg_ptr
)
344 struct predicate
*our_pred
;
345 struct stat stat_newer
;
347 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
349 if ((*xstat
) (argv
[*arg_ptr
], &stat_newer
))
350 error (1, errno
, "%s", argv
[*arg_ptr
]);
351 our_pred
= insert_primary (pred_cnewer
);
352 our_pred
->args
.time
= stat_newer
.st_mtime
;
358 parse_comma (char **argv
, int *arg_ptr
)
360 struct predicate
*our_pred
;
362 our_pred
= get_new_pred ();
363 our_pred
->pred_func
= pred_comma
;
365 our_pred
->p_name
= find_pred_name (pred_comma
);
367 our_pred
->p_type
= BI_OP
;
368 our_pred
->p_prec
= COMMA_PREC
;
369 our_pred
->need_stat
= false;
374 parse_ctime (char **argv
, int *arg_ptr
)
376 return (insert_time (argv
, arg_ptr
, pred_ctime
));
380 parse_daystart (char **argv
, int *arg_ptr
)
384 if (full_days
== false)
386 cur_day_start
+= DAYSECS
;
387 local
= localtime (&cur_day_start
);
388 cur_day_start
-= local
->tm_sec
+ local
->tm_min
* 60
389 + local
->tm_hour
* 3600;
396 parse_depth (char **argv
, int *arg_ptr
)
398 do_dir_first
= false;
403 parse_empty (char **argv
, int *arg_ptr
)
405 insert_primary (pred_empty
);
410 parse_exec (char **argv
, int *arg_ptr
)
412 return (insert_exec_ok (pred_exec
, argv
, arg_ptr
));
416 parse_false (char **argv
, int *arg_ptr
)
418 struct predicate
*our_pred
;
420 our_pred
= insert_primary (pred_false
);
421 our_pred
->need_stat
= false;
426 parse_fls (char **argv
, int *arg_ptr
)
428 struct predicate
*our_pred
;
430 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
432 our_pred
= insert_primary (pred_fls
);
433 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
434 our_pred
->side_effects
= true;
440 parse_fprintf (char **argv
, int *arg_ptr
)
444 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
446 if (argv
[*arg_ptr
+ 1] == NULL
)
448 /* Ensure we get "missing arg" message, not "invalid arg". */
452 fp
= open_output_file (argv
[*arg_ptr
]);
454 return (insert_fprintf (fp
, pred_fprintf
, argv
, arg_ptr
));
458 parse_follow (char **argv
, int *arg_ptr
)
462 no_leaf_check
= true;
467 parse_fprint (char **argv
, int *arg_ptr
)
469 struct predicate
*our_pred
;
471 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
473 our_pred
= insert_primary (pred_fprint
);
474 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
475 our_pred
->side_effects
= true;
476 our_pred
->need_stat
= false;
482 parse_fprint0 (char **argv
, int *arg_ptr
)
484 struct predicate
*our_pred
;
486 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
488 our_pred
= insert_primary (pred_fprint0
);
489 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
490 our_pred
->side_effects
= true;
491 our_pred
->need_stat
= false;
497 parse_fstype (char **argv
, int *arg_ptr
)
499 struct predicate
*our_pred
;
501 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
503 our_pred
= insert_primary (pred_fstype
);
504 our_pred
->args
.str
= argv
[*arg_ptr
];
510 parse_gid (char **argv
, int *arg_ptr
)
512 return (insert_num (argv
, arg_ptr
, pred_gid
));
516 parse_group (char **argv
, int *arg_ptr
)
518 struct group
*cur_gr
;
519 struct predicate
*our_pred
;
523 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
525 cur_gr
= getgrnam (argv
[*arg_ptr
]);
528 gid
= cur_gr
->gr_gid
;
531 gid_len
= strspn (argv
[*arg_ptr
], "0123456789");
532 if ((gid_len
== 0) || (argv
[*arg_ptr
][gid_len
] != '\0'))
534 gid
= atoi (argv
[*arg_ptr
]);
536 our_pred
= insert_primary (pred_group
);
537 our_pred
->args
.gid
= gid
;
543 parse_help (char **argv
, int *arg_ptr
)
546 Usage: %s [path...] [expression]\n"), program_name
);
548 default path is the current directory; default expression is -print\n\
549 expression may consist of:\n\
550 operators (decreasing precedence; -and is implicit where no others are given):\n\
551 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n"));
553 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n\
554 options (always true): -daystart -depth -follow --help\n\
555 -maxdepth LEVELS -mindepth LEVELS -mount -noleaf --version -xdev\n\
556 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n"));
558 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
559 -ilname PATTERN -iname PATTERN -inum N -ipath PATTERN -iregex PATTERN\n\
560 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE\n"));
562 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
563 -size N[bckw] -true -type [bcdpfls] -uid N -used N -user NAME\n\
564 -xtype [bcdpfls]\n"));
566 actions: -exec COMMAND ; -fprint FILE -fprint0 FILE -fprintf FILE FORMAT\n\
567 -ok COMMAND ; -print -print0 -printf FORMAT -prune -ls\n"));
572 parse_ilname (char **argv
, int *arg_ptr
)
574 struct predicate
*our_pred
;
576 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
578 our_pred
= insert_primary (pred_ilname
);
579 our_pred
->args
.str
= argv
[*arg_ptr
];
585 parse_iname (char **argv
, int *arg_ptr
)
587 struct predicate
*our_pred
;
589 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
591 our_pred
= insert_primary (pred_iname
);
592 our_pred
->need_stat
= false;
593 our_pred
->args
.str
= argv
[*arg_ptr
];
599 parse_inum (char **argv
, int *arg_ptr
)
601 return (insert_num (argv
, arg_ptr
, pred_inum
));
605 parse_ipath (char **argv
, int *arg_ptr
)
607 struct predicate
*our_pred
;
609 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
611 our_pred
= insert_primary (pred_ipath
);
612 our_pred
->need_stat
= false;
613 our_pred
->args
.str
= argv
[*arg_ptr
];
619 parse_iregex (char **argv
, int *arg_ptr
)
621 return insert_regex (argv
, arg_ptr
, true);
625 parse_links (char **argv
, int *arg_ptr
)
627 return (insert_num (argv
, arg_ptr
, pred_links
));
631 parse_lname (char **argv
, int *arg_ptr
)
633 struct predicate
*our_pred
;
635 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
637 our_pred
= insert_primary (pred_lname
);
638 our_pred
->args
.str
= argv
[*arg_ptr
];
644 parse_ls (char **argv
, int *arg_ptr
)
646 struct predicate
*our_pred
;
648 our_pred
= insert_primary (pred_ls
);
649 our_pred
->side_effects
= true;
654 parse_maxdepth (char **argv
, int *arg_ptr
)
658 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
660 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
661 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
663 maxdepth
= atoi (argv
[*arg_ptr
]);
671 parse_mindepth (char **argv
, int *arg_ptr
)
675 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
677 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
678 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
680 mindepth
= atoi (argv
[*arg_ptr
]);
688 parse_mmin (char **argv
, int *arg_ptr
)
690 struct predicate
*our_pred
;
692 enum comparison_type c_type
;
694 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
696 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
698 our_pred
= insert_primary (pred_mmin
);
699 our_pred
->args
.info
.kind
= c_type
;
700 our_pred
->args
.info
.l_val
= cur_day_start
+ DAYSECS
- num
* 60;
706 parse_mtime (char **argv
, int *arg_ptr
)
708 return (insert_time (argv
, arg_ptr
, pred_mtime
));
712 parse_name (char **argv
, int *arg_ptr
)
714 struct predicate
*our_pred
;
716 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
718 our_pred
= insert_primary (pred_name
);
719 our_pred
->need_stat
= false;
720 our_pred
->args
.str
= argv
[*arg_ptr
];
726 parse_negate (char **argv
, int *arg_ptr
)
728 struct predicate
*our_pred
;
730 our_pred
= get_new_pred_chk_op ();
731 our_pred
->pred_func
= pred_negate
;
733 our_pred
->p_name
= find_pred_name (pred_negate
);
735 our_pred
->p_type
= UNI_OP
;
736 our_pred
->p_prec
= NEGATE_PREC
;
737 our_pred
->need_stat
= false;
742 parse_newer (char **argv
, int *arg_ptr
)
744 struct predicate
*our_pred
;
745 struct stat stat_newer
;
747 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
749 if ((*xstat
) (argv
[*arg_ptr
], &stat_newer
))
750 error (1, errno
, "%s", argv
[*arg_ptr
]);
751 our_pred
= insert_primary (pred_newer
);
752 our_pred
->args
.time
= stat_newer
.st_mtime
;
758 parse_noleaf (char **argv
, int *arg_ptr
)
760 no_leaf_check
= true;
765 /* Arbitrary amount by which to increase size
766 of `uid_unused' and `gid_unused'. */
767 #define ALLOC_STEP 2048
769 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
770 char *uid_unused
= NULL
;
772 /* Number of elements in `uid_unused'. */
773 unsigned uid_allocated
;
775 /* Similar for GIDs and group entries. */
776 char *gid_unused
= NULL
;
777 unsigned gid_allocated
;
781 parse_nogroup (char **argv
, int *arg_ptr
)
783 struct predicate
*our_pred
;
785 our_pred
= insert_primary (pred_nogroup
);
787 if (gid_unused
== NULL
)
791 gid_allocated
= ALLOC_STEP
;
792 gid_unused
= xmalloc (gid_allocated
);
793 memset (gid_unused
, 1, gid_allocated
);
795 while ((gr
= getgrent ()) != NULL
)
797 if ((unsigned) gr
->gr_gid
>= gid_allocated
)
799 unsigned new_allocated
= (unsigned) gr
->gr_gid
+ ALLOC_STEP
;
800 gid_unused
= xrealloc (gid_unused
, new_allocated
);
801 memset (gid_unused
+ gid_allocated
, 1,
802 new_allocated
- gid_allocated
);
803 gid_allocated
= new_allocated
;
805 gid_unused
[(unsigned) gr
->gr_gid
] = 0;
814 parse_nouser (char **argv
, int *arg_ptr
)
816 struct predicate
*our_pred
;
818 our_pred
= insert_primary (pred_nouser
);
820 if (uid_unused
== NULL
)
824 uid_allocated
= ALLOC_STEP
;
825 uid_unused
= xmalloc (uid_allocated
);
826 memset (uid_unused
, 1, uid_allocated
);
828 while ((pw
= getpwent ()) != NULL
)
830 if ((unsigned) pw
->pw_uid
>= uid_allocated
)
832 unsigned new_allocated
= (unsigned) pw
->pw_uid
+ ALLOC_STEP
;
833 uid_unused
= xrealloc (uid_unused
, new_allocated
);
834 memset (uid_unused
+ uid_allocated
, 1,
835 new_allocated
- uid_allocated
);
836 uid_allocated
= new_allocated
;
838 uid_unused
[(unsigned) pw
->pw_uid
] = 0;
847 parse_ok (char **argv
, int *arg_ptr
)
849 return (insert_exec_ok (pred_ok
, argv
, arg_ptr
));
853 parse_open (char **argv
, int *arg_ptr
)
855 struct predicate
*our_pred
;
857 our_pred
= get_new_pred_chk_op ();
858 our_pred
->pred_func
= pred_open
;
860 our_pred
->p_name
= find_pred_name (pred_open
);
862 our_pred
->p_type
= OPEN_PAREN
;
863 our_pred
->p_prec
= NO_PREC
;
864 our_pred
->need_stat
= false;
869 parse_or (char **argv
, int *arg_ptr
)
871 struct predicate
*our_pred
;
873 our_pred
= get_new_pred ();
874 our_pred
->pred_func
= pred_or
;
876 our_pred
->p_name
= find_pred_name (pred_or
);
878 our_pred
->p_type
= BI_OP
;
879 our_pred
->p_prec
= OR_PREC
;
880 our_pred
->need_stat
= false;
885 parse_path (char **argv
, int *arg_ptr
)
887 struct predicate
*our_pred
;
889 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
891 our_pred
= insert_primary (pred_path
);
892 our_pred
->need_stat
= false;
893 our_pred
->args
.str
= argv
[*arg_ptr
];
899 parse_perm (char **argv
, int *arg_ptr
)
901 unsigned long perm_val
;
903 struct mode_change
*change
;
904 struct predicate
*our_pred
;
906 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
909 switch (argv
[*arg_ptr
][0])
920 change
= mode_compile (argv
[*arg_ptr
] + mode_start
, MODE_MASK_PLUS
);
921 if (change
== MODE_INVALID
)
922 error (1, 0, _("invalid mode `%s'"), argv
[*arg_ptr
]);
923 else if (change
== MODE_MEMORY_EXHAUSTED
)
924 error (1, 0, _("virtual memory exhausted"));
925 perm_val
= mode_adjust (0, change
);
928 our_pred
= insert_primary (pred_perm
);
930 switch (argv
[*arg_ptr
][0])
933 /* Set magic flag to indicate true if at least the given bits are set. */
934 our_pred
->args
.perm
= (perm_val
& 07777) | 010000;
937 /* Set magic flag to indicate true if any of the given bits are set. */
938 our_pred
->args
.perm
= (perm_val
& 07777) | 020000;
941 /* True if exactly the given bits are set. */
942 our_pred
->args
.perm
= (perm_val
& 07777);
950 parse_print (char **argv
, int *arg_ptr
)
952 struct predicate
*our_pred
;
954 our_pred
= insert_primary (pred_print
);
955 /* -print has the side effect of printing. This prevents us
956 from doing undesired multiple printing when the user has
957 already specified -print. */
958 our_pred
->side_effects
= true;
959 our_pred
->need_stat
= false;
964 parse_print0 (char **argv
, int *arg_ptr
)
966 struct predicate
*our_pred
;
968 our_pred
= insert_primary (pred_print0
);
969 /* -print0 has the side effect of printing. This prevents us
970 from doing undesired multiple printing when the user has
971 already specified -print0. */
972 our_pred
->side_effects
= true;
973 our_pred
->need_stat
= false;
978 parse_printf (char **argv
, int *arg_ptr
)
980 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
982 return (insert_fprintf (stdout
, pred_fprintf
, argv
, arg_ptr
));
986 parse_prune (char **argv
, int *arg_ptr
)
988 struct predicate
*our_pred
;
990 our_pred
= insert_primary (pred_prune
);
991 our_pred
->need_stat
= false;
996 parse_regex (char **argv
, int *arg_ptr
)
998 return insert_regex (argv
, arg_ptr
, false);
1002 insert_regex (char **argv
, int *arg_ptr
, boolean ignore_case
)
1004 struct predicate
*our_pred
;
1005 struct re_pattern_buffer
*re
;
1006 const char *error_message
;
1008 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1010 our_pred
= insert_primary (pred_regex
);
1011 our_pred
->need_stat
= false;
1012 re
= (struct re_pattern_buffer
*)
1013 xmalloc (sizeof (struct re_pattern_buffer
));
1014 our_pred
->args
.regex
= re
;
1015 re
->allocated
= 100;
1016 re
->buffer
= (unsigned char *) xmalloc (re
->allocated
);
1023 re
->translate
= xmalloc (256);
1024 /* Map uppercase characters to corresponding lowercase ones. */
1025 for (i
= 0; i
< 256; i
++)
1026 re
->translate
[i
] = ISUPPER (i
) ? tolower (i
) : i
;
1029 re
->translate
= NULL
;
1031 error_message
= re_compile_pattern (argv
[*arg_ptr
], strlen (argv
[*arg_ptr
]),
1034 error (1, 0, "%s", error_message
);
1040 parse_size (char **argv
, int *arg_ptr
)
1042 struct predicate
*our_pred
;
1044 enum comparison_type c_type
;
1048 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1050 len
= strlen (argv
[*arg_ptr
]);
1052 error (1, 0, _("invalid null argument to -size"));
1053 switch (argv
[*arg_ptr
][len
- 1])
1057 argv
[*arg_ptr
][len
- 1] = '\0';
1062 argv
[*arg_ptr
][len
- 1] = '\0';
1067 argv
[*arg_ptr
][len
- 1] = '\0';
1072 argv
[*arg_ptr
][len
- 1] = '\0';
1088 error (1, 0, _("invalid -size type `%c'"), argv
[*arg_ptr
][len
- 1]);
1090 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
1092 our_pred
= insert_primary (pred_size
);
1093 our_pred
->args
.size
.kind
= c_type
;
1094 our_pred
->args
.size
.blocksize
= blksize
;
1095 our_pred
->args
.size
.size
= num
;
1101 parse_true (char **argv
, int *arg_ptr
)
1103 struct predicate
*our_pred
;
1105 our_pred
= insert_primary (pred_true
);
1106 our_pred
->need_stat
= false;
1111 parse_type (char **argv
, int *arg_ptr
)
1113 return insert_type (argv
, arg_ptr
, pred_type
);
1117 parse_uid (char **argv
, int *arg_ptr
)
1119 return (insert_num (argv
, arg_ptr
, pred_uid
));
1123 parse_used (char **argv
, int *arg_ptr
)
1125 struct predicate
*our_pred
;
1126 unsigned long num_days
;
1127 enum comparison_type c_type
;
1129 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1131 if (!get_num (argv
[*arg_ptr
], &num_days
, &c_type
))
1133 our_pred
= insert_primary (pred_used
);
1134 our_pred
->args
.info
.kind
= c_type
;
1135 our_pred
->args
.info
.l_val
= num_days
* DAYSECS
;
1141 parse_user (char **argv
, int *arg_ptr
)
1143 struct passwd
*cur_pwd
;
1144 struct predicate
*our_pred
;
1148 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1150 cur_pwd
= getpwnam (argv
[*arg_ptr
]);
1152 if (cur_pwd
!= NULL
)
1153 uid
= cur_pwd
->pw_uid
;
1156 uid_len
= strspn (argv
[*arg_ptr
], "0123456789");
1157 if ((uid_len
== 0) || (argv
[*arg_ptr
][uid_len
] != '\0'))
1159 uid
= atoi (argv
[*arg_ptr
]);
1161 our_pred
= insert_primary (pred_user
);
1162 our_pred
->args
.uid
= uid
;
1168 parse_version (char **argv
, int *arg_ptr
)
1170 extern char *version_string
;
1173 printf (_("GNU find version %s\n"), version_string
);
1178 parse_xdev (char **argv
, int *arg_ptr
)
1180 stay_on_filesystem
= true;
1185 parse_xtype (char **argv
, int *arg_ptr
)
1187 return insert_type (argv
, arg_ptr
, pred_xtype
);
1191 insert_type (char **argv
, int *arg_ptr
, boolean (*which_pred
) (/* ??? */))
1193 unsigned long type_cell
;
1194 struct predicate
*our_pred
;
1196 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
)
1197 || (strlen (argv
[*arg_ptr
]) != 1))
1199 switch (argv
[*arg_ptr
][0])
1201 case 'b': /* block special */
1202 type_cell
= S_IFBLK
;
1204 case 'c': /* character special */
1205 type_cell
= S_IFCHR
;
1207 case 'd': /* directory */
1208 type_cell
= S_IFDIR
;
1210 case 'f': /* regular file */
1211 type_cell
= S_IFREG
;
1214 case 'l': /* symbolic link */
1215 type_cell
= S_IFLNK
;
1219 case 'p': /* pipe */
1220 type_cell
= S_IFIFO
;
1224 case 's': /* socket */
1225 type_cell
= S_IFSOCK
;
1228 default: /* None of the above ... nuke 'em. */
1231 our_pred
= insert_primary (which_pred
);
1232 our_pred
->args
.type
= type_cell
;
1233 (*arg_ptr
)++; /* Move on to next argument. */
1237 /* If true, we've determined that the current fprintf predicate
1238 uses stat information. */
1239 static boolean fprintf_stat_needed
;
1242 insert_fprintf (FILE *fp
, boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
1244 char *format
; /* Beginning of unprocessed format string. */
1245 register char *scan
; /* Current address in scanning `format'. */
1246 register char *scan2
; /* Address inside of element being scanned. */
1247 struct segment
**segmentp
; /* Address of current segment. */
1248 struct predicate
*our_pred
;
1250 format
= argv
[(*arg_ptr
)++];
1252 fprintf_stat_needed
= false; /* Might be overridden later. */
1253 our_pred
= insert_primary (func
);
1254 our_pred
->side_effects
= true;
1255 our_pred
->args
.printf_vec
.stream
= fp
;
1256 segmentp
= &our_pred
->args
.printf_vec
.segment
;
1259 for (scan
= format
; *scan
; scan
++)
1264 if (*scan2
>= '0' && *scan2
<= '7')
1268 for (i
= n
= 0; i
< 3 && (*scan2
>= '0' && *scan2
<= '7');
1270 n
= 8 * n
+ *scan2
- '0';
1285 make_segment (segmentp
, format
, scan
- format
, KIND_STOP
);
1286 our_pred
->need_stat
= fprintf_stat_needed
;
1304 /* *scan = '\\'; * it already is */
1308 _("warning: unrecognized escape `\\%c'"), *scan2
);
1313 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1315 format
= scan2
+ 1; /* Move past the escape. */
1316 scan
= scan2
; /* Incremented immediately by `for'. */
1318 else if (*scan
== '%')
1322 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1328 /* Scan past flags, width and precision, to verify kind. */
1329 for (scan2
= scan
; *++scan2
&& strchr ("-+ #", *scan2
);)
1331 while (ISDIGIT (*scan2
))
1334 for (scan2
++; ISDIGIT (*scan2
); scan2
++)
1336 if (strchr ("abcdfFgGhHiklmnpPstuU", *scan2
))
1338 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1343 else if (strchr ("ACT", *scan2
) && scan2
[1])
1345 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1346 *scan2
| (scan2
[1] << 8));
1353 /* An unrecognized % escape. Print the char after the %. */
1354 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1356 segmentp
= make_segment (segmentp
, format
, scan
- format
,
1365 make_segment (segmentp
, format
, scan
- format
, KIND_PLAIN
);
1366 our_pred
->need_stat
= fprintf_stat_needed
;
1370 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1371 from the text in FORMAT, which has length LEN.
1372 Return the address of the `next' pointer of the new segment. */
1374 static struct segment
**
1375 make_segment (struct segment
**segment
, char *format
, int len
, int kind
)
1379 *segment
= (struct segment
*) xmalloc (sizeof (struct segment
));
1381 (*segment
)->kind
= kind
;
1382 (*segment
)->next
= NULL
;
1383 (*segment
)->text_len
= len
;
1385 fmt
= (*segment
)->text
= xmalloc (len
+ 3); /* room for "ld\0" */
1386 strncpy (fmt
, format
, len
);
1389 switch (kind
& 0xff)
1391 case KIND_PLAIN
: /* Plain text string, no % conversion. */
1392 case KIND_STOP
: /* Terminate argument, no newline. */
1395 case 'a': /* atime in `ctime' format */
1396 case 'c': /* ctime in `ctime' format */
1397 case 'F': /* filesystem type */
1398 case 'g': /* group name */
1399 case 'l': /* object of symlink */
1400 case 't': /* mtime in `ctime' format */
1401 case 'u': /* user name */
1402 case 'A': /* atime in user-specified strftime format */
1403 case 'C': /* ctime in user-specified strftime format */
1404 case 'T': /* mtime in user-specified strftime format */
1405 fprintf_stat_needed
= true;
1407 case 'f': /* basename of path */
1408 case 'h': /* leading directories part of path */
1409 case 'H': /* ARGV element file was found under */
1410 case 'p': /* pathname */
1411 case 'P': /* pathname with ARGV element stripped */
1415 case 'b': /* size in 512-byte blocks */
1416 case 'k': /* size in 1K blocks */
1417 case 's': /* size in bytes */
1420 case 'n': /* number of links */
1421 fprintf_stat_needed
= true;
1423 case 'd': /* depth in search tree (0 = ARGV element) */
1427 case 'i': /* inode number */
1430 case 'G': /* GID number */
1431 case 'U': /* UID number */
1433 fprintf_stat_needed
= true;
1436 case 'm': /* mode as octal number (perms only) */
1438 fprintf_stat_needed
= true;
1443 return (&(*segment
)->next
);
1447 insert_exec_ok (boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
1449 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
1450 int num_paths
; /* Number of args with path replacements. */
1451 int path_pos
; /* Index in array of path replacements. */
1452 int vec_pos
; /* Index in array of args. */
1453 struct predicate
*our_pred
;
1454 struct exec_val
*execp
; /* Pointer for efficiency. */
1456 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1459 /* Count the number of args with path replacements, up until the ';'. */
1461 for (end
= start
, num_paths
= 0;
1463 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
1465 if (strstr (argv
[end
], "{}"))
1467 /* Fail if no command given or no semicolon found. */
1468 if ((end
== start
) || (argv
[end
] == NULL
))
1474 our_pred
= insert_primary (func
);
1475 our_pred
->side_effects
= true;
1476 execp
= &our_pred
->args
.exec_vec
;
1478 (struct path_arg
*) xmalloc (sizeof (struct path_arg
) * (num_paths
+ 1));
1479 execp
->vec
= (char **) xmalloc (sizeof (char *) * (end
- start
+ 1));
1480 /* Record the positions of all args, and the args with path replacements. */
1481 for (end
= start
, path_pos
= vec_pos
= 0;
1483 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
1488 execp
->paths
[path_pos
].count
= 0;
1489 for (p
= argv
[end
]; *p
; ++p
)
1490 if (p
[0] == '{' && p
[1] == '}')
1492 execp
->paths
[path_pos
].count
++;
1495 if (execp
->paths
[path_pos
].count
)
1497 execp
->paths
[path_pos
].offset
= vec_pos
;
1498 execp
->paths
[path_pos
].origarg
= argv
[end
];
1501 execp
->vec
[vec_pos
++] = argv
[end
];
1503 execp
->paths
[path_pos
].offset
= -1;
1504 execp
->vec
[vec_pos
] = NULL
;
1506 if (argv
[end
] == NULL
)
1513 /* Get a number of days and comparison type.
1514 STR is the ASCII representation.
1515 Set *NUM_DAYS to the number of days, taken as being from
1516 the current moment (or possibly midnight). Thus the sense of the
1517 comparison type appears to be reversed.
1518 Set *COMP_TYPE to the kind of comparison that is requested.
1520 Return true if all okay, false if input error.
1522 Used by -atime, -ctime and -mtime (parsers) to
1523 get the appropriate information for a time predicate processor. */
1526 get_num_days (char *str
, long unsigned int *num_days
, enum comparison_type
*comp_type
)
1528 int len_days
; /* length of field */
1535 *comp_type
= COMP_LT
;
1539 *comp_type
= COMP_GT
;
1552 *comp_type
= COMP_EQ
;
1558 /* We know the first char has been reasonable. Find the
1559 number of days to play with. */
1560 len_days
= strspn (str
, "0123456789");
1561 if ((len_days
== 0) || (str
[len_days
] != '\0'))
1563 *num_days
= (unsigned long) atol (str
);
1567 /* Insert a time predicate PRED.
1568 ARGV is a pointer to the argument array.
1569 ARG_PTR is a pointer to an index into the array, incremented if
1572 Return true if input is valid, false if not.
1574 A new predicate node is assigned, along with an argument node
1575 obtained with malloc.
1577 Used by -atime, -ctime, and -mtime parsers. */
1580 insert_time (char **argv
, int *arg_ptr
, PFB pred
)
1582 struct predicate
*our_pred
;
1583 unsigned long num_days
;
1584 enum comparison_type c_type
;
1586 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1588 if (!get_num_days (argv
[*arg_ptr
], &num_days
, &c_type
))
1590 our_pred
= insert_primary (pred
);
1591 our_pred
->args
.info
.kind
= c_type
;
1592 our_pred
->args
.info
.l_val
= cur_day_start
- num_days
* DAYSECS
1593 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0);
1596 printf (_("inserting %s\n"), our_pred
->p_name
);
1597 printf (_(" type: %s %s "),
1598 (c_type
== COMP_GT
) ? "gt" :
1599 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
1600 (c_type
== COMP_GT
) ? " >" :
1601 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? ">=" : " ?")));
1602 printf ("%ld %s", our_pred
->args
.info
.l_val
,
1603 ctime (&our_pred
->args
.info
.l_val
));
1604 if (c_type
== COMP_EQ
)
1606 our_pred
->args
.info
.l_val
+= DAYSECS
;
1607 printf (" < %ld %s", our_pred
->args
.info
.l_val
,
1608 ctime (&our_pred
->args
.info
.l_val
));
1609 our_pred
->args
.info
.l_val
-= DAYSECS
;
1615 /* Get a number with comparision information.
1616 The sense of the comparision information is 'normal'; that is,
1617 '+' looks for inums or links > than the number and '-' less than.
1619 STR is the ASCII representation of the number.
1620 Set *NUM to the number.
1621 Set *COMP_TYPE to the kind of comparison that is requested.
1623 Return true if all okay, false if input error.
1625 Used by the -inum and -links predicate parsers. */
1628 get_num (char *str
, long unsigned int *num
, enum comparison_type
*comp_type
)
1630 int len_num
; /* Length of field. */
1637 *comp_type
= COMP_GT
;
1641 *comp_type
= COMP_LT
;
1654 *comp_type
= COMP_EQ
;
1660 /* We know the first char has been reasonable. Find the number of
1661 days to play with. */
1662 len_num
= strspn (str
, "0123456789");
1663 if ((len_num
== 0) || (str
[len_num
] != '\0'))
1665 *num
= (unsigned long) atol (str
);
1669 /* Insert a number predicate.
1670 ARGV is a pointer to the argument array.
1671 *ARG_PTR is an index into ARGV, incremented if all went well.
1672 *PRED is the predicate processor to insert.
1674 Return true if input is valid, false if error.
1676 A new predicate node is assigned, along with an argument node
1677 obtained with malloc.
1679 Used by -inum and -links parsers. */
1682 insert_num (char **argv
, int *arg_ptr
, PFB pred
)
1684 struct predicate
*our_pred
;
1686 enum comparison_type c_type
;
1688 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1690 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
1692 our_pred
= insert_primary (pred
);
1693 our_pred
->args
.info
.kind
= c_type
;
1694 our_pred
->args
.info
.l_val
= num
;
1697 printf (_("inserting %s\n"), our_pred
->p_name
);
1698 printf (_(" type: %s %s "),
1699 (c_type
== COMP_GT
) ? "gt" :
1700 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
1701 (c_type
== COMP_GT
) ? " >" :
1702 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? " =" : " ?")));
1703 printf ("%ld\n", our_pred
->args
.info
.l_val
);
1709 open_output_file (char *path
)
1713 if (!strcmp (path
, "/dev/stderr"))
1715 else if (!strcmp (path
, "/dev/stdout"))
1717 f
= fopen (path
, "w");
1719 error (1, errno
, "%s", path
);