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