man.sh revision 228992
129881Swollman#! /bin/sh 229881Swollman# 329881Swollman# Copyright (c) 2010 Gordon Tetlow 429881Swollman# All rights reserved. 529881Swollman# 629881Swollman# Redistribution and use in source and binary forms, with or without 729881Swollman# modification, are permitted provided that the following conditions 829881Swollman# are met: 929881Swollman# 1. Redistributions of source code must retain the above copyright 1029881Swollman# notice, this list of conditions and the following disclaimer. 1129881Swollman# 2. Redistributions in binary form must reproduce the above copyright 1229881Swollman# notice, this list of conditions and the following disclaimer in the 1329881Swollman# documentation and/or other materials provided with the distribution. 1429881Swollman# 1529881Swollman# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1629881Swollman# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1729881Swollman# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1829881Swollman# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1929881Swollman# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2029881Swollman# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2129881Swollman# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2229881Swollman# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2329881Swollman# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2429881Swollman# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2529881Swollman# SUCH DAMAGE. 2629881Swollman# 2729881Swollman# $FreeBSD: head/usr.bin/man/man.sh 228992 2011-12-30 11:02:40Z uqs $ 2829881Swollman 2929881Swollman# Usage: add_to_manpath path 3029881Swollman# Adds a variable to manpath while ensuring we don't have duplicates. 3129881Swollman# Returns true if we were able to add something. False otherwise. 3229881Swollmanadd_to_manpath() { 3329881Swollman case "$manpath" in 3429881Swollman *:$1) decho " Skipping duplicate manpath entry $1" 2 ;; 3529881Swollman $1:*) decho " Skipping duplicate manpath entry $1" 2 ;; 3629881Swollman *:$1:*) decho " Skipping duplicate manpath entry $1" 2 ;; 3729881Swollman *) if [ -d "$1" ]; then 3829881Swollman decho " Adding $1 to manpath" 3929881Swollman manpath="$manpath:$1" 4029881Swollman return 0 4129881Swollman fi 4229881Swollman ;; 4329881Swollman esac 4429881Swollman 4529881Swollman return 1 4629881Swollman} 4729881Swollman 4829881Swollman# Usage: build_manlocales 4929881Swollman# Builds a correct MANLOCALES variable. 5029881Swollmanbuild_manlocales() { 5129881Swollman # If the user has set manlocales, who are we to argue. 5229881Swollman if [ -n "$MANLOCALES" ]; then 5329881Swollman return 5429881Swollman fi 5529881Swollman 5629881Swollman parse_configs 5729881Swollman 5829881Swollman # Trim leading colon 5929881Swollman MANLOCALES=${manlocales#:} 6029881Swollman 6129881Swollman decho "Available manual locales: $MANLOCALES" 6229881Swollman} 6329881Swollman 6429881Swollman# Usage: build_manpath 6529881Swollman# Builds a correct MANPATH variable. 6629881Swollmanbuild_manpath() { 6729881Swollman local IFS 6829881Swollman 6929881Swollman # If the user has set a manpath, who are we to argue. 7029881Swollman if [ -n "$MANPATH" ]; then 7129881Swollman return 7229881Swollman fi 7329881Swollman 7429881Swollman search_path 7529881Swollman 7629881Swollman decho "Adding default manpath entries" 7729881Swollman IFS=: 7829881Swollman for path in $man_default_path; do 7929881Swollman add_to_manpath "$path" 8029881Swollman done 8129881Swollman unset IFS 8229881Swollman 8329881Swollman parse_configs 8429881Swollman 8529881Swollman # Trim leading colon 8629881Swollman MANPATH=${manpath#:} 8729881Swollman 8829881Swollman decho "Using manual path: $MANPATH" 8929881Swollman} 9029881Swollman 9129881Swollman# Usage: check_cat catglob 9229881Swollman# Checks to see if a cat glob is available. 9329881Swollmancheck_cat() { 9429881Swollman if exists "$1"; then 9529881Swollman use_cat=yes 9629881Swollman catpage=$found 9729881Swollman setup_cattool $catpage 9829881Swollman decho " Found catpage $catpage" 9929881Swollman return 0 10029881Swollman else 10129881Swollman return 1 10229881Swollman fi 10329881Swollman} 10429881Swollman 10529881Swollman# Usage: check_man manglob catglob 10629881Swollman# Given 2 globs, figures out if the manglob is available, if so, check to 10729881Swollman# see if the catglob is also available and up to date. 10829881Swollmancheck_man() { 10929881Swollman if exists "$1"; then 11029881Swollman # We have a match, check for a cat page 11129881Swollman manpage=$found 11229881Swollman setup_cattool $manpage 11329881Swollman decho " Found manpage $manpage" 11429881Swollman 11529881Swollman if [ -n "${use_width}" ]; then 11629881Swollman # non-standard width 11729881Swollman unset use_cat 11829881Swollman decho " Skipping catpage: non-standard page width" 11929881Swollman elif exists "$2" && is_newer $found $manpage; then 12029881Swollman # cat page found and is newer, use that 12129881Swollman use_cat=yes 12229881Swollman catpage=$found 12329881Swollman setup_cattool $catpage 12429881Swollman decho " Using catpage $catpage" 12529881Swollman else 12629881Swollman # no cat page or is older 12729881Swollman unset use_cat 12829881Swollman decho " Skipping catpage: not found or old" 12929881Swollman fi 13029881Swollman return 0 13129881Swollman fi 13229881Swollman 13329881Swollman return 1 13429881Swollman} 13529881Swollman 13629881Swollman# Usage: decho "string" [debuglevel] 13729881Swollman# Echoes to stderr string prefaced with -- if high enough debuglevel. 13829881Swollmandecho() { 13929881Swollman if [ $debug -ge ${2:-1} ]; then 14029881Swollman echo "-- $1" >&2 14129881Swollman fi 14229881Swollman} 14329881Swollman 14429881Swollman# Usage: exists glob 14529881Swollman# Returns true if glob resolves to a real file. 14629881Swollmanexists() { 14729881Swollman local IFS 14829881Swollman 14929881Swollman # Don't accidentally inherit callers IFS (breaks perl manpages) 15029881Swollman unset IFS 15129881Swollman 15229881Swollman # Use some globbing tricks in the shell to determine if a file 15329881Swollman # exists or not. 15429881Swollman set +f 15529881Swollman set -- "$1" $1 15629881Swollman set -f 15729881Swollman 15829881Swollman if [ "$1" != "$2" -a -r "$2" ]; then 15929881Swollman found="$2" 16029881Swollman return 0 16129881Swollman fi 16229881Swollman 16329881Swollman return 1 16429881Swollman} 16529881Swollman 16629881Swollman# Usage: find_file path section subdir pagename 16729881Swollman# Returns: true if something is matched and found. 16829881Swollman# Search the given path/section combo for a given page. 16929881Swollmanfind_file() { 17029881Swollman local manroot catroot mann man0 catn cat0 17129881Swollman 17229881Swollman manroot="$1/man$2" 17329881Swollman catroot="$1/cat$2" 17429881Swollman if [ -n "$3" ]; then 17529881Swollman manroot="$manroot/$3" 17629881Swollman catroot="$catroot/$3" 17729881Swollman fi 17829881Swollman 17929881Swollman if [ ! -d "$manroot" ]; then 18029881Swollman return 1 18129881Swollman fi 18229881Swollman decho " Searching directory $manroot" 2 18329881Swollman 18429881Swollman mann="$manroot/$4.$2*" 18529881Swollman man0="$manroot/$4.0*" 18629881Swollman catn="$catroot/$4.$2*" 18729881Swollman cat0="$catroot/$4.0*" 18829881Swollman 18929881Swollman # This is the behavior as seen by the original man utility. 19029881Swollman # Let's not change that which doesn't seem broken. 19129881Swollman if check_man "$mann" "$catn"; then 19229881Swollman return 0 19329881Swollman elif check_man "$man0" "$cat0"; then 19429881Swollman return 0 19529881Swollman elif check_cat "$catn"; then 19629881Swollman return 0 19729881Swollman elif check_cat "$cat0"; then 19829881Swollman return 0 19929881Swollman fi 20029881Swollman 20129881Swollman return 1 20229881Swollman} 20329881Swollman 20429881Swollman# Usage: is_newer file1 file2 20529881Swollman# Returns true if file1 is newer than file2 as calculated by mtime. 20629881Swollmanis_newer() { 20729881Swollman if ! [ "$1" -ot "$2" ]; then 20829881Swollman decho " mtime: $1 not older than $2" 3 20929881Swollman return 0 21029881Swollman else 21129881Swollman decho " mtime: $1 older than $2" 3 21229881Swollman return 1 21329881Swollman fi 21429881Swollman} 21529881Swollman 21629881Swollman# Usage: manpath_parse_args "$@" 21729881Swollman# Parses commandline options for manpath. 21829881Swollmanmanpath_parse_args() { 21929881Swollman local cmd_arg 22029881Swollman 22129881Swollman while getopts 'Ldq' cmd_arg; do 22229881Swollman case "${cmd_arg}" in 22329881Swollman L) Lflag=Lflag ;; 22429881Swollman d) debug=$(( $debug + 1 )) ;; 22529881Swollman q) qflag=qflag ;; 22629881Swollman *) manpath_usage ;; 22729881Swollman esac 22829881Swollman done >&2 22929881Swollman} 23029881Swollman 23129881Swollman# Usage: manpath_usage 23229881Swollman# Display usage for the manpath(1) utility. 23329881Swollmanmanpath_usage() { 23429881Swollman echo 'usage: manpath [-Ldq]' >&2 23529881Swollman exit 1 23629881Swollman} 23729881Swollman 23829881Swollman# Usage: manpath_warnings 23929881Swollman# Display some warnings to stderr. 24029881Swollmanmanpath_warnings() { 24129881Swollman if [ -z "$Lflag" -a -n "$MANPATH" ]; then 24229881Swollman echo "(Warning: MANPATH environment variable set)" >&2 24329881Swollman fi 24429881Swollman 24529881Swollman if [ -n "$Lflag" -a -n "$MANLOCALES" ]; then 24629881Swollman echo "(Warning: MANLOCALES environment variable set)" >&2 24729881Swollman fi 24829881Swollman} 24929881Swollman 25029881Swollman# Usage: man_check_for_so page path 25129881Swollman# Returns: True if able to resolve the file, false if it ended in tears. 25229881Swollman# Detects the presence of the .so directive and causes the file to be 25329881Swollman# redirected to another source file. 25429881Swollmanman_check_for_so() { 25529881Swollman local IFS line tstr 25629881Swollman 25729881Swollman unset IFS 25829881Swollman 25929881Swollman # We need to loop to accommodate multiple .so directives. 26029881Swollman while true 26129881Swollman do 26229881Swollman line=$($cattool $manpage | head -1) 26329881Swollman case "$line" in 26429881Swollman .so*) trim "${line#.so}" 26529881Swollman decho "$manpage includes $tstr" 26629881Swollman # Glob and check for the file. 26729881Swollman if ! check_man "$path/$tstr*" ""; then 26829881Swollman decho " Unable to find $tstr" 26929881Swollman return 1 27029881Swollman fi 27129881Swollman ;; 27229881Swollman *) break ;; 27329881Swollman esac 27429881Swollman done 27529881Swollman 27629881Swollman return 0 27729881Swollman} 27829881Swollman 27929881Swollman# Usage: man_display_page 28029881Swollman# Display either the manpage or catpage depending on the use_cat variable 28129881Swollmanman_display_page() { 28229881Swollman local EQN NROFF PIC TBL TROFF REFER VGRIND 28329881Swollman local IFS l nroff_dev pipeline preproc_arg tool 28429881Swollman 28529881Swollman # We are called with IFS set to colon. This causes really weird 28629881Swollman # things to happen for the variables that have spaces in them. 28729881Swollman unset IFS 28829881Swollman 28929881Swollman # If we are supposed to use a catpage and we aren't using troff(1) 29029881Swollman # just zcat the catpage and we are done. 29129881Swollman if [ -z "$tflag" -a -n "$use_cat" ]; then 29229881Swollman if [ -n "$wflag" ]; then 29329881Swollman echo "$catpage (source: $manpage)" 29429881Swollman ret=0 29529881Swollman else 29629881Swollman if [ $debug -gt 0 ]; then 29729881Swollman decho "Command: $cattool $catpage | $MANPAGER" 29829881Swollman ret=0 29929881Swollman else 30029881Swollman eval "$cattool $catpage | $MANPAGER" 30129881Swollman ret=$? 30229881Swollman fi 30329881Swollman fi 30429881Swollman return 30529881Swollman fi 30629881Swollman 30729881Swollman # Okay, we are using the manpage, do we just need to output the 30829881Swollman # name of the manpage? 30929881Swollman if [ -n "$wflag" ]; then 31029881Swollman echo "$manpage" 31129881Swollman ret=0 31229881Swollman return 31329881Swollman fi 31429881Swollman 31529881Swollman # So, we really do need to parse the manpage. First, figure out the 31629881Swollman # device flag (-T) we have to pass to eqn(1) and groff(1). Then, 31729881Swollman # setup the pipeline of commands based on the user's request. 31829881Swollman 31929881Swollman # If the manpage is from a particular charset, we need to setup nroff 32029881Swollman # to properly output for the correct device. 32129881Swollman case "${manpage}" in 32229881Swollman *.${man_charset}/*) 32329881Swollman # I don't pretend to know this; I'm just copying from the 324 # previous version of man(1). 325 case "$man_charset" in 326 KOI8-R) nroff_dev="koi8-r" ;; 327 ISO8859-1) nroff_dev="latin1" ;; 328 ISO8859-15) nroff_dev="latin1" ;; 329 UTF-8) nroff_dev="utf8" ;; 330 *) nroff_dev="ascii" ;; 331 esac 332 333 NROFF="$NROFF -T$nroff_dev" 334 EQN="$EQN -T$nroff_dev" 335 336 # Iff the manpage is from the locale and not just the charset, 337 # then we need to define the locale string. 338 case "${manpage}" in 339 */${man_lang}_${man_country}.${man_charset}/*) 340 NROFF="$NROFF -dlocale=$man_lang.$man_charset" 341 ;; 342 */${man_lang}.${man_charset}/*) 343 NROFF="$NROFF -dlocale=$man_lang.$man_charset" 344 ;; 345 esac 346 347 # Allow language specific calls to override the default 348 # set of utilities. 349 l=$(echo $man_lang | tr [:lower:] [:upper:]) 350 for tool in EQN NROFF PIC TBL TROFF REFER VGRIND; do 351 eval "$tool=\${${tool}_$l:-\$$tool}" 352 done 353 ;; 354 *) NROFF="$NROFF -Tascii" 355 EQN="$EQN -Tascii" 356 ;; 357 esac 358 359 if [ -z "$MANCOLOR" ]; then 360 NROFF="$NROFF -P-c" 361 fi 362 363 if [ -n "${use_width}" ]; then 364 NROFF="$NROFF -rLL=${use_width}n -rLT=${use_width}n" 365 fi 366 367 if [ -n "$MANROFFSEQ" ]; then 368 set -- -$MANROFFSEQ 369 while getopts 'egprtv' preproc_arg; do 370 case "${preproc_arg}" in 371 e) pipeline="$pipeline | $EQN" ;; 372 g) ;; # Ignore for compatibility. 373 p) pipeline="$pipeline | $PIC" ;; 374 r) pipeline="$pipeline | $REFER" ;; 375 t) pipeline="$pipeline | $TBL" ;; 376 v) pipeline="$pipeline | $VGRIND" ;; 377 *) usage ;; 378 esac 379 done 380 # Strip the leading " | " from the resulting pipeline. 381 pipeline="${pipeline#" | "}" 382 else 383 pipeline="$TBL" 384 fi 385 386 if [ -n "$tflag" ]; then 387 pipeline="$pipeline | $TROFF" 388 else 389 pipeline="$pipeline | $NROFF | $MANPAGER" 390 fi 391 392 if [ $debug -gt 0 ]; then 393 decho "Command: $cattool $manpage | $pipeline" 394 ret=0 395 else 396 eval "$cattool $manpage | $pipeline" 397 ret=$? 398 fi 399} 400 401# Usage: man_find_and_display page 402# Search through the manpaths looking for the given page. 403man_find_and_display() { 404 local found_page locpath p path sect 405 406 # Check to see if it's a file. But only if it has a '/' in 407 # the filename. 408 case "$1" in 409 */*) if [ -f "$1" -a -r "$1" ]; then 410 decho "Found a usable page, displaying that" 411 unset use_cat 412 manpage="$1" 413 setup_cattool $manpage 414 if man_check_for_so $manpage $(dirname $manpage); then 415 found_page=yes 416 man_display_page 417 fi 418 return 419 fi 420 ;; 421 esac 422 423 IFS=: 424 for sect in $MANSECT; do 425 decho "Searching section $sect" 2 426 for path in $MANPATH; do 427 for locpath in $locpaths; do 428 p=$path/$locpath 429 p=${p%/.} # Rid ourselves of the trailing /. 430 431 # Check if there is a MACHINE specific manpath. 432 if find_file $p $sect $MACHINE "$1"; then 433 if man_check_for_so $manpage $p; then 434 found_page=yes 435 man_display_page 436 if [ -n "$aflag" ]; then 437 continue 2 438 else 439 return 440 fi 441 fi 442 fi 443 444 # Check if there is a MACHINE_ARCH 445 # specific manpath. 446 if find_file $p $sect $MACHINE_ARCH "$1"; then 447 if man_check_for_so $manpage $p; then 448 found_page=yes 449 man_display_page 450 if [ -n "$aflag" ]; then 451 continue 2 452 else 453 return 454 fi 455 fi 456 fi 457 458 # Check plain old manpath. 459 if find_file $p $sect '' "$1"; then 460 if man_check_for_so $manpage $p; then 461 found_page=yes 462 man_display_page 463 if [ -n "$aflag" ]; then 464 continue 2 465 else 466 return 467 fi 468 fi 469 fi 470 done 471 done 472 done 473 unset IFS 474 475 # Nothing? Well, we are done then. 476 if [ -z "$found_page" ]; then 477 echo "No manual entry for $1" >&2 478 ret=1 479 return 480 fi 481} 482 483# Usage: man_parse_args "$@" 484# Parses commandline options for man. 485man_parse_args() { 486 local IFS cmd_arg 487 488 while getopts 'M:P:S:adfhkm:op:tw' cmd_arg; do 489 case "${cmd_arg}" in 490 M) MANPATH=$OPTARG ;; 491 P) MANPAGER=$OPTARG ;; 492 S) MANSECT=$OPTARG ;; 493 a) aflag=aflag ;; 494 d) debug=$(( $debug + 1 )) ;; 495 f) fflag=fflag ;; 496 h) man_usage 0 ;; 497 k) kflag=kflag ;; 498 m) mflag=$OPTARG ;; 499 o) oflag=oflag ;; 500 p) MANROFFSEQ=$OPTARG ;; 501 t) tflag=tflag ;; 502 w) wflag=wflag ;; 503 *) man_usage ;; 504 esac 505 done >&2 506 507 shift $(( $OPTIND - 1 )) 508 509 # Check the args for incompatible options. 510 case "${fflag}${kflag}${tflag}${wflag}" in 511 fflagkflag*) echo "Incompatible options: -f and -k"; man_usage ;; 512 fflag*tflag*) echo "Incompatible options: -f and -t"; man_usage ;; 513 fflag*wflag) echo "Incompatible options: -f and -w"; man_usage ;; 514 *kflagtflag*) echo "Incompatible options: -k and -t"; man_usage ;; 515 *kflag*wflag) echo "Incompatible options: -k and -w"; man_usage ;; 516 *tflagwflag) echo "Incompatible options: -t and -w"; man_usage ;; 517 esac 518 519 # Short circuit for whatis(1) and apropos(1) 520 if [ -n "$fflag" ]; then 521 do_whatis "$@" 522 exit 523 fi 524 525 if [ -n "$kflag" ]; then 526 do_apropos "$@" 527 exit 528 fi 529 530 IFS=: 531 for sect in $man_default_sections; do 532 if [ "$sect" = "$1" ]; then 533 decho "Detected manual section as first arg: $1" 534 MANSECT="$1" 535 shift 536 break 537 fi 538 done 539 unset IFS 540 541 pages="$*" 542} 543 544# Usage: man_setup 545# Setup various trivial but essential variables. 546man_setup() { 547 # Setup machine and architecture variables. 548 if [ -n "$mflag" ]; then 549 MACHINE_ARCH=${mflag%%:*} 550 MACHINE=${mflag##*:} 551 fi 552 if [ -z "$MACHINE_ARCH" ]; then 553 MACHINE_ARCH=$($SYSCTL -n hw.machine_arch) 554 fi 555 if [ -z "$MACHINE" ]; then 556 MACHINE=$($SYSCTL -n hw.machine) 557 fi 558 decho "Using architecture: $MACHINE_ARCH:$MACHINE" 559 560 setup_pager 561 562 # Setup manual sections to search. 563 if [ -z "$MANSECT" ]; then 564 MANSECT=$man_default_sections 565 fi 566 decho "Using manual sections: $MANSECT" 567 568 build_manpath 569 man_setup_locale 570 man_setup_width 571} 572 573# Usage: man_setup_width 574# Set up page width. 575man_setup_width() { 576 local sizes 577 578 unset use_width 579 case "$MANWIDTH" in 580 [0-9]*) 581 if [ "$MANWIDTH" -gt 0 2>/dev/null ]; then 582 use_width=$MANWIDTH 583 fi 584 ;; 585 [Tt][Tt][Yy]) 586 if { sizes=$($STTY size 0>&3 2>/dev/null); } 3>&1; then 587 set -- $sizes 588 if [ $2 -gt 80 ]; then 589 use_width=$(($2-2)) 590 fi 591 fi 592 ;; 593 esac 594 if [ -n "$use_width" ]; then 595 decho "Using non-standard page width: ${use_width}" 596 else 597 decho 'Using standard page width' 598 fi 599} 600 601# Usage: man_setup_locale 602# Setup necessary locale variables. 603man_setup_locale() { 604 local lang_cc 605 606 locpaths='.' 607 man_charset='US-ASCII' 608 609 # Setup locale information. 610 if [ -n "$oflag" ]; then 611 decho 'Using non-localized manpages' 612 else 613 # Use the locale tool to give us the proper LC_CTYPE 614 eval $( $LOCALE ) 615 616 case "$LC_CTYPE" in 617 C) ;; 618 POSIX) ;; 619 [a-z][a-z]_[A-Z][A-Z]\.*) 620 lang_cc="${LC_CTYPE%.*}" 621 man_lang="${LC_CTYPE%_*}" 622 man_country="${lang_cc#*_}" 623 man_charset="${LC_CTYPE#*.}" 624 locpaths="$LC_CTYPE" 625 locpaths="$locpaths:$man_lang.$man_charset" 626 if [ "$man_lang" != "en" ]; then 627 locpaths="$locpaths:en.$man_charset" 628 fi 629 locpaths="$locpaths:." 630 ;; 631 *) echo 'Unknown locale, assuming C' >&2 632 ;; 633 esac 634 fi 635 636 decho "Using locale paths: $locpaths" 637} 638 639# Usage: man_usage [exitcode] 640# Display usage for the man utility. 641man_usage() { 642 echo 'Usage:' 643 echo ' man [-adho] [-t | -w] [-M manpath] [-P pager] [-S mansect]' 644 echo ' [-m arch[:machine]] [-p [eprtv]] [mansect] page [...]' 645 echo ' man -f page [...] -- Emulates whatis(1)' 646 echo ' man -k page [...] -- Emulates apropos(1)' 647 648 # When exit'ing with -h, it's not an error. 649 exit ${1:-1} 650} 651 652# Usage: parse_configs 653# Reads the end-user adjustable config files. 654parse_configs() { 655 local IFS file files 656 657 if [ -n "$parsed_configs" ]; then 658 return 659 fi 660 661 unset IFS 662 663 # Read the global config first in case the user wants 664 # to override config_local. 665 if [ -r "$config_global" ]; then 666 parse_file "$config_global" 667 fi 668 669 # Glob the list of files to parse. 670 set +f 671 files=$(echo $config_local) 672 set -f 673 674 for file in $files; do 675 if [ -r "$file" ]; then 676 parse_file "$file" 677 fi 678 done 679 680 parsed_configs='yes' 681} 682 683# Usage: parse_file file 684# Reads the specified config files. 685parse_file() { 686 local file line tstr var 687 688 file="$1" 689 decho "Parsing config file: $file" 690 while read line; do 691 decho " $line" 2 692 case "$line" in 693 \#*) decho " Comment" 3 694 ;; 695 MANPATH*) decho " MANPATH" 3 696 trim "${line#MANPATH}" 697 add_to_manpath "$tstr" 698 ;; 699 MANLOCALE*) decho " MANLOCALE" 3 700 trim "${line#MANLOCALE}" 701 manlocales="$manlocales:$tstr" 702 ;; 703 MANCONFIG*) decho " MANCONFIG" 3 704 trim "${line#MANCONFIG}" 705 config_local="$tstr" 706 ;; 707 # Set variables in the form of FOO_BAR 708 *_*[\ \ ]*) var="${line%%[\ \ ]*}" 709 trim "${line#$var}" 710 eval "$var=\"$tstr\"" 711 decho " Parsed $var" 3 712 ;; 713 esac 714 done < "$file" 715} 716 717# Usage: search_path 718# Traverse $PATH looking for manpaths. 719search_path() { 720 local IFS p path 721 722 decho "Searching PATH for man directories" 723 724 IFS=: 725 for path in $PATH; do 726 # Do a little special casing since the base manpages 727 # are in /usr/share/man instead of /usr/man or /man. 728 case "$path" in 729 /bin|/usr/bin) add_to_manpath "/usr/share/man" ;; 730 *) if add_to_manpath "$path/man"; then 731 : 732 elif add_to_manpath "$path/MAN"; then 733 : 734 else 735 case "$path" in 736 */bin) p="${path%/bin}/man" 737 add_to_manpath "$p" 738 ;; 739 *) ;; 740 esac 741 fi 742 ;; 743 esac 744 done 745 unset IFS 746 747 if [ -z "$manpath" ]; then 748 decho ' Unable to find any manpaths, using default' 749 manpath=$man_default_path 750 fi 751} 752 753# Usage: search_whatis cmd [arglist] 754# Do the heavy lifting for apropos/whatis 755search_whatis() { 756 local IFS bad cmd f good key keywords loc opt out path rval wlist 757 758 cmd="$1" 759 shift 760 761 whatis_parse_args "$@" 762 763 build_manpath 764 build_manlocales 765 setup_pager 766 767 if [ "$cmd" = "whatis" ]; then 768 opt="-w" 769 fi 770 771 f='whatis' 772 773 IFS=: 774 for path in $MANPATH; do 775 if [ \! -d "$path" ]; then 776 decho "Skipping non-existent path: $path" 2 777 continue 778 fi 779 780 if [ -f "$path/$f" -a -r "$path/$f" ]; then 781 decho "Found whatis: $path/$f" 782 wlist="$wlist $path/$f" 783 fi 784 785 for loc in $MANLOCALES; do 786 if [ -f "$path/$loc/$f" -a -r "$path/$loc/$f" ]; then 787 decho "Found whatis: $path/$loc/$f" 788 wlist="$wlist $path/$loc/$f" 789 fi 790 done 791 done 792 unset IFS 793 794 if [ -z "$wlist" ]; then 795 echo "$cmd: no whatis databases in $MANPATH" >&2 796 exit 1 797 fi 798 799 rval=0 800 for key in $keywords; do 801 out=$(grep -Ehi $opt -- "$key" $wlist) 802 if [ -n "$out" ]; then 803 good="$good\\n$out" 804 else 805 bad="$bad\\n$key: nothing appropriate" 806 rval=1 807 fi 808 done 809 810 # Strip leading carriage return. 811 good=${good#\\n} 812 bad=${bad#\\n} 813 814 if [ -n "$good" ]; then 815 echo -e "$good" | $MANPAGER 816 fi 817 818 if [ -n "$bad" ]; then 819 echo -e "$bad" >&2 820 fi 821 822 exit $rval 823} 824 825# Usage: setup_cattool page 826# Finds an appropriate decompressor based on extension 827setup_cattool() { 828 case "$1" in 829 *.bz) cattool='/usr/bin/bzcat' ;; 830 *.bz2) cattool='/usr/bin/bzcat' ;; 831 *.gz) cattool='/usr/bin/zcat' ;; 832 *.lzma) cattool='/usr/bin/lzcat' ;; 833 *.xz) cattool='/usr/bin/xzcat' ;; 834 *) cattool='/usr/bin/zcat -f' ;; 835 esac 836} 837 838# Usage: setup_pager 839# Correctly sets $MANPAGER 840setup_pager() { 841 # Setup pager. 842 if [ -z "$MANPAGER" ]; then 843 if [ -n "$MANCOLOR" ]; then 844 MANPAGER="less -sR" 845 else 846 if [ -n "$PAGER" ]; then 847 MANPAGER="$PAGER" 848 else 849 MANPAGER="more -s" 850 fi 851 fi 852 fi 853 decho "Using pager: $MANPAGER" 854} 855 856# Usage: trim string 857# Trims whitespace from beginning and end of a variable 858trim() { 859 tstr=$1 860 while true; do 861 case "$tstr" in 862 [\ \ ]*) tstr="${tstr##[\ \ ]}" ;; 863 *[\ \ ]) tstr="${tstr%%[\ \ ]}" ;; 864 *) break ;; 865 esac 866 done 867} 868 869# Usage: whatis_parse_args "$@" 870# Parse commandline args for whatis and apropos. 871whatis_parse_args() { 872 local cmd_arg 873 while getopts 'd' cmd_arg; do 874 case "${cmd_arg}" in 875 d) debug=$(( $debug + 1 )) ;; 876 *) whatis_usage ;; 877 esac 878 done >&2 879 880 shift $(( $OPTIND - 1 )) 881 882 keywords="$*" 883} 884 885# Usage: whatis_usage 886# Display usage for the whatis/apropos utility. 887whatis_usage() { 888 echo "usage: $cmd [-d] keyword [...]" 889 exit 1 890} 891 892 893 894# Supported commands 895do_apropos() { 896 search_whatis apropos "$@" 897} 898 899do_man() { 900 man_parse_args "$@" 901 if [ -z "$pages" ]; then 902 echo 'What manual page do you want?' >&2 903 exit 1 904 fi 905 man_setup 906 907 for page in $pages; do 908 decho "Searching for $page" 909 man_find_and_display "$page" 910 done 911 912 exit ${ret:-0} 913} 914 915do_manpath() { 916 manpath_parse_args "$@" 917 if [ -z "$qflag" ]; then 918 manpath_warnings 919 fi 920 if [ -n "$Lflag" ]; then 921 build_manlocales 922 echo $MANLOCALES 923 else 924 build_manpath 925 echo $MANPATH 926 fi 927 exit 0 928} 929 930do_whatis() { 931 search_whatis whatis "$@" 932} 933 934# User's PATH setting decides on the groff-suite to pick up. 935EQN=eqn 936NROFF='groff -S -P-h -Wall -mtty-char -man' 937PIC=pic 938REFER=refer 939TBL=tbl 940TROFF='groff -S -man' 941VGRIND=vgrind 942 943LOCALE=/usr/bin/locale 944STTY=/bin/stty 945SYSCTL=/sbin/sysctl 946 947debug=0 948man_default_sections='1:1aout:8:2:3:n:4:5:6:7:9:l' 949man_default_path='/usr/share/man:/usr/share/openssl/man:/usr/local/man' 950cattool='/usr/bin/zcat -f' 951 952config_global='/etc/man.conf' 953 954# This can be overridden via a setting in /etc/man.conf. 955config_local='/usr/local/etc/man.d/*.conf' 956 957# Set noglobbing for now. I don't want spurious globbing. 958set -f 959 960case "$0" in 961*apropos) do_apropos "$@" ;; 962*manpath) do_manpath "$@" ;; 963*whatis) do_whatis "$@" ;; 964*) do_man "$@" ;; 965esac 966