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