Merge branch 'vendor/FILE'
[dragonfly.git] / gnu / usr.bin / man / man / man.c
1 /*
2  * man.c
3  *
4  * Copyright (c) 1990, 1991, John W. Eaton.
5  *
6  * You may distribute under the terms of the GNU General Public
7  * License as specified in the file COPYING that comes with the man
8  * distribution.
9  *
10  * John W. Eaton
11  * jwe@che.utexas.edu
12  * Department of Chemical Engineering
13  * The University of Texas at Austin
14  * Austin, Texas  78712
15  *
16  * $FreeBSD: src/gnu/usr.bin/man/man/man.c,v 1.37.2.10 2003/02/14 15:38:51 ru Exp $
17  */
18
19 #define MAN_MAIN
20
21 #include <sys/file.h>
22 #include <sys/stat.h>
23 #include <sys/param.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <libgen.h>
27 #include <locale.h>
28 #include <langinfo.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <signal.h>
33 #include <unistd.h>
34 #include <zlib.h>
35
36 #include "config.h"
37 #include "gripes.h"
38 #include "util.h"
39 #include "version.h"
40
41 extern char **glob_filename (char *);
42 extern char *manpath (int);
43
44 char *is_section (char *);
45 char **get_section_list (void);
46 void man_getopt (int, char **);
47 void do_apropos (char *);
48 void do_whatis (char *);
49 int man (char *);
50 void usage (void);
51 char **add_dir_to_mpath_list (char **, char *);
52 char *convert_name (char *, int);
53 char **glob_for_file (char *, char *, char *, char *, int);
54 char **make_name (char *, char *, char *, char *, int);
55 const char *get_expander (char *);
56 int display_cat_file (char *);
57 char *ultimate_source (char *, char *);
58 void add_directive (int *, const char *, char *, char *, int);
59 int parse_roff_directive (const char *, char *, char *, int);
60 char *make_roff_command (char *);
61 void cleantmp(int);
62 void set_sigs(void);
63 void restore_sigs(void);
64 int make_cat_file (char *, char *, char *, int);
65 int format_and_display (char *, char *, char *);
66 int try_section (char *, char *, char *, char *, int);
67
68 char *prognam;
69 static char *pager;
70 static const char *machine;
71 static char *manp;
72 static char *manpathlist[MAXDIRS];
73 static char *shortsec;
74 static char *longsec;
75 static char *colon_sep_section_list;
76 static char **section_list;
77 static char *roff_directive;
78 static int apropos;
79 static int whatis;
80 static int findall;
81 static int print_where;
82
83 static char *locale, *locale_codeset;
84 static const char *locale_nroff, *locale_opts;
85 static char locale_terr[3], locale_lang[3];
86 static char *man_locale;
87 static int use_man_locale;
88 static int use_original;
89 struct ltable {
90         const char *lcode;
91         const char *nroff;
92 };
93 static struct ltable ltable[] = {
94         {"KOI8-R", "koi8-r"},
95         {"ISO8859-1", "latin1"},
96         {"ISO8859-15", "latin1"},
97         {NULL, NULL}
98 };
99
100 static int troff = 0;
101
102 int debug;
103
104 static char args[] = "M:P:S:adfhkm:op:tw?";
105
106 uid_t ruid;
107 uid_t euid;
108 gid_t rgid;
109 gid_t egid;
110
111 int
112 main (int argc, char **argv)
113 {
114   int status = 0;
115   char *nextarg;
116   char *tmp;
117
118   prognam = mkprogname (argv[0]);
119   longsec = NULL;
120
121   unsetenv("IFS");
122   (void) setlocale(LC_ALL, "");
123   man_getopt (argc, argv);
124
125   if (optind == argc)
126     gripe_no_name ((char *)NULL);
127
128   section_list = get_section_list ();
129
130   if (optind == argc - 1)
131     {
132       tmp = is_section (argv[optind]);
133
134       if (tmp != NULL)
135         gripe_no_name (tmp);
136     }
137
138   ruid = getuid();
139   rgid = getgid();
140   euid = geteuid();
141   egid = getegid();
142   setreuid(-1, ruid);
143   setregid(-1, rgid);
144
145   while (optind < argc)
146     {
147       nextarg = argv[optind++];
148
149       /*
150        * See if this argument is a valid section name.  If not,
151        * is_section returns NULL.
152        */
153       tmp = is_section (nextarg);
154
155       if (tmp != NULL)
156         {
157           shortsec = tmp;
158
159           if (debug)
160             fprintf (stderr, "\nsection: %s\n", shortsec);
161
162           continue;
163         }
164
165       if (apropos) {
166         do_apropos (nextarg);
167         status = (status ? 0 : 1); /* reverts status, see below */
168       }
169       else if (whatis) {
170         do_whatis (nextarg);
171         status = (status ? 0 : 1); /* reverts status, see below */
172       }
173       else
174         {
175           status = man (nextarg);
176
177           if (status == 0)
178             gripe_not_found (nextarg, longsec);
179         }
180     }
181   return (status==0);         /* status==1 --> exit(0),
182                                  status==0 --> exit(1) */
183 }
184
185 void
186 usage (void)
187 {
188   static char usage_string[1024] = "%s, version %s\n\n";
189
190   static char s1[] =
191     "usage: %s [-adfhkotw] [section] [-M path] [-P pager] [-S list]\n\
192            [-m machine] [-p string] name ...\n\n";
193
194 static char s2[] = "  a : find all matching entries\n\
195   d : print gobs of debugging information\n\
196   f : same as whatis(1)\n\
197   h : print this help message\n\
198   k : same as apropos(1)\n";
199
200   static char s3[] = "  o : use original, non-localized manpages\n";
201
202   static char s4[] = "  t : use troff to format pages for printing\n";
203
204   static char s5[] = "  w : print location of man page(s) that would be displayed\n\n\
205   M path    : set search path for manual pages to `path'\n\
206   P pager   : use program `pager' to display pages\n\
207   S list    : colon separated section list\n\
208   m machine : search for alternate architecture man pages\n";
209
210   static char s6[] = "  p string : string tells which preprocessors to run\n\
211                e - [n]eqn(1)   p - pic(1)    t - tbl(1)\n\
212                g - grap(1)     r - refer(1)  v - vgrind(1)\n";
213
214   strcat (usage_string, s1);
215   strcat (usage_string, s2);
216   strcat (usage_string, s3);
217
218   strcat (usage_string, s4);
219
220   strcat (usage_string, s5);
221
222   strcat (usage_string, s6);
223
224   fprintf (stderr, usage_string, prognam, version, prognam);
225   exit(1);
226 }
227
228 char **
229 add_dir_to_mpath_list (char **mp, char *p)
230 {
231   int status;
232
233   status = is_directory (p);
234
235   if (status < 0 && debug)
236     {
237       fprintf (stderr, "Warning: couldn't stat file %s!\n", p);
238     }
239   else if (status == 0 && debug)
240     {
241       fprintf (stderr, "Warning: %s isn't a directory!\n", p);
242     }
243   else if (status == 1)
244     {
245       if (debug)
246         fprintf (stderr, "adding %s to manpathlist\n", p);
247
248       *mp++ = strdup (p);
249     }
250   return mp;
251 }
252
253 /*
254  * Get options from the command line and user environment.
255  */
256 void
257 man_getopt (int argc, char **argv)
258 {
259   int c;
260   char *p;
261   char *end;
262   char **mp;
263
264   while ((c = getopt (argc, argv, args)) != -1)
265     {
266       switch (c)
267         {
268         case 'M':
269           manp = strdup (optarg);
270           break;
271         case 'P':
272           pager = strdup (optarg);
273           if (setenv("PAGER", pager, 1) != 0)
274                   (void)fprintf(stderr, "setenv PAGER=%s\n", pager);
275           break;
276         case 'S':
277           colon_sep_section_list = strdup (optarg);
278           break;
279         case 'a':
280           findall++;
281           break;
282         case 'd':
283           debug++;
284           break;
285         case 'f':
286           if (troff)
287             gripe_incompatible ("-f and -t");
288           if (apropos)
289             gripe_incompatible ("-f and -k");
290           if (print_where)
291             gripe_incompatible ("-f and -w");
292           whatis++;
293           break;
294         case 'k':
295           if (troff)
296             gripe_incompatible ("-k and -t");
297           if (whatis)
298             gripe_incompatible ("-k and -f");
299           if (print_where)
300             gripe_incompatible ("-k and -w");
301           apropos++;
302           break;
303         case 'm':
304           machine = optarg;
305           break;
306         case 'o':
307           use_original++;
308           break;
309         case 'p':
310           roff_directive = strdup (optarg);
311           break;
312         case 't':
313           if (apropos)
314             gripe_incompatible ("-t and -k");
315           if (whatis)
316             gripe_incompatible ("-t and -f");
317           if (print_where)
318             gripe_incompatible ("-t and -w");
319           troff++;
320           break;
321         case 'w':
322           if (apropos)
323             gripe_incompatible ("-w and -k");
324           if (whatis)
325             gripe_incompatible ("-w and -f");
326           if (troff)
327             gripe_incompatible ("-w and -t");
328           print_where++;
329           break;
330         case 'h':
331         case '?':
332         default:
333           usage();
334           break;
335         }
336     }
337
338   /* "" intentionally used to catch error */
339   if ((locale = setlocale(LC_CTYPE, "")) != NULL)
340         locale_codeset = nl_langinfo(CODESET);
341   if (!use_original && locale != NULL && *locale_codeset != '\0' &&
342       strcmp(locale_codeset, "US-ASCII") != 0
343      ) {
344         char *tmp, *short_locale;
345         struct ltable *pltable;
346         size_t locale_len;
347
348         *locale_lang = '\0';
349         *locale_terr = '\0';
350
351         if ((short_locale = strdup(locale)) == NULL) {
352                 perror ("ctype locale strdup");
353                 exit (1);
354         }
355         if ((tmp = strchr(short_locale, '.')) != NULL)
356                 *tmp = '\0';
357
358         locale_len = strlen(short_locale);
359         tmp = strchr(short_locale, '_');
360         if (locale_len == 5 && tmp == short_locale + 2) {
361                 /* assume position 3 and 4 are not "_"; don't check */
362                 strncpy(locale_terr, short_locale + 3, 2);
363                 locale_terr[2] = '\0';
364                 strncpy(locale_lang, short_locale, 2);
365                 locale_lang[2] = '\0';
366         } else if (locale_len == 10 && tmp == short_locale + 2 &&
367             short_locale[7] == '_') {
368                 /* assume positions 3-6 and 8-9 are not "_" */
369                 strncpy(locale_terr, short_locale + 8, 2);
370                 locale_terr[2] = '\0';
371                 strncpy(locale_lang, short_locale, 2);
372                 locale_lang[2] = '\0';
373         } else {
374                 errno = EINVAL;
375                 perror ("ctype locale format");
376                 locale = NULL;
377         }
378
379         free(short_locale);
380
381         if (locale != NULL) {
382                 for (pltable = ltable; pltable->lcode != NULL; pltable++) {
383                         if (strcmp(pltable->lcode, locale_codeset) == 0) {
384                                 locale_nroff = pltable->nroff;
385                                 break;
386                         }
387                 }
388                 asprintf(&man_locale, "%s.%s", locale_lang, locale_codeset);
389         }
390   } else {
391         if (locale == NULL) {
392                 errno = EINVAL;
393                 perror ("ctype locale");
394         } else {
395                 locale = NULL;
396                 if (*locale_codeset == '\0') {
397                         errno = EINVAL;
398                         perror ("ctype codeset");
399                 }
400         }
401   }
402
403   if (pager == NULL || *pager == '\0')
404     if ((pager = getenv ("PAGER")) == NULL || *pager == '\0')
405       pager = strdup (PAGER);
406
407   if (debug)
408     fprintf (stderr, "\nusing %s as pager\n", pager);
409
410   if (machine == NULL && (machine = getenv ("MACHINE")) == NULL)
411     machine = MACHINE;
412
413   if (debug)
414     fprintf (stderr, "\nusing %s architecture\n", machine);
415
416   if (manp == NULL)
417     {
418       if ((manp = manpath (0)) == NULL)
419         gripe_manpath ();
420
421       if (debug)
422         fprintf (stderr,
423                  "\nsearch path for pages determined by manpath is\n%s\n\n",
424                  manp);
425     }
426
427   /*
428    * Expand the manpath into a list for easier handling.
429    */
430   mp = manpathlist;
431   for (p = manp; ; p = end+1)
432     {
433       if (mp == manpathlist + MAXDIRS - 1) {
434         fprintf (stderr, "Warning: too many directories in manpath, truncated!\n");
435         break;
436       }
437       if ((end = strchr (p, ':')) != NULL)
438         *end = '\0';
439
440       mp = add_dir_to_mpath_list (mp, p);
441       if (end == NULL)
442         break;
443
444       *end = ':';
445     }
446   *mp = NULL;
447 }
448
449 /*
450  * Check to see if the argument is a valid section number.  If the
451  * first character of name is a numeral, or the name matches one of
452  * the sections listed in section_list, we'll assume that it's a section.
453  * The list of sections in config.h simply allows us to specify oddly
454  * named directories like .../man3f.  Yuk.
455  */
456 char *
457 is_section (char *name)
458 {
459   char **vs;
460   char *temp, *end, *loc;
461   char **plist;
462   int x;
463
464   for (vs = section_list; *vs != NULL; vs++)
465     if ((strcmp (*vs, name) == 0)
466         || (isdigit ((unsigned char)name[0]) && strlen(name) == 1))
467       return (longsec = strdup (name));
468
469   plist = manpathlist;
470   if (isdigit ((unsigned char)name[0]))
471     {
472       while (*plist != NULL)
473         {
474           asprintf(&temp, "%s/man%c/*", *plist, name[0]);
475           plist++;
476
477           x = 0;
478           vs = glob_filename (temp);
479           if (vs == (char **)-1)
480             {
481               free (temp);
482               return NULL;
483             }
484           for ( ; *vs != NULL; vs++)
485             {
486               end = strrchr (*vs, '/');
487               if ((loc = strstr (end, name)) != NULL && loc - end > 2
488                   && *(loc-1) == '.'
489                   && (*(loc+strlen(name)) == '\0' || *(loc+strlen(name)) == '.'))
490                 {
491                   x = 1;
492                   break;
493                 }
494             }
495           free (temp);
496           if (x == 1)
497             {
498               asprintf(&temp, "%c", name[0]);
499               longsec = strdup (name);
500               return (temp);
501             }
502         }
503     }
504   return NULL;
505 }
506
507 /*
508  * Handle the apropos option.  Cheat by using another program.
509  */
510 void
511 do_apropos (char *name)
512 {
513   int len;
514   char *command;
515
516   len = strlen (APROPOS) + strlen (name) + 4;
517
518   if ((command = (char *) malloc(len)) == NULL)
519     gripe_alloc (len, "command");
520
521   sprintf (command, "%s \"%s\"", APROPOS, name);
522
523   (void) do_system_command (command);
524
525   free (command);
526 }
527
528 /*
529  * Handle the whatis option.  Cheat by using another program.
530  */
531 void
532 do_whatis (char *name)
533 {
534   int len;
535   char *command;
536
537   len = strlen (WHATIS) + strlen (name) + 4;
538
539   if ((command = (char *) malloc(len)) == NULL)
540     gripe_alloc (len, "command");
541
542   sprintf (command, "%s \"%s\"", WHATIS, name);
543
544   (void) do_system_command (command);
545
546   free (command);
547 }
548
549 /*
550  * Change a name of the form ...man/man1/name.1 to ...man/cat1/name.1
551  * or a name of the form ...man/cat1/name.1 to ...man/man1/name.1
552  */
553 char *
554 convert_name (char *name, int to_cat)
555 {
556   char *to_name;
557   char *t1;
558   char *t2 = NULL;
559
560   if (to_cat)
561     {
562       int olen = strlen(name);
563       int cextlen = strlen(COMPRESS_EXT);
564       int len = olen + cextlen;
565
566       to_name = malloc (len+1);
567       if (to_name == NULL)
568         gripe_alloc (len+1, "to_name");
569       strcpy (to_name, name);
570       olen -= cextlen;
571       /* Avoid tacking it on twice */
572       if (olen >= 1 && strcmp(name + olen, COMPRESS_EXT) != 0)
573         strcat (to_name, COMPRESS_EXT);
574     }
575   else
576     to_name = strdup (name);
577
578   t1 = strrchr (to_name, '/');
579   if (t1 != NULL)
580     {
581       *t1 = '\0';
582       t2 = strrchr (to_name, '/');
583       *t1 = '/';
584
585       /* Skip architecture part (if present). */
586       if (t2 != NULL && (t1 - t2 < 5 || *(t2 + 1) != 'm' || *(t2 + 3) != 'n'))
587         {
588           t1 = t2;
589           *t1 = '\0';
590           t2 = strrchr (to_name, '/');
591           *t1 = '/';
592         }
593     }
594
595   if (t2 == NULL)
596     gripe_converting_name (name, to_cat);
597
598   if (to_cat)
599     {
600       *(++t2) = 'c';
601       *(t2+2) = 't';
602     }
603   else
604     {
605       *(++t2) = 'm';
606       *(t2+2) = 'n';
607     }
608
609   if (debug)
610     fprintf (stderr, "to_name in convert_name () is: %s\n", to_name);
611
612   return to_name;
613 }
614
615 /*
616  * Try to find the man page corresponding to the given name.  The
617  * reason we do this with globbing is because some systems have man
618  * page directories named man3 which contain files with names like
619  * XtPopup.3Xt.  Rather than requiring that this program know about
620  * all those possible names, we simply try to match things like
621  * .../man[sect]/name[sect]*.  This is *much* easier.
622  *
623  * Note that globbing is only done when the section is unspecified.
624  */
625 char **
626 glob_for_file (char *path, char *section, char *_longsec, char *name, int cat)
627 {
628   char pathname[FILENAME_MAX];
629   char **gf;
630
631   if (_longsec == NULL)
632     _longsec = section;
633
634   if (cat)
635     snprintf (pathname, sizeof(pathname), "%s/cat%s/%s.%s*", path, section,
636        name, _longsec);
637   else
638     snprintf (pathname, sizeof(pathname), "%s/man%s/%s.%s*", path, section,
639        name, _longsec);
640
641   if (debug)
642     fprintf (stderr, "globbing %s\n", pathname);
643
644   gf = glob_filename (pathname);
645
646   if ((gf == (char **) -1 || *gf == NULL) && isdigit ((unsigned char)*section)
647       && strlen (_longsec) == 1)
648     {
649       if (cat)
650         snprintf (pathname, sizeof(pathname), "%s/cat%s/%s.%c*", path, section, name, *section);
651       else
652         snprintf (pathname, sizeof(pathname), "%s/man%s/%s.%c*", path, section, name, *section);
653
654       gf = glob_filename (pathname);
655     }
656   if ((gf == (char **) -1 || *gf == NULL) && isdigit ((unsigned char)*section)
657       && strlen (_longsec) == 1)
658     {
659       if (cat)
660         snprintf (pathname, sizeof(pathname), "%s/cat%s/%s.0*", path, section, name);
661       else
662         snprintf (pathname, sizeof(pathname), "%s/man%s/%s.0*", path, section, name);
663       if (debug)
664         fprintf (stderr, "globbing %s\n", pathname);
665       gf = glob_filename (pathname);
666     }
667   return gf;
668 }
669
670 /*
671  * Return an un-globbed name in the same form as if we were doing
672  * globbing.
673  */
674 char **
675 make_name (char *path, char *section, char *_longsec, char *name, int cat)
676 {
677   int i = 0;
678   static char *names[3];
679   char buf[FILENAME_MAX];
680
681   if (cat)
682     snprintf (buf, sizeof(buf), "%s/cat%s/%s.%s", path, section, name, _longsec);
683   else
684     snprintf (buf, sizeof(buf), "%s/man%s/%s.%s", path, section, name, _longsec);
685
686   if (access (buf, R_OK) == 0)
687     names[i++] = strdup (buf);
688
689   /*
690    * If we're given a section that looks like `3f', we may want to try
691    * file names like .../man3/foo.3f as well.  This seems a bit
692    * kludgey to me, but what the hey...
693    */
694   if (section[1] != '\0')
695     {
696       if (cat)
697         snprintf (buf, sizeof(buf), "%s/cat%c/%s.%s", path, section[0], name, section);
698       else
699         snprintf (buf, sizeof(buf), "%s/man%c/%s.%s", path, section[0], name, section);
700
701       if (access (buf, R_OK) == 0)
702         names[i++] = strdup (buf);
703     }
704
705   names[i] = NULL;
706
707   return &names[0];
708 }
709
710 const char *
711 get_expander (char *file)
712 {
713   char *end = file + (strlen (file) - 1);
714
715   while (end > file && end[-1] != '.')
716     --end;
717   if (end == file)
718     return NULL;
719 #ifdef FCAT
720   if (*end == 'F')
721     return FCAT;
722 #endif  /* FCAT */
723 #ifdef YCAT
724   if (*end == 'Y')
725     return YCAT;
726 #endif  /* YCAT */
727 #ifdef ZCAT
728   if (*end == 'Z' || !strcmp(end, "gz"))
729     return ZCAT;
730 #endif  /* ZCAT */
731   return NULL;
732 }
733
734 /*
735  * Simply display the preformatted page.
736  */
737 int
738 display_cat_file (char *file)
739 {
740   int found;
741   char command[FILENAME_MAX];
742
743   found = 0;
744
745   if (access (file, R_OK) == 0)
746     {
747       const char *expander = get_expander (file);
748
749       if (expander != NULL)
750         snprintf (command, sizeof(command), "%s %s | %s", expander, file, pager);
751       else
752         snprintf (command, sizeof(command), "%s %s", pager, file);
753
754       found = do_system_command (command);
755     }
756   return found;
757 }
758
759 /*
760  * Try to find the ultimate source file.  If the first line of the
761  * current file is not of the form
762  *
763  *      .so man3/printf.3s
764  *
765  * the input file name is returned.
766  */
767 char *
768 ultimate_source (char *name, char *path)
769 {
770   static  char buf[BUFSIZ];
771   static  char ult[FILENAME_MAX];
772
773   FILE *fp;
774   char *beg;
775   char *end;
776
777   strncpy (ult, name, sizeof(ult)-1);
778   ult[sizeof(ult)-1] = '\0';
779   strncpy (buf, name, sizeof(buf)-1);
780   ult[sizeof(buf)-1] = '\0';
781
782  next:
783
784   if ((fp = fopen (ult, "r")) == NULL)
785     return ult;
786
787   end = fgets (buf, BUFSIZ, fp);
788   fclose(fp);
789
790   if (!end || strlen (buf) < 5)
791     return ult;
792
793   beg = buf;
794   if (*beg++ == '.' && *beg++ == 's' && *beg++ == 'o')
795     {
796       while ((*beg == ' ' || *beg == '\t') && *beg != '\0')
797         beg++;
798
799       end = beg;
800       while (*end != ' ' && *end != '\t' && *end != '\n' && *end != '\0')
801         end++;
802
803       *end = '\0';
804
805       snprintf(ult, sizeof(ult), "%s/%s", path, beg);
806       snprintf(buf, sizeof(buf), "%s", ult);
807
808       goto next;
809     }
810
811   if (debug)
812     fprintf (stderr, "found ultimate source file %s\n", ult);
813
814   return ult;
815 }
816
817 void
818 add_directive (int *first, const char *d, char *file, char *buf, int bufsize)
819 {
820   if (strcmp (d, "") != 0)
821     {
822       if (*first)
823         {
824           *first = 0;
825           snprintf(buf, bufsize, "%s %s", d, file);
826         }
827       else
828         {
829           strncat (buf, " | ", bufsize-strlen(buf)-1);
830           strncat (buf, d, bufsize-strlen(buf)-1);
831         }
832     }
833 }
834
835 int
836 parse_roff_directive (const char *cp, char *file, char *buf, int bufsize)
837 {
838   char c;
839   const char *exp;
840   int first = 1;
841   int preproc_found = 0;
842   int use_col = 0;
843
844   if ((exp = get_expander(file)) != NULL)
845         add_directive (&first, exp, file, buf, bufsize);
846
847   while ((c = *cp++) != '\0')
848     {
849       switch (c)
850         {
851         case 'e':
852
853           if (debug)
854             fprintf (stderr, "found eqn(1) directive\n");
855
856           preproc_found++;
857           if (troff)
858             add_directive (&first, EQN, file, buf, bufsize);
859           else {
860             char lbuf[FILENAME_MAX];
861
862             snprintf(lbuf, sizeof(lbuf), "%s -T%s", NEQN,
863                      locale_opts == NULL ? "ascii" : locale_opts);
864             add_directive (&first, lbuf, file, buf, bufsize);
865           }
866
867           break;
868
869         case 'g':
870
871           if (debug)
872             fprintf (stderr, "found grap(1) directive\n");
873
874           preproc_found++;
875           add_directive (&first, GRAP, file, buf, bufsize);
876
877           break;
878
879         case 'p':
880
881           if (debug)
882             fprintf (stderr, "found pic(1) directive\n");
883
884           preproc_found++;
885           add_directive (&first, PIC, file, buf, bufsize);
886
887           break;
888
889         case 't':
890
891           if (debug)
892             fprintf (stderr, "found tbl(1) directive\n");
893
894           preproc_found++;
895           use_col++;
896           add_directive (&first, TBL, file, buf, bufsize);
897           break;
898
899         case 'v':
900
901           if (debug)
902             fprintf (stderr, "found vgrind(1) directive\n");
903
904           add_directive (&first, VGRIND, file, buf, bufsize);
905           break;
906
907         case 'r':
908
909           if (debug)
910             fprintf (stderr, "found refer(1) directive\n");
911
912           add_directive (&first, REFER, file, buf, bufsize);
913           break;
914
915         case ' ':
916         case '\t':
917         case '\n':
918
919           goto done;
920
921         default:
922
923           return -1;
924         }
925     }
926
927  done:
928
929   if (troff)
930     add_directive (&first, TROFF, file, buf, bufsize);
931   else
932     {
933       char lbuf[FILENAME_MAX];
934
935       snprintf(lbuf, sizeof(lbuf), "%s -T%s%s%s", NROFF,
936                locale_opts == NULL ? "ascii" : locale_opts,
937                use_man_locale ? " -dlocale=" : "",
938                use_man_locale ? man_locale : "");
939             add_directive (&first, lbuf, file, buf, bufsize);
940     }
941   if (use_col && !troff)
942       add_directive (&first, COL, file, buf, bufsize);
943
944   if (preproc_found)
945     return 0;
946   else
947     return 1;
948 }
949
950 char *
951 make_roff_command (char *file)
952 {
953   gzFile fp;
954   char line [BUFSIZ];
955   static char buf [BUFSIZ];
956   int status;
957   char *cp;
958
959   if (roff_directive != NULL)
960     {
961       if (debug)
962         fprintf (stderr, "parsing directive from command line\n");
963
964       status = parse_roff_directive (roff_directive, file, buf, sizeof(buf));
965
966       if (status == 0)
967         return buf;
968
969       if (status == -1)
970         gripe_roff_command_from_command_line ();
971     }
972
973   if ((fp = gzopen (file, "r")) != NULL)
974     {
975       cp = line;
976       gzgets (fp, line, BUFSIZ);
977       gzclose(fp);
978       if (*cp++ == '\'' && *cp++ == '\\' && *cp++ == '"' && *cp++ == ' ')
979         {
980           if (debug)
981             fprintf (stderr, "parsing directive from file\n");
982
983           status = parse_roff_directive (cp, file, buf, sizeof(buf));
984
985           if (status == 0)
986             return buf;
987
988           if (status == -1)
989             gripe_roff_command_from_file (file);
990         }
991     }
992   else
993     {
994       /*
995        * Is there really any point in continuing to look for
996        * preprocessor options if we can't even read the man page source?
997        */
998       gripe_reading_man_file (file);
999       return NULL;
1000     }
1001
1002   if ((cp = getenv ("MANROFFSEQ")) != NULL)
1003     {
1004       if (debug)
1005         fprintf (stderr, "parsing directive from environment\n");
1006
1007       status = parse_roff_directive (cp, file, buf, sizeof(buf));
1008
1009       if (status == 0)
1010         return buf;
1011
1012       if (status == -1)
1013         gripe_roff_command_from_env ();
1014     }
1015
1016   if (debug)
1017     fprintf (stderr, "using default preprocessor sequence\n");
1018
1019   status = parse_roff_directive ("t", file, buf, sizeof(buf));
1020   if (status >= 0)
1021     return buf;
1022   else          /* can't happen */
1023     return NULL;
1024 }
1025
1026 sig_t ohup, oint, oquit, oterm;
1027 static char temp[FILENAME_MAX];
1028
1029 void
1030 cleantmp(int signo __unused)
1031 {
1032         unlink(temp);
1033         exit(1);
1034 }
1035
1036 void
1037 set_sigs(void)
1038 {
1039   ohup = signal(SIGHUP, cleantmp);
1040   oint = signal(SIGINT, cleantmp);
1041   oquit = signal(SIGQUIT, cleantmp);
1042   oterm = signal(SIGTERM, cleantmp);
1043 }
1044
1045 void
1046 restore_sigs(void)
1047 {
1048   signal(SIGHUP, ohup);
1049   signal(SIGINT, oint);
1050   signal(SIGQUIT, oquit);
1051   signal(SIGTERM, oterm);
1052 }
1053
1054 /*
1055  * Try to format the man page and create a new formatted file.  Return
1056  * 1 for success and 0 for failure.
1057  */
1058 int
1059 make_cat_file (char *path, char *man_file, char *cat_file, int manid)
1060 {
1061   int s, f;
1062   FILE *fp, *pp;
1063   char *roff_command;
1064   char command[FILENAME_MAX];
1065
1066   roff_command = make_roff_command (man_file);
1067   if (roff_command == NULL)
1068       return 0;
1069
1070   snprintf(temp, sizeof(temp), "%s.tmpXXXXXX", cat_file);
1071   if ((f = mkstemp(temp)) >= 0 && (fp = fdopen(f, "w")) != NULL)
1072     {
1073       set_sigs();
1074
1075       if (fchmod (f, CATMODE) < 0) {
1076         perror("fchmod");
1077         unlink(temp);
1078         restore_sigs();
1079         fclose(fp);
1080         return 0;
1081       } else if (debug)
1082         fprintf (stderr, "mode of %s is now %o\n", temp, CATMODE);
1083
1084       snprintf (command, sizeof(command), "(cd %s ; %s | %s)", path,
1085                 roff_command, COMPRESSOR);
1086       fprintf (stderr, "Formatting page, please wait...");
1087       fflush(stderr);
1088
1089       if (debug)
1090         fprintf (stderr, "\ntrying command: %s\n", command);
1091       else {
1092         if (manid) {
1093           setreuid(-1, ruid);
1094           setregid(-1, rgid);
1095         }
1096         if ((pp = popen(command, "r")) == NULL) {
1097           s = errno;
1098           fprintf(stderr, "Failed.\n");
1099           errno = s;
1100           perror("popen");
1101           if (manid) {
1102             setreuid(-1, euid);
1103             setregid(-1, egid);
1104           }
1105           unlink(temp);
1106           restore_sigs();
1107           fclose(fp);
1108           return 0;
1109         }
1110         if (manid) {
1111           setreuid(-1, euid);
1112           setregid(-1, egid);
1113         }
1114
1115         f = 0;
1116         while ((s = getc(pp)) != EOF) {
1117           putc(s, fp); f++;
1118         }
1119
1120         if (!f || ((s = pclose(pp)) == -1)) {
1121           s = errno;
1122           fprintf(stderr, "Failed.\n");
1123           errno = s;
1124           perror("pclose");
1125           unlink(temp);
1126           restore_sigs();
1127           fclose(fp);
1128           return 0;
1129         }
1130
1131         if (s != 0) {
1132           fprintf(stderr, "Failed.\n");
1133           gripe_system_command(s);
1134           unlink(temp);
1135           restore_sigs();
1136           fclose(fp);
1137           return 0;
1138         }
1139       }
1140
1141       if (debug)
1142         unlink(temp);
1143       else if (rename(temp, cat_file) == -1) {
1144         s = errno;
1145         fprintf(stderr,
1146                  "\nHmm!  Can't seem to rename %s to %s, check permissions on man dir!\n",
1147                  temp, cat_file);
1148         errno = s;
1149         perror("rename");
1150         unlink(temp);
1151         restore_sigs();
1152         fclose(fp);
1153         return 0;
1154       }
1155       restore_sigs();
1156
1157       if (fclose(fp)) {
1158         s = errno;
1159         if (!debug)
1160           unlink(cat_file);
1161         fprintf(stderr, "Failed.\n");
1162         errno = s;
1163         perror("fclose");
1164         return 0;
1165       }
1166
1167       if (debug) {
1168         fprintf(stderr, "No output, debug mode.\n");
1169         return 0;
1170       }
1171
1172       fprintf(stderr, "Done.\n");
1173
1174       return 1;
1175     }
1176   else
1177     {
1178       if (f >= 0) {
1179         s = errno;
1180         unlink(temp);
1181         errno = s;
1182       }
1183       if (debug) {
1184         s = errno;
1185         fprintf (stderr, "Couldn't open %s for writing.\n", temp);
1186         errno = s;
1187       }
1188       if (f >= 0) {
1189         perror("fdopen");
1190         close(f);
1191       }
1192
1193       return 0;
1194     }
1195 }
1196
1197 /*
1198  * Try to format the man page source and save it, then display it.  If
1199  * that's not possible, try to format the man page source and display
1200  * it directly.
1201  *
1202  * Note that we've already been handed the name of the ultimate source
1203  * file at this point.
1204  */
1205 int
1206 format_and_display (char *path, char *man_file, char *cat_file)
1207 {
1208   int status;
1209   int found;
1210   char *roff_command;
1211   char command[FILENAME_MAX];
1212
1213   found = 0;
1214
1215   if (access (man_file, R_OK) != 0)
1216     return 0;
1217
1218   if (troff)
1219     {
1220       roff_command = make_roff_command (man_file);
1221       if (roff_command == NULL)
1222         return 0;
1223       else
1224         snprintf (command, sizeof(command), "(cd %s ; %s)", path, roff_command);
1225
1226       found = do_system_command (command);
1227     }
1228   else
1229     {
1230       if (cat_file == NULL)
1231         goto format_and_display;
1232
1233       status = is_newer (man_file, cat_file);
1234       if (debug)
1235         fprintf (stderr, "status from is_newer() = %d\n", status);
1236
1237       if (status == 1 || status == -2)
1238         {
1239           /*
1240            * Cat file is out of date.  Try to format and save it.
1241            */
1242           if (print_where)
1243             {
1244               printf ("%s\n", man_file);
1245               found++;
1246             }
1247           else
1248             {
1249
1250               setreuid(-1, euid);
1251               setregid(-1, egid);
1252               found = make_cat_file (path, man_file, cat_file, 1);
1253               setreuid(-1, ruid);
1254               setregid(-1, rgid);
1255
1256               if (!found)
1257                 {
1258                   /* Try again as real user - see note below.
1259                      By running with
1260                        effective group (user) ID == real group (user) ID
1261                      except for the call above, I believe the problems
1262                      of reading private man pages is avoided.  */
1263                   found = make_cat_file (path, man_file, cat_file, 0);
1264                 }
1265               if (found)
1266                 {
1267                   /*
1268                    * Creating the cat file worked.  Now just display it.
1269                    */
1270                   (void) display_cat_file (cat_file);
1271                 }
1272               else
1273                 {
1274                   /*
1275                    * Couldn't create cat file.  Just format it and
1276                    * display it through the pager.
1277                    */
1278 format_and_display:
1279                   roff_command = make_roff_command (man_file);
1280                   if (roff_command == NULL)
1281                     return 0;
1282                   else
1283                     snprintf (command, sizeof(command), "(cd %s ; %s | %s)", path,
1284                              roff_command, pager);
1285
1286                   found = do_system_command (command);
1287                 }
1288             }
1289         }
1290       else if (access (cat_file, R_OK) == 0)
1291         {
1292           /*
1293            * Formatting not necessary.  Cat file is newer than source
1294            * file, or source file is not present but cat file is.
1295            */
1296           if (print_where)
1297             {
1298               printf ("%s (source: %s)\n", cat_file, man_file);
1299               found++;
1300             }
1301           else
1302             {
1303               found = display_cat_file (cat_file);
1304             }
1305         }
1306     }
1307   return found;
1308 }
1309
1310 /*
1311  * See if the preformatted man page or the source exists in the given
1312  * section.
1313  */
1314 int
1315 try_section (char *path, char *section, char *_longsec, char *name, int glob)
1316 {
1317   int found = 0;
1318   int to_cat;
1319   int cat;
1320   char **names;
1321   char **np;
1322   static int arch_search;
1323   char buf[FILENAME_MAX];
1324
1325   if (!arch_search)
1326     {
1327       snprintf(buf, sizeof(buf), "%s/man%s/%s", path, section, machine);
1328       if (is_directory (buf) == 1)
1329         {
1330           snprintf(buf, sizeof(buf), "%s/%s", machine, name);
1331           arch_search++;
1332           found = try_section (path, section, _longsec, buf, glob);
1333           arch_search--;
1334           if (found && !findall)   /* only do this architecture... */
1335             return found;
1336         }
1337     }
1338
1339   if (debug)
1340     {
1341       if (glob)
1342         fprintf (stderr, "trying section %s with globbing\n", section);
1343       else
1344         fprintf (stderr, "trying section %s without globbing\n", section);
1345     }
1346
1347 #ifndef NROFF_MISSING
1348   /*
1349    * Look for man page source files.
1350    */
1351   cat = 0;
1352   if (glob)
1353     names = glob_for_file (path, section, _longsec, name, cat);
1354   else
1355     names = make_name (path, section, _longsec, name, cat);
1356
1357   if (names == (char **) -1 || *names == NULL)
1358     /*
1359      * No files match.  See if there's a preformatted page around that
1360      * we can display.
1361      */
1362 #endif /* NROFF_MISSING */
1363     {
1364       if (!troff)
1365         {
1366           cat = 1;
1367           if (glob)
1368             names = glob_for_file (path, section, _longsec, name, cat);
1369           else
1370             names = make_name (path, section, _longsec, name, cat);
1371
1372           if (names != (char **) -1 && *names != NULL)
1373             {
1374               for (np = names; *np != NULL; np++)
1375                 {
1376                   if (print_where)
1377                     {
1378                       printf ("%s\n", *np);
1379                       found++;
1380                     }
1381                   else
1382                     {
1383                       found += display_cat_file (*np);
1384                     }
1385                 }
1386             }
1387         }
1388     }
1389 #ifndef NROFF_MISSING
1390   else
1391     {
1392       for (np = names; *np != NULL; np++)
1393         {
1394           char *cat_file = NULL;
1395           char *man_file;
1396
1397           man_file = ultimate_source (*np, path);
1398
1399           if (!troff)
1400             {
1401               to_cat = 1;
1402
1403               cat_file = convert_name (man_file, to_cat);
1404
1405               if (debug)
1406                 fprintf (stderr, "will try to write %s if needed\n", cat_file);
1407             }
1408
1409           found += format_and_display (path, man_file, cat_file);
1410         }
1411     }
1412 #endif /* NROFF_MISSING */
1413   return found;
1414 }
1415
1416 /*
1417  * Search for manual pages.
1418  *
1419  * If preformatted manual pages are supported, look for the formatted
1420  * file first, then the man page source file.  If they both exist and
1421  * the man page source file is newer, or only the source file exists,
1422  * try to reformat it and write the results in the cat directory.  If
1423  * it is not possible to write the cat file, simply format and display
1424  * the man file.
1425  *
1426  * If preformatted pages are not supported, or the troff option is
1427  * being used, only look for the man page source file.
1428  *
1429  */
1430 int
1431 man (char *name)
1432 {
1433   int found;
1434   int glob;
1435   char **mp;
1436   char **sp;
1437   int l_found;
1438   char buf[PATH_MAX];
1439
1440   found = 0;
1441
1442   fflush (stdout);
1443   if (strchr(name, '/'))  /* Treat name as file name if it's a path */
1444     {
1445       struct stat st;
1446
1447       if (debug)
1448         fprintf(stderr, "Trying as file name\n");
1449
1450       /*
1451        * We need to pass an absolute file name to format_and_display,
1452        * or it will run into problems later.
1453        */
1454       realpath(name, buf);
1455
1456       if (stat(buf, &st) == 0)
1457         found += format_and_display(dirname(buf), buf, NULL);
1458     }
1459   else if (shortsec != NULL)
1460     {
1461       for (mp = manpathlist; *mp != NULL; mp++)
1462         {
1463           if (debug)
1464             fprintf (stderr, "\nsearching in %s\n", *mp);
1465
1466           glob = 1;
1467
1468           l_found = 0;
1469           if (locale != NULL) {
1470             locale_opts = locale_nroff;
1471             use_man_locale = 1;
1472             if (*locale_lang != '\0' && *locale_terr != '\0') {
1473               snprintf(buf, sizeof(buf), "%s/%s_%s.%s", *mp,
1474                        locale_lang, locale_terr, locale_codeset);
1475               if (is_directory (buf) == 1)
1476                 l_found = try_section (buf, shortsec, longsec, name, glob);
1477             }
1478             if (!l_found) {
1479               if (*locale_lang != '\0') {
1480                 snprintf(buf, sizeof(buf), "%s/%s.%s", *mp,
1481                          locale_lang, locale_codeset);
1482                 if (is_directory (buf) == 1)
1483                   l_found = try_section (buf, shortsec, longsec, name, glob);
1484               }
1485               use_man_locale = 0;
1486               if (!l_found && strcmp(locale_lang, "en") != 0) {
1487                 snprintf(buf, sizeof(buf), "%s/en.%s", *mp,
1488                          locale_codeset);
1489                 if (is_directory (buf) == 1)
1490                   l_found = try_section (buf, shortsec, longsec, name, glob);
1491               }
1492             }
1493             locale_opts = NULL;
1494             use_man_locale = 0;
1495           }
1496           if (!l_found) {
1497           found += try_section (*mp, shortsec, longsec, name, glob);
1498           } else
1499             found += l_found;
1500
1501           if (found && !findall)   /* i.e. only do this section... */
1502             return found;
1503         }
1504     }
1505   else
1506     {
1507       for (sp = section_list; *sp != NULL; sp++)
1508         {
1509           for (mp = manpathlist; *mp != NULL; mp++)
1510             {
1511               if (debug)
1512                 fprintf (stderr, "\nsearching in %s\n", *mp);
1513
1514               glob = 1;
1515
1516               l_found = 0;
1517               if (locale != NULL) {
1518                 locale_opts = locale_nroff;
1519                 use_man_locale = 1;
1520                 if (*locale_lang != '\0' && *locale_terr != '\0') {
1521                   snprintf(buf, sizeof(buf), "%s/%s_%s.%s", *mp,
1522                            locale_lang, locale_terr, locale_codeset);
1523                   if (is_directory (buf) == 1)
1524                     l_found = try_section (buf, *sp, longsec, name, glob);
1525                 }
1526                 if (!l_found) {
1527                   if (*locale_lang != '\0') {
1528                     snprintf(buf, sizeof(buf), "%s/%s.%s", *mp,
1529                              locale_lang, locale_codeset);
1530                     if (is_directory (buf) == 1)
1531                       l_found = try_section (buf, *sp, longsec, name, glob);
1532                   }
1533                   use_man_locale = 0;
1534                   if (!l_found && strcmp(locale_lang, "en") != 0) {
1535                     snprintf(buf, sizeof(buf), "%s/en.%s", *mp,
1536                              locale_codeset);
1537                     if (is_directory (buf) == 1)
1538                       l_found = try_section (buf, *sp, longsec, name, glob);
1539                   }
1540                 }
1541                 locale_opts = NULL;
1542                 use_man_locale = 0;
1543               }
1544               if (!l_found) {
1545               found += try_section (*mp, *sp, longsec, name, glob);
1546               } else
1547                 found += l_found;
1548
1549               if (found && !findall)   /* i.e. only do this section... */
1550                 return found;
1551             }
1552         }
1553     }
1554   return found;
1555 }
1556
1557 char **
1558 get_section_list (void)
1559 {
1560   int i;
1561   char *p;
1562   char *end;
1563 #define TMP_SECTION_LIST_SIZE 100
1564   static char *tmp_section_list[TMP_SECTION_LIST_SIZE];
1565
1566   if (colon_sep_section_list == NULL)
1567     {
1568       if ((p = getenv ("MANSECT")) == NULL)
1569         {
1570           return __DECONST(char **, std_sections);
1571         }
1572       else
1573         {
1574           colon_sep_section_list = strdup (p);
1575         }
1576     }
1577
1578   i = 0;
1579   for (p = colon_sep_section_list; i < TMP_SECTION_LIST_SIZE ; p = end+1) 
1580     {
1581       if ((end = strchr (p, ':')) != NULL)
1582         *end = '\0';
1583
1584       tmp_section_list[i++] = strdup (p);
1585
1586       if (end == NULL)
1587         break;
1588     }
1589
1590   tmp_section_list [i] = NULL;
1591   return tmp_section_list;
1592 }