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