1/* 2 * man.c 3 * 4 * Copyright (c) 1990, 1991, John W. Eaton. 5 * 6 * You may distribute under the terms of the GNU General Public 7 * License as specified in the file COPYING that comes with the man 8 * distribution. 9 * 10 * John W. Eaton 11 * jwe@che.utexas.edu 12 * Department of Chemical Engineering 13 * The University of Texas at Austin 14 * Austin, Texas 78712 15 * 16 * Some manpath, compression and locale related changes - aeb - 940320 17 * Some suid related changes - aeb - 941008 18 * Some more fixes, Pauline Middelink & aeb, Oct 1994 19 * man -K: aeb, Jul 1995 20 * Split off of manfile for man2html, aeb, New Year's Eve 1997 21 */ 22 23#include <stdio.h> 24#include <ctype.h> 25#include <string.h> 26#include <stdlib.h> 27#include <sys/file.h> 28#include <sys/stat.h> /* for chmod */ 29#include <signal.h> 30#include <errno.h> 31#include <unistd.h> 32#include <locale.h> 33 34#ifndef R_OK 35#define R_OK 4 36#endif 37 38extern char *index (const char *, int); /* not always in <string.h> */ 39extern char *rindex (const char *, int); /* not always in <string.h> */ 40 41#include "defs.h" 42#include "gripes.h" 43#include "man.h" 44#include "manfile.h" 45#include "manpath.h" 46#include "man-config.h" 47#include "man-getopt.h" 48#include "man-iconv.h" 49#include "to_cat.h" 50#include "util.h" 51#include "glob.h" 52#include "different.h" 53#include "man-iconv.h" 54 55#define SIZE(x) (sizeof(x)/sizeof((x)[0])) 56 57const char *progname; 58const char *pager, *browser, *htmlpager; 59char *colon_sep_section_list; 60char *roff_directive; 61char *dohp = 0; 62int do_irix; 63int do_win32; 64int apropos; 65int whatis; 66int nocats; /* set by -c option: do not use cat page */ 67 /* this means that cat pages must not be used, 68 perhaps because the user knows they are 69 old or corrupt or so */ 70int can_use_cache; /* output device is a tty, width 80 */ 71 /* this means that the result may be written 72 in /var/cache, and may be read from there */ 73int findall; 74int print_where; 75int one_per_line; 76int do_troff; 77int preformat; 78int debug; 79int fhs; 80int fsstnd; 81int noautopath; 82int nocache; 83static int is_japanese; 84static char *language; 85static char **section_list; 86 87#ifdef DO_COMPRESS 88int do_compress = 1; 89#else 90int do_compress = 0; 91#endif 92 93#define BUFSIZE 8192 94 95/* 96 * Try to determine the line length to use. 97 * Preferences: 1. MANWIDTH, 2. ioctl, 3. COLUMNS, 4. 80 98 * 99 * joey, 950902 100 */ 101 102#include <sys/ioctl.h> 103 104int line_length = 80; 105int ll = 0; 106 107static void 108get_line_length(void){ 109 char *cp; 110 int width; 111 112 if (preformat) { 113 line_length = 80; 114 return; 115 } 116 if ((cp = getenv ("MANWIDTH")) != NULL && (width = atoi(cp)) > 0) { 117 line_length = width; 118 return; 119 } 120#ifdef TIOCGWINSZ 121 if (isatty(0) && isatty(1)) { /* Jon Tombs */ 122 struct winsize wsz; 123 124 if(ioctl(0, TIOCGWINSZ, &wsz)) 125 perror("TIOCGWINSZ failed\n"); 126 else if(wsz.ws_col) { 127 line_length = wsz.ws_col; 128 return; 129 } 130 } 131#endif 132 if ((cp = getenv ("COLUMNS")) != NULL && (width = atoi(cp)) > 0) 133 line_length = width; 134 else 135 line_length = 80; 136} 137 138static int 139setll(void) { 140 return 141 (!do_troff && (line_length < 66 || line_length > 80)) ? 142 line_length*9/10 : 0; 143} 144 145/* People prefer no page headings in their man screen output; 146 now ".pl 0" has a bad effect on .SH etc, so we need ".pl N" 147 for some large number N, like 1100i (a hundred pages). */ 148#define VERY_LONG_PAGE "1100i" 149 150static char * 151setpl(void) { 152 char *pl; 153 154 /* Short-circuit bogus behavior (3828722). */ 155 return NULL; 156 157 if (do_troff) 158 return NULL; 159 if (preformat) 160 pl = VERY_LONG_PAGE; 161 else 162 if ((pl = getenv("MANPL")) == 0) { 163 if (isatty(0) && isatty(1)) 164 pl = VERY_LONG_PAGE; 165 else 166 pl = "11i"; /* old troff default */ 167 } 168 return pl; 169} 170 171/* 172 * Check to see if the argument is a valid section number. If the 173 * first character of name is a numeral, or the name matches one of 174 * the sections listed in section_list, we'll assume that it's a section. 175 * The list of sections in config.h simply allows us to specify oddly 176 * named directories like .../man3f. Yuk. 177 */ 178static char * 179is_section (char *name) { 180 char **vs; 181 182 /* 3Xt may be a section, but 3DBorder is a man page */ 183 if (isdigit (name[0]) && !isdigit (name[1]) && strlen(name) < 5) 184 return my_strdup (name); 185 186 for (vs = section_list; *vs != NULL; vs++) 187 if (strcmp (*vs, name) == 0) 188 return my_strdup (name); 189 190 return NULL; 191} 192 193 194static void 195remove_file (char *file) { 196 int i; 197 198 i = unlink (file); 199 200 if (debug) { 201 if (i) 202 perror(file); 203 else 204 gripe (UNLINKED, file); 205 } 206} 207 208static void 209remove_other_catfiles (const char *catfile) { 210 char *pathname; 211 char *t; 212 char **gf; 213 int offset; 214 215 pathname = my_strdup(catfile); 216 t = rindex(pathname, '.'); 217 if (t == NULL || strcmp(t, getval("COMPRESS_EXT"))) 218 return; 219 offset = t - pathname; 220 strcpy(t, "*"); 221 gf = glob_filename (pathname); 222 223 if (gf != (char **) -1 && gf != NULL) { 224 for ( ; *gf; gf++) { 225 /* 226 * Only remove files with a known extension, like .Z 227 * (otherwise we might kill a lot when called with 228 * catfile = ".gz" ...) 229 */ 230 if (strlen (*gf) <= offset) { 231 if (strlen (*gf) == offset) /* uncompressed version */ 232 remove_file (*gf); 233 continue; 234 } 235 236 if (!strcmp (*gf + offset, getval("COMPRESS_EXT"))) 237 continue; 238 239 if (get_expander (*gf) != NULL) 240 remove_file (*gf); 241 } 242 } 243} 244 245/* 246 * Simply display the preformatted page. 247 */ 248static int 249display_cat_file (const char *file) { 250 int found; 251 252 if (preformat) 253 return 1; /* nothing to do - preformat only */ 254 255 found = 0; 256 257 if (access (file, R_OK) == 0 && different_cat_file(file)) { 258 char *command = NULL; 259 const char *expander = get_expander (file); 260 261 if (expander != NULL && expander[0] != 0) { 262 if (isatty(1)) 263 command = my_xsprintf("%s %S | %s", expander, file, pager); 264 else 265 command = my_xsprintf("%s %S", expander, file); 266 } else { 267 if (isatty(1)) { 268 command = my_xsprintf("%s %S", pager, file); 269 } else { 270 const char *cat = getval("CAT"); 271 command = my_xsprintf("%s %S", cat[0] ? cat : "cat", file); 272 } 273 } 274 found = !do_system_command (command, 0); 275 } 276 return found; 277} 278 279/* 280 * Simply display the preformatted page. 281 */ 282static int 283display_html_file (const char *file) { 284 int found; 285 286 found = 0; 287 288 if (access (file, R_OK) == 0 && different_cat_file(file)) { 289 char *command = NULL; 290 291 if (isatty(1)) { 292 command = my_xsprintf("%s %S", browser, file); 293 } else { 294 command = my_xsprintf("%s %S", htmlpager, file); 295 } 296 found = !do_system_command (command, 0); 297 } 298 return found; 299 300 return 1; 301} 302 303/* 304 * Try to find the ultimate source file. If the first line of the 305 * current file is not of the form 306 * 307 * .so man3/printf.3s 308 * 309 * the input file name is returned. 310 * 311 * For /cd/usr/src/usr.bin/util-linux-1.5/mount/umount.8.gz 312 * (which contains `.so man8/mount.8') 313 * we return /cd/usr/src/usr.bin/util-linux-1.5/mount/mount.8.gz . 314 * 315 * For /usr/man/man3/TIFFScanlineSize.3t 316 * (which contains `.so TIFFsize.3t') 317 * we return /usr/man/man3/TIFFsize.3t . 318 */ 319static const char * 320ultimate_source (const char *name0) { 321 FILE *fp; 322 char *name; 323 const char *expander; 324 int expfl = 0; 325 char *fgr; 326 char *beg; 327 char *end; 328 char *cp; 329 char buf[BUFSIZE]; 330 static char ultname[BUFSIZE]; 331 332 if (strlen(name0) >= sizeof(ultname)) 333 return name0; 334 strcpy(ultname, name0); 335 name = ultname; 336 337again: 338 expander = get_expander (name); 339 if (expander && *expander) { 340 char *command; 341 342 command = my_xsprintf ("%s '%Q'", expander, name); 343 fp = my_popen (command, "r"); 344 if (fp == NULL) { 345 perror("popen"); 346 gripe (EXPANSION_FAILED, command); 347 return (NULL); 348 } 349 fgr = fgets (buf, sizeof(buf), fp); 350 pclose (fp); 351 expfl = 1; 352 } else { 353 fp = fopen (name, "r"); 354 if (fp == NULL && expfl) { 355 char *extp = rindex (name0, '.'); 356 if (extp && *extp && strlen(name)+strlen(extp) < BUFSIZE) { 357 strcat(name, extp); 358 fp = fopen (name, "r"); 359 } 360 } 361 /* 362 * Some people have compressed man pages, but uncompressed 363 * .so files - we could glob for all possible extensions, 364 * for now: only try .gz 365 */ 366 else if (fp == NULL && get_expander(".gz") && 367 strlen(name)+strlen(".gz") < BUFSIZE) { 368 strcat(name, ".gz"); 369 fp = fopen (name, "r"); 370 } 371 372 if (fp == NULL) { 373 perror("fopen"); 374 gripe (OPEN_ERROR, name); 375 return (NULL); 376 } 377 fgr = fgets (buf, sizeof(buf), fp); 378 fclose (fp); 379 } 380 381 if (fgr == NULL) { 382 perror("fgets"); 383 gripe (READ_ERROR, name); 384 return (NULL); 385 } 386 387 if (!isascii(buf[0])) 388 return (NULL); 389 390 if (strncmp(buf, ".so", 3)) 391 return (my_strdup(name)); 392 393 beg = buf+3; 394 while (*beg == ' ' || *beg == '\t') 395 beg++; 396 397 end = beg; 398 while (*end != ' ' && *end != '\t' && *end != '\n' && *end != '\0') 399 end++; /* note that buf is NUL-terminated */ 400 *end = '\0'; 401 402 /* If name ends in path/manx/foo.9x then use path, otherwise 403 try same directory. */ 404 if ((cp = rindex(name, '/')) == NULL) /* very strange ... */ 405 return 0; 406 *cp = 0; 407 408 /* allow "man ./foo.3" where foo.3 contains ".so man2/bar.2" */ 409 if ((cp = rindex(name, '/')) != NULL && !strcmp(cp+1, ".")) 410 *cp = 0; 411 412 /* In all cases, the new name will be something from name 413 followed by something from beg. */ 414 if (strlen(name) + strlen(beg) + 1 >= BUFSIZ) 415 return 0; /* very long names, ignore */ 416 417 if (beg[0] == '/') { 418 strcpy(name, beg); 419 } else 420 if (!index(beg, '/')) { 421 /* strange.. try same directory as the .so file */ 422 strcat(name, "/"); 423 strcat(name, beg); 424 } else if((cp = rindex(name, '/')) != NULL && !strncmp(cp+1, "man", 3)) { 425 strcpy(cp+1, beg); 426 } else if((cp = rindex(beg, '/')) != NULL) { 427 strcat(name, cp); 428 } else { 429 strcat(name, "/"); 430 strcat(name, beg); 431 } 432 433 goto again; 434} 435 436static void 437add_directive (const char *d, const char *file, char *buf, int buflen) { 438 if ((d = getval(d)) != 0 && *d) { 439 if (*buf == 0) { 440 if (strlen(d) + strlen(file) + 2 + 2 > buflen) // 2 extra for the single quotes 441 return; 442 strcpy (buf, d); 443 strcat (buf, " "); 444 char *fileq = my_xsprintf("'%Q'", file); 445 strcat (buf, fileq); 446 free(fileq); 447 } else { 448 if (strlen(d) + strlen(buf) + 4 > buflen) 449 return; 450 strcat (buf, " | "); 451 strcat (buf, d); 452 } 453 } 454} 455 456static int 457is_lang_page (char *lang, const char *file) { 458 char lang_path[16] = ""; 459 460 snprintf(lang_path, sizeof(lang_path), "/%s/", lang); 461 if (strstr(file, lang_path)) 462 return 1; 463 if (strlen(lang) > 2) { 464 lang_path[3] = '/'; 465 lang_path[4] = 0; 466 if (strstr(file, lang_path)) 467 return 1; 468 } 469 return 0; 470} 471 472static int 473parse_roff_directive (char *cp, const char *file, char *buf, int buflen) { 474 char c; 475 int tbl_found = 0; 476 int use_jroff; 477 478 use_jroff = (is_japanese && 479 (strstr(file, "/jman/") || is_lang_page(language, file))); 480 481 while ((c = *cp++) != '\0') { 482 switch (c) { 483 case 'e': 484 if (debug) 485 gripe (FOUND_EQN); 486 add_directive((do_troff ? "EQN" : use_jroff ? "JNEQN": "NEQN"), 487 file, buf, buflen); 488 break; 489 490 case 'g': 491 if (debug) 492 gripe (FOUND_GRAP); 493 add_directive ("GRAP", file, buf, buflen); 494 break; 495 496 case 'p': 497 if (debug) 498 gripe (FOUND_PIC); 499 add_directive ("PIC", file, buf, buflen); 500 break; 501 502 case 't': 503 if (debug) 504 gripe (FOUND_TBL); 505 tbl_found++; 506 add_directive ("TBL", file, buf, buflen); 507 break; 508 509 case 'v': 510 if (debug) 511 gripe (FOUND_VGRIND); 512 add_directive ("VGRIND", file, buf, buflen); 513 break; 514 515 case 'r': 516 if (debug) 517 gripe (FOUND_REFER); 518 add_directive ("REFER", file, buf, buflen); 519 break; 520 521 case ' ': 522 case '\t': 523 case '\n': 524 goto done; 525 526 default: 527 return -1; 528 } 529 } 530 531done: 532 if (*buf == 0) 533 return 1; 534 535 add_directive (do_troff ? "TROFF" : use_jroff ? "JNROFF" : "NROFF", 536 "", buf, buflen); 537 538 if (tbl_found && !do_troff && *getval("COL")) 539 add_directive ("COL", "", buf, buflen); 540 541 return 0; 542} 543 544static char * 545eos(char *s) { 546 while(*s) s++; 547 return s; 548} 549 550/* 551 * Create command to format FILE, in the directory PATH/manX 552 */ 553static char * 554make_roff_command (const char *path, const char *file) { 555 FILE *fp; 556 static char buf [BUFSIZE]; 557 char line [BUFSIZE], bufh [BUFSIZE], buft [BUFSIZE]; 558 int status, ll; 559 char *cp, *fgr, *pl; 560 char *command = ""; 561 const char *expander; 562 const char *converter; 563 564 /* if window size differs much from 80, try to adapt */ 565 /* (but write only standard formatted files to the cat directory, 566 see can_use_cache) */ 567 ll = setll(); 568 pl = setpl(); 569 if (ll && debug) 570 gripe (NO_CAT_FOR_NONSTD_LL); 571 572 expander = get_expander (file); 573 converter = get_converter (path); 574 575 /* head */ 576 bufh[0] = 0; 577 if (ll || pl) { 578 /* some versions of echo do not accept the -e flag, 579 so we just use two echo calls when needed */ 580 strcat(bufh, "("); 581 if (ll) { 582 /* 583 * We should set line length and title line length. 584 * However, a .lt command here fails, only 585 * .ev 1; .lt ...; .ev helps for my version of groff. 586 * The LL assignment is needed by the mandoc macros. 587 */ 588 sprintf(eos(bufh), "echo \".ll %d.%di\"; ", ll/10, ll%10); 589 sprintf(eos(bufh), "echo \".nr LL %d.%di\"; ", ll/10, ll%10); 590#if 0 591 sprintf(eos(bufh), "echo \".lt %d.%di\"; ", ll/10, ll%10); 592#endif 593 } 594 if (pl) 595 sprintf(eos(bufh), "echo \".pl %.128s\"; ", pl); 596 } 597 598 /* tail */ 599 buft[0] = 0; 600 if (ll || pl) { 601 if (pl && !strcmp(pl, VERY_LONG_PAGE)) 602 /* At end of the nroff source, set the page length to 603 the current position plus 10 lines. This plus setpl() 604 gives us a single page that just contains the whole 605 man page. (William Webber, wew@cs.rmit.edu.au) */ 606 strcat(buft, "; echo \".\\\\\\\"\"; echo \".pl \\n(nlu+10\""); 607#if 0 608 /* In case this doesnt work for some reason, 609 michaelkjohnson suggests: I've got a simple 610 awk invocation that I throw into the pipeline: */ 611 612 awk 'BEGIN {RS="\n\n\n\n*"} /.*/ {print}' 613#endif 614 strcat(buft, ")"); 615 } 616 617 if (expander && *expander) { 618 if (converter && *converter) 619 command = my_xsprintf("%s%s '%Q' | %s%s", 620 bufh, expander, file, converter, buft); 621 else 622 command = my_xsprintf("%s%s '%Q'%s", 623 bufh, expander, file, buft); 624 } else if (ll || pl) { 625 const char *cat = getval("CAT"); 626 if (!cat || !*cat) 627 cat = "cat"; 628 629 if (converter && *converter) 630 command = my_xsprintf("%s%s '%Q' | %s%s", 631 bufh, cat, file, converter, buft); 632 else 633 command = my_xsprintf("%s%s '%Q'%s", 634 bufh, cat, file, buft); 635 } 636 637 if (strlen(command) >= sizeof(buf)) 638 exit(1); 639 strcpy(buf, command); 640 641 if (roff_directive != NULL) { 642 if (debug) 643 gripe (ROFF_FROM_COMMAND_LINE); 644 645 status = parse_roff_directive (roff_directive, file, 646 buf, sizeof(buf)); 647 648 if (status == 0) 649 return buf; 650 651 if (status == -1) 652 gripe (ROFF_CMD_FROM_COMMANDLINE_ERROR); 653 } 654 655 if (expander && *expander) { 656 char *cmd = my_xsprintf ("%s '%Q'", expander, file); 657 fp = my_popen (cmd, "r"); 658 if (fp == NULL) { 659 perror("popen"); 660 gripe (EXPANSION_FAILED, cmd); 661 return (NULL); 662 } 663 fgr = fgets (line, sizeof(line), fp); 664 pclose (fp); 665 } else { 666 fp = fopen (file, "r"); 667 if (fp == NULL) { 668 perror("fopen"); 669 gripe (OPEN_ERROR, file); 670 return (NULL); 671 } 672 fgr = fgets (line, sizeof(line), fp); 673 fclose (fp); 674 } 675 676 if (fgr == NULL) { 677 perror("fgets"); 678 gripe (READ_ERROR, file); 679 return (NULL); 680 } 681 682 cp = &line[0]; 683 if (*cp++ == '\'' && *cp++ == '\\' && *cp++ == '"' && *cp++ == ' ') { 684 if (debug) 685 gripe (ROFF_FROM_FILE, file); 686 687 status = parse_roff_directive (cp, file, buf, sizeof(buf)); 688 689 if (status == 0) 690 return buf; 691 692 if (status == -1) 693 gripe (ROFF_CMD_FROM_FILE_ERROR, file); 694 } 695 696 if ((cp = getenv ("MANROFFSEQ")) != NULL) { 697 if (debug) 698 gripe (ROFF_FROM_ENV); 699 700 status = parse_roff_directive (cp, file, buf, sizeof(buf)); 701 702 if (status == 0) 703 return buf; 704 705 if (status == -1) 706 gripe (MANROFFSEQ_ERROR); 707 } 708 709 if (debug) 710 gripe (USING_DEFAULT); 711 712 (void) parse_roff_directive ("t", file, buf, sizeof(buf)); 713 714 return buf; 715} 716 717/* 718 * Try to format the man page and create a new formatted file. Return 719 * 1 for success and 0 for failure. 720 */ 721static int 722make_cat_file (const char *path, const char *man_file, const char *cat_file) { 723 int mode; 724 FILE *fp; 725 char *roff_command; 726 char *command = NULL; 727 struct stat statbuf; 728 729 /* _Before_ first, make sure we will write to a regular file. */ 730 if (stat(cat_file, &statbuf) == 0) { 731 if(!S_ISREG(statbuf.st_mode)) { 732 if (debug) 733 gripe (CAT_OPEN_ERROR, cat_file); 734 return 0; 735 } 736 } 737 738 /* First make sure we can write the file; create an empty file. */ 739 /* If we are suid it must get mode 0666. */ 740 if ((fp = fopen (cat_file, "w")) == NULL) { 741 if (errno == ENOENT) /* directory does not exist */ 742 return 0; 743 744 /* If we cannot write the file, maybe we can delete it */ 745 if(unlink (cat_file) != 0 || (fp = fopen (cat_file, "w")) == NULL) { 746 if (errno == EROFS) /* possibly a CDROM */ 747 return 0; 748 if (debug) 749 gripe (CAT_OPEN_ERROR, cat_file); 750 if (!suid) 751 return 0; 752 753 /* maybe the real user can write it */ 754 /* note: just doing "> %s" gives the wrong exit status */ 755 command = my_xsprintf("cp /dev/null %S 2>/dev/null", cat_file); 756 if (do_system_command(command, 1)) { 757 if (debug) 758 gripe (USER_CANNOT_OPEN_CAT); 759 return 0; 760 } 761 if (debug) 762 gripe (USER_CAN_OPEN_CAT); 763 } 764 } else { 765 /* we can write it - good */ 766 fclose (fp); 767 768 /* but maybe the real user cannot - let's allow everybody */ 769 /* the mode is reset below */ 770 if (suid) { 771 if (chmod (cat_file, 0666)) { 772 /* probably we are sgid but not owner; 773 just delete the file and create it again */ 774 if(unlink(cat_file) != 0) { 775 command = my_xsprintf("rm %S", cat_file); 776 (void) do_system_command (command, 1); 777 } 778 if ((fp = fopen (cat_file, "w")) != NULL) 779 fclose (fp); 780 } 781 } 782 } 783 784 roff_command = make_roff_command (path, man_file); 785 if (roff_command == NULL) 786 return 0; 787 if (do_compress) 788 /* The cd is necessary, because of .so commands, 789 like .so man1/bash.1 in bash_builtins.1. 790 But it changes the meaning of man_file and cat_file, 791 if these are not absolute. */ 792 793 command = my_xsprintf("(cd %S && %s | %S > %S)", path, 794 roff_command, getval("COMPRESS"), cat_file); 795 else 796 command = my_xsprintf ("(cd %S && %s > %S)", path, 797 roff_command, cat_file); 798 799 /* 800 * Don't let the user interrupt the system () call and screw up 801 * the formatted man page if we're not done yet. 802 */ 803 signal (SIGINT, SIG_IGN); 804 805 gripe (PLEASE_WAIT); 806 807 if (!do_system_command (command, 0)) { 808 /* success */ 809 mode = ((ruid != euid) ? 0644 : (rgid != egid) ? 0464 : 0444); 810 if(chmod (cat_file, mode) != 0 && suid) { 811 command = my_xsprintf ("chmod 0%o %S", mode, cat_file); 812 (void) do_system_command (command, 1); 813 } 814 /* be silent about the success of chmod - it is not important */ 815 if (debug) 816 gripe (CHANGED_MODE, cat_file, mode); 817 } else { 818 /* something went wrong - remove garbage */ 819 if(unlink(cat_file) != 0 && suid) { 820 command = my_xsprintf ("rm %S", cat_file); 821 (void) do_system_command (command, 1); 822 } 823 } 824 825 signal (SIGINT, SIG_DFL); 826 827 return 1; 828} 829 830static int 831display_man_file(const char *path, const char *man_file) { 832 char *roff_command; 833 char *command; 834 835 if (!different_man_file (man_file)) 836 return 0; 837 roff_command = make_roff_command (path, man_file); 838 if (roff_command == NULL) 839 return 0; 840 if (do_troff) 841 command = my_xsprintf ("(cd '%Q' && %s)", path, roff_command); 842 else 843 command = my_xsprintf ("(cd '%Q' && %s | (%s || true))", path, 844 roff_command, pager); 845 846 return !do_system_command (command, 0); 847} 848 849/* 850 * make and display the cat file - return 0 if something went wrong 851 */ 852static int 853make_and_display_cat_file (const char *path, const char *man_file) { 854 const char *cat_file; 855 const char *ext; 856 int status; 857 int standards; 858 859 ext = (do_compress ? getval("COMPRESS_EXT") : 0); 860 861 standards = (fhs ? FHS : 0) | (fsstnd ? FSSTND : 0) | (dohp ? DO_HP : 0); 862 863 if ((cat_file = convert_to_cat(man_file, ext, standards)) == NULL) 864 return 0; 865 866 if (debug) 867 gripe (PROPOSED_CATFILE, cat_file); 868 869 /* 870 * If cat_file exists, check whether it is more recent. 871 * Otherwise, check for other cat files (maybe there are 872 * old .Z files that should be removed). 873 */ 874 875 status = ((nocats | preformat) ? -2 : is_newer (man_file, cat_file)); 876 if (debug) 877 gripe (IS_NEWER_RESULT, status); 878 if (status == -1 || status == -3) { 879 /* what? man_file does not exist anymore? */ 880 gripe (CANNOT_STAT, man_file); 881 return(0); 882 } 883 884 if (status != 0 || access (cat_file, R_OK) != 0) { 885 /* 886 * Cat file is out of date (status = 1) or does not exist or is 887 * empty or is to be rewritten (status = -2) or is unreadable. 888 * Try to format and save it. 889 */ 890 if (print_where) { 891 printf ("%s\n", man_file); 892 return 1; 893 } 894 895 if (!make_cat_file (path, man_file, cat_file)) 896 return 0; 897 898 /* 899 * If we just created this cat file, unlink any others. 900 */ 901 if (status == -2 && do_compress) 902 remove_other_catfiles(cat_file); 903 } else { 904 /* 905 * Formatting not necessary. Cat file is newer than source 906 * file, or source file is not present but cat file is. 907 */ 908 if (print_where) { 909 if (one_per_line) { 910 /* addition by marty leisner - leisner@sdsp.mc.xerox.com */ 911 printf("%s\n", cat_file); 912 printf("%s\n", man_file); 913 } else 914 printf ("%s (<-- %s)\n", cat_file, man_file); 915 return 1; 916 } 917 } 918 (void) display_cat_file (cat_file); 919 return 1; 920} 921 922/* 923 * Try to format the man page source and save it, then display it. If 924 * that's not possible, try to format the man page source and display 925 * it directly. 926 */ 927static int 928format_and_display (const char *man_file) { 929 const char *path; 930 931 if (access (man_file, R_OK) != 0) 932 return 0; 933 934 path = mandir_of(man_file); 935 if (path == NULL) 936 return 0; 937 938 /* first test for contents .so man1/xyzzy.1 */ 939 /* (in that case we do not want to make a cat file identical 940 to cat1/xyzzy.1) */ 941 man_file = ultimate_source (man_file); 942 if (man_file == NULL) 943 return 0; 944 945 if (do_troff) { 946 char *command; 947 char *roff_command = make_roff_command (path, man_file); 948 949 if (roff_command == NULL) 950 return 0; 951 952 command = my_xsprintf("(cd %S && %s)", path, roff_command); 953 return !do_system_command (command, 0); 954 } 955 956 if (can_use_cache && make_and_display_cat_file (path, man_file)) 957 return 1; 958 959 /* line length was wrong or could not display cat_file */ 960 if (print_where) { 961 printf ("%s\n", man_file); 962 return 1; 963 } 964 965 return display_man_file (path, man_file); 966} 967 968/* 969 * Search for manual pages. 970 * 971 * If preformatted manual pages are supported, look for the formatted 972 * file first, then the man page source file. If they both exist and 973 * the man page source file is newer, or only the source file exists, 974 * try to reformat it and write the results in the cat directory. If 975 * it is not possible to write the cat file, simply format and display 976 * the man file. 977 * 978 * If preformatted pages are not supported, or the troff option is 979 * being used, only look for the man page source file. 980 * 981 * Note that globbing is necessary also if the section is given, 982 * since a preformatted man page might be compressed. 983 * 984 */ 985static int 986man (const char *name, const char *section) { 987 int found, type, flags; 988 struct manpage *mp; 989 990 found = 0; 991 992 /* allow man ./manpage for formatting explicitly given man pages */ 993 if (index(name, '/')) { 994 char fullname[BUFSIZE]; 995 char fullpath[BUFSIZE]; 996 char *path; 997 char *cp; 998 FILE *fp = fopen(name, "r"); 999 1000 if (!fp) { 1001 perror(name); 1002 return 0; 1003 } 1004 fclose (fp); 1005 if (*name != '/' && getcwd(fullname, sizeof(fullname)) 1006 && strlen(fullname) + strlen(name) + 3 < sizeof(fullname)) { 1007 strcat (fullname, "/"); 1008 strcat (fullname, name); 1009 } else if (strlen(name) + 2 < sizeof(fullname)) { 1010 strcpy (fullname, name); 1011 } else { 1012 fprintf(stderr, "%s: name too long\n", name); 1013 return 0; 1014 } 1015 1016 strcpy (fullpath, fullname); 1017 if ((cp = rindex(fullpath, '/')) != NULL 1018 && cp-fullpath+4 < sizeof(fullpath)) { 1019 strcpy(cp+1, ".."); 1020 path = fullpath; 1021 } else 1022 path = "."; 1023 1024 name = ultimate_source (fullname); 1025 if (!name) 1026 return 0; 1027 1028 if (print_where) { 1029 printf("%s\n", name); 1030 return 1; 1031 } 1032 return display_man_file (path, name); 1033 } 1034 1035 fflush (stdout); 1036 init_manpath(); 1037 1038 can_use_cache = nocache ? 0 : (preformat || print_where || 1039 (isatty(0) && isatty(1) && !setll())); 1040 1041 if (do_troff) { 1042 const char *t = getval("TROFF"); 1043 if (!t || !*t) 1044 return 0; /* don't know how to format */ 1045 type = TYPE_MAN; 1046 } else { 1047 const char *n = getval("NROFF"); 1048 type = 0; 1049 if (can_use_cache) 1050 type |= TYPE_CAT; 1051 if (n && *n) 1052 type |= TYPE_MAN; 1053 if (fhs || fsstnd) 1054 type |= TYPE_SCAT; 1055 1056 n = getval("BROWSER"); 1057 if (n && *n) 1058 type |= TYPE_HTML; 1059 } 1060 1061 flags = type; 1062 if (!findall) 1063 flags |= ONLY_ONE; 1064 if (fsstnd) 1065 flags |= FSSTND; 1066 else if (fhs) 1067 flags |= FHS; 1068 if (dohp) 1069 flags |= DO_HP; 1070 if (do_irix) 1071 flags |= DO_IRIX; 1072 if (do_win32) 1073 flags |= DO_WIN32; 1074 1075 mp = manfile(name, section, flags, section_list, mandirlist, 1076 convert_to_cat); 1077 found = 0; 1078 while (mp) { 1079 if (mp->type == TYPE_MAN) { 1080 found = format_and_display(mp->filename); 1081 } else if (mp->type == TYPE_CAT || mp->type == TYPE_SCAT) { 1082 if (print_where) { 1083 printf ("%s\n", mp->filename); 1084 found = 1; 1085 } else 1086 found = display_cat_file(mp->filename); 1087 } else if (mp->type == TYPE_HTML) { 1088 if (print_where) { 1089 printf ("%s\n", mp->filename); 1090 found = 1; 1091 } else 1092 found = display_html_file(mp->filename); 1093 } else 1094 /* internal error */ 1095 break; 1096 if (found && !findall) 1097 break; 1098 mp = mp->next; 1099 } 1100 return found; 1101} 1102 1103static char ** 1104get_section_list (void) { 1105 int i; 1106 const char *p; 1107 char *end; 1108 static char *tmp_section_list[100]; 1109 1110 if (colon_sep_section_list == NULL) { 1111 if ((p = getenv ("MANSECT")) == NULL) 1112 p = getval ("MANSECT"); 1113 colon_sep_section_list = my_strdup (p); 1114 } 1115 1116 i = 0; 1117 for (p = colon_sep_section_list; ; p = end+1) { 1118 if ((end = strchr (p, ':')) != NULL) 1119 *end = '\0'; 1120 1121 tmp_section_list[i++] = my_strdup (p); 1122 1123 if (end == NULL || i+1 == SIZE(tmp_section_list)) 1124 break; 1125 } 1126 1127 tmp_section_list [i] = NULL; 1128 return tmp_section_list; 1129} 1130 1131/* return 0 when all was OK */ 1132static int 1133do_global_apropos (char *name, char *section) { 1134 char **dp, **gf; 1135 char *pathname; 1136 char *command; 1137 int status, res; 1138 1139 status = 0; 1140 init_manpath(); 1141 if (mandirlist) 1142 for (dp = mandirlist; *dp; dp++) { 1143 if (debug) 1144 gripe(SEARCHING, *dp); 1145 pathname = my_xsprintf("%s/man%s/*", *dp, section ? section : "*"); 1146 gf = glob_filename (pathname); 1147 free(pathname); 1148 1149 if (gf != (char **) -1 && gf != NULL) { 1150 for( ; *gf; gf++) { 1151 const char *expander = get_expander (*gf); 1152 if (expander) 1153 command = my_xsprintf("%s %S | grep '%Q'" 1154 "> /dev/null 2> /dev/null", 1155 expander, *gf, name); 1156 else 1157 command = my_xsprintf("grep '%Q' %S" 1158 "> /dev/null 2> /dev/null", 1159 name, *gf); 1160 res = do_system_command (command, 1); 1161 status |= res; 1162 free (command); 1163 if (res == 0) { 1164 if (print_where) 1165 printf("%s\n", *gf); 1166 else { 1167 /* should read LOCALE, but libc 4.6.27 doesn't 1168 seem to handle LC_RESPONSE yet */ 1169 int answer, c; 1170 char path[BUFSIZE]; 1171 1172 printf("%s? [ynq] ", *gf); 1173 fflush(stdout); 1174 answer = c = getchar(); 1175 while (c != '\n' && c != EOF) 1176 c = getchar(); 1177 if(index("QqXx", answer)) 1178 exit(0); 1179 if(index("YyJj", answer)) { 1180 char *ri; 1181 1182 strcpy(path, *gf); 1183 ri = rindex(path, '/'); 1184 if (ri) 1185 *ri = 0; 1186 format_and_display(*gf); 1187 } 1188 } 1189 } 1190 } 1191 } 1192 } 1193 return status; 1194} 1195 1196/* Special code for Japanese (to pick jnroff instead of nroff, etc.) */ 1197static void 1198setlang(void) { 1199 char *lang; 1200 1201 /* We use getenv() instead of setlocale(), because of 1202 glibc 2.1.x security policy for SetUID/SetGID binary. */ 1203 if ((lang = getenv("LANG")) == NULL && 1204 (lang = getenv("LC_ALL")) == NULL && 1205 (lang = getenv("LC_CTYPE")) == NULL) 1206 /* nothing */; 1207 1208 language = lang; 1209 is_japanese = (lang && strncmp(lang, "ja", 2) == 0); 1210} 1211 1212/* 1213 * Handle the apropos option. Cheat by using another program. 1214 */ 1215static int 1216do_apropos (char *name) { 1217 char *command; 1218 1219 command = my_xsprintf("'%s' '%Q'", getval("APROPOS"), name); 1220 return do_system_command (command, 0); 1221} 1222 1223/* 1224 * Handle the whatis option. Cheat by using another program. 1225 */ 1226static int 1227do_whatis (char *name) { 1228 char *command; 1229 1230 command = my_xsprintf("'%s' '%Q'", getval("WHATIS"), name); 1231 return do_system_command (command, 0); 1232} 1233 1234int 1235main (int argc, char **argv) { 1236 int status = 1; 1237 char *nextarg; 1238 char *tmp; 1239 char *section = 0; 1240 1241#ifdef __CYGWIN__ 1242 extern int optind; 1243#endif 1244 1245 1246#if 0 1247 { 1248 /* There are no known cases of buffer overflow caused by 1249 excessively long environment variables. In case you find one, 1250 the simplistic way to fix is to enable this stopgap. */ 1251 char *s; 1252#define CHECK(p,l) s=getenv(p); if(s && strlen(s)>(l)) { fprintf(stderr, "ERROR: Environment variable %s too long!\n", p); exit(1); } 1253 CHECK("LANG", 32); 1254 CHECK("LANGUAGE", 128); 1255 CHECK("LC_MESSAGES", 128); 1256 CHECK("MANPAGER", 128); 1257 CHECK("MANPL", 128); 1258 CHECK("MANROFFSEQ", 128); 1259 CHECK("MANSECT", 128); 1260 CHECK("MAN_HP_DIREXT", 128); 1261 CHECK("PAGER", 128); 1262 CHECK("SYSTEM", 64); 1263 CHECK("BROWSER", 64); 1264 CHECK("HTMLPAGER", 64); 1265 /* COLUMNS, LC_ALL, LC_CTYPE, MANPATH, MANWIDTH, MAN_IRIX_CATNAMES, 1266 MAN_ICONV_PATH, MAN_ICONV_OPT, MAN_ICONV_INPUT_CHARSET, 1267 MAN_ICONV_OUTPUT_CHARSET, NLSPATH, PATH */ 1268 } 1269#endif 1270 1271 1272#ifndef __FreeBSD__ 1273 /* Slaven Rezif: FreeBSD-2.2-SNAP does not recognize LC_MESSAGES. */ 1274 setlocale(LC_CTYPE, ""); /* used anywhere? maybe only isdigit()? */ 1275 setlocale(LC_MESSAGES, ""); 1276#endif 1277 1278 /* No doubt we'll need some generic language code here later. 1279 For the moment only Japanese support. */ 1280 setlang(); 1281 1282 /* Handle /usr/man/man1.Z/name.1 nonsense from HP */ 1283 dohp = getenv("MAN_HP_DIREXT"); /* .Z */ 1284 1285 /* Handle ls.z (instead of ls.1.z) cat page naming from IRIX */ 1286 if (getenv("MAN_IRIX_CATNAMES")) 1287 do_irix = 1; 1288 1289 /* Handle lack of ':' in NTFS file names */ 1290#if defined(_WIN32) || defined(__CYGWIN__) 1291 do_win32 = 1; 1292#endif 1293 1294 progname = mkprogname (argv[0]); 1295 1296 get_permissions (); 1297 get_line_length(); 1298 1299 /* 1300 * read command line options and man.conf 1301 */ 1302 man_getopt (argc, argv); 1303 1304 /* 1305 * manpath or man --path or man -w will only print the manpath 1306 */ 1307 if (!strcmp (progname, "manpath") || (optind == argc && print_where)) { 1308 init_manpath(); 1309 prmanpath(); 1310 exit(0); 1311 } 1312 1313 if (optind == argc) 1314 gripe(NO_NAME_NO_SECTION); 1315 1316 section_list = get_section_list (); 1317 1318 while (optind < argc) { 1319 nextarg = argv[optind++]; 1320 1321 /* is_section correctly accepts 3Xt as section, but also 9wm, 1322 so we should not believe is_section() for the last arg. */ 1323 tmp = is_section (nextarg); 1324 if (tmp && optind < argc) { 1325 section = tmp; 1326 if (debug) 1327 gripe (SECTION, section); 1328 continue; 1329 } 1330 1331 if (global_apropos) 1332 status = !do_global_apropos (nextarg, section); 1333 else if (apropos) 1334 status = !do_apropos (nextarg); 1335 else if (whatis) 1336 status = !do_whatis (nextarg); 1337 else { 1338 status = man (nextarg, section); 1339 1340 if (status == 0) { 1341 if (section) 1342 gripe (NO_SUCH_ENTRY_IN_SECTION, nextarg, section); 1343 else 1344 gripe (NO_SUCH_ENTRY, nextarg); 1345 } 1346 } 1347 } 1348 return status ? EXIT_SUCCESS : EXIT_FAILURE; 1349} 1350