xref: /titanic_50/usr/src/lib/libshell/common/scripts/shman.sh (revision 4c56998a4a895e2885b4848d6753357edccb6436)
1#!/usr/bin/ksh93
2
3#
4# CDDL HEADER START
5#
6# The contents of this file are subject to the terms of the
7# Common Development and Distribution License (the "License").
8# You may not use this file except in compliance with the License.
9#
10# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11# or http://www.opensolaris.org/os/licensing.
12# See the License for the specific language governing permissions
13# and limitations under the License.
14#
15# When distributing Covered Code, include this CDDL HEADER in each
16# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17# If applicable, add the following below this CDDL HEADER, with the
18# fields enclosed by brackets "[]" replaced with your own identifying
19# information: Portions Copyright [yyyy] [name of copyright owner]
20#
21# CDDL HEADER END
22#
23
24#
25# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
26#
27
28# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant
29export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin
30
31# Make sure all math stuff runs in the "C" locale to avoid problems
32# with alternative # radix point representations (e.g. ',' instead of
33# '.' in de_DE.*-locales). This needs to be set _before_ any
34# floating-point constants are defined in this script).
35if [[ "${LC_ALL}" != "" ]] ; then
36    export \
37        LC_MONETARY="${LC_ALL}" \
38        LC_MESSAGES="${LC_ALL}" \
39        LC_COLLATE="${LC_ALL}" \
40        LC_CTYPE="${LC_ALL}"
41        unset LC_ALL
42fi
43export LC_NUMERIC=C
44
45function fatal_error
46{
47	print -u2 "${progname}: $*"
48	exit 1
49}
50
51function debug_print
52{
53	# don't use "--" here to allow "-f" for formatting
54#	print -u2 "$@"
55	return 0
56}
57
58# Build a list of compound variables calculated from MANPATH and
59# locale which contain...
60# "manpath_element" - the MANPATH element this entry belongs to
61# "dir"             - physical directory of "manpath_element"
62# "sect"            - section (if "manpath_element" is something like /usr/share/man,1b)
63# ... and put the result in the array named by argv[1]
64function enumerate_mandirs
65{
66	nameref md=$1
67	typeset manpath_element dir sect manlang
68	integer i=0
69
70	if [[ "${LC_MESSAGES}" != "" ]] ; then
71		manlang="${LC_MESSAGES}"
72	else
73		manlang="${LANG}"
74	fi
75
76	print -r -- "${MANPATH//:/$'\n'}" | while read manpath_element ; do
77		# strip section from manpath elements like "/usr/share/man,1b"
78		dir="${manpath_element/~(E)(.*),(.*)/\1}"
79		sect="${manpath_element/~(E)(.*),(.*)/\2}"
80		[[ "${sect}" == "${dir}" ]] && sect=""
81
82		if [[ "${manlang}" != "" && -d "${dir}/${manlang}" ]] ; then
83			md+=(
84				manpath_element="${manpath_element}"
85				dir="${dir}/${manlang}"
86				sect="${sect}"
87			)
88		fi
89		if [[ -d "${dir}" ]] ; then
90			md+=(
91				manpath_element="${manpath_element}"
92				dir="${dir}"
93				sect="${sect}"
94			)
95		fi
96	done
97
98	return 0
99}
100
101function enumerate_mansects
102{
103	nameref ms=$1
104	nameref mandir_node=$2
105	typeset mancf="${mandir_node.dir}/man.cf"
106	typeset x s l
107
108	if [[ "${mandir_node.sect}" != "" ]] ; then
109		x="${mandir_node.sect}"
110	elif [[ "${MANSECTS}" != "" ]] ; then
111		x="${MANSECTS//,/$'\n'}"
112	elif [[ -f "${mancf}" && -r "${mancf}" ]] ; then
113		x="$(egrep -v '^#|^[[:space:]]*$' <"${mancf}" | egrep '^MANSECTS=')"
114		x="${x/MANSECTS=}/"
115		x="${x//,/$'\n'}"
116	else
117		x="$(cd "${mandir_node.dir}" ; \
118			ls -1d ~(El)(sman|man).*/ | \
119			while read s ; do \
120				s="${s/~(El)(sman|man)/}" ; \
121				s="${s/~(Er)\//}" ; \
122				print -r -- "$s" ; \
123			done)"
124	fi
125
126	while read l ; do
127		[[ "${l}" != ~(Elr)[[:blank:]]* ]] && ms+=( "${l}" )
128#		print -- "sect=$l"
129	done <<<"${x}"
130
131#	printf "enumerate_mansects: found %d entries.\n" ${#ms[@]}
132
133	return 0
134}
135
136# wrapper around more/less
137function browse_manpage
138{
139	typeset tmpdirname
140	typeset doc_filename="$1"
141	typeset doc_title="$2"
142
143	# squish characters in filename which are not allowed in a filesystem
144	# (currently '/')
145	doc_title="${doc_title//\//}"
146
147	# check if we have "less" installed, if not fall back to /usr/xpg4/bin/more
148	if which less >/dev/null 2>&1 ; then
149		# use "cat" here to avoid that "less" may try funny things
150		cat <"${doc_filename}" | less -I -M $"--prompt=MManual\ page\ ${doc_title}\ ?ltline\ %lt?L/%L.:"
151	else
152		tmpdirname="$(mktemp -t -d "shman_${PPID}_$$_XXXXXX")"
153
154		mkdir -p "${tmpdirname}" || { print -u2 -f $"Couldn't create tmp. dir %s\n" "${tmpdirname}" ; return 1 ; }
155
156		(
157			cd "${tmpdirname}"
158
159			# note: we need to support /dev/stdin
160			cat <"${doc_filename}" >"./${doc_title}"
161
162			/usr/xpg4/bin/more "${doc_title}"
163
164			rm -f "${doc_title}"
165		)
166
167		rmdir "${tmpdirname}"
168	fi
169
170	return 0
171}
172
173# /usr/bin/man <keyword>
174function show_manpage
175{
176	compound -a mandirs
177	integer i
178	integer j
179
180	enumerate_mandirs mandirs
181#	debug_print -- "${mandirs[@]}"
182
183	integer num_mandirs=${#mandirs[@]}
184
185	for ((i=0 ; i < num_mandirs ; i++ )) ; do
186		typeset mandir="${mandirs[i].dir}"
187
188		typeset -a mansects
189		enumerate_mansects mansects "mandirs[$i]"
190
191		integer num_mansects="${#mansects[@]}"
192#		debug_print -- "mansects=${mansects[@]}"
193
194		for ((j=0 ; j < num_mansects ; j++ )) ; do
195			typeset mansect="${mansects[j]}"
196
197			# try 1: SGML manpage
198			typeset match="${mandir}/sman${mansect}/${manname}.${mansect}"
199			if [[ -r "${match}" ]] ; then
200				typeset note nlink
201
202				# follow SGML links if needed (needs rework, including protection against link loops)
203				while true ; do
204					debug_print -f "match: %s\n" "${match}"
205
206					tmp="$(cd "${mandir}" ; LC_MESSAGES=C /usr/lib/sgml/sgml2roff "${match}")"
207					read note nlink <<<"${tmp}"
208
209					if [[ "${note}" == ".so" ]] ; then
210						match="${nlink}"
211					else
212						break
213					fi
214				done
215
216				tbl <<<"${tmp}" | eqn | nroff -u0 -Tlp -man - | col -x | browse_manpage /dev/stdin "${manname}(${mansect})"
217				return 0
218			fi
219
220			# try 2: troff manpage
221			match="${mandir}/man${mansect}/${manname}.${mansect}"
222			if [[ -r "${match}" ]] ; then
223				debug_print -f "match: %s\n" "${match}"
224				tbl <"${match}" | eqn | nroff -u0 -Tlp -man - | col -x | browse_manpage /dev/stdin "${manname}(${mansect})"
225				return 0
226			fi
227		done
228		unset mansects num_mansects
229	done
230
231	printf $"No manual entry for %s.\n" "${manname}"
232	return 0
233}
234
235# /usr/bin/man -l <keyword>
236function list_manpages
237{
238	compound -a mandirs
239
240	enumerate_mandirs mandirs
241	#debug_print -- "${mandirs[@]}"
242
243	integer num_mandirs=${#mandirs[@]}
244
245	for ((i=0 ; i < num_mandirs ; i++ )) ; do
246		typeset mandir="${mandirs[i].dir}"
247
248		typeset -a mansects
249		enumerate_mansects mansects "mandirs[$i]"
250
251		integer num_mansects="${#mansects[@]}"
252#		debug_print -- "mansects=${mansects[@]}"
253
254		for ((j=0 ; j < num_mansects ; j++ )) ; do
255			mansect="${mansects[j]}"
256
257			# try 1: SGML manpage
258			match="${mandir}/sman${mansect}/${manname}.${mansect}"
259			if [[ -r "${match}" ]] ; then
260				printf "%s (%s)\t-M %s\n" "${manname}" "${mansect}" "${mandir}"
261				continue
262			fi
263
264			# try 2: troff manpage
265			match="${mandir}/man${mansect}/${manname}.${mansect}"
266			if [[ -r "${match}" ]] ; then
267				printf "%s (%s)\t-M %s\n" "${manname}" "${mansect}" "${mandir}"
268				continue
269			fi
270		done
271		unset mansects num_mansects
272	done
273
274	return 0
275}
276
277# /usr/bin/appropos
278function list_keywords
279{
280	typeset -a mandirs
281	typeset name namesec title
282
283	enumerate_mandirs mandirs
284	#debug_print -- "${mandirs[@]}"
285
286	integer num_mandirs=${#mandirs[@]}
287
288	for ((i=0 ; i < num_mandirs ; i++ )) ; do
289		typeset mandir="${mandirs[i].dir}"
290		typeset windexfile="${mandir}/windex"
291
292		if [[ ! -r "${windexfile}" ]] ; then
293			print -u2 -f $"%s: Can't open %s.\n" "${progname}" "${windexfile}"
294			continue
295		fi
296
297		while IFS=$'\t' read name namesec title ; do
298			if [[ "${name}${namesec}${title}" == ~(Fi)${manname} ]] ; then
299				printf "%s\t%s\t%s\n" "${name}" "${namesec}" "${title}"
300			fi
301		done <"${windexfile}"
302	done
303
304	return 0
305}
306
307function usage
308{
309	OPTIND=0
310	getopts -a "${progname}" "${man_usage}" OPT '-?'
311	exit 2
312}
313
314# program start
315builtin basename
316builtin cat
317builtin date
318
319typeset progname="$(basename "${0}")"
320
321typeset -r man_usage=$'+
322[-?\n@(#)\$Id: shman (Roland Mainz) 2009-12-02 \$\n]
323[-author?Roland Mainz <roland.mainz@nrubsig.org>]
324[-author?Roland Mainz <roland.mainz@sun.com>]
325[+NAME?man - find and display reference manual pages]
326[+DESCRIPTION?The man command displays information from the reference
327	manuals. It displays complete manual pages that you select
328	by name, or one-line summaries selected  either  by  keyword
329	(-k), or by the name of an associated file (-f). If no
330	manual page is located, man prints an error message.]
331[+?write me.]
332[k:keyword?Prints out one-line summaries from the windex database (table of contents) that
333	contain any of the given  keywords. The windex database is created using
334	catman(1M).]
335[l:list?Lists all manual  pages  found  matching name within the search path.]
336[M:mpath?Specifies an alternate search  path  for manual  pages. path is a colon-separated
337	list of directories that contain  manual page directory subtrees. For example, if
338	path  is  /usr/share/man:/usr/local/man, man  searches  for  name in the standard
339	location, and then /usr/local/man.  When used  with  the -k or -f options, the -M
340	option must appear first. Each directory in  the  path is assumed to contain subdirectories of the form man* or sman*  ,
341	one  for each section. This option overrides the MANPATH environment variable.]:[path]
342[s:section?Specifies sections of the manual for man to search. The directories searched for
343	name are limited to those specified by section. section can be a numerical
344	digit, perhaps followed by one or more letters to match the desired section of
345	the manual, for example,  "3libucb". Also, section can be a word, for example,
346	local, new, old, public. section can also be a letter.
347	To specify multiple sections, separate each section with
348	a comma. This option overrides the MANPATH environment variable and the man.cf
349	file.
350	See Search Path below for an explanation of how man conducts its search.]:[section]
351
352name
353
354[+SEE ALSO?\bksh93\b(1), \bman\b(1)]
355'
356
357typeset do_list=false
358typeset do_keyword=false
359
360while getopts -a "${progname}" "${man_usage}" OPT ; do
361#    printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|"
362	case ${OPT} in
363		M)	MANPATH="${OPTARG}"	;;
364		l)	do_list=true		;;
365		k)	do_keyword=true		;;
366		s)	MANSECTS="${OPTARG}" 	;;
367		*)	usage			;;
368	esac
369done
370shift $((OPTIND-1))
371
372# cd /usr/man; LC_MESSAGES=C /usr/lib/sgml/sgml2roff /usr/man/sman1as/asadmin-list-timers.1as  | tbl | eqn | nroff -u0 -Tlp -man -  | col -x > /tmp/mpLQaqac
373
374# prechecks
375(( $# > 0 )) || usage
376
377# process arguments
378while (( $# > 0 )) ; do
379	typeset manname="$1"
380	shift
381
382	debug_print -f "# searching for %s ...\n" "${manname}"
383
384	if ${do_keyword} ; then
385		list_keywords
386	elif ${do_list} ; then
387		list_manpages
388	else
389		show_manpage
390	fi
391done
392
393# todo: better exit codes
394exit 0
395# EOF.
396