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