]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/man/man.sh
Switch the default pager for most commands to less
[FreeBSD/FreeBSD.git] / usr.bin / man / man.sh
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.
34 add_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.
52 build_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.
68 build_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.
118 check_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.
133 check_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.
163 decho() {
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.
171 exists() {
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.
194 find_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.
231 is_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.
243 manpath_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.
258 manpath_usage() {
259         echo 'usage: manpath [-Ldq]' >&2
260         exit 1
261 }
262
263 # Usage: manpath_warnings
264 # Display some warnings to stderr.
265 manpath_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.
275 man_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
305 man_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
371 man_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.
463 man_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.
545 man_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.
606 man_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.
635 man_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.
663 man_setup_locale() {
664         local lang_cc
665
666         locpaths='.'
667         man_charset='US-ASCII'
668
669         # Setup locale information.
670         if [ -n "$oflag" ]; then
671                 decho 'Using non-localized manpages'
672         else
673                 # Use the locale tool to give us the proper LC_CTYPE
674                 eval $( $LOCALE )
675
676                 case "$LC_CTYPE" in
677                 C)              ;;
678                 POSIX)          ;;
679                 [a-z][a-z]_[A-Z][A-Z]\.*)
680                                 lang_cc="${LC_CTYPE%.*}"
681                                 man_lang="${LC_CTYPE%_*}"
682                                 man_country="${lang_cc#*_}"
683                                 man_charset="${LC_CTYPE#*.}"
684                                 locpaths="$LC_CTYPE"
685                                 locpaths="$locpaths:$man_lang.$man_charset"
686                                 if [ "$man_lang" != "en" ]; then
687                                         locpaths="$locpaths:en.$man_charset"
688                                 fi
689                                 locpaths="$locpaths:."
690                                 ;;
691                 *)              echo 'Unknown locale, assuming C' >&2
692                                 ;;
693                 esac
694         fi
695
696         decho "Using locale paths: $locpaths"
697 }
698
699 # Usage: man_usage [exitcode]
700 # Display usage for the man utility.
701 man_usage() {
702         echo 'Usage:'
703         echo ' man [-adho] [-t | -w] [-M manpath] [-P pager] [-S mansect]'
704         echo '     [-m arch[:machine]] [-p [eprtv]] [mansect] page [...]'
705         echo ' man -f page [...] -- Emulates whatis(1)'
706         echo ' man -k page [...] -- Emulates apropos(1)'
707
708         # When exit'ing with -h, it's not an error.
709         exit ${1:-1}
710 }
711
712 # Usage: parse_configs
713 # Reads the end-user adjustable config files.
714 parse_configs() {
715         local IFS file files
716
717         if [ -n "$parsed_configs" ]; then
718                 return
719         fi
720
721         unset IFS
722
723         # Read the global config first in case the user wants
724         # to override config_local.
725         if [ -r "$config_global" ]; then
726                 parse_file "$config_global"
727         fi
728
729         # Glob the list of files to parse.
730         set +f
731         files=$(echo $config_local)
732         set -f
733
734         for file in $files; do
735                 if [ -r "$file" ]; then
736                         parse_file "$file"
737                 fi
738         done
739
740         parsed_configs='yes'
741 }
742
743 # Usage: parse_file file
744 # Reads the specified config files.
745 parse_file() {
746         local file line tstr var
747
748         file="$1"
749         decho "Parsing config file: $file"
750         while read line; do
751                 decho "  $line" 2
752                 case "$line" in
753                 \#*)            decho "    Comment" 3
754                                 ;;
755                 MANPATH*)       decho "    MANPATH" 3
756                                 trim "${line#MANPATH}"
757                                 add_to_manpath "$tstr"
758                                 ;;
759                 MANLOCALE*)     decho "    MANLOCALE" 3
760                                 trim "${line#MANLOCALE}"
761                                 manlocales="$manlocales:$tstr"
762                                 ;;
763                 MANCONFIG*)     decho "    MANCONFIG" 3
764                                 trim "${line#MANCONFIG}"
765                                 config_local="$tstr"
766                                 ;;
767                 # Set variables in the form of FOO_BAR
768                 *_*[\ \ ]*)     var="${line%%[\ \       ]*}"
769                                 trim "${line#$var}"
770                                 eval "$var=\"$tstr\""
771                                 decho "    Parsed $var" 3
772                                 ;;
773                 esac
774         done < "$file"
775 }
776
777 # Usage: search_path
778 # Traverse $PATH looking for manpaths.
779 search_path() {
780         local IFS p path
781
782         decho "Searching PATH for man directories"
783
784         IFS=:
785         for path in $PATH; do
786                 if add_to_manpath "$path/man"; then
787                         :
788                 elif add_to_manpath "$path/MAN"; then
789                         :
790                 else
791                         case "$path" in
792                         */bin)  p="${path%/bin}/share/man"
793                                 add_to_manpath "$p"
794                                 p="${path%/bin}/man"
795                                 add_to_manpath "$p"
796                                 ;;
797                         esac
798                 fi
799         done
800         unset IFS
801
802         if [ -z "$manpath" ]; then
803                 decho '  Unable to find any manpaths, using default'
804                 manpath=$man_default_path
805         fi
806 }
807
808 # Usage: search_whatis cmd [arglist]
809 # Do the heavy lifting for apropos/whatis
810 search_whatis() {
811         local IFS bad cmd f good key keywords loc opt out path rval wlist
812
813         cmd="$1"
814         shift
815
816         whatis_parse_args "$@"
817
818         build_manpath
819         build_manlocales
820         setup_pager
821
822         if [ "$cmd" = "whatis" ]; then
823                 opt="-w"
824         fi
825
826         f='whatis'
827
828         IFS=:
829         for path in $MANPATH; do
830                 if [ \! -d "$path" ]; then
831                         decho "Skipping non-existent path: $path" 2
832                         continue
833                 fi
834
835                 if [ -f "$path/$f" -a -r "$path/$f" ]; then
836                         decho "Found whatis: $path/$f"
837                         wlist="$wlist $path/$f"
838                 fi
839
840                 for loc in $MANLOCALES; do
841                         if [ -f "$path/$loc/$f" -a -r "$path/$loc/$f" ]; then
842                                 decho "Found whatis: $path/$loc/$f"
843                                 wlist="$wlist $path/$loc/$f"
844                         fi
845                 done
846         done
847         unset IFS
848
849         if [ -z "$wlist" ]; then
850                 echo "$cmd: no whatis databases in $MANPATH" >&2
851                 exit 1
852         fi
853
854         rval=0
855         for key in $keywords; do
856                 out=$(grep -Ehi $opt -- "$key" $wlist)
857                 if [ -n "$out" ]; then
858                         good="$good\\n$out"
859                 else
860                         bad="$bad\\n$key: nothing appropriate"
861                         rval=1
862                 fi
863         done
864
865         # Strip leading carriage return.
866         good=${good#\\n}
867         bad=${bad#\\n}
868
869         if [ -n "$good" ]; then
870                 echo -e "$good" | $MANPAGER
871         fi
872
873         if [ -n "$bad" ]; then
874                 echo -e "$bad" >&2
875         fi
876
877         exit $rval
878 }
879
880 # Usage: setup_cattool page
881 # Finds an appropriate decompressor based on extension
882 setup_cattool() {
883         case "$1" in
884         *.bz)   cattool='/usr/bin/bzcat' ;;
885         *.bz2)  cattool='/usr/bin/bzcat' ;;
886         *.gz)   cattool='/usr/bin/zcat' ;;
887         *.lzma) cattool='/usr/bin/lzcat' ;;
888         *.xz)   cattool='/usr/bin/xzcat' ;;
889         *)      cattool='/usr/bin/zcat -f' ;;
890         esac
891 }
892
893 # Usage: setup_pager
894 # Correctly sets $MANPAGER
895 setup_pager() {
896         # Setup pager.
897         if [ -z "$MANPAGER" ]; then
898                 if [ -n "$MANCOLOR" ]; then
899                         MANPAGER="less -sR"
900                 else
901                         if [ -n "$PAGER" ]; then
902                                 MANPAGER="$PAGER"
903                         else
904                                 MANPAGER="less -s"
905                         fi
906                 fi
907         fi
908         decho "Using pager: $MANPAGER"
909 }
910
911 # Usage: trim string
912 # Trims whitespace from beginning and end of a variable
913 trim() {
914         tstr=$1
915         while true; do
916                 case "$tstr" in
917                 [\ \    ]*)     tstr="${tstr##[\ \      ]}" ;;
918                 *[\ \   ])      tstr="${tstr%%[\ \      ]}" ;;
919                 *)              break ;;
920                 esac
921         done
922 }
923
924 # Usage: whatis_parse_args "$@"
925 # Parse commandline args for whatis and apropos.
926 whatis_parse_args() {
927         local cmd_arg
928         while getopts 'd' cmd_arg; do
929                 case "${cmd_arg}" in
930                 d)      debug=$(( $debug + 1 )) ;;
931                 *)      whatis_usage ;;
932                 esac
933         done >&2
934
935         shift $(( $OPTIND - 1 ))
936
937         keywords="$*"
938 }
939
940 # Usage: whatis_usage
941 # Display usage for the whatis/apropos utility.
942 whatis_usage() {
943         echo "usage: $cmd [-d] keyword [...]"
944         exit 1
945 }
946
947
948
949 # Supported commands
950 do_apropos() {
951         [ $(stat -f %i /usr/bin/man) -ne $(stat -f %i /usr/bin/apropos) ] && \
952                 exec apropos "$@"
953         search_whatis apropos "$@"
954 }
955
956 do_man() {
957         man_parse_args "$@"
958         if [ -z "$pages" ]; then
959                 echo 'What manual page do you want?' >&2
960                 exit 1
961         fi
962         man_setup
963
964         for page in $pages; do
965                 decho "Searching for $page"
966                 man_find_and_display "$page"
967         done
968
969         exit ${ret:-0}
970 }
971
972 do_manpath() {
973         manpath_parse_args "$@"
974         if [ -z "$qflag" ]; then
975                 manpath_warnings
976         fi
977         if [ -n "$Lflag" ]; then
978                 build_manlocales
979                 echo $MANLOCALES
980         else
981                 build_manpath
982                 echo $MANPATH
983         fi
984         exit 0
985 }
986
987 do_whatis() {
988         [ $(stat -f %i /usr/bin/man) -ne $(stat -f %i /usr/bin/whatis) ] && \
989                 exec whatis "$@"
990         search_whatis whatis "$@"
991 }
992
993 # User's PATH setting decides on the groff-suite to pick up.
994 EQN=eqn
995 NROFF='groff -S -P-h -Wall -mtty-char -man'
996 PIC=pic
997 REFER=refer
998 TBL=tbl
999 TROFF='groff -S -man'
1000 VGRIND=vgrind
1001
1002 LOCALE=/usr/bin/locale
1003 STTY=/bin/stty
1004 SYSCTL=/sbin/sysctl
1005
1006 debug=0
1007 man_default_sections='1:8:2:3:n:4:5:6:7:9:l'
1008 man_default_path='/usr/share/man:/usr/share/openssl/man:/usr/local/share/man:/usr/local/man'
1009 cattool='/usr/bin/zcat -f'
1010
1011 config_global='/etc/man.conf'
1012
1013 # This can be overridden via a setting in /etc/man.conf.
1014 config_local='/usr/local/etc/man.d/*.conf'
1015
1016 # Set noglobbing for now. I don't want spurious globbing.
1017 set -f
1018
1019 case "$0" in
1020 *apropos)       do_apropos "$@" ;;
1021 *manpath)       do_manpath "$@" ;;
1022 *whatis)        do_whatis "$@" ;;
1023 *)              do_man "$@" ;;
1024 esac