#!/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.