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