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