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