mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
ksh93/fun: add 'man' function, making builtins help easy to use
This adds a new 'man' function in src/cmd/ksh93/fun/man, meant for autoload. This integrates the --man self-documentation of ksh built-in and external AST commands with your system's 'man' command so you can conveniently use 'man' for all commands, whether built-in or external. See the file for details.
This commit is contained in:
parent
3e79027cd1
commit
663606866e
3 changed files with 181 additions and 0 deletions
5
NEWS
5
NEWS
|
@ -5,6 +5,11 @@ Any uppercase BUG_* names are modernish shell bug IDs.
|
|||
|
||||
2022-07-24:
|
||||
|
||||
- Add new 'man' function in src/cmd/ksh93/fun/man. This integrates the --man
|
||||
self-documentation of ksh built-in and external AST commands with your
|
||||
system's 'man' command so you can conveniently use 'man' for all commands,
|
||||
whether built-in or external. See the file for details.
|
||||
|
||||
- New feature to make 'set -b'/'set -o notify' more usable. When that option
|
||||
is on (and either the vi or emacs/gmacs line editor is in use), 'Done' and
|
||||
similar notifications from completed background jobs no longer mess up the
|
||||
|
|
|
@ -1447,6 +1447,12 @@ make install
|
|||
done fun/dirs
|
||||
exec - test '' = 'fun/dirs' || ${STDCMP} 2>/dev/null -s fun/dirs ${INSTALLROOT}/fun/dirs || { ${STDMV} ${INSTALLROOT}/fun/dirs ${INSTALLROOT}/fun/dirs.old 2>/dev/null || true; ${STDCP} fun/dirs ${INSTALLROOT}/fun/dirs && chmod ugo+x ${INSTALLROOT}/fun/dirs ;}
|
||||
done ${INSTALLROOT}/fun/dirs generated
|
||||
make ${INSTALLROOT}/fun/man
|
||||
prev ${INSTALLROOT}/fun
|
||||
make fun/man
|
||||
done fun/man
|
||||
exec - ${STDCMP} 2>/dev/null -s fun/man ${INSTALLROOT}/fun/man || { ${STDMV} ${INSTALLROOT}/fun/man ${INSTALLROOT}/fun/man.old 2>/dev/null || true; ${STDCP} fun/man ${INSTALLROOT}/fun/man && chmod ugo+x ${INSTALLROOT}/fun/man ;}
|
||||
done ${INSTALLROOT}/fun/man generated
|
||||
make ${INSTALLROOT}/fun/popd
|
||||
make fun/popd
|
||||
done fun/popd
|
||||
|
|
170
src/cmd/ksh93/fun/man
Executable file
170
src/cmd/ksh93/fun/man
Executable file
|
@ -0,0 +1,170 @@
|
|||
########################################################################
|
||||
# #
|
||||
# This file is part of the ksh 93u+m package #
|
||||
# Copyright (c) 2021-2022 Contributors to ksh 93u+m #
|
||||
# <https://github.com/ksh93/ksh> #
|
||||
# and is licensed under the #
|
||||
# Eclipse Public License, Version 1.0 #
|
||||
# #
|
||||
# A copy of the License is available at #
|
||||
# http://www.eclipse.org/org/documents/epl-v10.html #
|
||||
# (with md5 checksum b35adb5213ca9657e911e9befb180842) #
|
||||
# #
|
||||
# Martijn Dekker <martijn@inlv.org> #
|
||||
# #
|
||||
########################################################################
|
||||
|
||||
# This function integrates AST commands' --man self-documentation into the
|
||||
# general 'man' command, making it a great deal easier to use. Your pager from
|
||||
# $PAGER is automatically used if set, otherwise it tries 'less -R'. This works
|
||||
# for both ksh built-in commands and external commands with --man.
|
||||
#
|
||||
# For commands that do not have AST --man self-documentation, it passes control
|
||||
# to your regular 'man' command so you won't notice a difference. Result: you
|
||||
# can just use 'man somecommand' for everything.
|
||||
#
|
||||
# This function only handles 'man' commands with a single argument. If more
|
||||
# or less than one argument is given, it passes control to the system's 'man'.
|
||||
#
|
||||
# For path-bound built-ins (the ones starting with /opt/ast/bin in the output
|
||||
# of the 'builtin' command without arguments), each built-in --man page only
|
||||
# overrides the regular one if the built-in command would actually be executed
|
||||
# according to the value of $PATH. For external commands, the regular man page
|
||||
# is searched before looking for AST --man self-documentation. For path-bound
|
||||
# built-in commands and external commands with AST --man, you can also give it
|
||||
# the full pathname, e.g., 'man /opt/ast/bin/cat' or 'man /usr/bin/shcomp'.
|
||||
#
|
||||
# Recommended usage:
|
||||
# 1. Drop this file in a directory in your $FPATH.
|
||||
# 2. Add 'autoload man' to your ~/.kshrc to override external 'man'.
|
||||
# Or to try it out, just 'dot'/source this file manually.
|
||||
#
|
||||
# The code below illustrates how defining functions in a dedicated ksh
|
||||
# namespace can be used to compartmentalise code, making it more readable and
|
||||
# easier to understand. The basic idea is fairly simple: each function and
|
||||
# variable name N within 'namespace man' is actually treated as '.man.N'. This
|
||||
# allows using simple and readable names without conflicting with other code
|
||||
# using the same names.
|
||||
|
||||
namespace man
|
||||
{
|
||||
# Check for a built-in with --man, i.e., if:
|
||||
# - 'whence -t' says it is a built-in;
|
||||
# - it is not :, true, false, or echo;
|
||||
# - the name or path we would excute appears in output of 'builtin'.
|
||||
# This way, path-bound built-ins' --man is only used if found in $PATH.
|
||||
|
||||
builtin_has_selfdoc()
|
||||
{
|
||||
typeset LF=$'\n' # linefeed/newline shorthand
|
||||
[[ $(whence -t -- "$1") == builtin ]] \
|
||||
&& [[ ! $1 =~ ^(:|true|false|echo)$ \
|
||||
&& $LF$(builtin)$LF == *"$LF$(whence -- "$1")$LF"* ]]
|
||||
}
|
||||
|
||||
# Check if a binary or script has --man self-documentation by grepping
|
||||
# for an AST optget(3) usage string.
|
||||
|
||||
extcmd_has_selfdoc()
|
||||
{
|
||||
typeset p=$(whence -p -- "$1")
|
||||
[[ $p == /* ]] \
|
||||
&& LC_ALL=C grep -q '\[+NAME?' "$p" \
|
||||
&& LC_ALL=C grep -q '\[+DESCRIPTION?' "$p"
|
||||
}
|
||||
|
||||
# Show the self-documentation.
|
||||
# Exporting ERROR_OPTIONS tells ksh to emit pretty-printing escape
|
||||
# codes even if standard error is not on a terminal. Note that, in
|
||||
# fact, all --man output is a glorified error message! Strange but
|
||||
# true. So to capture the output, we need to redirect standard error to
|
||||
# standard ouput (2>&1). Also, 'test' and '[' need special invocations.
|
||||
# Also note: '--??man' is safer as some programs may override --man;
|
||||
# see any_builtin --??help (e.g., 'whence --??help') for more info.
|
||||
|
||||
show_selfdoc()
|
||||
{
|
||||
typeset -x ERROR_OPTIONS=emphasis
|
||||
case $1 in
|
||||
test ) command test '--??man' -- ;;
|
||||
[ ) command [ '--??man' -- ] ;;
|
||||
* ) command "$1" '--??man' ;;
|
||||
esac 2>&1
|
||||
}
|
||||
|
||||
# Run the user's configured pager. Unset IFS to get default split on
|
||||
# space/tab/newline, and disable globbing to avoid processing '*', '?'
|
||||
# etc., then expand and run the command from $PAGER, defaulting to
|
||||
# 'less -R' if it's unset or empty.
|
||||
|
||||
pager()
|
||||
{
|
||||
typeset IFS # local default split...
|
||||
set -o noglob # ...and no pathname expansion...
|
||||
${PAGER:-less -R} # ...so we can safely split $PAGER
|
||||
}
|
||||
|
||||
# Function to try if a system manual page in a section exists. If it
|
||||
# does, it is shown as normal, otherwise the function returns false.
|
||||
# On some systems 2>/dev/null will make man(1) misbehave if the page
|
||||
# is found, so erase the error message after the fact.
|
||||
|
||||
try_os_man()
|
||||
{
|
||||
if ! command man -s "$1" "$2"; then
|
||||
print -n $'\r\E[A\E[K' # erase man's error msg
|
||||
false
|
||||
fi
|
||||
}
|
||||
|
||||
# The main function puts it all together. When given a single argument,
|
||||
# it shows manual pages in the following order of preference:
|
||||
# 1. --man self-documentation of built-in commands;
|
||||
# 2. regular section 1 and section 8 manual pages (for example, we
|
||||
# probably prefer the full ksh.1 manual page over 'ksh --man');
|
||||
# 3. --man self-documentation of external commands;
|
||||
# 4. regular manual pages in other sections (programming manual).
|
||||
|
||||
main()
|
||||
{
|
||||
if (($# != 1)); then
|
||||
command man "$@"
|
||||
elif builtin_has_selfdoc "$1"; then
|
||||
show_selfdoc "$1" | pager
|
||||
elif try_os_man 1 "$1" || try_os_man 8 "$1"; then
|
||||
:
|
||||
elif extcmd_has_selfdoc "$1"; then
|
||||
show_selfdoc "$1" | pager
|
||||
else
|
||||
command man "$1"
|
||||
fi
|
||||
}
|
||||
}
|
||||
|
||||
# The main function simply invokes 'main' within the namespace. Because 'man'
|
||||
# cannot not itself be in the namespace block, it has to invoke the full
|
||||
# canonical name, '.man.main'.
|
||||
# (If we moved 'man' into the namespace block, it would actually be called
|
||||
# '.man.man' and have to be invoked as that, making it fairly useless.)
|
||||
#
|
||||
# Scoping note: The functions within the namespace all use the POSIX name()
|
||||
# syntax, which means they do not have their own local scope. However, 'man' is
|
||||
# defined with the 'function' keyword, which gives it a local scope. Invoking
|
||||
# the POSIX functions from 'man' makes them share its scope, which means any
|
||||
# local variables defined and shell options set within those POSIX functions
|
||||
# are made local to the 'man' function and shared between the POSIX functions
|
||||
# but not outside them.
|
||||
|
||||
function man
|
||||
{
|
||||
.man.main "$@"
|
||||
}
|
||||
|
||||
|
||||
# Do a check at autoload time. We depend on a non-buggy 'whence -t' option
|
||||
# (the ksh2020 version is broken; it claims path-bound builtins are files)
|
||||
|
||||
if ((.sh.version < 20211227)); then
|
||||
print "WARNING: this man wrapper function requires ksh 93u+m 2021-12-27 or later" >&2
|
||||
sleep 1
|
||||
fi
|
Loading…
Add table
Add a link
Reference in a new issue