Miscellaneous script portability enhancements.
[s-roff.git] / contrib / groffer / groffer.sh
blobc2146a233528e2cc815e77356bacc534acee0de7
1 #! /bin/sh
3 # groffer - display groff files
5 # Source file position: <groff-source>/contrib/groffer/groffer.sh
7 # Copyright (C) 2001,2002,2003,2004 Free Software Foundation, Inc.
8 # Written by Bernd Warken
10 # This file is part of groff version @VERSION@.
12 # groff is free software; you can redistribute it and/or modify it
13 # under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 2, or (at your option)
15 # any later version.
17 # groff is distributed in the hope that it will be useful, but WITHOUT
18 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
20 # License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with groff; see the files COPYING and LICENSE in the top
24 # directory of the groff source. If not, write to the Free Software
25 # Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 _PROGRAM_NAME='groffer';
28 _PROGRAM_VERSION='0.9.12';
29 _LAST_UPDATE='15 November 2004';
32 ########################################################################
33 # Determine the shell under which to run this script from the command
34 # line arguments or $GROFF_OPT; if none is specified, just go on with
35 # the starting shell.
37 if test _"${_groffer_run}"_ = __; then
38 # only reached during the first run of the script
40 export _groffer_run; # counter for the runs of groffer
41 _groffer_run='first';
43 export _PROGRAM_NAME;
44 export _PROGRAM_VERSION;
45 export _LAST_UPDATE;
47 export GROFFER_OPT; # option environment for groffer
48 export _GROFFER_SH; # file name of this shell script
49 export _OUTPUT_FILE_NAME; # output generated, see main_set_res..()
51 export _CONFFILES; # configuration files
52 _CONFFILES="/etc/groff/groffer.conf ${HOME}/.groff/groffer.conf";
54 case "$0" in
55 *${_PROGRAM_NAME}*)
56 _GROFFER_SH="$0";
57 # was: _GROFFER_SH="@BINDIR@/${_PROGRAM_NAME}";
60 echo "The ${_PROGRAM_NAME} script should be started directly." >&2
61 exit 1;
63 esac;
65 ###########################
66 # _get_opt_shell ("$@")
68 # Determine whether `--shell' was specified in $GROFF_OPT or in $*;
69 # if so echo its argument.
71 _get_opt_shell()
73 local i;
74 local _sh;
75 case " ${GROFFER_OPT} $*" in
76 *\ --shell\ *|*\ --shell=*)
78 eval set -- "${GROFFER_OPT}" '"$@"';
79 _sh='';
80 for i in "$@"; do
81 case "$1" in
82 --shell)
83 if test "$#" -ge 2; then
84 _sh="$2";
85 shift;
86 fi;
88 --shell=?*)
89 # delete up to first `=' character
90 _sh="$(echo -n "$1" | sed -e 's/^[^=]*=//')";
92 esac;
93 shift;
94 done;
95 echo -n "${_sh}";
98 esac;
102 ###########################
103 # _test_on_shell (<name>)
105 # Test whether <name> is a shell program of Bourne type (POSIX sh).
107 _test_on_shell()
109 if test "$#" -le 0 || test _"$1"_ = __; then
110 return 1;
112 # do not quote $1 to allow arguments
113 test _"$($1 -c 's=ok; echo -n "$s"' 2>/dev/null)"_ = _ok_;
116 # do the shell determination from command line and $GROFFER_OPT
117 _shell="$(_get_opt_shell "$@")";
119 if test _"${_shell}"_ = __; then
120 # none found, so look at the `--shell' lines in configuration files
121 export f;
122 for f in ${_CONFFILES}; do
123 if test -f $f; then
124 _all="$(cat $f | sed -n -e '/^--shell[= ] *\([^ ]*\)$/s//\1/p')"
125 for s in ${_all}; do
126 _shell=$s;
127 done;
129 done;
130 unset f;
131 unset s;
132 unset _all;
135 # restart the script with the last found $_shell, if it is a shell
136 if _test_on_shell "${_shell}"; then
137 _groffer_run='second';
138 # do not quote $_shell to allow arguments
139 exec ${_shell} "${_GROFFER_SH}" "$@";
140 exit;
143 _groffer_run='second';
144 unset _shell;
146 fi; # end of first run
148 if test _"${_groffer_run}"_ != _second_;
149 then
150 echo "$_groffer_run should be 'second' here." >&2
151 exit 1
154 unset _groffer_run
157 ########################################################################
158 # diagnostic messages
160 export _DEBUG;
161 _DEBUG='no'; # disable debugging information
162 #_DEBUG='yes'; # enable debugging information
164 export _DEBUG_LM;
165 _DEBUG_LM='no'; # disable landmark messages
166 #_DEBUG_LM='yes'; # enable landmark messages
169 ########################################################################
170 # Environment Variables
171 ########################################################################
173 # Environment variables that exist only for this file start with an
174 # underscore letter. Global variables to this file are written in
175 # upper case letters, e.g. $_GLOBAL_VARIABLE; temporary variables
176 # start with an underline and use only lower case letters and
177 # underlines, e.g. $_local_variable .
179 # [A-Z]* system variables, e.g. $MANPATH
180 # _[A-Z_]* global file variables, e.g. $_MAN_PATH
181 # _[a-z_]* temporary variables, e.g. $_manpath
183 # Due to incompatibilities of the `ash' shell, the name of loop
184 # variables in `for' must be single character
185 # [a-z] local loop variables, e.g. $i
188 ########################################################################
189 # read-only variables (global to this file)
190 ########################################################################
192 # characters
194 export _BQUOTE;
195 export _BSLASH;
196 export _DQUOTE;
197 export _NEWLINE;
198 export _LBRACK;
199 export _LPAR;
200 export _RBRACK;
201 export _RPAR;
202 export _SPACE;
203 export _SQUOTE;
204 export _TAB;
206 _BQUOTE='`';
207 _BSLASH='\';
208 _DQUOTE='"';
209 _NEWLINE='
211 _LBRACK='[';
212 _LPAR='(';
213 _RBRACK=']';
214 _RPAR=')';
215 _SPACE=' ';
216 _SQUOTE="'";
217 _TAB=' ';
219 # function return values; `0' means ok; other values are error codes
220 export _ALL_EXIT;
221 export _BAD;
222 export _ERROR;
223 export _GOOD;
224 export _NO;
225 export _OK;
226 export _YES;
228 _GOOD='0'; # return ok
229 _BAD='1'; # return negatively, error code `1'
230 _ERROR='7'; # for syntax errors; no `-1' in `ash'
232 _ALL_EXIT="${_GOOD} ${_BAD} ${_ERROR}"; # all exit codes (for `trap_set')
234 _NO="${_BAD}";
235 _YES="${_GOOD}";
236 _OK="${_GOOD}";
238 # quasi-functions, call with `eval'
239 export return_ok;
240 export return_good;
241 export return_bad;
242 export return_yes;
243 export return_no;
244 export return_error;
245 return_ok="func_pop; return ${_OK}";
246 return_good="func_pop; return ${_GOOD}";
247 return_bad="func_pop; return ${_BAD}";
248 return_yes="func_pop; return ${_YES}";
249 return_no="func_pop; return ${_NO}";
250 return_error="func_pop; return ${_ERROR}";
253 export _DEFAULT_MODES;
254 _DEFAULT_MODES='x,ps,tty';
255 export _DEFAULT_RESOLUTION;
256 _DEFAULT_RESOLUTION='75';
258 export _DEFAULT_TTY_DEVICE;
259 _DEFAULT_TTY_DEVICE='latin1';
261 # _VIEWER_* viewer programs for different modes (only X is necessary)
262 # _VIEWER_* a comma-separated list of viewer programs (with options)
263 export _VIEWER_DVI; # viewer program for dvi mode
264 export _VIEWER_PS; # viewer program for ps mode
265 export _VIEWER_HTML_X; # viewer program for html mode in X
266 export _VIEWER_HTML_TTY; # viewer program for html mode in tty
267 _VIEWER_DVI='xdvi,dvilx';
268 _VIEWER_PDF='xpdf,acroread';
269 _VIEWER_PS='gv,ghostview,gs_x11,gs';
270 _VIEWER_HTML='konqueror,mozilla,netscape,opera,amaya,arena,lynx';
271 _VIEWER_X='gxditview,xditview';
273 # Search automatically in standard sections `1' to `8', and in the
274 # traditional sections `9', `n', and `o'. On many systems, there
275 # exist even more sections, mostly containing a set of man pages
276 # special to a specific program package. These aren't searched for
277 # automatically, but must be specified on the command line.
278 export _MAN_AUTO_SEC;
279 _MAN_AUTO_SEC="'1' '2' '3' '4' '5' '6' '7' '8' '9' 'n' 'o'"
281 export _PROCESS_ID; # for shutting down the program
282 _PROCESS_ID="$$";
285 ############ the command line options of the involved programs
287 # The naming scheme for the options environment names is
288 # $_OPTS_<prog>_<length>[_<argspec>]
290 # <prog>: program name GROFFER, GROFF, or CMDLINE (for all
291 # command line options)
292 # <length>: LONG (long options) or SHORT (single character options)
293 # <argspec>: ARG for options with argument, NA for no argument;
294 # without _<argspec> both the ones with and without arg.
296 # Each option that takes an argument must be specified with a
297 # trailing : (colon).
299 # exports
300 export _OPTS_GROFFER_SHORT_NA;
301 export _OPTS_GROFFER_SHORT_ARG;
302 export _OPTS_GROFFER_LONG_NA;
303 export _OPTS_GROFFER_LONG_ARG;
304 export _OPTS_GROFF_SHORT_NA;
305 export _OPTS_GROFF_SHORT_ARG;
306 export _OPTS_GROFF_LONG_NA;
307 export _OPTS_GROFF_LONG_ARG;
308 export _OPTS_X_SHORT_ARG;
309 export _OPTS_X_SHORT_NA;
310 export _OPTS_X_LONG_ARG;
311 export _OPTS_X_LONG_NA;
312 export _OPTS_MAN_SHORT_ARG;
313 export _OPTS_MAN_SHORT_NA;
314 export _OPTS_MAN_LONG_ARG;
315 export _OPTS_MAN_LONG_NA;
316 export _OPTS_MANOPT_SHORT_ARG;
317 export _OPTS_MANOPT_SHORT_NA;
318 export _OPTS_MANOPT_LONG_ARG;
319 export _OPTS_MANOPT_LONG_NA;
320 export _OPTS_CMDLINE_SHORT_NA;
321 export _OPTS_CMDLINE_SHORT_ARG;
322 export _OPTS_CMDLINE_LONG_NA;
323 export _OPTS_CMDLINE_LONG_ARG;
325 ###### groffer native options
327 _OPTS_GROFFER_SHORT_NA="'h' 'Q' 'v' 'V' 'X' 'Z'";
328 _OPTS_GROFFER_SHORT_ARG="'T'";
330 _OPTS_GROFFER_LONG_NA="'auto' 'debug' 'default' 'dvi' \
331 'groff' 'help' 'intermediate-output' 'html' 'man' \
332 'no-location' 'no-man' 'pdf' 'ps' 'rv' 'source' 'text' 'text-device' \
333 'title' 'tty' 'tty-device' 'version' 'whatis' 'where' 'www' 'x' 'X'";
335 _OPTS_GROFFER_LONG_ARG="\
336 'apropos' 'apropos-data' 'apropos-devel' 'apropos-progs' \
337 'default-modes' 'device' 'dvi-viewer' 'extension' 'fg' 'fn' 'font' \
338 'foreground' 'html-viewer' 'mode' 'pdf-viewer' 'ps-viewer' 'shell' \
339 'tty-viewer' 'www-viewer' 'x-viewer' 'X-viewer'";
341 ##### groffer options inhereted from groff
343 _OPTS_GROFF_SHORT_NA="'a' 'b' 'c' 'C' 'e' 'E' 'g' 'G' 'i' 'l' 'N' 'p' \
344 'R' 's' 'S' 't' 'U' 'z'";
345 _OPTS_GROFF_SHORT_ARG="'d' 'f' 'F' 'I' 'L' 'm' 'M' 'n' 'o' 'P' 'r' \
346 'w' 'W'";
347 _OPTS_GROFF_LONG_NA="";
348 _OPTS_GROFF_LONG_ARG="";
350 ##### groffer options inhereted from the X Window toolkit
352 _OPTS_X_SHORT_NA="";
353 _OPTS_X_SHORT_ARG="";
355 _OPTS_X_LONG_NA="'iconic' 'rv'";
357 _OPTS_X_LONG_ARG="'background' 'bd' 'bg' 'bordercolor' 'borderwidth' \
358 'bw' 'display' 'fg' 'fn' 'font' 'foreground' 'ft', 'geometry'
359 'resolution' 'title' 'xrm'";
361 ###### groffer options inherited from man
363 _OPTS_MAN_SHORT_NA="";
364 _OPTS_MAN_SHORT_ARG="";
366 _OPTS_MAN_LONG_NA="'all' 'ascii' 'catman' 'debug' 'ditroff' 'help' \
367 'local-file' 'location' 'pager' 'troff' 'update' 'version' \
368 'whatis' 'where'";
370 _OPTS_MAN_LONG_ARG="'extension' 'locale' 'manpath' \
371 'pager' 'preprocessor' 'prompt' 'sections' 'systems' 'troff-device'";
373 ###### additional options for parsing $MANOPT only
375 _OPTS_MANOPT_SHORT_NA="'7' 'a' 'c' 'd' 'D' 'f' 'h' 'k' 'l' 't' 'u' \
376 'V' 'w' 'Z'";
377 _OPTS_MANOPT_SHORT_ARG="'e' 'L' 'm' 'M' 'p' 'P' 'r' 'S' 'T'";
379 _OPTS_MANOPT_LONG_NA="${_OPTS_MAN_LONG_NA} \
380 'apropos' 'debug' 'default' 'html' 'ignore-case' 'location-cat' \
381 'match-case' 'troff' 'update' 'version' 'where-cat'";
383 _OPTS_MANOPT_LONG_ARG="${_OPTS_MAN_LONG_NA} \
384 'config_file' 'encoding' 'locale'";
386 ###### collections of command line options
388 _OPTS_CMDLINE_SHORT_NA="${_OPTS_GROFFER_SHORT_NA}\
389 ${_OPTS_GROFF_SHORT_NA} ${_OPTS_X_SHORT_NA} ${_OPTS_MAN_SHORT_NA}";
390 _OPTS_CMDLINE_SHORT_ARG="${_OPTS_GROFFER_SHORT_ARG} \
391 ${_OPTS_GROFF_SHORT_ARG} ${_OPTS_X_SHORT_ARG} ${_OPTS_MAN_SHORT_ARG}";
393 _OPTS_CMDLINE_LONG_NA="${_OPTS_GROFFER_LONG_NA} \
394 ${_OPTS_GROFF_LONG_NA} ${_OPTS_X_LONG_NA} ${_OPTS_MAN_LONG_NA}";
395 _OPTS_CMDLINE_LONG_ARG="${_OPTS_GROFFER_LONG_ARG} \
396 ${_OPTS_GROFF_LONG_ARG} ${_OPTS_MAN_LONG_ARG} ${_OPTS_X_LONG_ARG}";
399 ########################################################################
400 # read-write variables (global to this file)
401 ########################################################################
403 export _ADDOPTS_GROFF; # Transp. options for groff (`eval').
404 export _ADDOPTS_POST; # Transp. options postproc (`eval').
405 export _ADDOPTS_X; # Transp. options X postproc (`eval').
406 export _DEFAULT_MODES; # Set default modes.
407 export _DISPLAY_MODE; # Display mode.
408 export _DISPLAY_PROG; # Viewer program to be used for display.
409 export _DISPLAY_ARGS; # X resources for the viewer program.
410 export _FILEARGS; # Stores filespec parameters.
411 export _FUNC_STACK; # Store debugging information.
412 export _REGISTERED_TITLE; # Processed file names.
413 # _HAS_* from availability tests
414 export _HAS_COMPRESSION; # `yes' if compression is available
415 export _HAS_OPTS_GNU; # `yes' if GNU `getopt' is available
416 export _HAS_OPTS_POSIX; # `yes' if POSIX `getopts' is available
417 # _MAN_* finally used configuration of man searching
418 export _MAN_ALL; # search all man pages per filespec
419 export _MAN_ENABLE; # enable search for man pages
420 export _MAN_EXT; # extension for man pages
421 export _MAN_FORCE; # force file parameter to be man pages
422 export _MAN_IS_SETUP; # setup man variables only once
423 export _MAN_LANG; # language for man pages
424 export _MAN_LANG_DONE; # language dirs added to man path
425 export _MAN_PATH; # search path for man pages
426 export _MAN_SEC; # sections for man pages; sep. `:'
427 export _MAN_SEC_DONE; # sections added to man path
428 export _MAN_SYS; # system names for man pages; sep. `,'
429 export _MAN_SYS; # system names added to man path
430 # _MANOPT_* as parsed from $MANOPT
431 export _MANOPT_ALL; # $MANOPT --all
432 export _MANOPT_EXTENSION; # $MANOPT --extension
433 export _MANOPT_LANG; # $MANOPT --locale
434 export _MANOPT_PATH; # $MANOPT --manpath
435 export _MANOPT_PAGER; # $MANOPT --pager
436 export _MANOPT_SEC; # $MANOPT --sections
437 export _MANOPT_SYS; # $MANOPT --systems
438 # _OPT_* as parsed from groffer command line
439 export _OPT_ALL; # display all suitable man pages.
440 export _OPT_APROPOS; # call `apropos' program.
441 export _OPT_APROPOS_DATA; # `apropos' for man sections 4, 5, 7
442 export _OPT_APROPOS_DEVEL; # `apropos' for man sections 2, 3, 9
443 export _OPT_APROPOS_PROGS; # `apropos' for man sections 1, 6, 8
444 export _OPT_BD; # set border color in some modes.
445 export _OPT_BG; # set background color in some modes.
446 export _OPT_BW; # set border width in some modes.
447 export _OPT_DEBUG; # print debugging information on stderr.
448 export _OPT_DEFAULT_MODES; # `,'-list of modes when no mode given.
449 export _OPT_DEVICE; # device option.
450 export _OPT_DISPLAY; # set X display.
451 export _OPT_FG; # set foreground color in some modes.
452 export _OPT_FN; # set font in some modes.
453 export _OPT_GEOMETRY; # set size and position of viewer in X.
454 export _OPT_ICONIC; # -iconic option for X viewers.
455 export _OPT_LANG; # set language for man pages
456 export _OPT_LOCATION; # print processed file names to stderr
457 export _OPT_MODE; # values: X, tty, Q, Z, ""
458 export _OPT_MANPATH; # manual setting of path for man-pages
459 export _OPT_PAGER; # specify paging program for tty mode
460 export _OPT_RESOLUTION; # set X resolution in dpi
461 export _OPT_RV; # reverse fore- and background colors.
462 export _OPT_SECTIONS; # sections for man page search
463 export _OPT_SYSTEMS; # man pages of different OS's
464 export _OPT_TITLE; # title for gxditview window
465 export _OPT_TEXT_DEVICE; # set device for tty mode.
466 export _OPT_V; # groff option -V.
467 export _OPT_VIEWER_DVI; # viewer program for dvi mode
468 export _OPT_VIEWER_PDF; # viewer program for pdf mode
469 export _OPT_VIEWER_PS; # viewer program for ps mode
470 export _OPT_VIEWER_HTML; # viewer program for html mode
471 export _OPT_VIEWER_X; # viewer program for x mode
472 export _OPT_WHATIS; # print the one-liner man info
473 export _OPT_XRM; # specify X resource.
474 export _OPT_Z; # groff option -Z.
475 # _TMP_* temporary files
476 export _TMP_DIR; # groffer directory for temporary files
477 export _TMP_CAT; # stores concatenation of everything
478 export _TMP_STDIN; # stores stdin, if any
480 # these variables are preset in section `Preset' after the rudim. test
483 ########################################################################
484 # Test of rudimentary shell functionality
485 ########################################################################
488 ########################################################################
489 # Test of `test'.
491 test "a" = "a" || exit 1;
494 ########################################################################
495 # Test of `echo' and the `$()' construct.
497 echo -n '' >/dev/null || exit "${_ERROR}";
498 if test _"$(echo -n 'te' && echo -n '' && echo -n 'st')"_ != _test_; then
499 exit "${_ERROR}";
503 ########################################################################
504 # Test of function definitions.
506 _t_e_s_t_f_u_n_c_()
508 return "${_OK}";
511 if _t_e_s_t_f_u_n_c_ 2>/dev/null; then
513 else
514 echo 'shell does not support function definitions.' >&2;
515 exit "${_ERROR}";
519 ########################################################################
520 # Preset and reset of read-write global variables
521 ########################################################################
524 # For variables that can be reset by option `--default', see reset().
526 _FILEARGS='';
528 # _HAS_* from availability tests
529 _HAS_COMPRESSION='';
530 _HAS_OPTS_GNU='';
531 _HAS_OPTS_POSIX='';
533 # _TMP_* temporary files
534 _TMP_DIR='';
535 _TMP_CAT='';
536 _TMP_STDIN='';
539 ########################################################################
540 # reset ()
542 # Reset the variables that can be affected by options to their default.
544 reset()
546 if test "$#" -ne 0; then
547 error "reset() does not have arguments.";
550 _ADDOPTS_GROFF='';
551 _ADDOPTS_POST='';
552 _ADDOPTS_X='';
553 _DISPLAY_ARGS='';
554 _DISPLAY_MODE='';
555 _DISPLAY_PROG='';
556 _REGISTERED_TITLE='';
558 # _MAN_* finally used configuration of man searching
559 _MAN_ALL='no';
560 _MAN_ENABLE='yes'; # do search for man-pages
561 _MAN_EXT='';
562 _MAN_FORCE='no'; # first local file, then search man page
563 _MAN_IS_SETUP='no';
564 _MAN_LANG='';
565 _MAN_LANG_DONE='no';
566 _MAN_PATH='';
567 _MAN_SEC='';
568 _MAN_SEC_DONE='no';
569 _MAN_SYS='';
570 _MAN_SYS_DONE='no';
572 # _MANOPT_* as parsed from $MANOPT
573 _MANOPT_ALL='no';
574 _MANOPT_EXTENSION='';
575 _MANOPT_LANG='';
576 _MANOPT_PATH='';
577 _MANOPT_PAGER='';
578 _MANOPT_SEC='';
579 _MANOPT_SYS='';
581 # _OPT_* as parsed from groffer command line
582 _OPT_ALL='no';
583 _OPT_APROPOS='';
584 _OPT_APROPOS_DATA='';
585 _OPT_APROPOS_DEVEL='';
586 _OPT_APROPOS_PROGS='';
587 _OPT_BD='';
588 _OPT_BG='';
589 _OPT_BW='';
590 _OPT_DEBUG='no';
591 _OPT_DEFAULT_MODES='';
592 _OPT_DEVICE='';
593 _OPT_DISPLAY='';
594 _OPT_FG='';
595 _OPT_FN='';
596 _OPT_GEOMETRY='';
597 _OPT_ICONIC='no';
598 _OPT_LANG='';
599 _OPT_LOCATION='no';
600 _OPT_MODE='';
601 _OPT_MANPATH='';
602 _OPT_PAGER='';
603 _OPT_RESOLUTION='';
604 _OPT_RV='no';
605 _OPT_SECTIONS='';
606 _OPT_SYSTEMS='';
607 _OPT_TITLE='';
608 _OPT_TEXT_DEVICE='';
609 _OPT_V='no';
610 _OPT_VIEWER_DVI='';
611 _OPT_VIEWER_PDF='';
612 _OPT_VIEWER_PS='';
613 _OPT_VIEWER_HTML='';
614 _OPT_VIEWER_X='';
615 _OPT_WHATIS='no';
616 _OPT_XRM='';
617 _OPT_Z='no';
621 reset;
624 ########################################################################
625 # Functions for error handling and debugging
626 ########################################################################
629 ##############
630 # landmark (<text>)
632 # Print <text> to standard error as a debugging aid.
634 # Globals: $_DEBUG_LM
636 landmark()
638 if test _"${_DEBUG_LM}"_ = _yes_; then
639 echo ">>> $*" >&2;
643 landmark "1: debugging functions";
646 ##############
647 # clean_up ()
649 # Clean up at exit.
651 clean_up()
653 if test -d "${_TMP_DIR}"; then
654 rm -f -r "${_TMP_DIR}";
659 ##############
660 # echo2 (<text>*)
662 # Output to stderr.
664 # Arguments : arbitrary text.
666 echo2()
668 echo "$*" >&2;
672 ##############
673 # echo2n (<text>*)
675 # Output to stderr.
677 # Arguments : arbitrary text.
679 echo2n()
681 echo -n "$*" >&2;
685 #############
686 # diag (text>*)
688 # Output a diagnostic message to stderr
690 diag()
692 echo2 '>>>>>'"$*";
696 #############
697 # error (<text>*)
699 # Print an error message to standard error; exit with an error condition
701 error()
703 local i;
704 local _code;
705 _code="${_ERROR}";
706 case "$#" in
707 0) true; ;;
708 1) echo2 'groffer error: '"$1"; ;;
710 echo2 'groffer error: '"$1";
711 _code="$2";
713 *) echo2 'groffer error: wrong number of arguments in error().'; ;;
714 esac;
715 if test _"${_DEBUG}"_ = _yes_; then
716 func_stack_dump;
718 clean_up;
719 kill "${_PROCESS_ID}" >/dev/null 2>&1;
720 kill -9 "${_PROCESS_ID}" >/dev/null 2>&1;
721 exit "${_code}";
725 #############
726 # abort (<text>*)
728 # Terminate program with error condition
730 abort()
732 error "Program aborted.";
733 exit 1;
737 #############
738 # func_check (<func_name> <rel_op> <nr_args> "$@")
740 # Check number of arguments and register to _FUNC_STACK.
742 # Arguments: >=3
743 # <func_name>: name of the calling function.
744 # <rel_op>: a relational operator: = != < > <= >=
745 # <nr_args>: number of arguments to be checked against <operator>
746 # "$@": the arguments of the calling function.
748 func_check()
750 local _comp;
751 local _fname;
752 local _nargs;
753 local _op;
754 local _s;
755 if test "$#" -lt 3; then
756 error 'func_check() needs at least 3 arguments.';
758 _fname="$1";
759 case "$3" in
761 _nargs="$3";
762 _s='';
764 0|[2-9])
765 _nargs="$3";
766 _s='s';
769 error "func_check(): third argument must be a digit.";
771 esac;
772 case "$2" in
773 '='|'-eq')
774 _op='-eq';
775 _comp='exactly';
777 '>='|'-ge')
778 _op='-ge';
779 _comp='at least';
781 '<='|'-le')
782 _op='-le';
783 _comp='at most';
785 '<'|'-lt')
786 _op='-lt';
787 _comp='less than';
789 '>'|'-gt')
790 _op='-gt';
791 _comp='more than';
793 '!='|'-ne')
794 _op='-ne';
795 _comp='not';
798 error \
799 'func_check(): second argument is not a relational operator.';
801 esac;
802 shift 3;
803 if test "$#" "${_op}" "${_nargs}"; then
804 do_nothing;
805 else
806 error \
807 "${_fname}"'() needs '"${_comp} ${_nargs}"' argument'"${_s}"'.';
809 if test _"${_DEBUG}"_ = _yes_; then
810 func_push "${_fname} $*";
815 #############
816 # func_pop ()
818 # Retrieve the top element from the stack.
820 # The stack elements are separated by `!'; the popped element is
821 # identical to the original element, except that all `!' characters
822 # were removed.
824 # Arguments: 1
826 func_pop()
828 if test _"${_DEBUG}"_ = _yes_; then
829 if test "$#" -ne 0; then
830 error 'func_pop() does not have arguments.';
832 case "${_FUNC_STACK}" in
834 error 'func_pop(): stack is empty.';
836 *!*)
837 # split at first bang `!'.
838 _FUNC_STACK="$(echo -n ${_FUNC_STACK} \
839 | sed -e 's/^[^!]*!//')";
842 _FUNC_STACK='';
844 esac;
849 #############
850 # func_push (<element>)
852 # Store another element to stack.
854 # The stack elements are separated by `!'; if <element> contains a `!'
855 # it is removed first.
857 # Arguments: 1
859 func_push()
861 local _element;
862 if test _"${_DEBUG}"_ = _yes_; then
863 if test "$#" -ne 1; then
864 error 'func_push() needs 1 argument.';
866 case "$1" in
867 *'!'*)
868 # remove all bangs `!'.
869 _element="$(echo -n "$1" | sed -e 's/!//g')";
872 _element="$1";
874 esac;
875 if test _"${_FUNC_STACK}"_ = __; then
876 _FUNC_STACK="${_element}";
877 else
878 _FUNC_STACK="${_element}!${_FUNC_STACK}";
884 #############
885 # func_stack_dump ()
887 # Print the content of the stack. Ignore the arguments.
889 func_stack_dump()
891 diag 'call stack:';
892 case "${_FUNC_STACK}" in
893 *!*)
894 _rest="${_FUNC_STACK}";
895 while test _"${_rest}"_ != __; do
896 # get part before the first bang `!'.
897 diag "$(echo -n "${_rest}" | sed -e 's/!.*$//')";
898 # delete part before and including the first bang `!'.
899 _rest="$(echo -n "${_rest}" | sed -e 's/^[^!]*!//')";
900 done;
903 diag "${_FUNC_STACK}";
905 esac;
909 ########################################################################
910 # System Test
911 ########################################################################
913 landmark "2: system test";
915 # Test the availability of the system utilities used in this script.
918 ########################################################################
919 # Test of `true'.
921 if true >/dev/null 2>&1; then
922 true;
923 else
924 true()
926 return "${_GOOD}";
929 false()
931 return "${_BAD}";
936 ########################################################################
937 # Test of `unset'.
939 _test='test';
940 if unset _test >/dev/null 2>&1 && test _"${_test}"_ = __; then
941 true;
942 else
943 unset()
945 for v in "$@"; do
946 eval "$v"='';
947 done;
950 unset _test;
952 ########################################################################
953 # Test of builtin `local'
956 _t_e_s_t_f_u_n_c_()
958 local _test >/dev/null 2>&1 || return "${_BAD}";
961 if _t_e_s_t_f_u_n_c_; then
963 else
964 local()
966 if test _"$1"_ != __; then
967 error "overriding global variable \`$1' with local value.";
973 ########################################################################
974 # Test of global setting in functions
976 _global='outside';
977 _clobber='outside';
979 _t_e_s_t_f_u_n_c_()
981 local _clobber;
982 _global='inside';
983 _clobber='inside';
986 _t_e_s_t_f_u_n_c_;
987 if test _"${_global}"_ != _inside_ || test _"${_clobber}"_ != _outside_;
988 then
989 error "Cannot assign to global variables from within functions.";
992 unset _global;
993 unset _clobber;
996 ########################################################################
997 # Test of function `sed'.
1000 if test _"$(echo xTesTx \
1001 | sed -e 's/^.\([Tt]e*x*sTT*\).*$/\1/' \
1002 | sed -e '\|T|s|T|t|g')"_ != _test_;
1003 then
1004 error 'Test of "sed" command failed.';
1008 ########################################################################
1009 # Test of function `cat'.
1011 if test _"$(echo test | cat)"_ != _test_; then
1012 error 'Test of "cat" command failed.';
1016 ########################################################################
1017 # Test for compression.
1019 if test _"$(echo 'test' | gzip -c -d -f - 2>/dev/null)"_ = _test_; then
1020 _HAS_COMPRESSION='yes';
1021 if echo 'test' | bzip2 -c 2>/dev/null | bzip2 -t 2>/dev/null \
1022 && test _"$(echo 'test' | bzip2 -c 2>/dev/null \
1023 | bzip2 -d -c 2>/dev/null)"_ \
1024 = _test_; then
1025 _HAS_BZIP='yes';
1026 else
1027 _HAS_BZIP='no';
1029 else
1030 _HAS_COMPRESSION='no';
1031 _HAS_BZIP='no';
1035 ########################################################################
1036 _t_e_s_t_f_u_n_c_()
1042 ########################################################################
1043 # Definition of normal Functions
1044 ########################################################################
1045 landmark "3: functions";
1047 ########################################################################
1048 # abort (<text>*)
1050 # Unconditionally terminate the program with error code;
1051 # useful for debugging.
1053 # defined above
1056 ########################################################################
1057 # apropos_run (<name>)
1060 apropos_run() {
1061 func_check apropos_run = 1 "$@";
1062 if apropos apropos >/dev/null 2>/dev/null; then
1063 apropos "$1";
1064 elif man --apropos man >/dev/null 2>/dev/null; then
1065 man --apropos "$1";
1066 elif man -k man >/dev/null 2>/dev/null; then
1067 man -k "$1";
1072 ########################################################################
1073 # base_name (<path>)
1075 # Get the file name part of <path>, i.e. delete everything up to last
1076 # `/' from the beginning of <path>. Remove final slashes, too, to get a
1077 # non-empty output.
1079 # Arguments : 1
1080 # Output : the file name part (without slashes)
1082 base_name()
1084 func_check base_name = 1 "$@";
1085 local f;
1086 f="$1";
1087 case "$f" in
1089 # delete all final slashes
1090 f="$(echo -n "$f" | sed -e '\|.*|s|//*$||')";
1092 esac;
1093 case "$f" in
1094 /|'')
1095 eval "${return_bad}";
1097 */*)
1098 # delete everything before and including the last slash `/'.
1099 echo -n "$f" | sed -e '\|.*|s|^.*//*\([^/]*\)$|\1|';
1102 echo -n "$f";
1104 esac;
1105 eval "${return_ok}";
1109 ########################################################################
1110 # catz (<file>)
1112 # Decompress if possible or just print <file> to standard output.
1114 # gzip, bzip2, and .Z decompression is supported.
1116 # Arguments: 1, a file name.
1117 # Output: the content of <file>, possibly decompressed.
1119 if test _"${_HAS_COMPRESSION}"_ = _yes_; then
1120 catz()
1122 func_check catz = 1 "$@";
1123 case "$1" in
1125 error 'catz(): empty file name';
1127 '-')
1128 error 'catz(): for standard input use save_stdin()';
1130 esac;
1131 if obj _HAS_BZIP is_yes; then
1132 if bzip2 -t "$1" 2>/dev/null; then
1133 bzip2 -c -d "$1" 2>/dev/null;
1134 eval "${return_ok}";
1137 gzip -c -d -f "$1" 2>/dev/null;
1138 eval "${return_ok}";
1140 else
1141 catz()
1143 func_check catz = 1 "$@";
1144 cat "$1";
1145 eval "${return_ok}";
1150 ########################################################################
1151 # clean_up ()
1153 # Do the final cleaning up before exiting; used by the trap calls.
1155 # defined above
1158 ########################################################################
1159 # diag (<text>*)
1161 # Print marked message to standard error; useful for debugging.
1163 # defined above
1166 ########################################################################
1167 landmark '4: dirname()*';
1168 ########################################################################
1170 #######################################################################
1171 # dirname_append (<dir> <name>)
1173 # Append `name' to `dir' with clean handling of `/'.
1175 # Arguments : 2
1176 # Output : the generated new directory name <dir>/<name>
1178 dirname_append()
1180 func_check dirname_append = 2 "$@";
1181 local _res;
1182 if is_empty "$1"; then
1183 error "dir_append(): first argument is empty.";
1185 if is_empty "$2"; then
1186 echo -n "$1";
1187 else
1188 dirname_chop "$1"/"$2";
1190 eval "${return_ok}";
1194 ########################################################################
1195 # dirname_chop (<name>)
1197 # Remove unnecessary slashes from directory name.
1199 # Argument: 1, a directory name.
1200 # Output: path without double, or trailing slashes.
1202 dirname_chop()
1204 func_check dirname_chop = 1 "$@";
1205 local _arg;
1206 local _res;
1207 local _sep;
1208 # replace all multiple slashes by a single slash `/'.
1209 _res="$(echo -n "$1" | sed -e '\|.*|s|///*|/|g')";
1210 case "${_res}" in
1211 ?*/)
1212 # remove trailing slash '/';
1213 echo -n "${_res}" | sed -e '\|.*|s|/$||';
1215 *) echo -n "${_res}"; ;;
1216 esac;
1217 eval "${return_ok}";
1221 ########################################################################
1222 # do_filearg (<filearg>)
1224 # Append the file, man-page, or standard input corresponding to the
1225 # argument to the temporary file. If this is compressed in the gzip
1226 # or Z format it is decompressed. A title element is generated.
1228 # Argument either:
1229 # - name of an existing files.
1230 # - `-' to represent standard input (several times allowed).
1231 # - `man:name.(section)' the man-page for `name' in `section'.
1232 # - `man:name.section' the man-page for `name' in `section'.
1233 # - `man:name' the man-page for `name' in the lowest `section'.
1234 # - `name.section' the man-page for `name' in `section'.
1235 # - `name' the man-page for `name' in the lowest `section'.
1236 # Globals :
1237 # $_TMP_STDIN, $_MAN_ENABLE, $_MAN_IS_SETUP, $_OPT_MAN
1239 # Output : none
1240 # Return : $_GOOD if found, ${_BAD} otherwise.
1242 do_filearg()
1244 func_check do_filearg = 1 "$@";
1245 local _filespec;
1246 local i;
1247 _filespec="$1";
1248 # store sequence into positional parameters
1249 case "${_filespec}" in
1251 eval "${return_good}";
1253 '-')
1254 register_file '-';
1255 eval "${return_good}";
1257 */*) # with directory part; so no man search
1258 set -- 'File';
1261 if obj _MAN_ENABLE is_yes; then
1262 if obj _MAN_FORCE is_yes; then
1263 set -- 'Manpage' 'File';
1264 else
1265 set -- 'File' 'Manpage';
1267 else
1268 set -- 'File';
1271 esac;
1272 for i in "$@"; do
1273 case "$i" in
1274 File)
1275 if test -f "${_filespec}"; then
1276 if test -r "${_filespec}"; then
1277 register_file "${_filespec}";
1278 eval "${return_good}";
1279 else
1280 echo2 "could not read \`${_filespec}'";
1281 eval "${return_bad}";
1283 else
1284 continue;
1287 Manpage) # parse filespec as man page
1288 if obj _MAN_IS_SETUP is_not_yes; then
1289 man_setup;
1291 if man_do_filespec "${_filespec}"; then
1292 eval "${return_good}";
1293 else
1294 continue;
1297 esac;
1298 done;
1299 eval "${return_bad}";
1300 } # do_filearg()
1303 ########################################################################
1304 # do_nothing ()
1306 # Dummy function.
1308 do_nothing()
1310 return "${_OK}";
1314 ########################################################################
1315 # echo2 (<text>*)
1317 # Print to standard error with final line break.
1319 # defined above
1322 ########################################################################
1323 # echo2n (<text>*)
1325 # Print to standard error without final line break.
1327 # defined above
1330 ########################################################################
1331 # error (<text>*)
1333 # Print error message and exit with error code.
1335 # defined above
1338 ########################################################################
1339 # func_check (<func_name> <rel_op> <nr_args> "$@")
1341 # Check number of arguments and register to _FUNC_STACK.
1343 # Arguments: >=3
1344 # <func_name>: name of the calling function.
1345 # <rel_op>: a relational operator: = != < > <= >=
1346 # <nr_args>: number of arguments to be checked against <operator>
1347 # "$@": the arguments of the calling function.
1349 # defined above
1351 #########################################################################
1352 # func_pop ()
1354 # Delete the top element from the function call stack.
1356 # defined above
1359 ########################################################################
1360 # func_push (<element>)
1362 # Store another element to function call stack.
1364 # defined above
1367 ########################################################################
1368 # func_stack_dump ()
1370 # Print the content of the stack.
1372 # defined above
1375 ########################################################################
1376 # get_first_essential (<arg>*)
1378 # Retrieve first non-empty argument.
1380 # Return : `1' if all arguments are empty, `0' if found.
1381 # Output : the retrieved non-empty argument.
1383 get_first_essential()
1385 func_check get_first_essential '>=' 0 "$@";
1386 local i;
1387 if is_equal "$#" 0; then
1388 eval "${return_ok}";
1390 for i in "$@"; do
1391 if obj i is_not_empty; then
1392 echo -n "$i";
1393 eval "${return_ok}";
1395 done;
1396 eval "${return_bad}";
1400 ########################################################################
1401 landmark '5: is_*()';
1402 ########################################################################
1404 ########################################################################
1405 # is_dir (<name>)
1407 # Test whether `name' is a directory.
1409 # Arguments : 1
1410 # Return : `0' if arg1 is a directory, `1' otherwise.
1412 is_dir()
1414 func_check is_dir = 1 "$@";
1415 if test -d "$1" && test -r "$1"; then
1416 eval "${return_yes}";
1418 eval "${return_no}";
1422 ########################################################################
1423 # is_empty (<string>)
1425 # Test whether `string' is empty.
1427 # Arguments : <=1
1428 # Return : `0' if arg1 is empty or does not exist, `1' otherwise.
1430 is_empty()
1432 func_check is_empty = 1 "$@";
1433 if test _"$1"_ = __; then
1434 eval "${return_yes}";
1436 eval "${return_no}";
1440 ########################################################################
1441 # is_equal (<string1> <string2>)
1443 # Test whether `string1' is equal to <string2>.
1445 # Arguments : 2
1446 # Return : `0' both arguments are equal strings, `1' otherwise.
1448 is_equal()
1450 func_check is_equal = 2 "$@";
1451 if test _"$1"_ = _"$2"_; then
1452 eval "${return_yes}";
1454 eval "${return_no}";
1458 ########################################################################
1459 # is_existing (<name>)
1461 # Test whether `name' is an existing file or directory.
1463 # Arguments : 1
1464 # Return : `0' if arg1 exists, `1' otherwise.
1466 is_existing()
1468 func_check is_existing = 1 "$@";
1469 if test -e "$1"; then
1470 eval "${return_yes}";
1472 eval "${return_no}";
1476 ########################################################################
1477 # is_file (<name>)
1479 # Test whether `name' is a readable file.
1481 # Arguments : 1
1482 # Return : `0' if arg1 is a readable file, `1' otherwise.
1484 is_file()
1486 func_check is_file = 1 "$@";
1487 if test -f "$1" && test -r "$1"; then
1488 eval "${return_yes}";
1490 eval "${return_no}";
1494 ########################################################################
1495 # is_non_empty_file (<file_name>)
1497 # Test whether `file_name' is a non-empty existing file.
1499 # Arguments : <=1
1500 # Return :
1501 # `0' if arg1 is a non-empty existing file
1502 # `1' otherwise
1504 is_non_empty_file()
1506 func_check is_non_empty_file = 1 "$@";
1507 if is_file "$1" && test -s "$1"; then
1508 eval "${return_yes}";
1510 eval "${return_no}";
1514 ########################################################################
1515 # is_not_dir (<name>)
1517 # Test whether `name' is not a readable directory.
1519 # Arguments : 1
1520 # Return : `0' if arg1 is a directory, `1' otherwise.
1522 is_not_dir()
1524 func_check is_not_dir = 1 "$@";
1525 if is_dir "$1"; then
1526 eval "${return_no}";
1528 eval "${return_yes}";
1532 ########################################################################
1533 # is_not_empty (<string>)
1535 # Test whether `string' is not empty.
1537 # Arguments : <=1
1538 # Return : `0' if arg1 exists and is not empty, `1' otherwise.
1540 is_not_empty()
1542 func_check is_not_empty = 1 "$@";
1543 if is_empty "$1"; then
1544 eval "${return_no}";
1546 eval "${return_yes}";
1550 ########################################################################
1551 # is_not_equal (<string1> <string2>)
1553 # Test whether `string1' differs from `string2'.
1555 # Arguments : 2
1557 is_not_equal()
1559 func_check is_not_equal = 2 "$@";
1560 if is_equal "$1" "$2"; then
1561 eval "${return_no}";
1563 eval "${return_yes}";
1567 ########################################################################
1568 # is_not_file (<filename>)
1570 # Test whether `name' is a not readable file.
1572 # Arguments : >=1 (empty allowed), more args are ignored
1574 is_not_file()
1576 func_check is_not_file '>=' 1 "$@";
1577 if is_file "$1"; then
1578 eval "${return_no}";
1580 eval "${return_yes}";
1584 ########################################################################
1585 # is_not_prog (<name>)
1587 # Verify that arg is a not program in $PATH.
1589 # Arguments : >=1 (empty allowed)
1590 # more args are ignored, this allows to specify progs with arguments
1592 is_not_prog()
1594 func_check is_not_prog '>=' 1 "$@";
1595 if where "$1" >/dev/null; then
1596 eval "${return_no}";
1598 eval "${return_yes}";
1602 ########################################################################
1603 # is_not_writable (<name>)
1605 # Test whether `name' is a not a writable file or directory.
1607 # Arguments : >=1 (empty allowed), more args are ignored
1609 is_not_writable()
1611 func_check is_not_writable '>=' 1 "$@";
1612 if is_writable "$1"; then
1613 eval "${return_no}";
1615 eval "${return_yes}";
1619 ########################################################################
1620 # is_not_yes (<string>)
1622 # Test whether `string' is not "yes".
1624 # Arguments : 1
1626 is_not_yes()
1628 func_check is_not_yes = 1 "$@";
1629 if is_yes "$1"; then
1630 eval "${return_no}";
1632 eval "${return_yes}";
1636 ########################################################################
1637 # is_prog (<name>)
1639 # Determine whether arg is a program in $PATH
1641 # Arguments : >=0 (empty allowed)
1642 # more args are ignored, this allows to specify progs with arguments
1644 is_prog()
1646 func_check is_prog '>=' 0 "$@";
1647 case "$#" in
1649 eval "${return_no}";
1652 if where "$1" >/dev/null; then
1653 eval "${return_yes}";
1656 esac
1657 eval "${return_no}";
1661 ########################################################################
1662 # is_writable (<name>)
1664 # Test whether `name' is a writable file or directory.
1666 # Arguments : >=1 (empty allowed), more args are ignored
1668 is_writable()
1670 func_check is_writable '>=' 1 "$@";
1671 if test -r "$1"; then
1672 if test -w "$1"; then
1673 eval "${return_yes}";
1676 eval "${return_no}";
1680 ########################################################################
1681 # is_yes (<string>)
1683 # Test whether `string' has value "yes".
1685 # Arguments : <=1
1686 # Return : `0' if arg1 is `yes', `1' otherwise.
1688 is_yes()
1690 func_check is_yes = 1 "$@";
1691 if is_equal "$1" 'yes'; then
1692 eval "${return_yes}";
1694 eval "${return_no}";
1698 ########################################################################
1699 # landmark ()
1701 # Print debugging information on standard error if $_DEBUG_LM is `yes'.
1703 # Globals: $_DEBUG_LM
1705 # Defined in section `Debugging functions'.
1708 ########################################################################
1709 # leave ()
1711 # Clean exit without an error.
1713 leave()
1715 clean_up;
1716 exit "${_OK}";
1720 ########################################################################
1721 landmark '6: list_*()';
1722 ########################################################################
1724 # `list' is an object class that represents an array or list. Its
1725 # data consists of space-separated single-quoted elements. So a list
1726 # has the form "'first' 'second' '...' 'last'". See list_append() for
1727 # more details on the list structure. The array elements of `list'
1728 # can be get by `set -- $list`.
1731 ########################################################################
1732 # list_append (<list> <element>...)
1734 # Arguments: >=2
1735 # <list>: a variable name for a list of single-quoted elements
1736 # <element>: some sequence of characters.
1737 # Output: none, but $<list> is set to
1738 # if <list> is empty: "'<element>' '...'"
1739 # otherwise: "$list '<element>' ..."
1741 list_append()
1743 func_check list_append '>=' 2 "$@";
1744 local _element;
1745 local _list;
1746 local _name;
1747 _name="$1";
1748 eval _list='"${'$1'}"';
1749 shift;
1750 for s in "$@"; do
1751 case "$s" in
1752 *\'*)
1753 # escape each single quote by replacing each
1754 # "'" (squote) by "'\''" (squote bslash squote squote);
1755 # note that the backslash must be doubled in the following `sed'
1756 _element="$(echo -n "$s" | sed -e 's/'"${_SQUOTE}"'/&\\&&/g')";
1758 '')
1759 _element="";
1762 _element="$s";
1764 esac;
1765 if obj _list is_empty; then
1766 _list="'${_element}'";
1767 else
1768 _list="${_list} '${_element}'";
1770 done;
1771 eval "${_name}"='"${_list}"';
1772 eval "${return_ok}";
1776 ########################################################################
1777 # list_from_cmdline (<pre_name_of_opt_lists> [<cmdline_arg>...])
1779 # Transform command line arguments into a normalized form.
1781 # Options, option arguments, and file parameters are identified and
1782 # output each as a single-quoted argument of its own. Options and
1783 # file parameters are separated by a '--' argument.
1785 # Arguments: >=1
1786 # <pre_name>: common part of a set of 4 environment variable names:
1787 # $<pre_name>_SHORT_NA: list of short options without an arg.
1788 # $<pre_name>_SHORT_ARG: list of short options that have an arg.
1789 # $<pre_name>_LONG_NA: list of long options without an arg.
1790 # $<pre_name>_LONG_ARG: list of long options that have an arg.
1791 # <cmdline_arg>...: the arguments from a command line, such as "$@",
1792 # the content of a variable, or direct arguments.
1794 # Output: ['-[-]opt' ['optarg']]... '--' ['filename']...
1796 # Example:
1797 # list_from_cmdline PRE 'a b' 'c' '' 'long' -a f1 -bcarg --long=larg f2
1798 # If $PRE_SHORT_NA, $PRE_SHORT_ARG, $PRE_LONG_NA, and $PRE_LONG_ARG are
1799 # none-empty option lists, this will result in printing:
1800 # '-a' '-b' '-c' 'arg' '--long' 'larg' '--' 'f1' 'f2'
1802 # Use this function in the following way:
1803 # eval set -- "$(args_norm PRE_NAME "$@")";
1804 # while test "$1" != '--'; do
1805 # case "$1" in
1806 # ...
1807 # esac;
1808 # shift;
1809 # done;
1810 # shift; #skip '--'
1811 # # all positional parameters ("$@") left are file name parameters.
1813 list_from_cmdline()
1815 func_check list_from_cmdline '>=' 1 "$@";
1816 local _fparams;
1817 local _fn;
1818 local _short_a;
1819 local _short_n;
1820 local _long_a;
1821 local _long_n;
1822 local _result;
1823 _short_n="$(obj_data "$1"_SHORT_NA)"; # short options, no argument
1824 _short_a="$(obj_data "$1"_SHORT_ARG)"; # short options, with argument
1825 _long_n="$(obj_data "$1"_LONG_NA)"; # long options, no argument
1826 _long_a="$(obj_data "$1"_LONG_ARG)"; # long options, with argument
1827 if obj _short_n is_empty; then
1828 error 'list_from_cmdline(): no $'"$1"'_SHORT_NA options.';
1830 if obj _short_a is_empty; then
1831 error 'list_from_cmdline(): no $'"$1"'_SHORT_ARG options.';
1833 if obj _long_n is_empty; then
1834 error 'list_from_cmdline(): no $'"$1"'_LONG_NA options.';
1836 if obj _long_a is_empty; then
1837 error 'list_from_cmdline(): no $'"$1"'_LONG_ARG options.';
1839 shift;
1840 _fn='list_from_cmdline():'; # for error messages
1841 if is_equal "$#" 0; then
1842 echo -n "'--'";
1843 eval "${return_ok}";
1845 _fparams='';
1846 _result='';
1847 while test "$#" -ge 1; do
1848 _arg="$1";
1849 shift;
1850 case "$_arg" in
1851 --) break; ;;
1852 --?*)
1853 # delete leading '--';
1854 _opt="$(echo -n "${_arg}" | sed -e 's/^..//')";
1855 if list_has _long_n "${_opt}"; then
1856 # long option, no argument
1857 list_append _result "--${_opt}";
1858 continue;
1860 # test on `--opt=arg'
1861 if string_contains "${_opt}" '='; then
1862 # extract option by deleting from the first '=' to the end
1863 _lopt="$(echo -n "${_opt}" | sed -e 's/=.*$//')";
1864 if list_has _long_a "${_lopt}"; then
1865 # get the option argument by deleting up to first `='
1866 _optarg="$(echo -n "${_opt}" | sed -e 's/^[^=]*=//')";
1867 list_append _result "--${_lopt}" "${_optarg}";
1868 continue;
1871 if list_has _long_a "${_opt}"; then
1872 # long option with argument
1873 if test "$#" -le 0; then
1874 error "${_fn} no argument for option --${_opt}."
1876 list_append _result "--${_opt}" "$1";
1877 shift;
1878 continue;
1880 error "${_fn} --${_opt} is not an option."
1882 -?*) # short option (cluster)
1883 # delete leading `-';
1884 _rest="$(echo -n "${_arg}" | sed -e 's/^-//')";
1885 while obj _rest is_not_empty; do
1886 # get next short option from cluster (first char of $_rest)
1887 _optchar="$(echo -n "${_rest}" | sed -e 's/^\(.\).*$/\1/')";
1888 # remove first character from ${_rest};
1889 _rest="$(echo -n "${_rest}" | sed -e 's/^.//')";
1890 if list_has _short_n "${_optchar}"; then
1891 list_append _result "-${_optchar}";
1892 continue;
1893 elif list_has _short_a "${_optchar}"; then
1894 if obj _rest is_empty; then
1895 if test "$#" -ge 1; then
1896 list_append _result "-${_optchar}" "$1";
1897 shift;
1898 continue;
1899 else
1900 error \
1901 "${_fn}"' no argument for option -'"${_optchar}."
1903 else # rest is the argument
1904 list_append _result "-${_optchar}" "${_rest}";
1905 _rest='';
1906 continue;
1908 else
1909 error "${_fn} unknown option -${_optchar}."
1911 done;
1914 # Here, $_arg is not an option, so a file parameter.
1915 list_append _fparams "${_arg}";
1917 # Ignore the strange option handling of $POSIXLY_CORRECT to
1918 # end option parsing after the first file name argument. To
1919 # reuse it, do a `break' here if $POSIXLY_CORRECT is
1920 # non-empty.
1922 esac;
1923 done;
1924 list_append _result '--';
1925 if obj _fparams is_not_empty; then
1926 _result="${_result} ${_fparams}";
1928 if test "$#" -gt 0; then
1929 list_append _result "$@";
1931 echo -n "$_result";
1932 eval "${return_ok}";
1933 } # list_from_cmdline()
1936 ########################################################################
1937 # list_from_split (<string> <separator>)
1939 # In <string>, escape all white space characters and replace each
1940 # <separator> by space.
1942 # Arguments: 2: a <string> that is to be split into parts divided by
1943 # <separator>
1944 # Output: the resulting list string
1946 list_from_split()
1948 func_check list_from_split = 2 "$@";
1949 local _s;
1951 # precede each space or tab by a backslash `\' (doubled for `sed')
1952 _s="$(echo -n "$1" | sed -e 's/\(['"${_SPACE}${_TAB}"']\)/\\\1/g')";
1954 # replace split character of string by the list separator ` ' (space).
1955 case "$2" in
1956 /) # cannot use normal `sed' separator
1957 echo -n "${_s}" | sed -e '\|.*|s|'"$2"'| |g';
1959 ?) # use normal `sed' separator
1960 echo -n "${_s}" | sed -e 's/'"$2"'/ /g';
1962 ??*)
1963 error 'list_from_split(): separator must be a single character.';
1965 esac;
1966 eval "${return_ok}";
1970 ########################################################################
1971 # list_get (<list>)
1973 # Check whether <list> is a space-separated list of '-quoted elements.
1975 # If the test fails an error is raised.
1976 # If the test succeeds the argument is echoed.
1978 # Testing criteria:
1979 # A list has the form "'first' 'second' '...' 'last'". So it has a
1980 # leading and a final quote and the elements are separated by "' '"
1981 # constructs. If these are all removed there should not be any
1982 # unescaped single-quotes left. Watch out for escaped single
1983 # quotes; they have the form '\'' (sq bs sq sq).
1985 # Arguments: 1
1986 # Output: the argument <list> unchanged, if the check succeeded.
1988 list_get()
1990 func_check list_get = 1 "$@";
1991 local _list;
1992 eval _list='"${'$1'}"';
1993 # remove leading and final space characters
1994 _list="$(echo -n "${_list}" | \
1995 sed -e 's/^['"${_SPACE}${_TAB}"']*//' | \
1996 sed -e 's/['"${_SPACE}${_TAB}"']*$//')";
1997 case "${_list}" in
1999 eval "${return_ok}";
2001 \'*\')
2002 echo -n "${_list}";
2003 eval "${return_ok}";
2006 error "list_get(): bad list: $1"
2008 esac;
2009 eval "${return_ok}";
2013 ########################################################################
2014 # list_has (<var_name> <element>)
2016 # Arguments: 2
2017 # <var_name>: a variable name for a list of single-quoted elements
2018 # <element>: some sequence of characters.
2019 # Output:
2020 # if <list> is empty: "'<element>' '...'"
2021 # otherwise: "list '<element>' ..."
2023 list_has()
2025 func_check list_has = 2 "$@";
2026 eval _list='"${'$1'}"';
2027 if obj _list is_empty; then
2028 eval "${return_no}";
2030 _element="$2";
2031 case "$2" in
2032 \'*\') _element="$2"; ;;
2033 *) _element="'$2'"; ;;
2034 esac;
2035 if string_contains "${_list}" "${_element}"; then
2036 eval "${return_yes}";
2037 else
2038 eval "${return_no}";
2040 eval "${return_ok}";
2044 ########################################################################
2045 # list_has_not (<list> <element>)
2047 # Arguments: 2
2048 # <list>: a space-separated list of single-quoted elements.
2049 # <element>: some sequence of characters.
2050 # Output:
2051 # if <list> is empty: "'<element>' '...'"
2052 # otherwise: "<list> '<element>' ..."
2054 list_has_not()
2056 func_check list_has_not = 2 "$@";
2057 eval _list='"${'$1'}"';
2058 if obj _list is_empty; then
2059 eval "${return_yes}";
2061 _element="$2";
2062 case "$2" in
2063 \'*\') _element="$2"; ;;
2064 *) _element="'$2'"; ;;
2065 esac;
2066 if string_contains "${_list}" "${_element}"; then
2067 eval "${return_no}";
2068 else
2069 eval "${return_yes}";
2071 eval "${return_ok}";
2075 ########################################################################
2076 landmark '7: man_*()';
2077 ########################################################################
2079 ########################################################################
2080 # man_do_filespec (<filespec>)
2082 # Print suitable man page(s) for filespec to $_TMP_CAT.
2084 # Arguments : 2
2085 # <filespec>: argument of the form `man:name.section', `man:name',
2086 # `man:name(section)', `name.section', `name'.
2088 # Globals : $_OPT_ALL
2090 # Output : none.
2091 # Return : `0' if man page was found, `1' else.
2093 # Only called from do_fileargs(), checks on $MANPATH and
2094 # $_MAN_ENABLE are assumed.
2096 man_do_filespec()
2098 func_check man_do_filespec = 1 "$@";
2099 local _got_one;
2100 local _name;
2101 local _prevsec;
2102 local _res;
2103 local _section;
2104 local _spec;
2105 local _string;
2106 local s;
2107 if obj _MAN_PATH is_empty; then
2108 eval "${return_bad}";
2110 if is_empty "$1"; then
2111 eval "${return_bad}";
2113 _spec="$1";
2114 _name='';
2115 _section='';
2116 case "${_spec}" in
2117 */*) # not a man spec when it contains '/'
2118 eval "${return_bad}";
2120 man:?*\(?*\)) # man:name(section)
2121 _name="$(echo -n "${_spec}" \
2122 | sed -e 's/^man:\(..*\)(\(..*\))$/\1/')";
2123 _section="$(echo -n "${_spec}" \
2124 | sed -e 's/^man:\(..*\)(\(..*\))$/\2/')";
2126 man:?*.[0-9on]) # man:name.section
2127 _name="$(echo -n "${_spec}" \
2128 | sed -e 's/^man:\(..*\)\..$/\1/')";
2129 _section="$(echo -n "${_spec}" \
2130 | sed -e 's/^.*\(.\)$/\1/')";
2132 man:?*) # man:name
2133 _name="$(echo -n "${_spec}" | sed -e 's/^man://')";
2135 ?*\(?*\)) # name(section)
2136 _name="$(echo -n "${_spec}" \
2137 | sed -e 's/^\(..*\)(\(..*\))$/\1/')";
2138 _section="$(echo -n "${_spec}" \
2139 | sed -e 's/^\(..*\)(\(..*\))$/\2/')";
2141 ?*.[0-9on]) # name.section
2142 _name="$(echo -n "${_spec}" \
2143 | sed -e 's/^\(..*\)\..$/\1/')";
2144 _section="$(echo -n "${_spec}" \
2145 | sed -e 's/^.*\(.\)$/\1/')";
2148 _name="${_filespec}";
2150 esac;
2151 if obj _name is_empty; then
2152 eval "${return_bad}";
2154 _got_one='no';
2155 if obj _section is_empty; then
2156 eval set -- "${_MAN_AUTO_SEC}";
2157 for s in "$@"; do
2158 if man_search_section "${_name}" "$s"; then # found
2159 if obj _MAN_ALL is_yes; then
2160 _got_one='yes';
2161 else
2162 eval "${return_good}";
2165 done;
2166 else
2167 if man_search_section "${_name}" "${_section}"; then
2168 eval "${return_good}";
2169 else
2170 eval "${return_bad}";
2173 if obj _MAN_ALL is_yes && is_yes "${_got_one}"; then
2174 eval "${return_good}";
2176 eval "${return_bad}";
2177 } # man_do_filespec()
2180 ########################################################################
2181 # man_register_file (<file> <name> [<section>])
2183 # Write a found man page file and register the title element.
2185 # Arguments: 1, 2, or 3; maybe empty
2186 # Output: none
2188 man_register_file()
2190 func_check man_register_file '>=' 2 "$@";
2191 case "$#" in
2192 2|3) do_nothing; ;;
2194 error "man_register_file() expects 2 or 3 arguments.";
2196 esac;
2197 if is_empty "$1"; then
2198 error 'man_register_file(): file name is empty';
2200 to_tmp "$1";
2201 case "$#" in
2203 register_title "man:$2";
2204 eval "${return_ok}";
2207 register_title "$2.$3";
2208 eval "${return_ok}";
2210 esac;
2211 eval "${return_ok}";
2215 ########################################################################
2216 # man_search_section (<name> <section>)
2218 # Retrieve man pages.
2220 # Arguments : 2
2221 # Globals : $_MAN_PATH, $_MAN_EXT
2222 # Return : 0 if found, 1 otherwise
2224 man_search_section()
2226 func_check man_search_section = 2 "$@";
2227 local _dir;
2228 local _ext;
2229 local _got_one;
2230 local _name;
2231 local _prefix
2232 local _section;
2233 local d;
2234 local f;
2235 if obj _MAN_PATH is_empty; then
2236 eval "${return_bad}";
2238 if is_empty "$1"; then
2239 eval "${return_bad}";
2241 if is_empty "$2"; then
2242 eval "${return_bad}";
2244 _name="$1";
2245 _section="$2";
2246 eval set -- "$(path_split "${_MAN_PATH}")";
2247 _got_one='no';
2248 if obj _MAN_EXT is_empty; then
2249 for d in "$@"; do
2250 _dir="$(dirname_append "$d" "man${_section}")";
2251 if obj _dir is_dir; then
2252 _prefix="$(dirname_append "${_dir}" "${_name}.${_section}")";
2253 for f in $(echo -n ${_prefix}*); do
2254 if obj f is_file; then
2255 if is_yes "${_got_one}"; then
2256 register_file "$f";
2257 elif obj _MAN_ALL is_yes; then
2258 man_register_file "$f" "${_name}";
2259 else
2260 man_register_file "$f" "${_name}" "${_section}";
2261 eval "${return_good}";
2263 _got_one='yes';
2265 done;
2267 done;
2268 else
2269 _ext="${_MAN_EXT}";
2270 # check for directory name having trailing extension
2271 for d in "$@"; do
2272 _dir="$(dirname_append $d man${_section}${_ext})";
2273 if obj _dir is_dir; then
2274 _prefix="$(dirname_append "${_dir}" "${_name}.${_section}")";
2275 for f in ${_prefix}*; do
2276 if obj f is_file; then
2277 if is_yes "${_got_one}"; then
2278 register_file "$f";
2279 elif obj _MAN_ALL is_yes; then
2280 man_register_file "$f" "${_name}";
2281 else
2282 man_register_file "$f" "${_name}" "${_section}";
2283 eval "${return_good}";
2285 _got_one='yes';
2287 done;
2289 done;
2290 # check for files with extension in directories without extension
2291 for d in "$@"; do
2292 _dir="$(dirname_append "$d" "man${_section}")";
2293 if obj _dir is_dir; then
2294 _prefix="$(dirname_append "${_dir}" \
2295 "${_name}.${_section}${_ext}")";
2296 for f in ${_prefix}*; do
2297 if obj f is_file; then
2298 if is_yes "${_got_one}"; then
2299 register_file "$f";
2300 elif obj _MAN_ALL is_yes; then
2301 man_register_file "$f" "${_name}";
2302 else
2303 man_register_file "$f" "${_name}" "${_section}";
2304 eval "${return_good}";
2306 _got_one='yes';
2308 done;
2310 done;
2312 if obj _MAN_ALL is_yes && is_yes "${_got_one}"; then
2313 eval "${return_good}";
2315 eval "${return_bad}";
2316 } # man_search_section()
2319 ########################################################################
2320 # man_setup ()
2322 # Setup the variables $_MAN_* needed for man page searching.
2324 # Globals:
2325 # in: $_OPT_*, $_MANOPT_*, $LANG, $LC_MESSAGES, $LC_ALL,
2326 # $MANPATH, $MANROFFSEQ, $MANSEC, $PAGER, $SYSTEM, $MANOPT.
2327 # out: $_MAN_PATH, $_MAN_LANG, $_MAN_SYS, $_MAN_LANG, $_MAN_LANG2,
2328 # $_MAN_SEC, $_MAN_ALL
2329 # in/out: $_MAN_ENABLE
2331 # The precedence for the variables related to `man' is that of GNU
2332 # `man', i.e.
2334 # $LANG; overridden by
2335 # $LC_MESSAGES; overridden by
2336 # $LC_ALL; this has the same precedence as
2337 # $MANPATH, $MANROFFSEQ, $MANSEC, $PAGER, $SYSTEM; overridden by
2338 # $MANOPT; overridden by
2339 # the groffer command line options.
2341 man_setup()
2343 func_check main_man_setup = 0 "$@";
2344 local _lang;
2346 if obj _MAN_IS_SETUP is_yes; then
2347 eval "${return_ok}";
2349 _MAN_IS_SETUP='yes';
2351 if obj _MAN_ENABLE is_not_yes; then
2352 eval "${return_ok}";
2355 # determine basic path for man pages
2356 _MAN_PATH="$(get_first_essential \
2357 "${_OPT_MANPATH}" "${_MANOPT_PATH}" "${MANPATH}")";
2358 if obj _MAN_PATH is_empty; then
2359 manpath_set_from_path;
2360 else
2361 _MAN_PATH="$(path_clean "${_MAN_PATH}")";
2363 if obj _MAN_PATH is_empty; then
2364 if is_prog 'manpath'; then
2365 _MAN_PATH="$(manpath 2>/dev/null)"; # not always available
2368 if obj _MAN_PATH is_empty; then
2369 _MAN_ENABLE="no";
2370 eval "${return_ok}";
2373 _MAN_ALL="$(get_first_essential "${_OPT_ALL}" "${_MANOPT_ALL}")";
2374 if obj _MAN_ALL is_empty; then
2375 _MAN_ALL='no';
2378 _MAN_SYS="$(get_first_essential \
2379 "${_OPT_SYSTEMS}" "${_MANOPT_SYS}" "${SYSTEM}")";
2380 _lang="$(get_first_essential \
2381 "${_OPT_LANG}" "${LC_ALL}" "${LC_MESSAGES}" "${LANG}")";
2382 case "${_lang}" in
2383 C|POSIX)
2384 _MAN_LANG="";
2385 _MAN_LANG2="";
2388 _MAN_LANG="${_lang}";
2389 _MAN_LANG2="";
2392 _MAN_LANG="${_lang}";
2393 # get first two characters of $_lang
2394 _MAN_LANG2="$(echo -n "${_lang}" | sed -e 's/^\(..\).*$/\1/')";
2396 esac;
2397 # from now on, use only $_LANG, forget about $_OPT_LANG, $LC_*.
2399 manpath_add_lang_sys; # this is very slow
2401 _MAN_SEC="$(get_first_essential \
2402 "${_OPT_SECT}" "${_MANOPT_SEC}" "${MANSEC}")";
2403 if obj _MAN_PATH is_empty; then
2404 _MAN_ENABLE="no";
2405 eval "${return_ok}";
2408 _MAN_EXT="$(get_first_essential \
2409 "${_OPT_EXTENSION}" "${_MANOPT_EXTENSION}")";
2410 eval "${return_ok}";
2411 } # man_setup()
2414 ########################################################################
2415 landmark '8: manpath_*()';
2416 ########################################################################
2418 ########################################################################
2419 # manpath_add_lang_sys ()
2421 # Add language and operating system specific directories to man path.
2423 # Arguments : 0
2424 # Output : none
2425 # Globals:
2426 # in: $_MAN_SYS: has the form `os1,os2,...', a comma separated
2427 # list of names of operating systems.
2428 # $_MAN_LANG and $_MAN_LANG2: each a single name
2429 # in/out: $_MAN_PATH: has the form `dir1:dir2:...', a colon
2430 # separated list of directories.
2432 manpath_add_lang_sys()
2434 func_check manpath_add_lang_sys = 0 "$@";
2435 local p;
2436 local _mp;
2437 if obj _MAN_PATH is_empty; then
2438 eval "${return_ok}";
2440 # twice test both sys and lang
2441 eval set -- "$(path_split "${_MAN_PATH}")";
2442 _mp='';
2443 for p in "$@"; do # loop on man path directories
2444 _mp="$(_manpath_add_lang_sys_single "${_mp}" "$p")";
2445 done;
2446 eval set -- "$(path_split "${_mp}")";
2447 for p in "$@"; do # loop on man path directories
2448 _mp="$(_manpath_add_lang_sys_single "${_mp}" "$p")";
2449 done;
2450 _MAN_PATH="$(path_chop "${_mp}")";
2451 eval "${return_ok}";
2455 _manpath_add_lang_sys_single()
2457 # To the directory in $1 append existing sys/lang subdirectories
2458 # Function is necessary to split the OS list.
2460 # globals: in: $_MAN_SYS, $_MAN_LANG, $_MAN_LANG2
2461 # argument: 2: `man_path' and `dir'
2462 # output: colon-separated path of the retrieved subdirectories
2464 func_check _manpath_add_lang_sys_single = 2 "$@";
2465 local d;
2466 _res="$1";
2467 _parent="$2";
2468 eval set -- "$(list_from_split "${_MAN_SYS}" ',')";
2469 for d in "$@" "${_MAN_LANG}" "${_MAN_LANG2}"; do
2470 _dir="$(dirname_append "${_parent}" "$d")";
2471 if obj _res path_not_contains "${_dir}" && obj _dir is_dir; then
2472 _res="${_res}:${_dir}";
2474 done;
2475 if path_not_contains "${_res}" "${_parent}"; then
2476 _res="${_res}:${_parent}";
2478 path_chop "${_res}";
2481 # end manpath_add_lang_sys ()
2484 ########################################################################
2485 # manpath_set_from_path ()
2487 # Determine basic search path for man pages from $PATH.
2489 # Return: `0' if a valid man path was retrieved.
2490 # Output: none
2491 # Globals:
2492 # in: $PATH
2493 # out: $_MAN_PATH
2495 manpath_set_from_path()
2497 func_check manpath_set_from_path = 0 "$@";
2498 local _base;
2499 local _mandir;
2500 local _manpath;
2501 local d;
2502 local e;
2503 _manpath='';
2505 # get a basic man path from $PATH
2506 if obj PATH is_not_empty; then
2507 eval set -- "$(path_split "${PATH}")";
2508 for d in "$@"; do
2509 # delete the final `/bin' part
2510 _base="$(echo -n "$d" | sed -e '\|.*|s|//*bin/*$||')";
2511 for e in /share/man /man; do
2512 _mandir="${_base}$e";
2513 if test -d "${_mandir}" && test -r "${_mandir}"; then
2514 _manpath="${_manpath}:${_mandir}";
2516 done;
2517 done;
2520 # append some default directories
2521 for d in /usr/local/share/man /usr/local/man \
2522 /usr/share/man /usr/man \
2523 /usr/X11R6/man /usr/openwin/man \
2524 /opt/share/man /opt/man \
2525 /opt/gnome/man /opt/kde/man; do
2526 if obj _manpath path_not_contains "$d" && obj d is_dir; then
2527 _manpath="${_manpath}:$d";
2529 done;
2531 _MAN_PATH="${_manpath}";
2532 eval "${return_ok}";
2533 } # manpath_set_from_path()
2536 ########################################################################
2537 landmark '9: obj_*()';
2538 ########################################################################
2540 ########################################################################
2541 # obj (<object> <call_name> <arg>...)
2543 # This works like a method (object function) call for an object.
2544 # Run "<call_name> $<object> <arg> ...".
2546 # The first argument represents an object whose data is given as first
2547 # argument to <call_name>().
2549 # Argument: >=2
2550 # <object>: variable name
2551 # <call_name>: a program or function name
2553 obj()
2555 func_check obj '>=' 2 "$@";
2556 local func;
2557 local var;
2558 if is_empty "$2"; then
2559 error "obj(): function name is empty."
2560 else
2561 func="$2";
2563 eval arg1='"${'$1'}"';
2564 shift;
2565 shift;
2566 eval "${func}"' "${arg1}" "$@"';
2570 ########################################################################
2571 # obj_data (<object>)
2573 # Print the data of <object>, i.e. the content of $<object>.
2574 # For possible later extensions.
2576 # Arguments: 1
2577 # <object>: a variable name
2578 # Output: the data of <object>
2580 obj_data()
2582 func_check obj '=' 1 "$@";
2583 if is_empty "$1"; then
2584 error "obj_data(): object name is empty."
2586 eval echo -n '"${'$1'}"';
2590 ########################################################################
2591 # obj_from_output (<object> <call_name> <arg>...)
2593 # Run '$<object>="$(<call_name> <arg>...)"' to set the result of a
2594 # function call to a global variable.
2596 # Arguments: >=2
2597 # <object>: a variable name
2598 # <call_name>: the name of a function or program
2599 # <arg>: optional argument to <call_name>
2600 # Output: none
2602 obj_from_output()
2604 func_check obj_from_output '>=' 2 "$@";
2605 local result_name;
2606 if is_empty "$1"; then
2607 error "res(): variable name is empty.";
2608 elif is_empty "$2"; then
2609 error "res(): function name is empty."
2610 else
2611 result_name="$1";
2613 shift;
2614 eval "${result_name}"'="$('"$@"')"';
2618 ########################################################################
2619 # obj_set (<object> <data>)
2621 # Set the data of <object>, i.e. call "$<object>=<data>".
2623 # Arguments: 2
2624 # <object>: a variable name
2625 # <data>: a string
2626 # Output:: none
2628 obj_set()
2630 func_check obj_set '=' 2 "$@";
2631 if is_empty "$1"; then
2632 error "obj_set(): object name is empty."
2634 eval "$1"='"$2"';
2638 ########################################################################
2639 # path_chop (<path>)
2641 # Remove unnecessary colons from path.
2643 # Argument: 1, a colon separated path.
2644 # Output: path without leading, double, or trailing colons.
2646 path_chop()
2648 func_check path_chop = 1 "$@";
2649 local _res;
2651 # replace multiple colons by a single colon `:'
2652 # remove leading and trailing colons
2653 echo -n "$1" | sed -e 's/:::*/:/g' |
2654 sed -e 's/^:*//' |
2655 sed -e 's/:*$//';
2656 eval "${return_ok}";
2660 ########################################################################
2661 # path_clean (<path>)
2663 # Remove non-existing directories from a colon-separated list.
2665 # Argument: 1, a colon separated path.
2666 # Output: colon-separated list of existing directories.
2668 path_clean()
2670 func_check path_clean = 1 "$@";
2671 local _arg;
2672 local _dir;
2673 local _res;
2674 local i;
2675 if is_not_equal "$#" 1; then
2676 error 'path_clean() needs 1 argument.';
2678 _arg="$1";
2679 eval set -- "$(path_split "${_arg}")";
2680 _res="";
2681 for i in "$@"; do
2682 if obj i is_not_empty \
2683 && obj _res path_not_contains "$i" \
2684 && obj i is_dir;
2685 then
2686 case "$i" in
2687 ?*/) _res="${_res}$(dirname_chop "$i")"; ;;
2688 *) _res="${_res}:$i";
2689 esac;
2691 done;
2692 if path_chop "${_res}"; then
2693 eval "${return_ok}";
2694 else
2695 eval "${return_badk}";
2700 ########################################################################
2701 # path_contains (<path> <dir>)
2703 # Test whether `dir' is contained in `path', a list separated by `:'.
2705 # Arguments : 2 arguments.
2706 # Return : `0' if arg2 is substring of arg1, `1' otherwise.
2708 path_contains()
2710 func_check path_contains = 2 "$@";
2711 case ":$1:" in
2712 *":$2:"*)
2713 eval "${return_yes}";
2716 eval "${return_no}";
2718 esac;
2719 eval "${return_ok}";
2723 ########################################################################
2724 # path_not_contains (<path> <dir>)
2726 # Test whether `dir' is not contained in colon separated `path'.
2728 # Arguments : 2 arguments.
2730 path_not_contains()
2732 func_check path_not_contains = 2 "$@";
2733 if path_contains "$1" "$2"; then
2734 eval "${return_no}";
2735 else
2736 eval "${return_yes}";
2738 eval "${return_ok}";
2742 ########################################################################
2743 # path_split (<path>)
2745 # In `path' escape white space and replace each colon by a space.
2747 # Arguments: 1: a colon-separated path
2748 # Output: the resulting list, process with `eval set --'
2750 path_split()
2752 func_check path_split = 1 "$@";
2753 list_from_split "$1" ':';
2754 eval "${return_ok}";
2758 ########################################################################
2759 landmark '10: register_*()';
2760 ########################################################################
2762 ########################################################################
2763 # register_file (<filename>)
2765 # Write a found file and register the title element.
2767 # Arguments: 1: a file name
2768 # Output: none
2770 register_file()
2772 func_check register_file = 1 "$@";
2773 if is_empty "$1"; then
2774 error 'register_file(): file name is empty';
2776 if is_equal "$1" '-'; then
2777 to_tmp "${_TMP_STDIN}";
2778 register_title '-';
2779 else
2780 to_tmp "$1";
2781 register_title "$(base_name "$1")";
2783 eval "${return_ok}";
2787 ########################################################################
2788 # register_title (<filespec>)
2790 # Create title element from <filespec> and append to $_REGISTERED_TITLE
2792 # Globals: $_REGISTERED_TITLE (rw)
2794 register_title()
2796 func_check register_title = 1 "$@";
2797 local _title;
2798 if is_empty "$1"; then
2799 eval "${return_ok}";
2801 _title="$(base_name "$1")"; # remove directory part
2803 # remove extension `.gz'
2804 _title="$(echo -n "${_title}" | sed -e 's/\.gz$//')";
2805 # remove extension `.Z'
2806 _title="$(echo -n "${_title}" | sed -e 's/\.Z$//')";
2808 if obj _title is_empty; then
2809 eval "${return_ok}";
2811 _REGISTERED_TITLE="${_REGISTERED_TITLE} ${_title}";
2812 eval "${return_ok}";
2816 ########################################################################
2817 # reset ()
2819 # Reset the variables that can be affected by options to their default.
2822 # Defined in section `Preset' after the rudimentary shell tests.
2825 ########################################################################
2826 # save_stdin ()
2828 # Store standard input to temporary file (with decompression).
2830 if obj _HAS_COMPRESSION is_yes; then
2831 save_stdin()
2833 local _f;
2834 func_check save_stdin = 0 "$@";
2835 _f="${_TMP_DIR}"/INPUT;
2836 cat >"${_f}";
2837 catz "${_f}" >"${_TMP_STDIN}";
2838 rm -f "${_f}";
2839 eval "${return_ok}";
2841 else
2842 save_stdin()
2844 func_check save_stdin = 0 "$@";
2845 cat >"${_TMP_STDIN}";
2846 eval "${return_ok}";
2851 ########################################################################
2852 landmark '11: stack_*()';
2853 ########################################################################
2855 ########################################################################
2856 # string_contains (<string> <part>)
2858 # Test whether `part' is contained in `string'.
2860 # Arguments : 2 text arguments.
2861 # Return : `0' if arg2 is substring of arg1, `1' otherwise.
2863 string_contains()
2865 func_check string_contains = 2 "$@";
2866 case "$1" in
2867 *"$2"*)
2868 eval "${return_yes}";
2871 eval "${return_no}";
2873 esac;
2874 eval "${return_ok}";
2878 ########################################################################
2879 # string_not_contains (<string> <part>)
2881 # Test whether `part' is not substring of `string'.
2883 # Arguments : 2 text arguments.
2884 # Return : `0' if arg2 is substring of arg1, `1' otherwise.
2886 string_not_contains()
2888 func_check string_not_contains = 2 "$@";
2889 if string_contains "$1" "$2"; then
2890 eval "${return_no}";
2891 else
2892 eval "${return_yes}";
2894 eval "${return_ok}";
2898 ########################################################################
2899 landmark '12: tmp_*()';
2900 ########################################################################
2902 ########################################################################
2903 # tmp_cat ()
2905 # output the temporary cat file (the concatenation of all input)
2907 tmp_cat()
2909 cat "${_TMP_CAT}";
2913 ########################################################################
2914 # tmp_create (<suffix>?)
2916 # create temporary file
2918 # It's safe to use the shell process ID together with a suffix to
2919 # have multiple temporary files.
2921 # Output : name of created file
2923 tmp_create()
2925 func_check tmp_create '<=' 1 "$@";
2926 local _tmp;
2927 # the output file does not have `,' as first character
2928 _tmp="${_TMP_DIR}/,$1";
2929 echo -n >"${_tmp}";
2930 echo -n "${_tmp}"; # output file name
2931 eval "${return_ok}";
2935 ########################################################################
2936 # to_tmp (<filename>)
2938 # print file (decompressed) to the temporary cat file
2940 to_tmp()
2942 func_check to_tmp = 1 "$@";
2943 if is_file "$1"; then
2944 if obj _OPT_LOCATION is_yes; then
2945 echo2 "$1";
2947 if obj _OPT_WHATIS is_yes; then
2948 what_is "$1" >>"${_TMP_CAT}";
2949 else
2950 catz "$1" >>"${_TMP_CAT}";
2952 else
2953 error "to_tmp(): could not read file \`$1'.";
2955 eval "${return_ok}";
2959 ########################################################################
2960 # trap_clean ()
2962 # disable trap on all exit codes ($_ALL_EXIT)
2964 # Arguments: 0
2965 # Globals: $_ALL_EXIT
2967 trap_clean()
2969 func_check trap_clean = 0 "$@";
2970 local i;
2971 for i in ${_ALL_EXIT}; do
2972 trap "" "$i" 2>/dev/null || true;
2973 done;
2974 eval "${return_ok}";
2978 ########################################################################
2979 # trap_set (<functionname>)
2981 # call function on all exit codes ($_ALL_EXIT)
2983 # Arguments: 1 (name of a shell function)
2984 # Globals: $_ALL_EXIT
2986 trap_set()
2988 func_check trap_set = 1 "$@";
2989 local i;
2990 for i in ${_ALL_EXIT}; do
2991 trap "$1" "$i" 2>/dev/null || true;
2992 done;
2993 eval "${return_ok}";
2997 ########################################################################
2998 # usage ()
3000 # print usage information to stderr; for groffer option --help.
3002 usage()
3004 func_check usage = 0 "$@";
3005 echo;
3006 version;
3007 echo 'Usage: '"${_PROGRAM_NAME}"' [option]... [filespec]...';
3008 cat <<EOF
3010 Display roff files, standard input, and/or Unix manual pages with a X
3011 Window viewer or in several text modes. All input is decompressed
3012 on-the-fly with all formats that gzip can handle.
3014 "filespec" is one of
3015 "filename" name of a readable file
3016 "-" for standard input
3017 "man:name.n" man page "name" in section "n"
3018 "man:name" man page "name" in first section found
3019 "name.n" man page "name" in section "n"
3020 "name" man page "name" in first section found
3021 and some more (see groffer(1) for details).
3023 -h --help print this usage message.
3024 -Q --source output as roff source.
3025 -T --device=name pass to groff using output device "name".
3026 -v --version print version information.
3027 -V display the groff execution pipe instead of formatting.
3028 -X --X --x display with "gxditview" using groff -X.
3029 -Z --ditroff --intermediate-output
3030 generate groff intermediate output without
3031 post-processing and viewing, like groff -Z.
3032 All other short options are interpreted as "groff" formatting options.
3034 The most important groffer long options are
3036 --apropos=name start man's "apropos" program for "name".
3037 --apropos-data=name
3038 "apropos" for "name" in man's data sections 4, 5, 7.
3039 --apropos-devel=name
3040 "apropos" for "name" in development sections 2, 3, 9.
3041 --apropos-progs=name
3042 "apropos" for "name" in man's program sections 1, 6, 8.
3043 --auto choose mode automatically from the default mode list.
3044 --default reset all options to the default value.
3045 --default-modes=mode1,mode2,...
3046 set sequence of automatically tried modes.
3047 --dvi display in a viewer for TeX device independent format.
3048 --dvi-viewer choose the viewer program for dvi mode.
3049 --groff process like groff, disable viewing features.
3050 --help display this helping output.
3051 --html --www display in a web browser.
3052 --html-viewer choose the web browser for www mode.
3053 --man check file parameters first whether they are man pages.
3054 --mode=auto|dvi|groff|html|pdf|ps|source|text|tty|www|x|X
3055 choose display mode.
3056 --no-man disable man-page facility.
3057 --pager=program preset the paging program for tty mode.
3058 --pdf display in a PDF viewer.
3059 --pdf-viewer choose the viewer program for pdf mode.
3060 --ps display in a Postscript viewer.
3061 --ps-viewer choose the viewer program for ps mode.
3062 --shell specify shell under which to run this program.
3063 --text output in a text device without a pager.
3064 --tty display with a pager on text terminal even when in X.
3065 --www-viewer same as --html-viewer
3066 --x-viewer choose viewer program for x mode (X mode).
3067 --X-viewer same as "--xviewer".
3069 The usual X Windows toolkit options transformed into GNU long options
3070 --background=color, --bd=size, --bg=color, --bordercolor=color,
3071 --borderwidth=size, --bw=size, --display=Xdisplay, --fg=color,
3072 --fn=font, --font=font, --foreground=color, --geometry=geom, --iconic,
3073 --resolution=dpi, --rv, --title=text, --xrm=resource
3075 Long options of GNU "man"
3076 --all, --ascii, --ditroff, --extension=suffix, --locale=language,
3077 --local-file=name, --location, --manpath=dir1:dir2:...,
3078 --sections=s1:s2:..., --systems=s1,s2,..., --whatis, --where, ...
3081 eval "${return_ok}";
3085 ########################################################################
3086 # version ()
3088 # print version information to stderr
3090 version()
3092 func_check version = 0 "$@";
3093 echo2 "${_PROGRAM_NAME} ${_PROGRAM_VERSION} of ${_LAST_UPDATE}";
3094 # also display groff's version, but not the called subprograms
3095 groff -v 2>&1 | sed -e '/^ *$/q' | sed -e '1s/^/is part of /' >&2;
3099 ########################################################################
3100 # warning (<string>)
3102 # Print warning to stderr
3104 warning()
3106 echo2 "warning: $*";
3110 ########################################################################
3111 # what_is (<filename>)
3113 # Interpret <filename> as a man page and display its `whatis'
3114 # information as a fragment written in the groff language.
3116 what_is()
3118 func_check what_is = 1 "$@";
3119 local _res;
3120 local _dot;
3121 if is_not_file "$1"; then
3122 error "what_is(): argument is not a readable file."
3124 _dot='^\.['"${_SPACE}${_TAB}"']*';
3125 echo '.br';
3126 echo "$1: ";
3127 echo '.br';
3128 echo -n ' ';
3129 # grep the line containing `.TH' macro, if any
3130 _res="$(catz "$1" | sed -e '/'"${_dot}"'TH /p
3131 d')";
3132 if obj _res is_not_empty; then # traditional man style
3133 # get the text between the first and the second `.SH' macro, by
3134 # - delete up to first .SH;
3135 # - of this, print everything up to next .SH, and delete the rest;
3136 # - of this, delete the final .SH line;
3137 catz "$1" | sed -e '1,/'"${_dot}"'SH/d' \
3138 | sed -e '1,/'"${_dot}"'SH/p
3139 d' \
3140 | sed -e '/'"${_dot}"'SH/d';
3141 eval "${return_ok}";
3143 # grep the line containing `.Dd' macro, if any
3144 _res="$(catz "$1" | sed -e '/'"${_dot}"'Dd /p
3145 d')";
3146 if obj _res is_not_empty; then # BSD doc style
3147 # get the text between the first and the second `.Nd' macro, by
3148 # - delete up to first .Nd;
3149 # - of this, print everything up to next .Nd, and delete the rest;
3150 # - of this, delete the final .Nd line;
3151 catz "$1" | sed -e '1,/'"${_dot}"'Nd/d' \
3152 | sed -e '1,/'"${_dot}"'Nd/p
3153 d' \
3154 | sed -e '/'"${_dot}"'Nd/d';
3155 eval "${return_ok}";
3157 echo 'is not a man page.';
3158 eval "${return_bad}";
3162 ########################################################################
3163 # where (<program>)
3165 # Output path of a program if in $PATH.
3167 # Arguments : >=1 (empty allowed)
3168 # more args are ignored, this allows to specify progs with arguments
3169 # Return : `0' if arg1 is a program in $PATH, `1' otherwise.
3171 where()
3173 func_check where '>=' 1 "$@";
3174 local _file;
3175 local _arg;
3176 local p;
3177 _arg="$1";
3178 if obj _arg is_empty; then
3179 eval "${return_bad}";
3181 case "${_arg}" in
3183 if test -f "${_arg}" && test -x "${_arg}"; then
3184 eval "${return_ok}";
3185 else
3186 eval "${return_bad}";
3189 esac;
3190 eval set -- "$(path_split "${PATH}")";
3191 for p in "$@"; do
3192 case "$p" in
3193 */) _file=${p}${_arg}; ;;
3194 *) _file=${p}/${_arg}; ;;
3195 esac;
3196 if test -f "${_file}" && test -x "${_file}"; then
3197 echo -n "${_file}";
3198 eval "${return_ok}";
3200 done;
3201 eval "${return_bad}";
3205 ########################################################################
3206 # main
3207 ########################################################################
3209 # The main area contains the following parts:
3210 # - main_init(): initialize temporary files and set exit trap
3211 # - parse $MANOPT
3212 # - main_parse_args(): argument parsing
3213 # - determine display mode
3214 # - process filespec arguments
3215 # - setup X resources
3216 # - do the displaying
3218 # These parts are implemented as functions, being defined below in the
3219 # sequence they are called in the main() function.
3222 #######################################################################
3223 # main_init ()
3225 # set exit trap and create temporary files
3227 # Globals: $_TMP_DIR, $_TMP_CAT, $_TMP_STDIN
3229 landmark '13: main_init()';
3230 main_init()
3232 func_check main_init = 0 "$@";
3233 # call clean_up() on any signal
3234 trap_set clean_up;
3236 # create temporary directory
3237 umask 0022;
3238 _TMP_DIR='';
3239 for d in "${GROFF_TMPDIR}" "${TMPDIR}" "${TMP}" "${TEMP}" \
3240 "${TEMPDIR}" "${HOME}"'/tmp' '/tmp' "${HOME}" '.';
3242 if obj d is_empty || obj d is_not_dir || obj d is_not_writable; then
3243 continue;
3245 case "$d" in
3247 _TMP_DIR="${d}";
3250 _TMP_DIR="${d}"'/';
3252 esac;
3253 _TMP_DIR="${_TMP_DIR}${_PROGRAM_NAME}${_PROCESS_ID}";
3254 while obj _TMP_DIR is_existing; do
3255 rm -f -r "${_TMP_DIR}" 2>/dev/null;
3256 if obj _TMP_DIR is_existing; then
3257 # $_TMP_DIR could not be removed
3258 _TMP_DIR="${_TMP_DIR}"'X';
3259 continue;
3260 else
3261 # $_TMP_DIR was removed
3262 break;
3264 done;
3265 mkdir "${_TMP_DIR}";
3266 if is_not_equal "$?" 0; then
3267 if obj _TMP_DIR is_existing; then
3268 rm -f -r "${_TMP_DIR}" 2>/dev/null;
3270 _TMP_DIR='';
3271 continue;
3273 if obj _TMP_DIR is_dir && obj _TMP_DIR is_writable; then
3274 # $_TMP_DIR can now be used as temporary directory
3275 break;
3277 if obj _TMP_DIR is_existing; then
3278 rm -f -r "${_TMP_DIR}" 2>/dev/null;
3280 _TMP_DIR='';
3281 continue;
3282 done;
3283 unset d;
3284 if obj _TMP_DIR is_empty; then
3285 error "Couldn't create a directory for storing temporary files.";
3288 _TMP_CAT="$(tmp_create groffer_cat)";
3289 _TMP_STDIN="$(tmp_create groffer_input)";
3291 # groffer configuration files
3292 for f in ${_CONFFILES}; do
3293 if obj f is_file; then
3294 echo '_groffer_opt=""' >>${_TMP_CAT};
3295 # collect the lines starting with a minus
3296 cat "$f" | sed -e \
3297 's/^[ ]*\(-.*\)$/_groffer_opt="${_groffer_opt} \1"'/ \
3298 >>${_TMP_CAT};
3299 # prepend the collected information to $GROFFER_OPT
3300 echo 'GROFFER_OPT="${_groffer_opt} ${GROFFER_OPT}"' >>${_TMP_CAT};
3302 done;
3303 . "${_TMP_CAT}";
3304 _TMP_CAT="$(tmp_create groffer_cat)";
3306 eval "${return_ok}";
3307 } # main_init()
3310 ########################################################################
3311 # main_parse_MANOPT ()
3313 # Parse $MANOPT to retrieve man options, but only if it is a non-empty
3314 # string; found man arguments can be overwritten by the command line.
3316 # Globals:
3317 # in: $MANOPT, $_OPTS_MANOPT_*
3318 # out: $_MANOPT_*
3319 # in/out: $GROFFER_OPT
3321 landmark '14: main_parse_MANOPT()';
3322 main_parse_MANOPT()
3324 func_check main_parse_MANOPT = 0 "$@";
3325 local _opt;
3326 local _list;
3327 _list='';
3328 if obj MANOPT is_not_empty; then
3329 MANOPT="$(echo -n "${MANOPT}" | \
3330 sed -e 's/^'"${_SPACE}${_SPACE}"'*//')";
3332 if obj MANOPT is_empty; then
3333 eval "${return_ok}";
3335 # add arguments in $MANOPT by mapping them to groffer options
3336 eval set -- "$(list_from_cmdline _OPTS_MANOPT "${MANOPT}")";
3337 until test "$#" -le 0 || is_equal "$1" '--'; do
3338 _opt="$1";
3339 shift;
3340 case "${_opt}" in
3341 -7|--ascii)
3342 list_append _list '--ascii';
3344 -a|--all)
3345 list_append _list '--all';
3347 -c|--catman)
3348 do_nothing;
3349 shift;
3351 -d|--debug)
3352 list_append _list '--debug';
3354 -D|--default)
3355 # undo all man options so far
3356 _list='';
3358 -e|--extension)
3359 list_append _list '--extension';
3360 shift;
3362 -f|--whatis)
3363 list_append _list '--whatis';
3364 shift;
3366 -h|--help)
3367 do_nothing;
3368 shift;
3370 -k|--apropos)
3371 # groffer's --apropos takes an argument, but man's does not, so
3372 do_nothing;
3373 shift;
3375 -l|--local-file)
3376 list_append _list '--local-file';
3378 -L|--locale)
3379 list_append _list '--locale' "$1";
3380 shift;
3382 -m|--systems)
3383 list_append _list '--systems' "$1";
3384 shift;
3386 -M|--manpath)
3387 list_append _list '--manpath' "$1";
3388 shift;
3390 -p|--preprocessor)
3391 do_nothing;
3392 shift;
3394 -P|--pager|--tty-viewer)
3395 list_append _list '--pager' "$1";
3396 shift;
3398 -r|--prompt)
3399 do_nothing;
3400 shift;
3402 -S|--sections)
3403 list_append _list '--sections' "$1";
3404 shift;
3406 -t|--troff)
3407 do_nothing;
3408 shift;
3410 -T|--device)
3411 list_append _list '-T' "$1";
3412 shift;
3414 -u|--update)
3415 do_nothing;
3416 shift;
3418 -V|--version)
3419 do_nothing;
3421 -w|--where|--location)
3422 list_append _list '--location';
3424 -Z|--ditroff)
3425 list_append _list '-Z' "$1";
3426 shift;
3428 # ignore all other options
3429 esac;
3430 done;
3431 # append the 2 lists in $_list and $GROFFER_OPT to $GROFFER_OPT
3432 if obj GROFFER_OPT is_empty; then
3433 GROFFER_OPT="${_list}";
3434 elif obj _list is_not_empty; then
3435 GROFFER_OPT="${_list} ${GROFFER_OPT}";
3437 eval "${return_ok}";
3438 } # main_parse_MANOPT()
3441 ########################################################################
3442 # main_parse_args (<command_line_args>*)
3444 # Parse arguments; process options and filespec parameters
3446 # Arguments: pass the command line arguments unaltered.
3447 # Globals:
3448 # in: $_OPTS_*
3449 # out: $_OPT_*, $_ADDOPTS, $_FILEARGS
3451 landmark '15: main_parse_args()';
3452 main_parse_args()
3454 func_check main_parse_args '>=' 0 "$@";
3455 local _arg;
3456 local _code;
3457 local _dpi;
3458 local _longopt;
3459 local _mode;
3460 local _opt;
3461 local _optchar;
3462 local _optarg;
3463 local _opts;
3464 local _string;
3466 eval set -- "${GROFFER_OPT}" '"$@"';
3468 eval set -- "$(list_from_cmdline _OPTS_CMDLINE "$@")";
3470 # By the call of `eval', unnecessary quoting was removed. So the
3471 # positional shell parameters ($1, $2, ...) are now guaranteed to
3472 # represent an option or an argument to the previous option, if any;
3473 # then a `--' argument for separating options and
3474 # parameters; followed by the filespec parameters if any.
3476 # Note, the existence of arguments to options has already been checked.
3477 # So a check for `$#' or `--' should not be done for arguments.
3479 until test "$#" -le 0 || is_equal "$1" '--'; do
3480 _opt="$1"; # $_opt is fed into the option handler
3481 shift;
3482 case "${_opt}" in
3483 -h|--help)
3484 usage;
3485 leave;
3487 -Q|--source) # output source code (`Quellcode').
3488 _OPT_MODE='source';
3490 -T|--device|--troff-device) # device; arg
3491 _OPT_DEVICE="$1";
3492 _check_device_with_mode;
3493 shift;
3495 -v|--version)
3496 version;
3497 leave;
3500 _OPT_V='yes';
3502 -Z|--ditroff|--intermediate-output) # groff intermediate output
3503 _OPT_Z='yes';
3505 -X|--X|--x)
3506 _OPT_MODE=x;
3509 # delete leading `-'
3510 _optchar="$(echo -n "${_opt}" | sed -e 's/^.//')";
3511 if list_has _OPTS_GROFF_SHORT_NA "${_optchar}";
3512 then
3513 list_append _ADDOPTS_GROFF "${_opt}";
3514 elif list_has _OPTS_GROFF_SHORT_ARG "${_optchar}";
3515 then
3516 list_append _ADDOPTS_GROFF "${_opt}" "$1";
3517 shift;
3518 else
3519 error "Unknown option : \`$1'";
3522 --all)
3523 _OPT_ALL="yes";
3525 --apropos) # run `apropos'
3526 apropos_run "$1";
3527 _code="$?";
3528 clean_up;
3529 exit "${_code}";
3531 --apropos-data) # run `apropos' for data sections
3532 apropos_run "$1" | grep '^[^(]*([457][^)]*)';
3533 _code="$?";
3534 clean_up;
3535 exit "${_code}";
3537 --apropos-devel) # run `apropos' for development sections
3538 apropos_run "$1" | grep '^[^(]*([239][^)]*)';
3539 _code="$?";
3540 clean_up;
3541 exit "${_code}";
3543 --apropos-progs) # run `apropos' for program sections
3544 apropos_run "$1" | grep '^[^(]*([168][^)]*)';
3545 _code="$?";
3546 clean_up;
3547 exit "${_code}";
3549 --ascii)
3550 list_append _ADDOPTS_GROFF '-mtty-char';
3551 if obj _mode is_empty; then
3552 _mode='text';
3555 --auto) # the default automatic mode
3556 _mode='';
3558 --bd) # border color for viewers, arg;
3559 _OPT_BD="$1";
3560 shift;
3562 --bg|--backgroud) # background color for viewers, arg;
3563 _OPT_BG="$1";
3564 shift;
3566 --bw) # border width for viewers, arg;
3567 _OPT_BW="$1";
3568 shift;
3570 --default) # reset variables to default
3571 reset;
3573 --default-modes) # sequence of modes in auto mode; arg
3574 _OPT_DEFAULT_MODES="$1";
3575 shift;
3577 --debug) # buggy, only for development
3578 _OPT_DEBUG='yes';
3580 --display) # set X display, arg
3581 _OPT_DISPLAY="$1";
3582 shift;
3584 --dvi)
3585 _OPT_MODE='dvi';
3587 --dvi-viewer) # viewer program for dvi mode; arg
3588 _OPT_VIEWER_DVI="$1";
3589 shift;
3591 --extension) # the extension for man pages, arg
3592 _OPT_EXTENSION="$1";
3593 shift;
3595 --fg|--foreground) # foreground color for viewers, arg;
3596 _OPT_FG="$1";
3597 shift;
3599 --fn|--font) # set font for viewers, arg;
3600 _OPT_FN="$1";
3601 shift;
3603 --geometry) # window geometry for viewers, arg;
3604 _OPT_GEOMETRY="$1";
3605 shift;
3607 --groff)
3608 _OPT_MODE='groff';
3610 --html|--www) # display with web browser
3611 _OPT_MODE=html;
3613 --html-viewer|--www-viewer) # viewer program for html mode; arg
3614 _OPT_VIEWER_HTML="$1";
3615 shift;
3617 --iconic) # start viewers as icons
3618 _OPT_ICONIC='yes';
3620 --locale) # set language for man pages, arg
3621 # argument is xx[_territory[.codeset[@modifier]]] (ISO 639,...)
3622 _OPT_LANG="$1";
3623 shift;
3625 --local-file) # force local files; same as `--no-man'
3626 _MAN_FORCE='no';
3627 _MAN_ENABLE='no';
3629 --location|--where) # print file locations to stderr
3630 _OPT_LOCATION='yes';
3632 --man) # force all file params to be man pages
3633 _MAN_ENABLE='yes';
3634 _MAN_FORCE='yes';
3636 --manpath) # specify search path for man pages, arg
3637 # arg is colon-separated list of directories
3638 _OPT_MANPATH="$1";
3639 shift;
3641 --mode) # display mode
3642 _arg="$1";
3643 shift;
3644 case "${_arg}" in
3645 auto|'') # search mode automatically among default
3646 _mode='';
3648 groff) # pass input to plain groff
3649 _mode='groff';
3651 html|www) # display with a web browser
3652 _mode='html';
3654 dvi) # display with xdvi viewer
3655 _mode='dvi';
3657 pdf) # display with PDF viewer
3658 _mode='pdf';
3660 ps) # display with Postscript viewer
3661 _mode='ps';
3663 text) # output on terminal
3664 _mode='text';
3666 tty) # output on terminal
3667 _mode='tty';
3669 X|x) # output on X roff viewer
3670 _mode='x';
3672 Q|source) # display source code
3673 _mode="source";
3676 error "unknown mode ${_arg}";
3678 esac;
3679 _OPT_MODE="${_mode}";
3681 --no-location) # disable former call to `--location'
3682 _OPT_LOCATION='yes';
3684 --no-man) # disable search for man pages
3685 # the same as --local-file
3686 _MAN_FORCE="no";
3687 _MAN_ENABLE="no";
3689 --pager) # set paging program for tty mode, arg
3690 _OPT_PAGER="$1";
3691 shift;
3693 --pdf)
3694 _OPT_MODE='pdf';
3696 --pdf-viewer) # viewer program for ps mode; arg
3697 _OPT_VIEWER_PDF="$1";
3698 shift;
3700 --ps)
3701 _OPT_MODE='ps';
3703 --ps-viewer) # viewer program for ps mode; arg
3704 _OPT_VIEWER_PS="$1";
3705 shift;
3707 --resolution) # set resolution for X devices, arg
3708 _arg="$1";
3709 shift;
3710 case "${_arg}" in
3711 75|75dpi)
3712 _dpi=75;
3714 100|100dpi)
3715 _dpi=100;
3718 error "only resoutions of 75 or 100 dpi are supported";
3720 esac;
3721 _OPT_RESOLUTION="${_dpi}";
3723 --rv)
3724 _OPT_RV='yes';
3726 --sections) # specify sections for man pages, arg
3727 # arg is colon-separated list of section names
3728 _OPT_SECTIONS="$1";
3729 shift;
3731 --shell)
3732 # already done during the first run; so ignore the argument
3733 shift;
3735 --systems) # man pages for different OS's, arg
3736 # argument is a comma-separated list
3737 _OPT_SYSTEMS="$1";
3738 shift;
3740 --text) # text mode without pager
3741 _OPT_MODE=text;
3743 --title) # title for X viewers; arg
3744 _OPT_TITLE="$1";
3745 shift;
3747 --tty) # tty mode, text with pager
3748 _OPT_MODE=tty;
3750 --text-device|--tty-device) # device for tty mode; arg
3751 _OPT_TEXT_DEVICE="$1";
3752 shift;
3754 --whatis)
3755 _OPT_WHATIS='yes';
3757 --xrm) # pass X resource string, arg;
3758 list_append _OPT_XRM "$1";
3759 shift;
3761 --x-viewer|--X-viewer) # viewer program for x mode; arg
3762 _OPT_VIEWER_X="$1";
3763 shift;
3766 error 'error on argument parsing : '"\`$*'";
3768 esac;
3769 done;
3770 shift; # remove `--' argument
3772 if obj _DEBUG is_not_yes; then
3773 if obj _OPT_DEBUG is_yes; then
3774 _DEBUG='yes';
3778 # Remaining arguments are file names (filespecs).
3779 # Save them to list $_FILEARGS
3780 if is_equal "$#" 0; then # use "-" for standard input
3781 set -- '-';
3783 _FILEARGS='';
3784 list_append _FILEARGS "$@";
3785 if list_has _FILEARGS '-'; then
3786 save_stdin;
3788 # $_FILEARGS must be retrieved with `eval set -- "$_FILEARGS"'
3789 eval "${return_ok}";
3790 } # main_parse_args()
3792 # Called from main_parse_args() because double `case' is not possible.
3793 # Globals: $_OPT_DEVICE, $_OPT_MODE
3794 _check_device_with_mode()
3796 func_check _check_device_with_mode = 0 "$@";
3797 case "${_OPT_DEVICE}" in
3798 dvi)
3799 _OPT_MODE=dvi;
3800 eval "${return_ok}";
3802 html)
3803 _OPT_MODE=html;
3804 eval "${return_ok}";
3806 lbp|lj4)
3807 _OPT_MODE=groff;
3808 eval "${return_ok}";
3811 _OPT_MODE=ps;
3812 eval "${return_ok}";
3814 ascii|cp1047|latin1|utf8)
3815 if obj _OPT_MODE is_not_equal text; then
3816 _OPT_MODE=tty; # default text mode
3818 eval "${return_ok}";
3821 _OPT_MODE=x;
3822 eval "${return_ok}";
3824 *) # unknown device, go to groff mode
3825 _OPT_MODE=groff;
3826 eval "${return_ok}";
3828 esac;
3829 eval "${return_error}";
3833 ########################################################################
3834 # main_set_mode ()
3836 # Determine the display mode.
3838 # Globals:
3839 # in: $DISPLAY, $_OPT_MODE, $_OPT_DEVICE
3840 # out: $_DISPLAY_MODE
3843 # _get_first_prog (<proglist>)
3845 # Retrieve first argument that represents an existing program in $PATH.
3846 # Local function for main_set_mode().
3848 # Arguments: 1; a comma-separated list of commands (with options),
3849 # like $_VIEWER_*.
3851 # Return : `1' if none found, `0' if found.
3852 # Output : the argument that succeded.
3854 landmark '16: main_set_mode()';
3855 main_set_mode()
3857 func_check main_set_mode = 0 "$@";
3858 local m;
3859 local _modes;
3860 local _viewer;
3861 local _viewers;
3863 # handle apropos
3864 if obj _OPT_APROPOS is_not_empty; then
3865 apropos "${_OPT_APROPOS}";
3866 _code="$?";
3867 clean_up;
3868 exit "${_code}";
3870 if obj _OPT_APROPOS_DATA is_not_empty; then
3871 apropos "$@" | grep '^[^(]*([457])';
3872 _code="$?";
3873 clean_up;
3874 exit "${_code}";
3876 if obj _OPT_APROPOS_DEVEL is_not_empty; then
3877 apropos "$@" | grep '^[^(]*([239])';
3878 _code="$?";
3879 clean_up;
3880 exit "${_code}";
3882 if obj _OPT_APROPOS_PROGS is_not_empty; then
3883 apropos "$@" | grep '^[^(]*([168])';
3884 _code="$?";
3885 clean_up;
3886 exit "${_code}";
3889 # set display
3890 if obj _OPT_DISPLAY is_not_empty; then
3891 DISPLAY="${_OPT_DISPLAY}";
3894 if obj _OPT_V is_yes; then
3895 _DISPLAY_MODE='groff';
3896 list_append _ADDOPTS_GROFF '-V';
3898 if obj _OPT_Z is_yes; then
3899 _DISPLAY_MODE='groff';
3900 list_append _ADDOPTS_GROFF '-Z';
3902 if obj _OPT_MODE is_equal 'groff'; then
3903 _DISPLAY_MODE='groff';
3905 if obj _DISPLAY_MODE is_equal 'groff'; then
3906 eval "${return_ok}";
3909 if obj _OPT_MODE is_equal 'source'; then
3910 _DISPLAY_MODE='source';
3911 eval "${return_ok}";
3914 case "${_OPT_MODE}" in
3915 '') # automatic mode
3916 case "${_OPT_DEVICE}" in
3918 if obj DISPLAY is_empty; then
3919 error "no X display found for device ${_OPT_DEVICE}";
3921 _DISPLAY_MODE='x';
3922 eval "${return_ok}";
3924 ascii|cp1047|latin1|utf8)
3925 if obj _DISPLAY_MODE is_not_equal 'text'; then
3926 _DISPLAY_MODE='tty';
3928 eval "${return_ok}";
3930 esac;
3931 if obj DISPLAY is_empty; then
3932 _DISPLAY_MODE='tty';
3933 eval "${return_ok}";
3936 if obj _OPT_DEFAULT_MODES is_empty; then
3937 _modes="${_DEFAULT_MODES}";
3938 else
3939 _modes="${_OPT_DEFAULT_MODES}";
3942 text)
3943 _DISPLAY_MODE='text';
3944 eval "${return_ok}";
3946 tty)
3947 _DISPLAY_MODE='tty';
3948 eval "${return_ok}";
3950 *) # display mode was given
3951 if obj DISPLAY is_empty; then
3952 error "you must be in X Window for ${_OPT_MODE} mode.";
3954 _modes="${_OPT_MODE}";
3956 esac;
3958 # only viewer modes are left
3959 eval set -- "$(list_from_split "${_modes}" ',')";
3960 while test "$#" -gt 0; do
3961 m="$1";
3962 shift;
3963 case "$m" in
3964 text)
3965 _DISPLAY_MODE='text';
3966 eval "${return_ok}";
3968 tty)
3969 _DISPLAY_MODE='tty';
3970 eval "${return_ok}";
3973 if obj _OPT_VIEWER_X is_not_empty; then
3974 _viewers="${_OPT_VIEWER_X}";
3975 else
3976 _viewers="${_VIEWER_X}";
3978 _viewer="$(_get_first_prog "${_viewers}")";
3979 if is_not_equal "$?" 0; then
3980 continue;
3982 _DISPLAY_PROG="${_viewer}";
3983 _DISPLAY_MODE='x';
3984 eval "${return_ok}";
3986 dvi)
3987 if obj _OPT_VIEWER_DVI is_not_empty; then
3988 _viewers="${_OPT_VIEWER_DVI}";
3989 else
3990 _viewers="${_VIEWER_DVI}";
3992 _viewer="$(_get_first_prog "${_viewers}")";
3993 if is_not_equal "$?" 0; then
3994 continue;
3996 _DISPLAY_PROG="${_viewer}";
3997 _DISPLAY_MODE="dvi";
3998 eval "${return_ok}";
4000 pdf)
4001 if obj _OPT_VIEWER_PDF is_not_empty; then
4002 _viewers="${_OPT_VIEWER_PDF}";
4003 else
4004 _viewers="${_VIEWER_PDF}";
4006 _viewer="$(_get_first_prog "${_viewers}")";
4007 if is_not_equal "$?" 0; then
4008 continue;
4010 _DISPLAY_PROG="${_viewer}";
4011 _DISPLAY_MODE="pdf";
4012 eval "${return_ok}";
4015 if obj _OPT_VIEWER_PS is_not_empty; then
4016 _viewers="${_OPT_VIEWER_PS}";
4017 else
4018 _viewers="${_VIEWER_PS}";
4020 _viewer="$(_get_first_prog "${_viewers}")";
4021 if is_not_equal "$?" 0; then
4022 continue;
4024 _DISPLAY_PROG="${_viewer}";
4025 _DISPLAY_MODE="ps";
4026 eval "${return_ok}";
4028 html)
4029 if obj _OPT_VIEWER_HTML is_not_empty; then
4030 _viewers="${_OPT_VIEWER_HTML}";
4031 else
4032 _viewers="${_VIEWER_HTML}";
4034 _viewer="$(_get_first_prog "${_viewers}")";
4035 if is_not_equal "$?" 0; then
4036 continue;
4038 _DISPLAY_PROG="${_viewer}";
4039 _DISPLAY_MODE=html;
4040 eval "${return_ok}";
4042 esac;
4043 done;
4044 error "no suitable display mode found.";
4047 _get_first_prog()
4049 local i;
4050 if is_equal "$#" 0; then
4051 error "_get_first_prog() needs 1 argument.";
4053 if is_empty "$1"; then
4054 return "${_BAD}";
4056 eval set -- "$(list_from_split "$1" ',')";
4057 for i in "$@"; do
4058 if obj i is_empty; then
4059 continue;
4061 if is_prog "$(get_first_essential $i)"; then
4062 echo -n "$i";
4063 return "${_GOOD}";
4065 done;
4066 return "${_BAD}";
4067 } # main_set_mode()
4070 #######################################################################
4071 # main_do_fileargs ()
4073 # Process filespec arguments in $_FILEARGS.
4075 # Globals:
4076 # in: $_FILEARGS (process with `eval set -- "$_FILEARGS"')
4078 landmark '17: main_do_fileargs()';
4079 main_do_fileargs()
4081 func_check main_do_fileargs = 0 "$@";
4082 local _exitcode;
4083 local _filespec;
4084 local _name;
4085 _exitcode="${_BAD}";
4086 eval set -- "${_FILEARGS}";
4087 unset _FILEARGS;
4088 # temporary storage of all input to $_TMP_CAT
4089 while test "$#" -ge 2; do
4090 # test for `s name' arguments, with `s' a 1-char standard section
4091 _filespec="$1";
4092 shift;
4093 case "${_filespec}" in
4095 continue;
4097 '-')
4098 if register_file '-'; then
4099 _exitcode="${_GOOD}";
4101 continue;
4104 if list_has_not _MAN_AUTO_SEC "${_filespec}"; then
4105 if do_filearg "${_filespec}"; then
4106 _exitcode="${_GOOD}";
4108 continue;
4110 _name="$1";
4111 case "${_name}" in
4112 */*|man:*|*\(*\)|*."${_filespec}")
4113 if do_filearg "${_filespec}"; then
4114 _exitcode="${_GOOD}";
4116 continue;
4118 esac;
4119 if do_filearg "man:${_name}(${_filespec})"; then
4120 _exitcode="${_GOOD}";
4121 shift;
4122 continue;
4123 else
4124 if do_filearg "${_filespec}"; then
4125 _exitcode="${_GOOD}";
4127 continue;
4131 if do_filearg "${_filespec}"; then
4132 _exitcode="${_GOOD}";
4134 continue;
4136 esac;
4137 done; # end of `s name' test
4138 while test "$#" -gt 0; do
4139 _filespec="$1";
4140 shift;
4141 if do_filearg "${_filespec}"; then
4142 _exitcode="${_GOOD}";
4144 done;
4145 rm -f "${_TMP_STDIN}";
4146 if is_equal "${_exitcode}" "${_BAD}"; then
4147 eval "${return_bad}";
4149 eval "${return_ok}";
4150 } # main_do_fileargs()
4153 ########################################################################
4154 # main_set_resources ()
4156 # Determine options for setting X resources with $_DISPLAY_PROG.
4158 # Globals: $_DISPLAY_PROG, $_OUTPUT_FILE_NAME
4160 landmark '18: main_set_resources()';
4161 main_set_resources()
4163 func_check main_set_resources = 0 "$@";
4164 local _prog; # viewer program
4165 local _rl; # resource list
4166 local n;
4167 _title="$(get_first_essential \
4168 "${_OPT_TITLE}" "${_REGISTERED_TITLE}")";
4169 _OUTPUT_FILE_NAME='';
4170 set -- ${_title};
4171 until is_equal "$#" 0; do
4172 n="$1";
4173 case "$n" in
4175 continue;
4178 n="$(echo -n "$1" | sed -e 's/^,,*//')";
4180 esac
4181 if obj n is_empty; then
4182 continue;
4184 if obj _OUTPUT_FILE_NAME is_not_empty; then
4185 _OUTPUT_FILE_NAME="${_OUTPUT_FILE_NAME},";
4187 _OUTPUT_FILE_NAME="${_OUTPUT_FILE_NAME}$n";
4188 shift;
4189 done;
4190 case "${_OUTPUT_FILE_NAME}" in
4192 _OUTPUT_FILE_NAME='-';
4195 error "$_OUTPUT_FILE_NAME starts with a comma.";
4197 esac;
4198 _OUTPUT_FILE_NAME="${_TMP_DIR}/${_OUTPUT_FILE_NAME}";
4200 if obj _DISPLAY_PROG is_empty; then # for example, for groff mode
4201 _DISPLAY_ARGS='';
4202 eval "${return_ok}";
4205 set -- ${_DISPLAY_PROG};
4206 _prog="$(base_name "$1")";
4207 _rl='';
4208 if obj _OPT_BD is_not_empty; then
4209 case "${_prog}" in
4210 ghostview|gv|gxditview|xditview|xdvi)
4211 list_append _rl '-bd' "${_OPT_BD}";
4213 esac;
4215 if obj _OPT_BG is_not_empty; then
4216 case "${_prog}" in
4217 ghostview|gv|gxditview|xditview|xdvi)
4218 list_append _rl '-bg' "${_OPT_BG}";
4220 xpdf)
4221 list_append _rl '-papercolor' "${_OPT_BG}";
4223 esac;
4225 if obj _OPT_BW is_not_empty; then
4226 case "${_prog}" in
4227 ghostview|gv|gxditview|xditview|xdvi)
4228 _list_append _rl '-bw' "${_OPT_BW}";
4230 esac;
4232 if obj _OPT_FG is_not_empty; then
4233 case "${_prog}" in
4234 ghostview|gv|gxditview|xditview|xdvi)
4235 list_append _rl '-fg' "${_OPT_FG}";
4237 esac;
4239 if is_not_empty "${_OPT_FN}"; then
4240 case "${_prog}" in
4241 ghostview|gv|gxditview|xditview|xdvi)
4242 list_append _rl '-fn' "${_OPT_FN}";
4244 esac;
4246 if is_not_empty "${_OPT_GEOMETRY}"; then
4247 case "${_prog}" in
4248 ghostview|gv|gxditview|xditview|xdvi|xpdf)
4249 list_append _rl '-geometry' "${_OPT_GEOMETRY}";
4251 esac;
4253 if is_empty "${_OPT_RESOLUTION}"; then
4254 _OPT_RESOLUTION="${_DEFAULT_RESOLUTION}";
4255 case "${_prog}" in
4256 gxditview|xditview)
4257 list_append _rl '-resolution' "${_DEFAULT_RESOLUTION}";
4259 xpdf)
4260 case "${_DEFAULT_RESOLUTION}" in
4262 # 72dpi is '100'
4263 list_append _rl '-z' '104';
4265 100)
4266 list_append _rl '-z' '139';
4268 esac;
4270 esac;
4271 else
4272 case "${_prog}" in
4273 ghostview|gv|gxditview|xditview|xdvi)
4274 list_append _rl '-resolution' "${_OPT_RESOLUTION}";
4276 xpdf)
4277 case "${_OPT_RESOLUTION}" in
4279 list_append _rl '-z' '104';
4280 # '100' corresponds to 72dpi
4282 100)
4283 list_append _rl '-z' '139';
4285 esac;
4287 esac;
4289 if is_yes "${_OPT_ICONIC}"; then
4290 case "${_prog}" in
4291 ghostview|gv|gxditview|xditview|xdvi)
4292 list_append _rl '-iconic';
4294 esac;
4296 if is_yes "${_OPT_RV}"; then
4297 case "${_prog}" in
4298 ghostview|gv|gxditview|xditview|xdvi)
4299 list_append _rl '-rv';
4301 esac;
4303 if is_not_empty "${_OPT_XRM}"; then
4304 case "${_prog}" in
4305 ghostview|gv|gxditview|xditview|xdvi|xpdf)
4306 eval set -- "{$_OPT_XRM}";
4307 for i in "$@"; do
4308 list_append _rl '-xrm' "$i";
4309 done;
4311 esac;
4313 if is_not_empty "${_title}"; then
4314 case "${_prog}" in
4315 gxditview|xditview)
4316 list_append _rl '-title' "${_title}";
4318 esac;
4320 _DISPLAY_ARGS="${_rl}";
4322 eval "${return_ok}";
4323 } # main_set_resources
4326 ########################################################################
4327 # main_display ()
4329 # Do the actual display of the whole thing.
4331 # Globals:
4332 # in: $_DISPLAY_MODE, $_OPT_DEVICE,
4333 # $_ADDOPTS_GROFF, $_ADDOPTS_POST, $_ADDOPTS_X,
4334 # $_REGISTERED_TITLE, $_TMP_CAT,
4335 # $_OPT_PAGER $PAGER $_MANOPT_PAGER
4337 landmark '19: main_display()';
4338 main_display()
4340 func_check main_display = 0 "$@";
4341 local p;
4342 local _addopts;
4343 local _device;
4344 local _groggy;
4345 local _modefile;
4346 local _options;
4347 local _pager;
4348 local _title;
4349 export _addopts;
4350 export _groggy;
4351 export _modefile;
4353 if obj _TMP_CAT is_non_empty_file; then
4354 _modefile="${_OUTPUT_FILE_NAME}";
4355 else
4356 clean_up;
4357 eval "${return_ok}";
4359 case "${_DISPLAY_MODE}" in
4360 groff)
4361 _ADDOPTS_GROFF="${_ADDOPTS_GROFF} ${_ADDOPTS_POST}";
4362 if obj _OPT_DEVICE is_not_empty; then
4363 _ADDOPTS_GROFF="${_ADDOPTS_GROFF} -T${_OPT_DEVICE}";
4365 _groggy="$(tmp_cat | eval grog "${_options}")";
4366 trap_clean;
4367 # start a new shell program to get another process ID.
4368 sh -c '
4369 set -e;
4370 test -f "${_modefile}" && rm -f "${_modefile}";
4371 mv "${_TMP_CAT}" "${_modefile}";
4372 cat "${_modefile}" | \
4374 clean_up()
4376 if test -d "${_TMP_DIR}"; then
4377 rm -f "${_TMP_DIR}"/* || true;
4378 rmdir "${_TMP_DIR}";
4381 trap clean_up 0 2>/dev/null || true;
4382 eval "${_groggy}" "${_ADDOPTS_GROFF}";
4383 ) &'
4385 text|tty)
4386 case "${_OPT_DEVICE}" in
4388 _device="$(get_first_essential \
4389 "${_OPT_TEXT_DEVICE}" "${_DEFAULT_TTY_DEVICE}")";
4391 ascii|cp1047|latin1|utf8)
4392 _device="${_OPT_DEVICE}";
4395 warning \
4396 "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
4398 esac;
4399 _addopts="${_ADDOPTS_GROFF} ${_ADDOPTS_POST}";
4400 _groggy="$(tmp_cat | grog -T${_device})";
4401 if obj _DISPLAY_MODE is_equal 'text'; then
4402 tmp_cat | eval "${_groggy}" "${_addopts}";
4403 else
4404 _pager='';
4405 for p in "${_OPT_PAGER}" "${PAGER}" "${_MANOPT_PAGER}" \
4406 'less -r -R' 'more' 'pager' 'cat'; do
4407 if is_prog $p; then # no "" for is_prog() allows args for $p
4408 _pager="$p";
4409 break;
4411 done;
4412 if obj _pager is_empty; then
4413 error 'no pager program found for tty mode';
4415 tmp_cat | eval "${_groggy}" "${_addopts}" | \
4416 eval "${_pager}";
4418 clean_up;
4421 #### viewer modes
4423 dvi)
4424 case "${_OPT_DEVICE}" in
4425 ''|dvi) do_nothing; ;;
4427 warning \
4428 "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
4430 esac;
4431 _groggy="$(tmp_cat | grog -Tdvi)";
4432 _do_display;
4434 html)
4435 case "${_OPT_DEVICE}" in
4436 ''|html) do_nothing; ;;
4438 warning \
4439 "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
4441 esac;
4442 _modefile="${_modefile}".html
4443 _groggy="$(tmp_cat | grog -Thtml)";
4444 _do_display;
4446 pdf)
4447 case "${_OPT_DEVICE}" in
4448 ''|ps)
4449 do_nothing;
4452 warning \
4453 "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
4455 esac;
4456 _modefile="${_modefile}"
4457 _groggy="$(tmp_cat | grog -Tps)";
4458 trap_clean;
4459 # start a new shell program to get another process ID.
4460 sh -c '
4461 set -e;
4462 _psfile="${_modefile}.ps";
4463 _modefile="${_modefile}.pdf";
4464 test -f "${_psfile}" && rm -f "${_psfile}";
4465 test -f "${_modefile}" && rm -f "${_modefile}";
4466 cat "${_TMP_CAT}" | \
4467 eval "${_groggy}" "${_ADDOPTS_GROFF}" > "${_psfile}";
4468 gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite \
4469 -sOutputFile="${_modefile}" -c save pop -f "${_psfile}";
4470 test -f "${_psfile}" && rm -f "${_psfile}";
4471 test -f "${_TMP_CAT}" && rm -f "${_TMP_CAT}";
4473 clean_up() {
4474 rm -f "${_modefile}";
4475 if test -d "${_TMP_DIR}"; then
4476 rm -f "${_TMP_DIR}"/* || true;
4477 rmdir "${_TMP_DIR}";
4480 trap clean_up 0 2>/dev/null || true;
4481 eval "${_DISPLAY_PROG}" ${_DISPLAY_ARGS} "${_modefile}";
4482 ) &'
4485 case "${_OPT_DEVICE}" in
4486 ''|ps)
4487 do_nothing;
4490 warning \
4491 "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
4493 esac;
4494 _groggy="$(tmp_cat | grog -Tps)";
4495 _do_display;
4497 source)
4498 tmp_cat;
4499 clean_up;
4502 case "${_OPT_DEVICE}" in
4504 _groggy="$(tmp_cat | grog -Z)";
4506 X*|ps)
4507 _groggy="$(tmp_cat | grog -T"${_OPT_DEVICE}" -Z)";
4510 warning \
4511 "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
4512 _groggy="$(tmp_cat | grog -Z)";
4514 esac;
4515 _do_display;
4518 error "unknown mode \`${_DISPLAY_MODE}'";
4520 esac;
4521 eval "${return_ok}";
4522 } # main_display()
4524 _do_display()
4526 func_check _do_display = 0 "$@";
4527 trap_clean;
4528 # start a new shell program for another process ID and better
4529 # cleaning-up of the temporary files.
4530 sh -c '
4531 set -e;
4532 test -f "${_modefile}" && rm -f "${_modefile}";
4533 cat "${_TMP_CAT}" | \
4534 eval "${_groggy}" "${_ADDOPTS_GROFF}" > "${_modefile}";
4535 rm -f "${_TMP_CAT}";
4537 clean_up() {
4538 if test -d "${_TMP_DIR}"; then
4539 rm -f "${_TMP_DIR}"/* || true;
4540 rmdir "${_TMP_DIR}";
4543 trap clean_up 0 2>/dev/null || true;
4544 eval "${_DISPLAY_PROG}" ${_DISPLAY_ARGS} "${_modefile}";
4545 ) &'
4549 ########################################################################
4550 # main (<command_line_args>*)
4552 # The main function for groffer.
4554 # Arguments:
4556 main()
4558 func_check main '>=' 0 "$@";
4559 # Do not change the sequence of the following functions!
4560 main_init;
4561 main_parse_MANOPT;
4562 main_parse_args "$@";
4563 main_set_mode;
4564 main_do_fileargs;
4565 main_set_resources;
4566 main_display;
4567 eval "${return_ok}";
4570 landmark '20: end of function definitions';
4572 ########################################################################
4574 main "$@";