]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/whereis.c
tests: add asan build-sys test
[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 74
6d00cfb2
KZ
75#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(whereis)
76#include "debugobj.h"
77
9db54a7e
SK
78static char uflag = 0;
79
80/* supported types */
81enum {
82 BIN_DIR = (1 << 1),
83 MAN_DIR = (1 << 2),
84 SRC_DIR = (1 << 3),
85
86 ALL_DIRS = BIN_DIR | MAN_DIR | SRC_DIR
87};
88
89/* directories */
90struct wh_dirlist {
91 int type;
92 dev_t st_dev;
93 ino_t st_ino;
94 char *path;
95
96 struct wh_dirlist *next;
97};
98
99static const char *bindirs[] = {
30ad62d0 100 "/usr/bin",
30ad62d0 101 "/usr/sbin",
9ed21178
KZ
102 "/bin",
103 "/sbin",
c9ab7387
AH
104#if defined(MULTIARCHTRIPLET)
105 "/lib/" MULTIARCHTRIPLET,
106 "/usr/lib/" MULTIARCHTRIPLET,
107 "/usr/local/lib/" MULTIARCHTRIPLET,
108#endif
f84559ee
KZ
109 "/usr/lib",
110 "/usr/lib64",
30ad62d0
SK
111 "/etc",
112 "/usr/etc",
113 "/lib",
30ad62d0 114 "/lib64",
30ad62d0
SK
115 "/usr/games",
116 "/usr/games/bin",
117 "/usr/games/lib",
118 "/usr/emacs/etc",
119 "/usr/lib/emacs/*/etc",
120 "/usr/TeX/bin",
121 "/usr/tex/bin",
122 "/usr/interviews/bin/LINUX",
123
124 "/usr/X11R6/bin",
125 "/usr/X386/bin",
126 "/usr/bin/X11",
127 "/usr/X11/bin",
128 "/usr/X11R5/bin",
129
130 "/usr/local/bin",
131 "/usr/local/sbin",
132 "/usr/local/etc",
133 "/usr/local/lib",
134 "/usr/local/games",
135 "/usr/local/games/bin",
136 "/usr/local/emacs/etc",
137 "/usr/local/TeX/bin",
138 "/usr/local/tex/bin",
139 "/usr/local/bin/X11",
140
141 "/usr/contrib",
142 "/usr/hosts",
143 "/usr/include",
144
145 "/usr/g++-include",
146
147 "/usr/ucb",
148 "/usr/old",
149 "/usr/new",
150 "/usr/local",
151 "/usr/libexec",
152 "/usr/share",
153
154 "/opt/*/bin",
9db54a7e 155 NULL
6dbe3af9 156};
2b6fc908 157
9db54a7e 158static const char *mandirs[] = {
2b6fc908 159 "/usr/man/*",
66ee8158 160 "/usr/share/man/*",
2b6fc908
KZ
161 "/usr/X386/man/*",
162 "/usr/X11/man/*",
163 "/usr/TeX/man/*",
6dbe3af9 164 "/usr/interviews/man/mann",
db0ccb76 165 "/usr/share/info",
9db54a7e 166 NULL
6dbe3af9 167};
2b6fc908 168
9db54a7e 169static const char *srcdirs[] = {
2b6fc908
KZ
170 "/usr/src/*",
171 "/usr/src/lib/libc/*",
172 "/usr/src/lib/libc/net/*",
6dbe3af9
KZ
173 "/usr/src/ucb/pascal",
174 "/usr/src/ucb/pascal/utilities",
175 "/usr/src/undoc",
9db54a7e 176 NULL
6dbe3af9
KZ
177};
178
3635f53c
KZ
179static void whereis_init_debug(void)
180{
a15dca2f 181 __UL_INIT_DEBUG_FROM_ENV(whereis, WHEREIS_DEBUG_, 0, WHEREIS_DEBUG);
3635f53c
KZ
182}
183
184static const char *whereis_type_to_name(int type)
185{
186 switch (type) {
187 case BIN_DIR: return "bin";
188 case MAN_DIR: return "man";
189 case SRC_DIR: return "src";
190 default: return "???";
191 }
192}
9db54a7e 193
5aaa966d 194static void __attribute__((__noreturn__)) usage(void)
58513363 195{
5aaa966d
RM
196 FILE *out = stdout;
197
ec3bc7aa 198 fputs(USAGE_HEADER, out);
bde76c04 199 fprintf(out, _(" %s [options] [-BMS <dir>... -f] <name>\n"), program_invocation_short_name);
ec3bc7aa 200
451dbcfa
BS
201 fputs(USAGE_SEPARATOR, out);
202 fputs(_("Locate the binary, source, and manual-page files for a command.\n"), out);
203
ec3bc7aa
SK
204 fputs(USAGE_OPTIONS, out);
205 fputs(_(" -b search only for binaries\n"), out);
206 fputs(_(" -B <dirs> define binaries lookup path\n"), out);
db0ccb76
KZ
207 fputs(_(" -m search only for manuals and infos\n"), out);
208 fputs(_(" -M <dirs> define man and info lookup path\n"), out);
ec3bc7aa
SK
209 fputs(_(" -s search only for sources\n"), out);
210 fputs(_(" -S <dirs> define sources lookup path\n"), out);
211 fputs(_(" -f terminate <dirs> argument list\n"), out);
212 fputs(_(" -u search for unusual entries\n"), out);
213 fputs(_(" -l output effective lookup paths\n"), out);
ec3bc7aa 214
5aaa966d 215 fputs(USAGE_SEPARATOR, out);
f45f3ec3
RM
216 printf(USAGE_HELP_OPTIONS(16));
217 printf(USAGE_MAN_TAIL("whereis(1)"));
5aaa966d 218 exit(EXIT_SUCCESS);
58513363
SK
219}
220
9db54a7e 221static void dirlist_add_dir(struct wh_dirlist **ls0, int type, const char *dir)
30ad62d0 222{
9db54a7e
SK
223 struct stat st;
224 struct wh_dirlist *prev = NULL, *ls = *ls0;
32ae96ae 225
9db54a7e
SK
226 if (access(dir, R_OK) != 0)
227 return;
228 if (stat(dir, &st) != 0 || !S_ISDIR(st.st_mode))
229 return;
3635f53c 230
9db54a7e
SK
231 while (ls) {
232 if (ls->st_ino == st.st_ino &&
233 ls->st_dev == st.st_dev &&
234 ls->type == type) {
3635f53c 235 DBG(LIST, ul_debugobj(*ls0, " ignore (already in list): %s", dir));
0b542a94 236 return;
0b542a94 237 }
9db54a7e
SK
238 prev = ls;
239 ls = ls->next;
0b542a94 240 }
6dbe3af9 241
9db54a7e
SK
242
243 ls = xcalloc(1, sizeof(*ls));
244 ls->st_ino = st.st_ino;
245 ls->st_dev = st.st_dev;
246 ls->type = type;
f84559ee 247 ls->path = canonicalize_path(dir);
9db54a7e
SK
248
249 if (!*ls0)
250 *ls0 = ls; /* first in the list */
251 else {
252 assert(prev);
253 prev->next = ls; /* add to the end of the list */
0b542a94 254 }
3635f53c
KZ
255
256 DBG(LIST, ul_debugobj(*ls0, " add dir: %s", ls->path));
0b542a94 257 return;
0b542a94 258}
6dbe3af9 259
9db54a7e
SK
260/* special case for '*' in the paths */
261static void dirlist_add_subdir(struct wh_dirlist **ls, int type, const char *dir)
f51be859 262{
9db54a7e
SK
263 char buf[PATH_MAX], *d;
264 DIR *dirp;
265 struct dirent *dp;
a2761af4
KZ
266 char *postfix;
267 size_t len;
f51be859 268
a2761af4
KZ
269 postfix = strchr(dir, '*');
270 if (!postfix)
271 goto ignore;
f51be859 272
a2761af4
KZ
273 /* copy begin of the path to the buffer (part before '*') */
274 len = (postfix - dir) + 1;
275 xstrncpy(buf, dir, len);
276
277 /* remember place where to append subdirs */
278 d = buf + len - 1;
279
280 /* skip '*' */
281 postfix++;
282 if (!*postfix)
283 postfix = NULL;
f51be859 284
a2761af4 285 /* open parental dir t scan */
9db54a7e
SK
286 dirp = opendir(buf);
287 if (!dirp)
a2761af4 288 goto ignore;
9db54a7e 289
a2761af4
KZ
290 DBG(LIST, ul_debugobj(*ls, " scanning subdirs: %s [%s<subdir>%s]",
291 dir, buf, postfix ? postfix : ""));
9db54a7e
SK
292
293 while ((dp = readdir(dirp)) != NULL) {
294 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
295 continue;
a2761af4
KZ
296 if (postfix)
297 snprintf(d, PATH_MAX - len, "%s%s", dp->d_name, postfix);
298 else
299 snprintf(d, PATH_MAX - len, "%s", dp->d_name);
300
9db54a7e
SK
301 dirlist_add_dir(ls, type, buf);
302 }
303 closedir(dirp);
304 return;
a2761af4
KZ
305ignore:
306 DBG(LIST, ul_debugobj(*ls, " ignore path: %s", dir));
f51be859
DB
307}
308
9db54a7e
SK
309static void construct_dirlist_from_env(const char *env,
310 struct wh_dirlist **ls,
311 int type)
f51be859 312{
9db54a7e 313 char *key = NULL, *tok = NULL, *pathcp, *path = getenv(env);
f51be859
DB
314
315 if (!path)
316 return;
317 pathcp = xstrdup(path);
318
3635f53c
KZ
319 DBG(ENV, ul_debugobj(*ls, "construct %s dirlist from: %s",
320 whereis_type_to_name(type), path));
f51be859 321
9db54a7e
SK
322 for (tok = strtok_r(pathcp, ":", &key); tok;
323 tok = strtok_r(NULL, ":", &key))
324 dirlist_add_dir(ls, type, tok);
f51be859 325
f51be859 326 free(pathcp);
9db54a7e 327 return;
f51be859
DB
328}
329
9db54a7e
SK
330static void construct_dirlist_from_argv(struct wh_dirlist **ls,
331 int *idx,
332 int argc,
333 char *argv[],
334 int type)
f51be859 335{
3635f53c 336 int i;
9db54a7e 337
3635f53c
KZ
338 DBG(ARGV, ul_debugobj(*ls, "construct %s dirlist from argv[%d..]",
339 whereis_type_to_name(type), *idx));
340
341 for (i = *idx; i < argc; i++) {
342 if (*argv[i] == '-') /* end of the list */
343 break;
344
345 DBG(ARGV, ul_debugobj(*ls, " using argv[%d]: %s", *idx, argv[*idx]));
346 dirlist_add_dir(ls, type, argv[i]);
347 *idx = i;
9db54a7e 348 }
3635f53c 349
9db54a7e 350 return;
f51be859
DB
351}
352
9db54a7e
SK
353static void construct_dirlist(struct wh_dirlist **ls,
354 int type,
355 const char **paths)
0b542a94 356{
9db54a7e 357 size_t i;
f51be859 358
3635f53c
KZ
359 DBG(STATIC, ul_debugobj(*ls, "construct %s dirlist from static array",
360 whereis_type_to_name(type)));
6dbe3af9 361
9db54a7e
SK
362 for (i = 0; paths[i]; i++) {
363 if (!strchr(paths[i], '*'))
364 dirlist_add_dir(ls, type, paths[i]);
365 else
366 dirlist_add_subdir(ls, type, paths[i]);
367 }
368 return;
0b542a94 369}
6dbe3af9 370
9db54a7e 371static void free_dirlist(struct wh_dirlist **ls0, int type)
0b542a94 372{
9db54a7e 373 struct wh_dirlist *prev = NULL, *next, *ls = *ls0;
6dbe3af9 374
9db54a7e 375 *ls0 = NULL;
6dbe3af9 376
3635f53c 377 DBG(LIST, ul_debugobj(*ls0, "free dirlist"));
9db54a7e
SK
378
379 while (ls) {
380 if (ls->type & type) {
381 next = ls->next;
3635f53c 382 DBG(LIST, ul_debugobj(*ls0, " free: %s", ls->path));
9db54a7e
SK
383 free(ls->path);
384 free(ls);
385 ls = next;
386 if (prev)
387 prev->next = ls;
388 } else {
389 if (!prev)
390 *ls0 = ls; /* first unremoved */
391 prev = ls;
392 ls = ls->next;
393 }
394 }
395
396 return;
6dbe3af9
KZ
397}
398
9db54a7e
SK
399
400static int filename_equal(const char *cp, const char *dp)
6dbe3af9 401{
9db54a7e
SK
402 int i = strlen(dp);
403
db0ccb76 404 DBG(SEARCH, ul_debug("compare '%s' and '%s'", cp, dp));
9db54a7e
SK
405
406 if (dp[0] == 's' && dp[1] == '.' && filename_equal(cp, dp + 2))
407 return 1;
408 if (!strcmp(dp + i - 2, ".Z"))
409 i -= 2;
410 else if (!strcmp(dp + i - 3, ".gz"))
411 i -= 3;
412 else if (!strcmp(dp + i - 3, ".xz"))
413 i -= 3;
414 else if (!strcmp(dp + i - 4, ".bz2"))
415 i -= 4;
416 while (*cp && *dp && *cp == *dp)
417 cp++, dp++, i--;
418 if (*cp == 0 && *dp == 0)
419 return 1;
420 while (isdigit(*dp))
421 dp++;
422 if (*cp == 0 && *dp++ == '.') {
423 --i;
424 while (i > 0 && *dp)
425 if (--i, *dp++ == '.')
426 return (*dp++ == 'C' && *dp++ == 0);
427 return 1;
428 }
429 return 0;
6dbe3af9 430}
fd6b7a7f 431
9db54a7e 432static void findin(const char *dir, const char *pattern, int *count, char **wait)
12b80642 433{
9db54a7e
SK
434 DIR *dirp;
435 struct dirent *dp;
436
437 dirp = opendir(dir);
438 if (dirp == NULL)
439 return;
440
3635f53c 441 DBG(SEARCH, ul_debug("find '%s' in '%s'", pattern, dir));
9db54a7e
SK
442
443 while ((dp = readdir(dirp)) != NULL) {
444 if (!filename_equal(pattern, dp->d_name))
445 continue;
446
447 if (uflag && *count == 0)
448 xasprintf(wait, "%s/%s", dir, dp->d_name);
449
450 else if (uflag && *count == 1 && *wait) {
451 printf("%s: %s %s/%s", pattern, *wait, dir, dp->d_name);
452 free(*wait);
453 *wait = NULL;
454 } else
455 printf(" %s/%s", dir, dp->d_name);
456 ++(*count);
12b80642 457 }
9db54a7e
SK
458 closedir(dirp);
459 return;
12b80642
SK
460}
461
9db54a7e 462static void lookup(const char *pattern, struct wh_dirlist *ls, int want)
30ad62d0 463{
9db54a7e
SK
464 char patbuf[PATH_MAX];
465 int count = 0;
466 char *wait = NULL, *p;
467
9db54a7e
SK
468 /* canonicalize pattern -- remove path suffix etc. */
469 p = strrchr(pattern, '/');
470 p = p ? p + 1 : (char *) pattern;
aa75c5eb 471 xstrncpy(patbuf, p, PATH_MAX);
3635f53c
KZ
472
473 DBG(SEARCH, ul_debug("lookup dirs for '%s' (%s), want: %s %s %s",
474 patbuf, pattern,
475 want & BIN_DIR ? "bin" : "",
7468a8d1 476 want & MAN_DIR ? "man" : "",
3635f53c 477 want & SRC_DIR ? "src" : ""));
9db54a7e
SK
478 p = strrchr(patbuf, '.');
479 if (p)
480 *p = '\0';
481
482 if (!uflag)
483 /* if -u not specified then we always print the pattern */
484 printf("%s:", patbuf);
485
486 for (; ls; ls = ls->next) {
487 if ((ls->type & want) && ls->path)
488 findin(ls->path, patbuf, &count, &wait);
6dbe3af9 489 }
9db54a7e
SK
490
491 free(wait);
492
7508d991 493 if (!uflag || count > 1)
9db54a7e
SK
494 putchar('\n');
495 return;
6dbe3af9
KZ
496}
497
3bfb9636
SK
498static void list_dirlist(struct wh_dirlist *ls)
499{
500 while (ls) {
501 if (ls->path) {
502 switch (ls->type) {
503 case BIN_DIR:
504 printf("bin: ");
505 break;
506 case MAN_DIR:
507 printf("man: ");
508 break;
509 case SRC_DIR:
510 printf("src: ");
511 break;
512 default:
513 abort();
514 }
515 printf("%s\n", ls->path);
516 }
517 ls = ls->next;
518 }
519}
520
9db54a7e 521int main(int argc, char **argv)
30ad62d0 522{
9db54a7e
SK
523 struct wh_dirlist *ls = NULL;
524 int want = ALL_DIRS;
a3d29ee0 525 int i, want_resetable = 0, opt_f_missing = 0;
9db54a7e 526
0b542a94
DB
527 setlocale(LC_ALL, "");
528 bindtextdomain(PACKAGE, LOCALEDIR);
529 textdomain(PACKAGE);
2c308875 530 close_stdout_atexit();
6dbe3af9 531
5aaa966d
RM
532 if (argc <= 1) {
533 warnx(_("not enough arguments"));
534 errtryhelp(EXIT_FAILURE);
535 } else {
536 /* first arg may be one of our standard longopts */
537 if (!strcmp(argv[1], "--help"))
538 usage();
2c308875
KZ
539 if (!strcmp(argv[1], "--version"))
540 print_version(EXIT_SUCCESS);
5aaa966d 541 }
6dbe3af9 542
3635f53c
KZ
543 whereis_init_debug();
544
9db54a7e
SK
545 construct_dirlist(&ls, BIN_DIR, bindirs);
546 construct_dirlist_from_env("PATH", &ls, BIN_DIR);
547
548 construct_dirlist(&ls, MAN_DIR, mandirs);
ccec32a1
SK
549 construct_dirlist_from_env("MANPATH", &ls, MAN_DIR);
550
9db54a7e
SK
551 construct_dirlist(&ls, SRC_DIR, srcdirs);
552
553 for (i = 1; i < argc; i++) {
554 const char *arg = argv[i];
3635f53c
KZ
555 int arg_i = i;
556
557 DBG(ARGV, ul_debug("argv[%d]: %s", i, arg));
6dbe3af9 558
9db54a7e
SK
559 if (*arg != '-') {
560 lookup(arg, ls, want);
f4802c90
KZ
561 /*
562 * The lookup mask ("want") is cumulative and it's
11026083 563 * resettable only when it has been already used.
9db54a7e 564 *
f4802c90
KZ
565 * whereis -b -m foo :'foo' mask=BIN|MAN
566 * whereis -b foo bar :'foo' and 'bar' mask=BIN|MAN
567 * whereis -b foo -m bar :'foo' mask=BIN; 'bar' mask=MAN
9db54a7e 568 */
f4802c90
KZ
569 want_resetable = 1;
570 continue;
3635f53c 571 }
9db54a7e
SK
572
573 for (++arg; arg && *arg; arg++) {
3635f53c
KZ
574 DBG(ARGV, ul_debug(" arg: %s", arg));
575
9db54a7e 576 switch (*arg) {
0b542a94 577 case 'f':
a3d29ee0 578 opt_f_missing = 0;
0b542a94 579 break;
9db54a7e
SK
580 case 'u':
581 uflag = 1;
a3d29ee0 582 opt_f_missing = 0;
0b542a94 583 break;
0b542a94 584 case 'B':
5aaa966d
RM
585 if (*(arg + 1)) {
586 warnx(_("bad usage"));
587 errtryhelp(EXIT_FAILURE);
588 }
9db54a7e
SK
589 i++;
590 free_dirlist(&ls, BIN_DIR);
591 construct_dirlist_from_argv(
592 &ls, &i, argc, argv, BIN_DIR);
a3d29ee0 593 opt_f_missing = 1;
0b542a94 594 break;
0b542a94 595 case 'M':
5aaa966d
RM
596 if (*(arg + 1)) {
597 warnx(_("bad usage"));
598 errtryhelp(EXIT_FAILURE);
599 }
9db54a7e
SK
600 i++;
601 free_dirlist(&ls, MAN_DIR);
602 construct_dirlist_from_argv(
603 &ls, &i, argc, argv, MAN_DIR);
a3d29ee0 604 opt_f_missing = 1;
9db54a7e
SK
605 break;
606 case 'S':
5aaa966d
RM
607 if (*(arg + 1)) {
608 warnx(_("bad usage"));
609 errtryhelp(EXIT_FAILURE);
610 }
9db54a7e
SK
611 i++;
612 free_dirlist(&ls, SRC_DIR);
613 construct_dirlist_from_argv(
614 &ls, &i, argc, argv, SRC_DIR);
a3d29ee0 615 opt_f_missing = 1;
0b542a94 616 break;
0b542a94 617 case 'b':
f4802c90
KZ
618 if (want_resetable) {
619 want = ALL_DIRS;
620 want_resetable = 0;
621 }
9db54a7e 622 want = want == ALL_DIRS ? BIN_DIR : want | BIN_DIR;
a3d29ee0 623 opt_f_missing = 0;
9db54a7e 624 break;
0b542a94 625 case 'm':
f4802c90
KZ
626 if (want_resetable) {
627 want = ALL_DIRS;
628 want_resetable = 0;
629 }
9db54a7e 630 want = want == ALL_DIRS ? MAN_DIR : want | MAN_DIR;
a3d29ee0 631 opt_f_missing = 0;
9db54a7e
SK
632 break;
633 case 's':
f4802c90
KZ
634 if (want_resetable) {
635 want = ALL_DIRS;
636 want_resetable = 0;
637 }
9db54a7e 638 want = want == ALL_DIRS ? SRC_DIR : want | SRC_DIR;
a3d29ee0 639 opt_f_missing = 0;
9db54a7e 640 break;
3bfb9636
SK
641 case 'l':
642 list_dirlist(ls);
643 break;
2c308875 644
0b542a94 645 case 'V':
2c308875 646 print_version(EXIT_SUCCESS);
0b542a94 647 case 'h':
5aaa966d 648 usage();
0b542a94 649 default:
5aaa966d
RM
650 warnx(_("bad usage"));
651 errtryhelp(EXIT_FAILURE);
0b542a94 652 }
3635f53c 653
6e834d67 654 if (arg_i < i) /* moved to the next argv[] item */
3635f53c 655 break;
4ff826f6 656 }
9db54a7e 657 }
f51be859 658
9db54a7e 659 free_dirlist(&ls, ALL_DIRS);
a3d29ee0
SK
660 if (opt_f_missing)
661 errx(EXIT_FAILURE, _("option -f is missing"));
0b542a94 662 return EXIT_SUCCESS;
6dbe3af9 663}