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