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