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