1213317Sgordon#! /bin/sh
2213317Sgordon#
3213317Sgordon#  Copyright (c) 2010 Gordon Tetlow
4213317Sgordon#  All rights reserved.
5213317Sgordon#
6213317Sgordon#  Redistribution and use in source and binary forms, with or without
7213317Sgordon#  modification, are permitted provided that the following conditions
8213317Sgordon#  are met:
9213317Sgordon#  1. Redistributions of source code must retain the above copyright
10213317Sgordon#     notice, this list of conditions and the following disclaimer.
11213317Sgordon#  2. Redistributions in binary form must reproduce the above copyright
12213317Sgordon#     notice, this list of conditions and the following disclaimer in the
13213317Sgordon#     documentation and/or other materials provided with the distribution.
14213317Sgordon#
15213317Sgordon#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16213317Sgordon#  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17213317Sgordon#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18213317Sgordon#  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19213317Sgordon#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20213317Sgordon#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21213317Sgordon#  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22213317Sgordon#  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23213317Sgordon#  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24213317Sgordon#  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25213317Sgordon#  SUCH DAMAGE.
26213317Sgordon#
27213317Sgordon# $FreeBSD: stable/10/usr.bin/man/man.sh 326688 2017-12-08 10:47:29Z bapt $
28213317Sgordon
29213317Sgordon# Usage: add_to_manpath path
30213317Sgordon# Adds a variable to manpath while ensuring we don't have duplicates.
31213317Sgordon# Returns true if we were able to add something. False otherwise.
32213317Sgordonadd_to_manpath() {
33213317Sgordon	case "$manpath" in
34213317Sgordon	*:$1)	decho "  Skipping duplicate manpath entry $1" 2 ;;
35213317Sgordon	$1:*)	decho "  Skipping duplicate manpath entry $1" 2 ;;
36213317Sgordon	*:$1:*)	decho "  Skipping duplicate manpath entry $1" 2 ;;
37213317Sgordon	*)	if [ -d "$1" ]; then
38213317Sgordon			decho "  Adding $1 to manpath"
39213317Sgordon			manpath="$manpath:$1"
40213317Sgordon			return 0
41213317Sgordon		fi
42213317Sgordon		;;
43213317Sgordon	esac
44213317Sgordon
45213317Sgordon	return 1
46213317Sgordon}
47213317Sgordon
48213317Sgordon# Usage: build_manlocales
49213317Sgordon# Builds a correct MANLOCALES variable.
50213317Sgordonbuild_manlocales() {
51213317Sgordon	# If the user has set manlocales, who are we to argue.
52213317Sgordon	if [ -n "$MANLOCALES" ]; then
53213317Sgordon		return
54213317Sgordon	fi
55213317Sgordon
56213317Sgordon	parse_configs
57213317Sgordon
58213317Sgordon	# Trim leading colon
59213317Sgordon	MANLOCALES=${manlocales#:}
60213317Sgordon
61213317Sgordon	decho "Available manual locales: $MANLOCALES"
62213317Sgordon}
63213317Sgordon
64213317Sgordon# Usage: build_manpath
65213317Sgordon# Builds a correct MANPATH variable.
66213317Sgordonbuild_manpath() {
67213317Sgordon	local IFS
68213317Sgordon
69213317Sgordon	# If the user has set a manpath, who are we to argue.
70213317Sgordon	if [ -n "$MANPATH" ]; then
71213317Sgordon		return
72213317Sgordon	fi
73213317Sgordon
74213317Sgordon	search_path
75213317Sgordon
76213317Sgordon	decho "Adding default manpath entries"
77213317Sgordon	IFS=:
78213317Sgordon	for path in $man_default_path; do
79213317Sgordon		add_to_manpath "$path"
80213317Sgordon	done
81213317Sgordon	unset IFS
82213317Sgordon
83213317Sgordon	parse_configs
84213317Sgordon
85213317Sgordon	# Trim leading colon
86213317Sgordon	MANPATH=${manpath#:}
87213317Sgordon
88213317Sgordon	decho "Using manual path: $MANPATH"
89213317Sgordon}
90213317Sgordon
91213317Sgordon# Usage: check_cat catglob
92213317Sgordon# Checks to see if a cat glob is available.
93213317Sgordoncheck_cat() {
94213317Sgordon	if exists "$1"; then
95213317Sgordon		use_cat=yes
96213317Sgordon		catpage=$found
97216140Sgordon		setup_cattool $catpage
98213317Sgordon		decho "    Found catpage $catpage"
99213317Sgordon		return 0
100213317Sgordon	else
101213317Sgordon		return 1
102213317Sgordon	fi
103213317Sgordon}
104213317Sgordon
105213317Sgordon# Usage: check_man manglob catglob
106213317Sgordon# Given 2 globs, figures out if the manglob is available, if so, check to
107213317Sgordon# see if the catglob is also available and up to date.
108213317Sgordoncheck_man() {
109213317Sgordon	if exists "$1"; then
110213317Sgordon		# We have a match, check for a cat page
111213317Sgordon		manpage=$found
112216140Sgordon		setup_cattool $manpage
113213317Sgordon		decho "    Found manpage $manpage"
114213317Sgordon
115222635Sru		if [ -n "${use_width}" ]; then
116222635Sru			# non-standard width
117222635Sru			unset use_cat
118222635Sru			decho "    Skipping catpage: non-standard page width"
119222635Sru		elif exists "$2" && is_newer $found $manpage; then
120213317Sgordon			# cat page found and is newer, use that
121213317Sgordon			use_cat=yes
122213317Sgordon			catpage=$found
123216140Sgordon			setup_cattool $catpage
124213317Sgordon			decho "    Using catpage $catpage"
125213317Sgordon		else
126213317Sgordon			# no cat page or is older
127213317Sgordon			unset use_cat
128213317Sgordon			decho "    Skipping catpage: not found or old"
129213317Sgordon		fi
130213317Sgordon		return 0
131213317Sgordon	fi
132213317Sgordon
133213317Sgordon	return 1
134213317Sgordon}
135213317Sgordon
136213317Sgordon# Usage: decho "string" [debuglevel]
137213317Sgordon# Echoes to stderr string prefaced with -- if high enough debuglevel.
138213317Sgordondecho() {
139213317Sgordon	if [ $debug -ge ${2:-1} ]; then
140213317Sgordon		echo "-- $1" >&2
141213317Sgordon	fi
142213317Sgordon}
143213317Sgordon
144213317Sgordon# Usage: exists glob
145213317Sgordon# Returns true if glob resolves to a real file.
146213317Sgordonexists() {
147213317Sgordon	local IFS
148213317Sgordon
149213317Sgordon	# Don't accidentally inherit callers IFS (breaks perl manpages)
150213317Sgordon	unset IFS
151213317Sgordon
152213317Sgordon	# Use some globbing tricks in the shell to determine if a file
153213317Sgordon	# exists or not.
154213317Sgordon	set +f
155213317Sgordon	set -- "$1" $1
156213317Sgordon	set -f
157213317Sgordon
158213317Sgordon	if [ "$1" != "$2" -a -r "$2" ]; then
159213317Sgordon		found="$2"
160213317Sgordon		return 0
161213317Sgordon	fi
162213317Sgordon
163213317Sgordon	return 1
164213317Sgordon}
165213317Sgordon
166213317Sgordon# Usage: find_file path section subdir pagename
167213317Sgordon# Returns: true if something is matched and found.
168213317Sgordon# Search the given path/section combo for a given page.
169213317Sgordonfind_file() {
170213317Sgordon	local manroot catroot mann man0 catn cat0
171213317Sgordon
172213317Sgordon	manroot="$1/man$2"
173213317Sgordon	catroot="$1/cat$2"
174213317Sgordon	if [ -n "$3" ]; then
175213317Sgordon		manroot="$manroot/$3"
176213317Sgordon		catroot="$catroot/$3"
177213317Sgordon	fi
178213317Sgordon
179326688Sbapt	if [ ! -d "$manroot" -a ! -d "$catroot" ]; then
180213317Sgordon		return 1
181213317Sgordon	fi
182213317Sgordon	decho "  Searching directory $manroot" 2
183213317Sgordon
184213317Sgordon	mann="$manroot/$4.$2*"
185213317Sgordon	man0="$manroot/$4.0*"
186213317Sgordon	catn="$catroot/$4.$2*"
187213317Sgordon	cat0="$catroot/$4.0*"
188213317Sgordon
189213317Sgordon	# This is the behavior as seen by the original man utility.
190213317Sgordon	# Let's not change that which doesn't seem broken.
191213317Sgordon	if check_man "$mann" "$catn"; then
192213317Sgordon		return 0
193213317Sgordon	elif check_man "$man0" "$cat0"; then
194213317Sgordon		return 0
195213317Sgordon	elif check_cat "$catn"; then
196213317Sgordon		return 0
197213317Sgordon	elif check_cat "$cat0"; then
198213317Sgordon		return 0
199213317Sgordon	fi
200213317Sgordon
201213317Sgordon	return 1
202213317Sgordon}
203213317Sgordon
204213317Sgordon# Usage: is_newer file1 file2
205213317Sgordon# Returns true if file1 is newer than file2 as calculated by mtime.
206213317Sgordonis_newer() {
207217831Suqs	if ! [ "$1" -ot "$2" ]; then
208217831Suqs		decho "    mtime: $1 not older than $2" 3
209213317Sgordon		return 0
210213317Sgordon	else
211213317Sgordon		decho "    mtime: $1 older than $2" 3
212213317Sgordon		return 1
213213317Sgordon	fi
214213317Sgordon}
215213317Sgordon
216213317Sgordon# Usage: manpath_parse_args "$@"
217213317Sgordon# Parses commandline options for manpath.
218213317Sgordonmanpath_parse_args() {
219213317Sgordon	local cmd_arg
220213317Sgordon
221213317Sgordon	while getopts 'Ldq' cmd_arg; do
222213317Sgordon		case "${cmd_arg}" in
223213317Sgordon		L)	Lflag=Lflag ;;
224213317Sgordon		d)	debug=$(( $debug + 1 )) ;;
225213317Sgordon		q)	qflag=qflag ;;
226213317Sgordon		*)	manpath_usage ;;
227213317Sgordon		esac
228213317Sgordon	done >&2
229213317Sgordon}
230213317Sgordon
231213317Sgordon# Usage: manpath_usage
232213317Sgordon# Display usage for the manpath(1) utility.
233213317Sgordonmanpath_usage() {
234213317Sgordon	echo 'usage: manpath [-Ldq]' >&2
235213317Sgordon	exit 1
236213317Sgordon}
237213317Sgordon
238213317Sgordon# Usage: manpath_warnings
239213317Sgordon# Display some warnings to stderr.
240213317Sgordonmanpath_warnings() {
241213317Sgordon	if [ -z "$Lflag" -a -n "$MANPATH" ]; then
242213317Sgordon		echo "(Warning: MANPATH environment variable set)" >&2
243213317Sgordon	fi
244213317Sgordon
245213317Sgordon	if [ -n "$Lflag" -a -n "$MANLOCALES" ]; then
246213317Sgordon		echo "(Warning: MANLOCALES environment variable set)" >&2
247213317Sgordon	fi
248213317Sgordon}
249213317Sgordon
250216140Sgordon# Usage: man_check_for_so page path
251216140Sgordon# Returns: True if able to resolve the file, false if it ended in tears.
252216140Sgordon# Detects the presence of the .so directive and causes the file to be
253216140Sgordon# redirected to another source file.
254216140Sgordonman_check_for_so() {
255216140Sgordon	local IFS line tstr
256216140Sgordon
257216140Sgordon	unset IFS
258326686Sbapt	if [ -n "$catpage" ]; then
259326686Sbapt		return 0
260326686Sbapt	fi
261216140Sgordon
262216140Sgordon	# We need to loop to accommodate multiple .so directives.
263216140Sgordon	while true
264216140Sgordon	do
265216140Sgordon		line=$($cattool $manpage | head -1)
266216140Sgordon		case "$line" in
267216140Sgordon		.so*)	trim "${line#.so}"
268216140Sgordon			decho "$manpage includes $tstr"
269216140Sgordon			# Glob and check for the file.
270216140Sgordon			if ! check_man "$path/$tstr*" ""; then
271216140Sgordon				decho "  Unable to find $tstr"
272216140Sgordon				return 1
273216140Sgordon			fi
274216140Sgordon			;;
275216140Sgordon		*)	break ;;
276216140Sgordon		esac
277216140Sgordon	done
278216140Sgordon
279216140Sgordon	return 0
280216140Sgordon}
281216140Sgordon
282278693Ssbruno# Usage: man_display_page
283278693Ssbruno# Display either the manpage or catpage depending on the use_cat variable
284213317Sgordonman_display_page() {
285278693Ssbruno	local EQN NROFF PIC TBL TROFF REFER VGRIND
286278693Ssbruno	local IFS l nroff_dev pipeline preproc_arg tool
287213317Sgordon
288213317Sgordon	# We are called with IFS set to colon. This causes really weird
289213317Sgordon	# things to happen for the variables that have spaces in them.
290213317Sgordon	unset IFS
291213317Sgordon
292213317Sgordon	# If we are supposed to use a catpage and we aren't using troff(1)
293213317Sgordon	# just zcat the catpage and we are done.
294213317Sgordon	if [ -z "$tflag" -a -n "$use_cat" ]; then
295213317Sgordon		if [ -n "$wflag" ]; then
296213317Sgordon			echo "$catpage (source: $manpage)"
297213317Sgordon			ret=0
298213317Sgordon		else
299213317Sgordon			if [ $debug -gt 0 ]; then
300222653Sru				decho "Command: $cattool $catpage | $MANPAGER"
301213317Sgordon				ret=0
302213317Sgordon			else
303222653Sru				eval "$cattool $catpage | $MANPAGER"
304213317Sgordon				ret=$?
305213317Sgordon			fi
306213317Sgordon		fi
307213317Sgordon		return
308213317Sgordon	fi
309213317Sgordon
310213317Sgordon	# Okay, we are using the manpage, do we just need to output the
311213317Sgordon	# name of the manpage?
312213317Sgordon	if [ -n "$wflag" ]; then
313213317Sgordon		echo "$manpage"
314213317Sgordon		ret=0
315213317Sgordon		return
316213317Sgordon	fi
317213317Sgordon
318213317Sgordon	# So, we really do need to parse the manpage. First, figure out the
319213317Sgordon	# device flag (-T) we have to pass to eqn(1) and groff(1). Then,
320213317Sgordon	# setup the pipeline of commands based on the user's request.
321213317Sgordon
322220261Sgordon	# If the manpage is from a particular charset, we need to setup nroff
323220261Sgordon	# to properly output for the correct device.
324220261Sgordon	case "${manpage}" in
325220261Sgordon	*.${man_charset}/*)
326213317Sgordon		# I don't pretend to know this; I'm just copying from the
327213317Sgordon		# previous version of man(1).
328213317Sgordon		case "$man_charset" in
329213317Sgordon		KOI8-R)		nroff_dev="koi8-r" ;;
330213317Sgordon		ISO8859-1)	nroff_dev="latin1" ;;
331213317Sgordon		ISO8859-15)	nroff_dev="latin1" ;;
332213317Sgordon		UTF-8)		nroff_dev="utf8" ;;
333213317Sgordon		*)		nroff_dev="ascii" ;;
334213317Sgordon		esac
335213317Sgordon
336220261Sgordon		NROFF="$NROFF -T$nroff_dev"
337213317Sgordon		EQN="$EQN -T$nroff_dev"
338213317Sgordon
339220261Sgordon		# Iff the manpage is from the locale and not just the charset,
340220261Sgordon		# then we need to define the locale string.
341220261Sgordon		case "${manpage}" in
342220261Sgordon		*/${man_lang}_${man_country}.${man_charset}/*)
343220261Sgordon			NROFF="$NROFF -dlocale=$man_lang.$man_charset"
344220261Sgordon			;;
345220261Sgordon		*/${man_lang}.${man_charset}/*)
346220261Sgordon			NROFF="$NROFF -dlocale=$man_lang.$man_charset"
347220261Sgordon			;;
348220261Sgordon		esac
349220261Sgordon
350213317Sgordon		# Allow language specific calls to override the default
351213317Sgordon		# set of utilities.
352213317Sgordon		l=$(echo $man_lang | tr [:lower:] [:upper:])
353222650Sru		for tool in EQN NROFF PIC TBL TROFF REFER VGRIND; do
354213317Sgordon			eval "$tool=\${${tool}_$l:-\$$tool}"
355213317Sgordon		done
356213317Sgordon		;;
357213317Sgordon	*)	NROFF="$NROFF -Tascii"
358213317Sgordon		EQN="$EQN -Tascii"
359213317Sgordon		;;
360213317Sgordon	esac
361213317Sgordon
362222653Sru	if [ -z "$MANCOLOR" ]; then
363222653Sru		NROFF="$NROFF -P-c"
364222653Sru	fi
365222653Sru
366222635Sru	if [ -n "${use_width}" ]; then
367222635Sru		NROFF="$NROFF -rLL=${use_width}n -rLT=${use_width}n"
368222635Sru	fi
369222635Sru
370213317Sgordon	if [ -n "$MANROFFSEQ" ]; then
371213317Sgordon		set -- -$MANROFFSEQ
372213317Sgordon		while getopts 'egprtv' preproc_arg; do
373213317Sgordon			case "${preproc_arg}" in
374213317Sgordon			e)	pipeline="$pipeline | $EQN" ;;
375228992Suqs			g)	;; # Ignore for compatibility.
376213317Sgordon			p)	pipeline="$pipeline | $PIC" ;;
377213317Sgordon			r)	pipeline="$pipeline | $REFER" ;;
378222650Sru			t)	pipeline="$pipeline | $TBL" ;;
379213317Sgordon			v)	pipeline="$pipeline | $VGRIND" ;;
380213317Sgordon			*)	usage ;;
381213317Sgordon			esac
382213317Sgordon		done
383213317Sgordon		# Strip the leading " | " from the resulting pipeline.
384213317Sgordon		pipeline="${pipeline#" | "}"
385213317Sgordon	else
386213317Sgordon		pipeline="$TBL"
387213317Sgordon	fi
388213317Sgordon
389213317Sgordon	if [ -n "$tflag" ]; then
390213317Sgordon		pipeline="$pipeline | $TROFF"
391213317Sgordon	else
392222653Sru		pipeline="$pipeline | $NROFF | $MANPAGER"
393213317Sgordon	fi
394213317Sgordon
395213317Sgordon	if [ $debug -gt 0 ]; then
396216140Sgordon		decho "Command: $cattool $manpage | $pipeline"
397213317Sgordon		ret=0
398213317Sgordon	else
399216140Sgordon		eval "$cattool $manpage | $pipeline"
400213317Sgordon		ret=$?
401213317Sgordon	fi
402213317Sgordon}
403213317Sgordon
404213317Sgordon# Usage: man_find_and_display page
405213317Sgordon# Search through the manpaths looking for the given page.
406213317Sgordonman_find_and_display() {
407213317Sgordon	local found_page locpath p path sect
408213317Sgordon
409213507Sgordon	# Check to see if it's a file. But only if it has a '/' in
410213507Sgordon	# the filename.
411213507Sgordon	case "$1" in
412213507Sgordon	*/*)	if [ -f "$1" -a -r "$1" ]; then
413213507Sgordon			decho "Found a usable page, displaying that"
414213507Sgordon			unset use_cat
415213507Sgordon			manpage="$1"
416216140Sgordon			setup_cattool $manpage
417216140Sgordon			if man_check_for_so $manpage $(dirname $manpage); then
418216140Sgordon				found_page=yes
419216140Sgordon				man_display_page
420216140Sgordon			fi
421213507Sgordon			return
422213507Sgordon		fi
423213507Sgordon		;;
424213507Sgordon	esac
425213507Sgordon
426213317Sgordon	IFS=:
427213317Sgordon	for sect in $MANSECT; do
428213317Sgordon		decho "Searching section $sect" 2
429213317Sgordon		for path in $MANPATH; do
430213317Sgordon			for locpath in $locpaths; do
431213317Sgordon				p=$path/$locpath
432213317Sgordon				p=${p%/.} # Rid ourselves of the trailing /.
433213317Sgordon
434213317Sgordon				# Check if there is a MACHINE specific manpath.
435213317Sgordon				if find_file $p $sect $MACHINE "$1"; then
436216140Sgordon					if man_check_for_so $manpage $p; then
437216140Sgordon						found_page=yes
438216140Sgordon						man_display_page
439216140Sgordon						if [ -n "$aflag" ]; then
440216140Sgordon							continue 2
441216140Sgordon						else
442216140Sgordon							return
443216140Sgordon						fi
444213317Sgordon					fi
445213317Sgordon				fi
446213317Sgordon
447213317Sgordon				# Check if there is a MACHINE_ARCH
448213317Sgordon				# specific manpath.
449213317Sgordon				if find_file $p $sect $MACHINE_ARCH "$1"; then
450216140Sgordon					if man_check_for_so $manpage $p; then
451216140Sgordon						found_page=yes
452216140Sgordon						man_display_page
453216140Sgordon						if [ -n "$aflag" ]; then
454216140Sgordon							continue 2
455216140Sgordon						else
456216140Sgordon							return
457216140Sgordon						fi
458213317Sgordon					fi
459213317Sgordon				fi
460213317Sgordon
461213317Sgordon				# Check plain old manpath.
462213317Sgordon				if find_file $p $sect '' "$1"; then
463216140Sgordon					if man_check_for_so $manpage $p; then
464216140Sgordon						found_page=yes
465216140Sgordon						man_display_page
466216140Sgordon						if [ -n "$aflag" ]; then
467216140Sgordon							continue 2
468216140Sgordon						else
469216140Sgordon							return
470216140Sgordon						fi
471213317Sgordon					fi
472213317Sgordon				fi
473213317Sgordon			done
474213317Sgordon		done
475213317Sgordon	done
476213317Sgordon	unset IFS
477213317Sgordon
478213317Sgordon	# Nothing? Well, we are done then.
479213317Sgordon	if [ -z "$found_page" ]; then
480213317Sgordon		echo "No manual entry for $1" >&2
481213317Sgordon		ret=1
482213317Sgordon		return
483213317Sgordon	fi
484213317Sgordon}
485213317Sgordon
486213317Sgordon# Usage: man_parse_args "$@"
487213317Sgordon# Parses commandline options for man.
488213317Sgordonman_parse_args() {
489213317Sgordon	local IFS cmd_arg
490213317Sgordon
491213317Sgordon	while getopts 'M:P:S:adfhkm:op:tw' cmd_arg; do
492213317Sgordon		case "${cmd_arg}" in
493213317Sgordon		M)	MANPATH=$OPTARG ;;
494222653Sru		P)	MANPAGER=$OPTARG ;;
495213317Sgordon		S)	MANSECT=$OPTARG ;;
496213317Sgordon		a)	aflag=aflag ;;
497213317Sgordon		d)	debug=$(( $debug + 1 )) ;;
498213317Sgordon		f)	fflag=fflag ;;
499213317Sgordon		h)	man_usage 0 ;;
500213317Sgordon		k)	kflag=kflag ;;
501213317Sgordon		m)	mflag=$OPTARG ;;
502213317Sgordon		o)	oflag=oflag ;;
503213317Sgordon		p)	MANROFFSEQ=$OPTARG ;;
504213317Sgordon		t)	tflag=tflag ;;
505213317Sgordon		w)	wflag=wflag ;;
506213317Sgordon		*)	man_usage ;;
507213317Sgordon		esac
508213317Sgordon	done >&2
509213317Sgordon
510213317Sgordon	shift $(( $OPTIND - 1 ))
511213317Sgordon
512213317Sgordon	# Check the args for incompatible options.
513213317Sgordon	case "${fflag}${kflag}${tflag}${wflag}" in
514213317Sgordon	fflagkflag*)	echo "Incompatible options: -f and -k"; man_usage ;;
515213317Sgordon	fflag*tflag*)	echo "Incompatible options: -f and -t"; man_usage ;;
516213317Sgordon	fflag*wflag)	echo "Incompatible options: -f and -w"; man_usage ;;
517213317Sgordon	*kflagtflag*)	echo "Incompatible options: -k and -t"; man_usage ;;
518213317Sgordon	*kflag*wflag)	echo "Incompatible options: -k and -w"; man_usage ;;
519213317Sgordon	*tflagwflag)	echo "Incompatible options: -t and -w"; man_usage ;;
520213317Sgordon	esac
521213317Sgordon
522213317Sgordon	# Short circuit for whatis(1) and apropos(1)
523213317Sgordon	if [ -n "$fflag" ]; then
524213317Sgordon		do_whatis "$@"
525213317Sgordon		exit
526213317Sgordon	fi
527213317Sgordon
528213317Sgordon	if [ -n "$kflag" ]; then
529213317Sgordon		do_apropos "$@"
530213317Sgordon		exit
531213317Sgordon	fi
532213317Sgordon
533213317Sgordon	IFS=:
534213317Sgordon	for sect in $man_default_sections; do
535213317Sgordon		if [ "$sect" = "$1" ]; then
536213317Sgordon			decho "Detected manual section as first arg: $1"
537213317Sgordon			MANSECT="$1"
538213317Sgordon			shift
539213317Sgordon			break
540213317Sgordon		fi
541213317Sgordon	done
542213317Sgordon	unset IFS
543213317Sgordon
544213317Sgordon	pages="$*"
545213317Sgordon}
546213317Sgordon
547213317Sgordon# Usage: man_setup
548213317Sgordon# Setup various trivial but essential variables.
549213317Sgordonman_setup() {
550213317Sgordon	# Setup machine and architecture variables.
551213317Sgordon	if [ -n "$mflag" ]; then
552213317Sgordon		MACHINE_ARCH=${mflag%%:*}
553213317Sgordon		MACHINE=${mflag##*:}
554213317Sgordon	fi
555213317Sgordon	if [ -z "$MACHINE_ARCH" ]; then
556216426Sgordon		MACHINE_ARCH=$($SYSCTL -n hw.machine_arch)
557213317Sgordon	fi
558213317Sgordon	if [ -z "$MACHINE" ]; then
559216426Sgordon		MACHINE=$($SYSCTL -n hw.machine)
560213317Sgordon	fi
561213317Sgordon	decho "Using architecture: $MACHINE_ARCH:$MACHINE"
562213317Sgordon
563213317Sgordon	setup_pager
564213317Sgordon
565213317Sgordon	# Setup manual sections to search.
566213317Sgordon	if [ -z "$MANSECT" ]; then
567213317Sgordon		MANSECT=$man_default_sections
568213317Sgordon	fi
569213317Sgordon	decho "Using manual sections: $MANSECT"
570213317Sgordon
571213317Sgordon	build_manpath
572213317Sgordon	man_setup_locale
573222635Sru	man_setup_width
574213317Sgordon}
575213317Sgordon
576222635Sru# Usage: man_setup_width
577222635Sru# Set up page width.
578222635Sruman_setup_width() {
579222635Sru	local sizes
580222635Sru
581222635Sru	unset use_width
582222635Sru	case "$MANWIDTH" in
583222635Sru	[0-9]*)
584222635Sru		if [ "$MANWIDTH" -gt 0 2>/dev/null ]; then
585222635Sru			use_width=$MANWIDTH
586222635Sru		fi
587222635Sru		;;
588222635Sru	[Tt][Tt][Yy])
589222635Sru		if { sizes=$($STTY size 0>&3 2>/dev/null); } 3>&1; then
590222635Sru			set -- $sizes
591222635Sru			if [ $2 -gt 80 ]; then
592222635Sru				use_width=$(($2-2))
593222635Sru			fi
594222635Sru		fi
595222635Sru		;;
596222635Sru	esac
597222635Sru	if [ -n "$use_width" ]; then
598222635Sru		decho "Using non-standard page width: ${use_width}"
599222635Sru	else
600222635Sru		decho 'Using standard page width'
601222635Sru	fi
602222635Sru}
603222635Sru
604213317Sgordon# Usage: man_setup_locale
605213317Sgordon# Setup necessary locale variables.
606213317Sgordonman_setup_locale() {
607220261Sgordon	local lang_cc
608220261Sgordon
609220261Sgordon	locpaths='.'
610220261Sgordon	man_charset='US-ASCII'
611220261Sgordon
612213317Sgordon	# Setup locale information.
613213317Sgordon	if [ -n "$oflag" ]; then
614220261Sgordon		decho 'Using non-localized manpages'
615220261Sgordon	else
616220261Sgordon		# Use the locale tool to give us the proper LC_CTYPE
617220261Sgordon		eval $( $LOCALE )
618220261Sgordon
619220261Sgordon		case "$LC_CTYPE" in
620220261Sgordon		C)		;;
621220261Sgordon		POSIX)		;;
622220261Sgordon		[a-z][a-z]_[A-Z][A-Z]\.*)
623220261Sgordon				lang_cc="${LC_CTYPE%.*}"
624220261Sgordon				man_lang="${LC_CTYPE%_*}"
625220261Sgordon				man_country="${lang_cc#*_}"
626220261Sgordon				man_charset="${LC_CTYPE#*.}"
627220261Sgordon				locpaths="$LC_CTYPE"
628220261Sgordon				locpaths="$locpaths:$man_lang.$man_charset"
629220261Sgordon				if [ "$man_lang" != "en" ]; then
630220261Sgordon					locpaths="$locpaths:en.$man_charset"
631220261Sgordon				fi
632220261Sgordon				locpaths="$locpaths:."
633220261Sgordon				;;
634220261Sgordon		*)		echo 'Unknown locale, assuming C' >&2
635220261Sgordon				;;
636220261Sgordon		esac
637213317Sgordon	fi
638213317Sgordon
639213317Sgordon	decho "Using locale paths: $locpaths"
640213317Sgordon}
641213317Sgordon
642213317Sgordon# Usage: man_usage [exitcode]
643213317Sgordon# Display usage for the man utility.
644213317Sgordonman_usage() {
645213317Sgordon	echo 'Usage:'
646213317Sgordon	echo ' man [-adho] [-t | -w] [-M manpath] [-P pager] [-S mansect]'
647213317Sgordon	echo '     [-m arch[:machine]] [-p [eprtv]] [mansect] page [...]'
648213317Sgordon	echo ' man -f page [...] -- Emulates whatis(1)'
649213317Sgordon	echo ' man -k page [...] -- Emulates apropos(1)'
650213317Sgordon
651213317Sgordon	# When exit'ing with -h, it's not an error.
652213317Sgordon	exit ${1:-1}
653213317Sgordon}
654213317Sgordon
655213317Sgordon# Usage: parse_configs
656213317Sgordon# Reads the end-user adjustable config files.
657213317Sgordonparse_configs() {
658213317Sgordon	local IFS file files
659213317Sgordon
660213317Sgordon	if [ -n "$parsed_configs" ]; then
661213317Sgordon		return
662213317Sgordon	fi
663213317Sgordon
664213317Sgordon	unset IFS
665213317Sgordon
666213317Sgordon	# Read the global config first in case the user wants
667213317Sgordon	# to override config_local.
668213317Sgordon	if [ -r "$config_global" ]; then
669213317Sgordon		parse_file "$config_global"
670213317Sgordon	fi
671213317Sgordon
672213317Sgordon	# Glob the list of files to parse.
673213317Sgordon	set +f
674213317Sgordon	files=$(echo $config_local)
675213317Sgordon	set -f
676213317Sgordon
677213317Sgordon	for file in $files; do
678213317Sgordon		if [ -r "$file" ]; then
679213317Sgordon			parse_file "$file"
680213317Sgordon		fi
681213317Sgordon	done
682213317Sgordon
683213317Sgordon	parsed_configs='yes'
684213317Sgordon}
685213317Sgordon
686213317Sgordon# Usage: parse_file file
687213317Sgordon# Reads the specified config files.
688213317Sgordonparse_file() {
689213317Sgordon	local file line tstr var
690213317Sgordon
691213317Sgordon	file="$1"
692213317Sgordon	decho "Parsing config file: $file"
693213317Sgordon	while read line; do
694213317Sgordon		decho "  $line" 2
695213317Sgordon		case "$line" in
696213317Sgordon		\#*)		decho "    Comment" 3
697213317Sgordon				;;
698213317Sgordon		MANPATH*)	decho "    MANPATH" 3
699213317Sgordon				trim "${line#MANPATH}"
700213317Sgordon				add_to_manpath "$tstr"
701213317Sgordon				;;
702213317Sgordon		MANLOCALE*)	decho "    MANLOCALE" 3
703213317Sgordon				trim "${line#MANLOCALE}"
704213317Sgordon				manlocales="$manlocales:$tstr"
705213317Sgordon				;;
706213317Sgordon		MANCONFIG*)	decho "    MANCONFIG" 3
707222638Sru				trim "${line#MANCONFIG}"
708213317Sgordon				config_local="$tstr"
709213317Sgordon				;;
710213317Sgordon		# Set variables in the form of FOO_BAR
711213317Sgordon		*_*[\ \	]*)	var="${line%%[\ \	]*}"
712213317Sgordon				trim "${line#$var}"
713213317Sgordon				eval "$var=\"$tstr\""
714213317Sgordon				decho "    Parsed $var" 3
715213317Sgordon				;;
716213317Sgordon		esac
717213317Sgordon	done < "$file"
718213317Sgordon}
719213317Sgordon
720213317Sgordon# Usage: search_path
721213317Sgordon# Traverse $PATH looking for manpaths.
722213317Sgordonsearch_path() {
723213317Sgordon	local IFS p path
724213317Sgordon
725213317Sgordon	decho "Searching PATH for man directories"
726213317Sgordon
727213317Sgordon	IFS=:
728213317Sgordon	for path in $PATH; do
729213317Sgordon		# Do a little special casing since the base manpages
730213317Sgordon		# are in /usr/share/man instead of /usr/man or /man.
731213317Sgordon		case "$path" in
732213317Sgordon		/bin|/usr/bin)	add_to_manpath "/usr/share/man" ;;
733213317Sgordon		*)	if add_to_manpath "$path/man"; then
734213317Sgordon				:
735213317Sgordon			elif add_to_manpath "$path/MAN"; then
736213317Sgordon				:
737213317Sgordon			else
738213317Sgordon				case "$path" in
739213317Sgordon				*/bin)	p="${path%/bin}/man"
740213317Sgordon					add_to_manpath "$p"
741213317Sgordon					;;
742213317Sgordon				*)	;;
743213317Sgordon				esac
744213317Sgordon			fi
745213317Sgordon			;;
746213317Sgordon		esac
747213317Sgordon	done
748213317Sgordon	unset IFS
749213317Sgordon
750213317Sgordon	if [ -z "$manpath" ]; then
751213317Sgordon		decho '  Unable to find any manpaths, using default'
752213317Sgordon		manpath=$man_default_path
753213317Sgordon	fi
754213317Sgordon}
755213317Sgordon
756213317Sgordon# Usage: search_whatis cmd [arglist]
757213317Sgordon# Do the heavy lifting for apropos/whatis
758213317Sgordonsearch_whatis() {
759213317Sgordon	local IFS bad cmd f good key keywords loc opt out path rval wlist
760213317Sgordon
761213317Sgordon	cmd="$1"
762213317Sgordon	shift
763213317Sgordon
764213317Sgordon	whatis_parse_args "$@"
765213317Sgordon
766213317Sgordon	build_manpath
767213317Sgordon	build_manlocales
768213317Sgordon	setup_pager
769213317Sgordon
770213317Sgordon	if [ "$cmd" = "whatis" ]; then
771213317Sgordon		opt="-w"
772213317Sgordon	fi
773213317Sgordon
774213317Sgordon	f='whatis'
775213317Sgordon
776213317Sgordon	IFS=:
777213317Sgordon	for path in $MANPATH; do
778213317Sgordon		if [ \! -d "$path" ]; then
779213317Sgordon			decho "Skipping non-existent path: $path" 2
780213317Sgordon			continue
781213317Sgordon		fi
782213317Sgordon
783213317Sgordon		if [ -f "$path/$f" -a -r "$path/$f" ]; then
784213317Sgordon			decho "Found whatis: $path/$f"
785213317Sgordon			wlist="$wlist $path/$f"
786213317Sgordon		fi
787213317Sgordon
788213317Sgordon		for loc in $MANLOCALES; do
789213317Sgordon			if [ -f "$path/$loc/$f" -a -r "$path/$loc/$f" ]; then
790213317Sgordon				decho "Found whatis: $path/$loc/$f"
791213317Sgordon				wlist="$wlist $path/$loc/$f"
792213317Sgordon			fi
793213317Sgordon		done
794213317Sgordon	done
795213317Sgordon	unset IFS
796213317Sgordon
797213317Sgordon	if [ -z "$wlist" ]; then
798213317Sgordon		echo "$cmd: no whatis databases in $MANPATH" >&2
799213317Sgordon		exit 1
800213317Sgordon	fi
801213317Sgordon
802213317Sgordon	rval=0
803213317Sgordon	for key in $keywords; do
804213317Sgordon		out=$(grep -Ehi $opt -- "$key" $wlist)
805213317Sgordon		if [ -n "$out" ]; then
806213317Sgordon			good="$good\\n$out"
807213317Sgordon		else
808213317Sgordon			bad="$bad\\n$key: nothing appropriate"
809213317Sgordon			rval=1
810213317Sgordon		fi
811213317Sgordon	done
812213317Sgordon
813213317Sgordon	# Strip leading carriage return.
814213317Sgordon	good=${good#\\n}
815213317Sgordon	bad=${bad#\\n}
816213317Sgordon
817213317Sgordon	if [ -n "$good" ]; then
818222653Sru		echo -e "$good" | $MANPAGER
819213317Sgordon	fi
820213317Sgordon
821213317Sgordon	if [ -n "$bad" ]; then
822213349Sgordon		echo -e "$bad" >&2
823213317Sgordon	fi
824213317Sgordon
825213317Sgordon	exit $rval
826213317Sgordon}
827213317Sgordon
828216140Sgordon# Usage: setup_cattool page
829216140Sgordon# Finds an appropriate decompressor based on extension
830216140Sgordonsetup_cattool() {
831216140Sgordon	case "$1" in
832216140Sgordon	*.bz)	cattool='/usr/bin/bzcat' ;;
833216140Sgordon	*.bz2)	cattool='/usr/bin/bzcat' ;;
834216140Sgordon	*.gz)	cattool='/usr/bin/zcat' ;;
835216140Sgordon	*.lzma)	cattool='/usr/bin/lzcat' ;;
836216140Sgordon	*.xz)	cattool='/usr/bin/xzcat' ;;
837216140Sgordon	*)	cattool='/usr/bin/zcat -f' ;;
838216140Sgordon	esac
839216140Sgordon}
840216140Sgordon
841213317Sgordon# Usage: setup_pager
842222653Sru# Correctly sets $MANPAGER
843213317Sgordonsetup_pager() {
844213317Sgordon	# Setup pager.
845222653Sru	if [ -z "$MANPAGER" ]; then
846222653Sru		if [ -n "$MANCOLOR" ]; then
847222653Sru			MANPAGER="less -sR"
848222653Sru		else
849222653Sru			if [ -n "$PAGER" ]; then
850222653Sru				MANPAGER="$PAGER"
851222653Sru			else
852222653Sru				MANPAGER="more -s"
853222653Sru			fi
854222653Sru		fi
855213317Sgordon	fi
856222653Sru	decho "Using pager: $MANPAGER"
857213317Sgordon}
858213317Sgordon
859213317Sgordon# Usage: trim string
860213317Sgordon# Trims whitespace from beginning and end of a variable
861213317Sgordontrim() {
862213317Sgordon	tstr=$1
863213317Sgordon	while true; do
864213317Sgordon		case "$tstr" in
865213317Sgordon		[\ \	]*)	tstr="${tstr##[\ \	]}" ;;
866213317Sgordon		*[\ \	])	tstr="${tstr%%[\ \	]}" ;;
867213317Sgordon		*)		break ;;
868213317Sgordon		esac
869213317Sgordon	done
870213317Sgordon}
871213317Sgordon
872213317Sgordon# Usage: whatis_parse_args "$@"
873213317Sgordon# Parse commandline args for whatis and apropos.
874213317Sgordonwhatis_parse_args() {
875213317Sgordon	local cmd_arg
876213317Sgordon	while getopts 'd' cmd_arg; do
877213317Sgordon		case "${cmd_arg}" in
878213317Sgordon		d)	debug=$(( $debug + 1 )) ;;
879213317Sgordon		*)	whatis_usage ;;
880213317Sgordon		esac
881213317Sgordon	done >&2
882213317Sgordon
883213317Sgordon	shift $(( $OPTIND - 1 ))
884213317Sgordon
885213317Sgordon	keywords="$*"
886213317Sgordon}
887213317Sgordon
888213317Sgordon# Usage: whatis_usage
889213317Sgordon# Display usage for the whatis/apropos utility.
890213317Sgordonwhatis_usage() {
891213317Sgordon	echo "usage: $cmd [-d] keyword [...]"
892213317Sgordon	exit 1
893213317Sgordon}
894213317Sgordon
895213317Sgordon
896213317Sgordon
897213317Sgordon# Supported commands
898213317Sgordondo_apropos() {
899213317Sgordon	search_whatis apropos "$@"
900213317Sgordon}
901213317Sgordon
902213317Sgordondo_man() {
903213317Sgordon	man_parse_args "$@"
904213317Sgordon	if [ -z "$pages" ]; then
905213317Sgordon		echo 'What manual page do you want?' >&2
906213317Sgordon		exit 1
907213317Sgordon	fi
908213317Sgordon	man_setup
909213317Sgordon
910213317Sgordon	for page in $pages; do
911213317Sgordon		decho "Searching for $page"
912213317Sgordon		man_find_and_display "$page"
913213317Sgordon	done
914213317Sgordon
915213317Sgordon	exit ${ret:-0}
916213317Sgordon}
917213317Sgordon
918213317Sgordondo_manpath() {
919213317Sgordon	manpath_parse_args "$@"
920213317Sgordon	if [ -z "$qflag" ]; then
921213317Sgordon		manpath_warnings
922213317Sgordon	fi
923213317Sgordon	if [ -n "$Lflag" ]; then
924213317Sgordon		build_manlocales
925213317Sgordon		echo $MANLOCALES
926213317Sgordon	else
927213317Sgordon		build_manpath
928213317Sgordon		echo $MANPATH
929213317Sgordon	fi
930213317Sgordon	exit 0
931213317Sgordon}
932213317Sgordon
933213317Sgordondo_whatis() {
934213317Sgordon	search_whatis whatis "$@"
935213317Sgordon}
936213317Sgordon
937221303Suqs# User's PATH setting decides on the groff-suite to pick up.
938221303SuqsEQN=eqn
939222653SruNROFF='groff -S -P-h -Wall -mtty-char -man'
940221303SuqsPIC=pic
941221303SuqsREFER=refer
942221303SuqsTBL=tbl
943222601SuqsTROFF='groff -S -man'
944221303SuqsVGRIND=vgrind
945221303Suqs
946220261SgordonLOCALE=/usr/bin/locale
947222635SruSTTY=/bin/stty
948216426SgordonSYSCTL=/sbin/sysctl
949213317Sgordon
950213317Sgordondebug=0
951245514Sbrooksman_default_sections='1:8:2:3:n:4:5:6:7:9:l'
952213317Sgordonman_default_path='/usr/share/man:/usr/share/openssl/man:/usr/local/man'
953216140Sgordoncattool='/usr/bin/zcat -f'
954213317Sgordon
955213317Sgordonconfig_global='/etc/man.conf'
956213317Sgordon
957213317Sgordon# This can be overridden via a setting in /etc/man.conf.
958213317Sgordonconfig_local='/usr/local/etc/man.d/*.conf'
959213317Sgordon
960213317Sgordon# Set noglobbing for now. I don't want spurious globbing.
961213317Sgordonset -f
962213317Sgordon
963213317Sgordoncase "$0" in
964213317Sgordon*apropos)	do_apropos "$@" ;;
965213317Sgordon*manpath)	do_manpath "$@" ;;
966213317Sgordon*whatis)	do_whatis "$@" ;;
967213317Sgordon*)		do_man "$@" ;;
968213317Sgordonesac
969