]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/catman/catman.c
Merge of libpcap 0.8.3 from tcpdump.org.
[FreeBSD/FreeBSD.git] / usr.bin / catman / catman.c
1 /*-
2  * Copyright (c) 2002 John Rochester
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  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/param.h>
35
36 #include <ctype.h>
37 #include <dirent.h>
38 #include <err.h>
39 #include <fcntl.h>
40 #include <locale.h>
41 #include <langinfo.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #define DEFAULT_MANPATH         "/usr/share/man"
48
49 #define TOP_LEVEL_DIR   0       /* signifies a top-level man directory */
50 #define MAN_SECTION_DIR 1       /* signifies a man section directory */
51 #define UNKNOWN         2       /* signifies an unclassifiable directory */
52
53 #define TEST_EXISTS     0x01
54 #define TEST_DIR        0x02
55 #define TEST_FILE       0x04
56 #define TEST_READABLE   0x08
57 #define TEST_WRITABLE   0x10
58 #define TEST_EXECUTABLE 0x20
59
60 static int verbose;             /* -v flag: be verbose with warnings */
61 static int pretend;             /* -n, -p flags: print out what would be done
62                                    instead of actually doing it */
63 static int force;               /* -f flag: force overwriting all cat pages */
64 static int rm_junk;             /* -r flag: remove garbage pages */
65 static char *locale;            /* user's locale if -L is used */
66 static char *lang_locale;       /* short form of locale */
67 static int exit_code;           /* exit code to use when finished */
68
69 /*
70  * -T argument for nroff
71  */
72 static const char *nroff_device = "ascii";
73
74 /*
75  * Mapping from locale to nroff device
76  */
77 static const char *locale_device[] = {
78         "KOI8-R",       "koi8-r",
79         "ISO8859-1",    "latin1",
80         "ISO8859-15",   "latin1",
81         NULL
82 };
83
84 #define BZ2_CMD         "bzip2"
85 #define BZ2_EXT         ".bz2"
86 #define BZ2CAT_CMD      "bz"
87 #define GZ_CMD          "gzip"
88 #define GZ_EXT          ".gz"
89 #define GZCAT_CMD       "z"
90 enum Ziptype {NONE, BZIP, GZIP};
91
92 static uid_t uid;
93 static gid_t gids[NGROUPS_MAX];
94 static int ngids;
95 static int starting_dir;
96 static char tmp_file[MAXPATHLEN];
97 struct stat test_st;
98
99 /*
100  * A hashtable is an array of chains composed of this entry structure.
101  */
102 struct hash_entry {
103         ino_t           inode_number;
104         dev_t           device_number;
105         const char      *data;
106         struct hash_entry *next;
107 };
108
109 #define HASHTABLE_ALLOC 16384   /* allocation for hashtable (power of 2) */
110 #define HASH_MASK       (HASHTABLE_ALLOC - 1)
111
112 static struct hash_entry *visited[HASHTABLE_ALLOC];
113 static struct hash_entry *links[HASHTABLE_ALLOC];
114
115 /*
116  * Inserts a string into a hashtable keyed by inode & device number.
117  */
118 static void
119 insert_hashtable(struct hash_entry **table,
120     ino_t inode_number,
121     dev_t device_number,
122     const char *data)
123 {
124         struct hash_entry *new_entry;
125         struct hash_entry **chain;
126
127         new_entry = (struct hash_entry *) malloc(sizeof(struct hash_entry));
128         if (new_entry == NULL)
129                 err(1, "can't insert into hashtable");
130         chain = &table[inode_number & HASH_MASK];
131         new_entry->inode_number = inode_number;
132         new_entry->device_number = device_number;
133         new_entry->data = data;
134         new_entry->next = *chain;
135         *chain = new_entry;
136 }
137
138 /*
139  * Finds a string in a hashtable keyed by inode & device number.
140  */
141 static const char *
142 find_hashtable(struct hash_entry **table,
143     ino_t inode_number,
144     dev_t device_number)
145 {
146         struct hash_entry *chain;
147
148         chain = table[inode_number & HASH_MASK];
149         while (chain != NULL) {
150                 if (chain->inode_number == inode_number &&
151                     chain->device_number == device_number)
152                         return chain->data;
153                 chain = chain->next;
154         }
155         return NULL;
156 }
157
158 static void
159 trap_signal(int sig __unused)
160 {
161         if (tmp_file[0] != '\0')
162                 unlink(tmp_file);
163         exit(1);
164 }
165
166 /*
167  * Deals with junk files in the man or cat section directories.
168  */
169 static void
170 junk(const char *mandir, const char *name, const char *reason)
171 {
172         if (verbose)
173                 fprintf(stderr, "%s/%s: %s\n", mandir, name, reason);
174         if (rm_junk) {
175                 fprintf(stderr, "rm %s/%s\n", mandir, name);
176                 if (!pretend && unlink(name) < 0)
177                         warn("%s/%s", mandir, name);
178         }
179 }
180
181 /*
182  * Returns TOP_LEVEL_DIR for .../man, MAN_SECTION_DIR for .../manXXX,
183  * and UNKNOWN for everything else.
184  */
185 static int
186 directory_type(char *dir)
187 {
188         char *p;
189
190         for (;;) {
191                 p = strrchr(dir, '/');
192                 if (p == NULL || p[1] != '\0')
193                         break;
194                 *p = '\0';
195         }
196         if (p == NULL)
197                 p = dir;
198         else
199                 p++;
200         if (strncmp(p, "man", 3) == 0) {
201                 p += 3;
202                 if (*p == '\0')
203                         return TOP_LEVEL_DIR;
204                 while (isalnum((unsigned char)*p) || *p == '_') {
205                         if (*++p == '\0')
206                                 return MAN_SECTION_DIR;
207                 }
208         }
209         return UNKNOWN;
210 }
211
212 /*
213  * Tests whether the given file name (without a preceding path)
214  * is a proper man page name (like "mk-amd-map.8.gz").
215  * Only alphanumerics and '_' are allowed after the last '.' and
216  * the last '.' can't be the first or last characters.
217  */
218 static int
219 is_manpage_name(char *name)
220 {
221         char *lastdot = NULL;
222         char *n = name;
223
224         while (*n != '\0') {
225                 if (!isalnum((unsigned char)*n)) {
226                         switch (*n) {
227                         case '_':
228                                 break;
229                         case '-':
230                         case '+':
231                         case '[':
232                         case ':':
233                                 lastdot = NULL;
234                                 break;
235                         case '.':
236                                 lastdot = n;
237                                 break;
238                         default:
239                                 return 0;
240                         }
241                 }
242                 n++;
243         }
244         return lastdot > name && lastdot + 1 < n;
245 }
246
247 static int
248 is_bzipped(char *name)
249 {
250         int len = strlen(name);
251         return len >= 5 && strcmp(&name[len - 4], BZ2_EXT) == 0;
252 }
253
254 static int
255 is_gzipped(char *name)
256 {
257         int len = strlen(name);
258         return len >= 4 && strcmp(&name[len - 3], GZ_EXT) == 0;
259 }
260
261 /*
262  * Converts manXXX to catXXX.
263  */
264 static char *
265 get_cat_section(char *section)
266 {
267         char *cat_section;
268
269         cat_section = strdup(section);
270         strncpy(cat_section, "cat", 3);
271         return cat_section;
272 }
273
274 /*
275  * Converts .../man/manXXX to .../man.
276  */
277 static char *
278 get_mandir(char *section)
279 {
280         char *slash;
281         char *mandir;
282
283         slash = strrchr(section, '/');
284         mandir = (char *) malloc(slash - section + 1);
285         strncpy(mandir, section, slash - section);
286         mandir[slash - section] = '\0';
287         return mandir;
288 }
289
290 /*
291  * Tests to see if the given directory has already been visited.
292  */
293 static int
294 already_visited(char *mandir, char *dir, int count_visit)
295 {
296         struct stat st;
297
298         if (stat(dir, &st) < 0) {
299                 if (mandir != NULL)
300                         warn("%s/%s", mandir, dir);
301                 else
302                         warn("%s", dir);
303                 exit_code = 1;
304                 return 1;
305         }
306         if (find_hashtable(visited, st.st_ino, st.st_dev) != NULL) {
307                 if (mandir != NULL)
308                         warnx("already visited %s/%s", mandir, dir);
309                 else
310                         warnx("already visited %s", dir);
311                 return 1;
312         }
313         if (count_visit)
314                 insert_hashtable(visited, st.st_ino, st.st_dev, "");
315         return 0;
316 }
317
318 /*
319  * Returns a set of TEST_* bits describing a file's type and permissions.
320  * If mod_time isn't NULL, it will contain the file's modification time.
321  */
322 static int
323 test_path(char *name, time_t *mod_time)
324 {
325         int result;
326
327         if (stat(name, &test_st) < 0)
328                 return 0;
329         result = TEST_EXISTS;
330         if (mod_time != NULL)
331                 *mod_time = test_st.st_mtime;
332         if (S_ISDIR(test_st.st_mode))
333                 result |= TEST_DIR;
334         else if (S_ISREG(test_st.st_mode))
335                 result |= TEST_FILE;
336         if (test_st.st_uid == uid) {
337                 test_st.st_mode >>= 6;
338         } else {
339                 int i;
340                 for (i = 0; i < ngids; i++) {
341                         if (test_st.st_gid == gids[i]) {
342                                 test_st.st_mode >>= 3;
343                                 break;
344                         }
345                 }
346         }
347         if (test_st.st_mode & S_IROTH)
348                 result |= TEST_READABLE;
349         if (test_st.st_mode & S_IWOTH)
350                 result |= TEST_WRITABLE;
351         if (test_st.st_mode & S_IXOTH)
352                 result |= TEST_EXECUTABLE;
353         return result;
354 }
355
356 /*
357  * Checks whether a file is a symbolic link.
358  */
359 static int
360 is_symlink(char *path)
361 {
362         struct stat st;
363
364         return lstat(path, &st) >= 0 && S_ISLNK(st.st_mode);
365 }
366
367 /*
368  * Tests to see if the given directory can be written to.
369  */
370 static void
371 check_writable(char *mandir)
372 {
373         if (verbose && !(test_path(mandir, NULL) & TEST_WRITABLE))
374                 fprintf(stderr, "%s: not writable - will only be able to write to existing cat directories\n", mandir);
375 }
376
377 /*
378  * If the directory exists, attempt to make it writable, otherwise
379  * attempt to create it.
380  */
381 static int
382 make_writable_dir(char *mandir, char *dir)
383 {
384         int test;
385
386         if ((test = test_path(dir, NULL)) != 0) {
387                 if (!(test & TEST_WRITABLE) && chmod(dir, 0755) < 0) {
388                         warn("%s/%s: chmod", mandir, dir);
389                         exit_code = 1;
390                         return 0;
391                 }
392         } else {
393                 if (verbose || pretend)
394                         fprintf(stderr, "mkdir %s\n", dir);
395                 if (!pretend) {
396                         unlink(dir);
397                         if (mkdir(dir, 0755) < 0) {
398                                 warn("%s/%s: mkdir", mandir, dir);
399                                 exit_code = 1;
400                                 return 0;
401                         }
402                 }
403         }
404         return 1;
405 }
406
407 /*
408  * Processes a single man page source by using nroff to create
409  * the preformatted cat page.
410  */
411 static void
412 process_page(char *mandir, char *src, char *cat, enum Ziptype zipped)
413 {
414         int src_test, cat_test;
415         time_t src_mtime, cat_mtime;
416         char cmd[MAXPATHLEN];
417         dev_t src_dev;
418         ino_t src_ino;
419         const char *link_name;
420
421         src_test = test_path(src, &src_mtime);
422         if (!(src_test & (TEST_FILE|TEST_READABLE))) {
423                 if (!(src_test & TEST_DIR)) {
424                         warnx("%s/%s: unreadable", mandir, src);
425                         exit_code = 1;
426                         if (rm_junk && is_symlink(src))
427                                 junk(mandir, src, "bogus symlink");
428                 }
429                 return;
430         }
431         src_dev = test_st.st_dev;
432         src_ino = test_st.st_ino;
433         cat_test = test_path(cat, &cat_mtime);
434         if (cat_test & (TEST_FILE|TEST_READABLE)) {
435                 if (!force && cat_mtime >= src_mtime) {
436                         if (verbose) {
437                                 fprintf(stderr, "\t%s/%s: up to date\n",
438                                     mandir, src);
439                         }
440                         return;
441                 }
442         }
443         /*
444          * Is the man page a link to one we've already processed?
445          */
446         if ((link_name = find_hashtable(links, src_ino, src_dev)) != NULL) {
447                 if (verbose || pretend) {
448                         fprintf(stderr, "%slink %s -> %s\n",
449                             verbose ? "\t" : "", cat, link_name);
450                 }
451                 if (!pretend)
452                         link(link_name, cat);
453                 return;
454         }
455         insert_hashtable(links, src_ino, src_dev, strdup(cat));
456         if (verbose || pretend) {
457                 fprintf(stderr, "%sformat %s -> %s\n",
458                     verbose ? "\t" : "", src, cat);
459                 if (pretend)
460                         return;
461         }
462         snprintf(tmp_file, sizeof tmp_file, "%s.tmp", cat);
463         snprintf(cmd, sizeof cmd,
464             "%scat %s | tbl | nroff -T%s -man | col | %s > %s.tmp",
465             zipped == BZIP ? BZ2CAT_CMD : zipped == GZIP ? GZCAT_CMD : "",
466             src, nroff_device,
467             zipped == BZIP ? BZ2_CMD : zipped == GZIP ? GZ_CMD : "cat",
468             cat);
469         if (system(cmd) != 0)
470                 err(1, "formatting pipeline");
471         if (rename(tmp_file, cat) < 0)
472                 warn("%s", cat);
473         tmp_file[0] = '\0';
474 }
475
476 /*
477  * Scan the man section directory for pages and process each one,
478  * then check for junk in the corresponding cat section.
479  */
480 static void
481 scan_section(char *mandir, char *section, char *cat_section)
482 {
483         struct dirent **entries;
484         char **expected = NULL;
485         int npages;
486         int nexpected = 0;
487         int i, e;
488         enum Ziptype zipped;
489         char *page_name;
490         char page_path[MAXPATHLEN];
491         char cat_path[MAXPATHLEN];
492         char zip_path[MAXPATHLEN];
493
494         /*
495          * scan the man section directory for pages
496          */
497         npages = scandir(section, &entries, NULL, alphasort);
498         if (npages < 0) {
499                 warn("%s/%s", mandir, section);
500                 exit_code = 1;
501                 return;
502         }
503         if (verbose || rm_junk) {
504                 /*
505                  * Maintain a list of all cat pages that should exist,
506                  * corresponding to existing man pages.
507                  */
508                 expected = (char **) calloc(npages, sizeof(char *));
509         }
510         for (i = 0; i < npages; free(entries[i++])) {
511                 page_name = entries[i]->d_name;
512                 snprintf(page_path, sizeof page_path, "%s/%s", section,
513                     page_name);
514                 if (!is_manpage_name(page_name)) {
515                         if (!(test_path(page_path, NULL) & TEST_DIR)) {
516                                 junk(mandir, page_path,
517                                     "invalid man page name");
518                         }
519                         continue;
520                 }
521                 zipped = is_bzipped(page_name) ? BZIP :
522                     is_gzipped(page_name) ? GZIP : NONE;
523                 if (zipped != NONE) {
524                         snprintf(cat_path, sizeof cat_path, "%s/%s",
525                             cat_section, page_name);
526                         if (expected != NULL)
527                                 expected[nexpected++] = strdup(page_name);
528                         process_page(mandir, page_path, cat_path, zipped);
529                 } else {
530                         /*
531                          * We've got an uncompressed man page,
532                          * check to see if there's a (preferred)
533                          * compressed one.
534                          */
535                         snprintf(zip_path, sizeof zip_path, "%s%s",
536                             page_path, GZ_EXT);
537                         if (test_path(zip_path, NULL) != 0) {
538                                 junk(mandir, page_path,
539                                     "man page unused due to existing " GZ_EXT);
540                         } else {
541                                 if (verbose) {
542                                         fprintf(stderr,
543                                                 "warning, %s is uncompressed\n",
544                                                 page_path);
545                                 }
546                                 snprintf(cat_path, sizeof cat_path, "%s/%s",
547                                     cat_section, page_name);
548                                 if (expected != NULL) {
549                                         asprintf(&expected[nexpected++],
550                                             "%s", page_name);
551                                 }
552                                 process_page(mandir, page_path, cat_path, NONE);
553                         }
554                 }
555         }
556         free(entries);
557         if (expected == NULL)
558             return;
559         /*
560          * scan cat sections for junk
561          */
562         npages = scandir(cat_section, &entries, NULL, alphasort);
563         e = 0;
564         for (i = 0; i < npages; free(entries[i++])) {
565                 const char *junk_reason;
566                 int cmp = 1;
567
568                 page_name = entries[i]->d_name;
569                 if (strcmp(page_name, ".") == 0 || strcmp(page_name, "..") == 0)
570                         continue;
571                 /*
572                  * Keep the index into the expected cat page list
573                  * ahead of the name we've found.
574                  */
575                 while (e < nexpected &&
576                     (cmp = strcmp(page_name, expected[e])) > 0)
577                         free(expected[e++]);
578                 if (cmp == 0)
579                         continue;
580                 /* we have an unexpected page */
581                 if (!is_manpage_name(page_name)) {
582                         junk_reason = "invalid cat page name";
583                 } else if (!is_gzipped(page_name) && e + 1 < nexpected &&
584                     strncmp(page_name, expected[e + 1], strlen(page_name)) == 0 &&
585                     strlen(expected[e + 1]) == strlen(page_name) + 3) {
586                         junk_reason = "cat page unused due to existing " GZ_EXT;
587                 } else
588                         junk_reason = "cat page without man page";
589                 snprintf(cat_path, sizeof cat_path, "%s/%s", cat_section,
590                     page_name);
591                 junk(mandir, cat_path, junk_reason);
592         }
593         free(entries);
594         while (e < nexpected)
595                 free(expected[e++]);
596         free(expected);
597 }
598
599
600 /*
601  * Processes a single man section.
602  */
603 static void
604 process_section(char *mandir, char *section)
605 {
606         char *cat_section;
607
608         if (already_visited(mandir, section, 1))
609                 return;
610         if (verbose)
611                 fprintf(stderr, "  section %s\n", section);
612         cat_section = get_cat_section(section);
613         if (make_writable_dir(mandir, cat_section))
614                 scan_section(mandir, section, cat_section);
615 }
616
617 static int
618 select_sections(struct dirent *entry)
619 {
620         return directory_type(entry->d_name) == MAN_SECTION_DIR;
621 }
622
623 /*
624  * Processes a single top-level man directory.  If section isn't NULL,
625  * it will only process that section sub-directory, otherwise it will
626  * process all of them.
627  */
628 static void
629 process_mandir(char *dir_name, char *section)
630 {
631         fchdir(starting_dir);
632         if (already_visited(NULL, dir_name, section == NULL))
633                 return;
634         check_writable(dir_name);
635         if (verbose)
636                 fprintf(stderr, "man directory %s\n", dir_name);
637         if (pretend)
638                 fprintf(stderr, "cd %s\n", dir_name);
639         if (chdir(dir_name) < 0) {
640                 warn("%s: chdir", dir_name);
641                 exit_code = 1;
642                 return;
643         }
644         if (section != NULL) {
645                 process_section(dir_name, section);
646         } else {
647                 struct dirent **entries;
648                 int nsections;
649                 int i;
650
651                 nsections = scandir(".", &entries, select_sections, alphasort);
652                 if (nsections < 0) {
653                         warn("%s", dir_name);
654                         exit_code = 1;
655                         return;
656                 }
657                 for (i = 0; i < nsections; i++) {
658                         process_section(dir_name, entries[i]->d_name);
659                         free(entries[i]);
660                 }
661                 free(entries);
662         }
663 }
664
665 /*
666  * Processes one argument, which may be a colon-separated list of
667  * directories.
668  */
669 static void
670 process_argument(const char *arg)
671 {
672         char *dir;
673         char *mandir;
674         char *parg;
675
676         parg = strdup(arg);
677         if (parg == NULL)
678                 err(1, "out of memory");
679         while ((dir = strsep(&parg, ":")) != NULL) {
680                 switch (directory_type(dir)) {
681                 case TOP_LEVEL_DIR:
682                         if (locale != NULL) {
683                                 asprintf(&mandir, "%s/%s", dir, locale);
684                                 process_mandir(mandir, NULL);
685                                 free(mandir);
686                                 if (lang_locale != NULL) {
687                                         asprintf(&mandir, "%s/%s", dir,
688                                             lang_locale);
689                                         process_mandir(mandir, NULL);
690                                         free(mandir);
691                                 }
692                         } else {
693                                 process_mandir(dir, NULL);
694                         }
695                         break;
696                 case MAN_SECTION_DIR: {
697                         mandir = get_mandir(dir);
698                         process_mandir(mandir, dir);
699                         break;
700                         }
701                 default:
702                         warnx("%s: directory name not in proper man form", dir);
703                         exit_code = 1;
704                 }
705         }
706         free(parg);
707 }
708
709 static void
710 determine_locale(void)
711 {
712         char *sep;
713
714         if ((locale = setlocale(LC_CTYPE, "")) == NULL) {
715                 warnx("-L option used, but no locale found\n");
716                 return;
717         }
718         sep = strchr(locale, '_');
719         if (sep != NULL && isupper((unsigned char)sep[1])
720                         && isupper((unsigned char)sep[2])) {
721                 asprintf(&lang_locale, "%.*s%s", sep - locale, locale, &sep[3]);
722         }
723         sep = nl_langinfo(CODESET);
724         if (sep != NULL && *sep != '\0' && strcmp(sep, "US-ASCII") != 0) {
725                 int i;
726
727                 for (i = 0; locale_device[i] != NULL; i += 2) {
728                         if (strcmp(sep, locale_device[i]) == 0) {
729                                 nroff_device = locale_device[i + 1];
730                                 break;
731                         }
732                 }
733         }
734         if (verbose) {
735                 if (lang_locale != NULL)
736                         fprintf(stderr, "short locale is %s\n", lang_locale);
737                 fprintf(stderr, "nroff device is %s\n", nroff_device);
738         }
739 }
740
741 static void
742 usage(void)
743 {
744         fprintf(stderr, "usage: %s [-fLnrv] [directories...]\n", getprogname());
745         exit(1);
746 }
747
748 int
749 main(int argc, char **argv)
750 {
751         int opt;
752
753         if ((uid = getuid()) == 0) {
754                 fprintf(stderr, "don't run %s as root, use:\n   echo", argv[0]);
755                 for (optind = 0; optind < argc; optind++) {
756                         fprintf(stderr, " %s", argv[optind]);
757                 }
758                 fprintf(stderr, " | nice -5 su -m man\n");
759                 exit(1);
760         }
761         while ((opt = getopt(argc, argv, "vnfLrh")) != -1) {
762                 switch (opt) {
763                 case 'f':
764                         force++;
765                         break;
766                 case 'L':
767                         determine_locale();
768                         break;
769                 case 'n':
770                         pretend++;
771                         break;
772                 case 'r':
773                         rm_junk++;
774                         break;
775                 case 'v':
776                         verbose++;
777                         break;
778                 default:
779                         usage();
780                         /* NOTREACHED */
781                 }
782         }
783         ngids = getgroups(NGROUPS_MAX, gids);
784         if ((starting_dir = open(".", 0)) < 0) {
785                 err(1, ".");
786         }
787         umask(022);
788         signal(SIGINT, trap_signal);
789         signal(SIGHUP, trap_signal);
790         signal(SIGQUIT, trap_signal);
791         signal(SIGTERM, trap_signal);
792         if (optind == argc) {
793                 const char *manpath = getenv("MANPATH");
794                 if (manpath == NULL)
795                         manpath = DEFAULT_MANPATH;
796                 process_argument(manpath);
797         } else {
798                 while (optind < argc)
799                         process_argument(argv[optind++]);
800         }
801         exit(exit_code);
802 }