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