man.c revision 1.52
1/* $NetBSD: man.c,v 1.52 2013/07/19 04:18:10 uwe 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 41static char sccsid[] = "@(#)man.c 8.17 (Berkeley) 1/31/95"; 42#else 43__RCSID("$NetBSD: man.c,v 1.52 2013/07/19 04:18:10 uwe 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 */ 77struct 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 size_t pagerlen; /* length of the above */ 105 const char *machine; /* machine */ 106 const char *machclass; /* machine class */ 107}; 108 109/* 110 * prototypes 111 */ 112static void build_page(char *, char **, struct manstate *); 113static void cat(char *); 114static const char *check_pager(const char *); 115static int cleanup(void); 116static void how(char *); 117static void jump(char **, const char *, const char *); 118static int manual(char *, struct manstate *, glob_t *); 119static void onsig(int); 120static void usage(void) __attribute__((__noreturn__)); 121static void addpath(struct manstate *, const char *, size_t, const char *); 122static const char *getclass(const char *); 123static void printmanpath(struct manstate *); 124 125/* 126 * main function 127 */ 128int 129main(int argc, char **argv) 130{ 131 static struct manstate m; 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 467static int 468manual_find_buildkeyword(const char *prefix, const char *escpage, 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), "%s%s%s", 487 prefix, escpage, suffix->s); 488 if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { 489 if (!mp->where) 490 build_page(p + 1, &pg->gl_pathv[cnt], mp); 491 *p = ' '; 492 found = 1; 493 break; 494 } 495 *p = ' '; 496 } 497 498 return found; 499} 500 501/* 502 * manual -- 503 * Search the manuals for the pages. 504 */ 505static int 506manual(char *page, struct manstate *mp, glob_t *pg) 507{ 508 ENTRY *suffix, *mdir; 509 int anyfound, error, found; 510 size_t cnt; 511 char *p, buf[MAXPATHLEN], *escpage, *eptr; 512 static const char escglob[] = "\\~?*{}[]"; 513 514 anyfound = 0; 515 516 /* 517 * Fixup page which may contain glob(3) special characters, e.g. 518 * the famous "No man page for [" FAQ. 519 */ 520 if ((escpage = malloc((2 * strlen(page)) + 1)) == NULL) { 521 warn("malloc"); 522 (void)cleanup(); 523 exit(EXIT_FAILURE); 524 } 525 526 p = page; 527 eptr = escpage; 528 529 while (*p) { 530 if (strchr(escglob, *p) != NULL) { 531 *eptr++ = '\\'; 532 *eptr++ = *p++; 533 } else 534 *eptr++ = *p++; 535 } 536 537 *eptr = '\0'; 538 539 /* 540 * If 'page' is given with a full or relative path 541 * then interpret it as a file specification. 542 */ 543 if ((page[0] == '/') || (page[0] == '.')) { 544 /* check if file actually exists */ 545 (void)strlcpy(buf, escpage, sizeof(buf)); 546 error = glob(buf, GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT, NULL, pg); 547 if (error != 0) { 548 if (error == GLOB_NOMATCH) { 549 goto notfound; 550 } else { 551 errx(EXIT_FAILURE, "glob failed"); 552 } 553 } 554 555 if (pg->gl_matchc == 0) 556 goto notfound; 557 558 /* clip suffix for the suffix check below */ 559 p = strrchr(escpage, '.'); 560 if (p && p[0] == '.' && isdigit((unsigned char)p[1])) 561 p[0] = '\0'; 562 563 found = 0; 564 for (cnt = pg->gl_pathc - pg->gl_matchc; 565 cnt < pg->gl_pathc; ++cnt) 566 { 567 found = manual_find_buildkeyword("", escpage, 568 mp, pg, cnt); 569 if (found) { 570 anyfound = 1; 571 if (!mp->all) { 572 /* Delete any other matches. */ 573 while (++cnt< pg->gl_pathc) 574 *pg->gl_pathv[cnt] = '\0'; 575 break; 576 } 577 continue; 578 } 579 580 /* It's not a man page, forget about it. */ 581 *pg->gl_pathv[cnt] = '\0'; 582 } 583 584 notfound: 585 if (!anyfound) { 586 if (addentry(mp->missinglist, page, 0) < 0) { 587 warn("malloc"); 588 (void)cleanup(); 589 exit(EXIT_FAILURE); 590 } 591 } 592 free(escpage); 593 return anyfound; 594 } 595 596 /* For each man directory in mymanpath ... */ 597 TAILQ_FOREACH(mdir, &mp->mymanpath->entrylist, q) { 598 599 /* 600 * use glob(3) to look in the filesystem for matching files. 601 * match any suffix here, as we will check that later. 602 */ 603 (void)snprintf(buf, sizeof(buf), "%s/%s.*", mdir->s, escpage); 604 if ((error = glob(buf, 605 GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT, NULL, pg)) != 0) { 606 if (error == GLOB_NOMATCH) 607 continue; 608 else { 609 warn("globbing"); 610 (void)cleanup(); 611 exit(EXIT_FAILURE); 612 } 613 } 614 if (pg->gl_matchc == 0) 615 continue; 616 617 /* 618 * start going through the matches glob(3) just found and 619 * use m.pathsearch (if present) to filter out pages we 620 * don't want. then verify the suffix is valid, and build 621 * the page if we have a _build suffix. 622 */ 623 for (cnt = pg->gl_pathc - pg->gl_matchc; 624 cnt < pg->gl_pathc; ++cnt) { 625 626 /* filter on directory path name */ 627 if (mp->pathsearch) { 628 p = strstr(pg->gl_pathv[cnt], mp->pathsearch); 629 if (!p || strchr(p, '/') == NULL) { 630 *pg->gl_pathv[cnt] = '\0'; /* zap! */ 631 continue; 632 } 633 } 634 635 /* 636 * Try the _suffix key words first. 637 * 638 * XXX 639 * Older versions of man.conf didn't have the suffix 640 * key words, it was assumed that everything was a .0. 641 * We just test for .0 first, it's fast and probably 642 * going to hit. 643 */ 644 (void)snprintf(buf, sizeof(buf), "*/%s.0", escpage); 645 if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) 646 goto next; 647 648 found = 0; 649 TAILQ_FOREACH(suffix, &mp->suffixlist->entrylist, q) { 650 (void)snprintf(buf, 651 sizeof(buf), "*/%s%s", escpage, 652 suffix->s); 653 if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { 654 found = 1; 655 break; 656 } 657 } 658 if (found) 659 goto next; 660 661 /* Try the _build key words next. */ 662 found = manual_find_buildkeyword("*/", escpage, 663 mp, pg, cnt); 664 if (found) { 665next: anyfound = 1; 666 if (!mp->all) { 667 /* Delete any other matches. */ 668 while (++cnt< pg->gl_pathc) 669 *pg->gl_pathv[cnt] = '\0'; 670 break; 671 } 672 continue; 673 } 674 675 /* It's not a man page, forget about it. */ 676 *pg->gl_pathv[cnt] = '\0'; 677 } 678 679 if (anyfound && !mp->all) 680 break; 681 } 682 683 /* If not found, enter onto the missing list. */ 684 if (!anyfound) { 685 if (addentry(mp->missinglist, page, 0) < 0) { 686 warn("malloc"); 687 (void)cleanup(); 688 exit(EXIT_FAILURE); 689 } 690 } 691 692 free(escpage); 693 return anyfound; 694} 695 696/* 697 * build_page -- 698 * Build a man page for display. 699 */ 700static void 701build_page(char *fmt, char **pathp, struct manstate *mp) 702{ 703 static int warned; 704 int olddir, fd, n; 705 size_t tmpdirlen; 706 char *p, *b; 707 char buf[MAXPATHLEN], cmd[MAXPATHLEN], tpath[MAXPATHLEN]; 708 const char *tmpdir; 709 710 /* Let the user know this may take awhile. */ 711 if (!warned) { 712 warned = 1; 713 warnx("Formatting manual page..."); 714 } 715 716 /* 717 * Historically man chdir'd to the root of the man tree. 718 * This was used in man pages that contained relative ".so" 719 * directives (including other man pages for command aliases etc.) 720 * It even went one step farther, by examining the first line 721 * of the man page and parsing the .so filename so it would 722 * make hard(?) links to the cat'ted man pages for space savings. 723 * (We don't do that here, but we could). 724 */ 725 726 /* copy and find the end */ 727 for (b = buf, p = *pathp; (*b++ = *p++) != '\0';) 728 continue; 729 730 /* 731 * skip the last two path components, page name and man[n] ... 732 * (e.g. buf will be "/usr/share/man" and p will be "man1/man.1") 733 * we also save a pointer to our current directory so that we 734 * can fchdir() back to it. this allows relative MANDIR paths 735 * to work with multiple man pages... e.g. consider: 736 * cd /usr/share && man -M ./man cat ls 737 * when no "cat1" subdir files are present. 738 */ 739 olddir = -1; 740 for (--b, --p, n = 2; b != buf; b--, p--) 741 if (*b == '/') 742 if (--n == 0) { 743 *b = '\0'; 744 olddir = open(".", O_RDONLY); 745 (void) chdir(buf); 746 p++; 747 break; 748 } 749 750 751 /* advance fmt pass the suffix spec to the printf format string */ 752 for (; *fmt && isspace((unsigned char)*fmt); ++fmt) 753 continue; 754 755 /* 756 * Get a temporary file and build a version of the file 757 * to display. Replace the old file name with the new one. 758 */ 759 if ((tmpdir = getenv("TMPDIR")) == NULL) 760 tmpdir = _PATH_TMP; 761 tmpdirlen = strlen(tmpdir); 762 (void)snprintf(tpath, sizeof (tpath), "%s%s%s", tmpdir, 763 (tmpdirlen > 0 && tmpdir[tmpdirlen-1] == '/') ? "" : "/", TMPFILE); 764 if ((fd = mkstemp(tpath)) == -1) { 765 warn("%s", tpath); 766 (void)cleanup(); 767 exit(EXIT_FAILURE); 768 } 769 (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath); 770 (void)snprintf(cmd, sizeof(cmd), buf, p); 771 (void)system(cmd); 772 (void)close(fd); 773 if ((*pathp = strdup(tpath)) == NULL) { 774 warn("malloc"); 775 (void)cleanup(); 776 exit(EXIT_FAILURE); 777 } 778 779 /* Link the built file into the remove-when-done list. */ 780 if (addentry(mp->intmp, *pathp, 0) < 0) { 781 warn("malloc"); 782 (void)cleanup(); 783 exit(EXIT_FAILURE); 784 } 785 786 /* restore old directory so relative manpaths still work */ 787 if (olddir != -1) { 788 fchdir(olddir); 789 close(olddir); 790 } 791} 792 793/* 794 * how -- 795 * display how information 796 */ 797static void 798how(char *fname) 799{ 800 FILE *fp; 801 802 int lcnt, print; 803 char *p, buf[256]; 804 805 if (!(fp = fopen(fname, "r"))) { 806 warn("%s", fname); 807 (void)cleanup(); 808 exit(EXIT_FAILURE); 809 } 810#define S1 "SYNOPSIS" 811#define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS" 812#define D1 "DESCRIPTION" 813#define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN" 814 for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) { 815 if (!strncmp(buf, S1, sizeof(S1) - 1) || 816 !strncmp(buf, S2, sizeof(S2) - 1)) { 817 print = 1; 818 continue; 819 } else if (!strncmp(buf, D1, sizeof(D1) - 1) || 820 !strncmp(buf, D2, sizeof(D2) - 1)) { 821 if (fp) 822 (void)fclose(fp); 823 return; 824 } 825 if (!print) 826 continue; 827 if (*buf == '\n') 828 ++lcnt; 829 else { 830 for(; lcnt; --lcnt) 831 (void)putchar('\n'); 832 for (p = buf; isspace((unsigned char)*p); ++p) 833 continue; 834 (void)fputs(p, stdout); 835 } 836 } 837 (void)fclose(fp); 838} 839 840/* 841 * cat -- 842 * cat out the file 843 */ 844static void 845cat(char *fname) 846{ 847 int fd; 848 ssize_t n; 849 char buf[2048]; 850 851 if ((fd = open(fname, O_RDONLY, 0)) < 0) { 852 warn("%s", fname); 853 (void)cleanup(); 854 exit(EXIT_FAILURE); 855 } 856 while ((n = read(fd, buf, sizeof(buf))) > 0) 857 if (write(STDOUT_FILENO, buf, (size_t)n) != n) { 858 warn("write"); 859 (void)cleanup(); 860 exit(EXIT_FAILURE); 861 } 862 if (n == -1) { 863 warn("read"); 864 (void)cleanup(); 865 exit(EXIT_FAILURE); 866 } 867 (void)close(fd); 868} 869 870/* 871 * check_pager -- 872 * check the user supplied page information 873 */ 874static const char * 875check_pager(const char *name) 876{ 877 const char *p; 878 879 /* 880 * if the user uses "more", we make it "more -s"; watch out for 881 * PAGER = "mypager /usr/ucb/more" 882 */ 883 for (p = name; *p && !isspace((unsigned char)*p); ++p) 884 continue; 885 for (; p > name && *p != '/'; --p); 886 if (p != name) 887 ++p; 888 889 /* make sure it's "more", not "morex" */ 890 if (!strncmp(p, "more", 4) && (!p[4] || isspace((unsigned char)p[4]))){ 891 char *newname; 892 (void)asprintf(&newname, "%s %s", p, "-s"); 893 name = newname; 894 } 895 896 return name; 897} 898 899/* 900 * jump -- 901 * strip out flag argument and jump 902 */ 903static void 904jump(char **argv, const char *flag, const char *name) 905{ 906 char **arg; 907 908 argv[0] = __UNCONST(name); 909 for (arg = argv + 1; *arg; ++arg) 910 if (!strcmp(*arg, flag)) 911 break; 912 for (; *arg; ++arg) 913 arg[0] = arg[1]; 914 execvp(name, argv); 915 err(EXIT_FAILURE, "Cannot execute `%s'", name); 916} 917 918/* 919 * onsig -- 920 * If signaled, delete the temporary files. 921 */ 922static void 923onsig(int signo) 924{ 925 926 (void)cleanup(); 927 928 (void)raise_default_signal(signo); 929 930 /* NOTREACHED */ 931 exit(EXIT_FAILURE); 932} 933 934/* 935 * cleanup -- 936 * Clean up temporary files, show any error messages. 937 */ 938static int 939cleanup(void) 940{ 941 TAG *intmpp, *missp; 942 ENTRY *ep; 943 int rval; 944 945 rval = EXIT_SUCCESS; 946 /* 947 * note that _missing and _intmp were created by main(), so 948 * gettag() cannot return NULL here. 949 */ 950 missp = gettag("_missing", 0); /* missing man pages */ 951 intmpp = gettag("_intmp", 0); /* tmp files we need to unlink */ 952 953 TAILQ_FOREACH(ep, &missp->entrylist, q) { 954 warnx("no entry for %s in the manual.", ep->s); 955 rval = EXIT_FAILURE; 956 } 957 958 TAILQ_FOREACH(ep, &intmpp->entrylist, q) 959 (void)unlink(ep->s); 960 961 return rval; 962} 963 964static const char * 965getclass(const char *machine) 966{ 967 char buf[BUFSIZ]; 968 TAG *t; 969 snprintf(buf, sizeof(buf), "_%s", machine); 970 t = gettag(buf, 0); 971 return t != NULL && !TAILQ_EMPTY(&t->entrylist) ? 972 TAILQ_FIRST(&t->entrylist)->s : NULL; 973} 974 975static void 976addpath(struct manstate *m, const char *dir, size_t len, const char *sub) 977{ 978 char buf[2 * MAXPATHLEN + 1]; 979 (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,%s%s%s}", 980 dir, (dir[len - 1] == '/') ? "" : "/", sub, m->machine, 981 m->machclass ? "/" : "", m->machclass ? m->machclass : "", 982 m->machclass ? "," : ""); 983 if (addentry(m->mymanpath, buf, 0) < 0) 984 errx(EXIT_FAILURE, "malloc failed"); 985} 986 987/* 988 * usage -- 989 * print usage message and die 990 */ 991static void 992usage(void) 993{ 994 (void)fprintf(stderr, "Usage: %s [-acw|-h] [-C cfg] [-M path] " 995 "[-m path] [-S srch] [[-s] sect] name ...\n", getprogname()); 996 (void)fprintf(stderr, 997 "Usage: %s -k [-C cfg] [-M path] [-m path] keyword ...\n", 998 getprogname()); 999 (void)fprintf(stderr, "Usage: %s -p\n", getprogname()); 1000 exit(EXIT_FAILURE); 1001} 1002 1003/* 1004 * printmanpath -- 1005 * Prints a list of directories containing man pages. 1006 */ 1007static void 1008printmanpath(struct manstate *m) 1009{ 1010 ENTRY *esubd; 1011 char *defaultpath = NULL; /* _default tag value from man.conf. */ 1012 char *buf; /* for storing temporary values */ 1013 char **ap; 1014 glob_t pg; 1015 struct stat sb; 1016 TAG *path = m->defaultpath; 1017 TAG *subdirs = m->subdirs; 1018 1019 /* the tail queue is empty if no _default tag is defined in * man.conf */ 1020 if (TAILQ_EMPTY(&path->entrylist)) 1021 errx(EXIT_FAILURE, "Empty manpath"); 1022 1023 defaultpath = TAILQ_LAST(&path->entrylist, tqh)->s; 1024 1025 if (glob(defaultpath, GLOB_BRACE | GLOB_NOSORT, NULL, &pg) != 0) 1026 err(EXIT_FAILURE, "glob failed"); 1027 1028 if (pg.gl_matchc == 0) { 1029 warnx("Default path in %s doesn't exist", _PATH_MANCONF); 1030 globfree(&pg); 1031 return; 1032 } 1033 1034 TAILQ_FOREACH(esubd, &subdirs->entrylist, q) { 1035 /* Drop cat page directory, only sources are relevant. */ 1036 if (strncmp(esubd->s, "man", 3)) 1037 continue; 1038 1039 for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 1040 if (asprintf(&buf, "%s%s", *ap, esubd->s) == -1) 1041 err(EXIT_FAILURE, "memory allocation error"); 1042 /* Skip non-directories. */ 1043 if (stat(buf, &sb) == 0 && S_ISDIR(sb.st_mode)) 1044 printf("%s\n", buf); 1045 1046 free(buf); 1047 } 1048 } 1049 globfree(&pg); 1050} 1051