]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/whereis.c
misc: add static keyword to where needed [smatch scan]
[thirdparty/util-linux.git] / misc-utils / whereis.c
CommitLineData
6dbe3af9
KZ
1/*-
2 * Copyright (c) 1980 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
9db54a7e
SK
32 *
33 * 1999-02-22 Arkadiusz Miƛkiewicz <misiek@pld.ORG.PL>
7eda085c 34 * - added Native Language Support
9db54a7e 35 * 2011-08-12 Davidlohr Bueso <dave@gnu.org>
f51be859 36 * - added $PATH lookup
9db54a7e
SK
37 *
38 * Copyright (C) 2013 Karel Zak <kzak@redhat.com>
39 * 2013 Sami Kerola <kerolasa@iki.fi>
f51be859
DB
40 */
41
6dbe3af9 42#include <sys/param.h>
7574f09b 43#include <sys/types.h>
2b6fc908 44#include <sys/stat.h>
7574f09b 45#include <dirent.h>
6dbe3af9 46#include <stdio.h>
66ee8158 47#include <stdlib.h>
fd6b7a7f 48#include <string.h>
6dbe3af9 49#include <ctype.h>
9db54a7e 50#include <assert.h>
f51be859
DB
51
52#include "xalloc.h"
7eda085c 53#include "nls.h"
58513363 54#include "c.h"
c05a80ca 55#include "closestream.h"
f84559ee 56#include "canonicalize.h"
6dbe3af9 57
3635f53c
KZ
58#include "debug.h"
59
2ba641e5 60static UL_DEBUG_DEFINE_MASK(whereis);
819d9a29 61UL_DEBUG_DEFINE_MASKNAMES(whereis) = UL_DEBUG_EMPTY_MASKNAMES;
3635f53c
KZ
62
63#define WHEREIS_DEBUG_INIT (1 << 1)
64#define WHEREIS_DEBUG_PATH (1 << 2)
65#define WHEREIS_DEBUG_ENV (1 << 3)
66#define WHEREIS_DEBUG_ARGV (1 << 4)
67#define WHEREIS_DEBUG_SEARCH (1 << 5)
68#define WHEREIS_DEBUG_STATIC (1 << 6)
69#define WHEREIS_DEBUG_LIST (1 << 7)
70#define WHEREIS_DEBUG_ALL 0xFFFF
71
72#define DBG(m, x) __UL_DBG(whereis, WHEREIS_DEBUG_, m, x)
73#define ON_DBG(m, x) __UL_DBG_CALL(whereis, WHEREIS_DEBUG_, m, x)
9db54a7e
SK
74
75static char uflag = 0;
76
77/* supported types */
78enum {
79 BIN_DIR = (1 << 1),
80 MAN_DIR = (1 << 2),
81 SRC_DIR = (1 << 3),
82
83 ALL_DIRS = BIN_DIR | MAN_DIR | SRC_DIR
84};
85
86/* directories */
87struct wh_dirlist {
88 int type;
89 dev_t st_dev;
90 ino_t st_ino;
91 char *path;
92
93 struct wh_dirlist *next;
94};
95
96static const char *bindirs[] = {
30ad62d0 97 "/usr/bin",
30ad62d0 98 "/usr/sbin",
f84559ee
KZ
99 "/usr/lib",
100 "/usr/lib64",
101 "/bin",
102 "/sbin",
30ad62d0
SK
103 "/etc",
104 "/usr/etc",
105 "/lib",
30ad62d0 106 "/lib64",
30ad62d0
SK
107 "/usr/games",
108 "/usr/games/bin",
109 "/usr/games/lib",
110 "/usr/emacs/etc",
111 "/usr/lib/emacs/*/etc",
112 "/usr/TeX/bin",
113 "/usr/tex/bin",
114 "/usr/interviews/bin/LINUX",
115
116 "/usr/X11R6/bin",
117 "/usr/X386/bin",
118 "/usr/bin/X11",
119 "/usr/X11/bin",
120 "/usr/X11R5/bin",
121
122 "/usr/local/bin",
123 "/usr/local/sbin",
124 "/usr/local/etc",
125 "/usr/local/lib",
126 "/usr/local/games",
127 "/usr/local/games/bin",
128 "/usr/local/emacs/etc",
129 "/usr/local/TeX/bin",
130 "/usr/local/tex/bin",
131 "/usr/local/bin/X11",
132
133 "/usr/contrib",
134 "/usr/hosts",
135 "/usr/include",
136
137 "/usr/g++-include",
138
139 "/usr/ucb",
140 "/usr/old",
141 "/usr/new",
142 "/usr/local",
143 "/usr/libexec",
144 "/usr/share",
145
146 "/opt/*/bin",
9db54a7e 147 NULL
6dbe3af9 148};
2b6fc908 149
9db54a7e 150static const char *mandirs[] = {
2b6fc908 151 "/usr/man/*",
66ee8158 152 "/usr/share/man/*",
2b6fc908
KZ
153 "/usr/X386/man/*",
154 "/usr/X11/man/*",
155 "/usr/TeX/man/*",
6dbe3af9 156 "/usr/interviews/man/mann",
db0ccb76 157 "/usr/share/info",
9db54a7e 158 NULL
6dbe3af9 159};
2b6fc908 160
9db54a7e 161static const char *srcdirs[] = {
2b6fc908
KZ
162 "/usr/src/*",
163 "/usr/src/lib/libc/*",
164 "/usr/src/lib/libc/net/*",
6dbe3af9
KZ
165 "/usr/src/ucb/pascal",
166 "/usr/src/ucb/pascal/utilities",
167 "/usr/src/undoc",
9db54a7e 168 NULL
6dbe3af9
KZ
169};
170
3635f53c
KZ
171static void whereis_init_debug(void)
172{
173 __UL_INIT_DEBUG(whereis, WHEREIS_DEBUG_, 0, WHEREIS_DEBUG);
174}
175
176static const char *whereis_type_to_name(int type)
177{
178 switch (type) {
179 case BIN_DIR: return "bin";
180 case MAN_DIR: return "man";
181 case SRC_DIR: return "src";
182 default: return "???";
183 }
184}
9db54a7e
SK
185
186static void __attribute__((__noreturn__)) usage(FILE *out)
58513363 187{
ec3bc7aa 188 fputs(USAGE_HEADER, out);
bde76c04 189 fprintf(out, _(" %s [options] [-BMS <dir>... -f] <name>\n"), program_invocation_short_name);
ec3bc7aa 190
451dbcfa
BS
191 fputs(USAGE_SEPARATOR, out);
192 fputs(_("Locate the binary, source, and manual-page files for a command.\n"), out);
193
ec3bc7aa
SK
194 fputs(USAGE_OPTIONS, out);
195 fputs(_(" -b search only for binaries\n"), out);
196 fputs(_(" -B <dirs> define binaries lookup path\n"), out);
db0ccb76
KZ
197 fputs(_(" -m search only for manuals and infos\n"), out);
198 fputs(_(" -M <dirs> define man and info lookup path\n"), out);
ec3bc7aa
SK
199 fputs(_(" -s search only for sources\n"), out);
200 fputs(_(" -S <dirs> define sources lookup path\n"), out);
201 fputs(_(" -f terminate <dirs> argument list\n"), out);
202 fputs(_(" -u search for unusual entries\n"), out);
203 fputs(_(" -l output effective lookup paths\n"), out);
204 fprintf(out, USAGE_MAN_TAIL("whereis(1)"));
205
58513363
SK
206 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
207}
208
9db54a7e 209static void dirlist_add_dir(struct wh_dirlist **ls0, int type, const char *dir)
30ad62d0 210{
9db54a7e
SK
211 struct stat st;
212 struct wh_dirlist *prev = NULL, *ls = *ls0;
32ae96ae 213
9db54a7e
SK
214 if (access(dir, R_OK) != 0)
215 return;
216 if (stat(dir, &st) != 0 || !S_ISDIR(st.st_mode))
217 return;
3635f53c 218
9db54a7e
SK
219 while (ls) {
220 if (ls->st_ino == st.st_ino &&
221 ls->st_dev == st.st_dev &&
222 ls->type == type) {
3635f53c 223 DBG(LIST, ul_debugobj(*ls0, " ignore (already in list): %s", dir));
0b542a94 224 return;
0b542a94 225 }
9db54a7e
SK
226 prev = ls;
227 ls = ls->next;
0b542a94 228 }
6dbe3af9 229
9db54a7e
SK
230
231 ls = xcalloc(1, sizeof(*ls));
232 ls->st_ino = st.st_ino;
233 ls->st_dev = st.st_dev;
234 ls->type = type;
f84559ee 235 ls->path = canonicalize_path(dir);
9db54a7e
SK
236
237 if (!*ls0)
238 *ls0 = ls; /* first in the list */
239 else {
240 assert(prev);
241 prev->next = ls; /* add to the end of the list */
0b542a94 242 }
3635f53c
KZ
243
244 DBG(LIST, ul_debugobj(*ls0, " add dir: %s", ls->path));
0b542a94 245 return;
0b542a94 246}
6dbe3af9 247
9db54a7e
SK
248/* special case for '*' in the paths */
249static void dirlist_add_subdir(struct wh_dirlist **ls, int type, const char *dir)
f51be859 250{
9db54a7e
SK
251 char buf[PATH_MAX], *d;
252 DIR *dirp;
253 struct dirent *dp;
f51be859 254
9db54a7e
SK
255 strncpy(buf, dir, PATH_MAX);
256 buf[PATH_MAX - 1] = '\0';
f51be859 257
9db54a7e
SK
258 d = strchr(buf, '*');
259 if (!d)
260 return;
261 *d = 0;
f51be859 262
9db54a7e
SK
263 dirp = opendir(buf);
264 if (!dirp)
265 return;
266
3635f53c 267 DBG(LIST, ul_debugobj(*ls, " scanning subdir: %s", dir));
9db54a7e
SK
268
269 while ((dp = readdir(dirp)) != NULL) {
270 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
271 continue;
272 snprintf(d, PATH_MAX - (d - buf), "%s", dp->d_name);
273 /* a dir definition can have a star in middle of path */
274 strcat(buf, strchr(dir, '*') + 1);
275 dirlist_add_dir(ls, type, buf);
276 }
277 closedir(dirp);
278 return;
f51be859
DB
279}
280
9db54a7e
SK
281static void construct_dirlist_from_env(const char *env,
282 struct wh_dirlist **ls,
283 int type)
f51be859 284{
9db54a7e 285 char *key = NULL, *tok = NULL, *pathcp, *path = getenv(env);
f51be859
DB
286
287 if (!path)
288 return;
289 pathcp = xstrdup(path);
290
3635f53c
KZ
291 DBG(ENV, ul_debugobj(*ls, "construct %s dirlist from: %s",
292 whereis_type_to_name(type), path));
f51be859 293
9db54a7e
SK
294 for (tok = strtok_r(pathcp, ":", &key); tok;
295 tok = strtok_r(NULL, ":", &key))
296 dirlist_add_dir(ls, type, tok);
f51be859 297
f51be859 298 free(pathcp);
9db54a7e 299 return;
f51be859
DB
300}
301
9db54a7e
SK
302static void construct_dirlist_from_argv(struct wh_dirlist **ls,
303 int *idx,
304 int argc,
305 char *argv[],
306 int type)
f51be859 307{
3635f53c 308 int i;
9db54a7e 309
3635f53c
KZ
310 DBG(ARGV, ul_debugobj(*ls, "construct %s dirlist from argv[%d..]",
311 whereis_type_to_name(type), *idx));
312
313 for (i = *idx; i < argc; i++) {
314 if (*argv[i] == '-') /* end of the list */
315 break;
316
317 DBG(ARGV, ul_debugobj(*ls, " using argv[%d]: %s", *idx, argv[*idx]));
318 dirlist_add_dir(ls, type, argv[i]);
319 *idx = i;
9db54a7e 320 }
3635f53c 321
9db54a7e 322 return;
f51be859
DB
323}
324
9db54a7e
SK
325static void construct_dirlist(struct wh_dirlist **ls,
326 int type,
327 const char **paths)
0b542a94 328{
9db54a7e 329 size_t i;
f51be859 330
3635f53c
KZ
331 DBG(STATIC, ul_debugobj(*ls, "construct %s dirlist from static array",
332 whereis_type_to_name(type)));
6dbe3af9 333
9db54a7e
SK
334 for (i = 0; paths[i]; i++) {
335 if (!strchr(paths[i], '*'))
336 dirlist_add_dir(ls, type, paths[i]);
337 else
338 dirlist_add_subdir(ls, type, paths[i]);
339 }
340 return;
0b542a94 341}
6dbe3af9 342
9db54a7e 343static void free_dirlist(struct wh_dirlist **ls0, int type)
0b542a94 344{
9db54a7e 345 struct wh_dirlist *prev = NULL, *next, *ls = *ls0;
6dbe3af9 346
9db54a7e 347 *ls0 = NULL;
6dbe3af9 348
3635f53c 349 DBG(LIST, ul_debugobj(*ls0, "free dirlist"));
9db54a7e
SK
350
351 while (ls) {
352 if (ls->type & type) {
353 next = ls->next;
3635f53c 354 DBG(LIST, ul_debugobj(*ls0, " free: %s", ls->path));
9db54a7e
SK
355 free(ls->path);
356 free(ls);
357 ls = next;
358 if (prev)
359 prev->next = ls;
360 } else {
361 if (!prev)
362 *ls0 = ls; /* first unremoved */
363 prev = ls;
364 ls = ls->next;
365 }
366 }
367
368 return;
6dbe3af9
KZ
369}
370
9db54a7e
SK
371
372static int filename_equal(const char *cp, const char *dp)
6dbe3af9 373{
9db54a7e
SK
374 int i = strlen(dp);
375
db0ccb76 376 DBG(SEARCH, ul_debug("compare '%s' and '%s'", cp, dp));
9db54a7e
SK
377
378 if (dp[0] == 's' && dp[1] == '.' && filename_equal(cp, dp + 2))
379 return 1;
380 if (!strcmp(dp + i - 2, ".Z"))
381 i -= 2;
382 else if (!strcmp(dp + i - 3, ".gz"))
383 i -= 3;
384 else if (!strcmp(dp + i - 3, ".xz"))
385 i -= 3;
386 else if (!strcmp(dp + i - 4, ".bz2"))
387 i -= 4;
388 while (*cp && *dp && *cp == *dp)
389 cp++, dp++, i--;
390 if (*cp == 0 && *dp == 0)
391 return 1;
392 while (isdigit(*dp))
393 dp++;
394 if (*cp == 0 && *dp++ == '.') {
395 --i;
396 while (i > 0 && *dp)
397 if (--i, *dp++ == '.')
398 return (*dp++ == 'C' && *dp++ == 0);
399 return 1;
400 }
401 return 0;
6dbe3af9 402}
fd6b7a7f 403
9db54a7e 404static void findin(const char *dir, const char *pattern, int *count, char **wait)
12b80642 405{
9db54a7e
SK
406 DIR *dirp;
407 struct dirent *dp;
408
409 dirp = opendir(dir);
410 if (dirp == NULL)
411 return;
412
3635f53c 413 DBG(SEARCH, ul_debug("find '%s' in '%s'", pattern, dir));
9db54a7e
SK
414
415 while ((dp = readdir(dirp)) != NULL) {
416 if (!filename_equal(pattern, dp->d_name))
417 continue;
418
419 if (uflag && *count == 0)
420 xasprintf(wait, "%s/%s", dir, dp->d_name);
421
422 else if (uflag && *count == 1 && *wait) {
423 printf("%s: %s %s/%s", pattern, *wait, dir, dp->d_name);
424 free(*wait);
425 *wait = NULL;
426 } else
427 printf(" %s/%s", dir, dp->d_name);
428 ++(*count);
12b80642 429 }
9db54a7e
SK
430 closedir(dirp);
431 return;
12b80642
SK
432}
433
9db54a7e 434static void lookup(const char *pattern, struct wh_dirlist *ls, int want)
30ad62d0 435{
9db54a7e
SK
436 char patbuf[PATH_MAX];
437 int count = 0;
438 char *wait = NULL, *p;
439
9db54a7e
SK
440 /* canonicalize pattern -- remove path suffix etc. */
441 p = strrchr(pattern, '/');
442 p = p ? p + 1 : (char *) pattern;
443 strncpy(patbuf, p, PATH_MAX);
444 patbuf[PATH_MAX - 1] = '\0';
3635f53c
KZ
445
446 DBG(SEARCH, ul_debug("lookup dirs for '%s' (%s), want: %s %s %s",
447 patbuf, pattern,
448 want & BIN_DIR ? "bin" : "",
449 want & MAN_DIR ? "min" : "",
450 want & SRC_DIR ? "src" : ""));
9db54a7e
SK
451 p = strrchr(patbuf, '.');
452 if (p)
453 *p = '\0';
454
455 if (!uflag)
456 /* if -u not specified then we always print the pattern */
457 printf("%s:", patbuf);
458
459 for (; ls; ls = ls->next) {
460 if ((ls->type & want) && ls->path)
461 findin(ls->path, patbuf, &count, &wait);
6dbe3af9 462 }
9db54a7e
SK
463
464 free(wait);
465
7508d991 466 if (!uflag || count > 1)
9db54a7e
SK
467 putchar('\n');
468 return;
6dbe3af9
KZ
469}
470
3bfb9636
SK
471static void list_dirlist(struct wh_dirlist *ls)
472{
473 while (ls) {
474 if (ls->path) {
475 switch (ls->type) {
476 case BIN_DIR:
477 printf("bin: ");
478 break;
479 case MAN_DIR:
480 printf("man: ");
481 break;
482 case SRC_DIR:
483 printf("src: ");
484 break;
485 default:
486 abort();
487 }
488 printf("%s\n", ls->path);
489 }
490 ls = ls->next;
491 }
492}
493
9db54a7e 494int main(int argc, char **argv)
30ad62d0 495{
9db54a7e
SK
496 struct wh_dirlist *ls = NULL;
497 int want = ALL_DIRS;
a3d29ee0 498 int i, want_resetable = 0, opt_f_missing = 0;
9db54a7e 499
0b542a94
DB
500 setlocale(LC_ALL, "");
501 bindtextdomain(PACKAGE, LOCALEDIR);
502 textdomain(PACKAGE);
c05a80ca 503 atexit(close_stdout);
6dbe3af9 504
9db54a7e 505 if (argc == 1)
0b542a94 506 usage(stderr);
6dbe3af9 507
3635f53c
KZ
508 whereis_init_debug();
509
9db54a7e
SK
510 construct_dirlist(&ls, BIN_DIR, bindirs);
511 construct_dirlist_from_env("PATH", &ls, BIN_DIR);
512
513 construct_dirlist(&ls, MAN_DIR, mandirs);
ccec32a1
SK
514 construct_dirlist_from_env("MANPATH", &ls, MAN_DIR);
515
9db54a7e
SK
516 construct_dirlist(&ls, SRC_DIR, srcdirs);
517
518 for (i = 1; i < argc; i++) {
519 const char *arg = argv[i];
3635f53c
KZ
520 int arg_i = i;
521
522 DBG(ARGV, ul_debug("argv[%d]: %s", i, arg));
6dbe3af9 523
9db54a7e
SK
524 if (*arg != '-') {
525 lookup(arg, ls, want);
f4802c90
KZ
526 /*
527 * The lookup mask ("want") is cumulative and it's
528 * resetable only when it has been already used.
9db54a7e 529 *
f4802c90
KZ
530 * whereis -b -m foo :'foo' mask=BIN|MAN
531 * whereis -b foo bar :'foo' and 'bar' mask=BIN|MAN
532 * whereis -b foo -m bar :'foo' mask=BIN; 'bar' mask=MAN
9db54a7e 533 */
f4802c90
KZ
534 want_resetable = 1;
535 continue;
3635f53c 536 }
9db54a7e
SK
537
538 for (++arg; arg && *arg; arg++) {
3635f53c
KZ
539 DBG(ARGV, ul_debug(" arg: %s", arg));
540
9db54a7e 541 switch (*arg) {
0b542a94 542 case 'f':
a3d29ee0 543 opt_f_missing = 0;
0b542a94 544 break;
9db54a7e
SK
545 case 'u':
546 uflag = 1;
a3d29ee0 547 opt_f_missing = 0;
0b542a94 548 break;
0b542a94 549 case 'B':
9db54a7e
SK
550 if (*(arg + 1))
551 usage(stderr);
552 i++;
553 free_dirlist(&ls, BIN_DIR);
554 construct_dirlist_from_argv(
555 &ls, &i, argc, argv, BIN_DIR);
a3d29ee0 556 opt_f_missing = 1;
0b542a94 557 break;
0b542a94 558 case 'M':
9db54a7e
SK
559 if (*(arg + 1))
560 usage(stderr);
561 i++;
562 free_dirlist(&ls, MAN_DIR);
563 construct_dirlist_from_argv(
564 &ls, &i, argc, argv, MAN_DIR);
a3d29ee0 565 opt_f_missing = 1;
9db54a7e
SK
566 break;
567 case 'S':
568 if (*(arg + 1))
569 usage(stderr);
570 i++;
571 free_dirlist(&ls, SRC_DIR);
572 construct_dirlist_from_argv(
573 &ls, &i, argc, argv, SRC_DIR);
a3d29ee0 574 opt_f_missing = 1;
0b542a94 575 break;
0b542a94 576 case 'b':
f4802c90
KZ
577 if (want_resetable) {
578 want = ALL_DIRS;
579 want_resetable = 0;
580 }
9db54a7e 581 want = want == ALL_DIRS ? BIN_DIR : want | BIN_DIR;
a3d29ee0 582 opt_f_missing = 0;
9db54a7e 583 break;
0b542a94 584 case 'm':
f4802c90
KZ
585 if (want_resetable) {
586 want = ALL_DIRS;
587 want_resetable = 0;
588 }
9db54a7e 589 want = want == ALL_DIRS ? MAN_DIR : want | MAN_DIR;
a3d29ee0 590 opt_f_missing = 0;
9db54a7e
SK
591 break;
592 case 's':
f4802c90
KZ
593 if (want_resetable) {
594 want = ALL_DIRS;
595 want_resetable = 0;
596 }
9db54a7e 597 want = want == ALL_DIRS ? SRC_DIR : want | SRC_DIR;
a3d29ee0 598 opt_f_missing = 0;
9db54a7e 599 break;
3bfb9636
SK
600 case 'l':
601 list_dirlist(ls);
602 break;
0b542a94 603 case 'V':
e421313d 604 printf(UTIL_LINUX_VERSION);
0b542a94
DB
605 return EXIT_SUCCESS;
606 case 'h':
607 usage(stdout);
608 default:
609 usage(stderr);
610 }
3635f53c 611
6e834d67 612 if (arg_i < i) /* moved to the next argv[] item */
3635f53c 613 break;
4ff826f6 614 }
9db54a7e 615 }
f51be859 616
9db54a7e 617 free_dirlist(&ls, ALL_DIRS);
a3d29ee0
SK
618 if (opt_f_missing)
619 errx(EXIT_FAILURE, _("option -f is missing"));
0b542a94 620 return EXIT_SUCCESS;
6dbe3af9 621}