diff options
author | wlemb <wlemb> | 2002-05-30 13:56:51 +0000 |
---|---|---|
committer | wlemb <wlemb> | 2002-05-30 13:56:51 +0000 |
commit | a20afffba417c6928d83a1472a1ab430c3c77a00 (patch) | |
tree | 678872ee6fd56b681fbcbf5d007fbf645cded073 /contrib/groffer | |
parent | 401849317227f20d5d78f9c1cfb194ebf2642c87 (diff) | |
download | groff-a20afffba417c6928d83a1472a1ab430c3c77a00.tar.gz |
* doc/Makefile.sub (CLEANADD): Add grnexmpl.g, groff, and groff-*
to list only if srcdir != currdir.
(distfiles): New target.
* Makefile.in (EXTRADIRS): Add font/devlj4/generate.
(NOMAKEDIRS): New variable.
(DISTDIRS): Use it.
________________________________________________________________
* release of groffer 0.6
This is almost a complete rewrite since groffer 0.5 .
________________________________________________________________
* Documentation
* groffer.man:
- Apply the changes done in www.tmac (.URL and .MTO)
- Replace \fP by \f[].
- Redesign and rewrite most macros.
- Include the documentation for the new features.
- Greatly enlarge section ENVIRONMENT
- Add examples.
* TODO:
- Start a TODO file with several sections.
* ChangeLog:
Due to the many changes, shorten and rearrange the entries
since groffer 0.5 .
________________________________________________________________
* Shell compatibility
* groffer.sh:
- Due to possible conflicts in old BSD versions, `[]' was
replaced by `test'; the `test' options `-a' and `-o' were
replaced by multiple calls of `test'.
- Write interface to the `sed' command `s' to become
independent of the delimiter character. Rewrite all text
manipulating function to use this new scheme. The new
functions are named `string_*'.
- `tr' is not needed any longer, replaced by `sed'.
- `grep' is not needed any longer, mostly replaced by `case'.
- Revision of test for `getopt'.
- Remove `set -a'; explicitly export variables.
- The only external programs used are POSIX `sed' and the
fallback to `apropos'. All other program calls were
replaced by shell builtins and functions.
________________________________________________________________
* Cosmetics
* groffer.sh:
- Implement a prefix based naming scheme for local variables
and functions (OOP-like).
- Introduce variables for white space (better readability with
$IFS).
- Store the names of the processed filespecs into a variable
instead of a temporary file.
- Error-prone shell constructions were replaced by functions
with a simple interface.
- To avoid too long pipes, replace supercat() by do_fileargs();
every input file is handled independently.
________________________________________________________________
* New features:
- Add support for more X devices (e.g. X75-12 and X100-12).
- Add long option `--intermediate_output' to `-Z'.
- Make the options for mode selection clobber each other.
- Add option `--mode' with an argument having the following
values:
`X': force displaying in X, same as options `-X';
`tty': display with a pager on text terminal; same as `--tty';
`source', `default', `auto', etc.
- Make the handling of the X mode like in groff (e.g. -X -Tps).
- Make resolution for gxditview behave like groff (default
75 dpi).
- Add environment variable $GROFFER_OPT to preset groffer
options.
________________________________________________________________
* implement most of the functionality of GNU `man'.
- Add all `man' long options to groffer.
- Add all `man' environment variables.
- Parse and use content of `$MANOPT'.
- The precedence of the options and environment variables
is regulated like in GNU `man'.
- Force the option `--manpath' to have a colon-separated
argument like GNU `man'.
- Support `man section name' calling convention.
- Remove all dependencies on `man -w'.
* groffer.sh:
- Add the new features above.
- Rewrite the search algorithm for man pages.
- Remove searching with `man -w' (problems with space
characters in file names).
- Fix and complement usage().
- The filespec parsers gets a function of its own do_manpage().
Diffstat (limited to 'contrib/groffer')
-rw-r--r-- | contrib/groffer/ChangeLog | 109 | ||||
-rw-r--r-- | contrib/groffer/groffer.man | 2168 | ||||
-rw-r--r-- | contrib/groffer/groffer.sh | 3733 |
3 files changed, 4499 insertions, 1511 deletions
diff --git a/contrib/groffer/ChangeLog b/contrib/groffer/ChangeLog index 07403444..f0b0ed14 100644 --- a/contrib/groffer/ChangeLog +++ b/contrib/groffer/ChangeLog @@ -1,17 +1,108 @@ -2002-02-17 Werner LEMBERG <wl@gnu.org> +2002-05-28 Bernd Warken <bwarken@mayn.de> - * groffer.man: Updated to latest changes in www.tmac. + ________________________________________________________________ + * release of groffer 0.6 -2002-01-08 Bernd Warken <bwarken@mayn.de> + This is almost a complete rewrite since groffer 0.5 . + ________________________________________________________________ + * Documentation + + * groffer.man: + - Apply the changes done in www.tmac (.URL and .MTO) + - Replace \fP by \f[]. + - Redesign and rewrite most macros. + - Include the documentation for the new features. + - Greatly enlarge section ENVIRONMENT + - Add examples. + + * TODO: + - Start a TODO file with several sections. + + * ChangeLog: + Due to the many changes, shorten and rearrange the entries + since groffer 0.5 . + ________________________________________________________________ + * Shell compatibility + + * groffer.sh: + - Due to possible conflicts in old BSD versions, `[]' was + replaced by `test'; the `test' options `-a' and `-o' were + replaced by multiple calls of `test'. + - Write interface to the `sed' command `s' to become + independent of the delimiter character. Rewrite all text + manipulating function to use this new scheme. The new + functions are named `string_*'. + - `tr' is not needed any longer, replaced by `sed'. + - `grep' is not needed any longer, mostly replaced by `case'. + - Revision of test for `getopt'. + - Remove `set -a'; explicitly export variables. + - The only external programs used are POSIX `sed' and the + fallback to `apropos'. All other program calls were + replaced by shell builtins and functions. + + ________________________________________________________________ + * Cosmetics + + * groffer.sh: + - Implement a prefix based naming scheme for local variables + and functions (OOP-like). + - Introduce variables for white space (better readability with + $IFS). + - Store the names of the processed filespecs into a variable + instead of a temporary file. + - Error-prone shell constructions were replaced by functions + with a simple interface. + - To avoid too long pipes, replace supercat() by do_fileargs(); + every input file is handled independently. + + ________________________________________________________________ + * New features: + - Add support for more X devices (e.g. X75-12 and X100-12). + - Add long option `--intermediate_output' to `-Z'. + - Make the options for mode selection clobber each other. + - Add option `--mode' with an argument having the following + values: + `X': force displaying in X, same as options `-X'; + `tty': display with a pager on text terminal; same as `--tty'; + `source', `default', `auto', etc. + - Make the handling of the X mode like in groff (e.g. -X -Tps). + - Make resolution for gxditview behave like groff (default + 75 dpi). + - Add environment variable $GROFFER_OPT to preset groffer + options. + + ________________________________________________________________ + * implement most of the functionality of GNU `man'. + + - Add all `man' long options to groffer. + - Add all `man' environment variables. + - Parse and use content of `$MANOPT'. + - The precedence of the options and environment variables + is regulated like in GNU `man'. + - Force the option `--manpath' to have a colon-separated + argument like GNU `man'. + - Support `man section name' calling convention. + - Remove all dependencies on `man -w'. + * groffer.sh: + - Add the new features above. + - Rewrite the search algorithm for man pages. + - Remove searching with `man -w' (problems with space + characters in file names). + - Fix and complement usage(). + - The filespec parsers gets a function of its own do_manpage(). + + +2002-01-08 Bernd Warken <bwarken@mayn.de> + * groffer 0.5 (beta) released * groffer.man: - Fix hyphenation problems with macros describing options. - Fix the handling of some `-' characters. - Examples of shell commands now print in font CR instead of CB. - - Remove documentation for option -X. - - Add documentation for option --dpi. + - Remove documentation for option `-X'. + - Add documentation for option `--dpi'. * groffer.sh: - New method for creating temporary files, based on process @@ -25,13 +116,13 @@ - Function usage() corrected and updated. - Unnecessary stuff removed. - Comments adjusted. - - Pass option -X to groff, i.e. force X output with 75 dpi. - - Implement option --dpi for setting the resolution for the X + - Pass option `-X' to groff, i.e. force X output with 75 dpi. + - Implement option `--dpi' for setting the resolution for the X viewer, which had already been documented in earlier versions. 2002-01-07 Bernd Warken <bwarken@mayn.de> - * groffer 0.4 (beta) released (integrated into groff `contrib') + * groffer 0.4 (beta) released (as groff `contrib') * groffer.man: - New features documented. @@ -114,7 +205,7 @@ 2001-11-28 Bernd Warken <bwarken@mayn.de> - * groffview 0.1 (experimental) and groffview.man released + ***** groffview 0.1 (experimental) and groffview.man released (predecessor of groffer, shell script) * Options : -h --help, -v --version diff --git a/contrib/groffer/groffer.man b/contrib/groffer/groffer.man index 41278240..c81b1252 100644 --- a/contrib/groffer/groffer.man +++ b/contrib/groffer/groffer.man @@ -1,8 +1,17 @@ +.TH GROFFER @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +groffer \- display groff files and man\~pages on X and tty +. +. +.\" -------------------------------------------------------------------- +.\" Legalize +.\" -------------------------------------------------------------------- +. .ig -groffer.man +groffer.man - man page for groffer (section 1). -Version : groffer 0.5 (beta) -Last update : 08 Jan 2002 +Version : groffer 0.6 +Last update : 28 May 2002 This file is part of groff, the GNU roff type-setting system. @@ -36,221 +45,500 @@ FDL in the main directory of the groff source package. . ftr CB CW .\} . -.ie t \{\ -. ds @- "\-\" -. ds @-- "\-\-\" -.\} -.el \{\ -. ds @- "-\" -. ds @-- "--\" -.\} +.ds @- "\-\"" +.ds @-- "\-\^\-\"" +. +.ds @b- "\f[CB]-\f[]\"" +.ds @b-- "\f[CB]--\f[]\"" +. +.ds @i- "\f[CI]-\f[]\"" +.ds @i-- "\f[CI]--\f[]\"" +. +.ds Ellpisis .\|.\|.\" +. +./" static registers (@+...) used for inter-macro communication" +.nr @.Shell_cmd_width 2m\" total width of prompt +. +.nr @+Synopsis_level 0 +. +.nr @+TP_level 0 +.rr @+TP_header +.rr @+TP_body +.rr @+TP_indent . -.ds Ellipsis .\|.\|.\" . .\" -------------------------------------------------------------------- .\" Start macro definitions -.eo . +.c -------------------------------------------------------------------- +.c .- ([<punct>]) +.c +.c Print `-' (minus sign); optional punctuation. +.c +.de - +. ie (\\n[.$] == 0) \ +. Opt_alt - "" +. el \ +. Opt_alt - "" "\\$1" +.. +.c -------------------------------------------------------------------- +.c .[-] ([<punct>]) +.c +.c Print `[-]' (minus sign in brackets); optional punctuation. +.c +.de [-] +. ie (\\n[.$] == 0) \ +. [Opt_alt] - "" +. el \ +. [Opt_alt] - "" "\\$1" +.. +.c -------------------------------------------------------------------- +.c .-- ([<punct>]) +.c +.c Print `--' (double minus); optional punctuation. +.c +.de -- +. ie (\\n[.$] == 0) \ +. Opt_alt -- "" +. el \ +. Opt_alt -- "" "\\$1" +.. +.c -------------------------------------------------------------------- +.c .[--] ([<punct>]) +.c +.c Print `[--]' (double minus in brackets); optional punctuation. +.c +.de [--] +. ie (\\n[.$] == 0) \ +. [Opt_alt] -- "" +. el \ +. [Opt_alt] -- "" "\\$1" +.. +.c -------------------------------------------------------------------- +.c .c ([<ignored>...]) +.c +.c Ignore all arguments like a comment, even after a .eo call. +.c .de c -.\" this is like a comment request when escape mechanism is off .. -.c -------------------------------------------------------------------- -.de Text -. nop \)\$* +.c -------------------------------------------------------------------- +.c .Error (<text>...) +.c +.c Print error message to terminal and abort. +.c +.de Error +. tm \\$* +. ab .. .c -------------------------------------------------------------------- -.c environment variable -.de EnvVar +.c .Env_var (<env_var_name> [<punct>]) +.c +.c Display an environment variable, with optional punctuation. +.c +.de Env_var . nh -. BR \$1 \$2 +. SM +. Text \f[CB]\\$1\f[]\\$2 . hy .. .c -------------------------------------------------------------------- -.c LongOpt ([name [punct]]) +.c .Long_opt ([<name> [<punct>]]) .c -.c `--name' somwhere in the text -.c second arg is punctuation +.c Print `--name' somewhere in the text; optional punctuation. .c -.de LongOpt -. ds @opt \$1\" -. shift -. nh -. Text \f[CB]\*[@--]\f[]\f[B]\*[@opt]\f[]\/\$* -. hy -. rm @opt +.de Long_opt +. Opt_alt -- "\\$1" "" "\\$2" .. .c -------------------------------------------------------------------- -.c [LongOpt] (name [arg]) +.c .[Long_opt] ([<name> [<punct>]]) .c -.c long option in synopsis +.c Print `--name' somewhere in the text; optional punctuation. .c -.de [LongOpt] -. if (\n[.$] == 0) \ -. return -. ds @opt \$1\" -. shift -. nh -. ie (\n[.$] == 0) \ -. Text \f[R][\f[]\f[CB]\*[@--]\f[]\f[B]\*[@opt]\f[]\f[R]]\f[] -. el \{\ -. Text \f[R][\f[]\f[CB]\*[@--]\f[]\f[B]\*[@opt] -. Text \f[]\f[I]\/\$*\f[]\f[R]]\f[] -. \} -. hy -. rm @opt +.de [Long_opt] +. [Opt_alt] -- "\\$1" "" "\\$2" .. .c -------------------------------------------------------------------- -.c OptDef (shortopt [longopt [argument]]) +.c .Opt_alt ([<minus> <opt>]... [<arg> [<punct>]]) .c -.c option documentation -.c args : `shortopt', `longopt' can be "" +.c Alternate options separated by a vertical bar. .c -.de OptDef -. rm @short -. rm @long -. rm @arg -. nh -. if (\n[.$] >= 1) \{\ -. ds @1 \$1\" -. if !'\*[@1]'' \ -. ds @short \f[CB]\*[@-]\*[@1]\f[]\" -. if (\n[.$] >= 2) \{\ -. if !'\*[@short]'' \ -. as @short \f[CW]\0\f[]\" -. ds @2 \$2\" -. if !'\*[@2]'' \ -. ds @long \f[CB]\*[@--]\f[]\f[B]\*[@2]\f[]\" -. if (\n[.$] >= 3) \{\ -. if !'\*[@long]'' \ -. as @long \|=\|\" -. shift 2 -. ds @arg \fI\$*\" -. \} +.c Arguments: +.c minus: either `-' or `--' (font CB). +.c opt: a name for an option, empty allowed (font CB). +.c arg: optionally, the argument to the option (font I). +.c punct: optional punctuation (in the starting font). +.c Result: +.c The minus/opt argument pairs, each +.c separated by a vertical bar `|', optionally add 'arg', separated +.c a space character ` '. +.c +.c Example: +.c .Opt_alt - T -- device -- device-troff device . +.c results in +.c -T|--device|--device-troff device. +.c +.de Opt_alt +. Opt_alt_base "" | "" \\$@ +.. +.c -------------------------------------------------------------------- +.c .[Opt_alt] ([<minus> <opt>]... [<arg> [<punct>]]) +.c +.c Alternate options in brackets for section SYNOPSIS. +.c +.c Arguments: +.c minus: either `-' or `--' (font CB). +.c opt: a name for an option, empty allowed (font CB). +.c arg: optionally, the argument to the option (font I). +.c punct: optional punctuation (in the starting font). +.c Global strings written to: +.c `@oa_prefix': left enclosing character (`[') +.c `@oa_sep': separator (`|') +.c `@oa_postfix': right enclosing character (`]') +.c Result: +.c The minus/opt argument pairs, each separated by a vertical +.c bar `|', optionally add 'arg', separated by a space character ` '. +.c +.c Example: +.c .[Opt_alt] - T -- device -- device-troff device . +.c results in +.c [-T|--device|--device-troff device]. +.c +.de [Opt_alt] +. Opt_alt_base [ | ] \\$@ +.. +.c -------------------------------------------------------------------- +.c .Opt_alt_base (<pre> <sep> <post> [<minus> <opt>]... [arg [punct]]) +.c +.c Alternating options; base macro for many others; do not use directly. +.c +.c Arguments: +.c <pre>: prefix, resulted is preceded by this. +.c <sep>: separator between minux/opt pairs. +.c <post>: postfix, is appended to the result. +.c <minus>: either `-' or `--' (font CB). +.c <opt>: a name for an option, empty allowed (font CB). +.c <arg>: optionally, the argument to the option (font I). +.c <punct>: optional punctuation (in the starting font). +.c Result: +.c String `<pre>' followed by the <minus>/<opt> argument pairs, each +.c separated by string `<sep>', optionally add '<arg>', separated by +.c a single space ` ', followed by the string `<post>'. +.c +.de Opt_alt_base +. nr @font \\n[.f]\" +. if (\\n[.$] < 3) \ +. Error .\\0: not enough arguments. +. ds @pre \)\\$1\)\" prefix +. ds @sep \)\\$2\)\" separator +. ds @post \)\\$3\)\" postfix +. shift 3 +. nr @count 0 +. ds @res \f[CW]\\*[@pre]\" +. while (\\n[.$] >= 2) \{\ +. c do the pairs, break on no `-' +. if !'\\$1'-' \{\ +. if !'\\$1'--' \ +. break +. \} +. c separator +. if (\\n[@count] > 0) \ +. as @res \f[CW]\\*[@sep]\" +. nr @count +1 +. as @res \f[CB]\\$1\\$2\:\" combine minus with option name +. shift 2 +. \} +. if (\\n[.$] >= 3) \ +. Error .\\0: wrong arguments: \\$@ +. c all pairs are done +. ie (\\n[.$] == 0) \ +. as @res \f[CW]\\*[@post]\" +. el \{\ +. c optional option argument +. if !'\\$1'' \ +. as @res \f[CW] \:\,\f[I]\\$1\" +. shift +. as @res \\f[CW]\\*[@post]\" postfix +. if (\\n[.$] >= 1) \{\ +. c add punctuation +. as @res \f[\\n[@font]]\\$1\" . \} . \} -. IP "\f[R]\*[@short]\*[@long]\*[@arg]\f[]" +. nh +. Text \\*[@res] . hy -. rm @1 -. rm @2 -. rm @arg -. rm @short -. rm @long +. ft \\n[@font] +. rr @count +. rr @font +. rm @pre +. rm @post +. rm @sep +. rm @res .. .c -------------------------------------------------------------------- -.c a shell command line -.de ShellCommand -. br -. ad l -. nh -. Text \f[I]shell#\h'1m'\f[]\f[CR]\$*\f[]\&\" -. ft R -. ft P -. hy -. ad +.c .Opt_def ([<minus> <opt>]... [<arg> [<punct>]]) +.c +.c Definitions of options in section OPTIONS. +.c +.c Arguments: +.c minus: either `-' or `--' (font CB). +.c opt: a name for an option, empty allowed (font CB). +.c arg: optionally, the argument to the option (font I). +.c punct: optional punctuation (in the starting font). +.c Result: +.c The header for an indented paragraph, consisting of +.c minus/opt argument pairs, each, separated by a space +.c character ` ', optionally add 'arg', separated a space +.c character ` '. +.c +.c Example: +.c .Opt_def - T -- device -- device-troff device . +.c results in +.c -T --device --device-troff device. +.c as the header of for indented paragraph. +.c +.de Opt_def +. TP +. Opt_alt_base "" "\~|\~" "" \\$@ .. .c -------------------------------------------------------------------- -.c ShellLongOpt ([name]) +.c .Opt_element ([<minus> <opt>]... [<arg> [<punct>]]) .c -.c `--name' in a shell command -.c second arg is punctuation +.c Definitions of options in section OPTIONS. .c -.de ShellLongOpt -. ds @1 \$1\" -. shift -. Text \*[@--]\*[@1] -. rm @1 +.c Arguments: +.c minus: either `-' or `--' (font CB). +.c opt: a name for an option, empty allowed (font CB). +.c arg: optionally, the argument to the option (font I). +.c punct: optional punctuation (in the starting font). +.c Result: +.c The minus/opt argument pairs, each, separated by a space +.c character ` ', optionally add 'arg', separated a space +.c character ` '. +.c +.c Example: +.c .Opt_element - T -- device -- device-troff device . +.c results in +.c -T --device --device-troff device. +.c +.de Opt_element +. Opt_alt_base "" "\~" "" \\$@ .. .c -------------------------------------------------------------------- -.c ShellShortOpt ([char]) +.als Opt_list Opt_element +. +.c -------------------------------------------------------------------- +.c .Shell_cmd (<CR> [<CI>] ...) .c -.c `-c' somwhere in the text -.c second arg is punctuation +.c A shell command line; display args alternating in fonts CR and CI. .c -.de ShellShortOpt -. ds @1 \$1\" -. shift -. Text \*[@-]\*[@1] -. rm @1 +.c Examples: +.c .Shell_cmd "groffer --dpi 100 file" +.c result: `sh# groffer --dpi 100 file' +.c with 'sh#' in font I, the rest in CR +.c +.c .Shell_cmd groffer\~--dpi\~100\~file +.c result: the same as above +.c +.c .Shell_cmd "groffer --dpi=" value " file" +.c result: sh# groffer --dpi=value file +.c with `groffer --dpi=' and `file' in CR; `value' in CI +.c +.c .Shell_cmd groffer\~--dpi= value \~file +.c result: the same as the previous example +.c +.de Shell_cmd +. Shell_cmd_base "sh#" \\$@ .. .c -------------------------------------------------------------------- -.c ShortOpt ([char [punct]]) +.c .Shell_cmd+ (<CR> [<CI>] ...) .c -.c `-c' somwhere in the text -.c second arg is punctuation +.c A continuation line for .Shell_cmd. .c -.de ShortOpt -. ds @opt \$1\" -. shift -. nh -. Text \f[CB]\*[@-]\f[]\f[B]\*[@opt]\fP\/\$* -. hy -. rm @opt +.de Shell_cmd+ +. Shell_cmd_base ">" \\$@ .. .c -------------------------------------------------------------------- -.c [ShortOpt] (name [arg]) +.c .Shell_cmd_base (<prompt> [<CR> [<CI>] ...]) .c -.c short option in synopsis +.c A shell command line; display args alternating in fonts CR and CI. .c -.de [ShortOpt] -. if (\n[.$] == 0] \ +.c Globals: read-only register @.Shell_cmd_width +.c +.de Shell_cmd_base +. if (\\n[.$] <= 0) \ . return -. ds @opt \$1\" +. nr @+font \\n[.f]\" +. ds @prompt \f[I]\\$1\" +. ft CR +. nr @+gap \\n[@.Shell_cmd_width]\" +. nr @+gap -\\w'\\*[@prompt]'\" gap between prompt and command +. ds @res \\*[@prompt]\h'\\n[@+gap]u'\" . shift -. nh -. ie (\n[.$] == 0) \ -. Text \f[R][\f[]\f[CB]\*[@-]\*[@opt]\f[]\f[R]]\f[] -. el \{\ -. Text \f[R][\f[]\f[CB]\*[@-]\*[@opt] -. Text \f[]\f[I]\/\$*\f[]\f[R]]\f[] +. ds @cf CR\" +. while (\\n[.$] > 0) \{\ +. as @res \\f[\\*[@cf]]\\$1\" +. shift +. ie '\\*[@cf]'CR' \ +. ds @cf I\" +. el \ +. ds @cf CR\" . \} +. br +. ad l +. nh +. nf +. Text \\*[@res]\" +. fi . hy -. rm @opt +. ad +. br +. ft \\n[@+font] +. rr @+font +. rr @+gap +. rm @cf +. rm @res +.. +.c -------------------------------------------------------------------- +.c .Short_opt ([<name> [<punct>]]) +.c +.c Print `-name' somewhere in the Text; optional punctuation. +.c +.de Short_opt +. Opt_alt - "\\$1" "" "\\$2" +.. +.c -------------------------------------------------------------------- +.c .[Short_opt] ([name [punct]]) +.c +.c Print `[-name]' somewhere in the Text; optional punctuation. +.c +.de [Short_opt] +. [Opt_alt] - "\\$1" "" "\\$2" .. .c -------------------------------------------------------------------- +.c .Synopsis () +.c +.c Begin a synopsis section, to be ended by a ./Synopsis macro. +.c .de Synopsis +. if (\\n[@+Synopsis_level] > 0) \ +. Error .\\$0: previous .Synopsis was not closed by ./Synopsis. . nh -. ds @1 \$1\" -. nr @old_indent \n(.i +. ds @1 \\$1\" +. nr @old_indent \\n(.i . ad l -. in +\w'\fB\*[@1]\0'u -. ti \n[@old_indent]u -. B \*[@1]\0\c +. in +\w'\fB\\*[@1]\0'u +. ti \\n[@old_indent]u +. B \\*[@1]\0\c . rr @old_indent . rm @1 +. nr @+Synopsis_level +1\" marker for ./Synopsis .. -.de EndSynopsis +.c -------------------------------------------------------------------- +.c ./Synopsis () +.c +.c Close a synopsis section opened by the previous .Synopsis macro. +.c +.de /Synopsis +. if (\\n[@+Synopsis_level] <= 0) \ +. Error .\\$0: no previous call of .Synopsis . br . ad . in . hy +. nr @+Synopsis_level -1 +.. +.c -------------------------------------------------------------------- +.c .Text (<text>...) +.c +.c Treat the arguments as text, no matter how they look. +.c +.de Text +. if (\\n[.$] == 0) \ +. return +. nop \)\\$*\) .. .c -------------------------------------------------------------------- -.c Topic +.c .Topic ([<indent>]) .c -.c a bulleted paragraph +.c A bulleted paragraph .c .de Topic -. TP 2m +. ie (\\n[.$] = 0) \ +. .ds @indent 2m\" +. el \ +. .ds @indent \\$1\" +. TP \\*[@indent] . Text \[bu] +. rm @indent .. .c -------------------------------------------------------------------- -.c Extends .TP header +.c .TP+ () +.c +.c Continuation line for .TP header. +.c .de TP+ . br . ns -. TP \$1 +. TP \\$1 +.. +.c -------------------------------------------------------------------- +.c .TP_header ([<indent>]) +.c +.c Start a multi-line header for a .TP-like paragraph +.c +.de TP_header +. if (\\n[@+TP_level] < 0) \ +. Error .\\$0: wrong level. +. nr @+TP_level +1 +. ie (\\n[.$] == 0) \ +. rr @+TP_indent +. el \ +. nr @+TP_indent \\$1 +. nr @+TP_header 1 +.. +.c -------------------------------------------------------------------- +.c .TP_body ([<indent>]) +.c +.c End a previous .TP-header and beging the body of the paragraph. +.c +.de TP_body +. if !r@+TP_header \ +. Error .\\$0: no previous call of .TP_header +. if (\\n[@+TP_level] <= 0) \ +. Error .\\$0: wrong level. +. br +. ie (\\n[.$] == 0) \{\ +. ie r@+TP_indent \{\ +. RS \\n[@+TP_indent]u +. \} +. el \ +. RS +. \} +. el \ +. RS \\$1u +. rr @+TP_indent +. rr @+TP_header +. nr @+TP_body 1 +.. +.c -------------------------------------------------------------------- +.c TP_end () +.c +.c End of former .TP_body paragraph. +.c +.de TP_end +. if !r@+TP_body \ +. Error .\\$0: no previous .TP_body. +. if (\\n[@+TP_level] <= 0) \ +. Error TP_end: wrong level. +. nr @+TP_level -1 +. rr @+TP_indent +. rr @+TP_header +. rr @+TP_body +. br +. RE .. -.ec -.\" End of macro definitions -. -. -.\" -------------------------------------------------------------------- -.\" Title -.\" -------------------------------------------------------------------- . -.TH GROFFER @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" -.SH NAME -groffer \- display groff files and man\~pages on X and tty +.\" End of macro definitions . . .\" -------------------------------------------------------------------- @@ -259,92 +547,177 @@ groffer \- display groff files and man\~pages on X and tty . .ad l .Synopsis groffer -.[ShortOpt] Q -.[ShortOpt] T device -.[ShortOpt] W arg -.[LongOpt] dpi resolution -.[LongOpt] man -.[LongOpt] manpath man_page_dirs -.[LongOpt] no\*[@-]man -.[LongOpt] title "'title in X mode'" -.[LongOpt] tty -.[LongOpt] xrdb X_resouce_options +.RI [ mode_options ] +.RI [ man_options ] .RI [ groff_options ] -.RI [ filespec -.Text \*[Ellipsis]] -.EndSynopsis +.[--] +.RI [ "filespec" "\*[Ellipsis]]" +./Synopsis . .Synopsis groffer -.ShortOpt h -| -.LongOpt help -.EndSynopsis +.Opt_alt - h -- help +./Synopsis . .Synopsis groffer -.ShortOpt v -| -.LongOpt version -.EndSynopsis +.Opt_alt - v -- version +./Synopsis +. +. +.TP +.I mode_options +These options determine and configure the display mode. +. +They are all compatible with the options in both +.BR groff (@MAN1EXT@) +and GNU +.BR man (1). . .P -where each element of the +.RS +.[Opt_alt] - P to_postprocessor +.[Opt_alt] - Q -- source +.[Opt_alt] - T -- device -- troff-device device +.[Opt_alt] - W arg +.[Opt_alt] - X +.[Opt_alt] - Z -- ditroff -- intermediate-output +.[Opt_alt] -- dpi value +.[Opt_alt] -- mode display_mode +.[Opt_alt] -- pager program +.[Opt_alt] -- title string +.[Opt_alt] -- tty +.RE +. +. +.TP +.I man_options +These options regulate whether and how man pages are searched. +. +They are compatible with the long options of the +.I GNU man +program. +. +. +.P +.RS +.[Opt_alt] -- all +.[Opt_alt] -- apropos +.[Opt_alt] -- extension suffix +.[Opt_alt] -- lang -- locale language +.[Opt_alt] -- local-file +.[Opt_alt] -- location -- where +.[Opt_alt] -- man +.[Opt_alt] -- manpath man_page_dirs +.[Opt_alt] -- no-location +.[Opt_alt] -- no-man +.[Opt_alt] -- sections colon_list +.[Opt_alt] -- systems comma_list +.[Opt_alt] -- whatis +.RE +. +The full set of long and short options of the GNU man program can be +passed via the environment variable +.Env_var $MANOPT , +see +.BR man (1) +if your system has GNU man installed. +. +. +.TP +.I groff_options +can be any combination of short options from the +.I groff +program; the options that are not explicitly handled by groffer are +transparently passed to groff. +. +Almost any letter in both lower and upper case represents some groff +option, see +.BR groff (@MAN1EXT@). +. +. +.TP .I filespec -sequence has one of the following forms, where testing is done in the -specified sequence. +is a sequence of file names or template parameters for searching +man\~pages, see +.BR man (1), +having one of the following forms. +. +. +.RS . .TP 10m .I filename the path name of an existing file. . +. .TP -.ShortOpt +.Short_opt stands for standard input (can occur several times). . +. .TP .BI man: name ( section ) search the man\~page .I name -in -.IR section , -the same as -.IB name . section -above +in section\~\c +.IR section . +. . .TP .BI man: name . section -the manual page (man\~page) +search the man\~page .I name -in -.IR section , -see -.BR man (1). +in section\~\c +.IR section . +. . .TP .BI man: name search the man\~page .I name -in the lowest section. +in the lowest available section. +. +. +.TP +.IB name ( section ) +search the man\~page +.I name +in section\~\c +.IR section . +. . .TP .IB name . section -the manual page (man\~page) +search the man\~page .I name -in -.IR section , -see -.BR man (1). +in section\~\c +.IR section . +. . .TP -.BI other_name -if not an existing file search the man\~page -.I other_name -in the lowest section. +.I standard_section +if this is `1', \*[Ellipsis], `9', `o', or `n' try to retrieve the +next argument as a man\~page in this section. +. +. +.TP +.I name +search for the man\~page +.I name +in the lowest available section. +. . .P No .I filespec parameters means standard input. . +.RE +. +. +.P +For details on the options, see section +.BR OPTIONS . +. . .\" -------------------------------------------------------------------- .SH DESCRIPTION @@ -363,6 +736,18 @@ using the graphical viewer program .BR gxditview (@MAN1EXT@), otherwise text output is displayed in a pager on the terminal. . +. +.P +A search facility for manual pages (man\~pages) is provided. +. +Almost the whole functionality of the +.I GNU man +program was provided or suitably adapted. +. +This makes the groffer program a valuable tool on systems with a bad +man program. +. +. .P The program always concatenates all input specified by the non-option parameters of the calling command line, or standard input if none are @@ -370,13 +755,6 @@ given. . Compressed standard input or files are decompressed on-the-fly. . -.P -Moreover, groffer provides a search facility for manual pages -(man\~pages). -. -This makes the groffer program a graphical extension of the -.BR man (1) -program. . .P Normally, the input is run through the @@ -384,12 +762,13 @@ Normally, the input is run through the text processor before being displayed. . By using the option -.ShortOpt Q , -the roff source code is displayed without being processed. +.Short_opt Q , +the roff source code is displayed without formatting. +. . .P The -.ShortOpt T +.Short_opt T option allows to produce output for any output devices provided by groff. . @@ -412,135 +791,467 @@ program. .\" -------------------------------------------------------------------- . If your system does not support GNU long options you can use the -.ShortOpt W +.Short_opt W to simulate long options. . POSIX has reserved this option for such uses. . +. .P -The following options are caught be groffer and have a special +All +.B groff +options are valid for groffer. +. +The groff options that are transparently transferred to groff are not +documented here. +. +The following options are caught by groffer and have a special meaning. . -.OptDef h help +. +.Opt_def - h Print usage message to standard error and exit. . -.OptDef Q source +. +.Opt_def - Q Output the roff source code of the input files unprocessed. . -.OptDef T device devname +. +.Opt_def - P opt_or_arg +Pass the argument as an option to the actual postprocessor, for +example to +.I gxditview +when displaying in X. +. +If the postprocessor option needs to be preceded by a single or double +minus sign then this prefix must also be specified in the argument to +option +.Short_opt P . +. +If the postprocessor option needs an argument this must be specified +as the argument of another, directly following +.Short_opt P +option. +. +. +.RS +. +.Opt_def - P \*[@i-]opt +Pass +.I \*[@i-]opt +unchanged to the actual postprocessor. +. +. +.Opt_def - P \*[@i--]opt +Pass +.I \*[@i--]opt +unchanged to the actual postprocessor. +. +. +.TP_header +.Opt_element - P \*[@i-]opt\~\c +.Opt_element - P arg +.TP_body +Pass +.I \*[@i-]opt arg +unchanged to the actual postprocessor. +.TP_end +. +. +.TP_header +.Opt_element - P \*[@i--]opt\~\c +.Opt_element - P arg +.TP_body +Pass +.I \*[@i--]opt arg +unchanged to the actual postprocessor. +.TP_end +. +.RE +. +. +See also section +.BR EXAMPLES . +. +. +.Opt_def - T devname Use .I devname -as the output device, just like in plain groff; -if this is -.B X75 -the X output with 75 dpi is selected, with -.B X100 -X output will have 100 dpi, which is the default anyway; all other -devices generate output that was processed for the specified device; -this is printed onto standard output without a pager. -. -.OptDef v version +as the output device, just like in plain groff. +. +The allowed device names are listed in +.BR groff (@MAN1EXT@). +. +All device names that do begin with the letter +.I X +force displaying in an X window using gxditview. +. +All other device names generate output for the specified device; this +is printed onto standard output without a pager. +. +. +.Opt_def - v Print version information onto standard error. . -.OptDef W "" arg -There are 3 applications for the -.ShortOpt W -option: -.br -.ShortOpt W -.I longopt -is equivalent to -.LongOpt longopt -.br -.ShortOpt W -.I 'longopt=arg' -is equivalent to -.LongOpt longopt -.I 'arg' +. +.Opt_def - W arg +There are two applications for this option. +. +If the argument +.I arg +starts with a +.-- +(double minus sign), it represents a long option, which is useful on +systems that do not support the GNU long options; otherwise it is +passed as option +.Short_opt W +to groff (disable a warning), see +.BR troff (@MAN1EXT@). +. +. +.RS +. +.TP +.Opt_alt - W "\*[@i--]longopt" +is equivalent to the groffer long option +.Long_opt longopt +without an argument. +. +. +.TP_header +.Opt_alt - W "\*[@i--]longopt\f[CW]=\f[]optarg" .br -.ShortOpt W -.I warning -is equivalent to the groff option -.ShortOpt W +.Opt_alt - W "\*[@i--]longopt" +.Opt_alt - W optarg +.TP_body +are both equivalent to the groffer long option +.Long_opt longopt +with the argument +.IR optarg . +.TP_end +. +. +.TP +.Opt_alt - W warning +is equivalent to the groff/troff option +.Short_opt W .I warning -and is internally sent to groff. +(disable the warning named +.IR warning ); +internally, this is sent transparently to groff. +. +More exactly, an argument to +.Short_opt W +that is supposed to represent long options must always be specified +together with a leading +.-- +(double minus); an argument to the option must either be appended to +the long option with a separating \f[CB]=\f[] (equals sign) or goes as +argument to another +.Short_opt W +option. +. +. +.P +An argument to +.Short_opt W +that does not start with a leading +.-- +(double minus) is interpreted as a groff warning option. +. +.RE +. +. +.Opt_def - X +Force displaying in an X window using gxditview. +. +This option was adapted from groff. +. +. +.Opt_def - Z +Display the groff intermediate output instead of the formatted input; +equivalent to +.Opt_alt -- mode Z . +. +The short option +.Short_opt Z +is inhereted from +.BR groff (@MAN1EXT@) , +while the name of the long option +.Long_opt ditroff +originates from +.BR man (1) +and was only implemented for compatibility. +. +. +.Opt_def -- all +In searching man pages, retrieve all suitable ones. +. +. +.Opt_def -- apropos +Instead of displaying, start the `apropos' command for searching +within man page descriptions; only kept for compatibility with `man'. +. +. +.Opt_def -- bg color +Set background color of display window. +. +The argument is an X color name, see +.BR (1) +for details. +. +If the actual display mode is not X then this option is ignored. +. . -.OptDef "" dpi "resolution" -Set resolution of the X viewer. +.Opt_def -- display X-diplay +Set the X display on which gxditview should be started, see +.BR (1) +for details. +. +If the actual display mode is not X then this option is ignored. +. +. +.Opt_def -- ditroff +Eqivalent to +.Short_opt Z . +This is kept for compatibiliy with GNU +.BR man (1). +. +. +.Opt_def -- dpi value +Set resolution of the X viewer +.BR gxditview (@MAN_EXT1@). . The only supported dpi values are .B 75 and .BR 100 . -The writings -.B 75dpi -and -.B 100dpi -are also recognized as a valid argument. +The resolution can also be forced by specifying option +.Opt_alt - TX value . +. +If the actual display mode is not X then this option is ignored. +. +. +.Opt_def -- extension suffix +Restrict man\~page search to file names that have +.I suffix +appended to their section element. +. +For example, in the file name +.I /usr/share/man/man3/terminfo.3ncurses.gz +the man\~page extension is +.IR ncurses . +. +Originates from GNU +.IR man . +. +. +.Opt_def -- fg color +Set foreground color of display window. +. +The argument is an X color name, see +.BR (1) +for details. +. +If the actual display mode is not X then this option is ignored. +. +. +.Opt_def -- device +Eqivalent to +.Short_opt T . +. +. +.Opt_def -- geometry size_pos +Set the geometry of the display window, that means its size and its +starting position. +. +See +.BR (1) +for details on the syntax of the argument. . -.OptDef "" man +If the actual display mode is not X then this option is ignored. +. +. +.Opt_def -- help +Eqivalent to +.Short_opt h . +. +. +.Opt_def -- intermediate-output +Equivalent to +.Short_opt Z . +. +. +.Opt_def -- lang language +. +Set the language for man pages. +. +. +.Opt_def -- location +Print the location of the retrieved files to standard error. +. +. +.Opt_def -- locale language +. +Equivalent to +.Long_opt lang . +. +This option originates from GNU +.BR man (1). +. +. +.Opt_def -- man Check the non-option command line arguments (filespecs) first on being man\~pages, then whether they represent an existing file. . By default, a filespec is first tested if it is an existing file. . -.OptDef "" manpath "'dir1 dir2 \*[Ellipsis]'" -.br -.ns -.OptDef "" manpath "'dir1:dir2:\*[Ellipsis]'" +. +.Opt_def -- manpath "'dir1:dir2:\*[Ellipsis]'" Use the specified search path for retrieving man\~pages instead of the program defaults. . If the argument is set to the empty string "" the search for man\~page is disabled. . -.OptDef "" no\*[@-]man +. +.Opt_def -- mode value +. +Set the display mode. +. +The following mode values are recognized: +. +. +.RS +. +.TP +.B auto +Let groffer choose where to display; this is the default. +. +. +.TP +.B tty +Display in a text terminal; same as +.Long_opt tty . +. +. +.TP +.B X +Display in a X window; same as +.Short_opt X . +. +. +.TP +.BR Q +Display source code; same as +.Short_opt Q . +. +. +.TP +.B source +Display source code; same as +.Short_opt Q . +. +. +.TP +.B Z +Display groff intermediate output; same as +.Short_opt Z . +. +. +.TP +.B intermediate-output +Display groff intermediate output; same as +.Short_opt Z . +. +. +.TP +.B default +Display in the default manner, being actually +.IR auto . +Useful for restoring default behavior when other options are active. +. +.RE +. +. +.Opt_def -- no-location +Do not display the location of retireved files; this resets a former +call to +.Long_opt location . +. +. +.Opt_def -- no-man Do not check for man\~pages. . -.OptDef "" title "'some title of your own'" +. +.Opt_def -- pager +Set the pager program in tty mode; default is +.IR less . +. +. +.Opt_def -- sections +Restrict searching for man pages to the given +.IR sections , +a colon-separated list. +. +. +.Opt_def -- source +Equivalent to +.Short_opt Q . +. +. +.Opt_def -- systems +Search for man pages for the given operating systems; the argument +.I systems +is a comma-separated list. +. +. +.Opt_def -- title "'this is my title'" Set the title for the diplay window. . This effects only the X mode. . -.OptDef "" tty -Use output on a text pager even when in X. . -.OptDef "" xrdb "'\*[@-]\fIopt1 arg1\fP \*[@-]\fIopt2 arg2 ...\fP'" -Pass the argument unchanged to the X display program gxditview of -groffer. +.Opt_def -- to-postproc opt_or_arg +Eqivalent to +.Short_opt P . . -All options of -.BR gxditview (@MAN1EXT@) -are allowed; this inludes -.ShortOpt bg -(background color), -.ShortOpt display -(the X display to be used), -.ShortOpt geometry -(size and position of the window), -.ShortOpt fg -(foreground color), -.ShortOpt fn -(font), -.ShortOpt xrm -(set general X resource), -and many more, see -.BR X (1). -Several -.LongOpt xrdb -options can be specified or several resource options can be stuffed -into a single -.LongOpt xrdb -option. . -But note that this is not correctly parsed when the argument for a -resource option contains a space or an embedded single quote character, -even when they are escaped. +.Opt_def -- troff-device +Eqivalent to +.Short_opt T . +This option is only kept for compatibility with GNU +.BR man (1). +. +. +.Opt_def -- tty +Choose tty display mode, that means use output on a text pager even +when in X; eqivalent to +.Long_opt mode\~tty . +. +. +.Opt_def -- version +Eqivalent to +.Short_opt v . +. +. +.Opt_def -- whatis +Instead of displaying the content, get the one-liner description from +the retrieved man page files \[em] or say that it is not a man page. +. +. +.Opt_def -- where +Eqivalent to +.Long_opt location . +. . .TP -.LongOpt \" just `--' +.-- Signals the end of option processing; all remaining arguments are -interpreted as filespec parameters. +interpreted as +.I filespec +parameters. +. . .P Besides these, groffer accepts all arguments that are valid for the @@ -554,174 +1265,229 @@ and much more can be manually specified. . . .\" -------------------------------------------------------------------- -.SH FEATURES +.SH "OUTPUT MODES" .\" -------------------------------------------------------------------- . -This chapter describes the details of the features of the groffer -program in detail. +By default, the groffer program formats the input and then +automatically chooses a suitable display mode, but the user can also +choose between the following modes: +. +.Topic +graphical display in X window with gxditview, +. +.Topic +text display in a pager on the text terminal (tty), +. +.Topic +generate output for a given device, +. +.Topic +source code streamed onto standard output, +. +.Topic +the groff intermediate output streamed onto standard output (for +debugging). +. +. +.P +If no mode selecting option was provided, the input is formatted and +diplayed; displaying on X is tried first, then the paging on the text +terminal. +. +The other modes cannot be reached automatically, they must be +specified by the user. . . .\" -------------------------------------------------------------------- -.SS "Output modes" +.SS "Displaying in X window" .\" -------------------------------------------------------------------- . -The groffer program provides 4 different operation modes, -.Topic -graphical display in X, +The X mode can be chosen by the following methods: +. +. .Topic -text display in a pager, +Automatically, if the environment variable +.Env_var $DISPLAY +is set and no other mode was forced; +. +. .Topic -output for a given device streamed onto standard output. +by the options +.Short_opt X , +.Long_opt X , +or +.Opt_alt -- mode X ; +. +. .Topic -source code streamed onto standard output. +by specifying one of the groff X* devices with option +.Short_opt T . +. . .P -Normally, the input is processed by groff and then displayed in a -viewer, either in X or on a text terminal. -. -If the environment variable -.EnvVar $DISPLAY -is set or one of the options -.ShortOpt X , -.ShortOpt TX100 , +In X mode, the formatted input is displayed with the +.BR gxditview (@MAN1EXT@) +program, which can use two resolutions, +.I 75 dpi or -.ShortOpt TX75 -is set the -.B gxditview -program will be started on the X terminal that is specified by the -.EnvVar $DISPLAY -variable. +.IR "100 dpi" , +where +.I dpi +means +.IR "dots per inch" . +By default, groffer follows groff, which actually chooses 75 dpi. +. +. +.P +This resolution can be changed to 100 dpi by one of the following +options: +. +.Topic +.Opt_alt -- dpi\~100 +. +.Topic +.Opt_alt - T\~X100 +. +.Topic +.Opt_alt - T\~X100-12 +. +.Topic +.Opt_alt - P\~resolution - P\~100 +. . .P -X provides two resolutions, the old value -.B 75 dpi -and the more modern value of -.BR "100 dpi" , -which should be available on almost all modern computers. +By replacing +.I 100 +by +.I 75 +in these options, the corresponding 75 dpi mode is activated. . -By default, the X resolution of 100 dpi is used if there are -corresponding fonts available; this is checked by questioning the X -font path using the shell command -.ShellCommand xset q . .P -The lower resolution can be explicitly specified by the option -.ShortOpt TX75 ; -the option -.ShortOpt X -is inherited from groff, actually it chooses 75 dpi as well. +The resolution can be preset to a fixed value for all calls to groffer +by including the option +.Long_opt dpi +into the environment variable +.Env_var GROFFER_OPT . +For example, +.Shell_cmd "GROFFER_OPT='--dpi 100'" +.Shell_cmd "export GROFFER_OPT" +sets 100 dpi as default resolution for all groffer runs in this shell +and its subshells. +. . .P +Note that the +.Opt_alt - TX ... +options force displaying in X, while the +.Long_opt dpi +is ignored when not displaying in X. +. +. +.\" -------------------------------------------------------------------- +.SS "Displaying on a tty" +.\" -------------------------------------------------------------------- +. If the variable -.EnvVar $DISPLAY +.Env_var $DISPLAY is not set or empty groffer assumes that it should produce output on a text terminal. . +This mode can be enforced by the options +.Long_opt tty +or +.Opt_alt -- mode tty . +. +. +.P In the actual implementation, the groff output device .I latin1 is chosen and the processed output is piped into a pager program. . -The pager (together with options) can be specified by the environment -variable -.EnvVar $PAGER . +The pager can be specified by the environment variable +.Env_var $PAGER . If this is not set or empty the .BR less (1) program is used as the default pager. . -.P -Besides the two display modes above, for X and text pager, there are -two more operating modes. . -These are streaming modes, that means both of them print to standard -output without using a pager. +.\" -------------------------------------------------------------------- +.SS "Displaying roff Source Files" +.\" -------------------------------------------------------------------- +. +Instead of the formatted output, it is also possible to have the +source code displayed. +. +This mode must deliberately be requested by one of the options +.Short_opt Q , +.Long_opt source , +.Long_opt "mode Q" , +.Long_opt "mode source" . +. . .P +The retrieved source files are decompressed and streamed to standard +output without any pager or further processing. +. +. +.\" -------------------------------------------------------------------- +.SS "Output for a specific Device" +.\" -------------------------------------------------------------------- +. If a device other than the .B X devices is specified with the -.ShortOpt T -option groffer assumes that the user wants to pipe the output into -some kind of postprocessor. +.Short_opt T +then the input is formatted for this device and then send to standard +output without further actions. +. +This enables the user save the generated output into a file of pipe it +into some kind of postprocessing. . -The reason for this is that the output of many devices, such as +. +.P +Output for some devices, such as .BR ps , or .B dvi -is not directly displayable. -. -.P -The user may also choose to read the source code of the input by -specifying the -.ShortOpt Q -option. -. -Again, no output pager is used to allow piping. +is not directly displayable without a special viewer. . . .\" -------------------------------------------------------------------- -.SS Decompression +.SH "FILE PARAMETERS" .\" -------------------------------------------------------------------- . -The program has a decompression facility. -. -If standard input or a file that is specified as a command line -parameter was compressed by a format that is understood by -.BR gzip (1) -it is decompressed on-the-fly. -. -This includes the GNU -.B .gz -and the traditional -.B .Z -compression. -. -The program displays the concatenation of all decompressed input in -the sequence that was specified on the command line. +The non-option command line parameters determine which files should be +displayed. . . .\" -------------------------------------------------------------------- -.SS "Man\~Pages" +.SS "Filespecs" .\" -------------------------------------------------------------------- . -The groffer program provides a search facility for system manual pages -(man\~pages). +The default behavior of groffer is to first test whether the file +parameter is represents a local file; if not, it is assumed to +represent a filespec for searching one or more man\~page. . -So it can be used as a replacement or a grapical extension for the -.BR man (1) -program. +This behavior can be modified by options. . -.P -Preformatted man\~pages (cat\~pages) are intentionally not supported, -because groffer is a roff program, not a text pager. . -With the excellent performance of the actual computers, the -preformatted man\~pages aren't necessary any longer. +.TP +.Long_opt man +forces to interpret all file parameters as filespecs for searching +man\~pages. . -Due to their inflexible nature, they provoke some trouble with -changing line lengths. +.TP +.Long_opt no-man +.TP+ +.Long_opt local_file +disable the man searching; so only local files are displayed. . -These cat\~pages should be disabled with the man\~program, or be -circumvented by using groffer instead. . .P -The groffer program determines if the user wanted to diplay a man\~page -by the following methods. -. -If a non-option command line parameter does not represent a valid file -name groffer suspects whether this could be a system manual page -(man\~page). -. The following parameter formats are recognized to represent a wanted man\~page. . -.TP -.IB name . section -the man\~page -.I name -in -.IR section . -The corresponding command with the man program would be -.ShellCommand man \f(CIsection name\fP . .TP .BI man: name ( section ) @@ -731,13 +1497,25 @@ represent the man\~page in .IR section . . +. .TP .BI man: name search the man\~page .I name in the lowest section. The corresponding command with the man program would be -.ShellCommand man \f(CIname\fP +.Shell_cmd man\~ name +. +. +.TP +.IB name . section +the man\~page +.I name +in +.IR section . +The corresponding command with the man program would be +.Shell_cmd man\~ section\~name +. . .TP .I name @@ -746,78 +1524,291 @@ if is not an existing file search for the man\~page .I name in the lowest section just like -.ShellCommand man \f(CIname\fP +.Shell_cmd man\~ name +. +. +.TP +.I section name +Even this curious construct known from the various +.I man +programs is handled. +. +For example, +.Shell_cmd "groffer 7 groff" +was modelled according to +.Shell_cmd "man 7 groff" +retrieves the man\~page named +.I groff +in section 7. +. +Only a few standard section names are accepted, being actually the +number sections +.I 1, 2, 3, 4, 5, 6, 7, 8, +and +.IR 9, +and the lower case letters +.I `o' +and +.IR `n' . +. +. +.P +If neither a local file nor a man\~page was retrieved for some file +parameter a warning is issued on standard error, but processing is +continued. +. +. +.\" -------------------------------------------------------------------- +.SS "Man\~Page Searching" +.\" -------------------------------------------------------------------- +. +The groffer program provides a search facility for system manual pages +(man\~pages). +. +All long options, all environment variables, and most of the +functionality of the GNU +.BR man (1) +program were implemented. +. +. +.P +Preformatted man\~pages (cat\~pages) are intentionally excluded from +the search because groffer is a roff program that wants to format by +its own, not spit out stuff that was digested previously by someone +else. +. +With the excellent performance of the actual computers, the +preformatted man\~pages aren't necessary any longer. +. +Due to their inflexible nature, they tend to provoke some trouble with +changing line lengths and different environments in networks. +. . .P The algorithm for retrieving man\~pages uses five search methods. . They are successively tried until a method works. . +. .Topic The search path can be manually specified by using the option -.LongOpt manpath . +.Long_opt manpath . An empty argument disables the man\~page searching. . This overwrites the other methods. . +. .Topic -The best results are obtained when the -.BR man (1) -program has the command line option -.ShortOpt w -to determine the path of a man\~page. +If this is not available the environment variable +.Env_var $MANPATH +is searched. . -This is available in the GNU version of -.IR man . . .Topic -If this isn't available the environment variable -.EnvVar $MANPATH -is searched. +If this is empty, the program tries to read it from the environment +variable +.Env_var $MANOPT . . .Topic -If this is empty the +If this does not work, the .BR manpath (1) program for determining a path of man directories is tried. . +. .Topic If this does not work a reasonable default path is searched for man\~pages. . +. +.P +After this, the path elements for the language (locale) and operating +system specific man\~pages are added to the man\~path; their sequence +is determined automatically. +. +For example, both +.I /usr/share/man/linux/fr +and +.I /usr/share/man/fr/linux +for french linux man\~pages are found. +. +The language and operating system names are determined from both +environment variables and command line options. +. +. +.P +The locale is determined like in GNU man, that is from highest to +lowest precedence: +.Topic +.Long_opt lang +and +.Long_opt locale +. +.Topic +.Env_var $GROFFER_OPT +. +.Topic +.Env_var $MANOPT +. +.Topic +.Env_var $LCALL +. +.Topic +.Env_var $LC_MESSAGES +. +.Topic +.Env_var $LANG . +. +. +.P +The language locale is usually specified in the POSIX 1003.1 based +format: +.P +\f[I]<language>\f[][\f[CB]_\f[]\f[I]<territory>\f[][\f[CB].\f[]\ +\f[I]<character-set>\f[][\f[CB],\f[]\f[I]<version>\f[]]]]. +. +. +.P +If no man\~pages for a complicated locale are found the country part +consisting of the first two characters (without the `\f[CB]_\f[]', +`\f[CB].\f[]', and `\f[CB],\f[]', parts) of the locale is searched as +well. +. +. +.P +If still not found the corresponding man\~page in the default language +is used instead. +. +As usual, this default can be specified by one of \f[CW]C\f[] or +\f[CW]POSIX\f[]. +. +The man\~pages in the default language are usually in English. +. +. +.P +Several operating systems can be given by appending their names, +separated by a comma. +. +This is then specified by the environment variable +.Env_var $SYSTEM +or by the command line option +.Long_opt systems . +The precedence is similar to the locale case above from highest to +lowest precedence: +. +Topic +.Long_opt systems +. +.Topic +.Env_var $GROFFER_OPT +. +.Topic +.Env_var $MANOPT +. +.Topic +.Env_var $SYSTEM . +. +. +.P +When searching for man\~pages this man\~path with the additional +language and system specific directories is used. +. +. +.P +The search can further be restricted by limiting it to certain +sections. +. +A single section can be specified within a filespec, several sections +as a colon-separated list in command line option +.Long_opt sections +or environment variable +.Env_var $MANSECT . +. +When no section was specified a set of standard sections is searched +until a suitable man\~page was found. +. +. +.P +Finally, the search can be restricted to a so-called +.IR extension . +This is a postfix that acts like a subsection. +. +It can be specified by +.Long_opt extension +or environment variable +.Env_var $EXTENSION . +. +. .P -In all cases, language-specific man\~pages are searched first if the -environment variable -.EnvVar $LANG -is set. +For further details on man\~page searching, see +.BR man (1). . . .\" -------------------------------------------------------------------- -.SS "Source Code" +.SS Decompression .\" -------------------------------------------------------------------- . -Usually, groffer displays the input in formatted form. +The program has a decompression facility. . -When, however, the option -.ShortOpt Q -is specified on the command line the source code of the input is -displayed instead; more exactly, it is printed onto standard output -as is, without any pager or other formatter. +If standard input or a file that was retrieved from the command line +parameters is compressed with a format that is supported by +.BR gzip (1) +it is decompressed on-the-fly. . -.P -In this source code displaying mode, the decompression and man\~page -search features are still active. +This includes the GNU +.B .gz +and the traditional +.B .Z +compression. +. +The program displays the concatenation of all decompressed input in +the sequence that was specified on the command line. . -As no formatter or X window program is run in this mode all opotions -different from -.ShortOpt Q -are silently ignored. . .\" -------------------------------------------------------------------- .SH "ENVIRONMENT" .\" -------------------------------------------------------------------- . +The groffer programs supports many system variables, most of them by +courtesy of other programs. +. +All environment variables of +.BR groff (@MAN1EXT@) +and GNU +.BR man (1) +and some standard system variables are honored. +. +. +.\" -------------------------------------------------------------------- +.SS "Native groffer Variables" +.\" -------------------------------------------------------------------- +. .TP -.EnvVar $DISPLAY +.Env_var GROFFER_OPT +Store options for a run of groffer. +. +The options specified in this variable are overridden by the options +given on the command line. +. +The content of this variable is run through the shell builitin `eval'; +so arguments containing white-space or special shell characters should +be quoted. +. +. +.\" -------------------------------------------------------------------- +.SS "System Variables" +.\" -------------------------------------------------------------------- +. +The groffer program is a shell script that is run through +.BR /bin/sh , +which can be internally linked to programs like +.BR bash (1). +The corresponding system environment is automatically effective. +. +The following variables have a special meaning for groffer. +. +. +.TP +.Env_var DISPLAY If this variable is set this indicates that the X window system is running. . @@ -829,29 +1820,157 @@ be used to start the graphical groffer on a remote X terminal. . For example, depending on your system, groffer can be started on the second monitor by the command -.ShellCommand DISPLAY=:0.1 groffer what.ever & +.Shell_cmd DISPLAY=:0.1\~groffer\~ what.ever & +. +. +.TP +.Env_var LC_ALL +.TP+ +.Env_var LC_MESSAGES +.TP+ +.Env_var LANG +If one of these variables is set (in the above sequence), its content +is interpreted as the locale, the language to be used, especially when +retrieving man\~pages. +. +A locale name is typically of the form +.IR language [\c +.B _\c +.IR territory [\c +.B .\c +.IR codeset [\c +.B @\c +.IR modifier ]]], +where +.I language +is an ISO 639 language code, +.I territory +is an ISO 3166 country code, and +.I codeset +is a character set or encoding identifier like ISO-8859-1 or UTF-8; +see +.BR setlocale (3). +. +The locale values\~\c +.B C +and +.B POSIX +stand for the default, i.e. the man\~page directories without a +language prefix. +. +This is the same behavior as when all 3\~variables are unset. +. . .TP -.EnvVar $PAGER +.Env_var PAGER This variable can be used to set the pager for the tty output. . For example, to disable the use of a pager completely set this variable to the .BR cat (1) program -.ShellCommand PAGER=cat groffer anything +.Shell_cmd PAGER=cat\~groffer\~ anything +. . .TP -.EnvVar $MANPATH -if set, this variable contains the directories in which the man\~page -trees are stored. +.Env_var PATH +All programs within the groffer shell script are called without a +fixed path. +. +Thus this environment variable determines the set of programs used +within the run of groffer. +. +. +.\" -------------------------------------------------------------------- +.SS "Groff Variables" +.\" -------------------------------------------------------------------- +. +The groffer program internally calls groff, so all environment +variables documented in +.BR groff (@MAN1EXT@) +are internally used within groffer as well; see there for details. +. +The following variables have a direct meaning for the groffer program. . .TP -.EnvVar $LANG - -if set, this variable contains the directories in which the man\~page +.Env_var GROFF_TMPDIR +If the value of this variable is an existing, writable directory, +groffer uses it for storing its temporary files, just as groff does. +. +. +.\" -------------------------------------------------------------------- +.SS "Man Variables" +.\" -------------------------------------------------------------------- +. +Parts of the functionality of the man program were implemented in +groffer; support for all environment variables documented in +.BR man (1) +was added to groffer, but the meaning was slightly modified due to the +different approach in groffer; but the user interface is the same. +. +The man environment variables can be overwritten by options provided +with +.Env_var MANOPT , +which in turn is overwritten by the command line. +. +. +.TP +.Env_var EXTENSION +Restrict the search for man\~pages to files having this extension. +. +This is overridden by option +.Long_opt extension ; +see there for details. +. +. +.TP +.Env_var MANOPT +This variable contains options as a preset for +.BR man (1). +As not all of these are relevant for groffer only the essential parts +of its value are extracted. +. +The options specified in this variable overwrite the values of the +other environment variables taht are specific to man. +. +All options specified in this variable are overridden by the options +given on the command line. +. +. +.TP +.Env_var MANPATH +If set, this variable contains the directories in which the man\~page trees are stored. . +This is overridden by option +.Long_opt manpath . +. +. +.TP +.Env_var MANSECT +If this is a colon separated list of section names, the search for +man\~pages is restricted to those manual sections in that order. +. +This is overridden by option +.Long_opt sections . +. +. +.TP +.Env_var SYSTEM +If this is set to a comma separated list of names these are interpreted +as man\~page trees for different operating systems. +. +This variable can be overwritten by option +.Long_opt systems ; +see there for details. +. +. +.P +The environment variable +.Env_var MANROFFSEQ +is ignored by groffer because the necessary preprocessors are +determined automatically. +. . .\" -------------------------------------------------------------------- .SH "EXAMPLES" @@ -859,63 +1978,75 @@ trees are stored. . The usage of groffer is very easy. . -Usually, it is just called with a filename or man\~page. +Usually, it is just called with a file name or man\~page. . The following examples, however, show that groffer has much more fancy capabilities. . +. .TP -.ShellCommand groffer docs/meintro.ms +.Shell_cmd "groffer\~/usr/local/share/doc/groff/meintro.ms" Format and display the file .I meintro.ms -in directory -.I ./docs +in the directory +.IR /usr/local/share/doc/groff , using a graphical viewer when in X window, or the -.B less +.BR less (1) pager program when not in X. . -The file must exist in this case because the slash character -.RB ` / ' -suggests that this cannot be a man\~page. . .TP -.ShellCommand groffer groffer.1 \[cq]less(1)\[cq] man:roff +.Shell_cmd "groffer\~groff.7\~groff\~\[cq]troff(1)\[cq]\~man:gxditview" . -If none of the arguments is an existing file then lookup the three +If none of the arguments is an existing file then lookup the four man\~pages named -.I groffer -(in section\~1), -.I less +.I groff +(in section\~7), +.I groff +(automatic search, should be found in section\~1), +.I troff (in section\~1), +.I gxditview +(automatic search, should be found in section\~1), and .I roff (in the section with the lowest number, being\~7 in this case). . +The quotes around +.I \[cq]troff(1)\[cq] +are necessary because the paranthesis are special shell characters; +escaping them with a backslash character +.I \[rs]( +and +.I \[rs]) +would be possible, too. +. The formatted files are concatenated and displayed in one piece. . +. .TP -.ShellCommand LANG=de groffer\~\c -.ShellLongOpt man\~\c -.ShellLongOpt source\~\c -.ShellShortOpt T\~\c -.Text html\~ls\~>ls.html +.Shell_cmd "LANG=de\~groffer\~--man\~-Thtml\~ls\~>ls.html" . -Lookup the source file of the German manual page for the +Lookup the German manual page for the .B ls -program, decompress it, convert it into htlm format and write the +program, decompress it, convert it into html format and write the result into the file -.IR ls.html +.IR ls.html . +The option +.Long_opt man +guarantees that the man\~page is retrieved, even when a local file +.I ls +exists in the actual directory. +. . .TP -.ShellCommand cat stdin.1.gz | PAGER=more groffer\~\c -.ShellLongOpt tty\~\c -.Text a.ms\~\c -.ShellShortOpt \~\c -.Text b.ms.Z +.Shell_cmd "cat\~stdin.1.gz\~|" +.TP+ +.Shell_cmd+ "PAGER=more\~groffer\~--tty\~a.ms\~-\~b.ms.Z" . Display existing files .IR a.ms , -.IR stdin.1.gz (decompress), +.IR the standard input (being here the file stdin.1.gz) (decompress), and .IR b.ms.Z (decompress) in this sequence on the text terminal, using @@ -923,50 +2054,56 @@ in this sequence on the text terminal, using as pager program instead of the default pager .BR less . . +. .TP -.ShellCommand groffer\~\c -.ShellShortOpt Wno\c -.ShellShortOpt man\~\c -.ShellShortOpt Wtitle=hello\~\c -.ShellShortOpt Wmac\~\c -.Text groff.1 -. -Assume that -.I groff.1 -is not meant to denote a man\~page, but an existing file -in the current directory. -. -Process this file with the -.RI ` mac ' -warnings disabled and display the result using -.RI ` hello ' -as the viewer window's title. +.Shell_cmd "groffer\~-X\~-W--title\~-W\~hello\~-W\~mac\~file" . The first two -.option W -options simulate long options; but as +.Short_opt W +options simulate groffer's long option +.Long_opt title +with its argument +.IR hello . +. +But as .I mac -is not a long option for groffer it is passed unchanged to groff, -where it disables the specified warnings. +does not start with a minus sign, the third +.Short_opt W +option is passed unchanged to +.BR groff (@MAN1EXT@), +where it disables the warning named +.IR mac . +. +The retrieved file is processed with the +.RI ` mac ' +warnings disabled and the result is displayed in X window with the +default resolution, using +.RI ` hello ' +as the viewer window's title. . This command is equivalent to -.ShellCommand groffer\~\c -.ShellLongOpt title=hello\~\c -.ShellLongOpt man\~\c -.ShellShortOpt W\~\c -.Text mac groff.7 +.Shell_cmd "groffer\~-X\~--title=hello\~-Wmac\~file" +. . .TP -.ShellCommand echo\~\[rs]fIgroffer\[rs]fP\~is\~\[rs]fBfat\[rs]fP |\& +.Shell_cmd "echo\~'\[rs]f[CB]WOW!'\~|" .TP+ -.ShellCommand groffer\~\c -.ShellLongOpt xrdb=\[cq]\c -.ShellShortOpt bg\~\c -.Text red\~\c -.ShellShortOpt fg\~\c -.Text yellow\[cq] +.Shell_cmd+ "groffer TX100 --bg red --fg yellow --geometry 200x100" +. +Display \f[CB]WOW!\f[] in a small window in constant-width bold font, +using color yellow on red background. . -This looks a bit strange, try it :\-) +If your shell does not have long options the options must be written as +.br +\f[CW]-TX100\~\:-W--bg\~\:-Wred\~\:-W--fg\~\:-Wyellow\~\:-W\c +--geometry\\~\:200x100\f[]. +.br +The equivalent options when using +.Short_opt P +are +.br +\f[CW]-TX100\~\:-P-bg\~\:-Pred\~\:-P-fg\~\:-Pyellow\~\:-P\c +-geometry 200x100\f[]. . . .\" -------------------------------------------------------------------- @@ -975,25 +2112,45 @@ This looks a bit strange, try it :\-) . The .B groffer -shell script should be compatible to both POSIX and GNU. +shell script is compatible to both POSIX and GNU. . POSIX compatibility refers to .B P1003.2/D11.2 of September 1991. . +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. +. +. +.P +Common implementations of the POSIX shell +.BR sh (1) +include +.BR bash (1), +.BR ksh (1), +and others. +. +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 -This document describes the behavior on GNU systems. +On systems without GNU +.BR getopt (1), +long options might not be available. . -Due to the limitations of POSIX as compared to GNU, not all features -of groffer are available on non-GNU systems. +In these cases, the groffer option +.Short_opt W +can be used to specify long options nevertheless; the option character +.B W +was reserved by the POSIX standard for applications like this. . -This includes arguments with embedded space characters, and the search -capabilities of man\~pages. . .P The groffer program can handle option arguments and file names that -contain space characters, but mutliple space characters are flattened -to a single space character. +contain any white space characters. . . .\" -------------------------------------------------------------------- @@ -1005,6 +2162,11 @@ to a single space character. the GNU roff program. . .TP +.BR troff (@MAN1EXT@) +details on some options, environment variables and the warnings used +in groff. +. +.TP .BR grog (@MAN1EXT@) tries to guess the groff command line options for given input files. . @@ -1022,9 +2184,15 @@ decompression of .gz or .Z files. .BR man (1) the standard way to diplay man\~pages. . +The +.I man +options and environment variables that are supported by groffer refer +to +.IR "GNU man" . +. . .\" -------------------------------------------------------------------- -.SH "AUTHORS" +.SH "AUTHOR" .\" -------------------------------------------------------------------- . Copyright (C) 2001, 2002 Free Software Foundation, Inc. diff --git a/contrib/groffer/groffer.sh b/contrib/groffer/groffer.sh index 683bc00d..f758ea6d 100644 --- a/contrib/groffer/groffer.sh +++ b/contrib/groffer/groffer.sh @@ -1,12 +1,10 @@ #!/bin/sh -# groffer.sh +# groffer - display groff files -PROGRAM_NAME=groffer -PROGRAM_VERSION="0.5 (beta)" -LAST_UPDATE="08 Jan 2002" +# File position: <groff-source>/contrib/groffer/groffer -# Copyright (C) 2001 Free Software Foundation, Inc. +# Copyright (C) 2001,2002 Free Software Foundation, Inc. # Written by Bernd Warken <bwarken@mayn.de> # This file is part of groff. @@ -22,242 +20,688 @@ LAST_UPDATE="08 Jan 2002" # License for more details. # You should have received a copy of the GNU General Public License -# along with groff; see the file COPYING. If not, write to the Free -# Software Foundation, 59 Temple Place - Suite 330, Boston, MA -# 02111-1307, USA. +# along with groff; see the file COPYING. If not, write to the +# Free Software Foundation, 59 Temple Place - Suite 330, Boston, +# MA 02111-1307, USA. + +export _PROGRAM_NAME; +export _PROGRAM_VERSION; +export _LAST_UPDATE; + +_PROGRAM_NAME=groffer +_PROGRAM_VERSION="0.6" +_LAST_UPDATE="27 May 2002" ######################################################################## -# Description +# Description ######################################################################## -# Display groff files on X or tty, even when zipped. +# Display groff files and man pages on X or tty, even when compressed. -# This script tries to avoid features of special shells, so some -# elements are programmed in a more complicated way than necessary. +### Usage +# Input comes from either standard input or command line parameters +# that represent either names of exisiting roff files or standardized +# specifications for man pages. All of these can be compressed in a +# format that is decompressible by `gzip'. + +# Five displaying modes are available: +# 1) Display processed input with the X roff viewer `gxditview'. +# 2) Display processed input in a text terminal using a text device. +# 3) Generate output for some groff device on stdout without a viewer. +# 4) Output only the source code without any groff processing. +# 5) Generate the troff intermediate output on standard output +# without groff postprocessing. +# By default, the program tries to display with `gxditview' (1); if +# this does not work, text display (2) is used. + + +### Error handling -######################################################################## -# Debugging -######################################################################## # Error handling and exit behavior is complicated by the fact that -# `exit' can only escape from the current shell. This leads to trouble -# in `$()' subshells. +# `exit' can only escape from the current shell; trouble occurs in +# subshells. This was solved by sending kill signals, see +# $_PROCESS_ID and error(). # -# for debugging only -#set -x -#set -v -function diag () -{ - echo '>>>>>' "$@" >&2; -} -function abort () -{ - [ $# -gt 0 ] && diag "$@"; - error 2>/dev/null || exit 1; -} +### TODO + +# Add to existing man path the wanted system, language, +# section/extension directories. Do not assume a fixed sequence of +# the 3 additions above. So run all additions 3 times. ######################################################################## -# Setup +# Compatibility ######################################################################## -export PROGRAM_NAME; -export PROGRAM_VERSION; -export LAST_UPDATE; +# This script is compatible to POSIX and GNU. It works best in a GNU +# system. Care was taken to restrict the programming technics used +# here in order to achieve POSIX compatibility as far back as POSIX +# P1003.2 Draft 11.2 of September 1991. -set -a +# In GNU, long options and the mixing of options and file name +# parameters are available. In non-GNU environments, long options can +# be simulated by preceding the long option and its argument by the +# option `-W', which was reserved by POSIX for such usage. All +# groffer features are accessible, but the usage is not as comfortable +# as in GNU systems. -# Survey of functions defined in this document + +######################################################################## +# General Setup +######################################################################## + +# set -a +# set -x +# set -v + +######################################################################## +# Survey of functions defined in this document +######################################################################## # The elements specified within paranthesis `(<>)' give hints to what # the arguments are meant to be; the argument names are irrelevant. # <>? 0 or 1 -# <>* arbitrarily many incl. none +# <>* arbitrarily many such arguments, incl. none +# <>+ one or more such arguments # <> exactly 1 -# append_args (<arg>*) +# A function that starts with an underscore `_' is an internal +# function for some function. The internal functions are defined just +# after their corresponding function; they are not mentioned in the +# following. + +# abort (text>*) # base_name (path) -# catz (<file>*) -# check_dpi () +# catz (<file>) # clean_up () -# count_next_quoted (<arg>*) -# del_all_leading_from (<regexp> <string>) -# del_ext_from (<extension> <filename>) +# clean_up_secondary () +# diag (text>*) +# dirname_append (<path> [<dir...>]) +# dirname_chop (<path>) +# do_filearg (<filearg>) +# do_nothing () # echo2 (<text>*) -# error (<err_no> <text>*) -# get_manpath () -# get_next_quoted (<arg>*) -# is_substring_of (<part> <string>) +# echo2n (<text>*) +# error (<text>*) +# get_first_essential (<arg>*) +# is_dir (<name>) +# is_empty (<string>) +# is_equal (<string1> <string2>) +# is_file (<name>) +# is_not_empty (<string>) +# is_not_equal (<string1> <string2>) +# is_prog (<name>) +# is_yes (<string>) # leave () -# make_title () -# manpage_search_filespec (<filespec>) -# manpage_search_name (<name> <section>?) -# normalize_args (<arg>*) -# output (<text>*) +# main_*(), see after the functions +# man_do_filespec (<filespec>) +# man_is_setup () +# man_register_file (<file> [<name> [<section>]]) +# man_search_section (<name> <section>) +# manpath_add_lang(<path> <language>) +# manpath_add_system() +# manpath_from_path () +# normalize_args (<shortopts> <longopts> <arg>*) +# path_chop (<path>) +# path_clean (<path>) +# path_contains (<path> <dir>) +# path_not_contains (<path> <dir>) # register_title (<filespec>*) -# save_stdin_if_any () -# shift_quoted (<arg>*) -# supercat (<filearg>*) +# save_stdin () +# string_contains (<string> <part>) +# string_del_leading (<string> <regex>) +# string_del_trailing (<string> <regex>) +# string_flatten() +# string_replace_all (<string> <regex> <replace>) +# string_sed_s (<string> <regex> [<replace> [<flag>]]) # tmp_cat () # tmp_create (<suffix>?) -# unquote (<arg>*) +# to_tmp (<filename>) # usage () # version () +# warning (<string>) +# whatis (<filename>) +# where (<program>) ######################################################################## # Environment Variables ######################################################################## -# Environment variables that are regarded as global to this file are -# written in upper case letters and can use the underline character -# inside, e.g. $GLOBAL_VARIABLE; local variables start with an -# underline and use only lower case letters and underlines, e.g. -# $_local_variable . - -# [A-Z]* system variables, e.g. $MANPATH -# [A-Z][A_Z_]* global file variables, e.g. $MAN_PATH -# _[a-z_]* local variables, e.g. $_manpath -# _[a-z_] local loop variables, e.g. $_i - -# global variables -DISPLAY_MODE="" # determined from command line arguments -FILE_ARGS="" # the non-option command line parameters -HAS_COMPRESSION="" # `yes' if compression is available -HAS_MANW="" # `yes' if `man -w' is available -HAS_OPTS_GNU="" # `yes' if GNU `getopt' is available -HAS_OPTS_POSIX="" # `yes' if POSIX `getopts' is available -MAN_PATH="" # search path for man-pages -OTHER_OPTIONS="" # given non-native command line options -OPT_SOURCE="" # source code option (`Quellcode') -OPT_DEVICE="" # device option -OPT_DPI="" # groff -X option -OPT_MAN="" # interpret file params as man-pages -OPT_MANPATH="" # manual setting of path for man-pages -OPT_TITLE="" # title for gxditview window -OPT_TTY="" # use text display instead of X -OPT_XRDB="" # X resource arguments to gxditview -TEMP_DIR="" # directory for temporary files -TEMP_PREFIX="" # directory for temporary files -TMP_CAT="" # stores concatenation of everything -TMP_INPUT="" # stores stdin, if any -TMP_TITLE="" # stores the names of processed args - -# command line arguments -GROFFER_LONGOPTS="help man no-man source tty version"; -GROFFER_ARG_LONGS="device: dpi: manpath: title: xrdb:"; -GROFFER_SHORTOPTS="hQT:v"; -GROFF_ARG_SHORTS="d:f:F:I:L:m:M:n:o:P:r:w:W:"; # inhereted from groff -GROFF_SHORTOPTS="abcegilpstzCEGNRSUVXZ"; # inhereted from groff -ALL_SHORTOPTS=\ -"${GROFFER_SHORTOPTS}${GROFF_SHORTOPTS}${GROFF_ARG_SHORTS}"; -ALL_LONGOPTS="${GROFFER_LONGOPTS} $GROFFER_ARG_LONGS"; - -PROCESS_ID="$$" # for shutting down the program - -ENABLE_MANPAGES=yes # enable search for man-pages +# Environment variables that exist only for this file start with an +# underscore letter. Global variables to this file are written in +# upper case letters, e.g. $_GLOBAL_VARIABLE; temporary variables +# start with an underline and use only lower case letters and +# underlines, e.g. $_local_variable . + +# [A-Z]* system variables, e.g. $MANPATH +# _[A-Z_]* global file variables, e.g. $_MAN_PATH +# _[a-z_]* temporary variables, e.g. $_manpath +# _[a-z] local loop variables, e.g. $_i ######################################################################## -# System Test +# external system environment variables that are explicitly used + +export DISPLAY; # Presets the X display. +export LANG; # For language specific man pages. +export LC_ALL; # For language specific man pages. +export LC_MESSAGES; # For language specific man pages. +export OPTARG; # For option processing with getopts(). +export OPTIND; # For option processing with getopts(). +export PAGER; # Paging program for tty mode. +export PATH; # Path for the programs called (: list). + +# groffer native environment variables +export GROFFER_OPT # preset options for groffer. + +# all groff environment variables are used, see groff(1) +export GROFF_BIN_PATH; # Path for all groff programs. +export GROFF_COMMAND_PREFIX; # '' (normally) or 'g' (several troffs). +export GROFF_FONT_PATH; # Path to non-default groff fonts. +export GROFF_TMAC_PATH; # Path to non-default groff macro files. +export GROFF_TYPESETTER; # Preset default device. +export GROFF_TMPDIR; # Directory for groff temporary files. + +# variables from `man' +export EXTENSION; # Restrict to man pages with extension. +export MANOPT; # Preset options for man pages. +export MANPATH; # Search path for man pages (: list). +export MANSECT; # Search man pages only in sections (:). +export SYSTEM; # Man pages for different OS's (, list). +export MANROFFSEQ; # Ignored because of grog guessing. + +# do not export IFS. +unset IFS; + + ######################################################################## +# read-only variables (global to this file) -# Test the availability of the system utilities used in this script. +export _SPACE; +export _TAB; +export _NEWLINE; + +_SPACE=' '; +_TAB=' '; +_NEWLINE=' +'; + +# function return values; `0' means ok; other values are error codes +export _BAD; +export _BAD2; +export _BAD3; +export _ERROR; +export _GOOD; +export _NO; +export _OK; +export _YES; + +_GOOD='0'; # return ok +_BAD='1'; # return negatively, error code `1' +_BAD2='2'; # return negatively, error code `2' +_BAD3='3'; # return negatively, error code `3' +_ERROR='-1'; # syntax errors, etc. + +_NO="$_BAD"; +_YES="$_GOOD"; +_OK="$_GOOD"; + +export _PROCESS_ID; # for shutting down the program +_PROCESS_ID="$$"; + +# Search automatically in standard sections `1' to `8', and in the +# traditional sections `9', `n', and `o'. On many systems, there +# exist even more sections, mostly containing a set of man pages +# special to a specific program package. These aren't searched for +# automatically, but must be specified on the command line. +export _MAN_AUTO_SEC; +_MAN_AUTO_SEC="1 2 3 4 5 6 7 8 9 n o" + + +############ the command line options of several programs +# +# The naming scheme for the options environment names is +# $_OPTS_<prog>_<length>[_<argspec>] +# +# <prog>: program name GROFFER, GROFF, or CMDLINE (for all +# command line options) +# <length>: LONG (long options) or SHORT (single character options) +# <argspec>: ARG for options with argument, NA for no argument; +# without _<argspec> both the ones with and without arg. +# +# Each option that takes an argument must be specified with a +# trailing : (colon). + + +###### native groffer options + +export _OPTS_GROFFER_SHORT_NA; +export _OPTS_GROFFER_SHORT_ARG; +export _OPTS_GROFFER_LONG_NA; +export _OPTS_GROFFER_LONG_ARG; + +_OPTS_GROFFER_SHORT_NA="hQvXZ"; +_OPTS_GROFFER_SHORT_ARG="P:T:W:"; + +_OPTS_GROFFER_LONG_NA="all apropos help intermediate-output \ +local-file location \ +man no-location no-man source title tty version whatis where"; + +_OPTS_GROFFER_LONG_ARG="bg: device: display: dpi: extension: fg: \ +geometry: lang: locale: manpath: mode: pager: resolution: sections: \ +systems: title: to-postproc: troff-device: xrm:"; + +##### options inhereted from groff + +export _OPTS_GROFF_SHORT_NA; +export _OPTS_GROFF_SHORT_ARG; +export _OPTS_GROFF_LONG_NA; +export _OPTS_GROFF_LONG_ARG; + +_OPTS_GROFF_SHORT_NA="abcegilpstzCEGNRSUV"; +_OPTS_GROFF_SHORT_ARG="d:f:F:I:L:m:M:n:o:r:w:"; +_OPTS_GROFF_LONG_NA=""; +_OPTS_GROFF_LONG_ARG=""; + +###### man options (for parsing $MANOPT only) + +export _OPTS_MAN_SHORT_ARG; +export _OPTS_MAN_SHORT_NA; +export _OPTS_MAN_LONG_ARG; +export _OPTS_MAN_LONG_NA; + +_OPTS_MAN_SHORT_ARG="e:L:m:M:p:P:r:S:T:"; +_OPTS_MAN_SHORT_NA="acdDfhkltuVwZ"; + +_OPTS_MAN_LONG_ARG="extension: lang: locale: manpath: pager: \ +preprocessor: prompt: sections: systems: troff-device:"; + +_OPTS_MAN_LONG_NA="all apropos catman debug default ditroff help \ +local-file location troff update version whatis where"; + +###### collections of options + +# groffer + +export _OPTS_GROFFER_LONG; +export _OPTS_GROFFER_SHORT; +_OPTS_GROFFER_LONG="${_OPTS_GROFFER_LONG_ARG} ${_OPTS_GROFFER_LONG_NA}"; +_OPTS_GROFFER_SHORT=\ +"${_OPTS_GROFFER_SHORT_ARG}${_OPTS_GROFFER_SHORT_NA}"; + +# groff + +export _OPTS_GROFF_LONG; +export _OPTS_GROFF_SHORT; +_OPTS_GROFF_LONG="${_OPTS_GROFF_LONG_ARG} ${_OPTS_GROFF_LONG_NA}"; +_OPTS_GROFF_SHORT="${_OPTS_GROFF_SHORT_ARG}${_OPTS_GROFF_SHORT_NA}"; + +# all command line options + +export _OPTS_CMDLINE_SHORT_NA; +export _OPTS_CMDLINE_SHORT_ARG; +export _OPTS_CMDLINE_SHORT; +export _OPTS_CMDLINE_LONG_NA; +export _OPTS_CMDLINE_LONG_ARG; +export _OPTS_CMDLINE_LONG; + +_OPTS_CMDLINE_SHORT_NA="\ +${_OPTS_GROFFER_SHORT_NA}${_OPTS_GROFF_SHORT_NA}"; +_OPTS_CMDLINE_SHORT_ARG="\ +${_OPTS_GROFFER_SHORT_ARG}${_OPTS_GROFF_SHORT_ARG}"; +_OPTS_CMDLINE_SHORT="${_OPTS_GROFFER_SHORT}${_OPTS_GROFF_SHORT}"; + +_OPTS_CMDLINE_LONG_NA="${_OPTS_GROFFER_LONG_NA} \ +${_OPTS_GROFF_LONG_NA} ${_OPTS_MAN_LONG_NA}"; +_OPTS_CMDLINE_LONG_ARG="${_OPTS_GROFFER_LONG_ARG} \ +${_OPTS_GROFF_LONG_ARG} ${_OPTS_MAN_LONG_ARG}"; +_OPTS_CMDLINE_LONG="${_OPTS_GROFFER_LONG} ${_OPTS_GROFF_LONG}"; + + +######################################################################## +# read-write variables (global to this file) + +export _DISPLAY_MODE; # From command line arguments. +export _DISPLAY_PAGER; # Pager to be used on tty. +export _FILEARGS; # Stores filespec parameters. +export _ADDOPTS_GROFF; # Transp. options for groff (`eval'). +export _ADDOPTS_POST; # Transp. options postproc (`eval'). +export _ADDOPTS_X; # Transp. options X postproc (`eval'). +export _REGISTERED_TITLE; # Processed file names. +_ADDOPTS_GROFF=''; +_ADDOPTS_POST=''; +_ADDOPTS_X=''; +_DISPLAY_MODE=''; +_DISPLAY_PAGER=''; +_FILEARGS=''; +_REGISTERED_TITLE=''; + +# _HAS_* from availability tests +export _HAS_COMPRESSION; # `yes' if compression is available +export _HAS_OPTS_GNU; # `yes' if GNU `getopt' is available +export _HAS_OPTS_POSIX; # `yes' if POSIX `getopts' is available +_HAS_COMPRESSION=''; +_HAS_OPTS_GNU=''; +_HAS_OPTS_POSIX=''; + +# _MAN_* finally used configuration of man searching +export _MAN_ALL; # search all man pages per filespec +export _MAN_ENABLE; # enable search for man pages +export _MAN_EXT; # extension for man pages +export _MAN_FORCE; # force file parameter to be man pages +export _MAN_IS_SETUP; # setup man variables only once +export _MAN_LANG; # language for man pages +export _MAN_LANG_DONE; # language dirs added to man path +export _MAN_PATH; # search path for man pages +export _MAN_SEC; # sections for man pages; sep. `:' +export _MAN_SEC_DONE; # sections added to man path +export _MAN_SYS; # system names for man pages; sep. `,' +export _MAN_SYS; # system names added to man path +_MAN_ALL='no'; +_MAN_ENABLE='yes'; # do search for man-pages +_MAN_EXT=''; +_MAN_FORCE='no'; # first local file, then search man page +_MAN_IS_SETUP='no'; +_MAN_LANG=''; +_MAN_LANG_DONE='no'; +_MAN_PATH=''; +_MAN_SEC=''; +_MAN_SEC_DONE='no'; +_MAN_SYS=''; +_MAN_SYS_DONE='no'; + +# _MANOPT_* as parsed from $MANOPT +export _MANOPT_ALL; # $MANOPT --all +export _MANOPT_EXTENSION; # $MANOPT --extension +export _MANOPT_LANG; # $MANOPT --locale +export _MANOPT_PATH; # $MANOPT --manpath +export _MANOPT_PAGER; # $MANOPT --pager +export _MANOPT_SEC; # $MANOPT --sections +export _MANOPT_SYS; # $MANOPT --systems +_MANOPT_ALL='no'; +_MANOPT_EXTENSION=''; +_MANOPT_LANG=''; +_MANOPT_PATH=''; +_MANOPT_PAGER=''; +_MANOPT_SEC=''; +_MANOPT_SYS=''; + +# _OPT_* as parsed from groffer command line +export _OPT_ALL; # display all suitable man pages +export _OPT_APROPOS; # branch to `apropos' program +export _OPT_DEVICE; # device option +export _OPT_LANG; # set language for man pages +export _OPT_LOCATION; # print processed file names to stderr +export _OPT_MODE; # values: X, tty, Q, Z, "" +export _OPT_MANPATH; # manual setting of path for man-pages +export _OPT_PAGER; # specify paging program for tty mode +export _OPT_SECTIONS; # sections for man page search +export _OPT_SYSTEMS; # man pages of different OS's +export _OPT_TITLE; # title for gxditview window +export _OPT_WHATIS; # print the one-liner man info +export _OPT_XRDB; # X resource arguments to gxditview +_OPT_ALL='no'; +_OPT_APROPOS='no'; +_OPT_DEVICE=''; +_OPT_LANG=''; +_OPT_LOCATION='no'; +_OPT_MODE=''; +_OPT_MANPATH=''; +_OPT_PAGER=''; +_OPT_SECTIONS=''; +_OPT_SYSTEMS=''; +_OPT_TITLE=''; +_MANOPT_WHATIS='no'; +_OPT_XRDB=''; + +# _TMP_* temporary files +export _TMP_DIR; # directory for temporary files +export _TMP_PREFIX; # dir and base name for temporary files +export _TMP_CAT; # stores concatenation of everything +export _TMP_STDIN; # stores stdin, if any +_TMP_DIR=''; +_TMP_PREFIX=''; +_TMP_CAT=''; +_TMP_STDIN=''; ######################################################################## -# Test of function "test". +# Test of rudimentary shell functionality +######################################################################## + +######################################################################## +# Test of `test'. # -[ "a" = "a" ] || exit 1; +test "a" = "a" || exit 1; ######################################################################## -# Test of function "echo". +# Test of `echo' and the `$()' construct. # -if [ "$(echo -n 'te' && echo -n && echo 'st')" != "test" ]; then - echo 'Test of "echo" command failed.' >&2; - exit 1; +echo -n '' >/dev/null || exit -1; +if test "$(echo -n 'te' && echo -n '' && echo -n 'st')" != "test"; then + exit -1; fi; ######################################################################## -# Test of function "true". +# Test of function definitions. # -if ! true >/dev/null 2>&1; then - true () - { - : ; - } +_test_func() +{ + return 0; +} - false () +if ! _test_func; then + echo 'shell does not support function definitions.' >&2; + exit -1; +fi; + + +######################################################################## +# Test of builtin `local' +# +_global='outside'; +test_local() +{ + _global='inside'; + local _local >/dev/null 2>&1 || return 1; +} +if ! test_local; then + local() { - ! : ; + for _i in "$@"; do + unset "${_i}"; + done; } + unset _i; +fi; +if test "${_global}" != 'inside'; then + error "Cannot assign to global variables from within functions."; fi; +unset _global; + +######################################################################## +# Functions for error handling and debugging ######################################################################## -# Test of function "sed". + +############## +# clean_up () # -if [ "$(echo teeest | sed -e '\|^teeest$|s|\(e\)\+|\1|')" != "test" ]; -then - echo 'Test of "sed" command failed.' >&2; +# Clean up at exit. +# +clean_up() +{ + clean_up_secondary; + rm -f "${_TMP_CAT}"; +} + + +############## +# clean_up_secondary () +# +# Clean up temporary files without $_TMP_CAT. +# +clean_up_secondary() +{ + local _i; + for _i in "${_TMP_STDIN}"; do + rm -f "${_i}"; + done; +} + + +############## +# echo2 (<text>*) +# +# Output to stderr. +# +# Arguments : arbitrary text. +# +echo2() +{ + echo "$*" >&2; +} + + +############## +# echo2n (<text>*) +# +# Output to stderr. +# +# Arguments : arbitrary text. +# +echo2n() +{ + echo -n "$*" >&2; +} + + +############# +# error (<text>*) +# +# Print an error message to standard error; exit with an error condition +# +error() +{ + local _code; + _code=-1; + case "$#" in + 0) true; ;; + 1) echo2 'groffer error: '"$1"; ;; + 2) + echo2 'groffer error: '"$1"; + _code="$2"; + ;; + *) echo2 'groffer error: wrong number of arguments in error().'; ;; + esac; + clean_up; + kill "${_PROCESS_ID}" >/dev/null 2>&1; + kill -9 "${_PROCESS_ID}" >/dev/null 2>&1; + exit "${_code}"; +} + + +############# +# diag (text>*) +# +# Output a diagnostic message to stderr +# +function diag () +{ + echo2 '>>>>>'"$*"; +} + + +############# +# abort (<text>*) +# +# Terminate program with error condition +# +function abort () +{ + error "Program aborted."; exit 1; +} + + +######################################################################## +# System Test +######################################################################## + +# Test the availability of the system utilities used in this script. + + +######################################################################## +# Test of function `true'. +# +if ! true >/dev/null 2>&1; then + true () + { + return 0; + } + + false () + { + return 1; + } fi; ######################################################################## -# Test of function "grep". +# Test of function `sed'. # -if [ "$( (echo no; echo test) | grep -e '^.e..$')" != "test" ]; then - echo 'Test of "grep" command failed.' >&2; - exit 1; +if test "$(echo teeest | sed -e '\|^teeest$|s|\(e\)\+|\1|')" != "test"; +then + error 'Test of "sed" command failed.'; fi; ######################################################################## -# Test of function "cat". +# Test of function `cat'. # -if [ "$(echo test | cat)" != "test" ]; then - echo 'Test of "cat" command failed.' >&2; - exit 1; +if test "$(echo test | cat)" != "test"; then + error 'Test of "cat" command failed.'; fi; ######################################################################## # Test for compression. # -if [ "$(echo test | gzip -c -d -f -)" = "test" ]; then - HAS_COMPRESSION="yes"; +if test "$(echo test | gzip -c -d -f -)" = "test"; then + _HAS_COMPRESSION="yes"; else - HAS_COMPRESSION="no"; + _HAS_COMPRESSION="no"; fi; ######################################################################## -# Test for temporary directory and file generating utility +# Test for temporary directory and file generating utility. # -# determine temporary directory into `$TEMP_DIR' -for _i in "$GROFF_TMPDIR $TMPDIR" "$TMP" "$TEMP" "$TEMPDIR" \ - "$HOME"/tmp /tmp "$HOME" .; +# determine temporary directory into `$_TMP_DIR' +for _i in "${GROFF_TMPDIR}" "${TMPDIR}" "${TMP}" "${TEMP}" \ + "${TEMPDIR}" "${HOME}"/tmp /tmp "${HOME}" .; do - if [ "$_i" != "" ]; then - if [ -d "$_i" -a -r "$_i" -a -w "$_i" ]; then - TEMP_DIR="$_i"; + if test "${_i}" != ""; then + if test -d "${_i}" && test -r "${_i}" && test -w "${_i}"; then + _TMP_DIR="${_i}"; break; fi; fi; -done -unset _i -if [ "$TEMP_DIR" = "" ]; then - echo "Couldn't find a directory for storing temorary files." >&2; - exit 1; +done; +unset _i; +if test "${_TMP_DIR}" = ""; then + error "Couldn't find a directory for storing temorary files."; fi; -TEMP_PREFIX="${TEMP_DIR}/${PROGRAM_NAME}"; +_TMP_PREFIX="${_TMP_DIR}/${_PROGRAM_NAME}"; ######################################################################## @@ -265,858 +709,1634 @@ TEMP_PREFIX="${TEMP_DIR}/${PROGRAM_NAME}"; # # GNU getopt -if _res="$(getopt -l GNU: ab: --GNU getopt test 2>/dev/null | - sed -e '\|^ *\(.*\) *$|s||\1|')"; then - if [ "$_res" = "--GNU 'getopt' -- 'test'" ]; then - HAS_OPTS_GNU="yes"; +unset GETOPT_COMPATIBLE; +getopt -T >/dev/null 2>&1; +if test "$?" -eq 4; then # special test for GNU enhanced version + _HAS_OPTS_GNU="yes"; +else + # POSIX getopts + OPTIND=1; + OPTARG=""; + if getopts "t:" _opt -test 2>/dev/null && \ + test "${_opt}" = "t" && \ + test "${OPTARG}" = "est" && \ + test "${OPTIND}" -eq 2; then + _HAS_OPTS_POSIX="yes"; + else + error "No argument parser available (`getopt' or `getopts')."; fi; + unset _opt; fi; -# POSIX getopts -OPTIND=1; -OPTARG=""; -getopts "t:" _opt -test 2>/dev/null; -if [ "$?" -eq 0 -a "$_opt" = "t" -a \ - "$OPTARG" = "est" -a "$OPTIND" -eq 2 ]; then - HAS_OPTS_POSIX="yes"; -fi; - -if [ "$HAS_OPTS_GNU" = "" -a "$HAS_OPTS_POSIX" = "" ]; then - error "No argument parser program available (`getopt' or `getopts')."; -fi; - -unset _opt; -unset _res; OPTIND=1; OPTARG=""; ######################################################################## -# Determine search method for man-pages -# -unset HAS_MANW # `yes' if `man -w' is available -if _files="$(man -w man 2>/dev/null)"; then - if [ "$_files" = "" ]; then - HAS_MANW="no"; - else - for _i in $_files; do - if [ -f "$_i" ]; then - HAS_MANW="yes"; - break; - fi; - done; - fi; - if [ "$HAS_MANW" != "yes" ]; then - HAS_MANW="no"; - fi; -fi; -unset _files; -unset _i; - -######################################################################## -# Shell Funtions +# Definition of Functions ######################################################################## ######################################################################## -# append_args (<arg>*) -# -# Append args to `string' separated by a space, omitting empty args. +# abort (<text>*) # -# Arguments : >=2 -# Output : the generated string +# Unconditionally terminate the program with error code; +# useful for debugging. # -append_args() -{ - local _res; - while [ "$1" = "" ]; do - if [ "$#" -eq 0 ]; then - return; - fi; - shift; - done; - _res="$1"; - shift; - while [ "$#" -ge 1 ]; do - if [ "$1" != "" ]; then - _res="$_res $1"; - fi; - shift; - done; - output "$_res"; -} +# defined above ######################################################################## -# base_name (path) +# base_name (<path>) # -# Delete the directory part of `path', i.e. everything up to last `/' -# at the beginning of `path'. +# Get the file name part of <path>, i.e. delete everything up to last +# `/' from the beginning of <path>. # # Arguments : 1 -# Output : the corrected string +# Output : the file name part (without slashes) # base_name() { - if [ "$#" != 1 ]; then - error 1 "Function base_name needs 1 argument."; - fi - output "$1" | sed -e '\|^\(.*/\)\+|s|||'; + if test "$#" != 1; then + error "base_name() needs 1 argument."; + return "$_ERROR"; + fi; + string_sed_s "$1" '^.*/\([^/]*\)$' '\1'; } ######################################################################## -# catz (<file>*) +# catz (<file>) # # If compression is available decompress standard input and write it to # standard output; otherwise copy standard input to standard output. # -if [ "$HAS_COMPRESSION" = "yes" ]; then +if test "${_HAS_COMPRESSION}" = 'yes'; then catz() { - cat "$@" | gzip -c -d -f; + if test "$#" -ne 1; then + error "catz() needs exactly 1 argument."; + return "$_ERROR"; + fi; + cat "$1" | gzip -c -d -f 2>/dev/null; } else catz() { - cat "$@"; + if test "$#" -ne 1; then + error "catz() needs exactly 1 argument."; + return "$_ERROR"; + fi; + cat "$1"; } fi; ######################################################################## -# check_dpi () +# clean_up () +# +# Do the final cleaning up before exiting; used by the trap calls. +# +# defined above + + +######################################################################## +# clean_up_secondary () +# +# Do the second but final cleaning up. +# +# defined above + + +######################################################################## +# diag (<text>*) +# +# Print marked message to standard error; useful for debugging. +# +# defined above + + +######################################################################## +# dirname_append (<dir> <name>) # -# Sanity check for having default X resolution 100 dpi (very defensive) +# Append `name' to `dir' with clean handling of `/'. # -# Output : generated title +# Arguments : >=1 +# Output : the generated new directory name # -check_dpi() +dirname_append() { - local _res=100; - local _fp; - if _fp="$(xset q | grep '/font' 2>/dev/null)"; then - case "$_fp" in - *100*) : ; ;; - *75*) _res=75; ;; # no 100 found in X font path, but 75 - esac; + local _res; + if test "$#" -ne 2; then + error "dir_append() needs 2 arguments."; + return "$_ERROR"; fi; - output "$_res"; + if is_empty "$1"; then + echo -n "$2"; + return "$_OK"; + fi; + dirname_chop "$1"/"$2"; } ######################################################################## -# clean_up () : +# dirname_chop (<name>) # -# Clean exit without an error. +# Remove unnecessary slashes from directory name. # -clean_up() +# Argument: 1, a directory name. +# Output: path without double, or trailing slashes. +# +dirname_chop() { - local _i; - for _i in "$TMP_CAT" "$TMP_INPUT" "$TMP_TITLE"; do - if [ -f "$_i" ]; then - rm -r "$_i"; - fi; - done; + local _arg; + local _res; + local _sep; + if test "$#" -ne 1; then + error 'dirname_chop() needs 1 argument.'; + return "$_ERROR"; + fi; + _res="$(string_replace_all "$1" '//\+' '/')"; + case "$_res" in + ?*/) string_del_trailing "$_res" '/'; ;; + *) echo -n "$_res"; ;; + esac; } ######################################################################## -# count_next_quoted (<arg>*) -# -# Expects single-quoted arguments, returns the first quoted argument. +# do_filearg (<filearg>) +# +# Append the file, man-page, or standard input corresponding to the +# argument to the temporary file. If this is compressed in the gzip +# or Z format it is decompressed. A title element is generated. +# +# Argument either: +# - name of an existing files. +# - `-' to represent standard input (several times allowed). +# - `man:name.(section)' the man-page for `name' in `section'. +# - `man:name.section' the man-page for `name' in `section'. +# - `man:name' the man-page for `name' in the lowest `section'. +# - `name.section' the man-page for `name' in `section'. +# - `name' the man-page for `name' in the lowest `section'. +# Globals : +# $_TMP_STDIN, $_MAN_ENABLE, $_MAN_IS_SETUP, $_OPT_MAN # -# Arguments : single-quoted, evt. with included spaces. -# Output : number of arguments within the single-quote. -# Return : `1' if arguments are not single-quoted, `0' otherwise. +# Output : none +# Return : $_GOOD if found, $_BAD otherwise. # -count_next_quoted() +do_filearg() { - local _args; - local _number; - local _quoted_block=""; - if [ "$#" -eq 0 ]; then - return 1; + local _filespec; + local _mode; + local _sequence; + if test "$#" -ne 1; then + error "do_filearg() expects 1 argument."; + return "$_ERROR"; fi; - if output $1 | grep -e "^'" >/dev/null 2>&1; then - # starts with a single quote - while ! (output $1 | grep -e \'\$ >/dev/null 2>&1); do - # doesn't end with a single quote - _quoted_block="$(append_args $_quoted_block $1)"; - shift; - if [ "$#" = 0 ]; then - error "count_next_quoted : no closing quote found" - return 1; + _filespec="$1"; + case "$_filespec" in + '') + return "$_GOOD"; + ;; + '-') + to_tmp "${_TMP_STDIN}"; + register_title "-"; + return "$_GOOD"; + ;; + */*) # with directory part; so no man search + sequence="File"; + ;; + *) + if is_yes "${_MAN_ENABLE}"; then + if is_yes "${_OPT_MAN}"; then + _sequence="Manpage File"; + else + _sequence="File Manpage"; + fi; + else + _sequence="File"; fi; - done; - # actual $1 has closing quote - _quoted_block="$(append_args $_quoted_block $1)"; - set -- $_quoted_block; - _number="$#"; - else - _number=1; - fi; - output $_number; + ;; + esac; + for _mode in ${_sequence}; do + case "${_mode}" in + File) + if test -f "${_filespec}"; then + if test ! -r "${_filespec}"; then + echo2 "could not read \`${_filespec}'"; + return "$_BAD"; + fi; + to_tmp "${_filespec}"; + register_title "$(base_name "${_filespec}")"; + return "$_GOOD"; + else + continue; + fi; + ;; + Manpage) # parse filespec as man page + if is_not_yes "$_MAN_IS_SETUP"; then + man_setup; + fi; + if man_do_filespec "${_filespec}"; then + return "$_GOOD"; + else + continue; + fi; + ;; + esac; + done; + return "$_BAD"; } ######################################################################## -# del_all_leading_from (<regexp> <string>) -# -# Delete every occurence of `regexp' at the beginning of `string'. +# do_nothing () # -# Arguments : 2 -# Output : the corrected string +# Dummy function. # -del_all_leading_from() +do_nothing() { - if [ "$#" != 2 ]; then - error 1 'Function "del_all_leading_from" needs 2 args.'; - fi - output "$2" | sed -e '\|^\('"$1"'\)\+|s|||'; + return "$_OK"; } ######################################################################## -# del_ext_from (<extension> <filename>) +# echo2 (<text>*) # -# Delete `extension' from the end of `filename'. +# Print to standard error with final line break. # -# Arguments : 2 -# Output : the corrected string +# defined above + + +######################################################################## +# echo2n (<text>*) +# +# Print to standard error without final line break. +# +# defined above + + +######################################################################## +# error (<text>*) # -del_ext_from() +# Print error message and exit with error code. +# +# defined above + + +######################################################################## +# get_first_essential (<arg>*) +# +# Retrieve first non-empty argument. +# +# Return : `1' if all arguments are empty, `0' if found. +# Output : the retrieved non-empty argument. +# +get_first_essential() { - if [ "$#" != 2 ]; then - error 1 'Function "del_ext_from" needs 2 args.'; - fi - output "$2" | sed -e "\|^ *\('\?.*\)$1\('\?\) *"'$|s||\1\2|'; + local _i; + if test "$#" -eq 0; then + return "$_OK"; + fi; + for _i in "$@"; do + if is_not_empty "${_i}"; then + echo -n "${_i}"; + return "$_OK"; + fi; + done; } ######################################################################## -# echo2 (<text>*) +# is_dir (<name>) # -# Output to stderr. +# Test whether `name' is a directory. # -# Arguments : arbitrary text. +# Arguments : 1 +# Return : `0' if arg1 is a directory, `1' otherwise. # -echo2() +is_dir() { - echo "$*" >&2; + if is_not_empty "$1" && test -d "$1" && test -r "$1"; then + return "$_GOOD"; + else + return "$_BAD"; + fi; } ######################################################################## -# error (<err_no> <text>*) +# is_empty (<string>) # -# Output argurments to stderr and abort. +# Test whether `string' is empty. # -# Arguments : arbitrary text +# Arguments : <=1 +# Return : `0' if arg1 is empty or does not exist, `1' otherwise. # -error() +is_empty() { - local _errno; - case "$#" in - 0) set -- 'unknown error'; ;; - esac; - echo2 "groffer error : $*"; - clean_up; - kill "$PROCESS_ID" >/dev/null 2>&1; - kill -9 "$PROCESS_ID" >/dev/null 2>&1; - exit 1; + if test "$#" -ne 1; then + error "is_empty() needs 1 argument."; + return "$_ERROR"; + fi; + if test -z "$1"; then + return "$_YES"; + else + return "$_NO"; + fi; } ######################################################################## -# get_manpath () +# is_equal (<string1> <string2>) # -# Determine search path for man-pages (only needed when no `man -w'). +# Test whether `string1' is equal to <string2>. # -# Return : `0' if a valid path was retrieved. -# Output : path as space-separated list (intended for $MAN_PATH). -# Globals : system : $MANPATH $LC_ALL $LANG -# file : $OPT_MANPATH $MAN_PATH +# Arguments : 2 +# Return : `0' both arguments are equal strings, `1' otherwise. # -get_manpath() +is_equal() { - local _files; - local _dirs; - local _i; - local _d; - local _p; - local _all; - local _manpath; - if [ "$OPT_MANPATH" != "" ]; then # --manpath was set - MANPATH="$OPT_MANPATH"; - fi; - if [ "$MANPATH" = "" ]; then # try `manpath' program - _dirs="$(manpath 2>/dev/null)"; - if [ "$?" = 0 -a "$_dirs" != "" ]; then - MANPATH="$_dirs"; - fi - fi - if [ "$MANPATH" = "" ]; then # set some default path - _manpath="/usr/local/share/man /usr/local/man \ - /usr/share/man /usr/man \ - /usr/X11R6/man /usr/openwin/man \ - /opt/man /opt/gnome/man /opt/kde/man"; - else - _manpath="$(echo -n $MANPATH | tr : ' ')"; + if test "$#" -ne 2; then + error "is_equal() needs 2 arguments."; + return "$_ERROR"; fi; - if [ "$_manpath" = "" ]; then - return 1; + if test "$1" = "$2"; then + return "$_YES"; + else + return "$_NO"; fi; - _dirs=""; - for _p in $_manpath; do # remove non-existing directories - if [ -d "$_p" -a -r "$_p" -a -x "$_p" ]; then - _dirs="$(append_args $_dirs $_p)"; - fi; - done; - _manpath="$_dirs"; - if [ "$_manpath" = "" ]; then - return 1; +} + + +######################################################################## +# is_file (<name>) +# +# Test whether `name' is a readable file. +# +# Arguments : 1 +# Return : `0' if arg1 is a readable file, `1' otherwise. +# +is_file() +{ + if is_not_empty "$1" && test -f "$1" && test -r "$1"; then + return "$_GOOD"; + else + return "$_BAD"; fi; - if [ "$LC_ALL" = "" -a "$LANG" = "" ]; then - MAN_PATH="$_manpath"; - else # language-specific directories - MAN_PATH=""; - if [ "$LC_ALL" != "" ]; then - _lang_var="$LC_ALL"; - else - _lang_var="$LANG"; - fi - # two-letter version of $LANG - _short_code="$(echo $_lang_var | sed -e '\|^\(..\).*$|s||\1|')"; - for _p in $_manpath; do - _langdir="${_p}/${_lang_var}"; - _all="$(ls -d "${_p}/${_short_code}"* 2>/dev/null)"; - # all dirs with this 2-letter lang code - _langs=""; - if [ "$_all" != "" ]; then - for _d in $_all; do - if [ "$_d" != "$_langdir" -a -d "$_d" ]; then - _langs="$(append_args "$_langs" "$_d")"; - fi; - done; - if [ -d "$_langdir" ]; then - _langs="$(append_args "$_langdir" $_langs)"; - fi; - fi; - MAN_PATH="$(append_args "$_langs" $_p)"; - done; +} + + +######################################################################## +# is_not_empty (<string>) +# +# Test whether `string' is not empty. +# +# Arguments : <=1 +# Return : `0' if arg1 exists and is not empty, `1' otherwise. +# +is_not_empty() +{ + if test "$#" -ne 1; then + error "is_not_empty() needs 1 argument."; + return "$_ERROR"; fi; - if [ "$MAN_PATH" = "" ]; then - return 1; + if test -n "$1"; then + return "$_YES"; + else + return "$_NO"; fi; - output "$MAN_PATH"; } ######################################################################## -# get_next_quoted (<arg>*) +# is_not_equal (<string1> <string2>) # -# Expects single-quoted arguments, returns the first quoted argument. +# Test whether `string1' is not equal to <string2>. # -# Arguments : single-quoted, evt. with included spaces. -# Output : everything up to the next arg terminated by a quote; -# the enclosing quotes are removed. -# Return : `1' if arguments are not single-quoted, `0' otherwise. +# Arguments : 2 +# Return : `0' the arguments are different strings, `1' otherwise. # -get_next_quoted() +is_not_equal() { - local _args="$*"; - local _number="$(count_next_quoted $_args)"; - shift $_number; - output $_args | sed -e '\|^\(.*\)'"$*"'$|s||\1|' | - sed -e "\|^ *'\(.*\)' *"'$|s||\1|'; + if test "$#" -ne 2; then + error "is_not_equal() needs 2 arguments."; + return "$_ERROR"; + fi; + if test "$1" != "$2"; then + return "$_YES"; + else + return "$_NO"; + fi; } ######################################################################## -# is_substring_of (<part> <string>) +# is_not_yes (<string>) # -# Test whether `part' is contained in `string'. +# Test whether `string' is not "yes". # -# Arguments : 2 text arguments. -# Return : `0' if arg1 is substring of arg2, `1' otherwise. +# Arguments : <=1 +# Return : `0' if arg1 is `yes', `1' otherwise. # -is_substring_of() +is_not_yes() { - if [ "$#" != 2 ]; then - false; - error "is_substring_of needs 2 arguments."; + if test "$#" -ne 1; then + error "is_not_yes() needs 1 argument."; + return "$_ERROR"; fi; - if output "$2" | grep -e "$1" >/dev/null 2>&1; then - return 0; + if test "$1" != "yes"; then + return "$_GOOD"; else - return 1; + return "$_BAD"; fi; } ######################################################################## -# leave () +# is_prog (<name>) # -# Clean exit without an error. +# Determine whether arg is a program in $PATH # -leave() +# Arguments : 1 (empty allowed) +# Return : `0' if arg is a program in $PATH, `1' otherwise. +# +is_prog() { - clean_up; - exit 0; + where "$@" >/dev/null; } ######################################################################## -# make_title () +# is_yes (<string>) # -# Create title for X from the different possibilities. -# Delete $TMP_TITLE file. +# Test whether `string' has value "yes". # -# Globals : $TMP_TITLE $OPT_XRDB $OPT_TITLE -# Output : retrieved title +# Arguments : <=1 +# Return : `0' if arg1 is `yes', `1' otherwise. # -make_title() +is_yes() { - if [ "$OPT_TITLE" != "" ]; then - # title was set by option --title - output "$OPT_TITLE"; - elif is_substring_of "-title" "$OPT_XRDB"; then - # $OPT_XRDB is handled anyway, so no extra output from here - true; - else - # no title was supplied on the command line, take the default title - # constisting of the processed filespecs, stored in file $TMP_TITLE. - if [ "$TMP_TITLE" != "" ]; then - cat "$TMP_TITLE"; - fi; + if test "$#" -ne 1; then + error "is_yes() needs 1 argument."; + return "$_ERROR"; fi; - if [ "$TMP_TITLE" != "" ]; then - rm -f "$TMP_TITLE"; + if is_equal "$1" 'yes'; then + return "$_GOOD"; + else + return "$_BAD"; fi; } ######################################################################## -# manpage_search_filespec (<filespec>) +# leave () # -# check argument with `man -w' +# Clean exit without an error. # -# Arguments : exactly 1 argument of the form `name.section', -# `man:name', or `man:name(section)'. -# Several args indicate an embedded space character. +leave() +{ + clean_up; + exit "$_OK"; +} + + +######################################################################## +# man_do_filespec (<filespec>) +# +# Print suitable man page(s) for filespec to $_TMP_CAT. +# +# Arguments : 2 +# <filespec>: argument of the form `man:name.section', `man:name', +# `man:name(section)', `name.section', `name'. +# +# Globals : $_OPT_ALL # -# Output : filename of man page, if any. +# Output : none. # Return : `0' if man page was found, `1' else. # -# Only called from supercat(). +# Only called from do_fileargs(), checks on $MANPATH and +# $_MAN_ENABLE are assumed. # -manpage_search_filespec() +man_do_filespec() { - local _file=""; - local _arg; + local _got_one; local _name; + local _prevsec; + local _res; + local _s; local _section; - if [ "$#" -ne 1 ]; then - return 1; + local _spec; + local _string; + if is_empty "${MANPATH}"; then + return "$_BAD"; fi; - _arg="$1"; - case "$_arg" in - */*) # contains directory part, not handled - return 1; + case "$#" in + 1) true; ;; + *) + error "man_do_filespec() needs exactly 1 argument."; + return "$_ERROR"; ;; + esac; + if is_empty "$1"; then + return "$_BAD"; + fi; + _spec="$1"; + _name=''; + _section=''; + case "${_spec}" in man:?*\(?*\)) # man:name(section) - _name="$(output "$_arg" | - sed -e '\|^man:\([^(]\+\)(\(.*\))$|s||\1|')"; - _section="$(output $_arg | - sed -e '\|^man:\([^(]\+\)(\(.*\))$|s||\2|')"; - if _file="$(manpage_search_name "$_name" "$_section")" && - [ "$_file" != "" ]; then - output "$_file"; - return 0; - fi; - return 1; + _string="$(string_del_leading "${_spec}" 'man:')"; + _string="$(string_del_trailing "${_string}" ')')"; + _name="$(string_del_trailing "${_string}" '(.\+')"; + _section="$(string_del_leading "${_string}" "${_name}"'(')"; ;; - man:?*.?*) # man:name.section - _name="$(output "$_arg" | - sed -e '\|^man:\([^.]\+\)\.\(.*\)$|s||\1|')"; - _section="$(output $_arg | - sed -e '\|^man:\([^.]\+\)\.\(.*\)$|s||\2|')"; - if _file="$(manpage_search_name "$_name" "$_section")" && - [ "$_file" != "" ]; then - output "$_file"; - return 0; - fi; - return 1; + man:?*.[^.]*) # man:name.section + _string="$(string_del_leading "${_spec}" 'man:')"; + _name="$(string_del_trailing "${_string}" '\.[^.]*')"; + _section="$(string_del_leading "${_string}" "${_name}"'\.')"; ;; man:?*) # man:name - _name="$(output "$_arg" | sed -e '\|^man:|s|||')"; - if _file="$(manpage_search_name "$_name")"; then - output "$_file"; - return 0; - else - return 1; - fi; + _name="$(string_del_leading "${_spec}" 'man:')"; ;; - ?*.?*) # name.section - _name="$(output "$_arg" | - sed -e '\|^\([^.]\+\)\.\([^.]\+\)$|s||\1|')"; - _section="$(output "$_arg" | - sed -e '\|^\([^.]\+\)\.\([^.]\+\)$|s||\2|')"; - _file="$(manpage_search_name "$_name" "$_section")"; - if [ "$?" -eq 0 -a "$_file" != "" ]; then - output "$_file"; - return 0; - fi; - _file="$(manpage_search_name "$_arg")" - if [ "$?" -eq 0 -a "$_file" != "" ]; then - output "$_file"; - return 0; - fi; - return 1; + ?*\(?*\)) # name(section) + _string="$(string_del_trailing "${_spec}" ')')"; + _name="$(string_del_trailing "${_string}" '(.\+')"; + _section="$(string_del_leading "${_string}" "${_name}"'(')"; + ;; + *.[^.]*) # name.section + _name="$(string_del_trailing "${_spec}" '\.[^.]\+')"; + _section="$(string_del_leading "${_spec}" "${_name}"'\.')"; ;; ?*) - _file="$(manpage_search_name "$_arg")"; - if [ "$?" -eq 0 -a "$_file" != "" ]; then - output "$_file"; - return 0; - fi; - return 1; + _name="${_filespec}"; ;; esac; - return 1; + if is_empty "${_name}"; then + return "$_BAD"; + fi; + _got_one='no'; + if is_empty "${_section}"; then + for _s in $_MAN_AUTO_SEC; do + if man_search_section "$_name" "$_s"; then # found + if is_yes "$_MAN_ALL"; then + _got_one='yes'; + else + return "$_GOOD"; + fi; + fi; + done; + else + man_search_section "$_name" "$_section"; + return "$?"; + fi; + if is_yes "$_MAN_ALL" && is_yes "$_got_one"; then + return "$_GOOD"; + fi; + return "$_BAD"; } ######################################################################## -# manpage_search_name (<name> <section>?) +# man_is_setup () +# +# Setup the variables $_MAN_* needed for man page searching. # -# Get position of man-page `name(section)', or just `name' in the -# lowest section using `man -w'. +# Globals: +# in: $_OPT_*, $_MANOPT_*, $LANG, $LC_MESSAGES, $LC_ALL, +# $MANPATH, $MANROFFSEQ, $MANSEC, $PAGER, $SYSTEM, $MANOPT. +# out: $_MAN_PATH, $_MAN_LANG, $_MAN_SYS, $_MAN_LANG, $_MAN_LANG2, +# $_MAN_SEC, $_MAN_ALL +# in/out: $_MAN_ENABLE # -# Arguments : either 2 (`name' `section') or 1 (`name'). -# Globals : $MAN_PATH must be preset as space-separated list of dirs. -# Output : the file position for the man-page +# The precedence for the variables related to `man' is that of GNU +# `man', i.e. # -# Only called from man_page_filespec(). +# $LANG; overridden by +# $LC_MESSAGES; overridden by +# $LC_ALL; this has the same precedence as +# $MANPATH, $MANROFFSEQ, $MANSEC, $PAGER, $SYSTEM; overridden by +# $MANOPT; overridden by +# the groffer command line options. # -if [ "$HAS_MANW" = "yes" ]; then # use man -w +man_setup() +{ + local _lang; - manpage_search_name() - { - local _i; - local _name; - local _section; - if [ "$MAN_PATH" = "" ]; then - return 0; - fi; - case "$#" in - 1) - _name="$1"; - _section=""; + if is_yes "$_MAN_IS_SETUP"; then + return "$_GOOD"; + fi; + _MAN_IS_SETUP='yes'; + + if is_not_yes "${_MAN_ENABLE}"; then + return "$_GOOD"; + fi; + + # determine basic path for man pages + _MAN_PATH="$(get_first_essential \ + "${_OPT_MANPATH}" "${_MANOPT_PATH}" "${MANPATH}")"; + if is_empty "${_MAN_PATH}"; then + _MAN_PATH="$(manpath 2>/dev/null)"; # not on all systems available + fi; + if is_empty "${_MAN_PATH}"; then + manpath_set_from_path; + else + _MAN_PATH="$(path_clean "${_MAN_PATH}")"; + fi; + if is_empty "${_MAN_PATH}"; then + _MAN_ENABLE="no"; + return; + fi; + + _MAN_ALL="$(get_first_essential "${_OPT_ALL}" "${_MANOPT_ALL}")"; + if is_empty "$_MAN_ALL"; then + _MAN_ALL='no'; + fi; + + _MAN_SYS="$(get_first_essential \ + "${_OPT_SYSTEMS}" "${_MANOPT_SYS}" "${SYSTEM}")"; + _lang="$(get_first_essential \ + "${_OPT_LANG}" "${LC_ALL}" "${LC_MESSAGES}" "${LANG}")"; + case "${_lang}" in + C|POSIX) + _MAN_LANG=""; + _MAN_LANG2=""; ;; - 2) - _name="$1"; - _section="$2"; + ?) + _MAN_LANG="${_lang}"; + _MAN_LANG2=""; ;; *) - error "manpage_search_name : needs 1 or 2 arguments."; + _MAN_LANG="${_lang}"; + _MAN_LANG2="$(string_get_leading "${_lang}" '..')"; ;; - esac; - for _i in $(man -w $_section "$_name" 2>/dev/null); do - if [ -f "$_i" -a -r "$_i" ] && - (catz "$_i" | grog | grep -e '-man') >/dev/null 2>&1; - then - output "$_i"; - return 0; - fi - done; - return 1; - } + esac; + # from now on, use only $_LANG, forget about $_OPT_LANG, $LC_*. -else # manually search man-page + manpath_add_lang_sys; - manpage_search_name() - { - local _name; - local _section; - local _p; - if [ "$MAN_PATH" = "" ]; then - return 0; - fi; - case "$#" in + _MAN_SEC="$(get_first_essential \ + "${_OPT_SECT}" "${_MANOPT_SEC}" "${MANSEC}")"; + if is_empty "${_MAN_PATH}"; then + _MAN_ENABLE="no"; + return; + fi; + + _MAN_EXT="$(get_first_essential \ + "${_OPT_EXTENSION}" "${_MANOPT_EXTENSION}")"; +} + + +######################################################################## +# man_parse_name (<filespec>) +# +# Parse the man page name part off from a filespec +# +# Arguments: 1, 2, or 3; maybe empty +# Output: none +# +man_parse_name() +{ + return; +} + + +######################################################################## +# man_register_file (<file> [<name> [<section>]]) +# +# Write a found man page file and register the title element. +# +# Arguments: 1, 2, or 3; maybe empty +# Output: none +# +man_register_file() +{ + case "$#" in 1) - _name="$1"; - _section=""; - ;; + if is_empty "$1"; then + error 'man_register_file(): file name is empty'; + else + to_tmp "$1"; + return "${_OK}"; + fi; + ;; 2) - _name="$1"; - _section="$2"; - ;; - *) - error "man_search_name : needs 1 or 2 arguments."; - ;; - esac; - for _p in $MAN_PATH; do - set -- "$(ls -d "${_p}/man${_section}"*"/${_name}.${_section}"* \ - 2>/dev/null)"; - while [ "$#" -gt 0 ]; do - if [ -f "$1" -a -r "$1" ]; then - output "$1"; - return 0; + if is_not_empty "$1"; then + to_tmp "$1"; + fi; + register_title "man:$2"; + return "${_OK}"; + ;; + 3) + if is_not_empty "$1"; then + to_tmp "$1"; + fi; + register_title "$2($3)"; + return "${_OK}"; + ;; + *) error 'man_register_file(): wrong number of arguments'; ;; + esac; +} + + +######################################################################## +# man_search_section (<name> <section>) +# +# Retrieve man pages. +# +# Arguments : 2 +# Globals : $_MAN_PATH, $_MAN_EXT +# Return : 0 if found, 1 otherwise +# +man_search_section() +{ + local _d; + local _dir; + local _ext; + local _got_one; + local _f; + local _name; + local _prefix + local _section; + if is_empty "${_MAN_PATH}"; then + return "${_BAD}"; + fi; + if test "$#" -ne 2; then + error "man_sec_first() needs 2 arguments."; + return "${_ERROR}"; + fi; + if is_empty "$1"; then + return "${_BAD}"; + fi; + if is_empty "$2"; then + return "${_BAD}"; + fi; + _name="$1"; + _section="$2"; + IFS=: + set -- ${_MAN_PATH} + unset IFS; + _got_one='no'; + if is_empty "$_MAN_EXT"; then + for _d in "$@"; do + _dir="$(dirname_append "${_d}" "man${_section}")"; + if is_dir "$_dir"; then + _prefix="$(dirname_append "$_dir" "${_name}.${_section}")"; + for _f in $(echo -n ${_prefix}*); do + if is_file "$_f"; then + man_register_file "$_f" "$_name" "$_section"; + if is_not_empty "$_MAN_ALL"; then + _got_one='yes'; + return "$_GOOD"; + fi; + fi; + done; + fi; + done; + else + _ext="${_MAN_EXT}"; + # check for directory name having trailing extension + for _d in "$@"; do + _dir="$(dirname_append ${_d} man${_section}${_ext})"; + if is_dir "$_dir"; then + _prefix="$(dirname_append "$_dir" "${_name}.${_section}")"; + for _f in ${_prefix}*; do + if is_file "$_f"; then + man_register_file "$_f" "$_name" "$_section"; + if is_not_empty "$_MAN_ALL"; then + _got_one='yes'; + return "$_GOOD"; + fi; + fi; + done; + fi; + done; + # check for files with extension in directories without extension + for _d in "$@"; do + _dir="$(dirname_append "${_d}" "man${_section}")"; + if is_dir "$_dir"; then + _prefix="$(dirname_append "$_dir" \ + "${_name}.${_section}${_ext}")"; + for _f in ${_prefix}*; do + if is_file "$_f"; then + man_register_file "$_f" "$_name" "$_section"; + if is_not_empty "$_MAN_ALL"; then + _got_one='yes'; + return "$_GOOD"; + fi; + fi; + done; + fi; + done; + fi; + if is_yes "$_MAN_ALL" && is_yes "$_got_one"; then + return "$_GOOD"; + fi; + return "$_BAD"; +} + + +######################################################################## +# manpath_add_lang_sys () +# +# Add language and operating system specific directories to man path. +# +# Arguments : 0 +# Output : none +# Globals: +# in: $_MAN_SYS: has the form `os1,os2,...', a comma separated +# list of names of operating systems. +# $_MAN_LANG and $_MAN_LANG2: each a single name +# in/out: $_MAN_PATH: has the form `dir1:dir2:...', a colon +# separated list of directories. +# +manpath_add_lang_sys() +{ + local _p; + local _mp; + if test "$#" -ne 0; then + error "manpath_add_system() does not have arguments."; + return "$_ERROR"; + fi; + if is_empty "${_MAN_PATH}"; then + return "${_GOOD}"; + fi; + # twice test both sys and lang + IFS=: + set -- ${_MAN_PATH}; + unset IFS; + _mp=''; + for _p in "$@"; do # loop on man path directories + _mp="$(_manpath_add_lang_sys_single "$_mp" "$_p")"; + done; + IFS=: + set -- ${_mp}; + unset IFS; + for _p in "$@"; do # loop on man path directories + _mp="$(_manpath_add_lang_sys_single "$_mp" "$_p")"; + done; + _MAN_PATH="$(path_chop "${_mp}")"; +} + + +_manpath_add_lang_sys_single() +{ + # To the directory in $1 append existing sys/lang subdirectories + # Function is necessary to split the OS list. + # + # globals: in: $_MAN_SYS, $_MAN_LANG, $_MAN_LANG2 + # argument: 2: `man_path' and `dir' + # output: colon-separated path of the retrieved subdirectories + # + local _d; + _res="$1"; + _parent="$2"; + IFS=, + set -- ${_MAN_SYS} ; + unset IFS; + for _d in "$@" "$_MAN_LANG" "$_MAN_LANG2"; do + _dir="$(dirname_append "$_parent" "$_d")"; + if path_not_contains "$_res" "$_dir" && is_dir "$_dir"; then + _res="${_res}:${_dir}"; + fi; + done; + if path_not_contains "$_res" "$_parent"; then + _res="${_res}:${_parent}"; + fi; + path_chop "$_res"; +} + + +######################################################################## +# manpath_set_from_path () +# +# Determine basic search path for man pages from $PATH. +# +# Return: `0' if a valid man path was retrieved. +# Output: none +# Globals: +# in: $PATH +# out: $_MAN_PATH +# +manpath_set_from_path() +{ + local _base; + local _d; + local _mandir; + local _manpath; + _manpath=''; + + # get a basic man path from $PATH + if is_not_empty "${PATH}"; then + IFS=: + set -- ${PATH} + unset IFS; + for _d in "$@"; do + _base="$(string_del_trailing "${_d}" '/\+bin/*')"; + for _e in /share/man /man; do + _mandir="${_base}${_e}"; + if test -d "${_mandir}" && test -r "${_mandir}"; then + _manpath="${_manpath}:${_mandir}"; fi; - shift; done; done; - return 1; - } + fi; -fi; + # append some default directories + for _d in /usr/local/share/man /usr/local/man \ + /usr/share/man /usr/man \ + /usr/X11R6/man /usr/openwin/man \ + /opt/share/man /opt/man \ + /opt/gnome/man /opt/kde/man; do + if path_not_contains "${_manpath}" "${_d}" && is_dir "${_d}"; then + _manpath="${_manpath}:${_d}"; + fi; + done; + + _MAN_PATH="${_manpath}"; +} ######################################################################## -# normalize_args (<arg>+) +# normalize_args (<shortopts> <longopts> <arg>+) # # Display arguments in the normalized form of GNU `getopt'. # -# Arguments : if no arguments are given, $* is parsed instead -# Globals : $ALL_LONGOPTS $ALL_SHORTOPTS -# Output : arguments in normalized form +# Arguments : if no arguments are given, `-' is assumed +# Globals : in: $_OPTS_LONG, $_OPTS_SHORT +# Output : arguments in normalized form; these must be processed by +# eval set -- "$(normalize_args ...)" # -if [ "$HAS_OPTS_GNU" = "yes" ]; then +if is_yes "${_HAS_OPTS_GNU}"; then normalize_args() { - local _args; - local _long_opts=""; + local _long_opts; + local _short_opts; local _i; local _res; - if [ "$#" -eq 0 ]; then - set -- -; + if test "$#" -lt 2; then + error "normalize_args() needs at least 2 arguments"; + return "$_ERROR"; fi; + _short_opts="$1"; _long_opts=""; - for _i in ${ALL_LONGOPTS}; do - _long_opts="$(append_args $_long_opts -l "$_i")"; - done; - if _res="$(getopt -l "$_long_opts" "$ALL_SHORTOPTS" "$@")"; then - output "$_res"; - return 0; + if is_not_empty "$2"; then + for _i in $2; do + _long_opts="${_long_opts} -l '${_i}'"; + done; + fi; + shift 2; + if test "$#" -eq 0; then + set -- -; + fi; + if _res="$(eval getopt "${_long_opts}" -o \"${_short_opts}\" \ + -- '"$@"')"; then + echo -n "${_res}"; + return "$_GOOD"; else - error 'wrong option'; + error 'normalize_args(): wrong option'; + return "$_ERROR"; fi; } -elif [ "$HAS_OPTS_POSIX" = "yes" ]; then # POSIX getopts +elif is_yes "${_HAS_OPTS_POSIX}"; then # POSIX getopts normalize_args() { - local _args; - local _long_opts=""; - local _i; - local _res; local _opt; local _param; - if [ "$#" -eq 0 ]; then + local _res; + local _short_opts; + if test "$#" -lt 2; then + error "normalize_args() needs at least 2 arguments"; + return "$_ERROR"; + fi; + _short_opts="$1"; + # ignore long options in $2 + shift 2; + if test "$#" -eq 0; then set -- -; fi; - case "--[^ ]" in - "$_args") error "long options are only available in GNU."; ;; - esac; OPTIND=1; OPTARG=""; + OPTERR=0; # set silent mode for getopts _res=""; - while getopts ":$ALL_SHORTOPTS" _opt $_args; do - if [ "$_opt" = ":" ]; then - if [ "$OPTARG" = "-" ]; then - error "your system does not allow GNU long options."; - else - error "unknown option."; - fi; - fi; - _res="$(append_args $_res -"$_opt")"; - if [ "$OPTARG" != "" ]; then - _res="$(append_args $_res "'$OPTARG'")"; - # option args are quoted; + # synopsis: getopts <optstring> <variable_for_optchar> <arg>* + while getopts "${_short_opts}" _opt "$@"; do + # getopts() does not fail when a wrong option is encountered. + case "${_opt}" in + \?) # wrong option found + if is_equal "${OPTARG}" '-'; then + error \ + "your system does not support long options; use \`-W'."; + else + error "unknown option \`-${OPTARG}'."; + fi; + return "$_ERROR"; + ;; + :) # argument not found (in silent mode) + error "no argument found for option \`-${OPTARG}'."; + return "$_ERROR"; + ;; + esac; + _res="${_res} -${_opt}"; + if is_not_empty "${OPTARG}"; then + _res="${_res} '${OPTARG}'"; OPTARG=""; fi; done; - if [ "$_opt" == '?' ]; then # end of options + if is_equal "${_opt}" '?'; then # end of options # non-option parameters are quoted in the output - _param=""; - set -- $_args; - if [ "$OPTIND" -le "$#" ]; then - _res="$(append_args $_res "--")"; - eval _param=${"$OPTIND"}; - if [ "$_param" != "--" ]; then - _res="$(append_args $_res "'$_param'")"; + _res="${_res} --"; + if test "${OPTIND}" -le "$#"; then + # first non-option parameter + eval _param='"$'${OPTIND}'"'; + if test "${_param}" != "--"; then + # save before shifting + _res="${_res} '${_param}'"; fi; - shift "$OPTIND"; - while [ "$#" -gt 0 ]; do - _res="$(append_args $_res "'$1'")"; + shift "${OPTIND}"; + while test "$#" -gt 0; do + _res="${_res} '$1'"; + shift; done; fi; - output $_res; - return 0; + echo -n "${_res}" + return "$_OK"; else error 'error in option parsing'; + return "$_ERROR"; fi; } else - error 'no option processor abvailable.'; + error 'no option processor available.'; + return "$_ERROR"; fi; ######################################################################## -# output (<text>*) +# path_chop (<path>) # -# Print arguments to standard output, if there are any. -# Handle `echo' programs that can have only 1 arg. +# Remove unnecessary colons from path. # -# Arguments : any. -# Output : the list of the arguments without a line break. +# Argument: 1, a colon separated path. +# Output: path without leading, double, or trailing colons. # -output() +path_chop() { - if [ "$#" -ge 1 ]; then - echo -n "$*"; + local _res; + if test "$#" -ne 1; then + error 'path_chop() needs 1 argument.'; + return "$_ERROR"; fi; + +# _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' | + sed -e '\|^:*|s|||' | + sed -e '\|:*$|s|||'; +} + + +######################################################################## +# path_clean (<path>) +# +# Remove non-existing directories from a colon-separated list. +# +# Argument: 1, a colon separated path. +# Output: colon-separated list of existing directories. +# +path_clean() +{ + local _arg; + local _dir; + local _res; + if test "$#" -ne 1; then + error 'path_clean() needs 1 argument.'; + return "$_ERROR"; + fi; + _arg="$1"; + IFS=: + set -- ${_arg}; + unset IFS; + _res=""; + for _i in "$@"; do + if is_not_empty "$_i" \ + && path_not_contains "${_res}" "$_i" \ + && is_dir "$_i"; + then + _dir="$(dirname_chop "$_i")"; + _res="${_res}:${_dir}"; + fi; + done; + path_chop "${_res}"; +} + + +######################################################################## +# path_contains (<path> <dir>) +#- +# Test whether `dir' is contained in `path', a list separated by `:'. +# +# Arguments : 2 arguments. +# Return : `0' if arg2 is substring of arg1, `1' otherwise. +# +path_contains() +{ + if test "$#" -ne 2; then + error "path_contains() needs 2 arguments."; + return "$_ERROR"; + fi; + case ":$1:" in + *":$2:"*) return "$_YES"; ;; + *) return "$_NO"; ;; + esac; +} + + +######################################################################## +# path_not_contains (<path> <dir>) +#- +# Test whether `dir' is not contained in colon separated `path'. +# +# Arguments : 2 arguments. +# Return : `1' if arg2 is substring of arg1, `0' otherwise. +# +path_not_contains() +{ + if test "$#" -ne 2; then + error "path_not_contains() needs 2 arguments."; + return "$_ERROR"; + fi; + case ":$1:" in + *":$2:"*) return "$_NO"; ;; + *) return "$_YES"; ;; + esac; } ######################################################################## # register_title (<filespec>) # -# Transform argument into a title element and append to $TMP_TITLE file. +# Create title element from <filespec> and append to $_REGISTERED_TITLE +# +# Globals: $_REGISTERED_TITLE (rw) +# +register_title() +{ + local _t; + if test "$#" -ne 1; then + error "register_title() needs exactly 1 argument."; + return "$_ERROR"; + fi; + if is_empty "$1"; then + return "$_OK"; + fi; + _t="$(base_name "$1")"; # remove directory part + _t="$(string_del_trailing "${_t}" '\.gz')"; # remove .gz + _t="$(string_del_trailing "${_t}" '\.Z')"; # remove .Z + if is_empty "${_t}"; then + return "$_OK"; + fi; + _REGISTERED_TITLE="${_REGISTERED_TITLE} ${_t}"; +} + + +######################################################################## +# save_stdin () +# +# Store standard input to temporary file. # -# This is defined in the main:temporary section +save_stdin() +{ + cat | catz - >"${_TMP_STDIN}"; # using `cat' first is safer +} +######################################################################## +# string_contains (<string> <part>) +# +# Test whether `part' is contained in `string'. +# +# Arguments : 2 text arguments. +# Return : `0' if arg2 is substring of arg1, `1' otherwise. +# +string_contains() +{ + if test "$#" != 2; then + error 'string_contains() needs 2 arguments.'; + return "$_ERROR"; + fi; + case "$1" in + *"$2"*) return "$_YES"; ;; + *) return "$_NO"; ;; + esac; +} + ######################################################################## -# save_stdin_if_any () +# string_del_leading (<string> <regex>) +# +# Delete the beginning <regex> of <string>, if any. # -# Check if stdin is needed; if so, store to temporary file. -# Globals : $FILE_ARGS +# 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. # -save_stdin_if_any() +string_del_leading() { - local _a - set -- $FILE_ARGS - for _a in "$@"; do - if [ "$_a" = "'-'" ]; then - cat | catz - >"$TMP_INPUT"; # using `cat' first is safer - break; + local _del; + local _result; + local _string; + if test "$#" -ne 2; then + error "string_del_leading() needs 2 arguments."; + return "$_ERROR"; + fi; + _string="$1"; + _del="$2"; + if is_empty "${_string}"; then + if is_empty "${_del}"; then + return "${_GOOD}"; + else + return "${_BAD}"; fi; - done; + fi; + if is_empty "${_del}"; then + echo -n "${_string}"; + return "${_GOOD}"; + fi; + _result="$(string_sed_s "${_string}" '^'"${_del}" '')"; + echo -n "${_result}"; + if is_equal "${_result}" "${_string}"; then + return "${_BAD}"; + else + return "${_GOOD}"; + fi; +} + + +######################################################################## +# 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() +{ + local _del; + local _result; + local _string; + if test "$#" -ne 2; then + error "string_del_trailing() needs 2 arguments."; + return "$_ERROR"; + fi; + _string="$1"; + _del="$2"; + if is_empty "${_string}"; then + if is_empty "${_del}"; then + return "$_GOOD"; + else + return "$_BAD"; + fi; + fi; + if is_empty "${_del}"; then + echo -n "${_string}"; + return "$_GOOD"; + fi; + _result="$(string_sed_s "${_string}" "${_del}"'$' '')"; + echo -n "${_result}"; + if is_equal "${_result}" "${_string}"; then + return "$_BAD"; + else + return "$_GOOD"; + fi; } ######################################################################## -# shift_quoted (<arg>*) +# string_flatten (<string> <char>) # -# Expects single-quoted arguments, strips the first quoted argument. +# Reduce multiple occurences of character <char> in <string> to one. # -# Arguments : single-quoted, evt. with included spaces. -# Output : delete everything up to the next arg terminated by a -# quote and the following space, output the rest. -# Return : `1' if arguments are not single-quoted, `0' otherwise. +# 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. # -shift_quoted() +string_flatten() { - local _args="$*"; - shift "$(count_next_quoted $_args)"; - output $*; + if test "$#" -ne 2; then + error "string_flatten() needs 2 arguments."; + return "$_ERROR"; + fi; + _string="$1"; + _char="$2"; + string_replace_all "$_string" "${_char}${_char}\+" "$_char"; } ######################################################################## -# supercat (<filearg>*) +# string_get_leading (<string> <regex>) # -# Output the concatenation of files, man-pages, or standard input to -# standard output. All parts that are stored in the gzip or Z -# compression format are decompressed. No other modifications. -# All processed arguments are added to the global variable -# $ARGS_DONE. +# Get the beginning <regex> of <string>, if any. # -# Arguments : -# All arguments are expected to be surrounded by single quotes. -# - names of existing files. -# - '-' to represent standard input (several times allowed). -# - 'man:name.(section)' the man-page for `name' in `section'. -# - 'man:name' the man-page for `name' in the lowest `section'. -# - 'name.section' the man-page for `name' in `section'. -# - 'name' the man-page for `name' in the lowest `section'. -# Globals : -# $TMP_INPUT : (read-only) -# $ARGS_DONE : (read-write) arguments with a corresponding file -# are added to variable ARGS_DONE. -# Output : the decompressed files corresponding to the arguments -# Return : 0 always, all errors are tolerated or fatal. +# 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. # -supercat() +string_get_leading() { - local _file; - local _filespec; - local _args; - local _mode; - local _sequence; - if [ "$#" -eq 0 ]; then - error 1 "supercat needs at least 1 arg"; - fi; - # remove enclosing quotes and space characters - _args="$(output $* | grep -e "^ *'.\+'"'$' | - sed -e '\|^ *\(.*\) *$|s||\1|')"; - if [ "$_args" = "" ]; then - error 1 "supercat : arguments are not quoted."; - fi; - while [ "$_args" != "" ]; do - _filespec="$(get_next_quoted $_args)"; - _args="$(shift_quoted $_args)"; - if [ "$_filespec" = "" ]; then - continue; - fi; - if [ "$_filespec" = "-" ]; then - catz "$TMP_INPUT"; - register_title "-"; - continue; - fi - if [ "$ENABLE_MANPAGES" = "yes" ]; then - if [ "$OPT_MAN" = "yes" ]; then - _sequence="Manpage File"; - else - _sequence="File Manpage"; - fi; + local _del; + local _result; + local _string; + if test "$#" -ne 2; then + error "string_get_leading() needs 2 arguments."; + return "$_ERROR"; + fi; + _string="$1"; + _get="$2"; + if is_empty "${_string}"; then + if is_empty "${_get}"; then + return "${_GOOD}"; else - _sequence="File"; + return "${_BAD}"; fi; - _done="no"; - for _mode in $_sequence; do - case "$_mode" in - File) - if [ -f "$_filespec" -a -r "$_filespec" ]; then - catz "$_filespec"; - register_title "$_filespec"; - _done="yes"; - break; - fi; - ;; - Manpage) - _manfile="$(manpage_search_filespec "$_filespec")"; - if [ "$?" -eq 0 ]; then - catz "$_manfile"; - register_title "$_manfile"; - _done="yes"; - break; - fi; - ;; - esac; - done; - if [ "$_done" != "yes" ]; then - echo2 \"$_filespec\" is neither a file nor a man-page.; + fi; + if is_empty "${_get}"; then + echo -n "${_string}"; + return "${_GOOD}"; + fi; + _result="$(string_sed_s "${_string}" '^\('"${_get}"'\).*$' '\1')"; + echo -n "${_result}"; + if is_equal "${_result}" "${_string}"; then + return "${_BAD}"; + else + return "${_GOOD}"; + fi; +} + + +######################################################################## +# 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() +{ + if test "$#" -ne 3; then + error "string_replace_all() needs exactly 3 arguments."; + return "$_ERROR"; + fi; + string_sed_s "$@" 'g'; +} + + +######################################################################## +# 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() +{ + 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."; + return "$_ERROR"; + ;; + esac; + _string="$1"; + _regex="$(_string_sed_s_esc_slash "$2")"; + if is_empty "${_string}"; then + return "$_OK"; + fi; + if is_empty "${_string}"; then + error "string_sed_s(): empty regular expression"; + return "$_ERROR"; + fi; + echo -n "${_string}" | \ + eval sed -e \'/"${_regex}"/s//"${_replace}"/"${_flag}"\'; +} # string_sed_s() + + +_string_sed_s_esc_slash() +{ + # Replace each slash `/' by `\/' outside of `[]' as in `sed s'. + # This makes the `sed' input independent of the delimiter '/'. + # + # Argument: 1, arbitrary string, may even contain line breaks. + # Output: the replaced string. + # + local _append; + local _last; + _append='Z'; + if test "$#" -ne 1; then + error '_string_sed_s_esc_slash() requires 1 argument.'; + return "$_ERROR"; + fi; + if is_empty "$1"; then + return "$_GOOD"; + fi; + # append a character to the argument to cover a final line break + unset IFS; + set -- "${1}${_append}"; + # split at line breaks + IFS="${_NEWLINE}" + set -- $1; + unset IFS; + while test "$#" -ge 2; do + _string_sed_s_esc_slash_line "$1"; + shift; + done; + if test "$#" -ne 1; then + error "string_sed_s_esc_slash() unexpected \`$#'"; + return "$_ERROR"; + fi; + if is_equal "${_last}" "${_append}"; then + echo; + return "$_GOOD"; + fi; + _last=$(echo -n "$1" | eval sed -e \''/'"${_append}"'$/s///'\'); + _string_sed_s_esc_slash_line "${_last}"; +} # _string_sed_s_esc_slash() + + +_string_sed_s_esc_slash_line() +{ + # In a text line replace each slash `/' by `\/', but not within `[]'. + local _beginning; + local _bracketed; + local _end; + local _rest; + local _result; + local _s; + if test "$#" -ne 1; then + error '_string_sed_s_esc_slash_line() requires 1 argument.'; + return "$_ERROR"; + fi; + _rest="$1"; + _result=''; + while true; do + if ! string_contains "${_rest}" '/'; then + _result="${_result}${_rest}"; + echo -n "${_result}"; + return "$_GOOD"; + fi; + if ! string_contains "${_rest}" '['; then + _result="${_result}$(_string_sed_s_esc_slash_unbracketed \ + "${_rest}")"; + echo -n "${_result}"; + 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 + _s="$(_string_sed_s_esc_slash_unbracketed "${_beginning}")"; + _result="${_result}${_s}"; + fi; + if ! string_contains "${_rest}" '/'; then + _result="${_result}${_rest}"; + echo -n "${_result}"; + 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'; + return "$_ERROR"; + ;; + esac; + _result="${_result}${_bracketed}"; + if ! string_contains "${_rest}" '/'; then + echo -n "${_result}${_rest}"; + return "$_GOOD"; fi; done; -} +} # _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. + # + local _arg; + local _result; + local _separator; + local _i; + if test "$#" -ne 1; then + error \ +'_string_sed_s_esc_slash_unbracketed() needs 1 argument).'; + return "$_ERROR"; + fi; + _arg="$1"; + if string_contains "$_arg" '['; then + error "_string_sed_s_esc_slash(): no bracket allowed in argument."; + return "$_ERROR"; + fi; + case "${_arg}" in + /) + echo -n '\/'; + return "$_OK"; + ;; + */*) + _result=""; + # split argument at `/' into the positional parameters + IFS=/ + set -- $(echo -n "${_arg}"); + unset IFS; + _result=""; + _separator=""; + while test "$#" -ge 1; do + _result="${_result}${_separator}$1"; + _separator='\/'; + shift; + done; + case "${_arg}" in # IFS character at the end is omitted + */) + _result="${_result}\/"; + ;; + esac; + echo -n "${_result}"; + return "$_OK"; + ;; + *) + echo -n "${_arg}"; + return "$_OK"; + ;; + esac; +} # _string_sed_s_esc_slash_unbracketed() ######################################################################## @@ -1126,7 +2346,7 @@ supercat() # tmp_cat() { - cat "$TMP_CAT"; + cat "${_TMP_CAT}"; } @@ -1143,38 +2363,36 @@ tmp_cat() tmp_create() { - local _i; - local _tmp=""; - _tmp="${TEMP_PREFIX}${PROCESS_ID}$1"; - echo -n >"$_tmp"; - output "$_tmp"; + local _tmp; + _tmp="${_TMP_PREFIX}${_PROCESS_ID}$1"; + echo -n >"${_tmp}"; + echo -n "${_tmp}"; } ######################################################################## -# unquote (<arg>*) -# -# Remove quotes around each argument and escape all space characters -# by a backslash `\'. +# to_tmp (<filename>) # -# Output : the same number of arguments, but each processed. -# -unquote() +# print file (decompressed) to the temporary cat file +to_tmp() { - local _res; - local _a; - local _args; - [ "$#" = 0 ] && return; - _res=""; - for _a in "$@"; do - _unq="$(eval output $_a | sed -e '\| |s||\\ |g')"; - if [ "$_res" = "" ]; then - _res="$_unq"; + if test "$#" -ne 1; then + error "to_tmp() expects 1 file argument." + return "$_ERROR"; + fi; + if is_file "$1"; then + if is_yes "$_OPT_LOCATION"; then + echo2 "$1"; + fi; + if is_yes "$_OPT_WHATIS"; then + what_is "$1" >>"${_TMP_CAT}"; else - _res="$_res $_unq"; + catz "$1" >>"${_TMP_CAT}"; fi; - done; - output "$_res"; + else + error "to_tmp(): could not read file \`$1'."; + return "$_ERROR"; + fi; } @@ -1185,15 +2403,22 @@ unquote() # usage() { + local _header; + local _gap; + _header="Usage: ${_PROGRAM_NAME}"; + _gap="$(string_replace_all "${_header}" '\.' ' ')"; echo2; - local _gap="$(echo -n $PROGRAM_NAME | sed -e '/./s// /g')"; version; cat >&2 <<EOF Copyright (C) 2001 Free Software Foundation, Inc. This is free software licensed under the GNU General Public License. -Usage : $PROGRAM_NAME [options] [file] [-] [[man:]manpage.x] - $_gap [[man:]manpage(x)] [[man:]manpage]... +EOF + + echo2 "${_header} [options] [file] [-] [[man:]manpage.x]"; + echo2 "${_gap} [[man:]manpage(x)] [[man:]manpage]..."; + + cat >&2 <<EOF Display roff files, standard input, and/or Unix manual pages with in a X window viewer or in a text pager. @@ -1206,17 +2431,23 @@ All input is decompressed on-the-fly (by gzip). -T --device=name set device for X or tty output. -v --version print version information. --dpi=res set resolution to "res" ("75" or "100" (default)). +--extension=ext restrict man pages to section suffix. +--local-file same as --no-man. +--locale=lang preset the language for man pages. --man check file parameters first whether they are man pages. --manpath=path preset path for searching man-pages. --no-man disable man-page facility. +--pager=program preset the paging program for tty mode. +--system=os1,... search man pages for different operating systems. --title='text' set the title of the viewer window in X. --tty force paging on text terminal even when in X. --xrdb=opt pass "opt" as option to gxditview (several allowed). -All other options are interpreted as "groff" parameters and transferred -unmodified to "grog". + +All other short options are interpreted as "groff" parameters and +transferred unmodified. EOF - if [ "$HAS_OPTS_GNU" != "yes" ]; then + if is_yes "${_HAS_OPTS_GNU}"; then cat >&2 <<EOF Your system does not support GNU long options. You can use the POSIX @@ -1238,7 +2469,106 @@ EOF # version() { - echo2 "$PROGRAM_NAME $PROGRAM_VERSION of $LAST_UPDATE"; + echo2 "${_PROGRAM_NAME} ${_PROGRAM_VERSION} of ${_LAST_UPDATE}"; +} + + +######################################################################## +# warning (<string>) +# +# Print warning to stderr +# +warning() +{ + echo2 "warning: $*"; +} + + +######################################################################## +# what_is (<filename>) +what_is () +{ + local _res; + local _dot; + if test "$#" -ne 1; then + error "to_tmp() expects 1 file argument." + return "$_ERROR"; + fi; + if ! is_file "$1"; then + error "to_tmp(): argument is not a readable file." + return "$_ERROR"; + fi; + _dot='^\.[ ]*'; + echo '.br'; + echo "$1: "; + echo '.br'; + echo -n ' '; + _res="$(catz "$1" | sed -e '/'"$_dot"'TH /p +d')"; + if is_not_empty "$_res"; then # traditional man style + catz "$1" | sed -e '1,/'"$_dot"'SH/d' \ + | sed -e '1,/'"$_dot"'SH/p +d' \ + | sed -e '/'"$_dot"'SH/d'; + return "$_GOOD"; + fi; + _res="$(catz "$1" | grep "$_dot"'Dd ')"; + if is_not_empty "$_res"; then # BSD doc style + catz "$1" | sed -e '/'"$_dot"'Nd /p +d' \ + | sed -e '2q' \ + | sed -e '/'"$_dot"'Nd *\(.*\)$/s//\1/'; + return "$_GOOD"; + fi; + echo 'is not a man page.'; + return "$_BAD"; +} + + +######################################################################## +# where (<program>) +# +# Print path of a program if in $PATH +# +# Arguments : 1 (empty allowed) +# Return : `0' if arg1 is a program in $PATH, `1' otherwise. +# +where() +{ + local _p; + local _file; + local _arg; + if test "$#" -ne 1; then + error "where() needs 1 argument."; + return "$_ERROR"; + fi; + _arg="$1"; + if is_empty "${_arg}"; then + return "$_BAD"; + fi; + case "${_arg}" in + /*) + if test -f "${_arg}" && test -x "${_arg}"; then + return "$_GOOD"; + else + return "$_BAD"; + fi; + ;; + esac; + IFS=: + set -- ${PATH} + unset IFS + for _p in "$@"; do + case "${_p}" in + */) _file=${_p}${_arg}; ;; + *) _file=${_p}/${_arg}; ;; + esac; + if test -f "${_file}" && test -x "${_file}"; then + echo -n "${_file}"; + return "$_GOOD"; + fi; + done; + return "$_BAD"; } @@ -1247,285 +2577,684 @@ version() ######################################################################## # The main area contains the following parts: -# - argument parsing -# - setup for display mode -# - setup for man-pages -# - temporary files -# - display +# - main_init(): initialize temporary files and set exit trap +# - main_parse_args(): argument parsing +# - determine display mode +# - setup display mode +# - parse $MANOPT +# - process filespecs +# - do the displaying + +# These parts are implemented as functions, being defined below in the +# sequence they are called in the main() function. +####################################################################### +# main_init () +# +# set exit trap and create temporary files +# +# Globals: $_TMP_CAT, $_TMP_STDIN +# +main_init() +{ + # call clean_up() on any signal + trap clean_up 2>/dev/null || true; + + _TMP_CAT="$(tmp_create)"; + _TMP_STDIN="$(tmp_create i)"; +} + ######################################################################## -# argument parsing (main) +# main_parse_args (<command_line_args>*) +# +# Parse arguments; process options and filespec parameters # -set -- $(normalize_args "$@"); +# Arguments: pass the command line arguments unaltered. +# Globals: +# in: $_OPTS_* +# out: $_OPT_*, $_ADDOPTS, $_FILEARGS +# +main_parse_args() +{ + local _arg; + local _code; + local _dpi; + local _longopt; + local _mode; + local _opt; + local _optchar; + local _optarg; + local _opts; + local _stdin_done; + local _string; + local _stripped; + local _warg; + + eval set -- "${GROFFER_OPT}" '"$@"'; + eval set -- "$(normalize_args \ + "${_OPTS_CMDLINE_SHORT}" "${_OPTS_CMDLINE_LONG}" "$@")"; + +# By the call of `eval', unnecessary quoting was removed. So the +# positional shell parameters ($1, $2, ...) are now guaranteed to +# represent an option or an argument to the previous option, if any; +# then a `--' argument for separating options and +# parameters; followed by the filespec parameters if any. + +# Note, the existence of arguments to options has already been checked. +# So a check for `$#' or `--' should not be done for arguments. + + until test "$#" -le 0 || is_equal "$1" '--'; do + _opt="$1"; # $_opt is fed into the option handler + shift; -# $* is garanteed to have a "--" argument, separating opts and params. -# Note that all arguments to options and all non-option parameters are -# enclosed in single quotes, while options are not quoted. The quotes -# must be removed before being used, see function `unqote'. For example, -# -X -m 'www' -- 'file1' '-' 'file2' -# parse options -until [ "$1" = "--" -o "$1" = "'--'" ]; do - # Note: arguments to options are quoted; these quotes must be handled. - # - _opt="$1"; - shift; - if [ "$_opt" = "-W" ]; then - _arg="$(get_next_quoted $*)"; - if echo "$GROFFER_LONGOPTS" | tr " " ' -' | grep "^$_arg"'$' >/dev/null 2>&1; then - # long option without argument triggered - _opt="--$_arg"; - set -- $(shift_quoted $*); - elif is_substring_of "[^=]=" "$_arg"; then - # possibly long option with argument - _argopt="$(echo "$_arg" | sed -e '\|^\([^=]\+\)=.*$|s||\1|')"; - _argarg="$(echo "$_arg" | sed -e '\|^[^=]\+=\(.*\)$|s||\1|')"; - if echo "$GROFFER_ARG_LONGS" | tr " " ' -' | grep "^$_argopt"':$' >/dev/null 2>&1; then - # long option with argument triggered - _opt="--$_argopt"; - set -- "'$_argarg'" $(shift_quoted $*); - fi - fi - fi; # else -W does not mean a long option - - case "$_opt" in - -h|--help) - usage; - leave; +# The special option `-W warg' can introduce a long groffer option +# (when `warg' starts with `--') or it is passed to groff (otherwise). +# It is worked on as follows. +# +# 1) If `warg' does not start with `--', `-W warg' is to be passed to +# groff as the groff no-warning option, so +# - store `-W warg' to `$_ADDOPTS'; +# - get to the next option by a `continue'. +# +# Otherwise, `warg' starts with `--'; so check whether `warg' can +# represent a long option by the following steps: +# +# 2) If `warg' is exactly a long groffer option without an argument then +# - store `warg' to `$_opt' (with the leading `--'); +# - go to the option handler. +# 3) If `warg' is exactly a long option that needs an argument then +# the argument for this option is the argument of the next `-W' +# command, which must follow immadiately; so +# - store `warg' to `$_opt'; +# - if the next positional parameter is not `-W', then error; +# - just skip the next `-W'; the wanted option argument is now `$1'; +# - go to the option handler. +# 4) If `arg' contains a `=' (equal sign) and the part before the +# first `=' is a long option that needs an argument, then +# - store this option to `$_opt'; +# - put the argument back as `$1' before the remaining positional +# parameters; +# - go to the option handler. +# Otherwise, error. +# 5) Otherwise, error. + + if is_equal "${_opt}" '-W'; then + _warg="$1"; + shift; + case "${_warg}" in + --*) # test on long option (steps 2-5) + _stripped="$(string_del_leading "${_warg}" '--')"; + if string_contains " ${_OPTS_CMDLINE_LONG_NA} " \ + " ${_stripped} "; + then # long option without argument (step 2) + _opt="--${_stripped}"; + elif string_contains \ + " ${_OPTS_CMDLINE_LONG_ARG} " " ${_stripped}: "; + then # separate argument expected (step 3) + _opt="--${_stripped}"; + if "$#" -eq 0; then + error "no argument found for \`${_opt}'"; + return "$_ERROR"; + fi + if is_equal "$1" '-W'; then + shift; # long option argument is now $1 + else + error "no argument found for \`${_opt}'"; + return "$_ERROR"; + fi + else # test on `=' (step 4) + case "${_stripped}" in + ?*=*) # has embedded `=' + # split off option before first `=' + _longopt="$(string_del_trailing "${_stripped}" '=.*')"; + if is_substring_of \ + " ${_OPTS_CMDLINE_LONG_ARG} " " ${_longopt}: "; + then # `opt=arg' verified (step 4) + # split off argument after first `=' + _optarg="$(string_del_leading \ + "${_stripped}" "${_longopt}=")"; + _opt="--${_longopt}"; + set -- "${_optarg}" "$@"; + else + error "wrong option \`-W ${_warg}'"; + return "$_ERROR"; + fi; + ;; + *) + error "wrong option \`-W ${_warg}'"; + return "$_ERROR"; + ;; + esac; + fi; + ;; + *) # argument is a warning + _ADDOPTS_GROFF="${_ADDOPTS_GROFF} -W '${_warg}'"; + if test "$#" -le 0 || is_equal "$1" '--' ]; then + break; + else + _opt="$1"; + shift; + fi; + ;; + esac; + else # not `-W' + do_nothing; + fi; + # now $_opt contains the option; $1 is its argument if needed. + + # handle options + case "${_opt}" in + -h|--help) + usage; + leave; + ;; + -P|--to-postproc) # option for postprocessor, arg; + _arg="$1"; + shift; + _ADDOPTS_POST="${_ADDOPTS_POST} -P '${_arg}'"; + ;; + -Q|--source) # output source code (`Quellcode'). + _OPT_MODE="source"; + ;; + -T|--device|--troff-device) + # device; arg + _arg="$1"; + shift; + _OPT_DEVICE="${_arg}"; + ;; + -v|--version) + version; + leave; + ;; + -X) + _OPT_MODE="X"; + ;; + -Z|--ditroff|--intermediate-output) + # groff intermediate output + _OPT_MODE="intermediate-output"; + ;; + -?) + _optchar="$(string_del_leading "${_opt}" '-')"; + if string_contains "${_OPTS_GROFF_SHORT_NA}" "${_optchar}"; then + _ADDOPTS_GROFF="${_ADDOPTS_GROFF} '${_opt}'"; + elif string_contains "${_OPTS_GROFF_SHORT_ARG}" "${_optchar}"; + then + _arg="$1"; + shift; + _ADDOPTS_GROFF="${_ADDOPTS_GROFF} '${_opt}' '${_arg}'"; + else + error "Unknown option : \`$1'"; + return "$_ERROR"; + fi; + ;; + --all) + _OPT_ALL="yes"; + ;; + --apropos) + _OPT_APROPOS="yes"; + ;; + --bg) # background color for gxditview, arg; + _arg="$1"; + shift; + _ADDOPTS_X="${_ADDOPTS_X} -P -bg -P '${_arg}'"; + ;; + --display) # set X display, arg + DISPLAY="$1"; + shift; + ;; + --dpi) # set resolution for X devices, arg + _arg="$1"; + shift; + case "${_arg}" in + 75|75dpi) + _dpi=75; + ;; + 100|100dpi) + _dpi=100; + ;; + *) + error "only resoutions of 75 or 100 dpi are supported"; + return "$_ERROR"; + ;; + esac; + _string="-P -resolution -P '${_dpi}'"; + _ADDOPTS_X="${_ADDOPTS_X} ${_string}"; + ;; + --extension) # the extension for man pages, arg + _OPT_EXTENSION="$1"; + shift; + ;; + --fg) # foreground color for gxditview, arg; + _arg="$1"; + shift; + _ADDOPTS_X="${_ADDOPTS_X} -P -fg -P '${_arg}'"; + ;; + --geometry) # geometry for gxditview window, arg; + _arg="$1"; + shift; + _ADDOPTS_X="${_ADDOPTS_X} -P -geometry -P '${_arg}'"; + ;; + --lang|--locale) # set language for man pages, arg + # argument is xx[_territory[.codeset[@modifier]]] (ISO 639,...) + _OPT_LANG="$1"; + shift; + ;; + --local-file) # force local files; same as `--no-man' + _MAN_FORCE="no"; + _MAN_ENABLE="no"; + ;; + --location|where) # print file locations to stderr + _OPT_LOCATION='yes'; + ;; + --man) # force all file params to be man pages + _MAN_ENABLE="yes"; + _MAN_FORCE="yes"; + ;; + --manpath) # specify search path for man pages, arg + # arg is colon-separated list of directories + _OPT_MANPATH="$1"; + shift; + ;; + --mode) # display mode + _arg="$1"; + shift; + case "${_arg}" in + auto|default|"") # default + _mode=""; + ;; + X|tty) # processed output + _mode="${_arg}"; + ;; + Q|source) # display source code + _mode="source"; + ;; + Z|intermediate-output) # generate only intermediate output + _mode="intermediate-output"; + ;; + *) + error "unknown mode ${_arg}"; + return "$_ERROR"; + ;; + esac; + _OPT_MODE="${_mode}"; + ;; + --no-location) # disable former call to `--location' + _OPT_LOCATION='yes'; + ;; + --no-man) # disable search for man pages + # the same as --local-file + _MAN_FORCE="no"; + _MAN_ENABLE="no"; + ;; + --pager) # set paging program for tty mode, arg + _OPT_PAGER="$1"; + shift; + ;; + --PX) # pass option to gxditview, arg; + _arg="$1"; + shift; + _ADDOPTS_X="${_ADDOPTS_X} -P '${_arg}'"; + ;; + --sections) # specify sections for man pages, arg + # arg is colon-separated list of section names + _OPT_SECTIONS="$1"; + shift; + ;; + --systems) # man pages for different OS's, arg + # argument is a comma-separated list + _OPT_SYSTEMS="$1"; + shift; + ;; + --title) # title for X, arg; OBSOLETE by -P + _arg="$1"; + _ADDOPTS_X="${_ADDOPTS_X} -P -title -P '${_arg}'"; + shift; + ;; + --tty) + _OPT_MODE="tty"; + ;; + --whatis) + _OPT_WHATIS='yes'; + ;; + --xrm) # pass X resource string, arg; + _arg="$1"; + shift; + _ADDOPTS_X="${_ADDOPTS_X} -P -xrm -P '${_arg}'"; + ;; + *) + error "error on argument parsing : \`$*'"; + return "$_ERROR"; + ;; + esac; + done; + shift; # remove `--' argument + # Remaining arguments are file names (filespecs). + + # Save filespecs to $_FILEARGS; must be retrieved with + # `eval set -- $_FILEARGS' + if test "$#" -eq 0; then # use "-" for standard input + _FILEARGS="'-'"; + save_stdin; + else + if is_yes "$_OPT_APROPOS"; then + apropos "$@"; + _code="$?"; + clean_up; + exit "$_code"; + fi; + + _FILEARGS=""; + _stdin_done="no"; + while test "$#" -gt 0 && is_not_yes "${_stdin_done}"; do + if is_equal "$1" '-'; then + save_stdin; + _stdin_done="yes"; + fi; + _FILEARGS="${_FILEARGS} '$1'"; + shift; + done; + fi; +} + + +######################################################################## +# main_set_mode () +# +# Determine the display mode. +# +# Globals: +# in: $DISPLAY, $_OPT_MODE, $_OPT_DEVICE +# out: $_DISPLAY_MODE +# +main_set_mode() +{ + case "${_OPT_MODE}" in + source|intermediate-output) + _DISPLAY_MODE="${_OPT_MODE}"; ;; - -Q|--source) # output source code (`Quellcode'). - OPT_SOURCE="yes"; + X) + if is_empty "${DISPLAY}"; then + error "you must be in X Window for this mode."; + return "$_ERROR"; + fi; + _DISPLAY_MODE="X"; ;; - -T|--device) # device, non-X* go to stdout, arg - _arg="$(get_next_quoted $*)"; - set -- $(shift_quoted $*); - case "$_arg" in - X75) - OPT_DEVICE=""; - OPT_DPI=75; + tty) + case "${_OPT_DEVICE}" in + "") + _DISPLAY_MODE="tty"; ;; - X100) - OPT_DEVICE=""; - OPT_DPI=100; + X*) + error "cannot display X device in a text terminal." + return "$_ERROR"; ;; *) - OPT_DEVICE="$_arg"; - OPT_DPI=""; + _DISPLAY_MODE="device"; ;; esac; ;; - -v|--version) - version; - leave; - ;; - --dpi) # set resolution for X devices, arg - _arg="$(get_next_quoted $*)"; - set -- $(shift_quoted $*); - case "$_arg" in - 75|75dpi) - OPT_DEVICE=""; - OPT_DPI=75; + "") + case "${_OPT_DEVICE}" in + "") + if is_empty "${DISPLAY}"; then + _DISPLAY_MODE="tty"; + else + _DISPLAY_MODE="X"; + fi; ;; - 100|100dpi) - OPT_DEVICE=""; - OPT_DPI=100; + X*) + if is_empty "${DISPLAY}"; then + error "cannot display X device in a text terminal." + return "$_ERROR"; + else + _DISPLAY_MODE="X"; + fi; ;; *) - error "only resoutions of 75 or 100 dpi are supported"; + _DISPLAY_MODE="device"; ;; esac; ;; - --man) # interpret all file params as man-pages - OPT_MAN="yes"; - if [ "$ENABLE_MANPAGES" != "yes" ] ; then - error "empty path for man-pages."; + esac; +} + +######################################################################## +# main_parse_MANOPT () +# +# Parse $MANOPT. +# +# Globals: +# in: $MANOPT, $_OPTS_MAN_* +# out: $_MANOPT_* +# in/out: $_MAN_ENABLE +# +main_parse_MANOPT() +{ + local _arg; + local _opt; + if is_not_yes "${_MAN_ENABLE}"; then + return "$_GOOD"; + fi; + eval set -- "$(normalize_args "${_OPTS_MAN_SHORT}" \ + "${_OPTS_MAN_LONG}" "${MANOPT}")"; + until test "$#" -le 0 || is_equal "$1" '--'; do + _opt="$1"; + shift; + case "${_opt}" in + -a|--all) + _OPT_ALL="yes"; + ;; + -D|--default) + # undo all man configuration so far (env vars and options) + : TODO; + ;; + -e|--extension) + _arg="$1"; + shift; + _MANOPT_EXTENSION="${_arg}"; + ;; + -l|--local-file) + _MAN_ENABLE="no"; + break; + ;; + -L|--locale) + _arg="$1"; + shift; + _MANOPT_LANG="${_arg}"; + ;; + -m|--systems) + _arg="$1"; + shift; + _MANOPT_SYS="${_arg}"; + ;; + -M|--manpath) + _arg="$1"; + shift; + _MANOPT_PATH="${_arg}"; + ;; + -P|--pager) + _arg="$1"; + shift; + _MANOPT_PAGER="${_arg}"; + ;; + -S|--sections) + _arg="$1"; + shift; + _MANOPT_SEC="${_arg}"; + ;; + -w|--where|--location) + _OPT_LOCATION='yes'; + ;; + # ignore all other options + esac + done +} + + +####################################################################### +# main_do_fileargs () +# +# Process filespec arguments in $_FILEARGS. +# +# Globals: +# in: $_FILEARGS +# +main_do_fileargs() +{ + local _filespec; + local _name; + local _ok; + local _sec; + eval set -- ${_FILEARGS}; + unset _FILEARGS; + # temporary storage of all input to $_TMP_CAT + while test "$#" -gt 0; do + _filespec="$1"; + shift; + + # test for `s name' arguments, with `s' a 1-char standard section + while true; do # just to allow `break' + _sec="$_filespec"; + if ! string_contains "$_MAN_AUTO_SEC" "$_sec"; then + break; fi; - ;; - --manpath) # specify search path for man-pages, arg - OPT_MANPATH="$(get_next_quoted $*)"; - set -- $(shift_quoted $*); - if [ "$OPT_MANPATH" = "" ]; then - ENABLE_MANPAGES="no"; - if [ "$OPT_MAN" = "yes" ] ; then - error "empty path for man-pages."; - fi; + case "$_sec" in + [^\ ]) do_nothing; ;; # non-space character + *) break; ;; + esac; + if test "$#" -le 0; then + break; + fi; + _name="$1"; + case "$_name" in + */*|man:*|*\(*\)|*."$_sec") break; ;; + esac; + if do_filearg "man:${_name}(${_sec})"; then + shift; + _filespec="$1"; + continue; else - ENABLE_MANPAGES="yes"; + break; fi; - HAS_MANW=""; + done; # end of `s name' test + + do_filearg "$_filespec"; + if test "$?" != "$_GOOD"; then + echo2 "\`${1}' is neither a file nor a man-page."; + fi; + done; +} + +######################################################################## +# main_display () +# +# Do the actual display of the whole thing. +# +# Globals: +# in: $_DISPLAY_MODE, $_OPT_DEVICE, +# $_ADDOPTS_GROFF, $_ADDOPTS_POST, $_ADDOPTS_X, +# $_REGISTERED_TITLE, $_TMP_PREFIX, $_TMP_CAT, +# $_OPT_PAGER $PAGER $_MANOPT_PAGER +# +main_display() +{ + local _addopts; + local _options; + local _groggy; + local _title; + local _old_tmp; + local _p; + local _pager; + export _addopts; + export _groggy; + case "${_DISPLAY_MODE}" in + source) + tmp_cat; + clean_up; ;; - --no-man) # interpret all file params as man-pages - OPT_MAN="no"; - ENABLE_MANPAGES="no"; + intermediate-output) + _options="-Z"; + if is_not_empty "${_OPT_DEVICE}"; then + _options="$_options -T'${_OPT_DEVICE}'"; + fi; + _groggy="$(tmp_cat | eval grog "${_options}")"; + tmpcat | eval "${_groggy}" "${_ADDOPTS_GROFF}"; + clean_up; ;; - --title) - OPT_TITLE="$(get_next_quoted $*)"; - set -- $(shift_quoted $*); + device) + _groggy="$(tmp_cat | grog -T"${_OPT_DEVICE}")"; + tmp_cat | eval "${_groggy}" "${_ADDOPTS_GROFF}"; + clean_up; ;; - --tty) - OPT_TTY="yes"; + X) + _addopts="${_ADDOPTS_GROFF} ${_ADDOPTS_POST} ${_ADDOPTS_X}"; + if is_not_empty "${_REGISTERED_TITLE}"; then + _title="${_REGISTERED_TITLE}"; + _addopts="-P -title -P '${_title}' $_addopts"; + fi; + if ! is_empty "${_OPT_DEVICE}"; then + _addopts="-T '${_OPT_DEVICE}' ${_addopts}"; + fi; + clean_up_secondary; + _groggy="$(tmp_cat | grog -X)"; + trap "" EXIT 2>/dev/null || true; + # start a new shell program to get another process ID. + sh -c ' + set -e; + _PROCESS_ID="$$"; + _old_tmp="${_TMP_CAT}"; + _TMP_CAT="${_TMP_PREFIX}${_PROCESS_ID}"; + rm -f "${_TMP_CAT}"; + mv "$_old_tmp" "${_TMP_CAT}"; + cat "${_TMP_CAT}" | \ + ( + clean_up() + { + rm -f "${_TMP_CAT}"; + } + trap clean_up EXIT 2>/dev/null || true; + eval "${_groggy}" "${_addopts}"; + ) &' ;; - --xrdb) # add X resource for gxditview, arg - _arg="$(get_next_quoted $*)"; - set -- $(shift_quoted $*); - OPT_XRDB="$(append_args "$OPT_XRDB" "$_arg")"; + tty) + _addopts="${_ADDOPTS_GROFF} ${_ADDOPTS_POST}"; + _groggy="$(tmp_cat | grog -Tlatin1)"; + _pager=""; + for _p in "${_OPT_PAGER}" "${PAGER}" "less" "${_MANOPT_PAGER}"; do + if is_prog "${_p}"; then + _pager="${_p}"; + break; + fi; + done; + tmp_cat | eval "${_groggy}" "${_addopts}" | \ + eval "${_pager}"; + clean_up; ;; - -?) - _opt_char="$(output $_opt | sed -e '\|-.|s|-||')"; - if is_substring_of "$_opt_char" "${GROFF_SHORTOPTS}"; then - OTHER_OPTIONS="$(append_args "$OTHER_OPTIONS" "$_opt")"; - elif is_substring_of "$_opt_char" "${GROFF_ARG_SHORTS}"; then - _arg="$(get_next_quoted $*)"; - OTHER_OPTIONS="$(\ - append_args $OTHER_OPTIONS "${_opt}${_arg}")"; - set -- $(shift_quoted $*); - else - error 1 "Unknown option : $1"; - fi; + *) + clean_up; ;; - *) error 1 "main : error on argument parsing : $*"; ;; esac; -done; -shift; # remove `--' argument - -unset _arg -unset _opt - -# Remaining arguments are file names, each enclosed in single quotes. -# Function supercat expects such arguments. - -if [ "$#" -eq 0 ]; then # use "-" for standard input - set -- "'-'"; -fi; -FILE_ARGS="$*"; # all file parameters; do not change +} ######################################################################## -# setup for display mode (main) +# main (<command_line_args>*) # -DISPLAY_MODE=""; -if [ "$OPT_SOURCE" = "yes" ]; then - DISPLAY_MODE="source"; # output source code -elif [ "$OPT_DEVICE" != "" ]; then - DISPLAY_MODE="device"; # non-X device, cat to stdout -elif [ "$DISPLAY" != "" -a "$OPT_TTY" != "yes" ]; then - DISPLAY_MODE="X"; # X -else - DISPLAY_MODE="tty"; # tty -fi; - - -######################################################################## -# setup for man-pages (main) +# The main function for groffer. # -if [ "$ENABLE_MANPAGES" = "yes" ]; then - MAN_PATH="$(get_manpath)"; - if [ "$MAN_PATH" = "" ]; then - ENABLE_MANPAGES="no"; - fi; -fi; - - -####################################################################### -# temporary files (main) +# Arguments: # +main() +{ + # Do not change the sequence of the following functions! + main_init; + main_parse_args "$@"; + main_set_mode; + main_parse_MANOPT; + main_do_fileargs; + main_display; +} -trap clean_up 2>/dev/null || true; - -# save standard input -TMP_INPUT="$(tmp_create i)"; -save_stdin_if_any; - -# built up title consisting of processed filespecs -if [ "$DISPLAY_MODE" = "X" ]; then - TMP_TITLE="$(tmp_create t)"; - output "$PROGRAM_NAME :" > $TMP_TITLE; - - register_title() - { - set -- $(base_name "$*"); # remove directory part - set -- $(del_ext_from .gz "$*"); # remove .hz - set -- $(del_ext_from .Z "$*"); # remove .Z - case "$#" in - 0) return; ;; - 1) _res="$1"; ;; - *) _res="'$*'"; ;; - esac; - output " $_res" >> "$TMP_TITLE"; - } - -else - - register_title() - { - true; # dummy - } - -fi; - -# temporary storage of all input -TMP_CAT="$(tmp_create)"; -supercat $FILE_ARGS >"$TMP_CAT"; # this does the main work - -if [ "$TMP_INPUT" != "" ]; then - rm -f "$TMP_INPUT"; -fi; - - -######################################################################## -# display (main) -# -case "$DISPLAY_MODE" in - source) - tmp_cat; - clean_up; - ;; - device) - _groggy="$(tmp_cat | grog $OTHER_OPTIONS -T"${OPT_DEVICE}")"; - tmp_cat | eval $_groggy; - clean_up; - ;; - X) - _title="$(make_title)"; - if [ "$OPT_DPI" = "" ]; then - OPT_DPI="$(check_dpi)"; # sanity check for using 100 dpi default - fi; - _groggy="$(tmp_cat | grog $OTHER_OPTIONS -TX"${OPT_DPI}" -Z )"; - trap "" EXIT 2>/dev/null || true; - # start a new shell program to get another process ID. - sh -c ' - clean_up() - { - if [ "$TMP_CAT" != "" ]; then - rm -f "$TMP_CAT"; - fi; - } - PROCESS_ID="$$"; - _old_tmp="$TMP_CAT"; - TMP_CAT="${TEMP_PREFIX}${PROCESS_ID}"; - rm -f "$TMP_CAT"; - mv "$_old_tmp" "$TMP_CAT"; - cat "$TMP_CAT" | eval $_groggy | \ - ( - trap clean_up EXIT 2>/dev/null || true; - gxditview $OPT_XRDB -title "$_title" -; - rm -f ; - ) &' - ;; - tty) - _groggy="$(tmp_cat | grog $OTHER_OPTIONS -Tlatin1)"; - if [ "$PAGER" = "" ]; then - _pager=less; - else - _pager=$PAGER; - fi; - tmp_cat | eval $_groggy | $_pager; - clean_up; - ;; - *) - clean_up; - ;; -esac; +main "$@"; |