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