Remove unneeded inclusions of <sys/cdefs.h> throughout the tree.
[games.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
347         *locale_lang = '\0';
348         *locale_terr = '\0';
349
350         if ((short_locale = strdup(locale)) == NULL) {
351                 perror ("ctype locale strdup");
352                 exit (1);
353         }
354         if ((tmp = strchr(short_locale, '.')) != NULL)
355                 *tmp = '\0';
356
357         if (strlen(short_locale) == 2)
358                 strcpy(locale_lang, short_locale);
359         else if ((tmp = strchr(short_locale, '_')) == NULL ||
360                  tmp != short_locale + 2 ||
361                  strlen(tmp + 1) != 2
362                 ) {
363                 errno = EINVAL;
364                 perror ("ctype locale format");
365                 locale = NULL;
366         } else {
367                 strncpy(locale_terr, short_locale + 3, 2);
368                 locale_terr[2] = '\0';
369                 strncpy(locale_lang, short_locale, 2);
370                 locale_lang[2] = '\0';
371         }
372
373         free(short_locale);
374
375         if (locale != NULL) {
376                 for (pltable = ltable; pltable->lcode != NULL; pltable++) {
377                         if (strcmp(pltable->lcode, locale_codeset) == 0) {
378                                 locale_nroff = pltable->nroff;
379                                 break;
380                         }
381                 }
382                 asprintf(&man_locale, "%s.%s", locale_lang, locale_codeset);
383         }
384   } else {
385         if (locale == NULL) {
386                 errno = EINVAL;
387                 perror ("ctype locale");
388         } else {
389                 locale = NULL;
390                 if (*locale_codeset == '\0') {
391                         errno = EINVAL;
392                         perror ("ctype codeset");
393                 }
394         }
395   }
396
397   if (pager == NULL || *pager == '\0')
398     if ((pager = getenv ("PAGER")) == NULL || *pager == '\0')
399       pager = strdup (PAGER);
400
401   if (debug)
402     fprintf (stderr, "\nusing %s as pager\n", pager);
403
404   if (machine == NULL && (machine = getenv ("MACHINE")) == NULL)
405     machine = MACHINE;
406
407   if (debug)
408     fprintf (stderr, "\nusing %s architecture\n", machine);
409
410   if (manp == NULL)
411     {
412       if ((manp = manpath (0)) == NULL)
413         gripe_manpath ();
414
415       if (debug)
416         fprintf (stderr,
417                  "\nsearch path for pages determined by manpath is\n%s\n\n",
418                  manp);
419     }
420
421   /*
422    * Expand the manpath into a list for easier handling.
423    */
424   mp = manpathlist;
425   for (p = manp; ; p = end+1)
426     {
427       if (mp == manpathlist + MAXDIRS - 1) {
428         fprintf (stderr, "Warning: too many directories in manpath, truncated!\n");
429         break;
430       }
431       if ((end = strchr (p, ':')) != NULL)
432         *end = '\0';
433
434       mp = add_dir_to_mpath_list (mp, p);
435       if (end == NULL)
436         break;
437
438       *end = ':';
439     }
440   *mp = NULL;
441 }
442
443 /*
444  * Check to see if the argument is a valid section number.  If the
445  * first character of name is a numeral, or the name matches one of
446  * the sections listed in section_list, we'll assume that it's a section.
447  * The list of sections in config.h simply allows us to specify oddly
448  * named directories like .../man3f.  Yuk.
449  */
450 char *
451 is_section (char *name)
452 {
453   char **vs;
454   char *temp, *end, *loc;
455   char **plist;
456   int x;
457
458   for (vs = section_list; *vs != NULL; vs++)
459     if ((strcmp (*vs, name) == 0)
460         || (isdigit ((unsigned char)name[0]) && strlen(name) == 1))
461       return (longsec = strdup (name));
462
463   plist = manpathlist;
464   if (isdigit ((unsigned char)name[0]))
465     {
466       while (*plist != NULL)
467         {
468           asprintf(&temp, "%s/man%c/*", *plist, name[0]);
469           plist++;
470
471           x = 0;
472           vs = glob_filename (temp);
473           if (vs == (char **)-1)
474             {
475               free (temp);
476               return NULL;
477             }
478           for ( ; *vs != NULL; vs++)
479             {
480               end = strrchr (*vs, '/');
481               if ((loc = strstr (end, name)) != NULL && loc - end > 2
482                   && *(loc-1) == '.'
483                   && (*(loc+strlen(name)) == '\0' || *(loc+strlen(name)) == '.'))
484                 {
485                   x = 1;
486                   break;
487                 }
488             }
489           free (temp);
490           if (x == 1)
491             {
492               asprintf(&temp, "%c", name[0]);
493               longsec = strdup (name);
494               return (temp);
495             }
496         }
497     }
498   return NULL;
499 }
500
501 /*
502  * Handle the apropos option.  Cheat by using another program.
503  */
504 void
505 do_apropos (char *name)
506 {
507   int len;
508   char *command;
509
510   len = strlen (APROPOS) + strlen (name) + 4;
511
512   if ((command = (char *) malloc(len)) == NULL)
513     gripe_alloc (len, "command");
514
515   sprintf (command, "%s \"%s\"", APROPOS, name);
516
517   (void) do_system_command (command);
518
519   free (command);
520 }
521
522 /*
523  * Handle the whatis option.  Cheat by using another program.
524  */
525 void
526 do_whatis (char *name)
527 {
528   int len;
529   char *command;
530
531   len = strlen (WHATIS) + strlen (name) + 4;
532
533   if ((command = (char *) malloc(len)) == NULL)
534     gripe_alloc (len, "command");
535
536   sprintf (command, "%s \"%s\"", WHATIS, name);
537
538   (void) do_system_command (command);
539
540   free (command);
541 }
542
543 /*
544  * Change a name of the form ...man/man1/name.1 to ...man/cat1/name.1
545  * or a name of the form ...man/cat1/name.1 to ...man/man1/name.1
546  */
547 char *
548 convert_name (char *name, int to_cat)
549 {
550   char *to_name;
551   char *t1;
552   char *t2 = NULL;
553
554   if (to_cat)
555     {
556       int olen = strlen(name);
557       int cextlen = strlen(COMPRESS_EXT);
558       int len = olen + cextlen;
559
560       to_name = malloc (len+1);
561       if (to_name == NULL)
562         gripe_alloc (len+1, "to_name");
563       strcpy (to_name, name);
564       olen -= cextlen;
565       /* Avoid tacking it on twice */
566       if (olen >= 1 && strcmp(name + olen, COMPRESS_EXT) != 0)
567         strcat (to_name, COMPRESS_EXT);
568     }
569   else
570     to_name = strdup (name);
571
572   t1 = strrchr (to_name, '/');
573   if (t1 != NULL)
574     {
575       *t1 = '\0';
576       t2 = strrchr (to_name, '/');
577       *t1 = '/';
578
579       /* Skip architecture part (if present). */
580       if (t2 != NULL && (t1 - t2 < 5 || *(t2 + 1) != 'm' || *(t2 + 3) != 'n'))
581         {
582           t1 = t2;
583           *t1 = '\0';
584           t2 = strrchr (to_name, '/');
585           *t1 = '/';
586         }
587     }
588
589   if (t2 == NULL)
590     gripe_converting_name (name, to_cat);
591
592   if (to_cat)
593     {
594       *(++t2) = 'c';
595       *(t2+2) = 't';
596     }
597   else
598     {
599       *(++t2) = 'm';
600       *(t2+2) = 'n';
601     }
602
603   if (debug)
604     fprintf (stderr, "to_name in convert_name () is: %s\n", to_name);
605
606   return to_name;
607 }
608
609 /*
610  * Try to find the man page corresponding to the given name.  The
611  * reason we do this with globbing is because some systems have man
612  * page directories named man3 which contain files with names like
613  * XtPopup.3Xt.  Rather than requiring that this program know about
614  * all those possible names, we simply try to match things like
615  * .../man[sect]/name[sect]*.  This is *much* easier.
616  *
617  * Note that globbing is only done when the section is unspecified.
618  */
619 char **
620 glob_for_file (char *path, char *section, char *_longsec, char *name, int cat)
621 {
622   char pathname[FILENAME_MAX];
623   char **gf;
624
625   if (_longsec == NULL)
626     _longsec = section;
627
628   if (cat)
629     snprintf (pathname, sizeof(pathname), "%s/cat%s/%s.%s*", path, section,
630        name, _longsec);
631   else
632     snprintf (pathname, sizeof(pathname), "%s/man%s/%s.%s*", path, section,
633        name, _longsec);
634
635   if (debug)
636     fprintf (stderr, "globbing %s\n", pathname);
637
638   gf = glob_filename (pathname);
639
640   if ((gf == (char **) -1 || *gf == NULL) && isdigit ((unsigned char)*section)
641       && strlen (_longsec) == 1)
642     {
643       if (cat)
644         snprintf (pathname, sizeof(pathname), "%s/cat%s/%s.%c*", path, section, name, *section);
645       else
646         snprintf (pathname, sizeof(pathname), "%s/man%s/%s.%c*", path, section, name, *section);
647
648       gf = glob_filename (pathname);
649     }
650   if ((gf == (char **) -1 || *gf == NULL) && isdigit ((unsigned char)*section)
651       && strlen (_longsec) == 1)
652     {
653       if (cat)
654         snprintf (pathname, sizeof(pathname), "%s/cat%s/%s.0*", path, section, name);
655       else
656         snprintf (pathname, sizeof(pathname), "%s/man%s/%s.0*", path, section, name);
657       if (debug)
658         fprintf (stderr, "globbing %s\n", pathname);
659       gf = glob_filename (pathname);
660     }
661   return gf;
662 }
663
664 /*
665  * Return an un-globbed name in the same form as if we were doing
666  * globbing.
667  */
668 char **
669 make_name (char *path, char *section, char *_longsec, char *name, int cat)
670 {
671   int i = 0;
672   static char *names[3];
673   char buf[FILENAME_MAX];
674
675   if (cat)
676     snprintf (buf, sizeof(buf), "%s/cat%s/%s.%s", path, section, name, _longsec);
677   else
678     snprintf (buf, sizeof(buf), "%s/man%s/%s.%s", path, section, name, _longsec);
679
680   if (access (buf, R_OK) == 0)
681     names[i++] = strdup (buf);
682
683   /*
684    * If we're given a section that looks like `3f', we may want to try
685    * file names like .../man3/foo.3f as well.  This seems a bit
686    * kludgey to me, but what the hey...
687    */
688   if (section[1] != '\0')
689     {
690       if (cat)
691         snprintf (buf, sizeof(buf), "%s/cat%c/%s.%s", path, section[0], name, section);
692       else
693         snprintf (buf, sizeof(buf), "%s/man%c/%s.%s", path, section[0], name, section);
694
695       if (access (buf, R_OK) == 0)
696         names[i++] = strdup (buf);
697     }
698
699   names[i] = NULL;
700
701   return &names[0];
702 }
703
704 const char *
705 get_expander (char *file)
706 {
707   char *end = file + (strlen (file) - 1);
708
709   while (end > file && end[-1] != '.')
710     --end;
711   if (end == file)
712     return NULL;
713 #ifdef FCAT
714   if (*end == 'F')
715     return FCAT;
716 #endif  /* FCAT */
717 #ifdef YCAT
718   if (*end == 'Y')
719     return YCAT;
720 #endif  /* YCAT */
721 #ifdef ZCAT
722   if (*end == 'Z' || !strcmp(end, "gz"))
723     return ZCAT;
724 #endif  /* ZCAT */
725   return NULL;
726 }
727
728 /*
729  * Simply display the preformatted page.
730  */
731 int
732 display_cat_file (char *file)
733 {
734   int found;
735   char command[FILENAME_MAX];
736
737   found = 0;
738
739   if (access (file, R_OK) == 0)
740     {
741       const char *expander = get_expander (file);
742
743       if (expander != NULL)
744         snprintf (command, sizeof(command), "%s %s | %s", expander, file, pager);
745       else
746         snprintf (command, sizeof(command), "%s %s", pager, file);
747
748       found = do_system_command (command);
749     }
750   return found;
751 }
752
753 /*
754  * Try to find the ultimate source file.  If the first line of the
755  * current file is not of the form
756  *
757  *      .so man3/printf.3s
758  *
759  * the input file name is returned.
760  */
761 char *
762 ultimate_source (char *name, char *path)
763 {
764   static  char buf[BUFSIZ];
765   static  char ult[FILENAME_MAX];
766
767   FILE *fp;
768   char *beg;
769   char *end;
770
771   strncpy (ult, name, sizeof(ult)-1);
772   ult[sizeof(ult)-1] = '\0';
773   strncpy (buf, name, sizeof(buf)-1);
774   ult[sizeof(buf)-1] = '\0';
775
776  next:
777
778   if ((fp = fopen (ult, "r")) == NULL)
779     return ult;
780
781   end = fgets (buf, BUFSIZ, fp);
782   fclose(fp);
783
784   if (!end || strlen (buf) < 5)
785     return ult;
786
787   beg = buf;
788   if (*beg++ == '.' && *beg++ == 's' && *beg++ == 'o')
789     {
790       while ((*beg == ' ' || *beg == '\t') && *beg != '\0')
791         beg++;
792
793       end = beg;
794       while (*end != ' ' && *end != '\t' && *end != '\n' && *end != '\0')
795         end++;
796
797       *end = '\0';
798
799       snprintf(ult, sizeof(ult), "%s/%s", path, beg);
800       snprintf(buf, sizeof(buf), "%s", ult);
801
802       goto next;
803     }
804
805   if (debug)
806     fprintf (stderr, "found ultimate source file %s\n", ult);
807
808   return ult;
809 }
810
811 void
812 add_directive (int *first, const char *d, char *file, char *buf, int bufsize)
813 {
814   if (strcmp (d, "") != 0)
815     {
816       if (*first)
817         {
818           *first = 0;
819           snprintf(buf, bufsize, "%s %s", d, file);
820         }
821       else
822         {
823           strncat (buf, " | ", bufsize-strlen(buf)-1);
824           strncat (buf, d, bufsize-strlen(buf)-1);
825         }
826     }
827 }
828
829 int
830 parse_roff_directive (const char *cp, char *file, char *buf, int bufsize)
831 {
832   char c;
833   const char *exp;
834   int first = 1;
835   int preproc_found = 0;
836   int use_col = 0;
837
838   if ((exp = get_expander(file)) != NULL)
839         add_directive (&first, exp, file, buf, bufsize);
840
841   while ((c = *cp++) != '\0')
842     {
843       switch (c)
844         {
845         case 'e':
846
847           if (debug)
848             fprintf (stderr, "found eqn(1) directive\n");
849
850           preproc_found++;
851           if (troff)
852             add_directive (&first, EQN, file, buf, bufsize);
853           else {
854             char lbuf[FILENAME_MAX];
855
856             snprintf(lbuf, sizeof(lbuf), "%s -T%s", NEQN,
857                      locale_opts == NULL ? "ascii" : locale_opts);
858             add_directive (&first, lbuf, file, buf, bufsize);
859           }
860
861           break;
862
863         case 'g':
864
865           if (debug)
866             fprintf (stderr, "found grap(1) directive\n");
867
868           preproc_found++;
869           add_directive (&first, GRAP, file, buf, bufsize);
870
871           break;
872
873         case 'p':
874
875           if (debug)
876             fprintf (stderr, "found pic(1) directive\n");
877
878           preproc_found++;
879           add_directive (&first, PIC, file, buf, bufsize);
880
881           break;
882
883         case 't':
884
885           if (debug)
886             fprintf (stderr, "found tbl(1) directive\n");
887
888           preproc_found++;
889           use_col++;
890           add_directive (&first, TBL, file, buf, bufsize);
891           break;
892
893         case 'v':
894
895           if (debug)
896             fprintf (stderr, "found vgrind(1) directive\n");
897
898           add_directive (&first, VGRIND, file, buf, bufsize);
899           break;
900
901         case 'r':
902
903           if (debug)
904             fprintf (stderr, "found refer(1) directive\n");
905
906           add_directive (&first, REFER, file, buf, bufsize);
907           break;
908
909         case ' ':
910         case '\t':
911         case '\n':
912
913           goto done;
914
915         default:
916
917           return -1;
918         }
919     }
920
921  done:
922
923   if (troff)
924     add_directive (&first, TROFF, file, buf, bufsize);
925   else
926     {
927       char lbuf[FILENAME_MAX];
928
929       snprintf(lbuf, sizeof(lbuf), "%s -T%s%s%s", NROFF,
930                locale_opts == NULL ? "ascii" : locale_opts,
931                use_man_locale ? " -dlocale=" : "",
932                use_man_locale ? man_locale : "");
933             add_directive (&first, lbuf, file, buf, bufsize);
934     }
935   if (use_col && !troff)
936       add_directive (&first, COL, file, buf, bufsize);
937
938   if (preproc_found)
939     return 0;
940   else
941     return 1;
942 }
943
944 char *
945 make_roff_command (char *file)
946 {
947   gzFile fp;
948   char line [BUFSIZ];
949   static char buf [BUFSIZ];
950   int status;
951   char *cp;
952
953   if (roff_directive != NULL)
954     {
955       if (debug)
956         fprintf (stderr, "parsing directive from command line\n");
957
958       status = parse_roff_directive (roff_directive, file, buf, sizeof(buf));
959
960       if (status == 0)
961         return buf;
962
963       if (status == -1)
964         gripe_roff_command_from_command_line ();
965     }
966
967   if ((fp = gzopen (file, "r")) != NULL)
968     {
969       cp = line;
970       gzgets (fp, line, BUFSIZ);
971       gzclose(fp);
972       if (*cp++ == '\'' && *cp++ == '\\' && *cp++ == '"' && *cp++ == ' ')
973         {
974           if (debug)
975             fprintf (stderr, "parsing directive from file\n");
976
977           status = parse_roff_directive (cp, file, buf, sizeof(buf));
978
979           if (status == 0)
980             return buf;
981
982           if (status == -1)
983             gripe_roff_command_from_file (file);
984         }
985     }
986   else
987     {
988       /*
989        * Is there really any point in continuing to look for
990        * preprocessor options if we can't even read the man page source?
991        */
992       gripe_reading_man_file (file);
993       return NULL;
994     }
995
996   if ((cp = getenv ("MANROFFSEQ")) != NULL)
997     {
998       if (debug)
999         fprintf (stderr, "parsing directive from environment\n");
1000
1001       status = parse_roff_directive (cp, file, buf, sizeof(buf));
1002
1003       if (status == 0)
1004         return buf;
1005
1006       if (status == -1)
1007         gripe_roff_command_from_env ();
1008     }
1009
1010   if (debug)
1011     fprintf (stderr, "using default preprocessor sequence\n");
1012
1013   status = parse_roff_directive ("t", file, buf, sizeof(buf));
1014   if (status >= 0)
1015     return buf;
1016   else          /* can't happen */
1017     return NULL;
1018 }
1019
1020 sig_t ohup, oint, oquit, oterm;
1021 static char temp[FILENAME_MAX];
1022
1023 void
1024 cleantmp(int signo __unused)
1025 {
1026         unlink(temp);
1027         exit(1);
1028 }
1029
1030 void
1031 set_sigs(void)
1032 {
1033   ohup = signal(SIGHUP, cleantmp);
1034   oint = signal(SIGINT, cleantmp);
1035   oquit = signal(SIGQUIT, cleantmp);
1036   oterm = signal(SIGTERM, cleantmp);
1037 }
1038
1039 void
1040 restore_sigs(void)
1041 {
1042   signal(SIGHUP, ohup);
1043   signal(SIGINT, oint);
1044   signal(SIGQUIT, oquit);
1045   signal(SIGTERM, oterm);
1046 }
1047
1048 /*
1049  * Try to format the man page and create a new formatted file.  Return
1050  * 1 for success and 0 for failure.
1051  */
1052 int
1053 make_cat_file (char *path, char *man_file, char *cat_file, int manid)
1054 {
1055   int s, f;
1056   FILE *fp, *pp;
1057   char *roff_command;
1058   char command[FILENAME_MAX];
1059
1060   roff_command = make_roff_command (man_file);
1061   if (roff_command == NULL)
1062       return 0;
1063
1064   snprintf(temp, sizeof(temp), "%s.tmpXXXXXX", cat_file);
1065   if ((f = mkstemp(temp)) >= 0 && (fp = fdopen(f, "w")) != NULL)
1066     {
1067       set_sigs();
1068
1069       if (fchmod (f, CATMODE) < 0) {
1070         perror("fchmod");
1071         unlink(temp);
1072         restore_sigs();
1073         fclose(fp);
1074         return 0;
1075       } else if (debug)
1076         fprintf (stderr, "mode of %s is now %o\n", temp, CATMODE);
1077
1078       snprintf (command, sizeof(command), "(cd %s ; %s | %s)", path,
1079                 roff_command, COMPRESSOR);
1080       fprintf (stderr, "Formatting page, please wait...");
1081       fflush(stderr);
1082
1083       if (debug)
1084         fprintf (stderr, "\ntrying command: %s\n", command);
1085       else {
1086         if (manid) {
1087           setreuid(-1, ruid);
1088           setregid(-1, rgid);
1089         }
1090         if ((pp = popen(command, "r")) == NULL) {
1091           s = errno;
1092           fprintf(stderr, "Failed.\n");
1093           errno = s;
1094           perror("popen");
1095           if (manid) {
1096             setreuid(-1, euid);
1097             setregid(-1, egid);
1098           }
1099           unlink(temp);
1100           restore_sigs();
1101           fclose(fp);
1102           return 0;
1103         }
1104         if (manid) {
1105           setreuid(-1, euid);
1106           setregid(-1, egid);
1107         }
1108
1109         f = 0;
1110         while ((s = getc(pp)) != EOF) {
1111           putc(s, fp); f++;
1112         }
1113
1114         if (!f || ((s = pclose(pp)) == -1)) {
1115           s = errno;
1116           fprintf(stderr, "Failed.\n");
1117           errno = s;
1118           perror("pclose");
1119           unlink(temp);
1120           restore_sigs();
1121           fclose(fp);
1122           return 0;
1123         }
1124
1125         if (s != 0) {
1126           fprintf(stderr, "Failed.\n");
1127           gripe_system_command(s);
1128           unlink(temp);
1129           restore_sigs();
1130           fclose(fp);
1131           return 0;
1132         }
1133       }
1134
1135       if (debug)
1136         unlink(temp);
1137       else if (rename(temp, cat_file) == -1) {
1138         s = errno;
1139         fprintf(stderr,
1140                  "\nHmm!  Can't seem to rename %s to %s, check permissions on man dir!\n",
1141                  temp, cat_file);
1142         errno = s;
1143         perror("rename");
1144         unlink(temp);
1145         restore_sigs();
1146         fclose(fp);
1147         return 0;
1148       }
1149       restore_sigs();
1150
1151       if (fclose(fp)) {
1152         s = errno;
1153         if (!debug)
1154           unlink(cat_file);
1155         fprintf(stderr, "Failed.\n");
1156         errno = s;
1157         perror("fclose");
1158         return 0;
1159       }
1160
1161       if (debug) {
1162         fprintf(stderr, "No output, debug mode.\n");
1163         return 0;
1164       }
1165
1166       fprintf(stderr, "Done.\n");
1167
1168       return 1;
1169     }
1170   else
1171     {
1172       if (f >= 0) {
1173         s = errno;
1174         unlink(temp);
1175         errno = s;
1176       }
1177       if (debug) {
1178         s = errno;
1179         fprintf (stderr, "Couldn't open %s for writing.\n", temp);
1180         errno = s;
1181       }
1182       if (f >= 0) {
1183         perror("fdopen");
1184         close(f);
1185       }
1186
1187       return 0;
1188     }
1189 }
1190
1191 /*
1192  * Try to format the man page source and save it, then display it.  If
1193  * that's not possible, try to format the man page source and display
1194  * it directly.
1195  *
1196  * Note that we've already been handed the name of the ultimate source
1197  * file at this point.
1198  */
1199 int
1200 format_and_display (char *path, char *man_file, char *cat_file)
1201 {
1202   int status;
1203   int found;
1204   char *roff_command;
1205   char command[FILENAME_MAX];
1206
1207   found = 0;
1208
1209   if (access (man_file, R_OK) != 0)
1210     return 0;
1211
1212   if (troff)
1213     {
1214       roff_command = make_roff_command (man_file);
1215       if (roff_command == NULL)
1216         return 0;
1217       else
1218         snprintf (command, sizeof(command), "(cd %s ; %s)", path, roff_command);
1219
1220       found = do_system_command (command);
1221     }
1222   else
1223     {
1224       if (cat_file == NULL)
1225         goto format_and_display;
1226
1227       status = is_newer (man_file, cat_file);
1228       if (debug)
1229         fprintf (stderr, "status from is_newer() = %d\n", status);
1230
1231       if (status == 1 || status == -2)
1232         {
1233           /*
1234            * Cat file is out of date.  Try to format and save it.
1235            */
1236           if (print_where)
1237             {
1238               printf ("%s\n", man_file);
1239               found++;
1240             }
1241           else
1242             {
1243
1244               setreuid(-1, euid);
1245               setregid(-1, egid);
1246               found = make_cat_file (path, man_file, cat_file, 1);
1247               setreuid(-1, ruid);
1248               setregid(-1, rgid);
1249
1250               if (!found)
1251                 {
1252                   /* Try again as real user - see note below.
1253                      By running with
1254                        effective group (user) ID == real group (user) ID
1255                      except for the call above, I believe the problems
1256                      of reading private man pages is avoided.  */
1257                   found = make_cat_file (path, man_file, cat_file, 0);
1258                 }
1259               if (found)
1260                 {
1261                   /*
1262                    * Creating the cat file worked.  Now just display it.
1263                    */
1264                   (void) display_cat_file (cat_file);
1265                 }
1266               else
1267                 {
1268                   /*
1269                    * Couldn't create cat file.  Just format it and
1270                    * display it through the pager.
1271                    */
1272 format_and_display:
1273                   roff_command = make_roff_command (man_file);
1274                   if (roff_command == NULL)
1275                     return 0;
1276                   else
1277                     snprintf (command, sizeof(command), "(cd %s ; %s | %s)", path,
1278                              roff_command, pager);
1279
1280                   found = do_system_command (command);
1281                 }
1282             }
1283         }
1284       else if (access (cat_file, R_OK) == 0)
1285         {
1286           /*
1287            * Formatting not necessary.  Cat file is newer than source
1288            * file, or source file is not present but cat file is.
1289            */
1290           if (print_where)
1291             {
1292               printf ("%s (source: %s)\n", cat_file, man_file);
1293               found++;
1294             }
1295           else
1296             {
1297               found = display_cat_file (cat_file);
1298             }
1299         }
1300     }
1301   return found;
1302 }
1303
1304 /*
1305  * See if the preformatted man page or the source exists in the given
1306  * section.
1307  */
1308 int
1309 try_section (char *path, char *section, char *_longsec, char *name, int glob)
1310 {
1311   int found = 0;
1312   int to_cat;
1313   int cat;
1314   char **names;
1315   char **np;
1316   static int arch_search;
1317   char buf[FILENAME_MAX];
1318
1319   if (!arch_search)
1320     {
1321       snprintf(buf, sizeof(buf), "%s/man%s/%s", path, section, machine);
1322       if (is_directory (buf) == 1)
1323         {
1324           snprintf(buf, sizeof(buf), "%s/%s", machine, name);
1325           arch_search++;
1326           found = try_section (path, section, _longsec, buf, glob);
1327           arch_search--;
1328           if (found && !findall)   /* only do this architecture... */
1329             return found;
1330         }
1331     }
1332
1333   if (debug)
1334     {
1335       if (glob)
1336         fprintf (stderr, "trying section %s with globbing\n", section);
1337       else
1338         fprintf (stderr, "trying section %s without globbing\n", section);
1339     }
1340
1341 #ifndef NROFF_MISSING
1342   /*
1343    * Look for man page source files.
1344    */
1345   cat = 0;
1346   if (glob)
1347     names = glob_for_file (path, section, _longsec, name, cat);
1348   else
1349     names = make_name (path, section, _longsec, name, cat);
1350
1351   if (names == (char **) -1 || *names == NULL)
1352     /*
1353      * No files match.  See if there's a preformatted page around that
1354      * we can display.
1355      */
1356 #endif /* NROFF_MISSING */
1357     {
1358       if (!troff)
1359         {
1360           cat = 1;
1361           if (glob)
1362             names = glob_for_file (path, section, _longsec, name, cat);
1363           else
1364             names = make_name (path, section, _longsec, name, cat);
1365
1366           if (names != (char **) -1 && *names != NULL)
1367             {
1368               for (np = names; *np != NULL; np++)
1369                 {
1370                   if (print_where)
1371                     {
1372                       printf ("%s\n", *np);
1373                       found++;
1374                     }
1375                   else
1376                     {
1377                       found += display_cat_file (*np);
1378                     }
1379                 }
1380             }
1381         }
1382     }
1383 #ifndef NROFF_MISSING
1384   else
1385     {
1386       for (np = names; *np != NULL; np++)
1387         {
1388           char *cat_file = NULL;
1389           char *man_file;
1390
1391           man_file = ultimate_source (*np, path);
1392
1393           if (!troff)
1394             {
1395               to_cat = 1;
1396
1397               cat_file = convert_name (man_file, to_cat);
1398
1399               if (debug)
1400                 fprintf (stderr, "will try to write %s if needed\n", cat_file);
1401             }
1402
1403           found += format_and_display (path, man_file, cat_file);
1404         }
1405     }
1406 #endif /* NROFF_MISSING */
1407   return found;
1408 }
1409
1410 /*
1411  * Search for manual pages.
1412  *
1413  * If preformatted manual pages are supported, look for the formatted
1414  * file first, then the man page source file.  If they both exist and
1415  * the man page source file is newer, or only the source file exists,
1416  * try to reformat it and write the results in the cat directory.  If
1417  * it is not possible to write the cat file, simply format and display
1418  * the man file.
1419  *
1420  * If preformatted pages are not supported, or the troff option is
1421  * being used, only look for the man page source file.
1422  *
1423  */
1424 int
1425 man (char *name)
1426 {
1427   int found;
1428   int glob;
1429   char **mp;
1430   char **sp;
1431   int l_found;
1432   char buf[PATH_MAX];
1433
1434   found = 0;
1435
1436   fflush (stdout);
1437   if (strchr(name, '/'))  /* Treat name as file name if it's a path */
1438     {
1439       struct stat st;
1440
1441       if (debug)
1442         fprintf(stderr, "Trying as file name\n");
1443
1444       /*
1445        * We need to pass an absolute file name to format_and_display,
1446        * or it will run into problems later.
1447        */
1448       realpath(name, buf);
1449
1450       if (stat(buf, &st) == 0)
1451         found += format_and_display(dirname(buf), buf, NULL);
1452     }
1453   else if (shortsec != NULL)
1454     {
1455       for (mp = manpathlist; *mp != NULL; mp++)
1456         {
1457           if (debug)
1458             fprintf (stderr, "\nsearching in %s\n", *mp);
1459
1460           glob = 1;
1461
1462           l_found = 0;
1463           if (locale != NULL) {
1464             locale_opts = locale_nroff;
1465             use_man_locale = 1;
1466             if (*locale_lang != '\0' && *locale_terr != '\0') {
1467               snprintf(buf, sizeof(buf), "%s/%s_%s.%s", *mp,
1468                        locale_lang, locale_terr, locale_codeset);
1469               if (is_directory (buf) == 1)
1470                 l_found = try_section (buf, shortsec, longsec, name, glob);
1471             }
1472             if (!l_found) {
1473               if (*locale_lang != '\0') {
1474                 snprintf(buf, sizeof(buf), "%s/%s.%s", *mp,
1475                          locale_lang, locale_codeset);
1476                 if (is_directory (buf) == 1)
1477                   l_found = try_section (buf, shortsec, longsec, name, glob);
1478               }
1479               use_man_locale = 0;
1480               if (!l_found && strcmp(locale_lang, "en") != 0) {
1481                 snprintf(buf, sizeof(buf), "%s/en.%s", *mp,
1482                          locale_codeset);
1483                 if (is_directory (buf) == 1)
1484                   l_found = try_section (buf, shortsec, longsec, name, glob);
1485               }
1486             }
1487             locale_opts = NULL;
1488             use_man_locale = 0;
1489           }
1490           if (!l_found) {
1491           found += try_section (*mp, shortsec, longsec, name, glob);
1492           } else
1493             found += l_found;
1494
1495           if (found && !findall)   /* i.e. only do this section... */
1496             return found;
1497         }
1498     }
1499   else
1500     {
1501       for (sp = section_list; *sp != NULL; sp++)
1502         {
1503           for (mp = manpathlist; *mp != NULL; mp++)
1504             {
1505               if (debug)
1506                 fprintf (stderr, "\nsearching in %s\n", *mp);
1507
1508               glob = 1;
1509
1510               l_found = 0;
1511               if (locale != NULL) {
1512                 locale_opts = locale_nroff;
1513                 use_man_locale = 1;
1514                 if (*locale_lang != '\0' && *locale_terr != '\0') {
1515                   snprintf(buf, sizeof(buf), "%s/%s_%s.%s", *mp,
1516                            locale_lang, locale_terr, locale_codeset);
1517                   if (is_directory (buf) == 1)
1518                     l_found = try_section (buf, *sp, longsec, name, glob);
1519                 }
1520                 if (!l_found) {
1521                   if (*locale_lang != '\0') {
1522                     snprintf(buf, sizeof(buf), "%s/%s.%s", *mp,
1523                              locale_lang, locale_codeset);
1524                     if (is_directory (buf) == 1)
1525                       l_found = try_section (buf, *sp, longsec, name, glob);
1526                   }
1527                   use_man_locale = 0;
1528                   if (!l_found && strcmp(locale_lang, "en") != 0) {
1529                     snprintf(buf, sizeof(buf), "%s/en.%s", *mp,
1530                              locale_codeset);
1531                     if (is_directory (buf) == 1)
1532                       l_found = try_section (buf, *sp, longsec, name, glob);
1533                   }
1534                 }
1535                 locale_opts = NULL;
1536                 use_man_locale = 0;
1537               }
1538               if (!l_found) {
1539               found += try_section (*mp, *sp, longsec, name, glob);
1540               } else
1541                 found += l_found;
1542
1543               if (found && !findall)   /* i.e. only do this section... */
1544                 return found;
1545             }
1546         }
1547     }
1548   return found;
1549 }
1550
1551 char **
1552 get_section_list (void)
1553 {
1554   int i;
1555   char *p;
1556   char *end;
1557 #define TMP_SECTION_LIST_SIZE 100
1558   static char *tmp_section_list[TMP_SECTION_LIST_SIZE];
1559
1560   if (colon_sep_section_list == NULL)
1561     {
1562       if ((p = getenv ("MANSECT")) == NULL)
1563         {
1564           return __DECONST(char **, std_sections);
1565         }
1566       else
1567         {
1568           colon_sep_section_list = strdup (p);
1569         }
1570     }
1571
1572   i = 0;
1573   for (p = colon_sep_section_list; i < TMP_SECTION_LIST_SIZE ; p = end+1) 
1574     {
1575       if ((end = strchr (p, ':')) != NULL)
1576         *end = '\0';
1577
1578       tmp_section_list[i++] = strdup (p);
1579
1580       if (end == NULL)
1581         break;
1582     }
1583
1584   tmp_section_list [i] = NULL;
1585   return tmp_section_list;
1586 }