1#! /bin/sh 2# 3# Copyright (c) 2010 Gordon Tetlow 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25# SUCH DAMAGE. 26# 27# $FreeBSD$ 28 29# Usage: add_to_manpath path 30# Adds a variable to manpath while ensuring we don't have duplicates. 31# Returns true if we were able to add something. False otherwise. 32add_to_manpath() { 33 case "$manpath" in 34 *:$1) decho " Skipping duplicate manpath entry $1" 2 ;; 35 $1:*) decho " Skipping duplicate manpath entry $1" 2 ;; 36 *:$1:*) decho " Skipping duplicate manpath entry $1" 2 ;; 37 *) if [ -d "$1" ]; then 38 decho " Adding $1 to manpath" 39 manpath="$manpath:$1" 40 return 0 41 fi 42 ;; 43 esac 44 45 return 1 46} 47 48# Usage: build_manlocales 49# Builds a correct MANLOCALES variable. 50build_manlocales() { 51 # If the user has set manlocales, who are we to argue. 52 if [ -n "$MANLOCALES" ]; then 53 return 54 fi 55 56 parse_configs 57 58 # Trim leading colon 59 MANLOCALES=${manlocales#:} 60 61 decho "Available manual locales: $MANLOCALES" 62} 63 64# Usage: build_manpath 65# Builds a correct MANPATH variable. 66build_manpath() { 67 local IFS 68 69 # If the user has set a manpath, who are we to argue. 70 if [ -n "$MANPATH" ]; then 71 case "$MANPATH" in 72 *:) PREPEND_MANPATH=${MANPATH} ;; 73 :*) APPEND_MANPATH=${MANPATH} ;; 74 *::*) 75 PREPEND_MANPATH=${MANPATH%%::*} 76 APPEND_MANPATH=${MANPATH#*::} 77 ;; 78 *) return ;; 79 esac 80 fi 81 82 if [ -n "$PREPEND_MANPATH" ]; then 83 IFS=: 84 for path in $PREPEND_MANPATH; do 85 add_to_manpath "$path" 86 done 87 unset IFS 88 fi 89 90 search_path 91 92 decho "Adding default manpath entries" 93 IFS=: 94 for path in $man_default_path; do 95 add_to_manpath "$path" 96 done 97 unset IFS 98 99 parse_configs 100 101 if [ -n "$APPEND_MANPATH" ]; then 102 IFS=: 103 for path in $APPEND_MANPATH; do 104 add_to_manpath "$path" 105 done 106 unset IFS 107 fi 108 # Trim leading colon 109 MANPATH=${manpath#:} 110 111 decho "Using manual path: $MANPATH" 112} 113 114# Usage: check_cat catglob 115# Checks to see if a cat glob is available. 116check_cat() { 117 if exists "$1"; then 118 use_cat=yes 119 catpage=$found 120 setup_cattool $catpage 121 decho " Found catpage $catpage" 122 return 0 123 else 124 return 1 125 fi 126} 127 128# Usage: check_man manglob catglob 129# Given 2 globs, figures out if the manglob is available, if so, check to 130# see if the catglob is also available and up to date. 131check_man() { 132 if exists "$1"; then 133 # We have a match, check for a cat page 134 manpage=$found 135 setup_cattool $manpage 136 decho " Found manpage $manpage" 137 138 if [ -n "${use_width}" ]; then 139 # non-standard width 140 unset use_cat 141 decho " Skipping catpage: non-standard page width" 142 elif exists "$2" && is_newer $found $manpage; then 143 # cat page found and is newer, use that 144 use_cat=yes 145 catpage=$found 146 setup_cattool $catpage 147 decho " Using catpage $catpage" 148 else 149 # no cat page or is older 150 unset use_cat 151 decho " Skipping catpage: not found or old" 152 fi 153 return 0 154 fi 155 156 return 1 157} 158 159# Usage: decho "string" [debuglevel] 160# Echoes to stderr string prefaced with -- if high enough debuglevel. 161decho() { 162 if [ $debug -ge ${2:-1} ]; then 163 echo "-- $1" >&2 164 fi 165} 166 167# Usage: exists glob 168# Returns true if glob resolves to a real file. 169exists() { 170 local IFS 171 172 # Don't accidentally inherit callers IFS (breaks perl manpages) 173 unset IFS 174 175 # Use some globbing tricks in the shell to determine if a file 176 # exists or not. 177 set +f 178 set -- "$1" $1 179 set -f 180 181 if [ "$1" != "$2" -a -r "$2" ]; then 182 found="$2" 183 return 0 184 fi 185 186 return 1 187} 188 189# Usage: find_file path section subdir pagename 190# Returns: true if something is matched and found. 191# Search the given path/section combo for a given page. 192find_file() { 193 local manroot catroot mann man0 catn cat0 194 195 manroot="$1/man$2" 196 catroot="$1/cat$2" 197 if [ -n "$3" ]; then 198 manroot="$manroot/$3" 199 catroot="$catroot/$3" 200 fi 201 202 if [ ! -d "$manroot" ]; then 203 return 1 204 fi 205 decho " Searching directory $manroot" 2 206 207 mann="$manroot/$4.$2*" 208 man0="$manroot/$4.0*" 209 catn="$catroot/$4.$2*" 210 cat0="$catroot/$4.0*" 211 212 # This is the behavior as seen by the original man utility. 213 # Let's not change that which doesn't seem broken. 214 if check_man "$mann" "$catn"; then 215 return 0 216 elif check_man "$man0" "$cat0"; then 217 return 0 218 elif check_cat "$catn"; then 219 return 0 220 elif check_cat "$cat0"; then 221 return 0 222 fi 223 224 return 1 225} 226 227# Usage: is_newer file1 file2 228# Returns true if file1 is newer than file2 as calculated by mtime. 229is_newer() { 230 if ! [ "$1" -ot "$2" ]; then 231 decho " mtime: $1 not older than $2" 3 232 return 0 233 else 234 decho " mtime: $1 older than $2" 3 235 return 1 236 fi 237} 238 239# Usage: manpath_parse_args "$@" 240# Parses commandline options for manpath. 241manpath_parse_args() { 242 local cmd_arg 243 244 while getopts 'Ldq' cmd_arg; do 245 case "${cmd_arg}" in 246 L) Lflag=Lflag ;; 247 d) debug=$(( $debug + 1 )) ;; 248 q) qflag=qflag ;; 249 *) manpath_usage ;; 250 esac 251 done >&2 252} 253 254# Usage: manpath_usage 255# Display usage for the manpath(1) utility. 256manpath_usage() { 257 echo 'usage: manpath [-Ldq]' >&2 258 exit 1 259} 260 261# Usage: manpath_warnings 262# Display some warnings to stderr. 263manpath_warnings() { 264 if [ -n "$Lflag" -a -n "$MANLOCALES" ]; then 265 echo "(Warning: MANLOCALES environment variable set)" >&2 266 fi 267} 268 269# Usage: man_check_for_so page path 270# Returns: True if able to resolve the file, false if it ended in tears. 271# Detects the presence of the .so directive and causes the file to be 272# redirected to another source file. 273man_check_for_so() { 274 local IFS line tstr 275 276 unset IFS 277 278 # We need to loop to accommodate multiple .so directives. 279 while true 280 do 281 line=$($cattool $manpage | head -1) 282 case "$line" in 283 .so*) trim "${line#.so}" 284 decho "$manpage includes $tstr" 285 # Glob and check for the file. 286 if ! check_man "$path/$tstr*" ""; then 287 decho " Unable to find $tstr" 288 return 1 289 fi 290 ;; 291 *) break ;; 292 esac 293 done 294 295 return 0 296} 297 298# Usage: man_display_page 299# Display either the manpage or catpage depending on the use_cat variable 300man_display_page() { 301 local IFS pipeline testline 302 303 # We are called with IFS set to colon. This causes really weird 304 # things to happen for the variables that have spaces in them. 305 unset IFS 306 307 # If we are supposed to use a catpage and we aren't using troff(1) 308 # just zcat the catpage and we are done. 309 if [ -z "$tflag" -a -n "$use_cat" ]; then 310 if [ -n "$wflag" ]; then 311 echo "$catpage (source: $manpage)" 312 ret=0 313 else 314 if [ $debug -gt 0 ]; then 315 decho "Command: $cattool $catpage | $MANPAGER" 316 ret=0 317 else 318 eval "$cattool $catpage | $MANPAGER" 319 ret=$? 320 fi 321 fi 322 return 323 fi 324 325 # Okay, we are using the manpage, do we just need to output the 326 # name of the manpage? 327 if [ -n "$wflag" ]; then 328 echo "$manpage" 329 ret=0 330 return 331 fi 332 333 if [ -n "$use_width" ]; then 334 mandoc_args="-O width=${use_width}" 335 fi 336 testline="mandoc -Tlint -Wunsupp 2>/dev/null" 337 if [ -n "$tflag" ]; then 338 pipeline="mandoc -Tps $mandoc_args" 339 else 340 pipeline="mandoc $mandoc_args | $MANPAGER" 341 fi 342 343 if ! eval "$cattool $manpage | $testline" ;then 344 if which -s groff; then 345 man_display_page_groff 346 else 347 echo "This manpage needs groff(1) to be rendered" >&2 348 echo "First install groff(1): " >&2 349 echo "pkg install groff " >&2 350 ret=1 351 fi 352 return 353 fi 354 355 if [ $debug -gt 0 ]; then 356 decho "Command: $cattool $manpage | $pipeline" 357 ret=0 358 else 359 eval "$cattool $manpage | $pipeline" 360 ret=$? 361 fi 362} 363 364# Usage: man_display_page_groff 365# Display the manpage using groff 366man_display_page_groff() { 367 local EQN NROFF PIC TBL TROFF REFER VGRIND 368 local IFS l nroff_dev pipeline preproc_arg tool 369 370 # So, we really do need to parse the manpage. First, figure out the 371 # device flag (-T) we have to pass to eqn(1) and groff(1). Then, 372 # setup the pipeline of commands based on the user's request. 373 374 # If the manpage is from a particular charset, we need to setup nroff 375 # to properly output for the correct device. 376 case "${manpage}" in 377 *.${man_charset}/*) 378 # I don't pretend to know this; I'm just copying from the 379 # previous version of man(1). 380 case "$man_charset" in 381 KOI8-R) nroff_dev="koi8-r" ;; 382 ISO8859-1) nroff_dev="latin1" ;; 383 ISO8859-15) nroff_dev="latin1" ;; 384 UTF-8) nroff_dev="utf8" ;; 385 *) nroff_dev="ascii" ;; 386 esac 387 388 NROFF="$NROFF -T$nroff_dev" 389 EQN="$EQN -T$nroff_dev" 390 391 # Iff the manpage is from the locale and not just the charset, 392 # then we need to define the locale string. 393 case "${manpage}" in 394 */${man_lang}_${man_country}.${man_charset}/*) 395 NROFF="$NROFF -dlocale=$man_lang.$man_charset" 396 ;; 397 */${man_lang}.${man_charset}/*) 398 NROFF="$NROFF -dlocale=$man_lang.$man_charset" 399 ;; 400 esac 401 402 # Allow language specific calls to override the default 403 # set of utilities. 404 l=$(echo $man_lang | tr [:lower:] [:upper:]) 405 for tool in EQN NROFF PIC TBL TROFF REFER VGRIND; do 406 eval "$tool=\${${tool}_$l:-\$$tool}" 407 done 408 ;; 409 *) NROFF="$NROFF -Tascii" 410 EQN="$EQN -Tascii" 411 ;; 412 esac 413 414 if [ -z "$MANCOLOR" ]; then 415 NROFF="$NROFF -P-c" 416 fi 417 418 if [ -n "${use_width}" ]; then 419 NROFF="$NROFF -rLL=${use_width}n -rLT=${use_width}n" 420 fi 421 422 if [ -n "$MANROFFSEQ" ]; then 423 set -- -$MANROFFSEQ 424 while getopts 'egprtv' preproc_arg; do 425 case "${preproc_arg}" in 426 e) pipeline="$pipeline | $EQN" ;; 427 g) ;; # Ignore for compatibility. 428 p) pipeline="$pipeline | $PIC" ;; 429 r) pipeline="$pipeline | $REFER" ;; 430 t) pipeline="$pipeline | $TBL" ;; 431 v) pipeline="$pipeline | $VGRIND" ;; 432 *) usage ;; 433 esac 434 done 435 # Strip the leading " | " from the resulting pipeline. 436 pipeline="${pipeline#" | "}" 437 else 438 pipeline="$TBL" 439 fi 440 441 if [ -n "$tflag" ]; then 442 pipeline="$pipeline | $TROFF" 443 else 444 pipeline="$pipeline | $NROFF | $MANPAGER" 445 fi 446 447 if [ $debug -gt 0 ]; then 448 decho "Command: $cattool $manpage | $pipeline" 449 ret=0 450 else 451 eval "$cattool $manpage | $pipeline" 452 ret=$? 453 fi 454} 455 456# Usage: man_find_and_display page 457# Search through the manpaths looking for the given page. 458man_find_and_display() { 459 local found_page locpath p path sect 460 461 # Check to see if it's a file. But only if it has a '/' in 462 # the filename. 463 case "$1" in 464 */*) if [ -f "$1" -a -r "$1" ]; then 465 decho "Found a usable page, displaying that" 466 unset use_cat 467 manpage="$1" 468 setup_cattool $manpage 469 if man_check_for_so $manpage $(dirname $manpage); then 470 found_page=yes 471 man_display_page 472 fi 473 return 474 fi 475 ;; 476 esac 477 478 IFS=: 479 for sect in $MANSECT; do 480 decho "Searching section $sect" 2 481 for path in $MANPATH; do 482 for locpath in $locpaths; do 483 p=$path/$locpath 484 p=${p%/.} # Rid ourselves of the trailing /. 485 486 # Check if there is a MACHINE specific manpath. 487 if find_file $p $sect $MACHINE "$1"; then 488 if man_check_for_so $manpage $p; then 489 found_page=yes 490 man_display_page 491 if [ -n "$aflag" ]; then 492 continue 2 493 else 494 return 495 fi 496 fi 497 fi 498 499 # Check if there is a MACHINE_ARCH 500 # specific manpath. 501 if find_file $p $sect $MACHINE_ARCH "$1"; then 502 if man_check_for_so $manpage $p; then 503 found_page=yes 504 man_display_page 505 if [ -n "$aflag" ]; then 506 continue 2 507 else 508 return 509 fi 510 fi 511 fi 512 513 # Check plain old manpath. 514 if find_file $p $sect '' "$1"; then 515 if man_check_for_so $manpage $p; then 516 found_page=yes 517 man_display_page 518 if [ -n "$aflag" ]; then 519 continue 2 520 else 521 return 522 fi 523 fi 524 fi 525 done 526 done 527 done 528 unset IFS 529 530 # Nothing? Well, we are done then. 531 if [ -z "$found_page" ]; then 532 echo "No manual entry for $1" >&2 533 ret=1 534 return 535 fi 536} 537 538# Usage: man_parse_args "$@" 539# Parses commandline options for man. 540man_parse_args() { 541 local IFS cmd_arg 542 543 while getopts 'M:P:S:adfhkm:op:tw' cmd_arg; do 544 case "${cmd_arg}" in 545 M) MANPATH=$OPTARG ;; 546 P) MANPAGER=$OPTARG ;; 547 S) MANSECT=$OPTARG ;; 548 a) aflag=aflag ;; 549 d) debug=$(( $debug + 1 )) ;; 550 f) fflag=fflag ;; 551 h) man_usage 0 ;; 552 k) kflag=kflag ;; 553 m) mflag=$OPTARG ;; 554 o) oflag=oflag ;; 555 p) MANROFFSEQ=$OPTARG ;; 556 t) tflag=tflag ;; 557 w) wflag=wflag ;; 558 *) man_usage ;; 559 esac 560 done >&2 561 562 shift $(( $OPTIND - 1 )) 563 564 # Check the args for incompatible options. 565 case "${fflag}${kflag}${tflag}${wflag}" in 566 fflagkflag*) echo "Incompatible options: -f and -k"; man_usage ;; 567 fflag*tflag*) echo "Incompatible options: -f and -t"; man_usage ;; 568 fflag*wflag) echo "Incompatible options: -f and -w"; man_usage ;; 569 *kflagtflag*) echo "Incompatible options: -k and -t"; man_usage ;; 570 *kflag*wflag) echo "Incompatible options: -k and -w"; man_usage ;; 571 *tflagwflag) echo "Incompatible options: -t and -w"; man_usage ;; 572 esac 573 574 # Short circuit for whatis(1) and apropos(1) 575 if [ -n "$fflag" ]; then 576 do_whatis "$@" 577 exit 578 fi 579 580 if [ -n "$kflag" ]; then 581 do_apropos "$@" 582 exit 583 fi 584 585 IFS=: 586 for sect in $man_default_sections; do 587 if [ "$sect" = "$1" ]; then 588 decho "Detected manual section as first arg: $1" 589 MANSECT="$1" 590 shift 591 break 592 fi 593 done 594 unset IFS 595 596 pages="$*" 597} 598 599# Usage: man_setup 600# Setup various trivial but essential variables. 601man_setup() { 602 # Setup machine and architecture variables. 603 if [ -n "$mflag" ]; then 604 MACHINE_ARCH=${mflag%%:*} 605 MACHINE=${mflag##*:} 606 fi 607 if [ -z "$MACHINE_ARCH" ]; then 608 MACHINE_ARCH=$($SYSCTL -n hw.machine_arch) 609 fi 610 if [ -z "$MACHINE" ]; then 611 MACHINE=$($SYSCTL -n hw.machine) 612 fi 613 decho "Using architecture: $MACHINE_ARCH:$MACHINE" 614 615 setup_pager 616 617 # Setup manual sections to search. 618 if [ -z "$MANSECT" ]; then 619 MANSECT=$man_default_sections 620 fi 621 decho "Using manual sections: $MANSECT" 622 623 build_manpath 624 man_setup_locale 625 man_setup_width 626} 627 628# Usage: man_setup_width 629# Set up page width. 630man_setup_width() { 631 local sizes 632 633 unset use_width 634 case "$MANWIDTH" in 635 [0-9]*) 636 if [ "$MANWIDTH" -gt 0 2>/dev/null ]; then 637 use_width=$MANWIDTH 638 fi 639 ;; 640 [Tt][Tt][Yy]) 641 if { sizes=$($STTY size 0>&3 2>/dev/null); } 3>&1; then 642 set -- $sizes 643 if [ $2 -gt 80 ]; then 644 use_width=$(($2-2)) 645 fi 646 fi 647 ;; 648 esac 649 if [ -n "$use_width" ]; then 650 decho "Using non-standard page width: ${use_width}" 651 else 652 decho 'Using standard page width' 653 fi 654} 655 656# Usage: man_setup_locale 657# Setup necessary locale variables. 658man_setup_locale() { 659 local lang_cc 660 661 locpaths='.' 662 man_charset='US-ASCII' 663 664 # Setup locale information. 665 if [ -n "$oflag" ]; then 666 decho 'Using non-localized manpages' 667 else 668 # Use the locale tool to give us the proper LC_CTYPE 669 eval $( $LOCALE ) 670 671 case "$LC_CTYPE" in 672 C) ;; 673 POSIX) ;; 674 [a-z][a-z]_[A-Z][A-Z]\.*) 675 lang_cc="${LC_CTYPE%.*}" 676 man_lang="${LC_CTYPE%_*}" 677 man_country="${lang_cc#*_}" 678 man_charset="${LC_CTYPE#*.}" 679 locpaths="$LC_CTYPE" 680 locpaths="$locpaths:$man_lang.$man_charset" 681 if [ "$man_lang" != "en" ]; then 682 locpaths="$locpaths:en.$man_charset" 683 fi 684 locpaths="$locpaths:." 685 ;; 686 *) echo 'Unknown locale, assuming C' >&2 687 ;; 688 esac 689 fi 690 691 decho "Using locale paths: $locpaths" 692} 693 694# Usage: man_usage [exitcode] 695# Display usage for the man utility. 696man_usage() { 697 echo 'Usage:' 698 echo ' man [-adho] [-t | -w] [-M manpath] [-P pager] [-S mansect]' 699 echo ' [-m arch[:machine]] [-p [eprtv]] [mansect] page [...]' 700 echo ' man -f page [...] -- Emulates whatis(1)' 701 echo ' man -k page [...] -- Emulates apropos(1)' 702 703 # When exit'ing with -h, it's not an error. 704 exit ${1:-1} 705} 706 707# Usage: parse_configs 708# Reads the end-user adjustable config files. 709parse_configs() { 710 local IFS file files 711 712 if [ -n "$parsed_configs" ]; then 713 return 714 fi 715 716 unset IFS 717 718 # Read the global config first in case the user wants 719 # to override config_local. 720 if [ -r "$config_global" ]; then 721 parse_file "$config_global" 722 fi 723 724 # Glob the list of files to parse. 725 set +f 726 files=$(echo $config_local) 727 set -f 728 729 for file in $files; do 730 if [ -r "$file" ]; then 731 parse_file "$file" 732 fi 733 done 734 735 parsed_configs='yes' 736} 737 738# Usage: parse_file file 739# Reads the specified config files. 740parse_file() { 741 local file line tstr var 742 743 file="$1" 744 decho "Parsing config file: $file" 745 while read line; do 746 decho " $line" 2 747 case "$line" in 748 \#*) decho " Comment" 3 749 ;; 750 MANPATH*) decho " MANPATH" 3 751 trim "${line#MANPATH}" 752 add_to_manpath "$tstr" 753 ;; 754 MANLOCALE*) decho " MANLOCALE" 3 755 trim "${line#MANLOCALE}" 756 manlocales="$manlocales:$tstr" 757 ;; 758 MANCONFIG*) decho " MANCONFIG" 3 759 trim "${line#MANCONFIG}" 760 config_local="$tstr" 761 ;; 762 # Set variables in the form of FOO_BAR 763 *_*[\ \ ]*) var="${line%%[\ \ ]*}" 764 trim "${line#$var}" 765 eval "$var=\"$tstr\"" 766 decho " Parsed $var" 3 767 ;; 768 esac 769 done < "$file" 770} 771 772# Usage: search_path 773# Traverse $PATH looking for manpaths. 774search_path() { 775 local IFS p path 776 777 decho "Searching PATH for man directories" 778 779 IFS=: 780 for path in $PATH; do 781 if add_to_manpath "$path/man"; then 782 : 783 elif add_to_manpath "$path/MAN"; then 784 : 785 else 786 case "$path" in 787 */bin) p="${path%/bin}/share/man" 788 add_to_manpath "$p" 789 p="${path%/bin}/man" 790 add_to_manpath "$p" 791 ;; 792 esac 793 fi 794 done 795 unset IFS 796 797 if [ -z "$manpath" ]; then 798 decho ' Unable to find any manpaths, using default' 799 manpath=$man_default_path 800 fi 801} 802 803# Usage: search_whatis cmd [arglist] 804# Do the heavy lifting for apropos/whatis 805search_whatis() { 806 local IFS bad cmd f good key keywords loc opt out path rval wlist 807 808 cmd="$1" 809 shift 810 811 whatis_parse_args "$@" 812 813 build_manpath 814 build_manlocales 815 setup_pager 816 817 if [ "$cmd" = "whatis" ]; then 818 opt="-w" 819 fi 820 821 f='whatis' 822 823 IFS=: 824 for path in $MANPATH; do 825 if [ \! -d "$path" ]; then 826 decho "Skipping non-existent path: $path" 2 827 continue 828 fi 829 830 if [ -f "$path/$f" -a -r "$path/$f" ]; then 831 decho "Found whatis: $path/$f" 832 wlist="$wlist $path/$f" 833 fi 834 835 for loc in $MANLOCALES; do 836 if [ -f "$path/$loc/$f" -a -r "$path/$loc/$f" ]; then 837 decho "Found whatis: $path/$loc/$f" 838 wlist="$wlist $path/$loc/$f" 839 fi 840 done 841 done 842 unset IFS 843 844 if [ -z "$wlist" ]; then 845 echo "$cmd: no whatis databases in $MANPATH" >&2 846 exit 1 847 fi 848 849 rval=0 850 for key in $keywords; do 851 out=$(grep -Ehi $opt -- "$key" $wlist) 852 if [ -n "$out" ]; then 853 good="$good\\n$out" 854 else 855 bad="$bad\\n$key: nothing appropriate" 856 rval=1 857 fi 858 done 859 860 # Strip leading carriage return. 861 good=${good#\\n} 862 bad=${bad#\\n} 863 864 if [ -n "$good" ]; then 865 echo -e "$good" | $MANPAGER 866 fi 867 868 if [ -n "$bad" ]; then 869 echo -e "$bad" >&2 870 fi 871 872 exit $rval 873} 874 875# Usage: setup_cattool page 876# Finds an appropriate decompressor based on extension 877setup_cattool() { 878 case "$1" in 879 *.bz) cattool='/usr/bin/bzcat' ;; 880 *.bz2) cattool='/usr/bin/bzcat' ;; 881 *.gz) cattool='/usr/bin/zcat' ;; 882 *.lzma) cattool='/usr/bin/lzcat' ;; 883 *.xz) cattool='/usr/bin/xzcat' ;; 884 *) cattool='/usr/bin/zcat -f' ;; 885 esac 886} 887 888# Usage: setup_pager 889# Correctly sets $MANPAGER 890setup_pager() { 891 # Setup pager. 892 if [ -z "$MANPAGER" ]; then 893 if [ -n "$MANCOLOR" ]; then 894 MANPAGER="less -sR" 895 else 896 if [ -n "$PAGER" ]; then 897 MANPAGER="$PAGER" 898 else 899 MANPAGER="more -s" 900 fi 901 fi 902 fi 903 decho "Using pager: $MANPAGER" 904} 905 906# Usage: trim string 907# Trims whitespace from beginning and end of a variable 908trim() { 909 tstr=$1 910 while true; do 911 case "$tstr" in 912 [\ \ ]*) tstr="${tstr##[\ \ ]}" ;; 913 *[\ \ ]) tstr="${tstr%%[\ \ ]}" ;; 914 *) break ;; 915 esac 916 done 917} 918 919# Usage: whatis_parse_args "$@" 920# Parse commandline args for whatis and apropos. 921whatis_parse_args() { 922 local cmd_arg 923 while getopts 'd' cmd_arg; do 924 case "${cmd_arg}" in 925 d) debug=$(( $debug + 1 )) ;; 926 *) whatis_usage ;; 927 esac 928 done >&2 929 930 shift $(( $OPTIND - 1 )) 931 932 keywords="$*" 933} 934 935# Usage: whatis_usage 936# Display usage for the whatis/apropos utility. 937whatis_usage() { 938 echo "usage: $cmd [-d] keyword [...]" 939 exit 1 940} 941 942 943 944# Supported commands 945do_apropos() { 946 [ $(stat -f %i /usr/bin/man) -ne $(stat -f %i /usr/bin/apropos) ] && \ 947 exec apropos "$@" 948 search_whatis apropos "$@" 949} 950 951do_man() { 952 man_parse_args "$@" 953 if [ -z "$pages" ]; then 954 echo 'What manual page do you want?' >&2 955 exit 1 956 fi 957 man_setup 958 959 for page in $pages; do 960 decho "Searching for $page" 961 man_find_and_display "$page" 962 done 963 964 exit ${ret:-0} 965} 966 967do_manpath() { 968 manpath_parse_args "$@" 969 if [ -z "$qflag" ]; then 970 manpath_warnings 971 fi 972 if [ -n "$Lflag" ]; then 973 build_manlocales 974 echo $MANLOCALES 975 else 976 build_manpath 977 echo $MANPATH 978 fi 979 exit 0 980} 981 982do_whatis() { 983 [ $(stat -f %i /usr/bin/man) -ne $(stat -f %i /usr/bin/whatis) ] && \ 984 exec whatis "$@" 985 search_whatis whatis "$@" 986} 987 988# User's PATH setting decides on the groff-suite to pick up. 989EQN=eqn 990NROFF='groff -S -P-h -Wall -mtty-char -man' 991PIC=pic 992REFER=refer 993TBL=tbl 994TROFF='groff -S -man' 995VGRIND=vgrind 996 997LOCALE=/usr/bin/locale 998STTY=/bin/stty 999SYSCTL=/sbin/sysctl 1000 1001debug=0 1002man_default_sections='1:8:2:3:n:4:5:6:7:9:l' 1003man_default_path='/usr/share/man:/usr/share/openssl/man:/usr/local/share/man:/usr/local/man' 1004cattool='/usr/bin/zcat -f' 1005 1006config_global='/etc/man.conf' 1007 1008# This can be overridden via a setting in /etc/man.conf. 1009config_local='/usr/local/etc/man.d/*.conf' 1010 1011# Set noglobbing for now. I don't want spurious globbing. 1012set -f 1013 1014case "$0" in 1015*apropos) do_apropos "$@" ;; 1016*manpath) do_manpath "$@" ;; 1017*whatis) do_whatis "$@" ;; 1018*) do_man "$@" ;; 1019esac 1020