summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwlemb <wlemb>2002-10-07 09:10:10 +0000
committerwlemb <wlemb>2002-10-07 09:10:10 +0000
commit0a718093b72d36a306d8954d4df78d507828d023 (patch)
treef193c467f114321cf70607839577cc516b41cfd3
parentd0768118ab6175d6b9f748856dcf23f6ce76bd90 (diff)
downloadgroff-0a718093b72d36a306d8954d4df78d507828d023.tar.gz
* groffer.sh: replace `sed' interface by direct `sed'
- This improves the performance of the shell programming parts and shortens the groffer script by about 5%. - Remove functions: string_del_append(), string_del_leading(), string_del_trailing(), string_flatten(), string_get_before(), string_get_leading(), string_replace_all(), string_sed_s(), and their auxiliary functions. - Replace all calls of these functions by direct `sed' commands. - Define variables for special characters to ease `sed' calls. - Remove `$APPEND'. - Restrict list_from_string() to single character separators. - Correct list_check() and base_name(). - Add comments to all calls of `sed'. * groffer.sh: add run-time support for several shells - New option `--shell': stop execution and rerun groffer under the shell specified in the argument of `--shell'. - If no shell was specified at run-time, `ash' is tried first; if `ash' is not available continue with the shell with which groffer was called from the command line, or with the shell name in the first line of the script, actually `/bin/sh'.
-rw-r--r--contrib/groffer/ChangeLog36
-rw-r--r--contrib/groffer/TODO7
-rw-r--r--contrib/groffer/groffer.man76
-rw-r--r--contrib/groffer/groffer.sh781
4 files changed, 396 insertions, 504 deletions
diff --git a/contrib/groffer/ChangeLog b/contrib/groffer/ChangeLog
index fc23e275..263dbcd3 100644
--- a/contrib/groffer/ChangeLog
+++ b/contrib/groffer/ChangeLog
@@ -1,4 +1,38 @@
+2002-09-30 Bernd Warken <bwarken@mayn.de>
+ ________________________________________________________________
+ * release of groffer 0.9.1
+
+ * TODO: remove done entries
+ - Remove request for different shells.
+ - Remove the 'sed' complaints.
+
+2002-07-15 Bernd Warken <bwarken@mayn.de>
+
+ * groffer.sh: replace `sed' interface by direct `sed'
+ - This improves the performance of the shell programming parts
+ and shortens the groffer script by about 5%.
+ - Remove functions: string_del_append(), string_del_leading(),
+ string_del_trailing(), string_flatten(), string_get_before(),
+ string_get_leading(), string_replace_all(), string_sed_s(),
+ and their auxiliary functions.
+ - Replace all calls of these functions by direct `sed' commands.
+ - Define variables for special characters to ease `sed' calls.
+ - Remove `$APPEND'.
+ - Restrict list_from_string() to single character separators.
+ - Correct list_check() and base_name().
+ - Add comments to all calls of `sed'.
+
+ * groffer.sh: add run-time support for several shells
+ - New option `--shell': stop execution and rerun groffer under
+ the shell specified in the argument of `--shell'.
+ - If no shell was specified at run-time, `ash' is tried first;
+ if `ash' is not available continue with the shell with which
+ groffer was called from the command line, or with the shell
+ name in the first line of the script, actually `/bin/sh'.
+
2002-07-12 Bernd Warken <bwarken@mayn.de>
+ ________________________________________________________________
+ * fixes for groffer 0.9.0
* groffer.sh: enhance version information
`groffer -v|--version' now print:
@@ -14,7 +48,7 @@
all display modes (not for source or groff modes).
* TODO:
- fix entry for `shoop'.
+ fix entry `shoop' (not 'shopt').
2002-06-28 Bernd Warken <bwarken@mayn.de>
________________________________________________________________
diff --git a/contrib/groffer/TODO b/contrib/groffer/TODO
index 4c4b41fa..6ff8f65c 100644
--- a/contrib/groffer/TODO
+++ b/contrib/groffer/TODO
@@ -2,7 +2,7 @@
# File position: <groff-source>/contrib/groffer/TODO
-# Last update: 12 July 2002
+# Last update: 30 Sep 2002
# Copyright (C) 2001,2002 Free Software Foundation, Inc.
# Written by Bernd Warken <bwarken@mayn.de>
@@ -37,15 +37,10 @@ Features:
- Consider using the `shoop' package (OOP for `sh').
- Revise option handling of `grog'.
- `gxditview' needs a complete shower.
-- Write an interface for choosing between different calling shells.
Revision:
- Should there be a native implementation for `--apropos'?
- Revise the `--all' feature to better reflect GNU man.
-- string_sed_s() has trouble with arguments that have embedded quotes,
- but it is good enough for the script so far.
-- Consider to replace string_sed_s() and friends in favor of native sed
- commands or to write suitable C++ utils.
- The debug function stack is buggy (no effect on normal operation).
Documentation:
diff --git a/contrib/groffer/groffer.man b/contrib/groffer/groffer.man
index c1de32c5..fd72504b 100644
--- a/contrib/groffer/groffer.man
+++ b/contrib/groffer/groffer.man
@@ -12,10 +12,11 @@ groffer \- display groff files and man\~pages on X and tty
.ig
groffer.1 - man page for groffer (section 1).
-File position: <groff_source_top>/contrib/groffer/groffer.man
+Source file position: <groff_source_top>/contrib/groffer/groffer.man
+Installed position: $prefix/share/man/man1/groffer.1
-Version : groffer 0.9.0
-Last update : 21 Aug 2002
+Version : groffer 0.9.1
+Last update : 30 Sep 2002
This file is part of groff, the GNU roff type-setting system.
@@ -659,6 +660,7 @@ display mode automatically.
.Opt_[alt] -- pdf-viewer prog
.Opt_[alt] -- ps
.Opt_[alt] -- ps-viewer prog
+.Opt_[alt] -- shell
.Opt_[alt] -- tty
.Opt_[alt] -- www
.Opt_[alt] -- www-viewer prog
@@ -871,7 +873,7 @@ program is part of
It can be used to display arbitrary documents written in the
.BR roff (@MAN7EXT@)
formatting language in several different ways, in an X window viewer
-program of in a text terminal.
+program or in a text terminal.
.
The viewer programs can be chosen as the groff native viewer
.BR gxditview (@MAN1EXT@),
@@ -1552,6 +1554,15 @@ Restrict searching for man pages to the given
a colon-separated list.
.
.
+.Opt_def -- shell "shell_program"
+Specify the shell under which the groffer script should be run.
+.
+The script first tests whether this option is set (either within
+.Env_var $GROFF_OPT
+or as a command line option); if so, the script is rerun under the
+shell program specified with the option argument.
+.
+.
.Opt_def -- source
Equivalent to
.Opt_short Q .
@@ -2642,35 +2653,70 @@ The
shell script is compatible to both POSIX and GNU.
.
POSIX compatibility refers to
-.B P1003.2/D11.2
-of September 1991.
+.B IEEE P1003.2/D11.2
+of September 1991, a very early version of this standard.
.
The script uses only a quite restricted set of shell language elements
-and shell utilities that is common to all POSIX versions; it should
-work on most actual commercial and free operating systems.
+and shell builtins, common to all POSIX versions; the only external
+program used is `sed', again only the most basic POSIX features of
+`sed' are used.
+.
+The groffer script should work on most actual free and commercial
+operating systems.
+.
+.
+.P
+The groffer program provides its own parser for command line options;
+it can handle option arguments and file names containing white space
+and a large set of special characters.
.
.
.P
The groffer shell script was tested with the following common
implementations of the POSIX shell:
-.BR sh (1),
.BR ash (1),
.BR bash (1),
.BR ksh (1),
+and POSIX
+.BR sh (1),
and others.
.
-The best performance is provided by the
-.I ash
-shell.
-.
Free POSIX compatible shells and shell utilities for most operating
systems are available at the
.URL http://\:www.gnu.org/software/ "GNU software archive" .
.
.
.P
-The groffer program can handle option arguments and file names that
-contain white space and a large set of special characters.
+The best performance was obtained with the
+.I ash
+shell; so groffer tries to run under
+.I ash
+whenever possible.
+.
+The procedure to determine the shell to run groffer was programmed to
+be as follows:
+.
+.
+.Topic
+the argument of the command line option
+.Opt_long shell ;
+if not set
+.
+.Topic
+the argument of the option
+.Opt_long shell
+in the environment variable
+.Env_var $GROFF_OPT ;
+if not set
+.
+.Topic
+try
+.IR ash ;
+if not available
+.
+.Topic
+continue with the shell under which the script was started in the
+first place.
.
.
.\" --------------------------------------------------------------------
diff --git a/contrib/groffer/groffer.sh b/contrib/groffer/groffer.sh
index 4ad166ec..6cabefa8 100644
--- a/contrib/groffer/groffer.sh
+++ b/contrib/groffer/groffer.sh
@@ -29,9 +29,107 @@ export _PROGRAM_VERSION;
export _LAST_UPDATE;
_PROGRAM_NAME='groffer';
-_PROGRAM_VERSION='0.9.0';
-_LAST_UPDATE='12 July 2002';
+_PROGRAM_VERSION='0.9.1';
+_LAST_UPDATE='30 Sep 2002';
+########################################################################
+# Determine the shell under which to run this script;
+# if `ash' is available restart the script using `ash';
+# otherwise just go on.
+
+if test "${_groffer_run}" != 'second'; then
+ # only reached during the first run of the script
+
+ export GROFFER_OPT;
+ export _groffer_run;
+ export _this;
+
+
+ #_this="@BINDIR@/${_PROGRAM_NAME}";
+ _this='groffer.sh';
+
+ ###########################
+ # _get_opt_shell ("$@")
+ #
+ # Determine whether `--shell' was specified in $GROFF_OPT or in $*;
+ # if so echo its argument.
+ #
+ _get_opt_shell()
+ {
+ local i;
+ local _sh;
+ case " ${GROFFER_OPT} $*" in
+ *\ --shell\ *|*\ --shell=*)
+ (
+ eval set -- "${GROFFER_OPT}" '"$@"';
+ _sh='';
+ for i in "$@"; do
+ case "$1" in
+ --shell)
+ if test "$#" -ge 2; then
+ _sh="$2";
+ shift;
+ fi;
+ ;;
+ --shell=?*)
+ # delete up to first `=' character
+ _sh="$(echo -n "$1" | sed -e 's/^[^=]*=//')";
+ ;;
+ esac;
+ shift;
+ done;
+ echo -n "${_sh}";
+ )
+ ;;
+ esac;
+ }
+
+
+ ###########################
+ # _test_on_shell (<name>)
+ #
+ # Test whether <name> is a shell program of Bourne type (POSIX sh).
+ #
+ _test_on_shell()
+ {
+ if test "$#" -le 0 || test "$1" = ''; then
+ return 1;
+ fi;
+ # do not quote $1 to allow arguments
+ test "$($1 -c 's=ok; echo -n "$s"' 2>/dev/null)" = 'ok';
+ }
+
+ # do the shell determination
+ _shell="$(_get_opt_shell "$@")";
+ if test "${_shell}" = ''; then
+ _shell='ash';
+ fi;
+ if _test_on_shell "${_shell}"; then
+ _groffer_run='second';
+ # do not quote $_shell to allow arguments
+ exec ${_shell} "${_this}" "$@";
+ exit;
+ fi;
+
+ # clean-up of shell determination
+ unset _shell;
+ unset _this;
+ unset _groffer_run;
+ _get_opt_shell()
+ {
+ return 0;
+ }
+ _test_on_shell()
+ {
+ return 0;
+ }
+
+fi;
+
+
+########################################################################
+# diagnostic messages
+#
export _DEBUG;
_DEBUG='no'; # disable debugging information
#_DEBUG='yes'; # enable debugging information
@@ -169,13 +267,7 @@ _DEBUG_LM='no'; # disable landmark messages
# reset ()
# save_stdin ()
# string_contains (<string> <part>)
-# string_del_append (<string>)
-# string_del_leading (<string> <regex>)
-# string_del_trailing (<string> <regex>)
-# string_flatten()
# string_not_contains (<string> <part>)
-# string_replace_all (<string> <regex> <replace>)
-# string_sed_s (<string> <regex> [<replace> [<flag>]])
# tmp_cat ()
# tmp_create (<suffix>?)
# to_tmp (<filename>)
@@ -248,15 +340,29 @@ fi;
# characters
-export _APPEND # Append to string to cover final char
+export _BQUOTE;
+export _BSLASH;
+export _DQUOTE;
export _NEWLINE;
+export _LBRACK;
+export _LPAR;
+export _RBRACK;
+export _RPAR;
export _SPACE;
+export _SQUOTE;
export _TAB;
-_APPEND='Z';
+_BQUOTE='`';
+_BSLASH='\';
+_DQUOTE='"';
_NEWLINE='
';
+_LBRACK='[';
+_LPAR='(';
+_RBRACK=']';
+_RPAR=')';
_SPACE=' ';
+_SQUOTE="'";
_TAB=' ';
# function return values; `0' means ok; other values are error codes
@@ -381,7 +487,7 @@ _OPTS_GROFFER_LONG_NA="'all' 'apropos' 'ascii' 'auto' 'default' 'dvi' \
_OPTS_GROFFER_LONG_ARG="'background' 'bd' 'bg' 'bw' 'default-modes' \
'device' 'display' 'dvi-viewer' 'extension' 'fg' 'fn' 'font' \
'foreground' 'geometry' 'locale' 'manpath' 'mode' 'pager' \
-'pdf-viewer' 'ps-viewer' 'resolution' 'sections' \
+'pdf-viewer' 'ps-viewer' 'resolution' 'sections' 'shell' \
'systems' 'title' 'troff-device' 'www-viewer' 'xrm' 'x-viewer'";
##### options inhereted from groff
@@ -582,7 +688,6 @@ reset()
{
if test "$#" -ne 0; then
error "reset() does not have arguments.";
- return "${_ERROR}";
fi;
_ADDOPTS_GROFF='';
@@ -841,7 +946,7 @@ func_check()
;;
*)
error \
- "func_check(): second argument is not a relational operator.";
+ 'func_check(): second argument is not a relational operator.';
;;
esac;
shift 3;
@@ -862,6 +967,10 @@ func_check()
#
# Retrieve the top element from the stack.
#
+# The stack elements are separated by `!'; the popped element is
+# identical to the original element, except that all `!' characters
+# were removed.
+#
# Arguments: 1
#
func_pop()
@@ -875,8 +984,7 @@ func_pop()
error 'func_pop(): stack is empty.';
;;
*!*)
-# echo2 '>> pop: ('"$(echo -n "${_FUNC_STACK}" \
-# | sed -e 's/^\([^ !]*\).*$/\1/')"')';
+ # split at first bang `!'.
_FUNC_STACK="$(echo -n ${_FUNC_STACK} \
| sed -e 's/^[^!]*!//')";
;;
@@ -893,6 +1001,9 @@ func_pop()
#
# Store another element to stack.
#
+# The stack elements are separated by `!'; if <element> contains a `!'
+# it is removed first.
+#
# Arguments: 1
#
func_push()
@@ -904,13 +1015,13 @@ func_push()
fi;
case "$1" in
*'!'*)
+ # remove all bangs `!'.
_element="$(echo -n "$1" | sed -e 's/!//g')";
;;
*)
_element="$1";
;;
esac;
-# echo2 '>> push: '"$_element";
if test "${_FUNC_STACK}" = ''; then
_FUNC_STACK="${_element}";
else
@@ -932,7 +1043,9 @@ func_stack_dump()
*!*)
_rest="${_FUNC_STACK}";
while test "${_rest}" != ''; do
+ # get part before the first bang `!'.
diag "$(echo -n "${_rest}" | sed -e 's/^\([^!]*\)!.*$/\1/')";
+ # delete part up to the first bang `!'.
_rest="$(echo -n "${_rest}" | sed -e 's/^!*[^!]*!*//')";
done;
;;
@@ -1034,7 +1147,7 @@ unset _clobber;
# Test of function `sed'.
#
if test "$(echo xTesTx \
- | sed -e 's/^.\([Tt]e*x*sT\+\).*$/\1/' \
+ | sed -e 's/^.\([Tt]e*x*sTT*\).*$/\1/' \
| sed -e '\|T|s||t|g')" != 'test';
then
error 'Test of "sed" command failed.';
@@ -1101,7 +1214,18 @@ landmark "3: functions";
base_name()
{
func_check base_name = 1 "$@";
- string_sed_s "$1" '^.*/\([^/]*\)$' '\1';
+ case "$1" in
+ */)
+ do_nothing;
+ ;;
+ */*)
+ # delete everything up to last slash `/'.
+ echo -n "$1" | sed -e '\|^.*/*\([^/]*\)$|s||\1|';
+ ;;
+ *)
+ echo -n "$1";
+ ;;
+ esac;
eval "${return_ok}";
}
@@ -1213,9 +1337,13 @@ dirname_chop()
local _arg;
local _res;
local _sep;
- _res="$(string_replace_all "$1" '//\+' '/')";
+ # replace all multiple slashes by a single slash `/'.
+ _res="$(echo -n "$1" | sed -e '\|///*|s||/|g')";
case "${_res}" in
- ?*/) string_del_trailing "${_res}" '/'; ;;
+ ?*/)
+ # remove trailing slash '/';
+ echo -n "${_res}" | sed -e '\|/$|s|||';
+ ;;
*) echo -n "${_res}"; ;;
esac;
eval "${return_ok}";
@@ -1249,7 +1377,7 @@ do_filearg()
local _filespec;
local i;
_filespec="$1";
- # store sequence positional parameters
+ # store sequence into positional parameters
case "${_filespec}" in
'')
eval "${return_good}";
@@ -1263,7 +1391,7 @@ do_filearg()
;;
*)
if is_yes "${_MAN_ENABLE}"; then
- if is_yes "${_OPT_MAN}"; then
+ if is_yes "${_MAN_FORCE}"; then
set -- 'Manpage' 'File';
else
set -- 'File' 'Manpage';
@@ -1686,10 +1814,12 @@ list_append()
_res="$1";
shift;
for s in "$@"; do
- # escape each single quote.
case "$s" in
*\'*)
- _element="$(echo -n "$s" | sed -e s/\'/'&\\&&'/g)";
+ # escape each single quote by replacing each "'" (squote)
+ # by "'\''" (squote bslash squote squote);
+ # note that the backslash must be doubled for `sed'.
+ _element="$(echo -n "$s" | sed -e 's/'"${_SQUOTE}"'/&\\&&/g')";
;;
*)
_element="$s";
@@ -1714,7 +1844,8 @@ list_append()
# A list has the form "'first' 'second' '...' 'last'".
# So it has a leading and a final quote and the elements are separated
# by "' '" constructs. If these are all removed there should not be
-# any single-quotes left. Watch out for "\'".
+# any single-quotes left. Watch out for escaped single quotes; they
+# have the form '\'' (sq bs sq sq).
#
# Arguments: 1
# Output: the argument <list> unchanged, it the check succeeded.
@@ -1732,13 +1863,20 @@ list_check()
error "list_check() bad list: $1"
;;
esac;
+ # Remove leading single quote,
+ # remove final single quote,
+ # remove escaped single quotes (squote bslash squote squote)
+ # [note that `sed' doubles the backslash (bslash bslash)],
+ # remove field separators (squote space squote).
_list="$(echo -n "${_list}" \
- | sed -e s/^"[']"'\(.*\)'"[']"'$/\1/' \
- | sed -e s/"[']"'[\]'"['][']"'//g' \
- | sed -e 's/\([^\]\)'"'"' *'"'"'/\1/g')"; # ' (for emacs)
+ | sed -e 's/^'"${_SQUOTE}"'//' \
+ | sed -e 's/'"${_SQUOTE}"'$//' \
+ | sed -e \
+ 's/'"${_SQUOTE}${_BSLASH}${_BSLASH}${_SQUOTE}${_SQUOTE}"'//g' \
+ | sed -e 's/'"${_SQUOTE}${_SPACE}${_SPACE}"'*'"${_SQUOTE}"'//g')";
case "${_list}" in
- *\'*)
- error "list_check() bad list: ${_list}"
+ *\'*) # criterium fails if squote is left
+ error 'list_check() bad list: '"${_list}";
;;
esac;
echo -n "$1";
@@ -1833,16 +1971,18 @@ list_from_cmdline()
{
func_check list_from_cmdline '>=' 4 "$@";
local _fparams;
+ local _fn;
local _result;
- if is_empty "${FUNCNAME}"; then
- local FUNCNAME;
- FUNCNAME='list_from_cmdline';
- fi;
+ local _long_a;
+ local _long_n;
+ local _short_a;
+ local _short_n;
_short_n="$(list_check "$1")"; # short options, no argument
_short_a="$(list_check "$2")"; # short options with argument
_long_n="$(list_check "$3")"; # long options, no argument
_long_a="$(list_check "$4")"; # long options with argument
shift 4;
+ _fn='list_from_cmdline():'; # for error messages
if test "$#" -eq 0; then
echo -n "'--'";
eval "${return_ok}";
@@ -1855,8 +1995,9 @@ list_from_cmdline()
case "$_arg" in
--) break; ;;
--?*)
- _opt="$(string_del_leading "${_arg}" "--")";
- if list_has "$_long_n" "${_opt}"; then
+ # delete leading '--';
+ _opt="$(echo -n "${_arg}" | sed -e 's/^..//')";
+ if list_has "${_long_n}" "${_opt}"; then
# long option, no argument
_result="$(list_append "${_result}" "--${_opt}")";
continue;
@@ -1865,34 +2006,50 @@ list_from_cmdline()
# long option with argument
_result="$(list_append "${_result}" "--${_opt}")";
if test "$#" -le 0; then
- error "${FUNCNAME}(): no argument for option --${_opt}."
+ error "${_fn} no argument for option --${_opt}."
fi;
_result="$(list_append "${_result}" "$1")";
shift;
continue;
fi;
# test on `--opt=arg'
- if string_contains "${_opt}" "="; then
- _lopt="$(string_del_trailing "${_opt}" '=.*')";
+ if string_contains "${_opt}" '='; then
+ # extract option by deleting from the first '=' to the end
+ _lopt="$(echo -n "${_opt}" | sed -e 's/=.*$//')";
if list_has "${_long_a}" "${_lopt}"; then
- _optarg="$(string_del_leading "${_opt}" "${_lopt}=")";
+ # get the option argument by deleting up to first `='
+ _optarg="$(echo -n "${_opt}" | sed -e 's/^[^=]*=//')";
_result="$(list_append "${_result}" \
"--${_lopt}" "${_optarg}")";
continue;
fi;
fi;
- error "${FUNCNAME}(): --${_opt} is not an option."
+ error "${_fn} --${_opt} is not an option."
;;
-?*) # short option (cluster)
- _rest="$(string_del_leading "${_arg}" "-")";
+ # delete leading `-';
+ _rest="$(echo -n "${_arg}" | sed -e 's/^.//')";
while is_not_empty "${_rest}"; do
- _optchar="$(string_get_leading "${_rest}" '.')";
- _rest="$(string_del_leading "${_rest}" '.')";
+ # get next short option from cluster (first char of $_rest)
+ _optchar="$(echo -n "${_rest}" | sed -e 's/^\(.\).*$/\1/')";
+ # remove first character from ${_rest};
+ _rest="$(echo -n "${_rest}" | 's/^.//')";
if list_has "${_short_n}" "${_optchar}"; then
_result="$(list_append "${_result}" "-${_optchar}")";
continue;
elif list_has "${_short_a}" "${_optchar}"; then
- _rest="$(string_del_leading "${_rest}" "${_optchar}")";
+ # remove leading character
+ case "${_optchar}" in
+ /) # cannot use normal `sed' separator
+ _rest="$(echo -n "${_rest}" | sed -e '\|^.|s|||')";
+ ;;
+ ?)
+ _rest="$(echo -n "${_rest}" | sed -e 's/^.//')";
+ ;;
+ *)
+ error "${_fn} several chars parsed for short option."
+ ;;
+ esac;
if is_empty "${_rest}"; then
if test "$#" -ge 1; then
_result="$(list_append "${_result}" \
@@ -1901,7 +2058,7 @@ list_from_cmdline()
continue;
else
error \
- "${FUNCNAME}(): no argument for option -${_optchar}."
+ "${_fn}"' no argument for option -'"${_optchar}."
fi;
else # rest is the argument
_result="$(list_append "${_result}" \
@@ -1910,7 +2067,7 @@ list_from_cmdline()
continue;
fi;
else
- error "${FUNCNAME}(): unknown option -${_optchar}."
+ error "${_fn} unknown option -${_optchar}."
fi;
done;
;;
@@ -1926,7 +2083,7 @@ list_from_cmdline()
;;
esac;
done;
- _result="$(list_append "${_result}" "--")";
+ _result="$(list_append "${_result}" '--')";
if is_not_empty "${_fparams}"; then
_result="${_result} ${_fparams}";
fi;
@@ -1955,7 +2112,6 @@ list_from_lists()
eval "${return_ok}";
}
-
########################################################################
# list_from_split (<string> <separator>)
#
@@ -1968,13 +2124,23 @@ list_from_lists()
list_from_split()
{
func_check list_from_split = 2 "$@";
- string_replace_all \
- "$(string_replace_all \
- "$1" \
- '\(['"${_SPACE}${_TAB}"']\)' \
- '\\\1')" \
- "$2" \
- ' ';
+ local _s;
+
+ # precede each space or tab by a backslash `\' (doubled for `sed')
+ _s="$(echo -n "$1" | sed -e 's/\(['"${_SPACE}${_TAB}"']\)/\\\1/g')";
+
+ # replace split character of string by the list separator ` ' (space).
+ case "$2" in
+ /) # cannot use normal `sed' separator
+ echo -n "${_s}" | sed -e '\|'"$2"'|s|| |g';
+ ;;
+ ?) # use normal `sed' separator
+ echo -n "${_s}" | sed -e 's/'"$2"'/ /g';
+ ;;
+ ??*)
+ error 'list_from_split(): separator must be a single character.';
+ ;;
+ esac;
eval "${return_ok}";
}
@@ -2060,6 +2226,8 @@ list_length()
########################################################################
# list_prepend (<list> <element>...)
#
+# Insert new <element> at the beginning of <list>
+#
# Arguments: >=2
# <list>: a space-separated list of single-quoted elements.
# <element>: some sequence of characters.
@@ -2074,7 +2242,7 @@ list_prepend()
_res="$1";
shift;
for s in "$@"; do
- # escape each single quote.
+ # escape single quotes in list style (squote bslash squote squote).
_element="$(echo -n "$s" | sed -e 's/'\''/&\\&&/g')";
_res="'${_element}' ${_res}";
done;
@@ -2082,6 +2250,7 @@ list_prepend()
eval "${return_ok}";
}
+
########################################################################
landmark '7: man_*()';
########################################################################
@@ -2124,28 +2293,35 @@ man_do_filespec()
_name='';
_section='';
case "${_spec}" in
+ */*) # not a man spec when it contains '/'
+ eval "${return_bad}";
+ ;;
man:?*\(?*\)) # man:name(section)
- _string="$(string_del_leading "${_spec}" 'man:')";
- _string="$(string_del_trailing "${_string}" ')')";
- _name="$(string_del_trailing "${_string}" '(.\+')";
- _section="$(string_del_leading "${_string}" "${_name}"'(')";
+ _name="$(echo -n "${_spec}" \
+ | sed -e 's/^man:\(..*\)(\(..*\))$/\1/')";
+ _section="$(echo -n "${_spec}" \
+ | sed -e 's/^man:\(..*\)(\(..*\))$/\2/')";
;;
man:?*.?*) # man:name.section
- _string="$(string_del_leading "${_spec}" 'man:')";
- _name="$(string_del_trailing "${_string}" '\.[^.]*')";
- _section="$(string_del_leading "${_string}" "${_name}"'\.')";
+ _name="$(echo -n "${_spec}" \
+ | sed -e 's/^man:\(..*\)\.\(..*\)$/\1/')";
+ _section="$(echo -n "${_spec}" \
+ | sed -e 's/^man:\(..*\)\.\(..*\)$/\2/')";
;;
man:?*) # man:name
- _name="$(string_del_leading "${_spec}" 'man:')";
+ _name="$(echo -n "${_spec}" | sed -e 's/^man://')";
;;
?*\(?*\)) # name(section)
- _string="$(string_del_trailing "${_spec}" ')')";
- _name="$(string_del_trailing "${_string}" '(.\+')";
- _section="$(string_del_leading "${_string}" "${_name}"'(')";
+ _name="$(echo -n "${_spec}" \
+ | sed -e 's/^\(..*\)(\(..*\))$/\1/')";
+ _section="$(echo -n "${_spec}" \
+ | sed -e 's/^\(..*\)(\(..*\))$/\2/')";
;;
?*.?*) # name.section
- _name="$(string_del_trailing "${_spec}" '\.[^.]\+')";
- _section="$(string_del_leading "${_spec}" "${_name}"'\.')";
+ _name="$(echo -n "${_spec}" \
+ | sed -e 's/^\(..*\)\.\(..*\)$/\1/')";
+ _section="$(echo -n "${_spec}" \
+ | sed -e 's/^\(..*\)\.\(..*\)$/\2/')";
;;
?*)
_name="${_filespec}";
@@ -2393,7 +2569,8 @@ man_setup()
;;
*)
_MAN_LANG="${_lang}";
- _MAN_LANG2="$(string_get_leading "${_lang}" '..')";
+ # get first two characters of $_lang
+ _MAN_LANG2="$(echo -n "${_lang}" | sed -e 's/^\(..\).*$/\1/')";
;;
esac;
# from now on, use only $_LANG, forget about $_OPT_LANG, $LC_*.
@@ -2510,7 +2687,8 @@ manpath_set_from_path()
if is_not_empty "${PATH}"; then
eval set -- "$(path_split "${PATH}")";
for d in "$@"; do
- _base="$(string_del_trailing "$d" '/\+bin/*')";
+ # delete the final `/bin' part
+ _base="$(echo -n "$d" | sed -e '\|//*bin/*$|s|||')";
for e in /share/man /man; do
_mandir="${_base}$e";
if test -d "${_mandir}" && test -r "${_mandir}"; then
@@ -2553,13 +2731,9 @@ path_chop()
func_check path_chop = 1 "$@";
local _res;
-# _res="$1";
-# _res="$(string_flatten "${_res}" ':')";
-# _res="$(string_del_leading "${_res}" ':')";
-# _res="$(string_del_trailing "${_res}" ':')";
-# echo -n "${_res}";
-
- echo -n "$1" | sed -e 's/::\+/:/g' |
+ # replace multiple colons by a single colon `:'
+ # remove leading and trailing colons
+ echo -n "$1" | sed -e 's/:::*/:/g' |
sed -e 's/^:*//' |
sed -e 's/:*$//';
eval "${return_ok}";
@@ -2717,8 +2891,12 @@ register_title()
eval "${return_ok}";
fi;
_title="$(base_name "$1")"; # remove directory part
- _title="$(string_del_trailing "${_title}" '\.gz')"; # remove .gz
- _title="$(string_del_trailing "${_title}" '\.Z')"; # remove .Z
+
+ # remove extension `.gz'
+ _title="$(echo -n "${_title}" | sed -e 's/\.gz$//')";
+ # remove extension `.Z'
+ _title="$(echo -n "${_title}" | sed -e 's/\.Z$//')";
+
if is_empty "${_title}"; then
eval "${return_ok}";
fi;
@@ -2781,218 +2959,6 @@ string_contains()
########################################################################
-# string_del_append (<string>)
-#
-# Delete $_APPEND from the end of <string>, if any.
-#
-# As this is needed within string_sed_s() this function must use `sed'.
-#
-# Arguments: <string>: arbitrary sequence of characters.
-# Globals: in: $_APPEND
-# Output: the shortened string.
-# Return: $_GOOD if successful, $_BAD if no replace took place.
-#
-string_del_append()
-{
- func_check string_del_append = 1 "$@";
- case "$1" in
- *"$_APPEND")
- echo -n "$1" | sed -e 's/'"$_APPEND"'$//'
- eval "${return_good}";
- ;;
- *)
- eval "${return_bad}";
- ;;
- esac;
- eval "${return_ok}";
-}
-
-
-########################################################################
-# string_del_leading (<string> <regex>)
-#
-# Delete the beginning <regex> of <string>, if any.
-#
-# Arguments: 2
-# <string>: arbitrary sequence of characters.
-# <regex>: is a BRE like in `sed';
-# Do not worry about the address delimiter, the program escapes them.
-# Output: the replaced string.
-#
-string_del_leading()
-{
- func_check string_del_leading = 2 "$@";
- local _del;
- local _result;
- local _string;
- _string="$1";
- _del="$2";
- if is_empty "${_string}"; then
- if is_empty "${_del}"; then
- eval "${return_good}";
- else
- eval "${return_bad}";
- fi;
- fi;
- if is_empty "${_del}"; then
- echo -n "${_string}";
- eval "${return_ok}";
- fi;
- _result="$(string_sed_s "${_string}" '^'"${_del}" '')";
- echo -n "${_result}";
- if is_equal "${_result}" "${_string}"; then
- eval "${return_bad}";
- else
- eval "${return_ok}";
- fi;
- eval "${return_ok}";
-}
-
-
-########################################################################
-# string_del_trailing (<string> <regex>)
-#
-# Delete the final <regex> of <string>, if any.
-#
-# Arguments: 2
-# <string>: arbitrary sequence of characters.
-# <regex>: is a BRE like in `sed';
-# Do not worry about the address delimiter, the program escapes them.
-# Output: the replaced string.
-#
-string_del_trailing()
-{
- func_check string_del_trailing = 2 "$@";
- local _del;
- local _result;
- local _string;
- _string="$1";
- _del="$2";
- if is_empty "${_string}"; then
- if is_empty "${_del}"; then
- eval "${return_ok}";
- else
- eval "${return_bad}";
- fi;
- fi;
- if is_empty "${_del}"; then
- echo -n "${_string}";
- eval "${return_good}";
- fi;
- _result="$(string_sed_s "${_string}" "${_del}"'$' '')";
- echo -n "${_result}";
- if is_equal "${_result}" "${_string}"; then
- eval "${return_bad}";
- else
- eval "${return_ok}";
- fi;
- eval "${return_ok}";
-}
-
-
-########################################################################
-# string_flatten (<string> <char>)
-#
-# Reduce multiple occurences of character <char> in <string> to one.
-#
-# Arguments: 2
-# <string>: arbitrary sequence of characters.
-# <char>: a character, or escaped character for sed.
-# Do not worry about the address delimiter, the program escapes them.
-# Output: the retrieved string.
-#
-string_flatten()
-{
- func_check string_flatten = 2 "$@";
- _string="$1";
- _char="$2";
- string_replace_all "${_string}" "${_char}${_char}\+" "${_char}";
- eval "${return_ok}";
-}
-
-
-########################################################################
-# string_get_before (<string> <regex>)
-#
-# Get the beginning <regex> of <string>, if any.
-#
-# Arguments: 2
-# <string>: arbitrary sequence of characters.
-# <regex>: is a BRE like in `sed';
-# Do not worry about the address delimiter, the program escapes them.
-# Output: the retrieved string.
-#
-string_get_before()
-{
- func_check string_get_before = 2 "$@";
- local _del;
- local _regex;
- local _res;
- local _string;
- if is_empty "$1"; then
- if is_empty "$2"; then
- eval "${return_ok}";
- else
- eval "${return_bad}";
- fi;
- fi;
- if is_empty "$2"; then
- echo -n "$1";
- eval "${return_ok}";
- fi;
- _res="$(string_sed_s "$1" "$2"'.*$' '')";
- echo -n "${_res}";
- if is_equal "${_res}" "$1"; then
- eval "${return_bad}";
- else
- eval "${return_ok}";
- fi;
- eval "${return_ok}";
-}
-
-
-########################################################################
-# string_get_leading (<string> <regex>)
-#
-# Get the beginning <regex> of <string>, if any.
-#
-# Arguments: 2
-# <string>: arbitrary sequence of characters.
-# <regex>: is a BRE like in `sed';
-# Do not worry about the address delimiter, the program escapes them.
-# Output: the retrieved string.
-#
-string_get_leading()
-{
- func_check string_get_leading = 2 "$@";
- local _del;
- local _result;
- local _string;
- _string="$1";
- _get="$2";
- if is_empty "${_string}"; then
- if is_empty "${_get}"; then
- eval "${return_ok}";
- else
- eval "${return_bad}";
- fi;
- fi;
- if is_empty "${_get}"; then
- echo -n "${_string}";
- eval "${return_ok}";
- fi;
- _result="$(string_sed_s "${_string}" '^\('"${_get}"'\).*$' '\1')";
- echo -n "${_result}";
- if is_equal "${_result}" "${_string}"; then
- eval "${return_bad}";
- else
- eval "${return_ok}";
- fi;
- eval "${return_ok}";
-}
-
-
-########################################################################
# string_not_contains (<string> <part>)
#
# Test whether `part' is not substring of `string'.
@@ -3013,181 +2979,6 @@ string_not_contains()
########################################################################
-# string_replace_all (<string> <regex> <replace>)
-#
-# Replace <regex> by <replace> in <string>. Interface to `sed s'.
-#
-# Arguments: 3
-# <regex>: is a BRE like in `sed';
-# <replace>: like the last element in `sed s', honors \1, etc.
-# <string>: no special characters, no restrictions.
-# Do not worry about the address delimiter, the program escapes them.
-# Output: the replaced string.
-# Return: `1', if no replace; `0' otherwise.
-#
-string_replace_all()
-{
- func_check string_replace_all = 3 "$@";
- string_sed_s "$@" 'g';
- eval "${return_ok}";
-}
-
-
-########################################################################
-# string_sed_s (<string> <regex> [<replace> [<flag>]])
-#
-# Feed command `sed s' independently of delimiter.
-#
-# Equivalent to:
-# echo -n <string> | sed -e '/<regex>/s//<replace>/<flag>';
-#
-# Arguments: do not worry about the deliniter character `/'.
-# 2: <replace>='', <flag>=''
-# 3: <flag>=''
-# 4: with sed flag, e.g. `g' for global
-# Output: the resulting string.
-#
-string_sed_s()
-{
- func_check string_sed_s '>=' 2 "$@";
- local _flag;
- local _regex;
- local _replace;
- local _string;
- case "$#" in
- 2)
- _replace='';
- _flag='';
- ;;
- 3)
- _replace="$(_string_sed_s_esc_slash "$3")";
- _flag='';
- ;;
- 4)
- _replace="$(_string_sed_s_esc_slash "$3")";
- _flag="$4";
- ;;
- *)
- error "string_sed_s() needs 2, 3, or 4 arguments.";
- ;;
- esac;
- _string="$1";
- _regex="$(_string_sed_s_esc_slash "$2")";
- if is_empty "${_string}"; then
- eval "${return_ok}";
- fi;
- if is_empty "${_string}"; then
- error "string_sed_s(): empty regular expression";
- fi;
- echo -n "${_string}" | \
- eval sed -e \'s/"${_regex}"/"${_replace}"/"${_flag}"\';
- eval "${return_ok}";
-} # string_sed_s()
-
-
-_string_sed_s_esc_slash()
-{
- # Replace each slash `/' by `\/', but not within `[]'.
- # Global: $_APPEND
- local _beginning;
- local _bracketed;
- local _end;
- local _rest;
- local _result;
- local _str;
-# if test "$#" -ne 1; then
-# error '_string_sed_s_esc_slash() requires 1 argument.';
-# fi;
- if is_empty "$1"; then
- return "${_GOOD}";
- fi;
- if string_not_contains "$1" '/'; then
- echo -n "$1";
- return "${_GOOD}";
- fi;
- _rest="$1""${_APPEND}";
- _result='';
- while true; do
- if string_not_contains "${_rest}" '/'; then
- string_del_append "${_result}${_rest}";
- return "${_GOOD}";
- fi;
- if string_not_contains "${_rest}" '['; then
- string_del_append \
- "${_result}$(_string_sed_s_esc_slash_unbracketed "${_rest}")";
- return "${_GOOD}";
- fi;
- # split at first bracket.
- _beginning="$(echo -n "${_rest}" | sed -e '/^\([^[]*\).*$/s//\1/')";
- _rest="$(echo -n "${_rest}" | sed -e 's/^[^[]*//')";
- if is_not_empty "${_beginning}"; then
- _str="$(_string_sed_s_esc_slash_unbracketed "${_beginning}")";
- _result="${_result}${_str}";
- fi;
- if string_not_contains "${_rest}" '/'; then
- string_del_append "${_result}${_rest}";
- return "${_GOOD}";
- fi;
- case "${_rest}" in
- \[\]*\]*) # `[]...]' construct
- _bracketed="$(echo -n "${_rest}" | \
- sed -e 's/^\(\[\][^]]*\]\).*$/\1/')";
- _rest="$(echo -n "${_rest}" | \
- sed -e 's/^\(\[\][^]]*\]\)\(.*\)$/\2/')";
- ;;
- \[^\]*\]*) # `[^]...]' construct
- _bracketed="$(echo -n "${_rest}" | \
- sed -e 's/^\(\[^\][^]]*\]\).*$/\1/')";
- _rest="$(echo -n "${_rest}" | \
- sed -e 's/^\(\[^\][^]]*\]\)\(.*\)$/\2/')";
- ;;
- \[*\]*) # `[...]' construct
- _bracketed="$(echo -n "${_rest}" | \
- sed -e 's/^\(\[[^]]*\]\).*$/\1/')";
- _rest="$(echo -n "${_rest}" | \
- sed -e 's/^\(\[[^]]*\]\)\(.*\)$/\2/')";
- ;;
- *)
- error \
- '_string_sed_s_esc_slash(): $_rest must start with a bracket';
- ;;
- esac;
- _result="$(string_del_trailing "${_result}${_bracketed}" \
- "$_APPEND")";
- done;
- return "${_BAD}";
-} # _string_sed_s_esc_slash_line()
-
-
-_string_sed_s_esc_slash_unbracketed()
-{
- # Do the escaping of slashes in strings that do not contain a bracket.
- #
- # Argument: 1, may not contain a `[' nor line breaks.
- # Output: precede each slash in the argument by a backslash.
- # Return: 1, if argument has a `['; 0 otherwise.
- #
-# if test "$#" -ne 1; then
-# error \
-# '_string_sed_s_esc_slash_unbracketed() needs 1 argument).';
-# fi;
-# if string_contains "$1" '['; then
-# error "_string_sed_s_esc_slash(): no bracket allowed in argument.";
-# fi;
- case "$1" in
- */*)
- echo -n "$1" | sed -e '\|/|s|/|\\/|g';
- return "${_OK}";
- ;;
- *)
- echo -n "$1";
- return "${_OK}";
- ;;
- esac;
-} # _string_sed_s_esc_slash_unbracketed()
-
-
-########################################################################
landmark '12: tmp_*()';
########################################################################
@@ -3255,10 +3046,6 @@ to_tmp()
usage()
{
func_check usage = 0 "$@";
- local _header;
- local _gap;
- _header="Usage: ${_PROGRAM_NAME}";
- _gap="$(string_replace_all "${_header}" '\.' ' ')";
echo2;
version;
cat >&2 <<EOF
@@ -3267,11 +3054,19 @@ This is free software licensed under the GNU General Public License.
EOF
- echo2 "${_header} [options] [file] [-] [[man:]manpage.x]";
- echo2 "${_gap} [[man:]manpage(x)] [[man:]manpage]...";
+ echo2 "Usage: ${_PROGRAM_NAME} ${_header} [option]... [filespec]...";
cat >&2 <<EOF
+where "filespec" is one of
+ "filename" name of a readablefile
+ "-" for standard input
+ "man:name.n" man page "name" in section "n"
+ "man:name" man page "name" in first section found
+ "name.n" man page "name" in section "n"
+ "name" man page "name" in first section found
+and some more (see groff(1) for details).
+
Display roff files, standard input, and/or Unix manual pages with
in a X window viewer or in a text pager.
"-" stands for including standard input.
@@ -3320,6 +3115,7 @@ The most important long options are
--pdf-viewer choose the viewer program for pdf mode.
--ps display in a Postscript viewer.
--ps-viewer choose the viewer program for ps mode.
+--shell specify shell under which to run this program.
--systems=os1,os2,...
search man pages for different operating systems.
--title='text' set the title of the viewer window (not in all modes.
@@ -3343,6 +3139,7 @@ EOF
version()
{
echo2 "${_PROGRAM_NAME} ${_PROGRAM_VERSION} of ${_LAST_UPDATE}";
+ # also display groff's version, but not the called subprograms
groff -v 2>&1 | sed -e '/^ *$/q' | sed -e '1s/^/is part of /' >&2;
}
@@ -3360,6 +3157,10 @@ warning()
########################################################################
# what_is (<filename>)
+#
+# Interpret <filename> as a man page and display its `whatis'
+# information as a fragment written in the groff language.
+#
what_is()
{
func_check what_is = 1 "$@";
@@ -3368,26 +3169,37 @@ what_is()
if is_not_file "$1"; then
error "what_is(): argument is not a readable file."
fi;
- _dot='^\.[ ]*';
+ _dot='^\.['"${_SPACE}${_TAB}"']*';
echo '.br';
echo "$1: ";
echo '.br';
echo -n ' ';
+ # grep the line containing `.TH' macro, if any
_res="$(catz "$1" | sed -e '/'"${_dot}"'TH /p
d')";
if is_not_empty "${_res}"; then # traditional man style
+ # get the text between the first and the second `.SH' macro, by
+ # - delete up to first .SH;
+ # - of this, print everything up to next .SH, and delete the rest;
+ # - of this, delete the final .SH line;
catz "$1" | sed -e '1,/'"${_dot}"'SH/d' \
| sed -e '1,/'"${_dot}"'SH/p
d' \
| sed -e '/'"${_dot}"'SH/d';
eval "${return_ok}";
fi;
- _res="$(catz "$1" | grep "${_dot}"'Dd ')";
+ # grep the line containing `.Dd' macro, if any
+ _res="$(catz "$1" | sed -e '/'"${_dot}"'Dd /p
+d')";
if is_not_empty "${_res}"; then # BSD doc style
- catz "$1" | sed -e '/'"${_dot}"'Nd /p
+ # get the text between the first and the second `.Nd' macro, by
+ # - delete up to first .Nd;
+ # - of this, print everything up to next .Nd, and delete the rest;
+ # - of this, delete the final .Nd line;
+ catz "$1" | sed -e '1,/'"${_dot}"'Nd/d' \
+ | sed -e '1,/'"${_dot}"'Nd/p
d' \
- | sed -e '2q' \
- | sed -e 's/'"${_dot}"'Nd *\(.*\)$/\1/';
+ | sed -e '/'"${_dot}"'Nd/d';
eval "${return_ok}";
fi;
echo 'is not a man page.';
@@ -3501,7 +3313,7 @@ main_init()
########################################################################
# main_parse_MANOPT ()
#
-# Parse $MANOPT.
+# Parse $MANOPT; this clobbered by the command line.
#
# Globals:
# in: $MANOPT, $_OPTS_MAN_*
@@ -3690,7 +3502,8 @@ main_parse_args()
_OPT_Z='yes';
;;
-?)
- _optchar="$(string_del_leading "${_opt}" '-')";
+ # delete leading `-'
+ _optchar="$(echo -n "${_opt}" | sed -e 's/^.//')";
if list_has "${_OPTS_GROFF_SHORT_NA}" "${_optchar}";
then
_ADDOPTS_GROFF="$(list_append "${_ADDOPTS_GROFF}" \
@@ -3874,6 +3687,9 @@ main_parse_args()
_OPT_SECTIONS="$1";
shift;
;;
+ --shell)
+ shift;
+ ;;
--systems) # man pages for different OS's, arg
# argument is a comma-separated list
_OPT_SYSTEMS="$1";
@@ -3912,7 +3728,7 @@ main_parse_args()
shift;
;;
*)
- error "error on argument parsing : \`$*'";
+ error 'error on argument parsing : '"\`$*'";
;;
esac;
done;
@@ -4390,6 +4206,7 @@ main_display()
# Some display programs have trouble with empty input.
# This is avoided by feeding a space character in this case.
+ # Test on non-empty file by tracking a line with at least 1 character.
if is_empty "$(tmp_cat | sed -e '/./q')"; then
echo ' ' > "${_TMP_CAT}";
fi;