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" -a ! -d "$catroot" ]; 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 OPTIND=1 247 while getopts 'Ldq' cmd_arg; do 248 case "${cmd_arg}" in 249 L) Lflag=Lflag ;; 250 d) debug=$(( $debug + 1 )) ;; 251 q) qflag=qflag ;; 252 *) manpath_usage ;; 253 esac 254 done >&2 255} 256 257# Usage: manpath_usage 258# Display usage for the manpath(1) utility. 259manpath_usage() { 260 echo 'usage: manpath [-Ldq]' >&2 261 exit 1 262} 263 264# Usage: manpath_warnings 265# Display some warnings to stderr. 266manpath_warnings() { 267 if [ -n "$Lflag" -a -n "$MANLOCALES" ]; then 268 echo "(Warning: MANLOCALES environment variable set)" >&2 269 fi 270} 271 272# Usage: man_check_for_so page path 273# Returns: True if able to resolve the file, false if it ended in tears. 274# Detects the presence of the .so directive and causes the file to be 275# redirected to another source file. 276man_check_for_so() { 277 local IFS line tstr 278 279 unset IFS 280 if [ -n "$catpage" ]; then 281 return 0 282 fi 283 284 # We need to loop to accommodate multiple .so directives. 285 while true 286 do 287 line=$($cattool $manpage | head -1) 288 case "$line" in 289 .so*) trim "${line#.so}" 290 decho "$manpage includes $tstr" 291 # Glob and check for the file. 292 if ! check_man "$path/$tstr*" ""; then 293 decho " Unable to find $tstr" 294 return 1 295 fi 296 ;; 297 *) break ;; 298 esac 299 done 300 301 return 0 302} 303 304# Usage: man_display_page 305# Display either the manpage or catpage depending on the use_cat variable 306man_display_page() { 307 local IFS pipeline testline 308 309 # We are called with IFS set to colon. This causes really weird 310 # things to happen for the variables that have spaces in them. 311 unset IFS 312 313 # If we are supposed to use a catpage and we aren't using troff(1) 314 # just zcat the catpage and we are done. 315 if [ -z "$tflag" -a -n "$use_cat" ]; then 316 if [ -n "$wflag" ]; then 317 echo "$catpage (source: $manpage)" 318 ret=0 319 else 320 if [ $debug -gt 0 ]; then 321 decho "Command: $cattool $catpage | $MANPAGER" 322 ret=0 323 else 324 eval "$cattool $catpage | $MANPAGER" 325 ret=$? 326 fi 327 fi 328 return 329 fi 330 331 # Okay, we are using the manpage, do we just need to output the 332 # name of the manpage? 333 if [ -n "$wflag" ]; then 334 echo "$manpage" 335 ret=0 336 return 337 fi 338 339 if [ -n "$use_width" ]; then 340 mandoc_args="-O width=${use_width}" 341 fi 342 testline="mandoc -Tlint -Wunsupp >/dev/null 2>&1" 343 if [ -n "$tflag" ]; then 344 pipeline="mandoc -Tps $mandoc_args" 345 else 346 pipeline="mandoc $mandoc_args | $MANPAGER" 347 fi 348 349 if ! eval "$cattool $manpage | $testline" ;then 350 if which -s groff; then 351 man_display_page_groff 352 else 353 echo "This manpage needs groff(1) to be rendered" >&2 354 echo "First install groff(1): " >&2 355 echo "pkg install groff " >&2 356 ret=1 357 fi 358 return 359 fi 360 361 if [ $debug -gt 0 ]; then 362 decho "Command: $cattool $manpage | $pipeline" 363 ret=0 364 else 365 eval "$cattool $manpage | $pipeline" 366 ret=$? 367 fi 368} 369 370# Usage: man_display_page_groff 371# Display the manpage using groff 372man_display_page_groff() { 373 local EQN NROFF PIC TBL TROFF REFER VGRIND 374 local IFS l nroff_dev pipeline preproc_arg tool 375 376 # So, we really do need to parse the manpage. First, figure out the 377 # device flag (-T) we have to pass to eqn(1) and groff(1). Then, 378 # setup the pipeline of commands based on the user's request. 379 380 # If the manpage is from a particular charset, we need to setup nroff 381 # to properly output for the correct device. 382 case "${manpage}" in 383 *.${man_charset}/*) 384 # I don't pretend to know this; I'm just copying from the 385 # previous version of man(1). 386 case "$man_charset" in 387 KOI8-R) nroff_dev="koi8-r" ;; 388 ISO8859-1) nroff_dev="latin1" ;; 389 ISO8859-15) nroff_dev="latin1" ;; 390 UTF-8) nroff_dev="utf8" ;; 391 *) nroff_dev="ascii" ;; 392 esac 393 394 NROFF="$NROFF -T$nroff_dev" 395 EQN="$EQN -T$nroff_dev" 396 397 # Iff the manpage is from the locale and not just the charset, 398 # then we need to define the locale string. 399 case "${manpage}" in 400 */${man_lang}_${man_country}.${man_charset}/*) 401 NROFF="$NROFF -dlocale=$man_lang.$man_charset" 402 ;; 403 */${man_lang}.${man_charset}/*) 404 NROFF="$NROFF -dlocale=$man_lang.$man_charset" 405 ;; 406 esac 407 408 # Allow language specific calls to override the default 409 # set of utilities. 410 l=$(echo $man_lang | tr [:lower:] [:upper:]) 411 for tool in EQN NROFF PIC TBL TROFF REFER VGRIND; do 412 eval "$tool=\${${tool}_$l:-\$$tool}" 413 done 414 ;; 415 *) NROFF="$NROFF -Tascii" 416 EQN="$EQN -Tascii" 417 ;; 418 esac 419 420 if [ -z "$MANCOLOR" ]; then 421 NROFF="$NROFF -P-c" 422 fi 423 424 if [ -n "${use_width}" ]; then 425 NROFF="$NROFF -rLL=${use_width}n -rLT=${use_width}n" 426 fi 427 428 if [ -n "$MANROFFSEQ" ]; then 429 set -- -$MANROFFSEQ 430 OPTIND=1 431 while getopts 'egprtv' preproc_arg; do 432 case "${preproc_arg}" in 433 e) pipeline="$pipeline | $EQN" ;; 434 g) ;; # Ignore for compatibility. 435 p) pipeline="$pipeline | $PIC" ;; 436 r) pipeline="$pipeline | $REFER" ;; 437 t) pipeline="$pipeline | $TBL" ;; 438 v) pipeline="$pipeline | $VGRIND" ;; 439 *) usage ;; 440 esac 441 done 442 # Strip the leading " | " from the resulting pipeline. 443 pipeline="${pipeline#" | "}" 444 else 445 pipeline="$TBL" 446 fi 447 448 if [ -n "$tflag" ]; then 449 pipeline="$pipeline | $TROFF" 450 else 451 pipeline="$pipeline | $NROFF | $MANPAGER" 452 fi 453 454 if [ $debug -gt 0 ]; then 455 decho "Command: $cattool $manpage | $pipeline" 456 ret=0 457 else 458 eval "$cattool $manpage | $pipeline" 459 ret=$? 460 fi 461} 462 463# Usage: man_find_and_display page 464# Search through the manpaths looking for the given page. 465man_find_and_display() { 466 local found_page locpath p path sect 467 468 # Check to see if it's a file. But only if it has a '/' in 469 # the filename. 470 case "$1" in 471 */*) if [ -f "$1" -a -r "$1" ]; then 472 decho "Found a usable page, displaying that" 473 unset use_cat 474 manpage="$1" 475 setup_cattool $manpage 476 if man_check_for_so $manpage $(dirname $manpage); then 477 found_page=yes 478 man_display_page 479 fi 480 return 481 fi 482 ;; 483 esac 484 485 IFS=: 486 for sect in $MANSECT; do 487 decho "Searching section $sect" 2 488 for path in $MANPATH; do 489 for locpath in $locpaths; do 490 p=$path/$locpath 491 p=${p%/.} # Rid ourselves of the trailing /. 492 493 # Check if there is a MACHINE specific manpath. 494 if find_file $p $sect $MACHINE "$1"; then 495 if man_check_for_so $manpage $p; then 496 found_page=yes 497 man_display_page 498 if [ -n "$aflag" ]; then 499 continue 2 500 else 501 return 502 fi 503 fi 504 fi 505 506 # Check if there is a MACHINE_ARCH 507 # specific manpath. 508 if find_file $p $sect $MACHINE_ARCH "$1"; then 509 if man_check_for_so $manpage $p; then 510 found_page=yes 511 man_display_page 512 if [ -n "$aflag" ]; then 513 continue 2 514 else 515 return 516 fi 517 fi 518 fi 519 520 # Check plain old manpath. 521 if find_file $p $sect '' "$1"; then 522 if man_check_for_so $manpage $p; then 523 found_page=yes 524 man_display_page 525 if [ -n "$aflag" ]; then 526 continue 2 527 else 528 return 529 fi 530 fi 531 fi 532 done 533 done 534 done 535 unset IFS 536 537 # Nothing? Well, we are done then. 538 if [ -z "$found_page" ]; then 539 echo "No manual entry for $1" >&2 540 ret=1 541 return 542 fi 543} 544 545# Usage: man_parse_args "$@" 546# Parses commandline options for man. 547man_parse_args() { 548 local IFS cmd_arg 549 550 OPTIND=1 551 while getopts 'K:M:P:S:adfhkm:op:tw' cmd_arg; do 552 case "${cmd_arg}" in 553 K) Kflag=Kflag 554 REGEXP=$OPTARG ;; 555 M) MANPATH=$OPTARG ;; 556 P) MANPAGER=$OPTARG ;; 557 S) MANSECT=$OPTARG ;; 558 a) aflag=aflag ;; 559 d) debug=$(( $debug + 1 )) ;; 560 f) fflag=fflag ;; 561 h) man_usage 0 ;; 562 k) kflag=kflag ;; 563 m) mflag=$OPTARG ;; 564 o) oflag=oflag ;; 565 p) MANROFFSEQ=$OPTARG ;; 566 t) tflag=tflag ;; 567 w) wflag=wflag ;; 568 *) man_usage ;; 569 esac 570 done >&2 571 572 shift $(( $OPTIND - 1 )) 573 574 # Check the args for incompatible options. 575 576 case "${Kflag}${fflag}${kflag}${tflag}${wflag}" in 577 Kflagfflag*) echo "Incompatible options: -K and -f"; man_usage ;; 578 Kflag*kflag*) echo "Incompatible options: -K and -k"; man_usage ;; 579 Kflag*tflag) echo "Incompatible options: -K and -t"; man_usage ;; 580 fflagkflag*) echo "Incompatible options: -f and -k"; man_usage ;; 581 fflag*tflag*) echo "Incompatible options: -f and -t"; man_usage ;; 582 fflag*wflag) echo "Incompatible options: -f and -w"; man_usage ;; 583 *kflagtflag*) echo "Incompatible options: -k and -t"; man_usage ;; 584 *kflag*wflag) echo "Incompatible options: -k and -w"; man_usage ;; 585 *tflagwflag) echo "Incompatible options: -t and -w"; man_usage ;; 586 esac 587 588 # Short circuit for whatis(1) and apropos(1) 589 if [ -n "$fflag" ]; then 590 do_whatis "$@" 591 exit 592 fi 593 594 if [ -n "$kflag" ]; then 595 do_apropos "$@" 596 exit 597 fi 598 599 IFS=: 600 for sect in $man_default_sections; do 601 if [ "$sect" = "$1" ]; then 602 decho "Detected manual section as first arg: $1" 603 MANSECT="$1" 604 shift 605 break 606 fi 607 done 608 unset IFS 609 610 pages="$*" 611} 612 613# Usage: man_setup 614# Setup various trivial but essential variables. 615man_setup() { 616 # Setup machine and architecture variables. 617 if [ -n "$mflag" ]; then 618 MACHINE_ARCH=${mflag%%:*} 619 MACHINE=${mflag##*:} 620 fi 621 if [ -z "$MACHINE_ARCH" ]; then 622 MACHINE_ARCH=$($SYSCTL -n hw.machine_arch) 623 fi 624 if [ -z "$MACHINE" ]; then 625 MACHINE=$($SYSCTL -n hw.machine) 626 fi 627 decho "Using architecture: $MACHINE_ARCH:$MACHINE" 628 629 setup_pager 630 631 # Setup manual sections to search. 632 if [ -z "$MANSECT" ]; then 633 MANSECT=$man_default_sections 634 fi 635 decho "Using manual sections: $MANSECT" 636 637 build_manpath 638 man_setup_locale 639 man_setup_width 640} 641 642# Usage: man_setup_width 643# Set up page width. 644man_setup_width() { 645 local sizes 646 647 unset use_width 648 case "$MANWIDTH" in 649 [0-9]*) 650 if [ "$MANWIDTH" -gt 0 2>/dev/null ]; then 651 use_width=$MANWIDTH 652 fi 653 ;; 654 [Tt][Tt][Yy]) 655 if { sizes=$($STTY size 0>&3 2>/dev/null); } 3>&1; then 656 set -- $sizes 657 if [ $2 -gt 80 ]; then 658 use_width=$(($2-2)) 659 fi 660 fi 661 ;; 662 esac 663 if [ -n "$use_width" ]; then 664 decho "Using non-standard page width: ${use_width}" 665 else 666 decho 'Using standard page width' 667 fi 668} 669 670# Usage: man_setup_locale 671# Setup necessary locale variables. 672man_setup_locale() { 673 local lang_cc 674 local locstr 675 676 locpaths='.' 677 man_charset='US-ASCII' 678 679 # Setup locale information. 680 if [ -n "$oflag" ]; then 681 decho 'Using non-localized manpages' 682 else 683 # Use the locale tool to give us proper locale information 684 eval $( $LOCALE ) 685 686 if [ -n "$LANG" ]; then 687 locstr=$LANG 688 else 689 locstr=$LC_CTYPE 690 fi 691 692 case "$locstr" in 693 C) ;; 694 C.UTF-8) ;; 695 POSIX) ;; 696 [a-z][a-z]_[A-Z][A-Z]\.*) 697 lang_cc="${locstr%.*}" 698 man_lang="${locstr%_*}" 699 man_country="${lang_cc#*_}" 700 man_charset="${locstr#*.}" 701 locpaths="$locstr" 702 locpaths="$locpaths:$man_lang.$man_charset" 703 if [ "$man_lang" != "en" ]; then 704 locpaths="$locpaths:en.$man_charset" 705 fi 706 locpaths="$locpaths:." 707 ;; 708 *) echo 'Unknown locale, assuming C' >&2 709 ;; 710 esac 711 fi 712 713 decho "Using locale paths: $locpaths" 714} 715 716# Usage: man_usage [exitcode] 717# Display usage for the man utility. 718man_usage() { 719 echo 'Usage:' 720 echo ' man [-adho] [-t | -w] [-K regexp] [-M manpath] [-P pager] [-S mansect]' 721 echo ' [-m arch[:machine]] [-p [eprtv]] [mansect] page [...]' 722 echo ' man -f page [...] -- Emulates whatis(1)' 723 echo ' man -k page [...] -- Emulates apropos(1)' 724 725 # When exit'ing with -h, it's not an error. 726 exit ${1:-1} 727} 728 729# Usage: parse_configs 730# Reads the end-user adjustable config files. 731parse_configs() { 732 local IFS file files 733 734 if [ -n "$parsed_configs" ]; then 735 return 736 fi 737 738 unset IFS 739 740 # Read the global config first in case the user wants 741 # to override config_local. 742 if [ -r "$config_global" ]; then 743 parse_file "$config_global" 744 fi 745 746 # Glob the list of files to parse. 747 set +f 748 files=$(echo $config_local) 749 set -f 750 751 for file in $files; do 752 if [ -r "$file" ]; then 753 parse_file "$file" 754 fi 755 done 756 757 parsed_configs='yes' 758} 759 760# Usage: parse_file file 761# Reads the specified config files. 762parse_file() { 763 local file line tstr var 764 765 file="$1" 766 decho "Parsing config file: $file" 767 while read line; do 768 decho " $line" 2 769 case "$line" in 770 \#*) decho " Comment" 3 771 ;; 772 MANPATH*) decho " MANPATH" 3 773 trim "${line#MANPATH}" 774 add_to_manpath "$tstr" 775 ;; 776 MANLOCALE*) decho " MANLOCALE" 3 777 trim "${line#MANLOCALE}" 778 manlocales="$manlocales:$tstr" 779 ;; 780 MANCONFIG*) decho " MANCONFIG" 3 781 trim "${line#MANCONFIG}" 782 config_local="$tstr" 783 ;; 784 # Set variables in the form of FOO_BAR 785 *_*[\ \ ]*) var="${line%%[\ \ ]*}" 786 trim "${line#$var}" 787 eval "$var=\"$tstr\"" 788 decho " Parsed $var" 3 789 ;; 790 esac 791 done < "$file" 792} 793 794# Usage: search_path 795# Traverse $PATH looking for manpaths. 796search_path() { 797 local IFS p path 798 799 decho "Searching PATH for man directories" 800 801 IFS=: 802 for path in $PATH; do 803 if add_to_manpath "$path/man"; then 804 : 805 elif add_to_manpath "$path/MAN"; then 806 : 807 else 808 case "$path" in 809 */bin) p="${path%/bin}/share/man" 810 add_to_manpath "$p" 811 p="${path%/bin}/man" 812 add_to_manpath "$p" 813 ;; 814 esac 815 fi 816 done 817 unset IFS 818 819 if [ -z "$manpath" ]; then 820 decho ' Unable to find any manpaths, using default' 821 manpath=$man_default_path 822 fi 823} 824 825# Usage: search_whatis cmd [arglist] 826# Do the heavy lifting for apropos/whatis 827search_whatis() { 828 local IFS bad cmd f good key keywords loc opt out path rval wlist 829 830 cmd="$1" 831 shift 832 833 whatis_parse_args "$@" 834 835 build_manpath 836 build_manlocales 837 setup_pager 838 839 if [ "$cmd" = "whatis" ]; then 840 opt="-w" 841 fi 842 843 f='whatis' 844 845 IFS=: 846 for path in $MANPATH; do 847 if [ \! -d "$path" ]; then 848 decho "Skipping non-existent path: $path" 2 849 continue 850 fi 851 852 if [ -f "$path/$f" -a -r "$path/$f" ]; then 853 decho "Found whatis: $path/$f" 854 wlist="$wlist $path/$f" 855 fi 856 857 for loc in $MANLOCALES; do 858 if [ -f "$path/$loc/$f" -a -r "$path/$loc/$f" ]; then 859 decho "Found whatis: $path/$loc/$f" 860 wlist="$wlist $path/$loc/$f" 861 fi 862 done 863 done 864 unset IFS 865 866 if [ -z "$wlist" ]; then 867 echo "$cmd: no whatis databases in $MANPATH" >&2 868 exit 1 869 fi 870 871 rval=0 872 for key in $keywords; do 873 out=$(grep -Ehi $opt -- "$key" $wlist) 874 if [ -n "$out" ]; then 875 good="$good\\n$out" 876 else 877 bad="$bad\\n$key: nothing appropriate" 878 rval=1 879 fi 880 done 881 882 # Strip leading carriage return. 883 good=${good#\\n} 884 bad=${bad#\\n} 885 886 if [ -n "$good" ]; then 887 echo -e "$good" | $MANPAGER 888 fi 889 890 if [ -n "$bad" ]; then 891 echo -e "$bad" >&2 892 fi 893 894 exit $rval 895} 896 897# Usage: setup_cattool page 898# Finds an appropriate decompressor based on extension 899setup_cattool() { 900 case "$1" in 901 *.bz) cattool='/usr/bin/bzcat' ;; 902 *.bz2) cattool='/usr/bin/bzcat' ;; 903 *.gz) cattool='/usr/bin/zcat' ;; 904 *.lzma) cattool='/usr/bin/lzcat' ;; 905 *.xz) cattool='/usr/bin/xzcat' ;; 906 *) cattool='/usr/bin/zcat -f' ;; 907 esac 908} 909 910# Usage: setup_pager 911# Correctly sets $MANPAGER 912setup_pager() { 913 # Setup pager. 914 if [ -z "$MANPAGER" ]; then 915 if [ -n "$MANCOLOR" ]; then 916 MANPAGER="less -sR" 917 else 918 if [ -n "$PAGER" ]; then 919 MANPAGER="$PAGER" 920 else 921 MANPAGER="less -s" 922 fi 923 fi 924 fi 925 decho "Using pager: $MANPAGER" 926} 927 928# Usage: trim string 929# Trims whitespace from beginning and end of a variable 930trim() { 931 tstr=$1 932 while true; do 933 case "$tstr" in 934 [\ \ ]*) tstr="${tstr##[\ \ ]}" ;; 935 *[\ \ ]) tstr="${tstr%%[\ \ ]}" ;; 936 *) break ;; 937 esac 938 done 939} 940 941# Usage: whatis_parse_args "$@" 942# Parse commandline args for whatis and apropos. 943whatis_parse_args() { 944 local cmd_arg 945 OPTIND=1 946 while getopts 'd' cmd_arg; do 947 case "${cmd_arg}" in 948 d) debug=$(( $debug + 1 )) ;; 949 *) whatis_usage ;; 950 esac 951 done >&2 952 953 shift $(( $OPTIND - 1 )) 954 955 keywords="$*" 956} 957 958# Usage: whatis_usage 959# Display usage for the whatis/apropos utility. 960whatis_usage() { 961 echo "usage: $cmd [-d] keyword [...]" 962 exit 1 963} 964 965 966 967# Supported commands 968do_apropos() { 969 [ $(stat -f %i /usr/bin/man) -ne $(stat -f %i /usr/bin/apropos) ] && \ 970 exec apropos "$@" 971 search_whatis apropos "$@" 972} 973 974# Usage: do_full_search reg_exp 975# Do a full search of the regular expression passed 976# as parameter in all man pages 977do_full_search() { 978 local gflags re 979 re=${1} 980 981 # Build grep(1) flags 982 gflags="-H" 983 984 # wflag implies -l for grep(1) 985 if [ -n "$wflag" ]; then 986 gflags="${gflags} -l" 987 fi 988 989 gflags="${gflags} --label" 990 991 set +f 992 for mpath in $(echo "${MANPATH}" | tr : [:blank:]); do 993 for section in $(echo "${MANSECT}" | tr : [:blank:]); do 994 for manfile in ${mpath}/man${section}/*.${section}*; do 995 mandoc "${manfile}" 2>/dev/null | 996 grep -E ${gflags} "${manfile}" -e ${re} 997 done 998 done 999 done 1000 set -f 1001} 1002 1003do_man() { 1004 man_parse_args "$@" 1005 if [ -z "$pages" -a -z "${Kflag}" ]; then 1006 echo 'What manual page do you want?' >&2 1007 exit 1 1008 fi 1009 man_setup 1010 1011 if [ ! -z "${Kflag}" ]; then 1012 # Short circuit because -K flag does a sufficiently 1013 # different thing like not showing the man page at all 1014 do_full_search "${REGEXP}" 1015 fi 1016 1017 for page in $pages; do 1018 decho "Searching for $page" 1019 man_find_and_display "$page" 1020 done 1021 1022 exit ${ret:-0} 1023} 1024 1025do_manpath() { 1026 manpath_parse_args "$@" 1027 if [ -z "$qflag" ]; then 1028 manpath_warnings 1029 fi 1030 if [ -n "$Lflag" ]; then 1031 build_manlocales 1032 echo $MANLOCALES 1033 else 1034 build_manpath 1035 echo $MANPATH 1036 fi 1037 exit 0 1038} 1039 1040do_whatis() { 1041 [ $(stat -f %i /usr/bin/man) -ne $(stat -f %i /usr/bin/whatis) ] && \ 1042 exec whatis "$@" 1043 search_whatis whatis "$@" 1044} 1045 1046# User's PATH setting decides on the groff-suite to pick up. 1047EQN=eqn 1048NROFF='groff -S -P-h -Wall -mtty-char -man' 1049PIC=pic 1050REFER=refer 1051TBL=tbl 1052TROFF='groff -S -man' 1053VGRIND=vgrind 1054 1055LOCALE=/usr/bin/locale 1056STTY=/bin/stty 1057SYSCTL=/sbin/sysctl 1058 1059debug=0 1060man_default_sections='1:8:2:3:3lua:n:4:5:6:7:9:l' 1061man_default_path='/usr/share/man:/usr/share/openssl/man:/usr/local/share/man:/usr/local/man' 1062cattool='/usr/bin/zcat -f' 1063 1064config_global='/etc/man.conf' 1065 1066# This can be overridden via a setting in /etc/man.conf. 1067config_local='/usr/local/etc/man.d/*.conf' 1068 1069# Set noglobbing for now. I don't want spurious globbing. 1070set -f 1071 1072case "$0" in 1073*apropos) do_apropos "$@" ;; 1074*manpath) do_manpath "$@" ;; 1075*whatis) do_whatis "$@" ;; 1076*) do_man "$@" ;; 1077esac 1078