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