xref: /freebsd/usr.bin/man/man.sh (revision 37be4197f72ae3a61bd5e93d2ebdc9bd6d09ed21)
1c535eb59SGordon Tetlow#! /bin/sh
2c535eb59SGordon Tetlow#
34d846d26SWarner Losh# SPDX-License-Identifier: BSD-2-Clause
41de7b4b8SPedro F. Giffuni#
5c535eb59SGordon Tetlow#  Copyright (c) 2010 Gordon Tetlow
6c535eb59SGordon Tetlow#  All rights reserved.
7c535eb59SGordon Tetlow#
8c535eb59SGordon Tetlow#  Redistribution and use in source and binary forms, with or without
9c535eb59SGordon Tetlow#  modification, are permitted provided that the following conditions
10c535eb59SGordon Tetlow#  are met:
11c535eb59SGordon Tetlow#  1. Redistributions of source code must retain the above copyright
12c535eb59SGordon Tetlow#     notice, this list of conditions and the following disclaimer.
13c535eb59SGordon Tetlow#  2. Redistributions in binary form must reproduce the above copyright
14c535eb59SGordon Tetlow#     notice, this list of conditions and the following disclaimer in the
15c535eb59SGordon Tetlow#     documentation and/or other materials provided with the distribution.
16c535eb59SGordon Tetlow#
17c535eb59SGordon Tetlow#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18c535eb59SGordon Tetlow#  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19c535eb59SGordon Tetlow#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20c535eb59SGordon Tetlow#  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21c535eb59SGordon Tetlow#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22c535eb59SGordon Tetlow#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23c535eb59SGordon Tetlow#  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24c535eb59SGordon Tetlow#  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25c535eb59SGordon Tetlow#  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26c535eb59SGordon Tetlow#  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27c535eb59SGordon Tetlow#  SUCH DAMAGE.
28c535eb59SGordon Tetlow#
29c535eb59SGordon Tetlow
30433c5a8aSWolfram Schneider# Rendering a manual page is fast. Even a manual page several 100k in size
31433c5a8aSWolfram Schneider# takes less than a CPU second. If it takes much longer, it is very likely
32433c5a8aSWolfram Schneider# that a tool like mandoc(1) is running in an infinite loop. In this case
33433c5a8aSWolfram Schneider# it is better to terminate it.
34433c5a8aSWolfram Schneiderulimit -t 20
35433c5a8aSWolfram Schneider
3614a5c106SWolfram Schneider# do not ignore the exit status of roff tools
3714a5c106SWolfram Schneiderset -o pipefail
3814a5c106SWolfram Schneider
3914a5c106SWolfram Schneider# ignore SIGPIPE exits because pagers may exit before reading all their input.
4014a5c106SWolfram Schneidertrap '' SIGPIPE
4114a5c106SWolfram Schneider
42c535eb59SGordon Tetlow# Usage: add_to_manpath path
43c535eb59SGordon Tetlow# Adds a variable to manpath while ensuring we don't have duplicates.
44c535eb59SGordon Tetlow# Returns true if we were able to add something. False otherwise.
45c535eb59SGordon Tetlowadd_to_manpath() {
46c535eb59SGordon Tetlow	case "$manpath" in
47c535eb59SGordon Tetlow	*:$1)	decho "  Skipping duplicate manpath entry $1" 2 ;;
48c535eb59SGordon Tetlow	$1:*)	decho "  Skipping duplicate manpath entry $1" 2 ;;
49c535eb59SGordon Tetlow	*:$1:*)	decho "  Skipping duplicate manpath entry $1" 2 ;;
50c535eb59SGordon Tetlow	*)	if [ -d "$1" ]; then
51c535eb59SGordon Tetlow			decho "  Adding $1 to manpath"
52c535eb59SGordon Tetlow			manpath="$manpath:$1"
53c535eb59SGordon Tetlow			return 0
54c535eb59SGordon Tetlow		fi
55c535eb59SGordon Tetlow		;;
56c535eb59SGordon Tetlow	esac
57c535eb59SGordon Tetlow
58c535eb59SGordon Tetlow	return 1
59c535eb59SGordon Tetlow}
60c535eb59SGordon Tetlow
61c535eb59SGordon Tetlow# Usage: build_manlocales
62c535eb59SGordon Tetlow# Builds a correct MANLOCALES variable.
63c535eb59SGordon Tetlowbuild_manlocales() {
64c535eb59SGordon Tetlow	# If the user has set manlocales, who are we to argue.
65c535eb59SGordon Tetlow	if [ -n "$MANLOCALES" ]; then
66c535eb59SGordon Tetlow		return
67c535eb59SGordon Tetlow	fi
68c535eb59SGordon Tetlow
69c535eb59SGordon Tetlow	parse_configs
70c535eb59SGordon Tetlow
71c535eb59SGordon Tetlow	# Trim leading colon
72c535eb59SGordon Tetlow	MANLOCALES=${manlocales#:}
73c535eb59SGordon Tetlow
74c535eb59SGordon Tetlow	decho "Available manual locales: $MANLOCALES"
75c535eb59SGordon Tetlow}
76c535eb59SGordon Tetlow
778edb6fb5SMohamed Akram# Usage: build_mansect
788edb6fb5SMohamed Akram# Builds a correct MANSECT variable.
798edb6fb5SMohamed Akrambuild_mansect() {
808edb6fb5SMohamed Akram	# If the user has set mansect, who are we to argue.
818edb6fb5SMohamed Akram	if [ -n "$MANSECT" ]; then
828edb6fb5SMohamed Akram		return
838edb6fb5SMohamed Akram	fi
848edb6fb5SMohamed Akram
858edb6fb5SMohamed Akram	parse_configs
868edb6fb5SMohamed Akram
878edb6fb5SMohamed Akram	# Trim leading colon
888edb6fb5SMohamed Akram	MANSECT=${mansect#:}
898edb6fb5SMohamed Akram
908edb6fb5SMohamed Akram	if [ -z "$MANSECT" ]; then
918edb6fb5SMohamed Akram		MANSECT=$man_default_sections
928edb6fb5SMohamed Akram	fi
938edb6fb5SMohamed Akram	decho "Using manual sections: $MANSECT"
948edb6fb5SMohamed Akram}
958edb6fb5SMohamed Akram
96c535eb59SGordon Tetlow# Usage: build_manpath
97c535eb59SGordon Tetlow# Builds a correct MANPATH variable.
98c535eb59SGordon Tetlowbuild_manpath() {
99c535eb59SGordon Tetlow	local IFS
100c535eb59SGordon Tetlow
101c535eb59SGordon Tetlow	# If the user has set a manpath, who are we to argue.
102c535eb59SGordon Tetlow	if [ -n "$MANPATH" ]; then
103b2394e73SBaptiste Daroussin		case "$MANPATH" in
104b2394e73SBaptiste Daroussin		*:) PREPEND_MANPATH=${MANPATH} ;;
105b2394e73SBaptiste Daroussin		:*) APPEND_MANPATH=${MANPATH} ;;
106b2394e73SBaptiste Daroussin		*::*)
107b2394e73SBaptiste Daroussin			PREPEND_MANPATH=${MANPATH%%::*}
108b2394e73SBaptiste Daroussin			APPEND_MANPATH=${MANPATH#*::}
109b2394e73SBaptiste Daroussin			;;
110b2394e73SBaptiste Daroussin		*) return ;;
111b2394e73SBaptiste Daroussin		esac
112b2394e73SBaptiste Daroussin	fi
113b2394e73SBaptiste Daroussin
114b2394e73SBaptiste Daroussin	if [ -n "$PREPEND_MANPATH" ]; then
115b2394e73SBaptiste Daroussin		IFS=:
116b2394e73SBaptiste Daroussin		for path in $PREPEND_MANPATH; do
117b2394e73SBaptiste Daroussin			add_to_manpath "$path"
118b2394e73SBaptiste Daroussin		done
119b2394e73SBaptiste Daroussin		unset IFS
120c535eb59SGordon Tetlow	fi
121c535eb59SGordon Tetlow
122c535eb59SGordon Tetlow	search_path
123c535eb59SGordon Tetlow
124c535eb59SGordon Tetlow	decho "Adding default manpath entries"
125c535eb59SGordon Tetlow	IFS=:
126c535eb59SGordon Tetlow	for path in $man_default_path; do
127c535eb59SGordon Tetlow		add_to_manpath "$path"
128c535eb59SGordon Tetlow	done
129c535eb59SGordon Tetlow	unset IFS
130c535eb59SGordon Tetlow
131c535eb59SGordon Tetlow	parse_configs
132c535eb59SGordon Tetlow
133b2394e73SBaptiste Daroussin	if [ -n "$APPEND_MANPATH" ]; then
134b2394e73SBaptiste Daroussin		IFS=:
135b2394e73SBaptiste Daroussin		for path in $APPEND_MANPATH; do
136b2394e73SBaptiste Daroussin			add_to_manpath "$path"
137b2394e73SBaptiste Daroussin		done
138b2394e73SBaptiste Daroussin		unset IFS
139b2394e73SBaptiste Daroussin	fi
140c535eb59SGordon Tetlow	# Trim leading colon
141c535eb59SGordon Tetlow	MANPATH=${manpath#:}
142c535eb59SGordon Tetlow
143c535eb59SGordon Tetlow	decho "Using manual path: $MANPATH"
144c535eb59SGordon Tetlow}
145c535eb59SGordon Tetlow
146c535eb59SGordon Tetlow# Usage: check_cat catglob
147c535eb59SGordon Tetlow# Checks to see if a cat glob is available.
148c535eb59SGordon Tetlowcheck_cat() {
149c535eb59SGordon Tetlow	if exists "$1"; then
150c535eb59SGordon Tetlow		use_cat=yes
151c535eb59SGordon Tetlow		catpage=$found
152c4368d03SWolfram Schneider		setup_cattool "$catpage"
153c4368d03SWolfram Schneider		decho "    Found catpage \"$catpage\""
154c535eb59SGordon Tetlow		return 0
155c535eb59SGordon Tetlow	else
156c535eb59SGordon Tetlow		return 1
157c535eb59SGordon Tetlow	fi
158c535eb59SGordon Tetlow}
159c535eb59SGordon Tetlow
160c535eb59SGordon Tetlow# Usage: check_man manglob catglob
161c535eb59SGordon Tetlow# Given 2 globs, figures out if the manglob is available, if so, check to
162c535eb59SGordon Tetlow# see if the catglob is also available and up to date.
163c535eb59SGordon Tetlowcheck_man() {
164c535eb59SGordon Tetlow	if exists "$1"; then
165c535eb59SGordon Tetlow		# We have a match, check for a cat page
166c535eb59SGordon Tetlow		manpage=$found
16778948070SWolfram Schneider		setup_cattool "$manpage"
16878948070SWolfram Schneider		decho "    Found manpage \"$manpage\""
169c535eb59SGordon Tetlow
170a0094449SRuslan Ermilov		if [ -n "${use_width}" ]; then
171a0094449SRuslan Ermilov			# non-standard width
172a0094449SRuslan Ermilov			unset use_cat
173a0094449SRuslan Ermilov			decho "    Skipping catpage: non-standard page width"
17478948070SWolfram Schneider		elif exists "$2" && is_newer $found "$manpage"; then
175c535eb59SGordon Tetlow			# cat page found and is newer, use that
176c535eb59SGordon Tetlow			use_cat=yes
177c535eb59SGordon Tetlow			catpage=$found
178c4368d03SWolfram Schneider			setup_cattool "$catpage"
179c4368d03SWolfram Schneider			decho "    Using catpage \"$catpage\""
180c535eb59SGordon Tetlow		else
181c535eb59SGordon Tetlow			# no cat page or is older
182c535eb59SGordon Tetlow			unset use_cat
183c535eb59SGordon Tetlow			decho "    Skipping catpage: not found or old"
184c535eb59SGordon Tetlow		fi
185c535eb59SGordon Tetlow		return 0
186c535eb59SGordon Tetlow	fi
187c535eb59SGordon Tetlow
188c535eb59SGordon Tetlow	return 1
189c535eb59SGordon Tetlow}
190c535eb59SGordon Tetlow
191c535eb59SGordon Tetlow# Usage: decho "string" [debuglevel]
192c535eb59SGordon Tetlow# Echoes to stderr string prefaced with -- if high enough debuglevel.
193c535eb59SGordon Tetlowdecho() {
194c535eb59SGordon Tetlow	if [ $debug -ge ${2:-1} ]; then
195c535eb59SGordon Tetlow		echo "-- $1" >&2
196c535eb59SGordon Tetlow	fi
197c535eb59SGordon Tetlow}
198c535eb59SGordon Tetlow
199c535eb59SGordon Tetlow# Usage: exists glob
200e3c7b76fSWolfram Schneider#
201e3c7b76fSWolfram Schneider# Returns true if glob resolves to a real file and store the first
202e3c7b76fSWolfram Schneider# found filename in the variable $found
203c535eb59SGordon Tetlowexists() {
204*37be4197SWolfram Schneider	if [ -z "$1" ]; then
205*37be4197SWolfram Schneider		return 1
206*37be4197SWolfram Schneider	fi
207*37be4197SWolfram Schneider
208c535eb59SGordon Tetlow	local IFS
209c535eb59SGordon Tetlow
210c535eb59SGordon Tetlow	# Don't accidentally inherit callers IFS (breaks perl manpages)
211c535eb59SGordon Tetlow	unset IFS
212c535eb59SGordon Tetlow
213c535eb59SGordon Tetlow	# Use some globbing tricks in the shell to determine if a file
214c535eb59SGordon Tetlow	# exists or not.
215c535eb59SGordon Tetlow	set +f
216e3c7b76fSWolfram Schneider	for file in "$1"*
217e3c7b76fSWolfram Schneider	do
218e3c7b76fSWolfram Schneider		if [ -r "$file" ]; then
219e3c7b76fSWolfram Schneider			found="$file"
220c535eb59SGordon Tetlow			set -f
221c535eb59SGordon Tetlow			return 0
222c535eb59SGordon Tetlow		fi
223e3c7b76fSWolfram Schneider	done
224e3c7b76fSWolfram Schneider	set -f
225c535eb59SGordon Tetlow
226c535eb59SGordon Tetlow	return 1
227c535eb59SGordon Tetlow}
228c535eb59SGordon Tetlow
229c535eb59SGordon Tetlow# Usage: find_file path section subdir pagename
230c535eb59SGordon Tetlow# Returns: true if something is matched and found.
231c535eb59SGordon Tetlow# Search the given path/section combo for a given page.
232c535eb59SGordon Tetlowfind_file() {
233c535eb59SGordon Tetlow	local manroot catroot mann man0 catn cat0
234c535eb59SGordon Tetlow
235c535eb59SGordon Tetlow	manroot="$1/man$2"
236c535eb59SGordon Tetlow	catroot="$1/cat$2"
237c535eb59SGordon Tetlow	if [ -n "$3" ]; then
238c535eb59SGordon Tetlow		manroot="$manroot/$3"
239c535eb59SGordon Tetlow		catroot="$catroot/$3"
240c535eb59SGordon Tetlow	fi
241c535eb59SGordon Tetlow
242625490e8SBaptiste Daroussin	if [ ! -d "$manroot" -a ! -d "$catroot" ]; then
243c535eb59SGordon Tetlow		return 1
244c535eb59SGordon Tetlow	fi
245c535eb59SGordon Tetlow	decho "  Searching directory $manroot" 2
246c535eb59SGordon Tetlow
247e3c7b76fSWolfram Schneider	mann="$manroot/$4.$2"
248e3c7b76fSWolfram Schneider	man0="$manroot/$4.0"
249e3c7b76fSWolfram Schneider	catn="$catroot/$4.$2"
250e3c7b76fSWolfram Schneider	cat0="$catroot/$4.0"
251c535eb59SGordon Tetlow
252c535eb59SGordon Tetlow	# This is the behavior as seen by the original man utility.
253c535eb59SGordon Tetlow	# Let's not change that which doesn't seem broken.
254c535eb59SGordon Tetlow	if check_man "$mann" "$catn"; then
255c535eb59SGordon Tetlow		return 0
256c535eb59SGordon Tetlow	elif check_man "$man0" "$cat0"; then
257c535eb59SGordon Tetlow		return 0
258c535eb59SGordon Tetlow	elif check_cat "$catn"; then
259c535eb59SGordon Tetlow		return 0
260c535eb59SGordon Tetlow	elif check_cat "$cat0"; then
261c535eb59SGordon Tetlow		return 0
262c535eb59SGordon Tetlow	fi
263c535eb59SGordon Tetlow
264c535eb59SGordon Tetlow	return 1
265c535eb59SGordon Tetlow}
266c535eb59SGordon Tetlow
267c535eb59SGordon Tetlow# Usage: is_newer file1 file2
268c535eb59SGordon Tetlow# Returns true if file1 is newer than file2 as calculated by mtime.
269c535eb59SGordon Tetlowis_newer() {
2709b61837aSUlrich Spörlein	if ! [ "$1" -ot "$2" ]; then
2719b61837aSUlrich Spörlein		decho "    mtime: $1 not older than $2" 3
272c535eb59SGordon Tetlow		return 0
273c535eb59SGordon Tetlow	else
274c535eb59SGordon Tetlow		decho "    mtime: $1 older than $2" 3
275c535eb59SGordon Tetlow		return 1
276c535eb59SGordon Tetlow	fi
277c535eb59SGordon Tetlow}
278c535eb59SGordon Tetlow
279c535eb59SGordon Tetlow# Usage: manpath_parse_args "$@"
280c535eb59SGordon Tetlow# Parses commandline options for manpath.
281c535eb59SGordon Tetlowmanpath_parse_args() {
282c535eb59SGordon Tetlow	local cmd_arg
283c535eb59SGordon Tetlow
284f555b39eSKyle Evans	OPTIND=1
285c535eb59SGordon Tetlow	while getopts 'Ldq' cmd_arg; do
286c535eb59SGordon Tetlow		case "${cmd_arg}" in
287c535eb59SGordon Tetlow		L)	Lflag=Lflag ;;
288c535eb59SGordon Tetlow		d)	debug=$(( $debug + 1 )) ;;
289c535eb59SGordon Tetlow		q)	qflag=qflag ;;
290c535eb59SGordon Tetlow		*)	manpath_usage ;;
291c535eb59SGordon Tetlow		esac
292c535eb59SGordon Tetlow	done >&2
293c535eb59SGordon Tetlow}
294c535eb59SGordon Tetlow
295c535eb59SGordon Tetlow# Usage: manpath_usage
296c535eb59SGordon Tetlow# Display usage for the manpath(1) utility.
297c535eb59SGordon Tetlowmanpath_usage() {
298c535eb59SGordon Tetlow	echo 'usage: manpath [-Ldq]' >&2
299c535eb59SGordon Tetlow	exit 1
300c535eb59SGordon Tetlow}
301c535eb59SGordon Tetlow
302c535eb59SGordon Tetlow# Usage: manpath_warnings
303c535eb59SGordon Tetlow# Display some warnings to stderr.
304c535eb59SGordon Tetlowmanpath_warnings() {
305c535eb59SGordon Tetlow	if [ -n "$Lflag" -a -n "$MANLOCALES" ]; then
306c535eb59SGordon Tetlow		echo "(Warning: MANLOCALES environment variable set)" >&2
307c535eb59SGordon Tetlow	fi
308c535eb59SGordon Tetlow}
309c535eb59SGordon Tetlow
310e85a6f8dSMohamed Akram# Usage: man_check_for_so path
31157cd9717SGordon Tetlow# Returns: True if able to resolve the file, false if it ended in tears.
31257cd9717SGordon Tetlow# Detects the presence of the .so directive and causes the file to be
31357cd9717SGordon Tetlow# redirected to another source file.
31457cd9717SGordon Tetlowman_check_for_so() {
31557cd9717SGordon Tetlow	local IFS line tstr
31657cd9717SGordon Tetlow
31757cd9717SGordon Tetlow	unset IFS
318d9405a92SBaptiste Daroussin	if [ -n "$catpage" ]; then
319d9405a92SBaptiste Daroussin		return 0
320d9405a92SBaptiste Daroussin	fi
32157cd9717SGordon Tetlow
32257cd9717SGordon Tetlow	# We need to loop to accommodate multiple .so directives.
32357cd9717SGordon Tetlow	while true
32457cd9717SGordon Tetlow	do
32514a5c106SWolfram Schneider		line=$($cattool "$manpage" 2>/dev/null | head -n1)
32657cd9717SGordon Tetlow		case "$line" in
32757cd9717SGordon Tetlow		.so*)	trim "${line#.so}"
32857cd9717SGordon Tetlow			decho "$manpage includes $tstr"
32957cd9717SGordon Tetlow			# Glob and check for the file.
330e85a6f8dSMohamed Akram			if ! check_man "$1/$tstr" ""; then
33157cd9717SGordon Tetlow				decho "  Unable to find $tstr"
33257cd9717SGordon Tetlow				return 1
33357cd9717SGordon Tetlow			fi
33457cd9717SGordon Tetlow			;;
33557cd9717SGordon Tetlow		*)	break ;;
33657cd9717SGordon Tetlow		esac
33757cd9717SGordon Tetlow	done
33857cd9717SGordon Tetlow
33957cd9717SGordon Tetlow	return 0
34057cd9717SGordon Tetlow}
34157cd9717SGordon Tetlow
342b43edc06SBaptiste Daroussin# Usage: man_display_page
343b43edc06SBaptiste Daroussin# Display either the manpage or catpage depending on the use_cat variable
344c535eb59SGordon Tetlowman_display_page() {
3451fb816daSBaptiste Daroussin	local IFS pipeline testline
346c535eb59SGordon Tetlow
347c535eb59SGordon Tetlow	# We are called with IFS set to colon. This causes really weird
348c535eb59SGordon Tetlow	# things to happen for the variables that have spaces in them.
349c535eb59SGordon Tetlow	unset IFS
350c535eb59SGordon Tetlow
351c535eb59SGordon Tetlow	# If we are supposed to use a catpage and we aren't using troff(1)
352c535eb59SGordon Tetlow	# just zcat the catpage and we are done.
353c535eb59SGordon Tetlow	if [ -z "$tflag" -a -n "$use_cat" ]; then
354c535eb59SGordon Tetlow		if [ -n "$wflag" ]; then
35578948070SWolfram Schneider			echo "$catpage (source: \"$manpage\")"
356c535eb59SGordon Tetlow			ret=0
357c535eb59SGordon Tetlow		else
358c535eb59SGordon Tetlow			if [ $debug -gt 0 ]; then
359c4368d03SWolfram Schneider				decho "Command: $cattool \"$catpage\" | $MANPAGER"
360c535eb59SGordon Tetlow				ret=0
361c535eb59SGordon Tetlow			else
362b8a484ecSWolfram Schneider				$cattool "$catpage" | $MANPAGER
363c535eb59SGordon Tetlow				ret=$?
364c535eb59SGordon Tetlow			fi
365c535eb59SGordon Tetlow		fi
366c535eb59SGordon Tetlow		return
367c535eb59SGordon Tetlow	fi
368c535eb59SGordon Tetlow
369c535eb59SGordon Tetlow	# Okay, we are using the manpage, do we just need to output the
370c535eb59SGordon Tetlow	# name of the manpage?
371c535eb59SGordon Tetlow	if [ -n "$wflag" ]; then
372c535eb59SGordon Tetlow		echo "$manpage"
373c535eb59SGordon Tetlow		ret=0
374c535eb59SGordon Tetlow		return
375c535eb59SGordon Tetlow	fi
376c535eb59SGordon Tetlow
377d433cf9aSBaptiste Daroussin	if [ -n "$use_width" ]; then
378d433cf9aSBaptiste Daroussin		mandoc_args="-O width=${use_width}"
379d433cf9aSBaptiste Daroussin	fi
380451c2becSBaptiste Daroussin	testline="mandoc -Tlint -Wunsupp >/dev/null 2>&1"
381449a792dSBaptiste Daroussin	if [ -n "$tflag" ]; then
382449a792dSBaptiste Daroussin		pipeline="mandoc -Tps $mandoc_args"
383449a792dSBaptiste Daroussin	else
384d433cf9aSBaptiste Daroussin		pipeline="mandoc $mandoc_args | $MANPAGER"
385449a792dSBaptiste Daroussin	fi
386d6096801SBaptiste Daroussin
387b8a484ecSWolfram Schneider	if ! $cattool "$manpage" | eval "$testline"; then
388f17575acSBaptiste Daroussin		if which -s groff; then
389d6096801SBaptiste Daroussin			man_display_page_groff
390d6096801SBaptiste Daroussin		else
391d6096801SBaptiste Daroussin			echo "This manpage needs groff(1) to be rendered" >&2
392d6096801SBaptiste Daroussin			echo "First install groff(1): " >&2
393d6096801SBaptiste Daroussin			echo "pkg install groff " >&2
394d6096801SBaptiste Daroussin			ret=1
395d6096801SBaptiste Daroussin		fi
396d6096801SBaptiste Daroussin		return
397d6096801SBaptiste Daroussin	fi
398d6096801SBaptiste Daroussin
399d6096801SBaptiste Daroussin	if [ $debug -gt 0 ]; then
400b8a484ecSWolfram Schneider		decho "Command: $cattool \"$manpage\" | eval \"$pipeline\""
401d6096801SBaptiste Daroussin		ret=0
402d6096801SBaptiste Daroussin	else
403b8a484ecSWolfram Schneider		$cattool "$manpage" | eval "$pipeline"
404d6096801SBaptiste Daroussin		ret=$?
405d6096801SBaptiste Daroussin	fi
406d6096801SBaptiste Daroussin}
407d6096801SBaptiste Daroussin
408b43edc06SBaptiste Daroussin# Usage: man_display_page_groff
409b43edc06SBaptiste Daroussin# Display the manpage using groff
410d6096801SBaptiste Daroussinman_display_page_groff() {
411d6096801SBaptiste Daroussin	local EQN NROFF PIC TBL TROFF REFER VGRIND
412d6096801SBaptiste Daroussin	local IFS l nroff_dev pipeline preproc_arg tool
413d6096801SBaptiste Daroussin
414c535eb59SGordon Tetlow	# So, we really do need to parse the manpage. First, figure out the
415c535eb59SGordon Tetlow	# device flag (-T) we have to pass to eqn(1) and groff(1). Then,
416c535eb59SGordon Tetlow	# setup the pipeline of commands based on the user's request.
417c535eb59SGordon Tetlow
418deeff310SGordon Tetlow	# If the manpage is from a particular charset, we need to setup nroff
419deeff310SGordon Tetlow	# to properly output for the correct device.
420deeff310SGordon Tetlow	case "${manpage}" in
421deeff310SGordon Tetlow	*.${man_charset}/*)
422c535eb59SGordon Tetlow		# I don't pretend to know this; I'm just copying from the
423c535eb59SGordon Tetlow		# previous version of man(1).
424c535eb59SGordon Tetlow		case "$man_charset" in
425c535eb59SGordon Tetlow		KOI8-R)		nroff_dev="koi8-r" ;;
426c535eb59SGordon Tetlow		ISO8859-1)	nroff_dev="latin1" ;;
427c535eb59SGordon Tetlow		ISO8859-15)	nroff_dev="latin1" ;;
428c535eb59SGordon Tetlow		UTF-8)		nroff_dev="utf8" ;;
429c535eb59SGordon Tetlow		*)		nroff_dev="ascii" ;;
430c535eb59SGordon Tetlow		esac
431c535eb59SGordon Tetlow
432deeff310SGordon Tetlow		NROFF="$NROFF -T$nroff_dev"
433c535eb59SGordon Tetlow		EQN="$EQN -T$nroff_dev"
434c535eb59SGordon Tetlow
435deeff310SGordon Tetlow		# Iff the manpage is from the locale and not just the charset,
436deeff310SGordon Tetlow		# then we need to define the locale string.
437deeff310SGordon Tetlow		case "${manpage}" in
438deeff310SGordon Tetlow		*/${man_lang}_${man_country}.${man_charset}/*)
439deeff310SGordon Tetlow			NROFF="$NROFF -dlocale=$man_lang.$man_charset"
440deeff310SGordon Tetlow			;;
441deeff310SGordon Tetlow		*/${man_lang}.${man_charset}/*)
442deeff310SGordon Tetlow			NROFF="$NROFF -dlocale=$man_lang.$man_charset"
443deeff310SGordon Tetlow			;;
444deeff310SGordon Tetlow		esac
445deeff310SGordon Tetlow
446c535eb59SGordon Tetlow		# Allow language specific calls to override the default
447c535eb59SGordon Tetlow		# set of utilities.
448c535eb59SGordon Tetlow		l=$(echo $man_lang | tr [:lower:] [:upper:])
449b70e2025SRuslan Ermilov		for tool in EQN NROFF PIC TBL TROFF REFER VGRIND; do
450c535eb59SGordon Tetlow			eval "$tool=\${${tool}_$l:-\$$tool}"
451c535eb59SGordon Tetlow		done
452c535eb59SGordon Tetlow		;;
453c535eb59SGordon Tetlow	*)	NROFF="$NROFF -Tascii"
454c535eb59SGordon Tetlow		EQN="$EQN -Tascii"
455c535eb59SGordon Tetlow		;;
456c535eb59SGordon Tetlow	esac
457c535eb59SGordon Tetlow
458a6a3e856SRuslan Ermilov	if [ -z "$MANCOLOR" ]; then
459a6a3e856SRuslan Ermilov		NROFF="$NROFF -P-c"
460a6a3e856SRuslan Ermilov	fi
461a6a3e856SRuslan Ermilov
462a0094449SRuslan Ermilov	if [ -n "${use_width}" ]; then
463a0094449SRuslan Ermilov		NROFF="$NROFF -rLL=${use_width}n -rLT=${use_width}n"
464a0094449SRuslan Ermilov	fi
465a0094449SRuslan Ermilov
466c535eb59SGordon Tetlow	if [ -n "$MANROFFSEQ" ]; then
467c535eb59SGordon Tetlow		set -- -$MANROFFSEQ
468f555b39eSKyle Evans		OPTIND=1
469c535eb59SGordon Tetlow		while getopts 'egprtv' preproc_arg; do
470c535eb59SGordon Tetlow			case "${preproc_arg}" in
471c535eb59SGordon Tetlow			e)	pipeline="$pipeline | $EQN" ;;
472487ac9acSUlrich Spörlein			g)	;; # Ignore for compatibility.
473c535eb59SGordon Tetlow			p)	pipeline="$pipeline | $PIC" ;;
474c535eb59SGordon Tetlow			r)	pipeline="$pipeline | $REFER" ;;
475b70e2025SRuslan Ermilov			t)	pipeline="$pipeline | $TBL" ;;
476c535eb59SGordon Tetlow			v)	pipeline="$pipeline | $VGRIND" ;;
477c535eb59SGordon Tetlow			*)	usage ;;
478c535eb59SGordon Tetlow			esac
479c535eb59SGordon Tetlow		done
480c535eb59SGordon Tetlow		# Strip the leading " | " from the resulting pipeline.
481c535eb59SGordon Tetlow		pipeline="${pipeline#" | "}"
482c535eb59SGordon Tetlow	else
483c535eb59SGordon Tetlow		pipeline="$TBL"
484c535eb59SGordon Tetlow	fi
485c535eb59SGordon Tetlow
486c535eb59SGordon Tetlow	if [ -n "$tflag" ]; then
487c535eb59SGordon Tetlow		pipeline="$pipeline | $TROFF"
488c535eb59SGordon Tetlow	else
489a6a3e856SRuslan Ermilov		pipeline="$pipeline | $NROFF | $MANPAGER"
490c535eb59SGordon Tetlow	fi
491c535eb59SGordon Tetlow
492c535eb59SGordon Tetlow	if [ $debug -gt 0 ]; then
493b8a484ecSWolfram Schneider		decho "Command: $cattool \"$manpage\" | eval \"$pipeline\""
494c535eb59SGordon Tetlow		ret=0
495c535eb59SGordon Tetlow	else
496b8a484ecSWolfram Schneider		$cattool "$manpage" | eval "$pipeline"
497c535eb59SGordon Tetlow		ret=$?
498c535eb59SGordon Tetlow	fi
499c535eb59SGordon Tetlow}
500c535eb59SGordon Tetlow
501c535eb59SGordon Tetlow# Usage: man_find_and_display page
502c535eb59SGordon Tetlow# Search through the manpaths looking for the given page.
503c535eb59SGordon Tetlowman_find_and_display() {
504c535eb59SGordon Tetlow	local found_page locpath p path sect
505c535eb59SGordon Tetlow
5063d9127f1SGordon Tetlow	# Check to see if it's a file. But only if it has a '/' in
5073d9127f1SGordon Tetlow	# the filename.
5083d9127f1SGordon Tetlow	case "$1" in
5093d9127f1SGordon Tetlow	*/*)	if [ -f "$1" -a -r "$1" ]; then
5103d9127f1SGordon Tetlow			decho "Found a usable page, displaying that"
5113d9127f1SGordon Tetlow			unset use_cat
5123d9127f1SGordon Tetlow			manpage="$1"
51378948070SWolfram Schneider			setup_cattool "$manpage"
514e85a6f8dSMohamed Akram			p=$(cd "$(dirname "$manpage")" && pwd)
515e85a6f8dSMohamed Akram			case "$(basename "$p")" in
516e85a6f8dSMohamed Akram				man*|cat*) p=$p/.. ;;
517e85a6f8dSMohamed Akram				*) p=$p/../.. ;;
518e85a6f8dSMohamed Akram			esac
519e85a6f8dSMohamed Akram			if man_check_for_so "$p"; then
52057cd9717SGordon Tetlow				found_page=yes
5213d9127f1SGordon Tetlow				man_display_page
52257cd9717SGordon Tetlow			fi
5233d9127f1SGordon Tetlow			return
5243d9127f1SGordon Tetlow		fi
5253d9127f1SGordon Tetlow		;;
5263d9127f1SGordon Tetlow	esac
5273d9127f1SGordon Tetlow
528c535eb59SGordon Tetlow	IFS=:
529c535eb59SGordon Tetlow	for sect in $MANSECT; do
530c535eb59SGordon Tetlow		decho "Searching section $sect" 2
531c535eb59SGordon Tetlow		for path in $MANPATH; do
532c535eb59SGordon Tetlow			for locpath in $locpaths; do
533c535eb59SGordon Tetlow				p=$path/$locpath
534c535eb59SGordon Tetlow				p=${p%/.} # Rid ourselves of the trailing /.
535c535eb59SGordon Tetlow
536c535eb59SGordon Tetlow				# Check if there is a MACHINE specific manpath.
537c535eb59SGordon Tetlow				if find_file $p $sect $MACHINE "$1"; then
538e85a6f8dSMohamed Akram					if man_check_for_so $p; then
539c535eb59SGordon Tetlow						found_page=yes
540c535eb59SGordon Tetlow						man_display_page
5411d7c660aSGordon Tetlow						if [ -n "$aflag" ]; then
5421d7c660aSGordon Tetlow							continue 2
5431d7c660aSGordon Tetlow						else
544c535eb59SGordon Tetlow							return
545c535eb59SGordon Tetlow						fi
546c535eb59SGordon Tetlow					fi
54757cd9717SGordon Tetlow				fi
548c535eb59SGordon Tetlow
549c535eb59SGordon Tetlow				# Check if there is a MACHINE_ARCH
550c535eb59SGordon Tetlow				# specific manpath.
551c535eb59SGordon Tetlow				if find_file $p $sect $MACHINE_ARCH "$1"; then
552e85a6f8dSMohamed Akram					if man_check_for_so $p; then
553c535eb59SGordon Tetlow						found_page=yes
554c535eb59SGordon Tetlow						man_display_page
5551d7c660aSGordon Tetlow						if [ -n "$aflag" ]; then
5561d7c660aSGordon Tetlow							continue 2
5571d7c660aSGordon Tetlow						else
558c535eb59SGordon Tetlow							return
559c535eb59SGordon Tetlow						fi
560c535eb59SGordon Tetlow					fi
56157cd9717SGordon Tetlow				fi
562c535eb59SGordon Tetlow
563c535eb59SGordon Tetlow				# Check plain old manpath.
564c535eb59SGordon Tetlow				if find_file $p $sect '' "$1"; then
565e85a6f8dSMohamed Akram					if man_check_for_so $p; then
566c535eb59SGordon Tetlow						found_page=yes
567c535eb59SGordon Tetlow						man_display_page
5681d7c660aSGordon Tetlow						if [ -n "$aflag" ]; then
5691d7c660aSGordon Tetlow							continue 2
5701d7c660aSGordon Tetlow						else
571c535eb59SGordon Tetlow							return
572c535eb59SGordon Tetlow						fi
573c535eb59SGordon Tetlow					fi
57457cd9717SGordon Tetlow				fi
575c535eb59SGordon Tetlow			done
576c535eb59SGordon Tetlow		done
577c535eb59SGordon Tetlow	done
578c535eb59SGordon Tetlow	unset IFS
579c535eb59SGordon Tetlow
580c535eb59SGordon Tetlow	# Nothing? Well, we are done then.
581c535eb59SGordon Tetlow	if [ -z "$found_page" ]; then
58278948070SWolfram Schneider		echo "No manual entry for \"$1\"" >&2
583c535eb59SGordon Tetlow		ret=1
584c535eb59SGordon Tetlow		return
585c535eb59SGordon Tetlow	fi
586c535eb59SGordon Tetlow}
587c535eb59SGordon Tetlow
5888edb6fb5SMohamed Akram# Usage: man_parse_opts "$@"
589c535eb59SGordon Tetlow# Parses commandline options for man.
5908edb6fb5SMohamed Akramman_parse_opts() {
5918edb6fb5SMohamed Akram	local cmd_arg
592c535eb59SGordon Tetlow
593f555b39eSKyle Evans	OPTIND=1
5941594084fSFernando Apesteguía	while getopts 'K:M:P:S:adfhkm:op:tw' cmd_arg; do
595c535eb59SGordon Tetlow		case "${cmd_arg}" in
5961594084fSFernando Apesteguía		K)	Kflag=Kflag
5971594084fSFernando Apesteguía			REGEXP=$OPTARG ;;
598c535eb59SGordon Tetlow		M)	MANPATH=$OPTARG ;;
599a6a3e856SRuslan Ermilov		P)	MANPAGER=$OPTARG ;;
600c535eb59SGordon Tetlow		S)	MANSECT=$OPTARG ;;
601c535eb59SGordon Tetlow		a)	aflag=aflag ;;
602c535eb59SGordon Tetlow		d)	debug=$(( $debug + 1 )) ;;
603c535eb59SGordon Tetlow		f)	fflag=fflag ;;
604c535eb59SGordon Tetlow		h)	man_usage 0 ;;
605c535eb59SGordon Tetlow		k)	kflag=kflag ;;
606c535eb59SGordon Tetlow		m)	mflag=$OPTARG ;;
607c535eb59SGordon Tetlow		o)	oflag=oflag ;;
608c535eb59SGordon Tetlow		p)	MANROFFSEQ=$OPTARG ;;
609c535eb59SGordon Tetlow		t)	tflag=tflag ;;
610c535eb59SGordon Tetlow		w)	wflag=wflag ;;
611c535eb59SGordon Tetlow		*)	man_usage ;;
612c535eb59SGordon Tetlow		esac
613c535eb59SGordon Tetlow	done >&2
614c535eb59SGordon Tetlow
615c535eb59SGordon Tetlow	shift $(( $OPTIND - 1 ))
616c535eb59SGordon Tetlow
617c535eb59SGordon Tetlow	# Check the args for incompatible options.
6181594084fSFernando Apesteguía
6191594084fSFernando Apesteguía	case "${Kflag}${fflag}${kflag}${tflag}${wflag}" in
6201594084fSFernando Apesteguía	Kflagfflag*)	echo "Incompatible options: -K and -f"; man_usage ;;
6211594084fSFernando Apesteguía	Kflag*kflag*)	echo "Incompatible options: -K and -k"; man_usage ;;
6221594084fSFernando Apesteguía	Kflag*tflag)	echo "Incompatible options: -K and -t"; man_usage ;;
623c535eb59SGordon Tetlow	fflagkflag*)	echo "Incompatible options: -f and -k"; man_usage ;;
624c535eb59SGordon Tetlow	fflag*tflag*)	echo "Incompatible options: -f and -t"; man_usage ;;
625c535eb59SGordon Tetlow	fflag*wflag)	echo "Incompatible options: -f and -w"; man_usage ;;
626c535eb59SGordon Tetlow	*kflagtflag*)	echo "Incompatible options: -k and -t"; man_usage ;;
627c535eb59SGordon Tetlow	*kflag*wflag)	echo "Incompatible options: -k and -w"; man_usage ;;
628c535eb59SGordon Tetlow	*tflagwflag)	echo "Incompatible options: -t and -w"; man_usage ;;
629c535eb59SGordon Tetlow	esac
630c535eb59SGordon Tetlow
631c535eb59SGordon Tetlow	# Short circuit for whatis(1) and apropos(1)
632c535eb59SGordon Tetlow	if [ -n "$fflag" ]; then
633c535eb59SGordon Tetlow		do_whatis "$@"
634c535eb59SGordon Tetlow		exit
635c535eb59SGordon Tetlow	fi
636c535eb59SGordon Tetlow
637c535eb59SGordon Tetlow	if [ -n "$kflag" ]; then
638c535eb59SGordon Tetlow		do_apropos "$@"
639c535eb59SGordon Tetlow		exit
640c535eb59SGordon Tetlow	fi
641c535eb59SGordon Tetlow}
642c535eb59SGordon Tetlow
643c535eb59SGordon Tetlow# Usage: man_setup
644c535eb59SGordon Tetlow# Setup various trivial but essential variables.
645c535eb59SGordon Tetlowman_setup() {
646c535eb59SGordon Tetlow	# Setup machine and architecture variables.
647c535eb59SGordon Tetlow	if [ -n "$mflag" ]; then
648c535eb59SGordon Tetlow		MACHINE_ARCH=${mflag%%:*}
649c535eb59SGordon Tetlow		MACHINE=${mflag##*:}
650c535eb59SGordon Tetlow	fi
651c535eb59SGordon Tetlow	if [ -z "$MACHINE_ARCH" ]; then
65282db8a5eSGordon Tetlow		MACHINE_ARCH=$($SYSCTL -n hw.machine_arch)
653c535eb59SGordon Tetlow	fi
654c535eb59SGordon Tetlow	if [ -z "$MACHINE" ]; then
65582db8a5eSGordon Tetlow		MACHINE=$($SYSCTL -n hw.machine)
656c535eb59SGordon Tetlow	fi
657c535eb59SGordon Tetlow	decho "Using architecture: $MACHINE_ARCH:$MACHINE"
658c535eb59SGordon Tetlow
659c535eb59SGordon Tetlow	setup_pager
660c535eb59SGordon Tetlow	build_manpath
6618edb6fb5SMohamed Akram	build_mansect
662c535eb59SGordon Tetlow	man_setup_locale
663a0094449SRuslan Ermilov	man_setup_width
664a0094449SRuslan Ermilov}
665a0094449SRuslan Ermilov
666a0094449SRuslan Ermilov# Usage: man_setup_width
667a0094449SRuslan Ermilov# Set up page width.
668a0094449SRuslan Ermilovman_setup_width() {
669a0094449SRuslan Ermilov	local sizes
670a0094449SRuslan Ermilov
671a0094449SRuslan Ermilov	unset use_width
672a0094449SRuslan Ermilov	case "$MANWIDTH" in
673a0094449SRuslan Ermilov	[0-9]*)
674a0094449SRuslan Ermilov		if [ "$MANWIDTH" -gt 0 2>/dev/null ]; then
675a0094449SRuslan Ermilov			use_width=$MANWIDTH
676a0094449SRuslan Ermilov		fi
677a0094449SRuslan Ermilov		;;
678a0094449SRuslan Ermilov	[Tt][Tt][Yy])
679a0094449SRuslan Ermilov		if { sizes=$($STTY size 0>&3 2>/dev/null); } 3>&1; then
680a0094449SRuslan Ermilov			set -- $sizes
681a0094449SRuslan Ermilov			if [ $2 -gt 80 ]; then
682a0094449SRuslan Ermilov				use_width=$(($2-2))
683a0094449SRuslan Ermilov			fi
684a0094449SRuslan Ermilov		fi
685a0094449SRuslan Ermilov		;;
686a0094449SRuslan Ermilov	esac
687a0094449SRuslan Ermilov	if [ -n "$use_width" ]; then
688a0094449SRuslan Ermilov		decho "Using non-standard page width: ${use_width}"
689a0094449SRuslan Ermilov	else
690a0094449SRuslan Ermilov		decho 'Using standard page width'
691a0094449SRuslan Ermilov	fi
692c535eb59SGordon Tetlow}
693c535eb59SGordon Tetlow
694c535eb59SGordon Tetlow# Usage: man_setup_locale
695c535eb59SGordon Tetlow# Setup necessary locale variables.
696c535eb59SGordon Tetlowman_setup_locale() {
697deeff310SGordon Tetlow	local lang_cc
6989508f8c0SYuri Pankov	local locstr
699deeff310SGordon Tetlow
700deeff310SGordon Tetlow	locpaths='.'
701deeff310SGordon Tetlow	man_charset='US-ASCII'
702deeff310SGordon Tetlow
703c535eb59SGordon Tetlow	# Setup locale information.
704c535eb59SGordon Tetlow	if [ -n "$oflag" ]; then
705deeff310SGordon Tetlow		decho 'Using non-localized manpages'
706deeff310SGordon Tetlow	else
7079508f8c0SYuri Pankov		# Use the locale tool to give us proper locale information
708deeff310SGordon Tetlow		eval $( $LOCALE )
709c535eb59SGordon Tetlow
7109508f8c0SYuri Pankov		if [ -n "$LANG" ]; then
7119508f8c0SYuri Pankov			locstr=$LANG
7129508f8c0SYuri Pankov		else
7139508f8c0SYuri Pankov			locstr=$LC_CTYPE
7149508f8c0SYuri Pankov		fi
7159508f8c0SYuri Pankov
7169508f8c0SYuri Pankov		case "$locstr" in
717deeff310SGordon Tetlow		C)		;;
7189508f8c0SYuri Pankov		C.UTF-8)	;;
719deeff310SGordon Tetlow		POSIX)		;;
720deeff310SGordon Tetlow		[a-z][a-z]_[A-Z][A-Z]\.*)
7219508f8c0SYuri Pankov				lang_cc="${locstr%.*}"
7229508f8c0SYuri Pankov				man_lang="${locstr%_*}"
723deeff310SGordon Tetlow				man_country="${lang_cc#*_}"
7249508f8c0SYuri Pankov				man_charset="${locstr#*.}"
7259508f8c0SYuri Pankov				locpaths="$locstr"
726c535eb59SGordon Tetlow				locpaths="$locpaths:$man_lang.$man_charset"
727c535eb59SGordon Tetlow				if [ "$man_lang" != "en" ]; then
728c535eb59SGordon Tetlow					locpaths="$locpaths:en.$man_charset"
729c535eb59SGordon Tetlow				fi
730c535eb59SGordon Tetlow				locpaths="$locpaths:."
731deeff310SGordon Tetlow				;;
732deeff310SGordon Tetlow		*)		echo 'Unknown locale, assuming C' >&2
733deeff310SGordon Tetlow				;;
734deeff310SGordon Tetlow		esac
735c535eb59SGordon Tetlow	fi
736deeff310SGordon Tetlow
737c535eb59SGordon Tetlow	decho "Using locale paths: $locpaths"
738c535eb59SGordon Tetlow}
739c535eb59SGordon Tetlow
740c535eb59SGordon Tetlow# Usage: man_usage [exitcode]
741c535eb59SGordon Tetlow# Display usage for the man utility.
742c535eb59SGordon Tetlowman_usage() {
743c535eb59SGordon Tetlow	echo 'Usage:'
7441594084fSFernando Apesteguía	echo ' man [-adho] [-t | -w] [-K regexp] [-M manpath] [-P pager] [-S mansect]'
745c535eb59SGordon Tetlow	echo '     [-m arch[:machine]] [-p [eprtv]] [mansect] page [...]'
746c535eb59SGordon Tetlow	echo ' man -f page [...] -- Emulates whatis(1)'
747c535eb59SGordon Tetlow	echo ' man -k page [...] -- Emulates apropos(1)'
748c535eb59SGordon Tetlow
749c535eb59SGordon Tetlow	# When exit'ing with -h, it's not an error.
750c535eb59SGordon Tetlow	exit ${1:-1}
751c535eb59SGordon Tetlow}
752c535eb59SGordon Tetlow
753c535eb59SGordon Tetlow# Usage: parse_configs
754c535eb59SGordon Tetlow# Reads the end-user adjustable config files.
755c535eb59SGordon Tetlowparse_configs() {
756c535eb59SGordon Tetlow	local IFS file files
757c535eb59SGordon Tetlow
758c535eb59SGordon Tetlow	if [ -n "$parsed_configs" ]; then
759c535eb59SGordon Tetlow		return
760c535eb59SGordon Tetlow	fi
761c535eb59SGordon Tetlow
762c535eb59SGordon Tetlow	unset IFS
763c535eb59SGordon Tetlow
764c535eb59SGordon Tetlow	# Read the global config first in case the user wants
765c535eb59SGordon Tetlow	# to override config_local.
766c535eb59SGordon Tetlow	if [ -r "$config_global" ]; then
767c535eb59SGordon Tetlow		parse_file "$config_global"
768c535eb59SGordon Tetlow	fi
769c535eb59SGordon Tetlow
770c535eb59SGordon Tetlow	# Glob the list of files to parse.
771c535eb59SGordon Tetlow	set +f
772c535eb59SGordon Tetlow	files=$(echo $config_local)
773c535eb59SGordon Tetlow	set -f
774c535eb59SGordon Tetlow
775c535eb59SGordon Tetlow	for file in $files; do
776c535eb59SGordon Tetlow		if [ -r "$file" ]; then
777c535eb59SGordon Tetlow			parse_file "$file"
778c535eb59SGordon Tetlow		fi
779c535eb59SGordon Tetlow	done
780c535eb59SGordon Tetlow
781c535eb59SGordon Tetlow	parsed_configs='yes'
782c535eb59SGordon Tetlow}
783c535eb59SGordon Tetlow
784c535eb59SGordon Tetlow# Usage: parse_file file
785c535eb59SGordon Tetlow# Reads the specified config files.
786c535eb59SGordon Tetlowparse_file() {
787c535eb59SGordon Tetlow	local file line tstr var
788c535eb59SGordon Tetlow
789c535eb59SGordon Tetlow	file="$1"
790c535eb59SGordon Tetlow	decho "Parsing config file: $file"
791c535eb59SGordon Tetlow	while read line; do
792c535eb59SGordon Tetlow		decho "  $line" 2
793c535eb59SGordon Tetlow		case "$line" in
794c535eb59SGordon Tetlow		\#*)		decho "    Comment" 3
795c535eb59SGordon Tetlow				;;
796c535eb59SGordon Tetlow		MANPATH*)	decho "    MANPATH" 3
797c535eb59SGordon Tetlow				trim "${line#MANPATH}"
798c535eb59SGordon Tetlow				add_to_manpath "$tstr"
799c535eb59SGordon Tetlow				;;
800c535eb59SGordon Tetlow		MANLOCALE*)	decho "    MANLOCALE" 3
801c535eb59SGordon Tetlow				trim "${line#MANLOCALE}"
802c535eb59SGordon Tetlow				manlocales="$manlocales:$tstr"
803c535eb59SGordon Tetlow				;;
804c535eb59SGordon Tetlow		MANCONFIG*)	decho "    MANCONFIG" 3
805a1528c80SRuslan Ermilov				trim "${line#MANCONFIG}"
806c535eb59SGordon Tetlow				config_local="$tstr"
807c535eb59SGordon Tetlow				;;
8088edb6fb5SMohamed Akram		MANSECT*)	decho "    MANSECT" 3
8098edb6fb5SMohamed Akram				trim "${line#MANSECT}"
8108edb6fb5SMohamed Akram				mansect="$mansect:$tstr"
8118edb6fb5SMohamed Akram				;;
812c535eb59SGordon Tetlow		# Set variables in the form of FOO_BAR
813c535eb59SGordon Tetlow		*_*[\ \	]*)	var="${line%%[\ \	]*}"
814c535eb59SGordon Tetlow				trim "${line#$var}"
815c535eb59SGordon Tetlow				eval "$var=\"$tstr\""
816c535eb59SGordon Tetlow				decho "    Parsed $var" 3
817c535eb59SGordon Tetlow				;;
818c535eb59SGordon Tetlow		esac
819c535eb59SGordon Tetlow	done < "$file"
820c535eb59SGordon Tetlow}
821c535eb59SGordon Tetlow
822c535eb59SGordon Tetlow# Usage: search_path
823c535eb59SGordon Tetlow# Traverse $PATH looking for manpaths.
824c535eb59SGordon Tetlowsearch_path() {
825c535eb59SGordon Tetlow	local IFS p path
826c535eb59SGordon Tetlow
827c535eb59SGordon Tetlow	decho "Searching PATH for man directories"
828c535eb59SGordon Tetlow
829c535eb59SGordon Tetlow	IFS=:
830c535eb59SGordon Tetlow	for path in $PATH; do
831971c1c42STijl Coosemans		if add_to_manpath "$path/man"; then
832c535eb59SGordon Tetlow			:
833c535eb59SGordon Tetlow		elif add_to_manpath "$path/MAN"; then
834c535eb59SGordon Tetlow			:
835c535eb59SGordon Tetlow		else
836c535eb59SGordon Tetlow			case "$path" in
837971c1c42STijl Coosemans			*/bin)	p="${path%/bin}/share/man"
838c535eb59SGordon Tetlow				add_to_manpath "$p"
839971c1c42STijl Coosemans				p="${path%/bin}/man"
84061d5f2d1SBaptiste Daroussin				add_to_manpath "$p"
841c535eb59SGordon Tetlow				;;
842c535eb59SGordon Tetlow			esac
843c535eb59SGordon Tetlow		fi
844c535eb59SGordon Tetlow	done
845c535eb59SGordon Tetlow	unset IFS
846c535eb59SGordon Tetlow
847c535eb59SGordon Tetlow	if [ -z "$manpath" ]; then
848c535eb59SGordon Tetlow		decho '  Unable to find any manpaths, using default'
849c535eb59SGordon Tetlow		manpath=$man_default_path
850c535eb59SGordon Tetlow	fi
851c535eb59SGordon Tetlow}
852c535eb59SGordon Tetlow
853c535eb59SGordon Tetlow# Usage: search_whatis cmd [arglist]
854c535eb59SGordon Tetlow# Do the heavy lifting for apropos/whatis
855c535eb59SGordon Tetlowsearch_whatis() {
856c535eb59SGordon Tetlow	local IFS bad cmd f good key keywords loc opt out path rval wlist
857c535eb59SGordon Tetlow
858c535eb59SGordon Tetlow	cmd="$1"
859c535eb59SGordon Tetlow	shift
860c535eb59SGordon Tetlow
861c535eb59SGordon Tetlow	whatis_parse_args "$@"
862c535eb59SGordon Tetlow
863c535eb59SGordon Tetlow	build_manpath
864c535eb59SGordon Tetlow	build_manlocales
865c535eb59SGordon Tetlow	setup_pager
866c535eb59SGordon Tetlow
867c535eb59SGordon Tetlow	if [ "$cmd" = "whatis" ]; then
868c535eb59SGordon Tetlow		opt="-w"
869c535eb59SGordon Tetlow	fi
870c535eb59SGordon Tetlow
871c535eb59SGordon Tetlow	f='whatis'
872c535eb59SGordon Tetlow
873c535eb59SGordon Tetlow	IFS=:
874c535eb59SGordon Tetlow	for path in $MANPATH; do
875c535eb59SGordon Tetlow		if [ \! -d "$path" ]; then
876c535eb59SGordon Tetlow			decho "Skipping non-existent path: $path" 2
877c535eb59SGordon Tetlow			continue
878c535eb59SGordon Tetlow		fi
879c535eb59SGordon Tetlow
880c535eb59SGordon Tetlow		if [ -f "$path/$f" -a -r "$path/$f" ]; then
881c535eb59SGordon Tetlow			decho "Found whatis: $path/$f"
882c535eb59SGordon Tetlow			wlist="$wlist $path/$f"
883c535eb59SGordon Tetlow		fi
884c535eb59SGordon Tetlow
885c535eb59SGordon Tetlow		for loc in $MANLOCALES; do
886c535eb59SGordon Tetlow			if [ -f "$path/$loc/$f" -a -r "$path/$loc/$f" ]; then
887c535eb59SGordon Tetlow				decho "Found whatis: $path/$loc/$f"
888c535eb59SGordon Tetlow				wlist="$wlist $path/$loc/$f"
889c535eb59SGordon Tetlow			fi
890c535eb59SGordon Tetlow		done
891c535eb59SGordon Tetlow	done
892c535eb59SGordon Tetlow	unset IFS
893c535eb59SGordon Tetlow
894c535eb59SGordon Tetlow	if [ -z "$wlist" ]; then
895c535eb59SGordon Tetlow		echo "$cmd: no whatis databases in $MANPATH" >&2
896c535eb59SGordon Tetlow		exit 1
897c535eb59SGordon Tetlow	fi
898c535eb59SGordon Tetlow
899c535eb59SGordon Tetlow	rval=0
900c535eb59SGordon Tetlow	for key in $keywords; do
901c535eb59SGordon Tetlow		out=$(grep -Ehi $opt -- "$key" $wlist)
902c535eb59SGordon Tetlow		if [ -n "$out" ]; then
903c535eb59SGordon Tetlow			good="$good\\n$out"
904c535eb59SGordon Tetlow		else
905c535eb59SGordon Tetlow			bad="$bad\\n$key: nothing appropriate"
906c535eb59SGordon Tetlow			rval=1
907c535eb59SGordon Tetlow		fi
908c535eb59SGordon Tetlow	done
909c535eb59SGordon Tetlow
910c535eb59SGordon Tetlow	# Strip leading carriage return.
911c535eb59SGordon Tetlow	good=${good#\\n}
912c535eb59SGordon Tetlow	bad=${bad#\\n}
913c535eb59SGordon Tetlow
914c535eb59SGordon Tetlow	if [ -n "$good" ]; then
915ec13a838SMohamed Akram		printf '%b\n' "$good" | $MANPAGER
916c535eb59SGordon Tetlow	fi
917c535eb59SGordon Tetlow
918c535eb59SGordon Tetlow	if [ -n "$bad" ]; then
919ec13a838SMohamed Akram		printf '%b\n' "$bad" >&2
920c535eb59SGordon Tetlow	fi
921c535eb59SGordon Tetlow
922c535eb59SGordon Tetlow	exit $rval
923c535eb59SGordon Tetlow}
924c535eb59SGordon Tetlow
92557cd9717SGordon Tetlow# Usage: setup_cattool page
92657cd9717SGordon Tetlow# Finds an appropriate decompressor based on extension
92757cd9717SGordon Tetlowsetup_cattool() {
92857cd9717SGordon Tetlow	case "$1" in
92957cd9717SGordon Tetlow	*.bz)	cattool='/usr/bin/bzcat' ;;
93057cd9717SGordon Tetlow	*.bz2)	cattool='/usr/bin/bzcat' ;;
931b35ea9baSMohamed Akram	*.gz)	cattool='/usr/bin/gzcat' ;;
93257cd9717SGordon Tetlow	*.lzma)	cattool='/usr/bin/lzcat' ;;
93357cd9717SGordon Tetlow	*.xz)	cattool='/usr/bin/xzcat' ;;
934c8abb673SCameron Katri	*.zst)	cattool='/usr/bin/zstdcat' ;;
93557cd9717SGordon Tetlow	*)	cattool='/usr/bin/zcat -f' ;;
93657cd9717SGordon Tetlow	esac
93757cd9717SGordon Tetlow}
93857cd9717SGordon Tetlow
939c535eb59SGordon Tetlow# Usage: setup_pager
940a6a3e856SRuslan Ermilov# Correctly sets $MANPAGER
941c535eb59SGordon Tetlowsetup_pager() {
942c535eb59SGordon Tetlow	# Setup pager.
943a6a3e856SRuslan Ermilov	if [ -z "$MANPAGER" ]; then
944a6a3e856SRuslan Ermilov		if [ -n "$MANCOLOR" ]; then
945a6a3e856SRuslan Ermilov			MANPAGER="less -sR"
946a6a3e856SRuslan Ermilov		else
947a6a3e856SRuslan Ermilov			if [ -n "$PAGER" ]; then
948a6a3e856SRuslan Ermilov				MANPAGER="$PAGER"
949a6a3e856SRuslan Ermilov			else
95047cc9ee1SAlan Somers				MANPAGER="less -s"
951c535eb59SGordon Tetlow			fi
952a6a3e856SRuslan Ermilov		fi
953a6a3e856SRuslan Ermilov	fi
954a6a3e856SRuslan Ermilov	decho "Using pager: $MANPAGER"
955c535eb59SGordon Tetlow}
956c535eb59SGordon Tetlow
957c535eb59SGordon Tetlow# Usage: trim string
958c535eb59SGordon Tetlow# Trims whitespace from beginning and end of a variable
959c535eb59SGordon Tetlowtrim() {
960c535eb59SGordon Tetlow	tstr=$1
961c535eb59SGordon Tetlow	while true; do
962c535eb59SGordon Tetlow		case "$tstr" in
963c535eb59SGordon Tetlow		[\ \	]*)	tstr="${tstr##[\ \	]}" ;;
964c535eb59SGordon Tetlow		*[\ \	])	tstr="${tstr%%[\ \	]}" ;;
965c535eb59SGordon Tetlow		*)		break ;;
966c535eb59SGordon Tetlow		esac
967c535eb59SGordon Tetlow	done
968c535eb59SGordon Tetlow}
969c535eb59SGordon Tetlow
970c535eb59SGordon Tetlow# Usage: whatis_parse_args "$@"
971c535eb59SGordon Tetlow# Parse commandline args for whatis and apropos.
972c535eb59SGordon Tetlowwhatis_parse_args() {
973c535eb59SGordon Tetlow	local cmd_arg
974f555b39eSKyle Evans	OPTIND=1
975c535eb59SGordon Tetlow	while getopts 'd' cmd_arg; do
976c535eb59SGordon Tetlow		case "${cmd_arg}" in
977c535eb59SGordon Tetlow		d)	debug=$(( $debug + 1 )) ;;
978c535eb59SGordon Tetlow		*)	whatis_usage ;;
979c535eb59SGordon Tetlow		esac
980c535eb59SGordon Tetlow	done >&2
981c535eb59SGordon Tetlow
982c535eb59SGordon Tetlow	shift $(( $OPTIND - 1 ))
983c535eb59SGordon Tetlow
984c535eb59SGordon Tetlow	keywords="$*"
985c535eb59SGordon Tetlow}
986c535eb59SGordon Tetlow
987c535eb59SGordon Tetlow# Usage: whatis_usage
988c535eb59SGordon Tetlow# Display usage for the whatis/apropos utility.
989c535eb59SGordon Tetlowwhatis_usage() {
990c535eb59SGordon Tetlow	echo "usage: $cmd [-d] keyword [...]"
991c535eb59SGordon Tetlow	exit 1
992c535eb59SGordon Tetlow}
993c535eb59SGordon Tetlow
994c535eb59SGordon Tetlow
995c535eb59SGordon Tetlow
996c535eb59SGordon Tetlow# Supported commands
997c535eb59SGordon Tetlowdo_apropos() {
99824ef7420SBaptiste Daroussin	[ $(stat -f %i /usr/bin/man) -ne $(stat -f %i /usr/bin/apropos) ] && \
999772246efSBaptiste Daroussin		exec apropos "$@"
1000c535eb59SGordon Tetlow	search_whatis apropos "$@"
1001c535eb59SGordon Tetlow}
1002c535eb59SGordon Tetlow
10031594084fSFernando Apesteguía# Usage: do_full_search reg_exp
10041594084fSFernando Apesteguía# Do a full search of the regular expression passed
10051594084fSFernando Apesteguía# as parameter in all man pages
10061594084fSFernando Apesteguíado_full_search() {
10071594084fSFernando Apesteguía	local gflags re
10081594084fSFernando Apesteguía	re=${1}
10091594084fSFernando Apesteguía
10101594084fSFernando Apesteguía	# Build grep(1) flags
10111594084fSFernando Apesteguía	gflags="-H"
10121594084fSFernando Apesteguía
10131594084fSFernando Apesteguía	# wflag implies -l for grep(1)
10141594084fSFernando Apesteguía	if [ -n "$wflag" ]; then
10151594084fSFernando Apesteguía		gflags="${gflags} -l"
10161594084fSFernando Apesteguía	fi
10171594084fSFernando Apesteguía
10181594084fSFernando Apesteguía	gflags="${gflags} --label"
10191594084fSFernando Apesteguía
10201594084fSFernando Apesteguía	set +f
10218a5c836bSEd Maste	for mpath in $(echo "${MANPATH}" | tr : '[:blank:]'); do
10228a5c836bSEd Maste		for section in $(echo "${MANSECT}" | tr : '[:blank:]'); do
10231594084fSFernando Apesteguía			for manfile in ${mpath}/man${section}/*.${section}*; do
10241594084fSFernando Apesteguía				mandoc "${manfile}" 2>/dev/null |
10258a5c836bSEd Maste					grep -E ${gflags} "${manfile}" -e "${re}"
10261594084fSFernando Apesteguía			done
10271594084fSFernando Apesteguía		done
10281594084fSFernando Apesteguía	done
10291594084fSFernando Apesteguía	set -f
10301594084fSFernando Apesteguía}
10311594084fSFernando Apesteguía
1032c535eb59SGordon Tetlowdo_man() {
10338edb6fb5SMohamed Akram	local IFS
10348edb6fb5SMohamed Akram
10358edb6fb5SMohamed Akram	man_parse_opts "$@"
10368edb6fb5SMohamed Akram	man_setup
10378edb6fb5SMohamed Akram
10388edb6fb5SMohamed Akram	shift $(( $OPTIND - 1 ))
10398edb6fb5SMohamed Akram	IFS=:
10408edb6fb5SMohamed Akram	for sect in $MANSECT; do
10418edb6fb5SMohamed Akram		if [ "$sect" = "$1" ]; then
10428edb6fb5SMohamed Akram			decho "Detected manual section as first arg: $1"
10438edb6fb5SMohamed Akram			MANSECT="$1"
10448edb6fb5SMohamed Akram			shift
10458edb6fb5SMohamed Akram			break
10468edb6fb5SMohamed Akram		fi
10478edb6fb5SMohamed Akram	done
10488edb6fb5SMohamed Akram	unset IFS
10498edb6fb5SMohamed Akram	pages="$*"
10508edb6fb5SMohamed Akram
10511594084fSFernando Apesteguía	if [ -z "$pages" -a -z "${Kflag}" ]; then
1052c535eb59SGordon Tetlow		echo 'What manual page do you want?' >&2
1053c535eb59SGordon Tetlow		exit 1
1054c535eb59SGordon Tetlow	fi
1055c535eb59SGordon Tetlow
10561594084fSFernando Apesteguía	if [ ! -z "${Kflag}" ]; then
10571594084fSFernando Apesteguía		# Short circuit because -K flag does a sufficiently
10581594084fSFernando Apesteguía		# different thing like not showing the man page at all
10591594084fSFernando Apesteguía		do_full_search "${REGEXP}"
10601594084fSFernando Apesteguía	fi
10611594084fSFernando Apesteguía
10621e82d882SWolfram Schneider	for page in "$@"; do
106378948070SWolfram Schneider		decho "Searching for \"$page\""
1064c535eb59SGordon Tetlow		man_find_and_display "$page"
1065c535eb59SGordon Tetlow	done
1066c535eb59SGordon Tetlow
1067c535eb59SGordon Tetlow	exit ${ret:-0}
1068c535eb59SGordon Tetlow}
1069c535eb59SGordon Tetlow
1070c535eb59SGordon Tetlowdo_manpath() {
1071c535eb59SGordon Tetlow	manpath_parse_args "$@"
1072c535eb59SGordon Tetlow	if [ -z "$qflag" ]; then
1073c535eb59SGordon Tetlow		manpath_warnings
1074c535eb59SGordon Tetlow	fi
1075c535eb59SGordon Tetlow	if [ -n "$Lflag" ]; then
1076c535eb59SGordon Tetlow		build_manlocales
1077c535eb59SGordon Tetlow		echo $MANLOCALES
1078c535eb59SGordon Tetlow	else
1079c535eb59SGordon Tetlow		build_manpath
1080c535eb59SGordon Tetlow		echo $MANPATH
1081c535eb59SGordon Tetlow	fi
1082c535eb59SGordon Tetlow	exit 0
1083c535eb59SGordon Tetlow}
1084c535eb59SGordon Tetlow
1085c535eb59SGordon Tetlowdo_whatis() {
108624ef7420SBaptiste Daroussin	[ $(stat -f %i /usr/bin/man) -ne $(stat -f %i /usr/bin/whatis) ] && \
1087772246efSBaptiste Daroussin		exec whatis "$@"
1088c535eb59SGordon Tetlow	search_whatis whatis "$@"
1089c535eb59SGordon Tetlow}
1090c535eb59SGordon Tetlow
1091aeea395eSUlrich Spörlein# User's PATH setting decides on the groff-suite to pick up.
1092aeea395eSUlrich SpörleinEQN=eqn
1093035f7c9aSWolfram SchneiderNROFF='groff -S -P-h -Wall -mtty-char -mandoc'
1094aeea395eSUlrich SpörleinPIC=pic
1095aeea395eSUlrich SpörleinREFER=refer
1096aeea395eSUlrich SpörleinTBL=tbl
1097035f7c9aSWolfram SchneiderTROFF='groff -S -mandoc'
1098aeea395eSUlrich SpörleinVGRIND=vgrind
1099aeea395eSUlrich Spörlein
1100deeff310SGordon TetlowLOCALE=/usr/bin/locale
1101a0094449SRuslan ErmilovSTTY=/bin/stty
110282db8a5eSGordon TetlowSYSCTL=/sbin/sysctl
1103c535eb59SGordon Tetlow
1104c535eb59SGordon Tetlowdebug=0
110573577bf0SRyan Moellerman_default_sections='1:8:2:3:3lua:n:4:5:6:7:9:l'
1106971c1c42STijl Coosemansman_default_path='/usr/share/man:/usr/share/openssl/man:/usr/local/share/man:/usr/local/man'
110757cd9717SGordon Tetlowcattool='/usr/bin/zcat -f'
1108c535eb59SGordon Tetlow
1109c535eb59SGordon Tetlowconfig_global='/etc/man.conf'
1110c535eb59SGordon Tetlow
1111c535eb59SGordon Tetlow# This can be overridden via a setting in /etc/man.conf.
1112c535eb59SGordon Tetlowconfig_local='/usr/local/etc/man.d/*.conf'
1113c535eb59SGordon Tetlow
1114c535eb59SGordon Tetlow# Set noglobbing for now. I don't want spurious globbing.
1115c535eb59SGordon Tetlowset -f
1116c535eb59SGordon Tetlow
1117c535eb59SGordon Tetlowcase "$0" in
1118c535eb59SGordon Tetlow*apropos)	do_apropos "$@" ;;
1119c535eb59SGordon Tetlow*manpath)	do_manpath "$@" ;;
1120c535eb59SGordon Tetlow*whatis)	do_whatis "$@" ;;
1121c535eb59SGordon Tetlow*)		do_man "$@" ;;
1122c535eb59SGordon Tetlowesac
1123