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