shman.sh revision 7c2fbfb345896881c631598ee3852ce9ce33fb07
#!/usr/bin/ksh93
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant
export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin
# Make sure all math stuff runs in the "C" locale to avoid problems
# with alternative # radix point representations (e.g. ',' instead of
# '.' in de_DE.*-locales). This needs to be set _before_ any
# floating-point constants are defined in this script).
if [[ "${LC_ALL}" != "" ]] ; then
export \
LC_MONETARY="${LC_ALL}" \
LC_MESSAGES="${LC_ALL}" \
LC_COLLATE="${LC_ALL}" \
LC_CTYPE="${LC_ALL}"
unset LC_ALL
fi
export LC_NUMERIC=C
function fatal_error
{
print -u2 "${progname}: $*"
exit 1
}
function debug_print
{
# don't use "--" here to allow "-f" for formatting
# print -u2 "$@"
return 0
}
# Build a list of compound variables calculated from MANPATH and
# locale which contain...
# "manpath_element" - the MANPATH element this entry belongs to
# "dir" - physical directory of "manpath_element"
# "sect" - section (if "manpath_element" is something like /usr/share/man,1b)
# ... and put the result in the array named by argv[1]
function enumerate_mandirs
{
nameref md=$1
typeset manpath_element dir sect manlang
integer i=0
if [[ "${LC_MESSAGES}" != "" ]] ; then
manlang="${LC_MESSAGES}"
else
manlang="${LANG}"
fi
print -r -- "${MANPATH//:/$'\n'}" | while read manpath_element ; do
# strip section from manpath elements like "/usr/share/man,1b"
dir="${manpath_element/~(E)(.*),(.*)/\1}"
sect="${manpath_element/~(E)(.*),(.*)/\2}"
[[ "${sect}" == "${dir}" ]] && sect=""
if [[ "${manlang}" != "" && -d "${dir}/${manlang}" ]] ; then
md+=(
manpath_element="${manpath_element}"
dir="${dir}/${manlang}"
sect="${sect}"
)
fi
if [[ -d "${dir}" ]] ; then
md+=(
manpath_element="${manpath_element}"
dir="${dir}"
sect="${sect}"
)
fi
done
return 0
}
function enumerate_mansects
{
nameref ms=$1
nameref mandir_node=$2
typeset mancf="${mandir_node.dir}/man.cf"
typeset x s l
if [[ "${mandir_node.sect}" != "" ]] ; then
x="${mandir_node.sect}"
elif [[ "${MANSECTS}" != "" ]] ; then
x="${MANSECTS//,/$'\n'}"
elif [[ -f "${mancf}" && -r "${mancf}" ]] ; then
x="$(egrep -v '^#|^[[:space:]]*$' <"${mancf}" | egrep '^MANSECTS=')"
x="${x/MANSECTS=}/"
x="${x//,/$'\n'}"
else
x="$(cd "${mandir_node.dir}" ; \
ls -1d ~(El)(sman|man).*/ | \
while read s ; do \
s="${s/~(El)(sman|man)/}" ; \
s="${s/~(Er)\//}" ; \
print -r -- "$s" ; \
done)"
fi
while read l ; do
[[ "${l}" != ~(Elr)[[:blank:]]* ]] && ms+=( "${l}" )
# print -- "sect=$l"
done <<<"${x}"
# printf "enumerate_mansects: found %d entries.\n" ${#ms[@]}
return 0
}
# wrapper around more/less
function browse_manpage
{
typeset tmpdirname
typeset doc_filename="$1"
typeset doc_title="$2"
# squish characters in filename which are not allowed in a filesystem
# (currently '/')
doc_title="${doc_title//\//}"
# check if we have "less" installed, if not fall back to /usr/xpg4/bin/more
if which less >/dev/null 2>&1 ; then
# use "cat" here to avoid that "less" may try funny things
cat <"${doc_filename}" | less -I -M $"--prompt=MManual\ page\ ${doc_title}\ ?ltline\ %lt?L/%L.:"
else
tmpdirname="$(mktemp -d "/tmp/shman_${PPID}_$$_XXXXXX")"
mkdir -p "${tmpdirname}" || { print -u2 -f $"Couldn't create tmp. dir %s\n" "${tmpdirname}" ; return 1 ; }
(
cd "${tmpdirname}"
# note: we need to support /dev/stdin
cat <"${doc_filename}" >"./${doc_title}"
/usr/xpg4/bin/more "${doc_title}"
rm -f "${doc_title}"
)
rmdir "${tmpdirname}"
fi
return 0
}
# /usr/bin/man <keyword>
function show_manpage
{
typeset -a -C mandirs
integer i
integer j
enumerate_mandirs mandirs
# debug_print -- "${mandirs[@]}"
integer num_mandirs=${#mandirs[@]}
for ((i=0 ; i < num_mandirs ; i++ )) ; do
typeset mandir="${mandirs[i].dir}"
typeset -a mansects
enumerate_mansects mansects "mandirs[$i]"
integer num_mansects="${#mansects[@]}"
# debug_print -- "mansects=${mansects[@]}"
for ((j=0 ; j < num_mansects ; j++ )) ; do
typeset mansect="${mansects[j]}"
# try 1: SGML manpage
typeset match="${mandir}/sman${mansect}/${manname}.${mansect}"
if [[ -r "${match}" ]] ; then
typeset note nlink
# follow SGML links if needed (needs rework, including protection against link loops)
while true ; do
debug_print -f "match: %s\n" "${match}"
tmp="$(cd "${mandir}" ; LC_MESSAGES=C /usr/lib/sgml/sgml2roff "${match}")"
read note nlink <<<"${tmp}"
if [[ "${note}" == ".so" ]] ; then
match="${nlink}"
else
break
fi
done
tbl <<<"${tmp}" | eqn | nroff -u0 -Tlp -man - | col -x | browse_manpage /dev/stdin "${manname}(${mansect})"
return 0
fi
# try 2: troff manpage
match="${mandir}/man${mansect}/${manname}.${mansect}"
if [[ -r "${match}" ]] ; then
debug_print -f "match: %s\n" "${match}"
tbl <"${match}" | eqn | nroff -u0 -Tlp -man - | col -x | browse_manpage /dev/stdin "${manname}(${mansect})"
return 0
fi
done
unset mansects num_mansects
done
printf $"No manual entry for %s.\n" "${manname}"
return 0
}
# /usr/bin/man -l <keyword>
function list_manpages
{
typeset -a -C mandirs
enumerate_mandirs mandirs
#debug_print -- "${mandirs[@]}"
integer num_mandirs=${#mandirs[@]}
for ((i=0 ; i < num_mandirs ; i++ )) ; do
typeset mandir="${mandirs[i].dir}"
typeset -a mansects
enumerate_mansects mansects "mandirs[$i]"
integer num_mansects="${#mansects[@]}"
# debug_print -- "mansects=${mansects[@]}"
for ((j=0 ; j < num_mansects ; j++ )) ; do
mansect="${mansects[j]}"
# try 1: SGML manpage
match="${mandir}/sman${mansect}/${manname}.${mansect}"
if [[ -r "${match}" ]] ; then
printf "%s (%s)\t-M %s\n" "${manname}" "${mansect}" "${mandir}"
continue
fi
# try 2: troff manpage
match="${mandir}/man${mansect}/${manname}.${mansect}"
if [[ -r "${match}" ]] ; then
printf "%s (%s)\t-M %s\n" "${manname}" "${mansect}" "${mandir}"
continue
fi
done
unset mansects num_mansects
done
return 0
}
# /usr/bin/appropos
function list_keywords
{
typeset -a mandirs
typeset name namesec title
enumerate_mandirs mandirs
#debug_print -- "${mandirs[@]}"
integer num_mandirs=${#mandirs[@]}
for ((i=0 ; i < num_mandirs ; i++ )) ; do
typeset mandir="${mandirs[i].dir}"
typeset windexfile="${mandir}/windex"
if [[ ! -r "${windexfile}" ]] ; then
print -u2 -f $"%s: Can't open %s.\n" "${progname}" "${windexfile}"
continue
fi
while IFS=$'\t' read name namesec title ; do
if [[ "${name}${namesec}${title}" == ~(Fi)${manname} ]] ; then
printf "%s\t%s\t%s\n" "${name}" "${namesec}" "${title}"
fi
done <"${windexfile}"
done
return 0
}
function usage
{
OPTIND=0
getopts -a "${progname}" "${man_usage}" OPT '-?'
exit 2
}
# program start
builtin basename
builtin cat
builtin date
typeset progname="$(basename "${0}")"
typeset -r man_usage=$'+
[-?\n@(#)\$Id: shman (Roland Mainz) 2008-10-14 \$\n]
[-author?Roland Mainz <roland.mainz@nrubsig.org>]
[-author?Roland Mainz <roland.mainz@sun.com>]
[+NAME?man - find and display reference manual pages]
[+DESCRIPTION?The man command displays information from the reference
manuals. It displays complete manual pages that you select
by name, or one-line summaries selected either by keyword
(-k), or by the name of an associated file (-f). If no
manual page is located, man prints an error message.]
[+?write me.]
[k:keyword?Prints out one-line summaries from the windex database (table of contents) that
contain any of the given keywords. The windex database is created using
catman(1M).]
[l:list?Lists all manual pages found matching name within the search path.]
[M:mpath?Specifies an alternate search path for manual pages. path is a colon-separated
list of directories that contain manual page directory subtrees. For example, if
path is /usr/share/man:/usr/local/man, man searches for name in the standard
location, and then /usr/local/man. When used with the -k or -f options, the -M
option must appear first. Each directory in the path is assumed to contain subdirectories of the form man* or sman* ,
one for each section. This option overrides the MANPATH environment variable.]:[path]
[s:section?Specifies sections of the manual for man to search. The directories searched for
name are limited to those specified by section. section can be a numerical
digit, perhaps followed by one or more letters to match the desired section of
the manual, for example, "3libucb". Also, section can be a word, for example,
local, new, old, public. section can also be a letter.
To specify multiple sections, separate each section with
a comma. This option overrides the MANPATH environment variable and the man.cf
file.
See Search Path below for an explanation of how man conducts its search.]:[section]
name
[+SEE ALSO?\bksh93\b(1), \bman\b(1)]
'
typeset do_list=false
typeset do_keyword=false
while getopts -a "${progname}" "${man_usage}" OPT ; do
# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|"
case ${OPT} in
M) MANPATH="${OPTARG}" ;;
l) do_list=true ;;
k) do_keyword=true ;;
s) MANSECTS="${OPTARG}" ;;
*) usage ;;
esac
done
shift $((OPTIND-1))
# 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
typeset manname="$1"
debug_print -f "# searching for %s ...\n" "${manname}"
if ${do_keyword} ; then
list_keywords
elif ${do_list} ; then
list_manpages
else
show_manpage
fi
# todo: better exit codes
exit 0
# EOF.