1
0
Fork 0
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:
Martijn Dekker 2022-07-24 10:17:17 +02:00
parent 3e79027cd1
commit 663606866e
3 changed files with 181 additions and 0 deletions

5
NEWS
View file

@ -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

View file

@ -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
View 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