1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T. */
26 /* All rights reserved. */
27
28 /*
29 * University Copyright- Copyright (c) 1982, 1986, 1988
30 * The Regents of the University of California
31 * All Rights Reserved
32 *
33 * University Acknowledgment- Portions of this document are derived from
34 * software developed by the University of California, Berkeley, and its
35 * contributors.
36 */
37
38
39 /*
40 * man
41 * links to apropos, whatis, and catman
42 * This version uses more for underlining and paging.
43 */
44
45 #include <stdio.h>
46 #include <ctype.h>
47 #include <sgtty.h>
48 #include <sys/param.h>
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <signal.h>
52 #include <string.h>
53 #include <malloc.h>
54 #include <dirent.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <locale.h>
58 #include <stdlib.h>
59 #include <unistd.h>
60 #include <memory.h>
61 #include <limits.h>
62 #include <wchar.h>
63
64 #define MACROF "tmac.an" /* name of <locale> macro file */
65 #define TMAC_AN "-man" /* default macro file */
66
67 /*
68 * The default search path for man subtrees.
69 */
70
71 #define MANDIR "/usr/share/man" /* default mandir */
72 #define MAKEWHATIS "/usr/lib/makewhatis"
73 #define WHATIS "windex"
74 #define TEMPLATE "/tmp/mpXXXXXX"
75 #define CONFIG "man.cf"
76
77 /*
78 * Names for formatting and display programs. The values given
79 * below are reasonable defaults, but sites with source may
80 * wish to modify them to match the local environment. The
81 * value for TCAT is particularly problematic as there's no
82 * accepted standard value available for it. (The definition
83 * below assumes C.A.T. troff output and prints it).
84 */
85
86 #define MORE "more -s" /* default paging filter */
87 #define CAT_S "/usr/bin/cat -s" /* for '-' opt (no more) */
88 #define CAT_ "/usr/bin/cat" /* for when output is not a tty */
89 #define TROFF "troff" /* local name for troff */
90 #define TCAT "lp -c -T troff" /* command to "display" troff output */
91
92 #define SOLIMIT 10 /* maximum allowed .so chain length */
93 #define MAXDIRS 128 /* max # of subdirs per manpath */
94 #define MAXPAGES 128 /* max # for multiple pages */
95 #define PLEN 3 /* prefix length {man, cat, fmt} */
96 #define TMPLEN 7 /* length of tmpfile prefix */
97 #define MAXTOKENS 64
98
99 #define DOT_SO ".so "
100 #define PREPROC_SPEC "'\\\" "
101
102 #define DPRINTF if (debug && !catmando) \
103 (void) printf
104
105 #define sys(s) (debug ? ((void)puts(s), 0) : system(s))
106 #define eq(a, b) (strcmp(a, b) == 0)
107 #define match(a, b, c) (strncmp(a, b, c) == 0)
108
109 #define ISDIR(A) ((A.st_mode & S_IFMT) == S_IFDIR)
110
111 #define SROFF_CMD "/usr/lib/sgml/sgml2roff" /* sgml converter */
112 #define MANDIRNAME "man" /* man directory */
113 #define SGMLDIR "sman" /* sman directory */
114 #define SGML_SYMBOL "<!DOCTYPE" /* a sgml file should contain this */
115 #define SGML_SYMBOL_LEN 9 /* length of SGML_SYMBOL */
116
117 /*
118 * Directory mapping of old directories to new directories
119 */
120
121 typedef struct {
122 char *old_name;
123 char *new_name;
124 } map_entry;
125
126 static const map_entry map[] = {
127 { "3b", "3ucb" },
128 { "3e", "3elf" },
129 { "3g", "3gen" },
130 { "3k", "3kstat" },
131 { "3n", "3socket" },
132 { "3r", "3rt" },
133 { "3s", "3c" },
134 { "3t", "3thr" },
135 { "3x", "3curses" },
136 { "3xc", "3xcurses" },
137 { "3xn", "3xnet" }
138 };
139
140 /*
141 * A list of known preprocessors to precede the formatter itself
142 * in the formatting pipeline. Preprocessors are specified by
143 * starting a manual page with a line of the form:
144 * '\" X
145 * where X is a string consisting of letters from the p_tag fields
146 * below.
147 */
148 static const struct preprocessor {
149 char p_tag;
150 char *p_nroff,
151 *p_troff,
152 *p_stdin_char;
153 } preprocessors [] = {
154 {'c', "cw", "cw", "-"},
155 {'e', "neqn /usr/share/lib/pub/eqnchar",
156 "eqn /usr/share/lib/pub/eqnchar", "-"},
157 {'p', "gpic", "gpic", "-"},
158 {'r', "refer", "refer", "-"},
159 {'t', "tbl", "tbl", ""},
160 {'v', "vgrind -f", "vgrind -f", "-"},
161 {0, 0, 0, 0}
162 };
163
164 struct suffix {
165 char *ds;
166 char *fs;
167 };
168
169 /*
170 * Flags that control behavior of build_manpath()
171 *
172 * BMP_ISPATH pathv is a vector constructed from PATH.
173 * Perform appropriate path translations for
174 * manpath.
175 * BMP_APPEND_MANDIR Add /usr/share/man to the end if it
176 * hasn't already appeared earlier.
177 * BMP_FALLBACK_MANDIR Append /usr/share/man only if no other
178 * manpath (including derived from PATH)
179 * elements are valid.
180 */
181 #define BMP_ISPATH 1
182 #define BMP_APPEND_MANDIR 2
183 #define BMP_FALLBACK_MANDIR 4
184
185 /*
186 * When doing equality comparisons of directories, device and inode
187 * comparisons are done. The dupsec and dupnode structures are used
188 * to form a list of lists for this processing.
189 */
190 struct secnode {
191 char *secp;
192 struct secnode *next;
193 };
194 struct dupnode {
195 dev_t dev; /* from struct stat st_dev */
196 ino_t ino; /* from struct stat st_ino */
197 struct secnode *secl; /* sections already considered */
198 struct dupnode *next;
199 };
200
201 /*
202 * Map directories that may appear in PATH to the corresponding
203 * man directory
204 */
205 static struct pathmap {
206 char *bindir;
207 char *mandir;
208 dev_t dev;
209 ino_t ino;
210 } bintoman[] = {
211 {"/sbin", "/usr/share/man,1m", 0, 0},
212 {"/usr/sbin", "/usr/share/man,1m", 0, 0},
213 {"/usr/ucb", "/usr/share/man,1b", 0, 0},
214 {"/usr/bin/X11", "/usr/X11/share/man", 0, 0},
215 /*
216 * Restrict to section 1 so that whatis /usr/{,xpg4,xpg6}/bin/ls
217 * does not confuse users with section 1 and 1b
218 */
219 {"/usr/bin", "/usr/share/man,1,1m,1s,1t,1c", 0, 0},
220 {"/usr/xpg4/bin", "/usr/share/man,1", 0, 0},
221 {"/usr/xpg6/bin", "/usr/share/man,1", 0, 0},
222 {NULL, NULL, 0, 0}
223 };
224
225 /*
226 * Subdirectories to search for unformatted/formatted man page
227 * versions, in nroff and troff variations. The searching
228 * code in manual() is structured to expect there to be two
229 * subdirectories apiece, the first for unformatted files
230 * and the second for formatted ones.
231 */
232 static char *nroffdirs[] = { "man", "cat", 0 };
233 static char *troffdirs[] = { "man", "fmt", 0 };
234
235 #define MAN_USAGE "\
236 usage:\tman [-] [-adFlprt] [-M path] [-T macro-package ] [ -s section ] \
237 name ...\n\
238 \tman [-M path] -k keyword ...\n\tman [-M path] -f file ..."
239 #define CATMAN_USAGE "\
240 usage:\tcatman [-p] [-c|-ntw] [-M path] [-T macro-package ] [sections]"
241
242 static char *opts[] = {
243 "FfkrpP:M:T:ts:lad", /* man */
244 "wpnP:M:T:tc" /* catman */
245 };
246
247 struct man_node {
248 char *path; /* mandir path */
249 char **secv; /* submandir suffices */
250 int defsrch; /* hint for man -p to avoid section list */
251 int frompath; /* hint for man -d and catman -p */
252 struct man_node *next;
253 };
254
255 static char *pages[MAXPAGES];
256 static char **endp = pages;
257
258 /*
259 * flags (options)
260 */
261 static int nomore;
262 static int troffit;
263 static int debug;
264 static int Tflag;
265 static int sargs;
266 static int margs;
267 static int force;
268 static int found;
269 static int list;
270 static int all;
271 static int whatis;
272 static int apropos;
273 static int catmando;
274 static int nowhatis;
275 static int whatonly;
276 static int compargs; /* -c option for catman */
277 static int printmp;
278
279 static char *CAT = CAT_;
280 static char macros[MAXPATHLEN];
281 static char *mansec;
282 static char *pager;
283 static char *troffcmd;
284 static char *troffcat;
285 static char **subdirs;
286
287 static char *check_config(char *);
288 static struct man_node *build_manpath(char **, int);
289 static void getpath(struct man_node *, char **);
290 static void getsect(struct man_node *, char **);
291 static void get_all_sect(struct man_node *);
292 static void catman(struct man_node *, char **, int);
293 static int makecat(char *, char **, int);
294 static int getdirs(char *, char ***, short);
295 static void whatapro(struct man_node *, char *, int);
296 static void lookup_windex(char *, char *, char **);
297 static int icmp(wchar_t *, wchar_t *);
298 static void more(char **, int);
299 static void cleanup(char **);
300 static void bye(int);
301 static char **split(char *, char);
302 static void freev(char **);
303 static void fullpaths(struct man_node **);
304 static void lower(char *);
305 static int cmp(const void *, const void *);
306 static int manual(struct man_node *, char *);
307 static void mandir(char **, char *, char *);
308 static void sortdir(DIR *, char ***);
309 static int searchdir(char *, char *, char *);
310 static int windex(char **, char *, char *);
311 static void section(struct suffix *, char *);
312 static int bfsearch(FILE *, char **, char *, char **);
313 static int compare(char *, char *, char **);
314 static int format(char *, char *, char *, char *);
315 static char *addlocale(char *);
316 static int get_manconfig(FILE *, char *);
317 static void malloc_error(void);
318 static int sgmlcheck(const char *);
319 static char *map_section(char *, char *);
320 static void free_manp(struct man_node *manp);
321 static void init_bintoman(void);
322 static char *path_to_manpath(char *);
323 static int dupcheck(struct man_node *, struct dupnode **);
324 static void free_dupnode(struct dupnode *);
325 static void print_manpath(struct man_node *, char *);
326
327 /*
328 * This flag is used when the SGML-to-troff converter
329 * is absent - all the SGML searches are bypassed.
330 */
331 static int no_sroff = 0;
332
333 /*
334 * This flag is used to describe the case where we've found
335 * an SGML formatted manpage in the sman directory, we haven't
336 * found a troff formatted manpage, and we don't have the SGML to troff
337 * conversion utility on the system.
338 */
339 static int sman_no_man_no_sroff;
340
341 static char language[PATH_MAX + 1]; /* LC_MESSAGES */
342 static char localedir[PATH_MAX + 1]; /* locale specific path component */
343
344 static int defaultmandir = 1; /* if processing default mandir, 1 */
345
346 static char *newsection = NULL;
347
348 int
main(int argc,char * argv[])349 main(int argc, char *argv[])
350 {
351 int badopts = 0;
352 int c;
353 char **pathv;
354 char *cmdname;
355 char *manpath = NULL;
356 static struct man_node *manpage = NULL;
357 int bmp_flags = 0;
358 int err = 0;
359
360 if (access(SROFF_CMD, F_OK | X_OK) != 0)
361 no_sroff = 1;
362
363 (void) setlocale(LC_ALL, "");
364 (void) strcpy(language, setlocale(LC_MESSAGES, (char *)0));
365 if (strcmp("C", language) != 0)
366 (void) sprintf(localedir, "%s", language);
367
368 #if !defined(TEXT_DOMAIN)
369 #define TEXT_DOMAIN "SYS_TEST"
370 #endif
371 (void) textdomain(TEXT_DOMAIN);
372
373 (void) strcpy(macros, TMAC_AN);
374
375 /*
376 * get base part of command name
377 */
378 if ((cmdname = strrchr(argv[0], '/')) != NULL)
379 cmdname++;
380 else
381 cmdname = argv[0];
382
383 if (eq(cmdname, "apropos") || eq(cmdname, "whatis")) {
384 whatis++;
385 apropos = (*cmdname == 'a');
386 if ((optind = 1) == argc) {
387 (void) fprintf(stderr, gettext("%s what?\n"), cmdname);
388 exit(2);
389 }
390 goto doargs;
391 } else if (eq(cmdname, "catman"))
392 catmando++;
393
394 opterr = 0;
395 while ((c = getopt(argc, argv, opts[catmando])) != -1)
396 switch (c) {
397
398 /*
399 * man specific options
400 */
401 case 'k':
402 apropos++;
403 /*FALLTHROUGH*/
404 case 'f':
405 whatis++;
406 break;
407 case 'F':
408 force++; /* do lookups the hard way */
409 break;
410 case 's':
411 mansec = optarg;
412 sargs++;
413 break;
414 case 'r':
415 nomore++, troffit++;
416 break;
417 case 'l':
418 list++; /* implies all */
419 /*FALLTHROUGH*/
420 case 'a':
421 all++;
422 break;
423 case 'd':
424 debug++;
425 break;
426 /*
427 * man and catman use -p differently. In catman it
428 * enables debug mode and in man it prints the (possibly
429 * derived from PATH or name operand) MANPATH.
430 */
431 case 'p':
432 if (catmando == 0) {
433 printmp++;
434 } else {
435 debug++;
436 }
437 break;
438 case 'n':
439 nowhatis++;
440 break;
441 case 'w':
442 whatonly++;
443 break;
444 case 'c': /* n|troff compatibility */
445 if (no_sroff)
446 (void) fprintf(stderr, gettext(
447 "catman: SGML conversion not "
448 "available -- -c flag ignored\n"));
449 else
450 compargs++;
451 continue;
452
453 /*
454 * shared options
455 */
456 case 'P': /* Backwards compatibility */
457 case 'M': /* Respecify path for man pages. */
458 manpath = optarg;
459 margs++;
460 break;
461 case 'T': /* Respecify man macros */
462 (void) strcpy(macros, optarg);
463 Tflag++;
464 break;
465 case 't':
466 troffit++;
467 break;
468 case '?':
469 badopts++;
470 }
471
472 /*
473 * Bad options or no args?
474 * (man -p and catman don't need args)
475 */
476 if (badopts || (!catmando && !printmp && optind == argc)) {
477 (void) fprintf(stderr, "%s\n", catmando ?
478 gettext(CATMAN_USAGE) : gettext(MAN_USAGE));
479 exit(2);
480 }
481
482 if (compargs && (nowhatis || whatonly || troffit)) {
483 (void) fprintf(stderr, "%s\n", gettext(CATMAN_USAGE));
484 (void) fprintf(stderr, gettext(
485 "-c option cannot be used with [-w][-n][-t]\n"));
486 exit(2);
487 }
488
489 if (sargs && margs && catmando) {
490 (void) fprintf(stderr, "%s\n", gettext(CATMAN_USAGE));
491 exit(2);
492 }
493
494 if (troffit == 0 && nomore == 0 && !isatty(fileno(stdout)))
495 nomore++;
496
497 /*
498 * Collect environment information.
499 */
500 if (troffit) {
501 if ((troffcmd = getenv("TROFF")) == NULL)
502 troffcmd = TROFF;
503 if ((troffcat = getenv("TCAT")) == NULL)
504 troffcat = TCAT;
505 } else {
506 if (((pager = getenv("PAGER")) == NULL) ||
507 (*pager == NULL))
508 pager = MORE;
509 }
510
511 doargs:
512 subdirs = troffit ? troffdirs : nroffdirs;
513
514 init_bintoman();
515
516 if (manpath == NULL && (manpath = getenv("MANPATH")) == NULL) {
517 if ((manpath = getenv("PATH")) != NULL) {
518 bmp_flags = BMP_ISPATH | BMP_APPEND_MANDIR;
519 } else {
520 manpath = MANDIR;
521 }
522 }
523
524 pathv = split(manpath, ':');
525
526 manpage = build_manpath(pathv, bmp_flags);
527
528 /* release pathv allocated by split() */
529 freev(pathv);
530
531 fullpaths(&manpage);
532
533 if (catmando) {
534 catman(manpage, argv+optind, argc-optind);
535 exit(0);
536 }
537
538 /*
539 * The manual routine contains windows during which
540 * termination would leave a temp file behind. Thus
541 * we blanket the whole thing with a clean-up routine.
542 */
543 if (signal(SIGINT, SIG_IGN) == SIG_DFL) {
544 (void) signal(SIGINT, bye);
545 (void) signal(SIGQUIT, bye);
546 (void) signal(SIGTERM, bye);
547 }
548
549 /*
550 * "man -p" without operands
551 */
552 if ((printmp != 0) && (optind == argc)) {
553 print_manpath(manpage, NULL);
554 exit(0);
555 }
556
557 for (; optind < argc; optind++) {
558 if (strcmp(argv[optind], "-") == 0) {
559 nomore++;
560 CAT = CAT_S;
561 } else {
562 char *cmd;
563 static struct man_node *mp;
564 char *pv[2];
565
566 /*
567 * If full path to command specified, customize
568 * manpath accordingly
569 */
570 if ((cmd = strrchr(argv[optind], '/')) != NULL) {
571 *cmd = '\0';
572 if ((pv[0] = strdup(argv[optind])) == NULL) {
573 malloc_error();
574 }
575 pv[1] = NULL;
576 *cmd = '/';
577 mp = build_manpath(pv,
578 BMP_ISPATH|BMP_FALLBACK_MANDIR);
579 } else {
580 mp = manpage;
581 }
582
583 if (whatis) {
584 whatapro(mp, argv[optind], apropos);
585 } else if (printmp != 0) {
586 print_manpath(mp, argv[optind]);
587 } else {
588 err += manual(mp, argv[optind]);
589 }
590
591 if (mp != NULL && mp != manpage) {
592 free(pv[0]);
593 free_manp(mp);
594 }
595 }
596 }
597 return (err == 0 ? 0 : 1);
598 /*NOTREACHED*/
599 }
600
601 /*
602 * This routine builds the manpage structure from MANPATH or PATH,
603 * depending on flags. See BMP_* definitions above for valid
604 * flags.
605 *
606 * Assumes pathv elements were malloc'd, as done by split().
607 * Elements may be freed and reallocated to have different contents.
608 */
609
610 static struct man_node *
build_manpath(char ** pathv,int flags)611 build_manpath(char **pathv, int flags)
612 {
613 struct man_node *manpage = NULL;
614 struct man_node *currp = NULL;
615 struct man_node *lastp = NULL;
616 char **p;
617 char **q;
618 char *mand = NULL;
619 char *mandir = MANDIR;
620 int s;
621 struct dupnode *didup = NULL;
622 struct stat sb;
623
624 s = sizeof (struct man_node);
625 for (p = pathv; *p; ) {
626
627 if (flags & BMP_ISPATH) {
628 if ((mand = path_to_manpath(*p)) == NULL) {
629 goto next;
630 }
631 free(*p);
632 *p = mand;
633 }
634 q = split(*p, ',');
635 if (stat(q[0], &sb) != 0 || (sb.st_mode & S_IFDIR) == 0) {
636 freev(q);
637 goto next;
638 }
639
640 if (access(q[0], R_OK|X_OK) != 0) {
641 if (catmando) {
642 (void) fprintf(stderr,
643 gettext("%s is not accessible.\n"),
644 q[0]);
645 (void) fflush(stderr);
646 }
647 } else {
648
649 /*
650 * Some element exists. Do not append MANDIR as a
651 * fallback.
652 */
653 flags &= ~BMP_FALLBACK_MANDIR;
654
655 if ((currp = (struct man_node *)calloc(1, s)) == NULL) {
656 malloc_error();
657 }
658
659 currp->frompath = (flags & BMP_ISPATH);
660
661 if (manpage == NULL) {
662 lastp = manpage = currp;
663 }
664
665 getpath(currp, p);
666 getsect(currp, p);
667
668 /*
669 * If there are no new elements in this path,
670 * do not add it to the manpage list
671 */
672 if (dupcheck(currp, &didup) != 0) {
673 freev(currp->secv);
674 free(currp);
675 } else {
676 currp->next = NULL;
677 if (currp != manpage) {
678 lastp->next = currp;
679 }
680 lastp = currp;
681 }
682 }
683 freev(q);
684 next:
685 /*
686 * Special handling of appending MANDIR.
687 * After all pathv elements have been processed, append MANDIR
688 * if needed.
689 */
690 if (p == &mandir) {
691 break;
692 }
693 p++;
694 if (*p != NULL) {
695 continue;
696 }
697 if (flags & (BMP_APPEND_MANDIR|BMP_FALLBACK_MANDIR)) {
698 p = &mandir;
699 flags &= ~BMP_ISPATH;
700 }
701 }
702
703 free_dupnode(didup);
704
705 return (manpage);
706 }
707
708 /*
709 * Stores the mandir path into the manp structure.
710 */
711
712 static void
getpath(struct man_node * manp,char ** pv)713 getpath(struct man_node *manp, char **pv)
714 {
715 char *s;
716 int i = 0;
717
718 s = *pv;
719
720 while (*s != NULL && *s != ',')
721 i++, s++;
722
723 manp->path = (char *)malloc(i+1);
724 if (manp->path == NULL)
725 malloc_error();
726 (void) strncpy(manp->path, *pv, i);
727 *(manp->path + i) = '\0';
728 }
729
730 /*
731 * Stores the mandir's corresponding sections (submandir
732 * directories) into the manp structure.
733 */
734
735 static void
getsect(struct man_node * manp,char ** pv)736 getsect(struct man_node *manp, char **pv)
737 {
738 char *sections;
739 char **sectp;
740
741 if (sargs) {
742 manp->secv = split(mansec, ',');
743
744 for (sectp = manp->secv; *sectp; sectp++)
745 lower(*sectp);
746 } else if ((sections = strchr(*pv, ',')) != NULL) {
747 if (debug) {
748 if (manp->frompath != 0) {
749 /*
750 * TRANSLATION_NOTE - message for man -d or catman -p
751 * ex. /usr/share/man: derived from PATH, MANSECTS=,1b
752 */
753 (void) printf(gettext(
754 "%s: derived from PATH, MANSECTS=%s\n"),
755 manp->path, sections);
756 } else {
757 /*
758 * TRANSLATION_NOTE - message for man -d or catman -p
759 * ex. /usr/share/man: from -M option, MANSECTS=,1,2,3c
760 */
761 (void) fprintf(stdout, gettext(
762 "%s: from -M option, MANSECTS=%s\n"),
763 manp->path, sections);
764 }
765 }
766 manp->secv = split(++sections, ',');
767 for (sectp = manp->secv; *sectp; sectp++)
768 lower(*sectp);
769
770 if (*manp->secv == NULL)
771 get_all_sect(manp);
772 } else if ((sections = check_config(*pv)) != NULL) {
773 manp->defsrch = 1;
774 /*
775 * TRANSLATION_NOTE - message for man -d or catman -p
776 * ex. /usr/share/man: from man.cf, MANSECTS=1,1m,1c
777 */
778 if (debug)
779 (void) fprintf(stdout, gettext(
780 "%s: from %s, MANSECTS=%s\n"),
781 manp->path, CONFIG, sections);
782 manp->secv = split(sections, ',');
783
784 for (sectp = manp->secv; *sectp; sectp++)
785 lower(*sectp);
786
787 if (*manp->secv == NULL)
788 get_all_sect(manp);
789 } else {
790 manp->defsrch = 1;
791 /*
792 * TRANSLATION_NOTE - message for man -d or catman -p
793 * if man.cf has not been found or sections has not been specified
794 * man/catman searches the sections lexicographically.
795 */
796 if (debug)
797 (void) fprintf(stdout, gettext(
798 "%s: search the sections lexicographically\n"),
799 manp->path);
800 manp->secv = NULL;
801 get_all_sect(manp);
802 }
803 }
804
805 /*
806 * Get suffices of all sub-mandir directories in a mandir.
807 */
808
809 static void
get_all_sect(struct man_node * manp)810 get_all_sect(struct man_node *manp)
811 {
812 DIR *dp;
813 char **dirv;
814 char **dv;
815 char **p;
816 char *prev = NULL;
817 char *tmp = NULL;
818 int plen;
819 int maxentries = MAXTOKENS;
820 int entries = 0;
821
822 if ((dp = opendir(manp->path)) == 0)
823 return;
824
825 /*
826 * sortdir() allocates memory for dirv and dirv[].
827 */
828 sortdir(dp, &dirv);
829
830 (void) closedir(dp);
831
832 if (manp->secv == NULL) {
833 /*
834 * allocates memory for manp->secv only if it's NULL
835 */
836 manp->secv = (char **)malloc(maxentries * sizeof (char *));
837 if (manp->secv == NULL)
838 malloc_error();
839 }
840
841 for (dv = dirv, p = manp->secv; *dv; dv++) {
842 plen = PLEN;
843 if (match(*dv, SGMLDIR, PLEN+1))
844 ++plen;
845
846 if (strcmp(*dv, CONFIG) == 0) {
847 /* release memory allocated by sortdir */
848 free(*dv);
849 continue;
850 }
851
852 if (tmp != NULL)
853 free(tmp);
854 tmp = strdup(*dv + plen);
855 if (tmp == NULL)
856 malloc_error();
857 (void) sprintf(tmp, "%s", *dv + plen);
858
859 if (prev != NULL) {
860 if (strcmp(prev, tmp) == 0) {
861 /* release memory allocated by sortdir */
862 free(*dv);
863 continue;
864 }
865 }
866
867 if (prev != NULL)
868 free(prev);
869 prev = strdup(*dv + plen);
870 if (prev == NULL)
871 malloc_error();
872 (void) sprintf(prev, "%s", *dv + plen);
873 /*
874 * copy the string in (*dv + plen) to *p
875 */
876 *p = strdup(*dv + plen);
877 if (*p == NULL)
878 malloc_error();
879 p++;
880 entries++;
881 if (entries == maxentries) {
882 maxentries += MAXTOKENS;
883 manp->secv = (char **)realloc(manp->secv,
884 sizeof (char *) * maxentries);
885 if (manp->secv == NULL)
886 malloc_error();
887 p = manp->secv + entries;
888 }
889 /* release memory allocated by sortdir */
890 free(*dv);
891 }
892 *p = 0;
893 /* release memory allocated by sortdir */
894 free(dirv);
895 }
896
897 /*
898 * Format man pages (build cat pages); if no
899 * sections are specified, build all of them.
900 * When building cat pages:
901 * catman() tries to build cat pages for locale specific
902 * man dirs first. Then, catman() tries to build cat pages
903 * for the default man dir (for C locale like /usr/share/man)
904 * regardless of the locale.
905 * When building windex file:
906 * catman() tries to build windex file for locale specific
907 * man dirs first. Then, catman() tries to build windex file
908 * for the default man dir (for C locale like /usr/share/man)
909 * regardless of the locale.
910 */
911
912 static void
catman(struct man_node * manp,char ** argv,int argc)913 catman(struct man_node *manp, char **argv, int argc)
914 {
915 char cmdbuf[BUFSIZ];
916 char **dv;
917 int changed;
918 struct man_node *p;
919 int ndirs = 0;
920 char *ldir;
921 int i;
922 struct dupnode *dnp = NULL;
923 char **realsecv;
924 char *fakesecv[2] = { " catman ", NULL };
925
926 for (p = manp; p != NULL; p = p->next) {
927 /*
928 * prevent catman from doing very heavy lifting multiple
929 * times on some directory
930 */
931 realsecv = p->secv;
932 p->secv = fakesecv;
933 if (dupcheck(p, &dnp) != 0) {
934 p->secv = realsecv;
935 continue;
936 }
937
938 /*
939 * TRANSLATION_NOTE - message for catman -p
940 * ex. mandir path = /usr/share/man
941 */
942 if (debug)
943 (void) fprintf(stdout, gettext(
944 "\nmandir path = %s\n"), p->path);
945 ndirs = 0;
946
947 /*
948 * Build cat pages
949 * addlocale() allocates memory and returns it
950 */
951 ldir = addlocale(p->path);
952 if (!whatonly) {
953 if (*localedir != '\0') {
954 if (defaultmandir)
955 defaultmandir = 0;
956 /* getdirs allocate memory for dv */
957 ndirs = getdirs(ldir, &dv, 1);
958 if (ndirs != 0) {
959 changed = argc ?
960 makecat(ldir, argv, argc) :
961 makecat(ldir, dv, ndirs);
962 /* release memory by getdirs */
963 for (i = 0; i < ndirs; i++) {
964 free(dv[i]);
965 }
966 free(dv);
967 }
968 }
969
970 /* default man dir is always processed */
971 defaultmandir = 1;
972 ndirs = getdirs(p->path, &dv, 1);
973 changed = argc ?
974 makecat(p->path, argv, argc) :
975 makecat(p->path, dv, ndirs);
976 /* release memory allocated by getdirs */
977 for (i = 0; i < ndirs; i++) {
978 free(dv[i]);
979 }
980 free(dv);
981 }
982 /*
983 * Build whatis database
984 * print error message if locale is set and man dir not found
985 * won't build it at all if -c option is on
986 */
987 if (!compargs && (whatonly || (!nowhatis && changed))) {
988 if (*localedir != '\0') {
989 /* just count the number of ndirs */
990 if ((ndirs = getdirs(ldir, NULL, 0)) != 0) {
991 (void) sprintf(cmdbuf,
992 "/usr/bin/sh %s %s",
993 MAKEWHATIS, ldir);
994 (void) sys(cmdbuf);
995 }
996 }
997 /* whatis database of the default man dir */
998 /* will be always built in C locale. */
999 (void) sprintf(cmdbuf,
1000 "/usr/bin/sh %s %s",
1001 MAKEWHATIS, p->path);
1002 (void) sys(cmdbuf);
1003 }
1004 /* release memory allocated by addlocale() */
1005 free(ldir);
1006 }
1007 free_dupnode(dnp);
1008 }
1009
1010 /*
1011 * Build cat pages for given sections
1012 */
1013
1014 static int
makecat(char * path,char ** dv,int ndirs)1015 makecat(char *path, char **dv, int ndirs)
1016 {
1017 DIR *dp, *sdp;
1018 struct dirent *d;
1019 struct stat sbuf;
1020 char mandir[MAXPATHLEN+1];
1021 char smandir[MAXPATHLEN+1];
1022 char catdir[MAXPATHLEN+1];
1023 char *dirp, *sdirp;
1024 int i, fmt;
1025 int manflag, smanflag;
1026
1027 for (i = fmt = 0; i < ndirs; i++) {
1028 (void) snprintf(mandir, MAXPATHLEN, "%s/%s%s",
1029 path, MANDIRNAME, dv[i]);
1030 (void) snprintf(smandir, MAXPATHLEN, "%s/%s%s",
1031 path, SGMLDIR, dv[i]);
1032 (void) snprintf(catdir, MAXPATHLEN, "%s/%s%s",
1033 path, subdirs[1], dv[i]);
1034 dirp = strrchr(mandir, '/') + 1;
1035 sdirp = strrchr(smandir, '/') + 1;
1036
1037 manflag = smanflag = 0;
1038
1039 if ((dp = opendir(mandir)) != NULL)
1040 manflag = 1;
1041
1042 if (!no_sroff && (sdp = opendir(smandir)) != NULL)
1043 smanflag = 1;
1044
1045 if (dp == 0 && sdp == 0) {
1046 if (strcmp(mandir, CONFIG) == 0)
1047 perror(mandir);
1048 continue;
1049 }
1050 /*
1051 * TRANSLATION_NOTE - message for catman -p
1052 * ex. Building cat pages for mandir = /usr/share/man/ja
1053 */
1054 if (debug)
1055 (void) fprintf(stdout, gettext(
1056 "Building cat pages for mandir = %s\n"), path);
1057
1058 if (!compargs && stat(catdir, &sbuf) < 0) {
1059 (void) umask(02);
1060 /*
1061 * TRANSLATION_NOTE - message for catman -p
1062 * ex. mkdir /usr/share/man/ja/cat3c
1063 */
1064 if (debug)
1065 (void) fprintf(stdout, gettext("mkdir %s\n"),
1066 catdir);
1067 else {
1068 if (mkdir(catdir, 0755) < 0) {
1069 perror(catdir);
1070 continue;
1071 }
1072 (void) chmod(catdir, 0755);
1073 }
1074 }
1075
1076 /*
1077 * if it is -c option of catman, if there is no
1078 * coresponding man dir for sman files to go to,
1079 * make the man dir
1080 */
1081
1082 if (compargs && !manflag) {
1083 if (mkdir(mandir, 0755) < 0) {
1084 perror(mandir);
1085 continue;
1086 }
1087 (void) chmod(mandir, 0755);
1088 }
1089
1090 if (smanflag) {
1091 while ((d = readdir(sdp))) {
1092 if (eq(".", d->d_name) || eq("..", d->d_name))
1093 continue;
1094
1095 if (format(path, sdirp, (char *)0, d->d_name)
1096 > 0)
1097 fmt++;
1098 }
1099 }
1100
1101 if (manflag && !compargs) {
1102 while ((d = readdir(dp))) {
1103 if (eq(".", d->d_name) || eq("..", d->d_name))
1104 continue;
1105
1106 if (format(path, dirp, (char *)0, d->d_name)
1107 > 0)
1108 fmt++;
1109 }
1110 }
1111
1112 if (manflag)
1113 (void) closedir(dp);
1114
1115 if (smanflag)
1116 (void) closedir(sdp);
1117
1118 }
1119 return (fmt);
1120 }
1121
1122
1123 /*
1124 * Get all "man" and "sman" dirs under a given manpath
1125 * and return the number found
1126 * If -c option is on, only count sman dirs
1127 */
1128
1129 static int
getdirs(char * path,char *** dirv,short flag)1130 getdirs(char *path, char ***dirv, short flag)
1131 {
1132 DIR *dp;
1133 struct dirent *d;
1134 int n = 0;
1135 int plen, sgml_flag, man_flag;
1136 int i = 0;
1137 int maxentries = MAXDIRS;
1138 char **dv;
1139
1140 if ((dp = opendir(path)) == 0) {
1141 if (debug) {
1142 if (*localedir != '\0')
1143 (void) printf(gettext("\
1144 locale is %s, search in %s\n"), localedir, path);
1145 perror(path);
1146 }
1147 return (0);
1148 }
1149
1150 if (flag) {
1151 /* allocate memory for dirv */
1152 *dirv = (char **)malloc(sizeof (char *) *
1153 maxentries);
1154 if (*dirv == NULL)
1155 malloc_error();
1156 dv = *dirv;
1157 }
1158 while ((d = readdir(dp))) {
1159 plen = PLEN;
1160 man_flag = sgml_flag = 0;
1161 if (match(d->d_name, SGMLDIR, PLEN+1)) {
1162 plen = PLEN + 1;
1163 sgml_flag = 1;
1164 i++;
1165 }
1166
1167 if (match(subdirs[0], d->d_name, PLEN))
1168 man_flag = 1;
1169
1170 if (compargs && sgml_flag) {
1171 if (flag) {
1172 *dv = strdup(d->d_name+plen);
1173 if (*dv == NULL)
1174 malloc_error();
1175 dv++;
1176 n = i;
1177 }
1178 } else if (!compargs && (sgml_flag || man_flag)) {
1179 if (flag) {
1180 *dv = strdup(d->d_name+plen);
1181 if (*dv == NULL)
1182 malloc_error();
1183 dv++;
1184 }
1185 n++;
1186 }
1187 if (flag) {
1188 if ((dv - *dirv) == maxentries) {
1189 int entries = maxentries;
1190 maxentries += MAXTOKENS;
1191 *dirv = (char **)realloc(*dirv,
1192 sizeof (char *) * maxentries);
1193 if (*dirv == NULL)
1194 malloc_error();
1195 dv = *dirv + entries;
1196 }
1197 }
1198 }
1199
1200 (void) closedir(dp);
1201 return (n);
1202 }
1203
1204
1205 /*
1206 * Find matching whatis or apropos entries
1207 * whatapro() tries to handle the windex file of the locale specific
1208 * man dirs first, then tries to handle the windex file of the default
1209 * man dir (of C locale like /usr/share/man).
1210 */
1211
1212 static void
whatapro(struct man_node * manp,char * word,int apropos)1213 whatapro(struct man_node *manp, char *word, int apropos)
1214 {
1215 char whatpath[MAXPATHLEN+1];
1216 char *p;
1217 struct man_node *b;
1218 int ndirs = 0;
1219 char *ldir;
1220
1221
1222 /*
1223 * TRANSLATION_NOTE - message for man -d
1224 * %s takes a parameter to -k option.
1225 */
1226 DPRINTF(gettext("word = %s \n"), word);
1227
1228 /*
1229 * get base part of name
1230 */
1231 if (!apropos) {
1232 if ((p = strrchr(word, '/')) == NULL)
1233 p = word;
1234 else
1235 p++;
1236 } else {
1237 p = word;
1238 }
1239
1240 for (b = manp; b != NULL; b = b->next) {
1241
1242 if (*localedir != '\0') {
1243 /* addlocale() allocates memory and returns it */
1244 ldir = addlocale(b->path);
1245 if (defaultmandir)
1246 defaultmandir = 0;
1247 ndirs = getdirs(ldir, NULL, 0);
1248 if (ndirs != 0) {
1249 (void) sprintf(whatpath, "%s/%s", ldir, WHATIS);
1250 /*
1251 * TRANSLATION_NOTE - message for man -d
1252 * ex. mandir path = /usr/share/man/ja
1253 */
1254 DPRINTF(gettext("\nmandir path = %s\n"), ldir);
1255 lookup_windex(whatpath, p, b->secv);
1256 }
1257 /* release memory allocated by addlocale() */
1258 free(ldir);
1259 }
1260
1261 defaultmandir = 1;
1262 (void) sprintf(whatpath, "%s/%s", b->path, WHATIS);
1263 /*
1264 * TRANSLATION_NOTE - message for man -d
1265 * ex. mandir path = /usr/share/man
1266 */
1267 DPRINTF(gettext("\nmandir path = %s\n"), b->path);
1268
1269 lookup_windex(whatpath, p, b->secv);
1270 }
1271 }
1272
1273
1274 static void
lookup_windex(char * whatpath,char * word,char ** secv)1275 lookup_windex(char *whatpath, char *word, char **secv)
1276 {
1277 FILE *fp;
1278 char *matches[MAXPAGES];
1279 char **pp;
1280 wchar_t wbuf[BUFSIZ];
1281 wchar_t *word_wchar = NULL;
1282 wchar_t *ws;
1283 size_t word_len, ret;
1284
1285 if ((fp = fopen(whatpath, "r")) == NULL) {
1286 perror(whatpath);
1287 return;
1288 }
1289
1290 if (apropos) {
1291 word_len = strlen(word) + 1;
1292 if ((word_wchar = (wchar_t *)malloc(sizeof (wchar_t) *
1293 word_len)) == NULL) {
1294 malloc_error();
1295 }
1296 ret = mbstowcs(word_wchar, (const char *)word, word_len);
1297 if (ret == (size_t)-1) {
1298 (void) fprintf(stderr, gettext(
1299 "Invalid character in keyword\n"));
1300 exit(1);
1301 }
1302 while (fgetws(wbuf, BUFSIZ, fp) != NULL)
1303 for (ws = wbuf; *ws; ws++)
1304 if (icmp(word_wchar, ws) == 0) {
1305 (void) printf("%ws", wbuf);
1306 break;
1307 }
1308 } else {
1309 if (bfsearch(fp, matches, word, secv))
1310 for (pp = matches; *pp; pp++) {
1311 (void) printf("%s", *pp);
1312 /*
1313 * release memory allocated by
1314 * strdup() in bfsearch()
1315 */
1316 free(*pp);
1317 }
1318 }
1319 (void) fclose(fp);
1320 if (word_wchar)
1321 free(word_wchar);
1322
1323 }
1324
1325
1326 /*
1327 * case-insensitive compare unless upper case is used
1328 * ie) "mount" matches mount, Mount, MOUNT
1329 * "Mount" matches Mount, MOUNT
1330 * "MOUNT" matches MOUNT only
1331 * If matched return 0. Otherwise, return 1.
1332 */
1333
1334 static int
icmp(wchar_t * ws,wchar_t * wt)1335 icmp(wchar_t *ws, wchar_t *wt)
1336 {
1337 for (; (*ws == 0) ||
1338 (*ws == (iswupper(*ws) ? *wt: towlower(*wt)));
1339 ws++, wt++)
1340 if (*ws == 0)
1341 return (0);
1342
1343 return (1);
1344 }
1345
1346
1347 /*
1348 * Invoke PAGER with all matching man pages
1349 */
1350
1351 static void
more(char ** pages,int plain)1352 more(char **pages, int plain)
1353 {
1354 char cmdbuf[BUFSIZ];
1355 char **vp;
1356
1357 /*
1358 * Dont bother.
1359 */
1360 if (list || (*pages == 0))
1361 return;
1362
1363 if (plain && troffit) {
1364 cleanup(pages);
1365 return;
1366 }
1367 (void) sprintf(cmdbuf, "%s", troffit ? troffcat :
1368 plain ? CAT : pager);
1369
1370 /*
1371 * Build arg list
1372 */
1373 for (vp = pages; vp < endp; vp++) {
1374 (void) strcat(cmdbuf, " ");
1375 (void) strcat(cmdbuf, *vp);
1376 }
1377 (void) sys(cmdbuf);
1378 cleanup(pages);
1379 }
1380
1381
1382 /*
1383 * Get rid of dregs.
1384 */
1385
1386 static void
cleanup(char ** pages)1387 cleanup(char **pages)
1388 {
1389 char **vp;
1390
1391 for (vp = pages; vp < endp; vp++) {
1392 if (match(TEMPLATE, *vp, TMPLEN))
1393 (void) unlink(*vp);
1394 free(*vp);
1395 }
1396
1397 endp = pages; /* reset */
1398 }
1399
1400
1401 /*
1402 * Clean things up after receiving a signal.
1403 */
1404
1405 /*ARGSUSED*/
1406 static void
bye(int sig)1407 bye(int sig)
1408 {
1409 cleanup(pages);
1410 exit(1);
1411 /*NOTREACHED*/
1412 }
1413
1414
1415 /*
1416 * Split a string by specified separator.
1417 * ignore empty components/adjacent separators.
1418 * returns vector to all tokens
1419 */
1420
1421 static char **
split(char * s1,char sep)1422 split(char *s1, char sep)
1423 {
1424 char **tokv, **vp;
1425 char *mp, *tp;
1426 int maxentries = MAXTOKENS;
1427 int entries = 0;
1428
1429 tokv = vp = (char **)malloc(maxentries * sizeof (char *));
1430 if (tokv == NULL)
1431 malloc_error();
1432 mp = s1;
1433 for (; mp && *mp; mp = tp) {
1434 tp = strchr(mp, sep);
1435 if (mp == tp) { /* empty component */
1436 tp++; /* ignore */
1437 continue;
1438 }
1439 if (tp) {
1440 /* a component found */
1441 size_t len;
1442
1443 len = tp - mp;
1444 *vp = (char *)malloc(sizeof (char) * len + 1);
1445 if (*vp == NULL)
1446 malloc_error();
1447 (void) strncpy(*vp, mp, len);
1448 *(*vp + len) = '\0';
1449 tp++;
1450 vp++;
1451 } else {
1452 /* the last component */
1453 *vp = strdup(mp);
1454 if (*vp == NULL)
1455 malloc_error();
1456 vp++;
1457 }
1458 entries++;
1459 if (entries == maxentries) {
1460 maxentries += MAXTOKENS;
1461 tokv = (char **)realloc(tokv,
1462 maxentries * sizeof (char *));
1463 if (tokv == NULL)
1464 malloc_error();
1465 vp = tokv + entries;
1466 }
1467 }
1468 *vp = 0;
1469 return (tokv);
1470 }
1471
1472 /*
1473 * Free a vector allocated by split();
1474 */
1475 static void
freev(char ** v)1476 freev(char **v)
1477 {
1478 int i;
1479 if (v != NULL) {
1480 for (i = 0; v[i] != NULL; i++) {
1481 free(v[i]);
1482 }
1483 free(v);
1484 }
1485 }
1486
1487 /*
1488 * Convert paths to full paths if necessary
1489 *
1490 */
1491
1492 static void
fullpaths(struct man_node ** manp_head)1493 fullpaths(struct man_node **manp_head)
1494 {
1495 char *cwd = NULL;
1496 char *p;
1497 char cwd_gotten = 0;
1498 struct man_node *manp = *manp_head;
1499 struct man_node *b;
1500 struct man_node *prev = NULL;
1501
1502 for (b = manp; b != NULL; b = b->next) {
1503 if (*(b->path) == '/') {
1504 prev = b;
1505 continue;
1506 }
1507
1508 /* try to get cwd if haven't already */
1509 if (!cwd_gotten) {
1510 cwd = getcwd(NULL, MAXPATHLEN+1);
1511 cwd_gotten = 1;
1512 }
1513
1514 if (cwd) {
1515 /* case: relative manpath with cwd: make absolute */
1516 if ((p = malloc(strlen(b->path)+strlen(cwd)+2)) ==
1517 NULL) {
1518 malloc_error();
1519 }
1520 (void) sprintf(p, "%s/%s", cwd, b->path);
1521 /*
1522 * resetting b->path
1523 */
1524 free(b->path);
1525 b->path = p;
1526 } else {
1527 /* case: relative manpath but no cwd: omit path entry */
1528 if (prev)
1529 prev->next = b->next;
1530 else
1531 *manp_head = b->next;
1532
1533 free_manp(b);
1534 }
1535 }
1536 /*
1537 * release memory allocated by getcwd()
1538 */
1539 free(cwd);
1540 }
1541
1542 /*
1543 * Free a man_node structure and its contents
1544 */
1545
1546 static void
free_manp(struct man_node * manp)1547 free_manp(struct man_node *manp)
1548 {
1549 char **p;
1550
1551 free(manp->path);
1552 p = manp->secv;
1553 while ((p != NULL) && (*p != NULL)) {
1554 free(*p);
1555 p++;
1556 }
1557 free(manp->secv);
1558 free(manp);
1559 }
1560
1561
1562 /*
1563 * Map (in place) to lower case
1564 */
1565
1566 static void
lower(char * s)1567 lower(char *s)
1568 {
1569 if (s == 0)
1570 return;
1571 while (*s) {
1572 if (isupper(*s))
1573 *s = tolower(*s);
1574 s++;
1575 }
1576 }
1577
1578
1579 /*
1580 * compare for sort()
1581 * sort first by section-spec, then by prefix {sman, man, cat, fmt}
1582 * note: prefix is reverse sorted so that "sman" and "man" always
1583 * comes before {cat, fmt}
1584 */
1585
1586 static int
cmp(const void * arg1,const void * arg2)1587 cmp(const void *arg1, const void *arg2)
1588 {
1589 int n;
1590 char **p1 = (char **)arg1;
1591 char **p2 = (char **)arg2;
1592
1593
1594 /* by section; sman always before man dirs */
1595 if ((n = strcmp(*p1 + PLEN + (**p1 == 's' ? 1 : 0),
1596 *p2 + PLEN + (**p2 == 's' ? 1 : 0))))
1597 return (n);
1598
1599 /* by prefix reversed */
1600 return (strncmp(*p2, *p1, PLEN));
1601 }
1602
1603
1604 /*
1605 * Find a man page ...
1606 * Loop through each path specified,
1607 * first try the lookup method (whatis database),
1608 * and if it doesn't exist, do the hard way.
1609 */
1610
1611 static int
manual(struct man_node * manp,char * name)1612 manual(struct man_node *manp, char *name)
1613 {
1614 struct man_node *p;
1615 struct man_node *local;
1616 int ndirs = 0;
1617 char *ldir;
1618 char *ldirs[2];
1619 char *fullname = name;
1620 char *slash;
1621
1622 if ((slash = strrchr(name, '/')) != NULL) {
1623 name = slash + 1;
1624 }
1625
1626 /*
1627 * for each path in MANPATH
1628 */
1629 found = 0;
1630
1631 for (p = manp; p != NULL; p = p->next) {
1632 /*
1633 * TRANSLATION_NOTE - message for man -d
1634 * ex. mandir path = /usr/share/man
1635 */
1636 DPRINTF(gettext("\nmandir path = %s\n"), p->path);
1637
1638 if (*localedir != '\0') {
1639 /* addlocale() allocates memory and returns it */
1640 ldir = addlocale(p->path);
1641 if (defaultmandir)
1642 defaultmandir = 0;
1643 /*
1644 * TRANSLATION_NOTE - message for man -d
1645 * ex. localedir = ja, ldir = /usr/share/man/ja
1646 */
1647 if (debug)
1648 (void) printf(gettext(
1649 "localedir = %s, ldir = %s\n"),
1650 localedir, ldir);
1651 ndirs = getdirs(ldir, NULL, 0);
1652 if (ndirs != 0) {
1653 ldirs[0] = ldir;
1654 ldirs[1] = NULL;
1655 local = build_manpath(ldirs, 0);
1656 if (force ||
1657 windex(local->secv, ldir, name) < 0)
1658 mandir(local->secv, ldir, name);
1659 free_manp(local);
1660 }
1661 /* release memory allocated by addlocale() */
1662 free(ldir);
1663 }
1664
1665 defaultmandir = 1;
1666 /*
1667 * locale mandir not valid, man page in locale
1668 * mandir not found, or -a option present
1669 */
1670 if (ndirs == 0 || !found || all) {
1671 if (force || windex(p->secv, p->path, name) < 0)
1672 mandir(p->secv, p->path, name);
1673 }
1674
1675 if (found && !all)
1676 break;
1677 }
1678
1679 if (found) {
1680 more(pages, nomore);
1681 } else {
1682 if (sargs) {
1683 (void) fprintf(stderr, gettext("No entry for %s in "
1684 "section(s) %s of the manual.\n"),
1685 fullname, mansec);
1686 } else {
1687 (void) fprintf(stderr, gettext(
1688 "No manual entry for %s.\n"), fullname, mansec);
1689 }
1690
1691 if (sman_no_man_no_sroff)
1692 (void) fprintf(stderr, gettext("(An SGML manpage was "
1693 "found for '%s' but it cannot be displayed.)\n"),
1694 fullname, mansec);
1695 }
1696 sman_no_man_no_sroff = 0;
1697 return (!found);
1698 }
1699
1700
1701 /*
1702 * For a specified manual directory,
1703 * read, store, & sort section subdirs,
1704 * for each section specified
1705 * find and search matching subdirs
1706 */
1707
1708 static void
mandir(char ** secv,char * path,char * name)1709 mandir(char **secv, char *path, char *name)
1710 {
1711 DIR *dp;
1712 char **dirv;
1713 char **dv, **pdv;
1714 int len, dslen, plen = PLEN;
1715
1716 if ((dp = opendir(path)) == 0) {
1717 /*
1718 * TRANSLATION_NOTE - message for man -d or catman -p
1719 * opendir(%s) returned 0
1720 */
1721 if (debug)
1722 (void) fprintf(stdout, gettext(
1723 " opendir on %s failed\n"), path);
1724 return;
1725 }
1726
1727 /*
1728 * TRANSLATION_NOTE - message for man -d or catman -p
1729 * ex. mandir path = /usr/share/man/ja
1730 */
1731 if (debug)
1732 (void) printf(gettext("mandir path = %s\n"), path);
1733
1734 /*
1735 * sordir() allocates memory for dirv and dirv[].
1736 */
1737 sortdir(dp, &dirv);
1738 /*
1739 * Search in the order specified by MANSECTS
1740 */
1741 for (; *secv; secv++) {
1742 /*
1743 * TRANSLATION_NOTE - message for man -d or catman -p
1744 * ex. section = 3c
1745 */
1746 DPRINTF(gettext(" section = %s\n"), *secv);
1747 len = strlen(*secv);
1748 for (dv = dirv; *dv; dv++) {
1749 plen = PLEN;
1750 if (*dv[0] == 's')
1751 plen++;
1752 dslen = strlen(*dv+plen);
1753 if (dslen > len)
1754 len = dslen;
1755 if (**secv == '\\') {
1756 if (!eq(*secv + 1, *dv+plen))
1757 continue;
1758 } else if (strncasecmp(*secv, *dv+plen, len) != 0) {
1759 /* check to see if directory name changed */
1760 if (!all &&
1761 (newsection = map_section(*secv, path))
1762 == NULL) {
1763 continue;
1764 }
1765 if (newsection == NULL)
1766 newsection = "";
1767 if (!match(newsection, *dv+plen, len)) {
1768 continue;
1769 }
1770 }
1771
1772 if (searchdir(path, *dv, name) == 0)
1773 continue;
1774
1775 if (!all) {
1776 /* release memory allocated by sortdir() */
1777 pdv = dirv;
1778 while (*pdv) {
1779 free(*pdv);
1780 pdv++;
1781 }
1782 (void) closedir(dp);
1783 /* release memory allocated by sortdir() */
1784 free(dirv);
1785 return;
1786 }
1787 /*
1788 * if we found a match in the man dir skip
1789 * the corresponding cat dir if it exists
1790 */
1791 if (all && **dv == 'm' && *(dv+1) &&
1792 eq(*(dv+1)+plen, *dv+plen))
1793 dv++;
1794 }
1795 }
1796 /* release memory allocated by sortdir() */
1797 pdv = dirv;
1798 while (*pdv) {
1799 free(*pdv);
1800 pdv++;
1801 }
1802 free(dirv);
1803 (void) closedir(dp);
1804 }
1805
1806 /*
1807 * Sort directories.
1808 */
1809
1810 static void
sortdir(DIR * dp,char *** dirv)1811 sortdir(DIR *dp, char ***dirv)
1812 {
1813 struct dirent *d;
1814 char **dv;
1815 int maxentries = MAXDIRS;
1816 int entries = 0;
1817
1818 *dirv = (char **)malloc(sizeof (char *) * maxentries);
1819 dv = *dirv;
1820 while ((d = readdir(dp))) { /* store dirs */
1821 if (eq(d->d_name, ".") || eq(d->d_name, "..")) /* ignore */
1822 continue;
1823
1824 /* check if it matches sman, man, cat format */
1825 if (match(d->d_name, SGMLDIR, PLEN+1) ||
1826 match(d->d_name, subdirs[0], PLEN) ||
1827 match(d->d_name, subdirs[1], PLEN)) {
1828 *dv = malloc(strlen(d->d_name) + 1);
1829 if (*dv == NULL)
1830 malloc_error();
1831 (void) strcpy(*dv, d->d_name);
1832 dv++;
1833 entries++;
1834 if (entries == maxentries) {
1835 maxentries += MAXDIRS;
1836 *dirv = (char **)realloc(*dirv,
1837 sizeof (char *) * maxentries);
1838 if (*dirv == NULL)
1839 malloc_error();
1840 dv = *dirv + entries;
1841 }
1842 }
1843 }
1844 *dv = 0;
1845
1846 qsort((void *)*dirv, dv - *dirv, sizeof (char *), cmp);
1847
1848 }
1849
1850
1851 /*
1852 * Search a section subdirectory for a
1853 * given man page, return 1 for success
1854 */
1855
1856 static int
searchdir(char * path,char * dir,char * name)1857 searchdir(char *path, char *dir, char *name)
1858 {
1859 DIR *sdp;
1860 struct dirent *sd;
1861 char sectpath[MAXPATHLEN+1];
1862 char file[MAXNAMLEN+1];
1863 char dname[MAXPATHLEN+1];
1864 char *last;
1865 int nlen;
1866
1867 /*
1868 * TRANSLATION_NOTE - message for man -d or catman -p
1869 * ex. scanning = man3c
1870 */
1871 DPRINTF(gettext(" scanning = %s\n"), dir);
1872 (void) sprintf(sectpath, "%s/%s", path, dir);
1873 (void) snprintf(file, MAXPATHLEN, "%s.", name);
1874
1875 if ((sdp = opendir(sectpath)) == 0) {
1876 if (errno != ENOTDIR) /* ignore matching cruft */
1877 perror(sectpath);
1878 return (0);
1879 }
1880 while ((sd = readdir(sdp))) {
1881 last = strrchr(sd->d_name, '.');
1882 nlen = last - sd->d_name;
1883 (void) sprintf(dname, "%.*s.", nlen, sd->d_name);
1884 if (eq(dname, file) || eq(sd->d_name, name)) {
1885 if (no_sroff && *dir == 's') {
1886 sman_no_man_no_sroff = 1;
1887 return (0);
1888 }
1889 (void) format(path, dir, name, sd->d_name);
1890 (void) closedir(sdp);
1891 return (1);
1892 }
1893 }
1894 (void) closedir(sdp);
1895 return (0);
1896 }
1897
1898 /*
1899 * Check the hash table of old directory names to see if there is a
1900 * new directory name.
1901 * Returns new directory name if a match; after checking to be sure
1902 * directory exists.
1903 * Otherwise returns NULL
1904 */
1905
1906 static char *
map_section(char * section,char * path)1907 map_section(char *section, char *path)
1908 {
1909 int i;
1910 int len;
1911 char fullpath[MAXPATHLEN];
1912
1913 if (list) /* -l option fall through */
1914 return (NULL);
1915
1916 for (i = 0; i <= ((sizeof (map)/sizeof (map[0]) - 1)); i++) {
1917 if (strlen(section) > strlen(map[i].new_name)) {
1918 len = strlen(section);
1919 } else {
1920 len = strlen(map[i].new_name);
1921 }
1922 if (match(section, map[i].old_name, len)) {
1923 (void) sprintf(fullpath,
1924 "%s/sman%s", path, map[i].new_name);
1925 if (!access(fullpath, R_OK | X_OK)) {
1926 return (map[i].new_name);
1927 } else {
1928 return (NULL);
1929 }
1930 }
1931 }
1932
1933 return (NULL);
1934 }
1935
1936
1937 /*
1938 * Use windex database for quick lookup of man pages
1939 * instead of mandir() (brute force search)
1940 */
1941
1942 static int
windex(char ** secv,char * path,char * name)1943 windex(char **secv, char *path, char *name)
1944 {
1945 FILE *fp;
1946 struct stat sbuf;
1947 struct suffix *sp;
1948 struct suffix psecs[MAXPAGES+1];
1949 char whatfile[MAXPATHLEN+1];
1950 char page[MAXPATHLEN+1];
1951 char *matches[MAXPAGES];
1952 char *file, *dir;
1953 char **sv, **vp;
1954 int len, dslen, exist, i;
1955 int found_in_windex = 0;
1956 char *tmp[] = {0, 0, 0, 0};
1957
1958
1959 (void) sprintf(whatfile, "%s/%s", path, WHATIS);
1960 if ((fp = fopen(whatfile, "r")) == NULL) {
1961 if (errno == ENOENT)
1962 return (-1);
1963 return (0);
1964 }
1965
1966 /*
1967 * TRANSLATION_NOTE - message for man -d or catman -p
1968 * ex. search in = /usr/share/man/ja/windex file
1969 */
1970 if (debug)
1971 (void) fprintf(stdout, gettext(
1972 " search in = %s file\n"), whatfile);
1973
1974 if (bfsearch(fp, matches, name, NULL) == 0) {
1975 (void) fclose(fp);
1976 return (-1); /* force search in mandir */
1977 }
1978
1979 (void) fclose(fp);
1980
1981 /*
1982 * Save and split sections
1983 * section() allocates memory for sp->ds
1984 */
1985 for (sp = psecs, vp = matches; *vp; vp++, sp++) {
1986 if ((sp - psecs) < MAXPAGES) {
1987 section(sp, *vp);
1988 } else {
1989 if (debug)
1990 (void) fprintf(stderr, gettext(
1991 "too many sections in %s windex entry\n"),
1992 name);
1993
1994 /* Setting sp->ds to NULL signifies end-of-data. */
1995 sp->ds = 0;
1996 goto finish;
1997 }
1998 }
1999
2000 sp->ds = 0;
2001
2002 /*
2003 * Search in the order specified
2004 * by MANSECTS
2005 */
2006 for (; *secv; secv++) {
2007 len = strlen(*secv);
2008
2009 /*
2010 * TRANSLATION_NOTE - message for man -d or catman -p
2011 * ex. search an entry to match printf.3c
2012 */
2013 if (debug)
2014 (void) fprintf(stdout, gettext(
2015 " search an entry to match %s.%s\n"), name, *secv);
2016 /*
2017 * For every whatis entry that
2018 * was matched
2019 */
2020 for (sp = psecs; sp->ds; sp++) {
2021 dslen = strlen(sp->ds);
2022 if (dslen > len)
2023 len = dslen;
2024 if (**secv == '\\') {
2025 if (!eq(*secv + 1, sp->ds))
2026 continue;
2027 } else if (!match(*secv, sp->ds, len)) {
2028 /* check to see if directory name changed */
2029 if (!all &&
2030 (newsection = map_section(*secv, path))
2031 == NULL) {
2032 continue;
2033 }
2034 if (newsection == NULL)
2035 newsection = "";
2036 if (!match(newsection, sp->ds, len)) {
2037 continue;
2038 }
2039 }
2040 /*
2041 * here to form "sman", "man", "cat"|"fmt" in
2042 * order
2043 */
2044 if (!no_sroff) {
2045 tmp[0] = SGMLDIR;
2046 for (i = 1; i < 4; i++)
2047 tmp[i] = subdirs[i-1];
2048 } else {
2049 for (i = 0; i < 3; i++)
2050 tmp[i] = subdirs[i];
2051 }
2052
2053 for (sv = tmp; *sv; sv++) {
2054 (void) sprintf(page,
2055 "%s/%s%s/%s%s%s", path, *sv,
2056 sp->ds, name, *sp->fs ? "." : "",
2057 sp->fs);
2058 exist = (stat(page, &sbuf) == 0);
2059 if (exist)
2060 break;
2061 }
2062 if (!exist) {
2063 (void) fprintf(stderr, gettext(
2064 "%s entry incorrect: %s(%s) not found.\n"),
2065 WHATIS, name, sp->ds);
2066 continue;
2067 }
2068
2069 file = strrchr(page, '/'), *file = 0;
2070 dir = strrchr(page, '/');
2071
2072 /*
2073 * By now we have a match
2074 */
2075 found_in_windex = 1;
2076 (void) format(path, ++dir, name, ++file);
2077
2078 if (!all)
2079 goto finish;
2080 }
2081 }
2082 finish:
2083 /*
2084 * release memory allocated by section()
2085 */
2086 sp = psecs;
2087 while (sp->ds) {
2088 free(sp->ds);
2089 sp->ds = NULL;
2090 sp++;
2091 }
2092
2093 /*
2094 * If we didn't find a match, return failure as if we didn't find
2095 * the windex at all. Why? Well, if you create a windex, then upgrade
2096 * to a later release that contains new man pages, and forget to
2097 * recreate the windex (since we don't do that automatically), you
2098 * won't see any new man pages since they aren't in the windex.
2099 * Pretending we didn't see a windex at all if there are no matches
2100 * forces a search of the underlying directory. After all, the
2101 * goal of the windex is to enable searches (man -k) and speed things
2102 * up, not to _prevent_ you from seeing new man pages, so this seems
2103 * ok. The only problem is when there are multiple entries (different
2104 * sections), and some are in and some are out. Say you do 'man ls',
2105 * and ls(1) isn't in the windex, but ls(1B) is. In that case, we
2106 * will find a match in ls(1B), and you'll see that man page.
2107 * That doesn't seem bad since if you specify the section the search
2108 * will be restricted too. So in the example above, if you do
2109 * 'man -s 1 ls' you'll get ls(1).
2110 */
2111 if (found_in_windex)
2112 return (0);
2113 else
2114 return (-1);
2115 }
2116
2117
2118 /*
2119 * Return pointers to the section-spec
2120 * and file-suffix of a whatis entry
2121 */
2122
2123 static void
section(struct suffix * sp,char * s)2124 section(struct suffix *sp, char *s)
2125 {
2126 char *lp, *p;
2127
2128 lp = strchr(s, '(');
2129 p = strchr(s, ')');
2130
2131 if (++lp == 0 || p == 0 || lp == p) {
2132 (void) fprintf(stderr,
2133 gettext("mangled windex entry:\n\t%s\n"), s);
2134 return;
2135 }
2136 *p = 0;
2137
2138 /*
2139 * copy the string pointed to by lp
2140 */
2141 lp = strdup(lp);
2142 if (lp == NULL)
2143 malloc_error();
2144 /*
2145 * release memory in s
2146 * s has been allocated memory in bfsearch()
2147 */
2148 free(s);
2149
2150 lower(lp);
2151
2152 /*
2153 * split section-specifier if file-name
2154 * suffix differs from section-suffix
2155 */
2156 sp->ds = lp;
2157 if ((p = strchr(lp, '/'))) {
2158 *p++ = 0;
2159 sp->fs = p;
2160 } else
2161 sp->fs = lp;
2162 }
2163
2164
2165 /*
2166 * Binary file search to find matching man
2167 * pages in whatis database.
2168 */
2169
2170 static int
bfsearch(FILE * fp,char ** matchv,char * key,char ** secv)2171 bfsearch(FILE *fp, char **matchv, char *key, char **secv)
2172 {
2173 char entry[BUFSIZ];
2174 char **vp;
2175 long top, bot, mid;
2176 int c;
2177
2178 vp = matchv;
2179 bot = 0;
2180 (void) fseek(fp, 0L, 2);
2181 top = ftell(fp);
2182 for (;;) {
2183 mid = (top+bot)/2;
2184 (void) fseek(fp, mid, 0);
2185 do {
2186 c = getc(fp);
2187 mid++;
2188 } while (c != EOF && c != '\n');
2189 if (fgets(entry, sizeof (entry), fp) == NULL)
2190 break;
2191 switch (compare(key, entry, secv)) {
2192 case -2:
2193 case -1:
2194 case 0:
2195 if (top <= mid)
2196 break;
2197 top = mid;
2198 continue;
2199 case 1:
2200 case 2:
2201 bot = mid;
2202 continue;
2203 }
2204 break;
2205 }
2206 (void) fseek(fp, bot, 0);
2207 while (ftell(fp) < top) {
2208 if (fgets(entry, sizeof (entry), fp) == NULL) {
2209 *matchv = 0;
2210 return (matchv - vp);
2211 }
2212 switch (compare(key, entry, secv)) {
2213 case -2:
2214 *matchv = 0;
2215 return (matchv - vp);
2216 case -1:
2217 case 0:
2218 *matchv = strdup(entry);
2219 if (*matchv == NULL)
2220 malloc_error();
2221 else
2222 matchv++;
2223 break;
2224 case 1:
2225 case 2:
2226 continue;
2227 }
2228 break;
2229 }
2230 while (fgets(entry, sizeof (entry), fp)) {
2231 switch (compare(key, entry, secv)) {
2232 case -1:
2233 case 0:
2234 *matchv = strdup(entry);
2235 if (*matchv == NULL)
2236 malloc_error();
2237 else
2238 matchv++;
2239 continue;
2240 }
2241 break;
2242 }
2243 *matchv = 0;
2244 return (matchv - vp);
2245 }
2246
2247 static int
compare(char * key,char * entry,char ** secv)2248 compare(char *key, char *entry, char **secv)
2249 {
2250 char *entbuf;
2251 char *s;
2252 int comp, mlen;
2253 int mbcurmax = MB_CUR_MAX;
2254 char *secp = NULL;
2255 int rv;
2256 int eblen;
2257
2258 entbuf = strdup(entry);
2259 if (entbuf == NULL) {
2260 malloc_error();
2261 }
2262 eblen = strlen(entbuf);
2263
2264 s = entbuf;
2265 while (*s) {
2266 if (*s == '\t' || *s == ' ') {
2267 *s = '\0';
2268 break;
2269 }
2270 mlen = mblen(s, mbcurmax);
2271 if (mlen == -1) {
2272 (void) fprintf(stderr, gettext(
2273 "Invalid character in windex file.\n"));
2274 exit(1);
2275 }
2276 s += mlen;
2277 }
2278 /*
2279 * Find the section within parantheses
2280 */
2281 if (secv != NULL && (s - entbuf) < eblen) {
2282 if ((secp = strchr(s + 1, ')')) != NULL) {
2283 *secp = '\0';
2284 if ((secp = strchr(s + 1, '(')) != NULL) {
2285 secp++;
2286 }
2287 }
2288 }
2289
2290 comp = strcmp(key, entbuf);
2291 if (comp == 0) {
2292 if (secp == NULL) {
2293 rv = 0;
2294 } else {
2295 while (*secv != NULL) {
2296 if ((strcmp(*secv, secp)) == 0) {
2297 rv = 0;
2298 break;
2299 }
2300 secv++;
2301 }
2302 }
2303 } else if (comp < 0) {
2304 rv = -2;
2305 } else {
2306 rv = 2;
2307 }
2308 free(entbuf);
2309 return (rv);
2310 }
2311
2312
2313 /*
2314 * Format a man page and follow .so references
2315 * if necessary.
2316 */
2317
2318 static int
format(char * path,char * dir,char * name,char * pg)2319 format(char *path, char *dir, char *name, char *pg)
2320 {
2321 char manpname[MAXPATHLEN+1], catpname[MAXPATHLEN+1];
2322 char manpname_sgml[MAXPATHLEN+1], smantmpname[MAXPATHLEN+1];
2323 char soed[MAXPATHLEN+1], soref[MAXPATHLEN+1];
2324 char manbuf[BUFSIZ], cmdbuf[BUFSIZ], tmpbuf[BUFSIZ];
2325 char tmpdir[MAXPATHLEN+1];
2326 int socount, updatedcat, regencat;
2327 struct stat mansb, catsb, smansb;
2328 char *tmpname;
2329 int catonly = 0;
2330 struct stat statb;
2331 int plen = PLEN;
2332 FILE *md;
2333 int tempfd;
2334 ssize_t count;
2335 int temp, sgml_flag = 0, check_flag = 0;
2336 char prntbuf[BUFSIZ + 1];
2337 char *ptr;
2338 char *new_m;
2339 char *tmpsubdir;
2340
2341 found++;
2342
2343 if (*dir != 'm' && *dir != 's')
2344 catonly++;
2345
2346
2347 if (*dir == 's') {
2348 tmpsubdir = SGMLDIR;
2349 ++plen;
2350 (void) sprintf(manpname_sgml, "%s/man%s/%s",
2351 path, dir+plen, pg);
2352 } else
2353 tmpsubdir = MANDIRNAME;
2354
2355 if (list) {
2356 (void) printf(gettext("%s (%s)\t-M %s\n"),
2357 name, dir+plen, path);
2358 return (-1);
2359 }
2360
2361 (void) sprintf(manpname, "%s/%s%s/%s", path, tmpsubdir, dir+plen, pg);
2362 (void) sprintf(catpname, "%s/%s%s/%s", path, subdirs[1], dir+plen, pg);
2363
2364 (void) sprintf(smantmpname, "%s/%s%s/%s", path, SGMLDIR, dir+plen, pg);
2365
2366 /*
2367 * TRANSLATION_NOTE - message for man -d or catman -p
2368 * ex. unformatted = /usr/share/man/ja/man3s/printf.3s
2369 */
2370 DPRINTF(gettext(
2371 " unformatted = %s\n"), catonly ? "" : manpname);
2372 /*
2373 * TRANSLATION_NOTE - message for man -d or catman -p
2374 * ex. formatted = /usr/share/man/ja/cat3s/printf.3s
2375 */
2376 DPRINTF(gettext(
2377 " formatted = %s\n"), catpname);
2378
2379 /*
2380 * Take care of indirect references to other man pages;
2381 * i.e., resolve files containing only ".so manx/file.x".
2382 * We follow .so chains, replacing title with the .so'ed
2383 * file at each stage, and keeping track of how many times
2384 * we've done so, so that we can avoid looping.
2385 */
2386 *soed = 0;
2387 socount = 0;
2388 for (;;) {
2389 FILE *md;
2390 char *cp;
2391 char *s;
2392 char *new_s;
2393
2394 if (catonly)
2395 break;
2396 /*
2397 * Grab manpname's first line, stashing it in manbuf.
2398 */
2399
2400
2401 if ((md = fopen(manpname, "r")) == NULL) {
2402 if (*soed && errno == ENOENT) {
2403 (void) fprintf(stderr,
2404 gettext("Can't find referent of "
2405 ".so in %s\n"), soed);
2406 (void) fflush(stderr);
2407 return (-1);
2408 }
2409 perror(manpname);
2410 return (-1);
2411 }
2412
2413 /*
2414 * If this is a directory, just ignore it.
2415 */
2416 if (fstat(fileno(md), &statb) == NULL) {
2417 if (S_ISDIR(statb.st_mode)) {
2418 if (debug) {
2419 (void) fprintf(stderr,
2420 "\tignoring directory %s\n",
2421 manpname);
2422 (void) fflush(stderr);
2423 }
2424 (void) fclose(md);
2425 return (-1);
2426 }
2427 }
2428
2429 if (fgets(manbuf, BUFSIZ-1, md) == NULL) {
2430 (void) fclose(md);
2431 (void) fprintf(stderr, gettext("%s: null file\n"),
2432 manpname);
2433 (void) fflush(stderr);
2434 return (-1);
2435 }
2436 (void) fclose(md);
2437
2438 if (strncmp(manbuf, DOT_SO, sizeof (DOT_SO) - 1))
2439 break;
2440 so_again: if (++socount > SOLIMIT) {
2441 (void) fprintf(stderr, gettext(".so chain too long\n"));
2442 (void) fflush(stderr);
2443 return (-1);
2444 }
2445 s = manbuf + sizeof (DOT_SO) - 1;
2446 if ((check_flag == 1) && ((new_s = strrchr(s, '/')) != NULL)) {
2447 new_s++;
2448 (void) sprintf(s, "%s%s/%s",
2449 tmpsubdir, dir+plen, new_s);
2450 }
2451
2452 cp = strrchr(s, '\n');
2453 if (cp)
2454 *cp = '\0';
2455 /*
2456 * Compensate for sloppy typists by stripping
2457 * trailing white space.
2458 */
2459 cp = s + strlen(s);
2460 while (--cp >= s && (*cp == ' ' || *cp == '\t'))
2461 *cp = '\0';
2462
2463 /*
2464 * Go off and find the next link in the chain.
2465 */
2466 (void) strcpy(soed, manpname);
2467 (void) strcpy(soref, s);
2468 (void) sprintf(manpname, "%s/%s", path, s);
2469 /*
2470 * TRANSLATION_NOTE - message for man -d or catman -p
2471 * ex. .so ref = man3c/string.3c
2472 */
2473 DPRINTF(gettext(".so ref = %s\n"), s);
2474 }
2475
2476 /*
2477 * Make symlinks if so'ed and cattin'
2478 */
2479 if (socount && catmando) {
2480 (void) sprintf(cmdbuf, "cd %s; rm -f %s; ln -s ../%s%s %s",
2481 path, catpname, subdirs[1], soref+plen, catpname);
2482 (void) sys(cmdbuf);
2483 return (1);
2484 }
2485
2486 /*
2487 * Obtain the cat page that corresponds to the man page.
2488 * If it already exists, is up to date, and if we haven't
2489 * been told not to use it, use it as it stands.
2490 */
2491 regencat = updatedcat = 0;
2492 if (compargs || (!catonly && stat(manpname, &mansb) >= 0 &&
2493 (stat(catpname, &catsb) < 0 || catsb.st_mtime < mansb.st_mtime)) ||
2494 (access(catpname, R_OK) != 0)) {
2495 /*
2496 * Construct a shell command line for formatting manpname.
2497 * The resulting file goes initially into /tmp. If possible,
2498 * it will later be moved to catpname.
2499 */
2500
2501 int pipestage = 0;
2502 int needcol = 0;
2503 char *cbp = cmdbuf;
2504
2505 regencat = updatedcat = 1;
2506
2507 if (!catmando && !debug && !check_flag) {
2508 (void) fprintf(stderr, gettext(
2509 "Reformatting page. Please Wait..."));
2510 if (sargs && (newsection != NULL) &&
2511 (*newsection != '\0')) {
2512 (void) fprintf(stderr, gettext(
2513 "\nThe directory name has been changed "
2514 "to %s\n"), newsection);
2515 }
2516 (void) fflush(stderr);
2517 }
2518
2519 /*
2520 * in catman command, if the file exists in sman dir already,
2521 * don't need to convert the file in man dir to cat dir
2522 */
2523
2524 if (!no_sroff && catmando &&
2525 match(tmpsubdir, MANDIRNAME, PLEN) &&
2526 stat(smantmpname, &smansb) >= 0)
2527 return (1);
2528
2529 /*
2530 * cd to path so that relative .so commands will work
2531 * correctly
2532 */
2533 (void) sprintf(cbp, "cd %s; ", path);
2534 cbp += strlen(cbp);
2535
2536
2537 /*
2538 * check to see whether it is a sgml file
2539 * assume sgml symbol(>!DOCTYPE) can be found in the first
2540 * BUFSIZ bytes
2541 */
2542
2543 if ((temp = open(manpname, 0)) == -1) {
2544 perror(manpname);
2545 return (-1);
2546 }
2547
2548 if ((count = read(temp, prntbuf, BUFSIZ)) <= 0) {
2549 perror(manpname);
2550 return (-1);
2551 }
2552
2553 prntbuf[count] = '\0'; /* null terminate */
2554 ptr = prntbuf;
2555 if (sgmlcheck((const char *)ptr) == 1) {
2556 sgml_flag = 1;
2557 if (defaultmandir && *localedir) {
2558 (void) sprintf(cbp, "LC_MESSAGES=C %s %s ",
2559 SROFF_CMD, manpname);
2560 } else {
2561 (void) sprintf(cbp, "%s %s ",
2562 SROFF_CMD, manpname);
2563 }
2564 cbp += strlen(cbp);
2565 } else if (*dir == 's') {
2566 (void) close(temp);
2567 return (-1);
2568 }
2569 (void) close(temp);
2570
2571 /*
2572 * Check for special formatting requirements by examining
2573 * manpname's first line preprocessor specifications.
2574 */
2575
2576 if (strncmp(manbuf, PREPROC_SPEC,
2577 sizeof (PREPROC_SPEC) - 1) == 0) {
2578 char *ptp;
2579
2580 ptp = manbuf + sizeof (PREPROC_SPEC) - 1;
2581 while (*ptp && *ptp != '\n') {
2582 const struct preprocessor *pp;
2583
2584 /*
2585 * Check for a preprocessor we know about.
2586 */
2587 for (pp = preprocessors; pp->p_tag; pp++) {
2588 if (pp->p_tag == *ptp)
2589 break;
2590 }
2591 if (pp->p_tag == 0) {
2592 (void) fprintf(stderr,
2593 gettext("unknown preprocessor "
2594 "specifier %c\n"), *ptp);
2595 (void) fflush(stderr);
2596 return (-1);
2597 }
2598
2599 /*
2600 * Add it to the pipeline.
2601 */
2602 (void) sprintf(cbp, "%s %s |",
2603 troffit ? pp->p_troff : pp->p_nroff,
2604 pipestage++ == 0 ? manpname :
2605 pp->p_stdin_char);
2606 cbp += strlen(cbp);
2607
2608 /*
2609 * Special treatment: if tbl is among the
2610 * preprocessors and we'll process with
2611 * nroff, we have to pass things through
2612 * col at the end of the pipeline.
2613 */
2614 if (pp->p_tag == 't' && !troffit)
2615 needcol++;
2616
2617 ptp++;
2618 }
2619 }
2620
2621 /*
2622 * if catman, use the cat page name
2623 * otherwise, dup template and create another
2624 * (needed for multiple pages)
2625 */
2626 if (catmando)
2627 tmpname = catpname;
2628 else {
2629 tmpname = strdup(TEMPLATE);
2630 if (tmpname == NULL)
2631 malloc_error();
2632 (void) close(mkstemp(tmpname));
2633 }
2634
2635 if (! Tflag) {
2636 if (*localedir != '\0') {
2637 (void) sprintf(macros, "%s/%s", path, MACROF);
2638 /*
2639 * TRANSLATION_NOTE - message for man -d or catman -p
2640 * ex. locale macros = /usr/share/man/ja/tmac.an
2641 */
2642 if (debug)
2643 (void) printf(gettext(
2644 "\nlocale macros = %s "),
2645 macros);
2646 if (stat(macros, &statb) < 0)
2647 (void) strcpy(macros, TMAC_AN);
2648 /*
2649 * TRANSLATION_NOTE - message for man -d or catman -p
2650 * ex. macros = /usr/share/man/ja/tman.an
2651 */
2652 if (debug)
2653 (void) printf(gettext(
2654 "\nmacros = %s\n"),
2655 macros);
2656 }
2657 }
2658
2659 tmpdir[0] = '\0';
2660 if (sgml_flag == 1) {
2661 if (check_flag == 0) {
2662 strcpy(tmpdir, "/tmp/sman_XXXXXX");
2663 if ((tempfd = mkstemp(tmpdir)) == -1) {
2664 (void) fprintf(stderr, gettext(
2665 "%s: null file\n"), tmpdir);
2666 (void) fflush(stderr);
2667 return (-1);
2668 }
2669
2670 if (debug)
2671 close(tempfd);
2672
2673 (void) sprintf(tmpbuf, "%s > %s",
2674 cmdbuf, tmpdir);
2675 if (sys(tmpbuf)) {
2676 /*
2677 * TRANSLATION_NOTE - message for man -d or catman -p
2678 * Error message if sys(%s) failed
2679 */
2680 (void) fprintf(stderr, gettext(
2681 "sys(%s) fail!\n"), tmpbuf);
2682 (void) fprintf(stderr,
2683 gettext(" aborted (sorry)\n"));
2684 (void) fflush(stderr);
2685 /* release memory for tmpname */
2686 if (!catmando) {
2687 (void) unlink(tmpdir);
2688 (void) unlink(tmpname);
2689 free(tmpname);
2690 }
2691 return (-1);
2692 } else if (debug == 0) {
2693 if ((md = fdopen(tempfd, "r"))
2694 == NULL) {
2695 (void) fprintf(stderr, gettext(
2696 "%s: null file\n"), tmpdir);
2697 (void) fflush(stderr);
2698 close(tempfd);
2699 /* release memory for tmpname */
2700 if (!catmando)
2701 free(tmpname);
2702 return (-1);
2703 }
2704
2705 /* if the file is empty, */
2706 /* it's a fragment, do nothing */
2707 if (fgets(manbuf, BUFSIZ-1, md)
2708 == NULL) {
2709 (void) fclose(md);
2710 /* release memory for tmpname */
2711 if (!catmando)
2712 free(tmpname);
2713 return (1);
2714 }
2715 (void) fclose(md);
2716
2717 if (strncmp(manbuf, DOT_SO,
2718 sizeof (DOT_SO) - 1) == 0) {
2719 if (!compargs) {
2720 check_flag = 1;
2721 (void) unlink(tmpdir);
2722 (void) unlink(tmpname);
2723 /* release memory for tmpname */
2724 if (!catmando)
2725 free(tmpname);
2726 goto so_again;
2727 } else {
2728 (void) unlink(tmpdir);
2729 strcpy(tmpdir,
2730 "/tmp/sman_XXXXXX");
2731 tempfd = mkstemp(tmpdir);
2732 if ((tempfd == -1) ||
2733 (md = fdopen(tempfd, "w"))
2734 == NULL) {
2735 (void) fprintf(stderr,
2736 gettext(
2737 "%s: null file\n"),
2738 tmpdir);
2739 (void) fflush(stderr);
2740 if (tempfd != -1)
2741 close(tempfd);
2742 /* release memory for tmpname */
2743 if (!catmando)
2744 free(tmpname);
2745 return (-1);
2746 }
2747 if ((new_m = strrchr(manbuf, '/')) != NULL) {
2748 (void) fprintf(md, ".so man%s%s\n", dir+plen, new_m);
2749 } else {
2750 /*
2751 * TRANSLATION_NOTE - message for catman -c
2752 * Error message if unable to get file name
2753 */
2754 (void) fprintf(stderr,
2755 gettext("file not found\n"));
2756 (void) fflush(stderr);
2757 return (-1);
2758 }
2759 (void) fclose(md);
2760 }
2761 }
2762 }
2763 if (catmando && compargs)
2764 (void) sprintf(cmdbuf, "cat %s > %s",
2765 tmpdir, manpname_sgml);
2766 else
2767 (void) sprintf(cmdbuf, " cat %s | tbl | eqn | %s %s - %s > %s",
2768 tmpdir, troffit ? troffcmd : "nroff -u0 -Tlp",
2769 macros, troffit ? "" : " | col -x", tmpname);
2770 } else
2771 if (catmando && compargs)
2772 (void) sprintf(cbp, " > %s",
2773 manpname_sgml);
2774 else
2775 (void) sprintf(cbp, " | tbl | eqn | %s %s - %s > %s",
2776 troffit ? troffcmd : "nroff -u0 -Tlp",
2777 macros, troffit ? "" : " | col -x", tmpname);
2778
2779 } else
2780 (void) sprintf(cbp, "%s %s %s%s > %s",
2781 troffit ? troffcmd : "nroff -u0 -Tlp",
2782 macros, pipestage == 0 ? manpname : "-",
2783 troffit ? "" : " | col -x", tmpname);
2784
2785 /* Reformat the page. */
2786 if (sys(cmdbuf)) {
2787 /*
2788 * TRANSLATION_NOTE - message for man -d or catman -p
2789 * Error message if sys(%s) failed
2790 */
2791 (void) fprintf(stderr, gettext(
2792 "sys(%s) fail!\n"), cmdbuf);
2793 (void) fprintf(stderr, gettext(" aborted (sorry)\n"));
2794 (void) fflush(stderr);
2795 (void) unlink(tmpname);
2796 /* release memory for tmpname */
2797 if (!catmando)
2798 free(tmpname);
2799 return (-1);
2800 }
2801
2802 if (tmpdir[0] != '\0')
2803 (void) unlink(tmpdir);
2804
2805 if (catmando)
2806 return (1);
2807
2808 /*
2809 * Attempt to move the cat page to its proper home.
2810 */
2811 (void) sprintf(cmdbuf,
2812 "trap '' 1 15; /usr/bin/mv -f %s %s 2> /dev/null",
2813 tmpname,
2814 catpname);
2815 if (sys(cmdbuf))
2816 updatedcat = 0;
2817 else if (debug == 0)
2818 (void) chmod(catpname, 0644);
2819
2820 if (debug) {
2821 /* release memory for tmpname */
2822 if (!catmando)
2823 free(tmpname);
2824 (void) unlink(tmpname);
2825 return (1);
2826 }
2827
2828 (void) fprintf(stderr, gettext(" done\n"));
2829 (void) fflush(stderr);
2830 }
2831
2832 /*
2833 * Save file name (dup if necessary)
2834 * to view later
2835 * fix for 1123802 - don't save names if we are invoked as catman
2836 */
2837 if (!catmando) {
2838 char **tmpp;
2839 int dup;
2840 char *newpage;
2841
2842 if (regencat && !updatedcat)
2843 newpage = tmpname;
2844 else {
2845 newpage = strdup(catpname);
2846 if (newpage == NULL)
2847 malloc_error();
2848 }
2849 /* make sure we don't add a dup */
2850 dup = 0;
2851 for (tmpp = pages; tmpp < endp; tmpp++) {
2852 if (strcmp(*tmpp, newpage) == 0) {
2853 dup = 1;
2854 break;
2855 }
2856 }
2857 if (!dup)
2858 *endp++ = newpage;
2859 if (endp >= &pages[MAXPAGES]) {
2860 fprintf(stderr,
2861 gettext("Internal pages array overflow!\n"));
2862 exit(1);
2863 }
2864 }
2865
2866 return (regencat);
2867 }
2868
2869 /*
2870 * Add <localedir> to the path.
2871 */
2872
2873 static char *
addlocale(char * path)2874 addlocale(char *path)
2875 {
2876
2877 char *tmp;
2878
2879 tmp = malloc(strlen(path) + strlen(localedir) + 2);
2880 if (tmp == NULL)
2881 malloc_error();
2882 (void) sprintf(tmp, "%s/%s", path, localedir);
2883 return (tmp);
2884
2885 }
2886
2887 /*
2888 * From the configuration file "man.cf", get the order of suffices of
2889 * sub-mandirs to be used in the search path for a given mandir.
2890 */
2891
2892 static char *
check_config(char * path)2893 check_config(char *path)
2894 {
2895 FILE *fp;
2896 static char submandir[BUFSIZ];
2897 char *sect;
2898 char fname[MAXPATHLEN];
2899
2900 (void) sprintf(fname, "%s/%s", path, CONFIG);
2901
2902 if ((fp = fopen(fname, "r")) == NULL)
2903 return (NULL);
2904 else {
2905 if (get_manconfig(fp, submandir) == -1) {
2906 (void) fclose(fp);
2907 return (NULL);
2908 }
2909
2910 (void) fclose(fp);
2911
2912 sect = strchr(submandir, '=');
2913 if (sect != NULL)
2914 return (++sect);
2915 else
2916 return (NULL);
2917 }
2918 }
2919
2920 /*
2921 * This routine is for getting the MANSECTS entry from man.cf.
2922 * It sets submandir to the line in man.cf that contains
2923 * MANSECTS=sections[,sections]...
2924 */
2925
2926 static int
get_manconfig(FILE * fp,char * submandir)2927 get_manconfig(FILE *fp, char *submandir)
2928 {
2929 char *s, *t, *rc;
2930 char buf[BUFSIZ];
2931
2932 while ((rc = fgets(buf, sizeof (buf), fp)) != NULL) {
2933
2934 /*
2935 * skip leading blanks
2936 */
2937 for (t = buf; *t != '\0'; t++) {
2938 if (!isspace(*t))
2939 break;
2940 }
2941 /*
2942 * skip line that starts with '#' or empty line
2943 */
2944 if (*t == '#' || *t == '\0')
2945 continue;
2946
2947 if (strstr(buf, "MANSECTS") != NULL)
2948 break;
2949 }
2950
2951 /*
2952 * the man.cf file doesn't have a MANSECTS entry
2953 */
2954 if (rc == NULL)
2955 return (-1);
2956
2957 s = strchr(buf, '\n');
2958 *s = '\0'; /* replace '\n' with '\0' */
2959
2960 (void) strcpy(submandir, buf);
2961 return (0);
2962 }
2963
2964 static void
malloc_error(void)2965 malloc_error(void)
2966 {
2967 (void) fprintf(stderr, gettext(
2968 "Memory allocation failed.\n"));
2969 exit(1);
2970 }
2971
2972 static int
sgmlcheck(const char * s1)2973 sgmlcheck(const char *s1)
2974 {
2975 const char *s2 = SGML_SYMBOL;
2976 int len;
2977
2978 while (*s1) {
2979 /*
2980 * Assume the first character of SGML_SYMBOL(*s2) is '<'.
2981 * Therefore, not necessary to do toupper(*s1) here.
2982 */
2983 if (*s1 == *s2) {
2984 /*
2985 * *s1 is '<'. Check the following substring matches
2986 * with "!DOCTYPE".
2987 */
2988 s1++;
2989 if (strncasecmp(s1, s2 + 1, SGML_SYMBOL_LEN - 1)
2990 == 0) {
2991 /*
2992 * SGML_SYMBOL found
2993 */
2994 return (1);
2995 }
2996 continue;
2997 } else if (isascii(*s1)) {
2998 /*
2999 * *s1 is an ASCII char
3000 * Skip one character
3001 */
3002 s1++;
3003 continue;
3004 } else {
3005 /*
3006 * *s1 is a non-ASCII char or
3007 * the first byte of the multibyte char.
3008 * Skip one character
3009 */
3010 len = mblen(s1, MB_CUR_MAX);
3011 if (len == -1)
3012 len = 1;
3013 s1 += len;
3014 continue;
3015 }
3016 }
3017 /*
3018 * SGML_SYMBOL not found
3019 */
3020 return (0);
3021 }
3022
3023 /*
3024 * Initializes the bintoman array with appropriate device and inode info
3025 */
3026
3027 static void
init_bintoman(void)3028 init_bintoman(void)
3029 {
3030 int i;
3031 struct stat sb;
3032
3033 for (i = 0; bintoman[i].bindir != NULL; i++) {
3034 if (stat(bintoman[i].bindir, &sb) == 0) {
3035 bintoman[i].dev = sb.st_dev;
3036 bintoman[i].ino = sb.st_ino;
3037 } else {
3038 bintoman[i].dev = NODEV;
3039 }
3040 }
3041 }
3042
3043 /*
3044 * If a duplicate is found, return 1
3045 * If a duplicate is not found, add it to the dupnode list and return 0
3046 */
3047 static int
dupcheck(struct man_node * mnp,struct dupnode ** dnp)3048 dupcheck(struct man_node *mnp, struct dupnode **dnp)
3049 {
3050 struct dupnode *curdnp;
3051 struct secnode *cursnp;
3052 struct stat sb;
3053 int i;
3054 int rv = 1;
3055 int dupfound;
3056
3057 /*
3058 * If the path doesn't exist, treat it as a duplicate
3059 */
3060 if (stat(mnp->path, &sb) != 0) {
3061 return (1);
3062 }
3063
3064 /*
3065 * If no sections were found in the man dir, treat it as duplicate
3066 */
3067 if (mnp->secv == NULL) {
3068 return (1);
3069 }
3070
3071 /*
3072 * Find the dupnode structure for the previous time this directory
3073 * was looked at. Device and inode numbers are compared so that
3074 * directories that are reached via different paths (e.g. /usr/man vs.
3075 * /usr/share/man) are treated as equivalent.
3076 */
3077 for (curdnp = *dnp; curdnp != NULL; curdnp = curdnp->next) {
3078 if (curdnp->dev == sb.st_dev && curdnp->ino == sb.st_ino) {
3079 break;
3080 }
3081 }
3082
3083 /*
3084 * First time this directory has been seen. Add a new node to the
3085 * head of the list. Since all entries are guaranteed to be unique
3086 * copy all sections to new node.
3087 */
3088 if (curdnp == NULL) {
3089 if ((curdnp = calloc(1, sizeof (struct dupnode))) == NULL) {
3090 malloc_error();
3091 }
3092 for (i = 0; mnp->secv[i] != NULL; i++) {
3093 if ((cursnp = calloc(1, sizeof (struct secnode)))
3094 == NULL) {
3095 malloc_error();
3096 }
3097 cursnp->next = curdnp->secl;
3098 curdnp->secl = cursnp;
3099 if ((cursnp->secp = strdup(mnp->secv[i])) == NULL) {
3100 malloc_error();
3101 }
3102 }
3103 curdnp->dev = sb.st_dev;
3104 curdnp->ino = sb.st_ino;
3105 curdnp->next = *dnp;
3106 *dnp = curdnp;
3107 return (0);
3108 }
3109
3110 /*
3111 * Traverse the section vector in the man_node and the section list
3112 * in dupnode cache to eliminate all duplicates from man_node
3113 */
3114 for (i = 0; mnp->secv[i] != NULL; i++) {
3115 dupfound = 0;
3116 for (cursnp = curdnp->secl; cursnp != NULL;
3117 cursnp = cursnp->next) {
3118 if (strcmp(mnp->secv[i], cursnp->secp) == 0) {
3119 dupfound = 1;
3120 break;
3121 }
3122 }
3123 if (dupfound) {
3124 mnp->secv[i][0] = '\0';
3125 continue;
3126 }
3127
3128
3129 /*
3130 * Update curdnp and set return value to indicate that this
3131 * was not all duplicates.
3132 */
3133 if ((cursnp = calloc(1, sizeof (struct secnode))) == NULL) {
3134 malloc_error();
3135 }
3136 cursnp->next = curdnp->secl;
3137 curdnp->secl = cursnp;
3138 if ((cursnp->secp = strdup(mnp->secv[i])) == NULL) {
3139 malloc_error();
3140 }
3141 rv = 0;
3142 }
3143
3144 return (rv);
3145 }
3146
3147 /*
3148 * Given a bin directory, return the corresponding man directory.
3149 * Return string must be free()d by the caller.
3150 *
3151 * NULL will be returned if no matching man directory can be found.
3152 */
3153
3154 static char *
path_to_manpath(char * bindir)3155 path_to_manpath(char *bindir)
3156 {
3157 char *mand, *p;
3158 int i;
3159 struct stat sb;
3160
3161 /*
3162 * First look for known translations for specific bin paths
3163 */
3164 if (stat(bindir, &sb) != 0) {
3165 return (NULL);
3166 }
3167 for (i = 0; bintoman[i].bindir != NULL; i++) {
3168 if (sb.st_dev == bintoman[i].dev &&
3169 sb.st_ino == bintoman[i].ino) {
3170 if ((mand = strdup(bintoman[i].mandir)) == NULL) {
3171 malloc_error();
3172 }
3173 if ((p = strchr(mand, ',')) != NULL) {
3174 *p = '\0';
3175 }
3176 if (stat(mand, &sb) != 0) {
3177 free(mand);
3178 return (NULL);
3179 }
3180 if (p != NULL) {
3181 *p = ',';
3182 }
3183 return (mand);
3184 }
3185 }
3186
3187 /*
3188 * No specific translation found. Try `dirname $bindir`/man
3189 * and `dirname $bindir`/share/man
3190 */
3191 if ((mand = malloc(PATH_MAX)) == NULL) {
3192 malloc_error();
3193 }
3194
3195 if (strlcpy(mand, bindir, PATH_MAX) >= PATH_MAX) {
3196 free(mand);
3197 return (NULL);
3198 }
3199
3200 /*
3201 * Advance to end of buffer, strip trailing /'s then remove last
3202 * directory component.
3203 */
3204 for (p = mand; *p != '\0'; p++)
3205 ;
3206 for (; p > mand && *p == '/'; p--)
3207 ;
3208 for (; p > mand && *p != '/'; p--)
3209 ;
3210 if (p == mand && *p == '.') {
3211 if (realpath("..", mand) == NULL) {
3212 free(mand);
3213 return (NULL);
3214 }
3215 for (; *p != '\0'; p++)
3216 ;
3217 } else {
3218 *p = '\0';
3219 }
3220
3221 if (strlcat(mand, "/man", PATH_MAX) >= PATH_MAX) {
3222 free(mand);
3223 return (NULL);
3224 }
3225
3226 if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
3227 return (mand);
3228 }
3229
3230 /*
3231 * Strip the /man off and try /share/man
3232 */
3233 *p = '\0';
3234 if (strlcat(mand, "/share/man", PATH_MAX) >= PATH_MAX) {
3235 free(mand);
3236 return (NULL);
3237 }
3238 if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
3239 return (mand);
3240 }
3241
3242 /*
3243 * No man or share/man directory found
3244 */
3245 free(mand);
3246 return (NULL);
3247 }
3248
3249 /*
3250 * Free a linked list of dupnode structs
3251 */
3252 void
free_dupnode(struct dupnode * dnp)3253 free_dupnode(struct dupnode *dnp) {
3254 struct dupnode *dnp2;
3255 struct secnode *snp;
3256
3257 while (dnp != NULL) {
3258 dnp2 = dnp;
3259 dnp = dnp->next;
3260 while (dnp2->secl != NULL) {
3261 snp = dnp2->secl;
3262 dnp2->secl = dnp2->secl->next;
3263 free(snp->secp);
3264 free(snp);
3265 }
3266 free(dnp2);
3267 }
3268 }
3269
3270 /*
3271 * prints manp linked list to stdout.
3272 *
3273 * If namep is NULL, output can be used for setting MANPATH.
3274 *
3275 * If namep is not NULL output is two columns. First column is the string
3276 * pointed to by namep. Second column is a MANPATH-compatible representation
3277 * of manp linked list.
3278 */
3279 void
print_manpath(struct man_node * manp,char * namep)3280 print_manpath(struct man_node *manp, char *namep)
3281 {
3282 char colon[2];
3283 char **secp;
3284
3285 if (namep != NULL) {
3286 (void) printf("%s ", namep);
3287 }
3288
3289 colon[0] = '\0';
3290 colon[1] = '\0';
3291
3292 for (; manp != NULL; manp = manp->next) {
3293 (void) printf("%s%s", colon, manp->path);
3294 colon[0] = ':';
3295
3296 /*
3297 * If man.cf or a directory scan was used to create section
3298 * list, do not print section list again. If the output of
3299 * man -p is used to set MANPATH, subsequent runs of man
3300 * will re-read man.cf and/or scan man directories as
3301 * required.
3302 */
3303 if (manp->defsrch != 0) {
3304 continue;
3305 }
3306
3307 for (secp = manp->secv; *secp != NULL; secp++) {
3308 /*
3309 * Section deduplication may have eliminated some
3310 * sections from the vector. Avoid displaying this
3311 * detail which would appear as ",," in output
3312 */
3313 if ((*secp)[0] != '\0') {
3314 (void) printf(",%s", *secp);
3315 }
3316 }
3317 }
3318 (void) printf("\n");
3319 }
3320