1 /* $NetBSD: man.c,v 1.44 2012/01/03 17:49:57 joerg Exp $ */ 2 3 /* 4 * Copyright (c) 1987, 1993, 1994, 1995 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 34 #ifndef lint 35 __COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994, 1995\ 36 The Regents of the University of California. All rights reserved."); 37 #endif /* not lint */ 38 39 #ifndef lint 40 #if 0 41 static char sccsid[] = "@(#)man.c 8.17 (Berkeley) 1/31/95"; 42 #else 43 __RCSID("$NetBSD: man.c,v 1.44 2012/01/03 17:49:57 joerg Exp $"); 44 #endif 45 #endif /* not lint */ 46 47 #include <sys/param.h> 48 #include <sys/queue.h> 49 #include <sys/stat.h> 50 #include <sys/utsname.h> 51 52 #include <ctype.h> 53 #include <err.h> 54 #include <errno.h> 55 #include <fcntl.h> 56 #include <fnmatch.h> 57 #include <glob.h> 58 #include <signal.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <unistd.h> 63 #include <util.h> 64 #include <locale.h> 65 66 #include "manconf.h" 67 #include "pathnames.h" 68 69 #ifndef MAN_DEBUG 70 #define MAN_DEBUG 0 /* debug path output */ 71 #endif 72 73 /* 74 * manstate: structure collecting the current global state so we can 75 * easily identify it and pass it to helper functions in one arg. 76 */ 77 struct manstate { 78 /* command line flags */ 79 int all; /* -a: show all matches rather than first */ 80 int cat; /* -c: do not use a pager */ 81 char *conffile; /* -C: use alternate config file */ 82 int how; /* -h: show SYNOPSIS only */ 83 char *manpath; /* -M: alternate MANPATH */ 84 char *addpath; /* -m: add these dirs to front of manpath */ 85 char *pathsearch; /* -S: path of man must contain this string */ 86 char *sectionname; /* -s: limit search to a given man section */ 87 int where; /* -w: just show paths of all matching files */ 88 int getpath; /* -p: print the path of directories containing man pages */ 89 90 /* important tags from the config file */ 91 TAG *defaultpath; /* _default: default MANPATH */ 92 TAG *subdirs; /* _subdir: default subdir search list */ 93 TAG *suffixlist; /* _suffix: for files that can be cat()'d */ 94 TAG *buildlist; /* _build: for files that must be built */ 95 96 /* tags for internal use */ 97 TAG *intmp; /* _intmp: tmp files we must cleanup */ 98 TAG *missinglist; /* _missing: pages we couldn't find */ 99 TAG *mymanpath; /* _new_path: final version of MANPATH */ 100 TAG *section; /* <sec>: tag for m.sectionname */ 101 102 /* other misc stuff */ 103 const char *pager; /* pager to use */ 104 const char *machine; /* machine */ 105 const char *machclass; /* machine class */ 106 size_t pagerlen; /* length of the above */ 107 }; 108 109 /* 110 * prototypes 111 */ 112 static void build_page(char *, char **, struct manstate *); 113 static void cat(char *); 114 static const char *check_pager(const char *); 115 static int cleanup(void); 116 static void how(char *); 117 static void jump(char **, char *, char *); 118 static int manual(char *, struct manstate *, glob_t *); 119 static void onsig(int); 120 static void usage(void) __attribute__((__noreturn__)); 121 static void addpath(struct manstate *, const char *, size_t, const char *); 122 static const char *getclass(const char *); 123 static void printmanpath(struct manstate *); 124 125 /* 126 * main function 127 */ 128 int 129 main(int argc, char **argv) 130 { 131 static struct manstate m = { 0 }; /* init to zero */ 132 int ch, abs_section, found; 133 ENTRY *esubd, *epath; 134 char *p, **ap, *cmd; 135 size_t len; 136 glob_t pg; 137 138 setprogname(argv[0]); 139 setlocale(LC_ALL, ""); 140 /* 141 * parse command line... 142 */ 143 while ((ch = getopt(argc, argv, "-aC:cfhkM:m:P:ps:S:w")) != -1) 144 switch (ch) { 145 case 'a': 146 m.all = 1; 147 break; 148 case 'C': 149 m.conffile = optarg; 150 break; 151 case 'c': 152 case '-': /* XXX: '-' is a deprecated version of '-c' */ 153 m.cat = 1; 154 break; 155 case 'h': 156 m.how = 1; 157 break; 158 case 'm': 159 m.addpath = optarg; 160 break; 161 case 'M': 162 case 'P': /* -P for backward compatibility */ 163 m.manpath = strdup(optarg); 164 break; 165 case 'p': 166 m.getpath = 1; 167 break; 168 /* 169 * The -f and -k options are backward compatible, 170 * undocumented ways of calling whatis(1) and apropos(1). 171 */ 172 case 'f': 173 jump(argv, "-f", "whatis"); 174 /* NOTREACHED */ 175 case 'k': 176 jump(argv, "-k", "apropos"); 177 /* NOTREACHED */ 178 case 's': 179 if (m.sectionname != NULL) 180 usage(); 181 m.sectionname = optarg; 182 break; 183 case 'S': 184 m.pathsearch = optarg; 185 break; 186 case 'w': 187 m.all = m.where = 1; 188 break; 189 case '?': 190 default: 191 usage(); 192 } 193 argc -= optind; 194 argv += optind; 195 196 if (!m.getpath && !argc) 197 usage(); 198 199 /* 200 * read the configuration file and collect any other information 201 * we will need (machine type, pager, section [if specified 202 * without '-s'], and MANPATH through the environment). 203 */ 204 config(m.conffile); /* exits on error ... */ 205 206 if ((m.machine = getenv("MACHINE")) == NULL) { 207 struct utsname utsname; 208 209 if (uname(&utsname) == -1) 210 err(EXIT_FAILURE, "uname"); 211 m.machine = utsname.machine; 212 } 213 214 m.machclass = getclass(m.machine); 215 216 if (!m.cat && !m.how && !m.where) { /* if we need a pager ... */ 217 if (!isatty(STDOUT_FILENO)) { 218 m.cat = 1; 219 } else { 220 if ((m.pager = getenv("PAGER")) != NULL && 221 m.pager[0] != '\0') 222 m.pager = check_pager(m.pager); 223 else 224 m.pager = _PATH_PAGER; 225 m.pagerlen = strlen(m.pager); 226 } 227 } 228 229 /* do we need to set m.section to a non-null value? */ 230 if (m.sectionname) { 231 232 m.section = gettag(m.sectionname, 0); /* -s must be a section */ 233 if (m.section == NULL) 234 errx(EXIT_FAILURE, "unknown section: %s", m.sectionname); 235 236 } else if (argc > 1) { 237 238 m.section = gettag(*argv, 0); /* might be a section? */ 239 if (m.section) { 240 argv++; 241 argc--; 242 } 243 244 } 245 246 if (m.manpath == NULL) 247 m.manpath = getenv("MANPATH"); /* note: -M overrides getenv */ 248 249 250 /* 251 * get default values from config file, plus create the tags we 252 * use for keeping internal state. make sure all our mallocs 253 * go through. 254 */ 255 /* from cfg file */ 256 m.defaultpath = gettag("_default", 1); 257 m.subdirs = gettag("_subdir", 1); 258 m.suffixlist = gettag("_suffix", 1); 259 m.buildlist = gettag("_build", 1); 260 /* internal use */ 261 m.mymanpath = gettag("_new_path", 1); 262 m.missinglist = gettag("_missing", 1); 263 m.intmp = gettag("_intmp", 1); 264 if (!m.defaultpath || !m.subdirs || !m.suffixlist || !m.buildlist || 265 !m.mymanpath || !m.missinglist || !m.intmp) 266 errx(EXIT_FAILURE, "malloc failed"); 267 268 /* 269 * are we using a section whose elements are all absolute paths? 270 * (we only need to look at the first entry on the section list, 271 * as config() will ensure that any additional entries will match 272 * the first one.) 273 */ 274 abs_section = (m.section != NULL && 275 !TAILQ_EMPTY(&m.section->entrylist) && 276 *(TAILQ_FIRST(&m.section->entrylist)->s) == '/'); 277 278 /* 279 * now that we have all the data we need, we must determine the 280 * manpath we are going to use to find the requested entries using 281 * the following steps... 282 * 283 * [1] if the user specified a section and that section's elements 284 * from the config file are all absolute paths, then we override 285 * defaultpath and -M/MANPATH with the section's absolute paths. 286 */ 287 if (abs_section) { 288 m.manpath = NULL; /* ignore -M/MANPATH */ 289 m.defaultpath = m.section; /* overwrite _default path */ 290 m.section = NULL; /* promoted to defaultpath */ 291 } 292 293 /* 294 * [2] section can now only be non-null if the user asked for 295 * a section and that section's elements did not have 296 * absolute paths. in this case we use the section's 297 * elements to override _subdir from the config file. 298 * 299 * after this step, we are done processing "m.section"... 300 */ 301 if (m.section) 302 m.subdirs = m.section; 303 304 /* 305 * [3] we need to setup the path we want to use (m.mymanpath). 306 * if the user gave us a path (m.manpath) use it, otherwise 307 * go with the default. in either case we need to append 308 * the subdir and machine spec to each element of the path. 309 * 310 * for absolute section paths that come from the config file, 311 * we only append the subdir spec if the path ends in 312 * a '/' --- elements that do not end in '/' are assumed to 313 * not have subdirectories. this is mainly for backward compat, 314 * but it allows non-subdir configs like: 315 * sect3 /usr/share/man/{old/,}cat3 316 * doc /usr/{pkg,share}/doc/{sendmail/op,sendmail/intro} 317 * 318 * note that we try and be careful to not put double slashes 319 * in the path (e.g. we want /usr/share/man/man1, not 320 * /usr/share/man//man1) because "more" will put the filename 321 * we generate in its prompt and the double slashes look ugly. 322 */ 323 if (m.manpath) { 324 325 /* note: strtok is going to destroy m.manpath */ 326 for (p = strtok(m.manpath, ":") ; p ; p = strtok(NULL, ":")) { 327 len = strlen(p); 328 if (len < 1) 329 continue; 330 TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q) 331 addpath(&m, p, len, esubd->s); 332 } 333 334 } else { 335 336 TAILQ_FOREACH(epath, &m.defaultpath->entrylist, q) { 337 /* handle trailing "/" magic here ... */ 338 if (abs_section && epath->s[epath->len - 1] != '/') { 339 addpath(&m, "", 1, epath->s); 340 continue; 341 } 342 343 TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q) 344 addpath(&m, epath->s, epath->len, esubd->s); 345 } 346 347 } 348 349 /* 350 * [4] finally, prepend the "-m" m.addpath to mymanpath if it 351 * was specified. subdirs and machine are always applied to 352 * m.addpath. 353 */ 354 if (m.addpath) { 355 356 /* note: strtok is going to destroy m.addpath */ 357 for (p = strtok(m.addpath, ":") ; p ; p = strtok(NULL, ":")) { 358 len = strlen(p); 359 if (len < 1) 360 continue; 361 TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q) 362 addpath(&m, p, len, esubd->s); 363 } 364 365 } 366 367 if (m.getpath) 368 printmanpath(&m); 369 370 /* 371 * now m.mymanpath is complete! 372 */ 373 #if MAN_DEBUG 374 printf("mymanpath:\n"); 375 TAILQ_FOREACH(epath, &m.mymanpath->entrylist, q) { 376 printf("\t%s\n", epath->s); 377 } 378 #endif 379 380 /* 381 * start searching for matching files and format them if necessary. 382 * setup an interrupt handler so that we can ensure that temporary 383 * files go away. 384 */ 385 (void)signal(SIGINT, onsig); 386 (void)signal(SIGHUP, onsig); 387 (void)signal(SIGPIPE, onsig); 388 389 memset(&pg, 0, sizeof(pg)); 390 for (found = 0; *argv; ++argv) 391 if (manual(*argv, &m, &pg)) { 392 found = 1; 393 } 394 395 /* if nothing found, we're done. */ 396 if (!found) { 397 (void)cleanup(); 398 exit(EXIT_FAILURE); 399 } 400 401 /* 402 * handle the simple display cases first (m.cat, m.how, m.where) 403 */ 404 if (m.cat) { 405 for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 406 if (**ap == '\0') 407 continue; 408 cat(*ap); 409 } 410 exit(cleanup()); 411 } 412 if (m.how) { 413 for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 414 if (**ap == '\0') 415 continue; 416 how(*ap); 417 } 418 exit(cleanup()); 419 } 420 if (m.where) { 421 for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 422 if (**ap == '\0') 423 continue; 424 (void)printf("%s\n", *ap); 425 } 426 exit(cleanup()); 427 } 428 429 /* 430 * normal case - we display things in a single command, so 431 * build a list of things to display. first compute total 432 * length of buffer we will need so we can malloc it. 433 */ 434 for (ap = pg.gl_pathv, len = m.pagerlen + 1; *ap != NULL; ++ap) { 435 if (**ap == '\0') 436 continue; 437 len += strlen(*ap) + 1; 438 } 439 if ((cmd = malloc(len)) == NULL) { 440 warn("malloc"); 441 (void)cleanup(); 442 exit(EXIT_FAILURE); 443 } 444 445 /* now build the command string... */ 446 p = cmd; 447 len = m.pagerlen; 448 memcpy(p, m.pager, len); 449 p += len; 450 *p++ = ' '; 451 for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 452 if (**ap == '\0') 453 continue; 454 len = strlen(*ap); 455 memcpy(p, *ap, len); 456 p += len; 457 *p++ = ' '; 458 } 459 *--p = '\0'; 460 461 /* Use system(3) in case someone's pager is "pager arg1 arg2". */ 462 (void)system(cmd); 463 464 exit(cleanup()); 465 } 466 467 static int 468 manual_find_buildkeyword(char *escpage, const char *fmt, 469 struct manstate *mp, glob_t *pg, size_t cnt) 470 { 471 ENTRY *suffix; 472 int found; 473 char *p, buf[MAXPATHLEN]; 474 475 found = 0; 476 /* Try the _build key words next. */ 477 TAILQ_FOREACH(suffix, &mp->buildlist->entrylist, q) { 478 for (p = suffix->s; 479 *p != '\0' && !isspace((unsigned char)*p); 480 ++p) 481 continue; 482 if (*p == '\0') 483 continue; 484 485 *p = '\0'; 486 (void)snprintf(buf, sizeof(buf), fmt, escpage, suffix->s); 487 if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { 488 if (!mp->where) 489 build_page(p + 1, &pg->gl_pathv[cnt], mp); 490 *p = ' '; 491 found = 1; 492 break; 493 } 494 *p = ' '; 495 } 496 497 return found; 498 } 499 500 /* 501 * manual -- 502 * Search the manuals for the pages. 503 */ 504 static int 505 manual(char *page, struct manstate *mp, glob_t *pg) 506 { 507 ENTRY *suffix, *mdir; 508 int anyfound, error, found; 509 size_t cnt; 510 char *p, buf[MAXPATHLEN], *escpage, *eptr; 511 static const char escglob[] = "\\~?*{}[]"; 512 513 anyfound = 0; 514 515 /* 516 * Fixup page which may contain glob(3) special characters, e.g. 517 * the famous "No man page for [" FAQ. 518 */ 519 if ((escpage = malloc((2 * strlen(page)) + 1)) == NULL) { 520 warn("malloc"); 521 (void)cleanup(); 522 exit(EXIT_FAILURE); 523 } 524 525 p = page; 526 eptr = escpage; 527 528 while (*p) { 529 if (strchr(escglob, *p) != NULL) { 530 *eptr++ = '\\'; 531 *eptr++ = *p++; 532 } else 533 *eptr++ = *p++; 534 } 535 536 *eptr = '\0'; 537 538 /* 539 * If 'page' is given with a full or relative path 540 * then interpret it as a file specification. 541 */ 542 if ((page[0] == '/') || (page[0] == '.')) { 543 /* check if file actually exists */ 544 (void)strlcpy(buf, escpage, sizeof(buf)); 545 error = glob(buf, GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT, NULL, pg); 546 if (error != 0) { 547 if (error == GLOB_NOMATCH) { 548 goto notfound; 549 } else { 550 errx(EXIT_FAILURE, "glob failed"); 551 } 552 } 553 554 if (pg->gl_matchc == 0) 555 goto notfound; 556 557 /* clip suffix for the suffix check below */ 558 p = strrchr(escpage, '.'); 559 if (p && p[0] == '.' && isdigit((unsigned char)p[1])) 560 p[0] = '\0'; 561 562 found = 0; 563 for (cnt = pg->gl_pathc - pg->gl_matchc; 564 cnt < pg->gl_pathc; ++cnt) 565 { 566 found = manual_find_buildkeyword(escpage, "%s%s", 567 mp, pg, cnt); 568 if (found) { 569 anyfound = 1; 570 if (!mp->all) { 571 /* Delete any other matches. */ 572 while (++cnt< pg->gl_pathc) 573 pg->gl_pathv[cnt] = ""; 574 break; 575 } 576 continue; 577 } 578 579 /* It's not a man page, forget about it. */ 580 pg->gl_pathv[cnt] = ""; 581 } 582 583 notfound: 584 if (!anyfound) { 585 if (addentry(mp->missinglist, page, 0) < 0) { 586 warn("malloc"); 587 (void)cleanup(); 588 exit(EXIT_FAILURE); 589 } 590 } 591 free(escpage); 592 return anyfound; 593 } 594 595 /* For each man directory in mymanpath ... */ 596 TAILQ_FOREACH(mdir, &mp->mymanpath->entrylist, q) { 597 598 /* 599 * use glob(3) to look in the filesystem for matching files. 600 * match any suffix here, as we will check that later. 601 */ 602 (void)snprintf(buf, sizeof(buf), "%s/%s.*", mdir->s, escpage); 603 if ((error = glob(buf, 604 GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT, NULL, pg)) != 0) { 605 if (error == GLOB_NOMATCH) 606 continue; 607 else { 608 warn("globbing"); 609 (void)cleanup(); 610 exit(EXIT_FAILURE); 611 } 612 } 613 if (pg->gl_matchc == 0) 614 continue; 615 616 /* 617 * start going through the matches glob(3) just found and 618 * use m.pathsearch (if present) to filter out pages we 619 * don't want. then verify the suffix is valid, and build 620 * the page if we have a _build suffix. 621 */ 622 for (cnt = pg->gl_pathc - pg->gl_matchc; 623 cnt < pg->gl_pathc; ++cnt) { 624 625 /* filter on directory path name */ 626 if (mp->pathsearch) { 627 p = strstr(pg->gl_pathv[cnt], mp->pathsearch); 628 if (!p || strchr(p, '/') == NULL) { 629 pg->gl_pathv[cnt] = ""; /* zap! */ 630 continue; 631 } 632 } 633 634 /* 635 * Try the _suffix key words first. 636 * 637 * XXX 638 * Older versions of man.conf didn't have the suffix 639 * key words, it was assumed that everything was a .0. 640 * We just test for .0 first, it's fast and probably 641 * going to hit. 642 */ 643 (void)snprintf(buf, sizeof(buf), "*/%s.0", escpage); 644 if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) 645 goto next; 646 647 found = 0; 648 TAILQ_FOREACH(suffix, &mp->suffixlist->entrylist, q) { 649 (void)snprintf(buf, 650 sizeof(buf), "*/%s%s", escpage, 651 suffix->s); 652 if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { 653 found = 1; 654 break; 655 } 656 } 657 if (found) 658 goto next; 659 660 /* Try the _build key words next. */ 661 found = manual_find_buildkeyword(escpage, "*/%s%s", 662 mp, pg, cnt); 663 if (found) { 664 next: anyfound = 1; 665 if (!mp->all) { 666 /* Delete any other matches. */ 667 while (++cnt< pg->gl_pathc) 668 pg->gl_pathv[cnt] = ""; 669 break; 670 } 671 continue; 672 } 673 674 /* It's not a man page, forget about it. */ 675 pg->gl_pathv[cnt] = ""; 676 } 677 678 if (anyfound && !mp->all) 679 break; 680 } 681 682 /* If not found, enter onto the missing list. */ 683 if (!anyfound) { 684 if (addentry(mp->missinglist, page, 0) < 0) { 685 warn("malloc"); 686 (void)cleanup(); 687 exit(EXIT_FAILURE); 688 } 689 } 690 691 free(escpage); 692 return anyfound; 693 } 694 695 /* 696 * build_page -- 697 * Build a man page for display. 698 */ 699 static void 700 build_page(char *fmt, char **pathp, struct manstate *mp) 701 { 702 static int warned; 703 int olddir, fd, n, tmpdirlen; 704 char *p, *b; 705 char buf[MAXPATHLEN], cmd[MAXPATHLEN], tpath[MAXPATHLEN]; 706 const char *tmpdir; 707 708 /* Let the user know this may take awhile. */ 709 if (!warned) { 710 warned = 1; 711 warnx("Formatting manual page..."); 712 } 713 714 /* 715 * Historically man chdir'd to the root of the man tree. 716 * This was used in man pages that contained relative ".so" 717 * directives (including other man pages for command aliases etc.) 718 * It even went one step farther, by examining the first line 719 * of the man page and parsing the .so filename so it would 720 * make hard(?) links to the cat'ted man pages for space savings. 721 * (We don't do that here, but we could). 722 */ 723 724 /* copy and find the end */ 725 for (b = buf, p = *pathp; (*b++ = *p++) != '\0';) 726 continue; 727 728 /* 729 * skip the last two path components, page name and man[n] ... 730 * (e.g. buf will be "/usr/share/man" and p will be "man1/man.1") 731 * we also save a pointer to our current directory so that we 732 * can fchdir() back to it. this allows relative MANDIR paths 733 * to work with multiple man pages... e.g. consider: 734 * cd /usr/share && man -M ./man cat ls 735 * when no "cat1" subdir files are present. 736 */ 737 olddir = -1; 738 for (--b, --p, n = 2; b != buf; b--, p--) 739 if (*b == '/') 740 if (--n == 0) { 741 *b = '\0'; 742 olddir = open(".", O_RDONLY); 743 (void) chdir(buf); 744 p++; 745 break; 746 } 747 748 749 /* advance fmt pass the suffix spec to the printf format string */ 750 for (; *fmt && isspace((unsigned char)*fmt); ++fmt) 751 continue; 752 753 /* 754 * Get a temporary file and build a version of the file 755 * to display. Replace the old file name with the new one. 756 */ 757 if ((tmpdir = getenv("TMPDIR")) == NULL) 758 tmpdir = _PATH_TMP; 759 tmpdirlen = strlen(tmpdir); 760 (void)snprintf(tpath, sizeof (tpath), "%s%s%s", tmpdir, 761 (tmpdirlen && tmpdir[tmpdirlen-1] == '/') ? "" : "/", TMPFILE); 762 if ((fd = mkstemp(tpath)) == -1) { 763 warn("%s", tpath); 764 (void)cleanup(); 765 exit(EXIT_FAILURE); 766 } 767 (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath); 768 (void)snprintf(cmd, sizeof(cmd), buf, p); 769 (void)system(cmd); 770 (void)close(fd); 771 if ((*pathp = strdup(tpath)) == NULL) { 772 warn("malloc"); 773 (void)cleanup(); 774 exit(EXIT_FAILURE); 775 } 776 777 /* Link the built file into the remove-when-done list. */ 778 if (addentry(mp->intmp, *pathp, 0) < 0) { 779 warn("malloc"); 780 (void)cleanup(); 781 exit(EXIT_FAILURE); 782 } 783 784 /* restore old directory so relative manpaths still work */ 785 if (olddir != -1) { 786 fchdir(olddir); 787 close(olddir); 788 } 789 } 790 791 /* 792 * how -- 793 * display how information 794 */ 795 static void 796 how(char *fname) 797 { 798 FILE *fp; 799 800 int lcnt, print; 801 char *p, buf[256]; 802 803 if (!(fp = fopen(fname, "r"))) { 804 warn("%s", fname); 805 (void)cleanup(); 806 exit(EXIT_FAILURE); 807 } 808 #define S1 "SYNOPSIS" 809 #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS" 810 #define D1 "DESCRIPTION" 811 #define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN" 812 for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) { 813 if (!strncmp(buf, S1, sizeof(S1) - 1) || 814 !strncmp(buf, S2, sizeof(S2) - 1)) { 815 print = 1; 816 continue; 817 } else if (!strncmp(buf, D1, sizeof(D1) - 1) || 818 !strncmp(buf, D2, sizeof(D2) - 1)) { 819 if (fp) 820 (void)fclose(fp); 821 return; 822 } 823 if (!print) 824 continue; 825 if (*buf == '\n') 826 ++lcnt; 827 else { 828 for(; lcnt; --lcnt) 829 (void)putchar('\n'); 830 for (p = buf; isspace((unsigned char)*p); ++p) 831 continue; 832 (void)fputs(p, stdout); 833 } 834 } 835 (void)fclose(fp); 836 } 837 838 /* 839 * cat -- 840 * cat out the file 841 */ 842 static void 843 cat(char *fname) 844 { 845 int fd, n; 846 char buf[2048]; 847 848 if ((fd = open(fname, O_RDONLY, 0)) < 0) { 849 warn("%s", fname); 850 (void)cleanup(); 851 exit(EXIT_FAILURE); 852 } 853 while ((n = read(fd, buf, sizeof(buf))) > 0) 854 if (write(STDOUT_FILENO, buf, n) != n) { 855 warn("write"); 856 (void)cleanup(); 857 exit(EXIT_FAILURE); 858 } 859 if (n == -1) { 860 warn("read"); 861 (void)cleanup(); 862 exit(EXIT_FAILURE); 863 } 864 (void)close(fd); 865 } 866 867 /* 868 * check_pager -- 869 * check the user supplied page information 870 */ 871 static const char * 872 check_pager(const char *name) 873 { 874 const char *p; 875 876 /* 877 * if the user uses "more", we make it "more -s"; watch out for 878 * PAGER = "mypager /usr/ucb/more" 879 */ 880 for (p = name; *p && !isspace((unsigned char)*p); ++p) 881 continue; 882 for (; p > name && *p != '/'; --p); 883 if (p != name) 884 ++p; 885 886 /* make sure it's "more", not "morex" */ 887 if (!strncmp(p, "more", 4) && (!p[4] || isspace((unsigned char)p[4]))){ 888 char *newname; 889 (void)asprintf(&newname, "%s %s", p, "-s"); 890 name = newname; 891 } 892 893 return name; 894 } 895 896 /* 897 * jump -- 898 * strip out flag argument and jump 899 */ 900 static void 901 jump(char **argv, char *flag, char *name) 902 { 903 char **arg; 904 905 argv[0] = name; 906 for (arg = argv + 1; *arg; ++arg) 907 if (!strcmp(*arg, flag)) 908 break; 909 for (; *arg; ++arg) 910 arg[0] = arg[1]; 911 execvp(name, argv); 912 err(EXIT_FAILURE, "Cannot execute `%s'", name); 913 } 914 915 /* 916 * onsig -- 917 * If signaled, delete the temporary files. 918 */ 919 static void 920 onsig(int signo) 921 { 922 923 (void)cleanup(); 924 925 (void)raise_default_signal(signo); 926 927 /* NOTREACHED */ 928 exit(EXIT_FAILURE); 929 } 930 931 /* 932 * cleanup -- 933 * Clean up temporary files, show any error messages. 934 */ 935 static int 936 cleanup(void) 937 { 938 TAG *intmpp, *missp; 939 ENTRY *ep; 940 int rval; 941 942 rval = EXIT_SUCCESS; 943 /* 944 * note that _missing and _intmp were created by main(), so 945 * gettag() cannot return NULL here. 946 */ 947 missp = gettag("_missing", 0); /* missing man pages */ 948 intmpp = gettag("_intmp", 0); /* tmp files we need to unlink */ 949 950 TAILQ_FOREACH(ep, &missp->entrylist, q) { 951 warnx("no entry for %s in the manual.", ep->s); 952 rval = EXIT_FAILURE; 953 } 954 955 TAILQ_FOREACH(ep, &intmpp->entrylist, q) 956 (void)unlink(ep->s); 957 958 return rval; 959 } 960 961 static const char * 962 getclass(const char *machine) 963 { 964 char buf[BUFSIZ]; 965 TAG *t; 966 snprintf(buf, sizeof(buf), "_%s", machine); 967 t = gettag(buf, 0); 968 return t != NULL && !TAILQ_EMPTY(&t->entrylist) ? 969 TAILQ_FIRST(&t->entrylist)->s : NULL; 970 } 971 972 static void 973 addpath(struct manstate *m, const char *dir, size_t len, const char *sub) 974 { 975 char buf[2 * MAXPATHLEN + 1]; 976 (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,%s%s%s}", 977 dir, (dir[len - 1] == '/') ? "" : "/", sub, m->machine, 978 m->machclass ? "/" : "", m->machclass ? m->machclass : "", 979 m->machclass ? "," : ""); 980 if (addentry(m->mymanpath, buf, 0) < 0) 981 errx(EXIT_FAILURE, "malloc failed"); 982 } 983 984 /* 985 * usage -- 986 * print usage message and die 987 */ 988 static void 989 usage(void) 990 { 991 (void)fprintf(stderr, "Usage: %s [-acw|-h] [-C cfg] [-M path] " 992 "[-m path] [-S srch] [[-s] sect] name ...\n", getprogname()); 993 (void)fprintf(stderr, 994 "Usage: %s -k [-C cfg] [-M path] [-m path] keyword ...\n", 995 getprogname()); 996 (void)fprintf(stderr, "Usage: %s -p\n", getprogname()); 997 exit(EXIT_FAILURE); 998 } 999 1000 /* 1001 * printmanpath -- 1002 * Prints a list of directories containing man pages. 1003 */ 1004 static void 1005 printmanpath(struct manstate *m) 1006 { 1007 ENTRY *esubd; 1008 char *defaultpath = NULL; /* _default tag value from man.conf. */ 1009 char *buf; /* for storing temporary values */ 1010 char **ap; 1011 glob_t pg; 1012 struct stat sb; 1013 TAG *path = m->defaultpath; 1014 TAG *subdirs = m->subdirs; 1015 1016 /* the tail queue is empty if no _default tag is defined in * man.conf */ 1017 if (TAILQ_EMPTY(&path->entrylist)) 1018 errx(EXIT_FAILURE, "Empty manpath"); 1019 1020 defaultpath = TAILQ_LAST(&path->entrylist, tqh)->s; 1021 1022 if (glob(defaultpath, GLOB_BRACE | GLOB_NOSORT, NULL, &pg) != 0) 1023 err(EXIT_FAILURE, "glob failed"); 1024 1025 if (pg.gl_matchc == 0) { 1026 warnx("Default path in %s doesn't exist", _PATH_MANCONF); 1027 globfree(&pg); 1028 return; 1029 } 1030 1031 TAILQ_FOREACH(esubd, &subdirs->entrylist, q) { 1032 /* Drop cat page directory, only sources are relevant. */ 1033 if (strncmp(esubd->s, "man", 3)) 1034 continue; 1035 1036 for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 1037 if (asprintf(&buf, "%s%s", *ap, esubd->s) == -1) 1038 err(EXIT_FAILURE, "memory allocation error"); 1039 /* Skip non-directories. */ 1040 if (stat(buf, &sb) == 0 && S_ISDIR(sb.st_mode)) 1041 printf("%s\n", buf); 1042 1043 free(buf); 1044 } 1045 } 1046 globfree(&pg); 1047 } 1048