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