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