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