Get rid of the old texinfo.
[dragonfly.git] / contrib / groff / contrib / groffer / groffer.sh
1 #!/bin/sh
2
3 # groffer - display groff files
4
5 # File position: <groff-source>/contrib/groffer/groffer
6
7 # Copyright (C) 2001,2002 Free Software Foundation, Inc.
8 # Written by Bernd Warken <bwarken@mayn.de>
9
10 # This file is part of groff.
11
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.
16
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.
21
22 # You should have received a copy of the GNU General Public License
23 # along with groff; see the file COPYING.  If not, write to the
24 # Free Software Foundation, 59 Temple Place - Suite 330, Boston,
25 # MA 02111-1307, USA.
26
27 export _PROGRAM_NAME;
28 export _PROGRAM_VERSION;
29 export _LAST_UPDATE;
30
31 _PROGRAM_NAME='groffer';
32 _PROGRAM_VERSION='0.9.1';
33 _LAST_UPDATE='30 Sep 2002';
34
35 ########################################################################
36 # Determine the shell under which to run this script;
37 # if `ash' is available restart the script using `ash';
38 # otherwise just go on.
39
40 if test "${_groffer_run}" != 'second'; then
41   # only reached during the first run of the script
42
43   export GROFFER_OPT;
44   export _groffer_run;
45   export _this;
46
47
48   #_this="@BINDIR@/${_PROGRAM_NAME}";
49   _this='groffer.sh';
50
51   ###########################
52   # _get_opt_shell ("$@")
53   #
54   # Determine whether `--shell' was specified in $GROFF_OPT or in $*;
55   # if so echo its argument.
56   #
57   _get_opt_shell()
58   {
59     local i;
60     local _sh;
61     case " ${GROFFER_OPT} $*" in
62       *\ --shell\ *|*\ --shell=*)
63         (
64           eval set -- "${GROFFER_OPT}" '"$@"';
65           _sh='';
66           for i in "$@"; do
67             case "$1" in
68               --shell)
69                 if test "$#" -ge 2; then
70                   _sh="$2";
71                   shift;
72                 fi;
73                 ;;
74               --shell=?*)
75                 # delete up to first `=' character
76                 _sh="$(echo -n "$1" | sed -e 's/^[^=]*=//')";
77                 ;;
78             esac;
79             shift;
80           done;
81           echo -n "${_sh}";
82         )
83         ;;
84     esac;
85   }
86
87
88   ###########################
89   # _test_on_shell (<name>)
90   #
91   # Test whether <name> is a shell program of Bourne type (POSIX sh).
92   #
93   _test_on_shell()
94   {
95     if test "$#" -le 0 || test "$1" = ''; then
96       return 1;
97     fi;
98     # do not quote $1 to allow arguments
99     test "$($1 -c 's=ok; echo -n "$s"' 2>/dev/null)" = 'ok';
100   }
101
102   # do the shell determination
103   _shell="$(_get_opt_shell "$@")";
104   if test "${_shell}" = ''; then
105     _shell='ash';
106   fi;
107   if _test_on_shell "${_shell}"; then
108     _groffer_run='second';
109     # do not quote $_shell to allow arguments
110     exec ${_shell} "${_this}" "$@";
111     exit;
112   fi;
113
114   # clean-up of shell determination
115   unset _shell;
116   unset _this;
117   unset _groffer_run;
118   _get_opt_shell()
119   {
120     return 0;
121   }
122   _test_on_shell()
123   {
124     return 0;
125   }
126
127 fi;
128
129
130 ########################################################################
131 # diagnostic messages
132 #
133 export _DEBUG;
134 _DEBUG='no';                    # disable debugging information
135 #_DEBUG='yes';                  # enable debugging information
136
137 export _DEBUG_LM;
138 _DEBUG_LM='no';                 # disable landmark messages
139 #_DEBUG_LM='yes';               # enable landmark messages
140
141
142 ########################################################################
143 #                          Description
144 ########################################################################
145
146 # Display groff files and man pages on X or tty, even when compressed.
147
148
149 ### Usage
150
151 # Input comes from either standard input or command line parameters
152 # that represent either names of exisiting roff files or standardized
153 # specifications for man pages.  All of these can be compressed in a
154 # format that is decompressible by `gzip'.
155
156 # The following displaying modes are available:
157 # - Display formatted input with the X roff viewer `gxditview',
158 # - with a Prostcript viewer,
159 # - with a dvi viewer,
160 # - with a web browser.
161 # - Display formatted input in a text terminal using a text device.
162 # - Generate output for some groff device on stdout without a viewer.
163 # - Output only the source code without any groff processing.
164 # - Generate the troff intermediate output on standard output
165 #   without groff postprocessing.
166 # By default, the program tries to display with `gxditview' (1); if
167 # this does not work, text display (2) is used.
168
169
170 ### Error handling
171
172 # Error handling and exit behavior is complicated by the fact that
173 # `exit' can only escape from the current shell; trouble occurs in
174 # subshells.  This was solved by sending kill signals, see
175 # $_PROCESS_ID and error().
176
177
178 ### Compatibility
179
180 # This shell script is compatible to the both the GNU and the POSIX
181 # shell and utilities.  Care was taken to restrict the programming
182 # technics used here in order to achieve POSIX compatibility as far
183 # back as POSIX P1003.2 Draft 11.2 of September 1991.
184
185 # The only non-builtin used here is POSIX `sed'.  This script was
186 # tested under `bash', `ash', and `ksh'.  The speed under `ash' is
187 # more than double when compared to the larger shells.
188
189 # This script provides its own option parser.  It is compatible to the
190 # usual GNU style command line (option clusters, long options, mixing
191 # of options and non-option file names), except that it is not
192 # possible to abbreviate long option names.
193
194 # The mixing of options and file names can be prohibited by setting
195 # the environment variable `$POSIXLY_CORRECT' to a non-empty value.
196 # This enables the rather wicked POSIX behavior to terminate option
197 # parsing when the first non-option command line argument is found.
198
199
200 ########################################################################
201 #            Survey of functions defined in this document
202 ########################################################################
203
204 # The elements specified within paranthesis `(<>)' give hints to what
205 # the arguments are meant to be; the argument names are irrelevant.
206 # <>?     0 or 1
207 # <>*     arbitrarily many such arguments, incl. none
208 # <>+     one or more such arguments
209 # <>      exactly 1
210
211 # A function that starts with an underscore `_' is an internal
212 # function for some function.  The internal functions are defined just
213 # after their corresponding function; they are not mentioned in the
214 # following.
215
216 # abort (text>*)
217 # base_name (path)
218 # catz (<file>)
219 # clean_up ()
220 # clean_up_secondary ()
221 # diag (text>*)
222 # dirname_append (<path> [<dir...>])
223 # dirname_chop (<path>)
224 # do_filearg (<filearg>)
225 # do_nothing ()
226 # echo2 (<text>*)
227 # echo2n (<text>*)
228 # error (<text>*)
229 # get_first_essential (<arg>*)
230 # is_dir (<name>)
231 # is_empty (<string>)
232 # is_equal (<string1> <string2>)
233 # is_file (<name>)
234 # is_not_empty (<string>)
235 # is_not_equal (<string1> <string2>)
236 # is_not_file (<name>)
237 # is_not_prog (<name>)
238 # is_prog (<name>)
239 # is_yes (<string>)
240 # leave ()
241 # landmark (<text>)
242 # list_append (<list> <element>...)
243 # list_check (<list>)
244 # list_from_args (<arg>...)
245 # list_from_cmdline (<s_n> <s_a> <l_n> <l_n> [<cmdline_arg>...])
246 # list_from_split (<string> <separator>)
247 # list_has (<list> <element>)
248 # list_has_not (<list> <element>)
249 # list_length (<list>)
250 #   main_*(), see after the functions
251 # man_do_filespec (<filespec>)
252 # man_setup ()
253 # man_register_file (<file> [<name> [<section>]])
254 # man_search_section (<name> <section>)
255 # man_set()
256 # manpath_add_lang(<path> <language>)
257 # manpath_add_system()
258 # manpath_from_path ()
259 # normalize_args (<shortopts> <longopts> <arg>*)
260 # path_chop (<path>)
261 # path_clean (<path>)
262 # path_contains (<path> <dir>)
263 # path_not_contains (<path> <dir>)
264 # path_split (<path>)
265 # register_file (<filename>)
266 # register_title (<filespec>)
267 # reset ()
268 # save_stdin ()
269 # string_contains (<string> <part>)
270 # string_not_contains (<string> <part>)
271 # tmp_cat ()
272 # tmp_create (<suffix>?)
273 # to_tmp (<filename>)
274 # usage ()
275 # version ()
276 # warning (<string>)
277 # whatis (<filename>)
278 # where (<program>)
279
280
281 ########################################################################
282 #                       Environment Variables
283 ########################################################################
284
285 # Environment variables that exist only for this file start with an
286 # underscore letter.  Global variables to this file are written in
287 # upper case letters, e.g. $_GLOBAL_VARIABLE; temporary variables
288 # start with an underline and use only lower case letters and
289 # underlines, e.g.  $_local_variable .
290
291 #   [A-Z]*     system variables,      e.g. $MANPATH
292 #   _[A-Z_]*   global file variables, e.g. $_MAN_PATH
293 #   _[a-z_]*   temporary variables,   e.g. $_manpath
294
295 # Due to incompatibilities of the `ash' shell, the name of loop
296 # variables in `for' must be single character
297 #   [a-z]      local loop variables,   e.g. $i
298
299
300 ########################################################################
301 # External environment variables
302
303 # If these variables are exported here then the `ash' shell coughs
304 # when calling `groff' in `main_display()'.
305
306 if test "${GROFFER_EXPORT_EXTERNALS}" = 'yes'; then
307
308   # external system environment variables that are explicitly used
309   export DISPLAY;               # Presets the X display.
310   export LANG;                  # For language specific man pages.
311   export LC_ALL;                # For language specific man pages.
312   export LC_MESSAGES;           # For language specific man pages.
313   export PAGER;                 # Paging program for tty mode.
314   export PATH;                  # Path for the programs called (: list).
315
316   # groffer native environment variables
317   export GROFFER_OPT            # preset options for groffer.
318
319   # all groff environment variables are used, see groff(1)
320   export GROFF_BIN_PATH;        # Path for all groff programs.
321   export GROFF_COMMAND_PREFIX;  # '' (normally) or 'g' (several troffs).
322   export GROFF_FONT_PATH;       # Path to non-default groff fonts.
323   export GROFF_TMAC_PATH;       # Path to non-default groff macro files.
324   export GROFF_TMPDIR;          # Directory for groff temporary files.
325   export GROFF_TYPESETTER;      # Preset default device.
326
327   # all GNU man environment variables are used, see man(1).
328   export MANOPT;                # Preset options for man pages.
329   export MANPATH;               # Search path for man pages (: list).
330   export MANROFFSEQ;            # Ignored because of grog guessing.
331   export MANSECT;               # Search man pages only in sections (:).
332   export SYSTEM;                # Man pages for different OS's (, list).
333
334 fi;
335
336
337 ########################################################################
338 # read-only variables (global to this file)
339 ########################################################################
340
341 # characters
342
343 export _BQUOTE;
344 export _BSLASH;
345 export _DQUOTE;
346 export _NEWLINE;
347 export _LBRACK;
348 export _LPAR;
349 export _RBRACK;
350 export _RPAR;
351 export _SPACE;
352 export _SQUOTE;
353 export _TAB;
354
355 _BQUOTE='`';
356 _BSLASH='\';
357 _DQUOTE='"';
358 _NEWLINE='
359 ';
360 _LBRACK='[';
361 _LPAR='(';
362 _RBRACK=']';
363 _RPAR=')';
364 _SPACE=' ';
365 _SQUOTE="'";
366 _TAB='  ';
367
368 # function return values; `0' means ok; other values are error codes
369 export _BAD;
370 export _BAD2;
371 export _BAD3;
372 export _ERROR;
373 export _GOOD;
374 export _NO;
375 export _OK;
376 export _YES;
377
378 _GOOD='0';                      # return ok
379 _BAD='1';                       # return negatively, error code `1'
380 _BAD2='2';                      # return negatively, error code `2'
381 _BAD3='3';                      # return negatively, error code `3'
382 _ERROR='255';                   # for syntax errors; no `-1' in `ash'
383
384 _NO="${_BAD}";
385 _YES="${_GOOD}";
386 _OK="${_GOOD}";
387
388 # quasi-functions, call with `eval'
389 export return_ok;
390 export return_good;
391 export return_bad;
392 export return_yes;
393 export return_no;
394 export return_error;
395 return_ok="func_pop; return ${_OK}";
396 return_good="func_pop; return ${_GOOD}";
397 return_bad="func_pop; return ${_BAD}";
398 return_yes="func_pop; return ${_YES}";
399 return_no="func_pop; return ${_NO}";
400 return_error="func_pop; return ${_ERROR}";
401
402
403 export _CONFFILES;
404 _CONFFILES="/etc/groff/groffer.conf ${HOME}/.groff/groffer.conf";
405
406 export _DEFAULT_MODES;
407 _DEFAULT_MODES='ps,x,tty';
408 export _DEFAULT_RESOLUTION;
409 _DEFAULT_RESOLUTION='100';
410
411 export _DEFAULT_TTY_DEVICE;
412 _DEFAULT_TTY_DEVICE='latin1';
413
414 # _VIEWER_* viewer programs for different modes (only X is necessary)
415 # _VIEWER_* a comma-separated list of viewer programs (with options)
416 export _VIEWER_DVI;             # viewer program for dvi mode
417 export _VIEWER_PS;              # viewer program for ps mode
418 export _VIEWER_WWW_X;           # viewer program for www mode in X
419 export _VIEWER_WWW_TTY;         # viewer program for www mode in tty
420 _VIEWER_DVI='xdvi,dvilx';
421 _VIEWER_PDF='xpdf,acroread';
422 _VIEWER_PS='gv,ghostview,gs_x11,gs';
423 _VIEWER_WWW='mozilla,netscape,opera,amaya,arena';
424 _VIEWER_X='gxditview,xditview';
425
426 # Search automatically in standard sections `1' to `8', and in the
427 # traditional sections `9', `n', and `o'.  On many systems, there
428 # exist even more sections, mostly containing a set of man pages
429 # special to a specific program package.  These aren't searched for
430 # automatically, but must be specified on the command line.
431 export _MAN_AUTO_SEC;
432 _MAN_AUTO_SEC="'1' '2' '3' '4' '5' '6' '7' '8' '9' 'n' 'o'"
433
434 export _PROCESS_ID;             # for shutting down the program
435 _PROCESS_ID="$$";
436
437
438 ############ the command line options of the involved programs
439 #
440 # The naming scheme for the options environment names is
441 # $_OPTS_<prog>_<length>[_<argspec>]
442 #
443 # <prog>:    program name GROFFER, GROFF, or CMDLINE (for all
444 #            command line options)
445 # <length>:  LONG (long options) or SHORT (single character options)
446 # <argspec>: ARG for options with argument, NA for no argument;
447 #            without _<argspec> both the ones with and without arg.
448 #
449 # Each option that takes an argument must be specified with a
450 # trailing : (colon).
451
452 # exports
453 export _OPTS_GROFFER_SHORT_NA;
454 export _OPTS_GROFFER_SHORT_ARG;
455 export _OPTS_GROFFER_LONG_NA;
456 export _OPTS_GROFFER_LONG_ARG;
457 export _OPTS_GROFF_SHORT_NA;
458 export _OPTS_GROFF_SHORT_ARG;
459 export _OPTS_GROFF_LONG_NA;
460 export _OPTS_GROFF_LONG_ARG;
461 export _OPTS_MAN_SHORT_ARG;
462 export _OPTS_MAN_SHORT_NA;
463 export _OPTS_MAN_LONG_ARG;
464 export _OPTS_MAN_LONG_NA;
465 export _OPTS_GROFFER_LONG;
466 export _OPTS_GROFFER_SHORT;
467 export _OPTS_GROFF_LONG;
468 export _OPTS_GROFF_SHORT;
469 export _OPTS_CMDLINE_SHORT_NA;
470 export _OPTS_CMDLINE_SHORT_ARG;
471 export _OPTS_CMDLINE_SHORT;
472 export _OPTS_CMDLINE_LONG_NA;
473 export _OPTS_CMDLINE_LONG_ARG;
474 export _OPTS_CMDLINE_LONG;
475
476
477 ###### native groffer options
478
479 _OPTS_GROFFER_SHORT_NA="'h' 'Q' 'v' 'V' 'X' 'Z'";
480 _OPTS_GROFFER_SHORT_ARG="'T'";
481
482 _OPTS_GROFFER_LONG_NA="'all' 'apropos' 'ascii' 'auto' 'default' 'dvi' \
483 'groff' 'help' 'intermediate-output' 'local-file' 'location' 'man' \
484 'no-location' 'no-man' 'pdf' 'ps' 'rv' 'source' 'tty' 'tty-device' \
485 'version' 'whatis' 'where' 'www' 'x'";
486
487 _OPTS_GROFFER_LONG_ARG="'background' 'bd' 'bg' 'bw' 'default-modes' \
488 'device' 'display' 'dvi-viewer' 'extension' 'fg' 'fn' 'font' \
489 'foreground' 'geometry' 'locale' 'manpath' 'mode' 'pager' \
490 'pdf-viewer' 'ps-viewer' 'resolution' 'sections' 'shell' \
491 'systems' 'title' 'troff-device' 'www-viewer' 'xrm' 'x-viewer'";
492
493 ##### options inhereted from groff
494
495 _OPTS_GROFF_SHORT_NA="'a' 'b' 'c' 'e' 'g' 'i' 'l' 'p' 's' 't' 'z' \
496 'C' 'E' 'G' 'N' 'R' 'S' 'U' 'V'";
497 _OPTS_GROFF_SHORT_ARG="'d' 'f' 'F' 'I' 'L' 'm' 'M' 'n' 'o' 'P' 'r' \
498 'w' 'W'";
499 _OPTS_GROFF_LONG_NA="";
500 _OPTS_GROFF_LONG_ARG="";
501
502 ###### man options (for parsing $MANOPT only)
503
504 _OPTS_MAN_SHORT_NA="'7' 'a' 'c' 'd' 'D' 'f' 'h' 'k' 'l' 't' 'u' \
505 'V' 'w' 'Z'";
506 _OPTS_MAN_SHORT_ARG="'e' 'L' 'm' 'M' 'p' 'P' 'r' 'S' 'T'";
507
508 _OPTS_MAN_LONG_NA="'all' 'ascii' 'apropos' 'catman' 'debug' 'default' \
509 'ditroff' 'help' 'local-file' 'location' 'troff' 'update' 'version' \
510 'whatis' 'where'";
511
512 _OPTS_MAN_LONG_ARG="'extension' 'locale' 'manpath' \
513 'pager' 'preprocessor' 'prompt' 'sections' 'systems' 'troff-device'";
514
515 ###### collections of options
516
517 # groffer
518
519 _OPTS_GROFFER_LONG="${_OPTS_GROFFER_LONG_ARG} ${_OPTS_GROFFER_LONG_NA}";
520 _OPTS_GROFFER_SHORT=\
521 "${_OPTS_GROFFER_SHORT_ARG} ${_OPTS_GROFFER_SHORT_NA}";
522
523 # groff
524
525 _OPTS_GROFF_LONG="${_OPTS_GROFF_LONG_ARG} ${_OPTS_GROFF_LONG_NA}";
526 _OPTS_GROFF_SHORT="${_OPTS_GROFF_SHORT_ARG} ${_OPTS_GROFF_SHORT_NA}";
527
528 # all command line options
529
530 _OPTS_CMDLINE_SHORT_NA="\
531 ${_OPTS_GROFFER_SHORT_NA} ${_OPTS_GROFF_SHORT_NA}";
532 _OPTS_CMDLINE_SHORT_ARG="\
533 ${_OPTS_GROFFER_SHORT_ARG} ${_OPTS_GROFF_SHORT_ARG}";
534 _OPTS_CMDLINE_SHORT="${_OPTS_GROFFER_SHORT} ${_OPTS_GROFF_SHORT}";
535
536 _OPTS_CMDLINE_LONG_NA="${_OPTS_GROFFER_LONG_NA} \
537 ${_OPTS_GROFF_LONG_NA} ${_OPTS_MAN_LONG_NA}";
538 _OPTS_CMDLINE_LONG_ARG="${_OPTS_GROFFER_LONG_ARG} \
539 ${_OPTS_GROFF_LONG_ARG} ${_OPTS_MAN_LONG_ARG}";
540 _OPTS_CMDLINE_LONG="${_OPTS_GROFFER_LONG} ${_OPTS_GROFF_LONG}";
541
542
543 ########################################################################
544 # read-write variables (global to this file)
545 ########################################################################
546
547 export _ADDOPTS_GROFF;          # Transp. options for groff (`eval').
548 export _ADDOPTS_POST;           # Transp. options postproc (`eval').
549 export _ADDOPTS_X;              # Transp. options X postproc (`eval').
550 export _DEFAULT_MODES;          # Set default modes.
551 export _DISPLAY_MODE;           # Display mode.
552 export _DISPLAY_PROG;           # Viewer program to be used for display.
553 export _DISPLAY_ARGS;           # X resources for the viewer program.
554 export _FILEARGS;               # Stores filespec parameters.
555 export _FUNC_STACK;             # Store debugging information.
556 export _REGISTERED_TITLE;       # Processed file names.
557 # _HAS_* from availability tests
558 export _HAS_COMPRESSION;        # `yes' if compression is available
559 export _HAS_OPTS_GNU;           # `yes' if GNU `getopt' is available
560 export _HAS_OPTS_POSIX;         # `yes' if POSIX `getopts' is available
561 # _MAN_* finally used configuration of man searching
562 export _MAN_ALL;                # search all man pages per filespec
563 export _MAN_ENABLE;             # enable search for man pages
564 export _MAN_EXT;                # extension for man pages
565 export _MAN_FORCE;              # force file parameter to be man pages
566 export _MAN_IS_SETUP;           # setup man variables only once
567 export _MAN_LANG;               # language for man pages
568 export _MAN_LANG_DONE;          # language dirs added to man path
569 export _MAN_PATH;               # search path for man pages
570 export _MAN_SEC;                # sections for man pages; sep. `:'
571 export _MAN_SEC_DONE;           # sections added to man path
572 export _MAN_SYS;                # system names for man pages; sep. `,'
573 export _MAN_SYS;                # system names added to man path
574 # _MANOPT_* as parsed from $MANOPT
575 export _MANOPT_ALL;             # $MANOPT --all
576 export _MANOPT_EXTENSION;       # $MANOPT --extension
577 export _MANOPT_LANG;            # $MANOPT --locale
578 export _MANOPT_PATH;            # $MANOPT --manpath
579 export _MANOPT_PAGER;           # $MANOPT --pager
580 export _MANOPT_SEC;             # $MANOPT --sections
581 export _MANOPT_SYS;             # $MANOPT --systems
582 # _OPT_* as parsed from groffer command line
583 export _OPT_ALL;                # display all suitable man pages.
584 export _OPT_APROPOS;            # branch to `apropos' program.
585 export _OPT_BD;                 # set border color in some modes.
586 export _OPT_BG;                 # set background color in some modes.
587 export _OPT_BW;                 # set border width in some modes.
588 export _OPT_DEBUG;              # print debugging information on stderr.
589 export _OPT_DEFAULT_MODES;      # `,'-list of modes when no mode given.
590 export _OPT_DEVICE;             # device option.
591 export _OPT_DISPLAY;            # set X display.
592 export _OPT_FG;                 # set foreground color in some modes.
593 export _OPT_FN;                 # set font in some modes.
594 export _OPT_GEOMETRY;           # set size and position of viewer in X.
595 export _OPT_LANG;               # set language for man pages
596 export _OPT_LOCATION;           # print processed file names to stderr
597 export _OPT_MODE;               # values: X, tty, Q, Z, ""
598 export _OPT_MANPATH;            # manual setting of path for man-pages
599 export _OPT_PAGER;              # specify paging program for tty mode
600 export _OPT_RESOLUTION;         # set X resolution in dpi
601 export _OPT_RV;                 # reverse fore- and background colors.
602 export _OPT_SECTIONS;           # sections for man page search
603 export _OPT_SYSTEMS;            # man pages of different OS's
604 export _OPT_TITLE;              # title for gxditview window
605 export _OPT_TTY_DEVICE;         # set device for tty mode.
606 export _OPT_V;                  # groff option -V.
607 export _OPT_VIEWER_DVI;         # viewer program for dvi mode
608 export _OPT_VIEWER_PDF;         # viewer program for pdf mode
609 export _OPT_VIEWER_PS;          # viewer program for ps mode
610 export _OPT_VIEWER_WWW;         # viewer program for www mode
611 export _OPT_VIEWER_X;           # viewer program for x mode
612 export _OPT_WHATIS;             # print the one-liner man info
613 export _OPT_X;                  # groff option -X.
614 export _OPT_XRM;                # specify X resource.
615 export _OPT_Z;                  # groff option -Z.
616 # _TMP_* temporary files
617 export _TMP_DIR;                # directory for temporary files
618 export _TMP_CAT;                # stores concatenation of everything
619 export _TMP_PREFIX;             # dir and base name for temporary files
620 export _TMP_STDIN;              # stores stdin, if any
621
622 # these variables are preset in section `Preset' after the rudim. test
623
624
625 ########################################################################
626 #             Test of rudimentary shell functionality
627 ########################################################################
628
629
630 ########################################################################
631 # Test of `test'.
632 #
633 test "a" = "a" || exit 1;
634
635
636 ########################################################################
637 # Test of `echo' and the `$()' construct.
638 #
639 echo -n '' >/dev/null || exit "${_ERROR}";
640 if test "$(echo -n 'te' && echo -n '' && echo -n 'st')" != "test"; then
641   exit "${_ERROR}";
642 fi;
643
644
645 ########################################################################
646 # Test of function definitions.
647 #
648 _t_e_s_t_f_u_n_c_()
649 {
650   return "${_OK}";
651 }
652
653 if _t_e_s_t_f_u_n_c_ 2>/dev/null; then
654   :
655 else
656   echo 'shell does not support function definitions.' >&2;
657   exit "${_ERROR}";
658 fi;
659
660
661 ########################################################################
662 # Preset and reset of read-write global variables
663 ########################################################################
664
665
666 # For variables that can be reset by option `--default', see reset().
667
668 _FILEARGS='';
669
670 # _HAS_* from availability tests
671 _HAS_COMPRESSION='';
672 _HAS_OPTS_GNU='';
673 _HAS_OPTS_POSIX='';
674
675 # _TMP_* temporary files
676 _TMP_DIR='';
677 _TMP_CAT='';
678 _TMP_PREFIX='';
679 _TMP_STDIN='';
680
681
682 ########################################################################
683 # reset ()
684 #
685 # Reset the variables that can be affected by options to their default.
686 #
687 reset()
688 {
689   if test "$#" -ne 0; then
690     error "reset() does not have arguments.";
691   fi;
692
693   _ADDOPTS_GROFF='';
694   _ADDOPTS_POST='';
695   _ADDOPTS_X='';
696   _DISPLAY_ARGS='';
697   _DISPLAY_MODE='';
698   _DISPLAY_PROG='';
699   _REGISTERED_TITLE='';
700
701   # _MAN_* finally used configuration of man searching
702   _MAN_ALL='no';
703   _MAN_ENABLE='yes';            # do search for man-pages
704   _MAN_EXT='';
705   _MAN_FORCE='no';              # first local file, then search man page
706   _MAN_IS_SETUP='no';
707   _MAN_LANG='';
708   _MAN_LANG_DONE='no';
709   _MAN_PATH='';
710   _MAN_SEC='';
711   _MAN_SEC_DONE='no';
712   _MAN_SYS='';
713   _MAN_SYS_DONE='no';
714
715   # _MANOPT_* as parsed from $MANOPT
716   _MANOPT_ALL='no';
717   _MANOPT_EXTENSION='';
718   _MANOPT_LANG='';
719   _MANOPT_PATH='';
720   _MANOPT_PAGER='';
721   _MANOPT_SEC='';
722   _MANOPT_SYS='';
723
724   # _OPT_* as parsed from groffer command line
725   _OPT_ALL='no';
726   _OPT_APROPOS='no';
727   _OPT_BD='';
728   _OPT_BG='';
729   _OPT_BW='';
730   _OPT_DEBUG='no';
731   _OPT_DEFAULT_MODES='';
732   _OPT_DEVICE='';
733   _OPT_DISPLAY='';
734   _OPT_FG='';
735   _OPT_FN='';
736   _OPT_GEOMETRY='';
737   _OPT_LANG='';
738   _OPT_LOCATION='no';
739   _OPT_MODE='';
740   _OPT_MANPATH='';
741   _OPT_PAGER='';
742   _OPT_RESOLUTION='';
743   _OPT_RV='';
744   _OPT_SECTIONS='';
745   _OPT_SYSTEMS='';
746   _OPT_TITLE='';
747   _OPT_TTY_DEVICE='';
748   _OPT_V='no';
749   _OPT_VIEWER_DVI='';
750   _OPT_VIEWER_PDF='';
751   _OPT_VIEWER_PS='';
752   _OPT_VIEWER_WWW='';
753   _OPT_VIEWER_X='';
754   _OPT_WHATIS='no';
755   _OPT_X='no';
756   _OPT_XRM='';
757   _OPT_Z='no';
758
759 }
760
761 reset;
762
763
764 ########################################################################
765 #          Functions for error handling and debugging
766 ########################################################################
767
768
769 ##############
770 # landmark (<text>)
771 #
772 # Print <text> to standard error as a debugging aid.
773 #
774 # Globals: $_DEBUG_LM
775 #
776 landmark()
777 {
778   if test "${_DEBUG_LM}" = 'yes'; then
779     echo ">>> $*" >&2;
780   fi;
781 }
782
783 landmark "1: debugging functions";
784
785
786 ##############
787 # clean_up ()
788 #
789 # Clean up at exit.
790 #
791 clean_up()
792 {
793   clean_up_secondary;
794   rm -f "${_TMP_CAT}";
795 }
796
797
798 ##############
799 # clean_up_secondary ()
800 #
801 # Clean up temporary files without $_TMP_CAT.
802 #
803 clean_up_secondary()
804 {
805   rm -f "${_TMP_STDIN}";
806 }
807
808
809 ##############
810 # echo2 (<text>*)
811 #
812 # Output to stderr.
813 #
814 # Arguments : arbitrary text.
815 #
816 echo2()
817 {
818   echo "$*" >&2;
819 }
820
821
822 ##############
823 # echo2n (<text>*)
824 #
825 # Output to stderr.
826 #
827 # Arguments : arbitrary text.
828 #
829 echo2n()
830 {
831   echo -n "$*" >&2;
832 }
833
834
835 #############
836 # diag (text>*)
837 #
838 # Output a diagnostic message to stderr
839 #
840 diag()
841 {
842   echo2 '>>>>>'"$*";
843 }
844
845
846 #############
847 # error (<text>*)
848 #
849 # Print an error message to standard error; exit with an error condition
850 #
851 error()
852 {
853   local i;
854   local _code;
855   _code="${_ERROR}";
856   case "$#" in
857     0) true; ;;
858     1) echo2 'groffer error: '"$1"; ;;
859     2)
860       echo2 'groffer error: '"$1";
861       _code="$2";
862       ;;
863     *) echo2 'groffer error: wrong number of arguments in error().'; ;;
864   esac;
865   if test "${_DEBUG}" = 'yes'; then
866     func_stack_dump;
867   fi;
868   clean_up;
869   kill "${_PROCESS_ID}" >/dev/null 2>&1;
870   kill -9 "${_PROCESS_ID}" >/dev/null 2>&1;
871   exit "${_code}";
872 }
873
874
875 #############
876 # abort (<text>*)
877 #
878 # Terminate program with error condition
879 #
880 abort()
881 {
882   error "Program aborted.";
883   exit 1;
884 }
885
886
887 #############
888 # func_check (<func_name> <rel_op> <nr_args> "$@")
889 #
890 # Check number of arguments and register to _FUNC_STACK.
891 #
892 # Arguments: >=3
893 #   <func_name>: name of the calling function.
894 #   <rel_op>:    a relational operator: = != < > <= >= 
895 #   <nr_args>:   number of arguments to be checked against <operator>
896 #   "$@":        the arguments of the calling function.
897 #
898 func_check()
899 {
900   local _comp;
901   local _fname;
902   local _nargs;
903   local _op;
904   local _s;
905   if test "$#" -lt 3; then
906     error 'func_check() needs at least 3 arguments.';
907   fi;
908   _fname="$1";
909   case "$3" in
910     1)
911       _nargs="$3";
912       _s='';
913       ;;
914     0|[2-9])
915       _nargs="$3";
916       _s='s';
917       ;;
918     *)
919       error "func_check(): third argument must be a digit.";
920       ;;
921   esac;
922   case "$2" in
923     '='|'-eq')
924       _op='-eq';
925       _comp='exactly';
926       ;;
927     '>='|'-ge')
928       _op='-ge';
929       _comp='at least';
930       ;;
931     '<='|'-le')
932       _op='-le';
933       _comp='at most';
934       ;;
935     '<'|'-lt')
936       _op='-lt';
937       _comp='less than';
938       ;;
939     '>'|'-gt')
940       _op='-gt';
941       _comp='more than';
942       ;;
943     '!='|'-ne')
944       _op='-ne';
945       _comp='not';
946       ;;
947     *) 
948       error \
949         'func_check(): second argument is not a relational operator.';
950       ;;
951   esac;
952   shift 3;
953   if test "$#" "${_op}" "${_nargs}"; then
954     do_nothing;
955   else
956     error \
957       "${_fname}"'() needs '"${_comp} ${_nargs}"' argument'"${_s}"'.';
958   fi;
959   if test "${_DEBUG}" = 'yes'; then
960     func_push "${_fname} $*";
961   fi;
962 }
963
964
965 #############
966 # func_pop ()
967 #
968 # Retrieve the top element from the stack.
969 #
970 # The stack elements are separated by `!'; the popped element is
971 # identical to the original element, except that all `!' characters
972 # were removed.
973 #
974 # Arguments: 1
975 #
976 func_pop()
977 {
978   if test "${_DEBUG}" = 'yes'; then
979     if test "$#" -ne 0; then
980       error 'func_pop() does not have arguments.';
981     fi;
982     case "${_FUNC_STACK}" in
983       '')
984         error 'func_pop(): stack is empty.';
985         ;;
986       *!*)
987         # split at first bang `!'.
988         _FUNC_STACK="$(echo -n ${_FUNC_STACK} \
989                        | sed -e 's/^[^!]*!//')";
990         ;;
991       *)
992         _FUNC_STACK='';
993         ;;
994     esac;
995   fi;
996 }
997
998
999 #############
1000 # func_push (<element>)
1001 #
1002 # Store another element to stack.
1003 #
1004 # The stack elements are separated by `!'; if <element> contains a `!'
1005 # it is removed first.
1006 #
1007 # Arguments: 1
1008 #
1009 func_push()
1010 {
1011   local _element;
1012   if test "${_DEBUG}" = 'yes'; then
1013     if test "$#" -ne 1; then
1014       error 'func_push() needs 1 argument.';
1015     fi;
1016     case "$1" in
1017       *'!'*)
1018         # remove all bangs `!'.
1019         _element="$(echo -n "$1" | sed -e 's/!//g')";
1020         ;;
1021       *)
1022         _element="$1";
1023         ;;
1024     esac;
1025     if test "${_FUNC_STACK}" = ''; then
1026       _FUNC_STACK="${_element}";
1027     else
1028       _FUNC_STACK="${_element}!${_FUNC_STACK}";
1029     fi;
1030   fi;
1031 }
1032
1033
1034 #############
1035 # func_stack_dump ()
1036 #
1037 # Print the content of the stack.  Ignore the arguments.
1038 #
1039 func_stack_dump()
1040 {
1041   diag 'call stack:';
1042   case "${_FUNC_STACK}" in
1043     *!*)
1044       _rest="${_FUNC_STACK}";
1045       while test "${_rest}" != ''; do
1046         # get part before the first bang `!'.
1047         diag "$(echo -n "${_rest}" | sed -e 's/^\([^!]*\)!.*$/\1/')";
1048         # delete part up to the first bang `!'.
1049         _rest="$(echo -n "${_rest}" | sed -e 's/^!*[^!]*!*//')";
1050       done;
1051       ;;
1052     *)
1053       diag "${_FUNC_STACK}";
1054       ;;
1055   esac;
1056 }
1057
1058
1059 ########################################################################
1060 #                        System Test
1061 ########################################################################
1062
1063 landmark "2: system test";
1064
1065 # Test the availability of the system utilities used in this script.
1066
1067
1068 ########################################################################
1069 # Test of `true'.
1070 #
1071 if true >/dev/null 2>&1; then
1072   true;
1073 else
1074   true()
1075   {
1076     return "${_GOOD}";
1077   }
1078
1079   false()
1080   {
1081     return "${_BAD}";
1082   }
1083 fi;
1084
1085
1086 ########################################################################
1087 # Test of `unset'.
1088 #
1089 _test='test';
1090 if unset _test >/dev/null 2>&1 && test "${_test}" = ''; then
1091   true;
1092 else
1093   unset()
1094   {
1095     for v in "$@"; do
1096       eval "$v"='';
1097     done;
1098   }
1099 fi;
1100 unset _test;
1101
1102 ########################################################################
1103 # Test of builtin `local'
1104 #
1105
1106 _t_e_s_t_f_u_n_c_()
1107 {
1108   local _test >/dev/null 2>&1 || return "${_BAD}";
1109 }
1110
1111 if _t_e_s_t_f_u_n_c_; then
1112   :
1113 else
1114   local()
1115   {
1116     if test "$1" != ''; then
1117       error "overriding global variable \`$1' with local value.";
1118     fi;
1119   }
1120 fi;
1121
1122
1123 ########################################################################
1124 # Test of global setting in functions
1125 #
1126 _global='outside';
1127 _clobber='outside';
1128
1129 _t_e_s_t_f_u_n_c_()
1130 {
1131   local _clobber;
1132   _global='inside';
1133   _clobber='inside';
1134 }
1135
1136 _t_e_s_t_f_u_n_c_;
1137 if test "${_global}" != 'inside' || test "${_clobber}" != 'outside';
1138 then
1139   error "Cannot assign to global variables from within functions.";
1140 fi;
1141
1142 unset _global;
1143 unset _clobber;
1144
1145
1146 ########################################################################
1147 # Test of function `sed'.
1148 #
1149 if test "$(echo xTesTx \
1150            | sed -e 's/^.\([Tt]e*x*sTT*\).*$/\1/' \
1151            | sed -e '\|T|s||t|g')" != 'test';
1152 then
1153   error 'Test of "sed" command failed.';
1154 fi;
1155
1156
1157 ########################################################################
1158 # Test of function `cat'.
1159 #
1160 if test "$(echo test | cat)" != "test"; then
1161   error 'Test of "cat" command failed.';
1162 fi;
1163
1164
1165 ########################################################################
1166 # Test for compression.
1167 #
1168 if test "$(echo 'test' | gzip -c -d -f - 2>/dev/null)" = 'test'; then
1169   _HAS_COMPRESSION='yes';
1170   if echo 'test' | bzip2 -c 2>/dev/null | bzip2 -t 2>/dev/null \
1171      && test "$(echo 'test' | bzip2 -c 2>/dev/null \
1172                             | bzip2 -d -c 2>/dev/null)" \
1173              = 'test'; then
1174     _HAS_BZIP='yes';
1175   else
1176     _HAS_BZIP='no';
1177   fi;
1178 else
1179   _HAS_COMPRESSION='no';
1180   _HAS_BZIP='no';
1181 fi;
1182
1183
1184 ########################################################################
1185 _t_e_s_t_f_u_n_c_()
1186 {
1187   :
1188 }
1189
1190
1191 ########################################################################
1192 #                   Definition of normal Functions
1193 ########################################################################
1194 landmark "3: functions";
1195
1196 ########################################################################
1197 # abort (<text>*)
1198 #
1199 # Unconditionally terminate the program with error code;
1200 # useful for debugging.
1201 #
1202 # defined above
1203
1204
1205 ########################################################################
1206 # base_name (<path>)
1207 #
1208 # Get the file name part of <path>, i.e. delete everything up to last
1209 # `/' from the beginning of <path>.
1210 #
1211 # Arguments : 1
1212 # Output    : the file name part (without slashes)
1213 #
1214 base_name()
1215 {
1216   func_check base_name = 1 "$@";
1217   case "$1" in
1218     */)
1219       do_nothing;
1220       ;;
1221     */*)
1222       # delete everything up to last slash `/'.
1223       echo -n "$1" | sed -e '\|^.*/*\([^/]*\)$|s||\1|';
1224       ;;
1225     *)
1226       echo -n "$1";
1227       ;;
1228   esac;
1229   eval "${return_ok}";
1230 }
1231
1232
1233 ########################################################################
1234 # catz (<file>)
1235 #
1236 # Decompress if possible or just print <file> to standard output.
1237 #
1238 # gzip, bzip2, and .Z decompression is supported.
1239 #
1240 # Arguments: 1, a file name.
1241 # Output: the content of <file>, possibly decompressed.
1242 #
1243 if test "${_HAS_COMPRESSION}" = 'yes'; then
1244   catz()
1245   {
1246     func_check catz = 1 "$@";
1247     case "$1" in
1248       '')
1249         error 'catz(): empty file name';
1250         ;;
1251       '-')
1252         error 'catz(): for standard input use save_stdin()';
1253         ;;
1254     esac;
1255     if is_yes "${_HAS_BZIP}"; then
1256       if bzip2 -t "$1" 2>/dev/null; then
1257         bzip2 -c -d "$1" 2>/dev/null;
1258         eval "${return_ok}";
1259       fi;
1260     fi;
1261     gzip -c -d -f "$1" 2>/dev/null;
1262     eval "${return_ok}";
1263   }
1264 else
1265   catz()
1266   {
1267     func_check catz = 1 "$@";
1268     cat "$1";
1269     eval "${return_ok}";
1270   }
1271 fi;
1272
1273
1274 ########################################################################
1275 # clean_up ()
1276 #
1277 # Do the final cleaning up before exiting; used by the trap calls.
1278 #
1279 # defined above
1280
1281
1282 ########################################################################
1283 # clean_up_secondary ()
1284 #
1285 # Do the second but final cleaning up.
1286 #
1287 # defined above
1288
1289
1290 ########################################################################
1291 # diag (<text>*)
1292 #
1293 # Print marked message to standard error; useful for debugging.
1294 #
1295 # defined above
1296
1297
1298 ########################################################################
1299 landmark '4: dirname()*';
1300 ########################################################################
1301
1302 #######################################################################
1303 # dirname_append (<dir> <name>)
1304 #
1305 # Append `name' to `dir' with clean handling of `/'.
1306 #
1307 # Arguments : 2
1308 # Output    : the generated new directory name <dir>/<name>
1309 #
1310 dirname_append()
1311 {
1312   func_check dirname_append = 2 "$@";
1313   local _res;
1314   if is_empty "$1"; then
1315     error "dir_append(): first argument is empty.";
1316   fi;
1317   if is_empty "$2"; then
1318     echo -n "$1";
1319   else
1320     dirname_chop "$1"/"$2";
1321   fi;
1322   eval "${return_ok}";
1323 }
1324
1325
1326 ########################################################################
1327 # dirname_chop (<name>)
1328 #
1329 # Remove unnecessary slashes from directory name.
1330 #
1331 # Argument: 1, a directory name.
1332 # Output:   path without double, or trailing slashes.
1333 #
1334 dirname_chop()
1335 {
1336   func_check dirname_chop = 1 "$@";
1337   local _arg;
1338   local _res;
1339   local _sep;
1340   # replace all multiple slashes by a single slash `/'.
1341   _res="$(echo -n "$1" | sed -e '\|///*|s||/|g')";
1342   case "${_res}" in
1343     ?*/)
1344       # remove trailing slash '/';
1345       echo -n "${_res}" | sed -e '\|/$|s|||';
1346       ;;
1347     *) echo -n "${_res}"; ;;
1348   esac;
1349   eval "${return_ok}";
1350 }
1351
1352
1353 ########################################################################
1354 # do_filearg (<filearg>)
1355 #
1356 # Append the file, man-page, or standard input corresponding to the
1357 # argument to the temporary file.  If this is compressed in the gzip
1358 # or Z format it is decompressed.  A title element is generated.
1359 #
1360 # Argument either:
1361 #   - name of an existing files.
1362 #   - `-' to represent standard input (several times allowed).
1363 #   - `man:name.(section)' the man-page for `name' in `section'.
1364 #   - `man:name.section' the man-page for `name' in `section'.
1365 #   - `man:name' the man-page for `name' in the lowest `section'.
1366 #   - `name.section' the man-page for `name' in `section'.
1367 #   - `name' the man-page for `name' in the lowest `section'.
1368 # Globals :
1369 #   $_TMP_STDIN, $_MAN_ENABLE, $_MAN_IS_SETUP, $_OPT_MAN
1370 #
1371 # Output  : none
1372 # Return  : $_GOOD if found, ${_BAD} otherwise.
1373 #
1374 do_filearg()
1375 {
1376   func_check do_filearg = 1 "$@";
1377   local _filespec;
1378   local i;
1379   _filespec="$1";
1380   # store sequence into positional parameters
1381   case "${_filespec}" in
1382     '')
1383        eval "${return_good}";
1384        ;;
1385     '-')
1386       register_file '-';
1387       eval "${return_good}";
1388       ;;
1389     */*)                        # with directory part; so no man search
1390       set -- 'File';
1391       ;;
1392     *)
1393       if is_yes "${_MAN_ENABLE}"; then
1394         if is_yes "${_MAN_FORCE}"; then
1395           set -- 'Manpage' 'File';
1396         else
1397           set -- 'File' 'Manpage';
1398         fi;
1399       else
1400         set -- 'File';
1401       fi;
1402       ;;
1403   esac;
1404   for i in "$@"; do
1405     case "$i" in
1406       File)
1407         if test -f "${_filespec}"; then
1408           if test -r "${_filespec}"; then
1409             register_file "${_filespec}";
1410             eval "${return_good}";
1411           else
1412             echo2 "could not read \`${_filespec}'";
1413             eval "${return_bad}";
1414           fi;
1415         else
1416           continue;
1417         fi;
1418         ;;
1419       Manpage)                  # parse filespec as man page
1420         if is_not_yes "${_MAN_IS_SETUP}"; then
1421           man_setup;
1422         fi;
1423         if man_do_filespec "${_filespec}"; then
1424           eval "${return_good}";
1425         else
1426           continue;
1427         fi;
1428         ;;
1429     esac;
1430   done;
1431   eval "${return_bad}";
1432 } # do_filearg()
1433
1434
1435 ########################################################################
1436 # do_nothing ()
1437 #
1438 # Dummy function.
1439 #
1440 do_nothing()
1441 {
1442   return "${_OK}";
1443 }
1444
1445
1446 ########################################################################
1447 # echo2 (<text>*)
1448 #
1449 # Print to standard error with final line break.
1450 #
1451 # defined above
1452
1453
1454 ########################################################################
1455 # echo2n (<text>*)
1456 #
1457 # Print to standard error without final line break.
1458 #
1459 # defined above
1460
1461
1462 ########################################################################
1463 # error (<text>*)
1464 #
1465 # Print error message and exit with error code.
1466 #
1467 # defined above
1468
1469
1470 ########################################################################
1471 # func_check (<func_name> <rel_op> <nr_args> "$@")
1472 #
1473 # Check number of arguments and register to _FUNC_STACK.
1474 #
1475 # Arguments: >=3
1476 #   <func_name>: name of the calling function.
1477 #   <rel_op>:    a relational operator: = != < > <= >= 
1478 #   <nr_args>:   number of arguments to be checked against <operator>
1479 #   "$@":        the arguments of the calling function.
1480 #
1481 # defined above
1482
1483 #########################################################################
1484 # func_pop ()
1485 #
1486 # Delete the top element from the function call stack.
1487 #
1488 # defined above
1489
1490
1491 ########################################################################
1492 # func_push (<element>)
1493 #
1494 # Store another element to function call stack.
1495 #
1496 # defined above
1497
1498
1499 ########################################################################
1500 # func_stack_dump ()
1501 #
1502 # Print the content of the stack.
1503 #
1504 # defined above
1505
1506
1507 ########################################################################
1508 # get_first_essential (<arg>*)
1509 #
1510 # Retrieve first non-empty argument.
1511 #
1512 # Return  : `1' if all arguments are empty, `0' if found.
1513 # Output  : the retrieved non-empty argument.
1514 #
1515 get_first_essential()
1516 {
1517   func_check get_first_essential '>=' 0 "$@";
1518   local i;
1519   if test "$#" -eq 0; then
1520     eval "${return_ok}";
1521   fi;
1522   for i in "$@"; do
1523     if is_not_empty "$i"; then
1524       echo -n "$i";
1525       eval "${return_ok}";
1526     fi;
1527   done;
1528   eval "${return_bad}";
1529 }
1530
1531
1532 ########################################################################
1533 landmark '5: is_*()';
1534 ########################################################################
1535
1536 ########################################################################
1537 # is_dir (<name>)
1538 #
1539 # Test whether `name' is a directory.
1540 #
1541 # Arguments : 1
1542 # Return    : `0' if arg1 is a directory, `1' otherwise.
1543 #
1544 is_dir()
1545 {
1546   func_check is_dir = 1 "$@";
1547   if is_not_empty "$1" && test -d "$1" && test -r "$1"; then
1548     eval "${return_yes}";
1549   else
1550     eval "${return_no}";
1551   fi;
1552   eval "${return_ok}";
1553 }
1554
1555
1556 ########################################################################
1557 # is_empty (<string>)
1558 #
1559 # Test whether `string' is empty.
1560 #
1561 # Arguments : <=1
1562 # Return    : `0' if arg1 is empty or does not exist, `1' otherwise.
1563 #
1564 is_empty()
1565 {
1566   func_check is_empty = 1 "$@";
1567   if test -z "$1"; then
1568     eval "${return_yes}";
1569   else
1570     eval "${return_no}";
1571   fi;
1572   eval "${return_ok}";
1573 }
1574
1575
1576 ########################################################################
1577 # is_equal (<string1> <string2>)
1578 #
1579 # Test whether `string1' is equal to <string2>.
1580 #
1581 # Arguments : 2
1582 # Return    : `0' both arguments are equal strings, `1' otherwise.
1583 #
1584 is_equal()
1585 {
1586   func_check is_equal = 2 "$@";
1587   if test "$1" = "$2"; then
1588     eval "${return_yes}";
1589   else
1590     eval "${return_no}";
1591   fi;
1592   eval "${return_ok}";
1593 }
1594
1595
1596 ########################################################################
1597 # is_file (<name>)
1598 #
1599 # Test whether `name' is a readable file.
1600 #
1601 # Arguments : 1
1602 # Return    : `0' if arg1 is a readable file, `1' otherwise.
1603 #
1604 is_file()
1605 {
1606   func_check is_file = 1 "$@";
1607   if is_not_empty "$1" && test -f "$1" && test -r "$1"; then
1608     eval "${return_yes}";
1609   else
1610     eval "${return_no}";
1611   fi;
1612   eval "${return_ok}";
1613 }
1614
1615
1616 ########################################################################
1617 # is_not_dir (<name>)
1618 #
1619 # Test whether `name' is not a readable directory.
1620 #
1621 # Arguments : 1
1622 # Return    : `0' if arg1 is a directory, `1' otherwise.
1623 #
1624 is_not_dir()
1625 {
1626   func_check is_not_dir = 1 "$@";
1627   if is_dir "$1"; then
1628     eval "${return_no}";
1629   else
1630     eval "${return_yes}";
1631   fi;
1632   eval "${return_ok}";
1633 }
1634
1635
1636 ########################################################################
1637 # is_not_empty (<string>)
1638 #
1639 # Test whether `string' is not empty.
1640 #
1641 # Arguments : <=1
1642 # Return    : `0' if arg1 exists and is not empty, `1' otherwise.
1643 #
1644 is_not_empty()
1645 {
1646   func_check is_not_empty = 1 "$@";
1647   if is_empty "$1"; then
1648     eval "${return_no}";
1649   else
1650     eval "${return_yes}";
1651   fi;
1652   eval "${return_ok}";
1653 }
1654
1655
1656 ########################################################################
1657 # is_not_equal (<string1> <string2>)
1658 #
1659 # Test whether `string1' and <string2> differ.
1660 #
1661 # Arguments : 2
1662 #
1663 is_not_equal()
1664 {
1665   func_check is_not_equal = 2 "$@";
1666   if is_equal "$1" "$2"; then
1667     eval "${return_no}";
1668   else
1669     eval "${return_yes}";
1670   fi;
1671   eval "${return_ok}";
1672 }
1673
1674
1675 ########################################################################
1676 # is_not_file (<filename>)
1677 #
1678 # Test whether `name' is a not readable file.
1679 #
1680 # Arguments : >=1 (empty allowed), more args are ignored
1681 #
1682 is_not_file()
1683 {
1684   func_check is_not_file '>=' 1 "$@";
1685   if is_file "$1"; then
1686     eval "${return_no}";
1687   else
1688     eval "${return_yes}";
1689   fi;
1690   eval "${return_ok}";
1691 }
1692
1693
1694 ########################################################################
1695 # is_not_prog (<name>)
1696 #
1697 # Verify that arg is a not program in $PATH.
1698 #
1699 # Arguments : >=1 (empty allowed)
1700 #   more args are ignored, this allows to specify progs with arguments
1701 #
1702 is_not_prog()
1703 {
1704   func_check is_not_prog '>=' 1 "$@";
1705   if where "$1" >/dev/null; then
1706     eval "${return_no}";
1707   else
1708     eval "${return_yes}";
1709   fi;
1710   eval "${return_ok}";
1711 }
1712
1713
1714 ########################################################################
1715 # is_not_yes (<string>)
1716 #
1717 # Test whether `string' is not "yes".
1718 #
1719 # Arguments : 1
1720 #
1721 is_not_yes()
1722 {
1723   func_check is_not_yes = 1 "$@";
1724   if test "$1" = 'yes'; then
1725     eval "${return_no}";
1726   else
1727     eval "${return_yes}";
1728   fi;
1729   eval "${return_ok}";
1730 }
1731
1732
1733 ########################################################################
1734 # is_prog (<name>)
1735 #
1736 # Determine whether arg is a program in $PATH
1737 #
1738 # Arguments : >=1 (empty allowed)
1739 #   more args are ignored, this allows to specify progs with arguments
1740 #
1741 is_prog()
1742 {
1743   func_check is_prog '>=' 1 "$@";
1744   if where "$1" >/dev/null; then
1745     eval "${return_yes}";
1746   else
1747     eval "${return_no}";
1748   fi;
1749   eval "${return_ok}";
1750 }
1751
1752
1753 ########################################################################
1754 # is_yes (<string>)
1755 #
1756 # Test whether `string' has value "yes".
1757 #
1758 # Arguments : <=1
1759 # Return    : `0' if arg1 is `yes', `1' otherwise.
1760 #
1761 is_yes()
1762 {
1763   func_check is_yes = 1 "$@";
1764   if test "$1" = 'yes'; then
1765     eval "${return_yes}";
1766   else
1767     eval "${return_no}";
1768   fi;
1769   eval "${return_ok}";
1770 }
1771
1772
1773 ########################################################################
1774 # landmark ()
1775 #
1776 # Print debugging information on standard error if $_DEBUG_LM is `yes'.
1777 #
1778 # Globals: $_DEBUG_LM
1779 #
1780 # Defined in section `Debugging functions'.
1781
1782
1783 ########################################################################
1784 # leave ()
1785 #
1786 # Clean exit without an error.
1787 #
1788 leave()
1789 {
1790   clean_up;
1791   exit "${_OK}";
1792 }
1793
1794
1795 ########################################################################
1796 landmark '6: list_*()';
1797 ########################################################################
1798
1799 ########################################################################
1800 # list_append (<list> <element>...)
1801 #
1802 # Arguments: >=2
1803 #   <list>:   a space-separated list of single-quoted elements.
1804 #   <element>: some sequence of characters.
1805 # Output:
1806 #   if <list> is empty:  "'<element>' '...'"
1807 #   otherwise:           "<list> '<element>' ..."
1808 #
1809 list_append()
1810 {
1811   func_check list_append '>=' 2 "$@";
1812   local _element;
1813   local _res;
1814   _res="$1";
1815   shift;
1816   for s in "$@"; do
1817     case "$s" in
1818       *\'*)
1819         # escape each single quote by replacing each "'" (squote)
1820         # by "'\''" (squote bslash squote squote);
1821         # note that the backslash must be doubled for `sed'.
1822         _element="$(echo -n "$s" | sed -e 's/'"${_SQUOTE}"'/&\\&&/g')";
1823         ;;
1824       *)
1825         _element="$s";
1826         ;;
1827     esac;
1828     _res="${_res} '${_element}'";
1829   done;
1830   echo -n "${_res}";
1831   eval "${return_ok}";
1832 }
1833
1834
1835 ########################################################################
1836 # list_check (<list>)
1837 #
1838 # Check whether <list> is a space-separated list of '-quoted elements.
1839 #
1840 # If the test fails an error is raised.
1841 # If the test succeeds the argument is echoed.
1842 #
1843 # Testing criteria:
1844 #   A list has the form "'first' 'second' '...' 'last'".
1845 #   So it has a leading and a final quote and the elements are separated
1846 #   by "' '" constructs.  If these are all removed there should not be
1847 #   any single-quotes left.  Watch out for escaped single quotes; they
1848 #   have the form '\'' (sq bs sq sq).
1849 #
1850 # Arguments: 1
1851 # Output: the argument <list> unchanged, it the check succeeded.
1852 #
1853 list_check()
1854 {
1855   func_check list_check = 1 "$@";
1856   local _list;
1857   if is_empty "$1"; then
1858     eval "${return_ok}";
1859   fi;
1860   case "$1" in
1861     \'*\') _list="$1"; ;;
1862     *)
1863       error "list_check() bad list: $1"
1864       ;;
1865   esac;
1866   # Remove leading single quote,
1867   # remove final single quote,
1868   # remove escaped single quotes (squote bslash squote squote)
1869   #   [note that `sed' doubles the backslash (bslash bslash)],
1870   # remove field separators (squote space squote).
1871   _list="$(echo -n "${_list}" \
1872     | sed -e 's/^'"${_SQUOTE}"'//' \
1873     | sed -e 's/'"${_SQUOTE}"'$//' \
1874     | sed -e \
1875         's/'"${_SQUOTE}${_BSLASH}${_BSLASH}${_SQUOTE}${_SQUOTE}"'//g' \
1876     | sed -e 's/'"${_SQUOTE}${_SPACE}${_SPACE}"'*'"${_SQUOTE}"'//g')";
1877   case "${_list}" in
1878     *\'*)                       # criterium fails if squote is left
1879       error 'list_check() bad list: '"${_list}";
1880       ;;
1881   esac;
1882   echo -n "$1";
1883   eval "${return_ok}";
1884 }
1885
1886
1887 ########################################################################
1888 # list_element_from_arg (<arg>)
1889 #
1890 # Arguments: 1
1891 #   <arg>: some sequence of characters (also single quotes allowed).
1892 # Output: the list element generated from <arg>.
1893 #
1894 list_element_from_arg()
1895 {
1896   func_check list_element_from_arg = 1 "$@";
1897   local _res;
1898   echo -n "'";
1899   # replace each single quote "'" by "'\''". 
1900   echo -n "$1" | sed -e 's/'\''/&\\&&/g'; # ' for emacs
1901   echo -n "'";
1902   eval "${return_ok}";
1903 }
1904
1905
1906 ########################################################################
1907 # list_from_args (<arg>...)
1908 #
1909 # Generate a space-separated list of single-quoted elements from args.
1910 #
1911 # Arguments:
1912 #   <arg>: some sequence of characters.
1913 # Output: "'<arg>' '...'"
1914 #
1915 list_from_args()
1916 {
1917   func_check list_from_args '>=' 1 "$@";
1918   local _list;
1919   _list="";
1920   for s in "$@"; do
1921     _list="$(list_append "${_list}" "$s")";
1922   done;
1923   echo -n "${_list}";
1924   eval "${return_ok}";
1925 }
1926
1927
1928 ########################################################################
1929 # list_from_cmdline (<s_n> <s_a> <l_n> <l_n> [<cmdline_arg>...])
1930 #
1931 # Transform command line arguments into a normalized form.
1932 #
1933 # Options, option arguments, and file parameters are identified and
1934 # output each as a single-quoted argument of its own.  Options and
1935 # file parameters are separated by a '--' argument.
1936 #
1937 # Arguments: >=4
1938 #   <s_n>: space-separated list of short options without an arg.
1939 #   <s_a>: space-separated list of short options that have an arg.
1940 #   <l_n>: space-separated list of long options without an arg.
1941 #   <l_a>: space-separated list of long options that have an arg.
1942 #   <cmdline_arg>...: the arguments from the command line (by "$@").
1943 #
1944 # Globals: $POSIXLY_CORRECT (only kept for compatibility).
1945 #
1946 # Output: ['-[-]opt' ['optarg']]... '--' ['filename']...
1947 #
1948 # Example:
1949 #     list_normalize 'a b' 'c' '' 'long' -a f1 -bcarg --long=larg f2
1950 #   will result in printing:
1951 #     '-a' '-b' '-c' 'arg' '--long' 'larg' '--' 'f1' 'f2'
1952 #   If $POSIXLY_CORRECT is not empty, the result will be:
1953 #     '-a' '--' 'f1' '-bcarg' '--long=larg' 'f2'
1954 #
1955 #   Rationale:
1956 #     In POSIX, the first non-option ends the option processing.
1957 #     In GNU mode (default), non-options are sorted behind the options.
1958 #
1959 #   Use this function only in the following way:
1960 #     eval set -- "$(args_norm '...' '...' '...' '...' "$@")";
1961 #     while test "$1" != '--'; do
1962 #       case "$1" in
1963 #       ...
1964 #       esac;
1965 #       shift;
1966 #     done;
1967 #     shift;         #skip '--'
1968 #     # all positional parameters ("$@") left are file name parameters.
1969 #
1970 list_from_cmdline()
1971 {
1972   func_check list_from_cmdline '>=' 4 "$@";
1973   local _fparams;
1974   local _fn;
1975   local _result;
1976   local _long_a;
1977   local _long_n;
1978   local _short_a;
1979   local _short_n;
1980   _short_n="$(list_check "$1")"; # short options, no argument
1981   _short_a="$(list_check "$2")"; # short options with argument
1982   _long_n="$(list_check "$3")";  # long options, no argument
1983   _long_a="$(list_check "$4")";  # long options with argument
1984   shift 4;
1985   _fn='list_from_cmdline():';    # for error messages
1986   if test "$#" -eq 0; then
1987     echo -n "'--'";
1988     eval "${return_ok}";
1989   fi;
1990   _fparams='';
1991   _result='';
1992   while test "$#" -ge 1; do
1993     _arg="$1";
1994     shift;
1995     case "$_arg" in
1996       --) break; ;;
1997       --?*)
1998         # delete leading '--';
1999         _opt="$(echo -n "${_arg}" | sed -e 's/^..//')";
2000         if list_has "${_long_n}" "${_opt}"; then
2001           # long option, no argument
2002           _result="$(list_append "${_result}" "--${_opt}")";
2003           continue;
2004         fi;
2005         if list_has "${_long_a}" "${_opt}"; then
2006           # long option with argument
2007           _result="$(list_append "${_result}" "--${_opt}")";
2008           if test "$#" -le 0; then
2009             error "${_fn} no argument for option --${_opt}."
2010           fi;
2011           _result="$(list_append "${_result}" "$1")";
2012           shift;
2013           continue;
2014         fi;
2015         # test on `--opt=arg'
2016         if string_contains "${_opt}" '='; then
2017           # extract option by deleting from the first '=' to the end
2018           _lopt="$(echo -n "${_opt}" | sed -e 's/=.*$//')";
2019           if list_has "${_long_a}" "${_lopt}"; then
2020             # get the option argument by deleting up to first `='
2021             _optarg="$(echo -n "${_opt}" | sed -e 's/^[^=]*=//')";
2022             _result="$(list_append "${_result}" \
2023                                    "--${_lopt}" "${_optarg}")";
2024             continue;
2025           fi;
2026         fi;
2027         error "${_fn} --${_opt} is not an option."
2028         ;;
2029       -?*)                      # short option (cluster)
2030         # delete leading `-';
2031         _rest="$(echo -n "${_arg}" | sed -e 's/^.//')";
2032         while is_not_empty "${_rest}"; do
2033           # get next short option from cluster (first char of $_rest)
2034           _optchar="$(echo -n "${_rest}" | sed -e 's/^\(.\).*$/\1/')";
2035           # remove first character from ${_rest};
2036           _rest="$(echo -n "${_rest}" | 's/^.//')";
2037           if list_has "${_short_n}" "${_optchar}"; then
2038             _result="$(list_append "${_result}" "-${_optchar}")";
2039             continue;
2040           elif list_has "${_short_a}" "${_optchar}"; then
2041             # remove leading character
2042             case "${_optchar}" in
2043               /)                # cannot use normal `sed' separator
2044                 _rest="$(echo -n "${_rest}" | sed -e '\|^.|s|||')";
2045                 ;;
2046               ?)
2047                 _rest="$(echo -n "${_rest}" | sed -e 's/^.//')";
2048                 ;;
2049               *)
2050                 error "${_fn} several chars parsed for short option."
2051                 ;;
2052             esac;
2053             if is_empty "${_rest}"; then
2054               if test "$#" -ge 1; then
2055                 _result="$(list_append "${_result}" \
2056                                           "-${_optchar}" "$1")";
2057                 shift;
2058                 continue;
2059               else
2060                 error \
2061                   "${_fn}"' no argument for option -'"${_optchar}."
2062               fi;
2063             else                # rest is the argument
2064               _result="$(list_append "${_result}" \
2065                                      "-${_optchar}" "${_rest}")";
2066               _rest='';
2067               continue;
2068             fi;
2069           else
2070             error "${_fn} unknown option -${_optchar}."
2071           fi;
2072         done;
2073         ;;
2074       *)
2075         # Here, $_arg is not an option, so a file parameter.
2076         # When $POSIXLY_CORRECT is set this ends option parsing;
2077         # otherwise, the argument is stored as a file parameter and
2078         # option processing is continued.
2079         _fparams="$(list_append "${_fparams}" "${_arg}")";
2080         if is_not_empty "$POSIXLY_CORRECT"; then
2081           break;
2082         fi;
2083         ;;
2084     esac;
2085   done;
2086   _result="$(list_append "${_result}" '--')";
2087   if is_not_empty "${_fparams}"; then
2088     _result="${_result} ${_fparams}";
2089   fi;
2090   if test "$#" -gt 0; then
2091     _result="$(list_append "${_result}" "$@")";
2092   fi;
2093   echo -n "$_result";
2094   eval "${return_ok}";
2095 } # list_from_cmdline()
2096
2097
2098 ########################################################################
2099 # list_from_lists (<list1> <list2>...)
2100 #
2101 # Generate a list from the concatenation of the lists in the arguments.
2102 #
2103 # Arguments: >=2
2104 #   <list*>: string of space-separated single-quoted elements.
2105 # Output: "'<element1_of_list1>' ..."
2106 #
2107 list_from_lists()
2108 {
2109   func_check list_from_lists '>=' 2 "$@";
2110   _list='';
2111   echo -n "$*";
2112   eval "${return_ok}";
2113 }
2114
2115 ########################################################################
2116 # list_from_split (<string> <separator>)
2117 #
2118 # In <string> escape white space and replace each <separator> by space.
2119 #
2120 # Arguments: 2: a <string> that is to be split into parts divided by
2121 #               <separator>
2122 # Output:    the resulting string
2123 #
2124 list_from_split()
2125 {
2126   func_check list_from_split = 2 "$@";
2127   local _s;
2128
2129   # precede each space or tab by a backslash `\' (doubled for `sed')
2130   _s="$(echo -n "$1" | sed -e 's/\(['"${_SPACE}${_TAB}"']\)/\\\1/g')";
2131
2132   # replace split character of string by the list separator ` ' (space).
2133   case "$2" in
2134     /)                          # cannot use normal `sed' separator
2135       echo -n "${_s}" | sed -e '\|'"$2"'|s|| |g';
2136       ;;
2137     ?)                          # use normal `sed' separator
2138       echo -n "${_s}" | sed -e 's/'"$2"'/ /g';
2139       ;;
2140     ??*)
2141       error 'list_from_split(): separator must be a single character.';
2142       ;;
2143   esac;
2144   eval "${return_ok}";
2145 }
2146
2147
2148 ########################################################################
2149 # list_has (<list> <element>)
2150 #
2151 # Arguments: 2
2152 #   <list>:    a space-separated list of single-quoted elements.
2153 #   <element>: some sequence of characters.
2154 # Output:
2155 #   if <list> is empty:  "'<element>' '...'"
2156 #   otherwise:           "<list> '<element>' ..."
2157 #
2158 list_has()
2159 {
2160   func_check list_has = 2 "$@";
2161   if is_empty "$1"; then
2162     eval "${return_no}";
2163   fi;
2164   _list="$1";
2165   _element="$2";
2166   case "$2" in
2167     \'*\')  _element="$2"; ;;
2168     *)      _element="'$2'"; ;;
2169   esac;
2170   if string_contains "${_list}" "${_element}"; then
2171     eval "${return_yes}";
2172   else
2173     eval "${return_no}";
2174   fi;
2175   eval "${return_ok}";
2176 }
2177
2178
2179 ########################################################################
2180 # list_has_not (<list> <element>)
2181 #
2182 # Arguments: 2
2183 #   <list>:    a space-separated list of single-quoted elements.
2184 #   <element>: some sequence of characters.
2185 # Output:
2186 #   if <list> is empty:  "'<element>' '...'"
2187 #   otherwise:           "<list> '<element>' ..."
2188 #
2189 list_has_not()
2190 {
2191   func_check list_has_not = 2 "$@";
2192   if is_empty "$1"; then
2193     eval "${return_yes}";
2194   fi;
2195   _list="$1";
2196   _element="$2";
2197   case "$2" in
2198     \'*\')  _element="$2"; ;;
2199     *)      _element="'$2'"; ;;
2200   esac;
2201   if string_contains "${_list}" "${_element}"; then
2202     eval "${return_no}";
2203   else
2204     eval "${return_yes}";
2205   fi;
2206   eval "${return_ok}";
2207 }
2208
2209
2210 ########################################################################
2211 # list_length (<list>)
2212 #
2213 # Arguments: 1
2214 #   <list>:    a space-separated list of single-quoted elements.
2215 # Output: the number of elements in <list>
2216 #
2217 list_length()
2218 {
2219   func_check list_length = 1 "$@";
2220   eval set -- "$1";
2221   echo -n "$#";
2222   eval "${return_ok}";
2223 }
2224
2225
2226 ########################################################################
2227 # list_prepend (<list> <element>...)
2228 #
2229 # Insert new <element> at the beginning of <list>
2230 #
2231 # Arguments: >=2
2232 #   <list>:   a space-separated list of single-quoted elements.
2233 #   <element>: some sequence of characters.
2234 # Output:
2235 #   if <list> is empty:  "'<element>' ..."
2236 #   otherwise:           "'<element>' ... <list>"
2237 #
2238 list_prepend()
2239 {
2240   func_check list_prepend '>=' 2 "$@";
2241   local _res;
2242   _res="$1";
2243   shift;
2244   for s in "$@"; do
2245     # escape single quotes in list style (squote bslash squote squote).
2246     _element="$(echo -n "$s" | sed -e 's/'\''/&\\&&/g')";
2247     _res="'${_element}' ${_res}";
2248   done;
2249   echo -n "${_res}";
2250   eval "${return_ok}";
2251 }
2252
2253
2254 ########################################################################
2255 landmark '7: man_*()';
2256 ########################################################################
2257
2258 ########################################################################
2259 # man_do_filespec (<filespec>)
2260 #
2261 # Print suitable man page(s) for filespec to $_TMP_CAT.
2262 #
2263 # Arguments : 2
2264 #   <filespec>: argument of the form `man:name.section', `man:name',
2265 #               `man:name(section)', `name.section', `name'.
2266 #
2267 # Globals   : $_OPT_ALL
2268 #
2269 # Output    : none.
2270 # Return    : `0' if man page was found, `1' else.
2271 #
2272 # Only called from do_fileargs(), checks on $MANPATH and
2273 # $_MAN_ENABLE are assumed.
2274 #
2275 man_do_filespec()
2276 {
2277   func_check man_do_filespec = 1 "$@";
2278   local _got_one;
2279   local _name;
2280   local _prevsec;
2281   local _res;
2282   local _section;
2283   local _spec;
2284   local _string;
2285   local s;
2286   if is_empty "${MANPATH}"; then
2287     eval "${return_bad}";
2288   fi;
2289   if is_empty "$1"; then
2290     eval "${return_bad}";
2291   fi;
2292   _spec="$1";
2293   _name='';
2294   _section='';
2295   case "${_spec}" in
2296     */*)                        # not a man spec when it contains '/'
2297       eval "${return_bad}";
2298       ;;
2299     man:?*\(?*\))               # man:name(section)
2300       _name="$(echo -n "${_spec}" \
2301                | sed -e 's/^man:\(..*\)(\(..*\))$/\1/')";
2302       _section="$(echo -n "${_spec}" \
2303                | sed -e 's/^man:\(..*\)(\(..*\))$/\2/')";
2304       ;;
2305     man:?*.?*)                  # man:name.section
2306       _name="$(echo -n "${_spec}" \
2307                | sed -e 's/^man:\(..*\)\.\(..*\)$/\1/')";
2308       _section="$(echo -n "${_spec}" \
2309                | sed -e 's/^man:\(..*\)\.\(..*\)$/\2/')";
2310       ;;
2311     man:?*)                     # man:name
2312       _name="$(echo -n "${_spec}" | sed -e 's/^man://')";
2313       ;;
2314     ?*\(?*\))                   # name(section)
2315       _name="$(echo -n "${_spec}" \
2316                | sed -e 's/^\(..*\)(\(..*\))$/\1/')";
2317       _section="$(echo -n "${_spec}" \
2318                | sed -e 's/^\(..*\)(\(..*\))$/\2/')";
2319       ;;
2320     ?*.?*)                      # name.section
2321       _name="$(echo -n "${_spec}" \
2322                | sed -e 's/^\(..*\)\.\(..*\)$/\1/')";
2323       _section="$(echo -n "${_spec}" \
2324                | sed -e 's/^\(..*\)\.\(..*\)$/\2/')";
2325       ;;
2326     ?*)
2327       _name="${_filespec}";
2328       ;;
2329   esac;
2330   if is_empty "${_name}"; then
2331     eval "${return_bad}";
2332   fi;
2333   _got_one='no';
2334   if is_empty "${_section}"; then
2335     eval set -- "${_MAN_AUTO_SEC}";
2336     for s in "$@"; do
2337       if man_search_section "${_name}" "$s"; then # found
2338         if is_yes "${_MAN_ALL}"; then
2339           _got_one='yes';
2340         else
2341           eval "${return_good}";
2342         fi;
2343       fi;
2344     done;
2345   else
2346     if man_search_section "${_name}" "${_section}"; then
2347       eval "${return_good}";
2348     else
2349       eval "${return_bad}";
2350     fi;
2351   fi;
2352   if is_yes "${_MAN_ALL}" && is_yes "${_got_one}"; then
2353     eval "${return_good}";
2354   fi;
2355   eval "${return_bad}";
2356 } # man_do_filespec()
2357
2358
2359 ########################################################################
2360 # man_register_file (<file> <name> [<section>])
2361 #
2362 # Write a found man page file and register the title element.
2363 #
2364 # Arguments: 1, 2, or 3; maybe empty
2365 # Output: none
2366 #
2367 man_register_file()
2368 {
2369   func_check man_register_file '>=' 2 "$@";
2370   case "$#" in
2371     2|3) do_nothing; ;;
2372     *)
2373       error "man_register_file() expects 2 or 3 arguments.";
2374       ;;
2375   esac;
2376   if is_empty "$1"; then
2377     error 'man_register_file(): file name is empty';
2378   fi;
2379   to_tmp "$1";
2380   case "$#" in
2381     2)
2382        register_title "man:$2";
2383        eval "${return_ok}";
2384        ;;
2385     3)
2386        register_title "$2($3)";
2387        eval "${return_ok}";
2388        ;;
2389   esac;
2390   eval "${return_ok}";
2391 }
2392
2393
2394 ########################################################################
2395 # man_search_section (<name> <section>)
2396 #
2397 # Retrieve man pages.
2398 #
2399 # Arguments : 2
2400 # Globals   : $_MAN_PATH, $_MAN_EXT
2401 # Return    : 0 if found, 1 otherwise
2402 #
2403 man_search_section()
2404 {
2405   func_check man_search_section = 2 "$@";
2406   local _dir;
2407   local _ext;
2408   local _got_one;
2409   local _name;
2410   local _prefix
2411   local _section;
2412   local d;
2413   local f;
2414   if is_empty "${_MAN_PATH}"; then
2415     eval "${return_bad}";
2416   fi;
2417   if is_empty "$1"; then
2418     eval "${return_bad}";
2419   fi;
2420   if is_empty "$2"; then
2421     eval "${return_bad}";
2422   fi;
2423   _name="$1";
2424   _section="$2";
2425   eval set -- "$(path_split "${_MAN_PATH}")";
2426   _got_one='no';
2427   if is_empty "${_MAN_EXT}"; then
2428     for d in "$@"; do
2429       _dir="$(dirname_append "$d" "man${_section}")";
2430       if is_dir "${_dir}"; then
2431         _prefix="$(dirname_append "${_dir}" "${_name}.${_section}")";
2432         for f in $(echo -n ${_prefix}*); do
2433           if is_file "$f"; then
2434             if is_yes "${_got_one}"; then
2435               register_file "$f";
2436             elif is_yes "${_MAN_ALL}"; then
2437               man_register_file "$f" "${_name}";
2438             else
2439               man_register_file "$f" "${_name}" "${_section}";
2440               eval "${return_good}";
2441             fi;
2442             _got_one='yes';
2443           fi;
2444         done;
2445       fi;
2446     done;
2447   else
2448     _ext="${_MAN_EXT}";
2449     # check for directory name having trailing extension
2450     for d in "$@"; do
2451       _dir="$(dirname_append $d man${_section}${_ext})";
2452       if is_dir "${_dir}"; then
2453         _prefix="$(dirname_append "${_dir}" "${_name}.${_section}")";
2454         for f in ${_prefix}*; do
2455           if is_file "$f"; then
2456             if is_yes "${_got_one}"; then
2457               register_file "$f";
2458             elif is_yes "${_MAN_ALL}"; then
2459               man_register_file "$f" "${_name}";
2460             else
2461               man_register_file "$f" "${_name}" "${_section}";
2462               eval "${return_good}";
2463             fi;
2464             _got_one='yes';
2465           fi;
2466         done;
2467       fi;
2468     done;
2469     # check for files with extension in directories without extension
2470     for d in "$@"; do
2471       _dir="$(dirname_append "$d" "man${_section}")";
2472       if is_dir "${_dir}"; then
2473         _prefix="$(dirname_append "${_dir}" \
2474                                   "${_name}.${_section}${_ext}")";
2475         for f in ${_prefix}*; do
2476           if is_file "$f"; then
2477             if is_yes "${_got_one}"; then
2478               register_file "$f";
2479             elif is_yes "${_MAN_ALL}"; then
2480               man_register_file "$f" "${_name}";
2481             else
2482               man_register_file "$f" "${_name}" "${_section}";
2483               eval "${return_good}";
2484             fi;
2485             _got_one='yes';
2486           fi;
2487         done;
2488       fi;
2489     done;
2490   fi;
2491   if is_yes "${_MAN_ALL}" && is_yes "${_got_one}"; then
2492     eval "${return_good}";
2493   fi;
2494   eval "${return_bad}";
2495 } # man_search_section()
2496
2497
2498 ########################################################################
2499 # man_setup ()
2500 #
2501 # Setup the variables $_MAN_* needed for man page searching.
2502 #
2503 # Globals:
2504 #   in:     $_OPT_*, $_MANOPT_*, $LANG, $LC_MESSAGES, $LC_ALL,
2505 #           $MANPATH, $MANROFFSEQ, $MANSEC, $PAGER, $SYSTEM, $MANOPT.
2506 #   out:    $_MAN_PATH, $_MAN_LANG, $_MAN_SYS, $_MAN_LANG, $_MAN_LANG2,
2507 #           $_MAN_SEC, $_MAN_ALL
2508 #   in/out: $_MAN_ENABLE
2509 #
2510 # The precedence for the variables related to `man' is that of GNU
2511 # `man', i.e.
2512 #
2513 # $LANG; overridden by
2514 # $LC_MESSAGES; overridden by
2515 # $LC_ALL; this has the same precedence as
2516 # $MANPATH, $MANROFFSEQ, $MANSEC, $PAGER, $SYSTEM; overridden by
2517 # $MANOPT; overridden by
2518 # the groffer command line options.
2519 #
2520 man_setup()
2521 {
2522   func_check main_man_setup = 0 "$@";
2523   local _lang;
2524
2525   if is_yes "${_MAN_IS_SETUP}"; then
2526     eval "${return_ok}";
2527   fi;
2528   _MAN_IS_SETUP='yes';
2529
2530   if is_not_yes "${_MAN_ENABLE}"; then
2531     eval "${return_ok}";
2532   fi;
2533
2534   # determine basic path for man pages
2535   _MAN_PATH="$(get_first_essential \
2536                "${_OPT_MANPATH}" "${_MANOPT_PATH}" "${MANPATH}")";
2537   if is_empty "${_MAN_PATH}"; then
2538     if is_prog 'manpath'; then
2539       _MAN_PATH="$(manpath 2>/dev/null)"; # not always available
2540     fi;
2541   fi;
2542   if is_empty "${_MAN_PATH}"; then
2543     manpath_set_from_path;
2544   else
2545     _MAN_PATH="$(path_clean "${_MAN_PATH}")";
2546   fi;
2547   if is_empty "${_MAN_PATH}"; then
2548     _MAN_ENABLE="no";
2549     eval "${return_ok}";
2550   fi;
2551
2552   _MAN_ALL="$(get_first_essential "${_OPT_ALL}" "${_MANOPT_ALL}")";
2553   if is_empty "${_MAN_ALL}"; then
2554     _MAN_ALL='no';
2555   fi;
2556
2557   _MAN_SYS="$(get_first_essential \
2558               "${_OPT_SYSTEMS}" "${_MANOPT_SYS}" "${SYSTEM}")";
2559   _lang="$(get_first_essential \
2560            "${_OPT_LANG}" "${LC_ALL}" "${LC_MESSAGES}" "${LANG}")";
2561   case "${_lang}" in
2562     C|POSIX)
2563       _MAN_LANG="";
2564       _MAN_LANG2="";
2565       ;;
2566     ?)
2567       _MAN_LANG="${_lang}";
2568       _MAN_LANG2="";
2569       ;;
2570     *)
2571       _MAN_LANG="${_lang}";
2572       # get first two characters of $_lang
2573       _MAN_LANG2="$(echo -n "${_lang}" | sed -e 's/^\(..\).*$/\1/')";
2574       ;;
2575   esac;
2576   # from now on, use only $_LANG, forget about $_OPT_LANG, $LC_*.
2577
2578   manpath_add_lang_sys;         # this is very slow
2579
2580   _MAN_SEC="$(get_first_essential \
2581               "${_OPT_SECT}" "${_MANOPT_SEC}" "${MANSEC}")";
2582   if is_empty "${_MAN_PATH}"; then
2583     _MAN_ENABLE="no";
2584     eval "${return_ok}";
2585   fi;
2586
2587   _MAN_EXT="$(get_first_essential \
2588               "${_OPT_EXTENSION}" "${_MANOPT_EXTENSION}")";
2589   eval "${return_ok}";
2590 } # man_setup()
2591
2592
2593 ########################################################################
2594 landmark '8: manpath_*()';
2595 ########################################################################
2596
2597 ########################################################################
2598 # manpath_add_lang_sys ()
2599 #
2600 # Add language and operating system specific directories to man path.
2601 #
2602 # Arguments : 0
2603 # Output    : none
2604 # Globals:
2605 #   in:     $_MAN_SYS: has the form `os1,os2,...', a comma separated
2606 #             list of names of operating systems.
2607 #           $_MAN_LANG and $_MAN_LANG2: each a single name
2608 #   in/out: $_MAN_PATH: has the form `dir1:dir2:...', a colon
2609 #             separated list of directories.
2610 #
2611 manpath_add_lang_sys()
2612 {
2613   func_check manpath_add_lang_sys = 0 "$@";
2614   local p;
2615   local _mp;
2616   if is_empty "${_MAN_PATH}"; then
2617     eval "${return_ok}";
2618   fi;
2619   # twice test both sys and lang
2620   eval set -- "$(path_split "${_MAN_PATH}")";
2621   _mp='';
2622   for p in "$@"; do             # loop on man path directories
2623     _mp="$(_manpath_add_lang_sys_single "${_mp}" "$p")";
2624   done;
2625   eval set -- "$(path_split "${_mp}")";
2626   for p in "$@"; do             # loop on man path directories
2627     _mp="$(_manpath_add_lang_sys_single "${_mp}" "$p")";
2628   done;
2629   _MAN_PATH="$(path_chop "${_mp}")";
2630   eval "${return_ok}";
2631 }
2632
2633
2634 _manpath_add_lang_sys_single()
2635 {
2636   # To the directory in $1 append existing sys/lang subdirectories
2637   # Function is necessary to split the OS list.
2638   #
2639   # globals: in: $_MAN_SYS, $_MAN_LANG, $_MAN_LANG2
2640   # argument: 2: `man_path' and `dir'
2641   # output: colon-separated path of the retrieved subdirectories
2642   #
2643   local d;
2644 #  if test "$#" -ne 2; then
2645 #    error "manpath_add_system_single() needs 2 arguments.";
2646 #  fi;
2647   _res="$1";
2648   _parent="$2";
2649   eval set -- "$(list_from_split "${_MAN_SYS}" ',')";
2650   for d in "$@" "${_MAN_LANG}" "${_MAN_LANG2}"; do
2651     _dir="$(dirname_append "${_parent}" "$d")";
2652     if path_not_contains "${_res}" "${_dir}" && is_dir "${_dir}"; then
2653       _res="${_res}:${_dir}";
2654     fi;
2655   done;
2656   if path_not_contains "${_res}" "${_parent}"; then
2657     _res="${_res}:${_parent}";
2658   fi;
2659   path_chop "${_res}";
2660 }
2661
2662 # end manpath_add_lang_sys ()
2663
2664
2665 ########################################################################
2666 # manpath_set_from_path ()
2667 #
2668 # Determine basic search path for man pages from $PATH.
2669 #
2670 # Return:    `0' if a valid man path was retrieved.
2671 # Output:    none
2672 # Globals:
2673 #   in:  $PATH
2674 #   out: $_MAN_PATH
2675 #
2676 manpath_set_from_path()
2677 {
2678   func_check manpath_set_from_path = 0 "$@";
2679   local _base;
2680   local _mandir;
2681   local _manpath;
2682   local d;
2683   local e;
2684   _manpath='';
2685
2686   # get a basic man path from $PATH
2687   if is_not_empty "${PATH}"; then
2688     eval set -- "$(path_split "${PATH}")";
2689     for d in "$@"; do
2690       # delete the final `/bin' part
2691       _base="$(echo -n "$d" | sed -e '\|//*bin/*$|s|||')";
2692       for e in /share/man /man; do
2693         _mandir="${_base}$e";
2694         if test -d "${_mandir}" && test -r "${_mandir}"; then
2695         _manpath="${_manpath}:${_mandir}";
2696         fi;
2697       done;
2698     done;
2699   fi;
2700
2701   # append some default directories
2702   for d in /usr/local/share/man /usr/local/man \
2703             /usr/share/man /usr/man \
2704             /usr/X11R6/man /usr/openwin/man \
2705             /opt/share/man /opt/man \
2706             /opt/gnome/man /opt/kde/man; do
2707     if path_not_contains "${_manpath}" "$d" && is_dir "$d"; then
2708       _manpath="${_manpath}:$d";
2709     fi;
2710   done;
2711
2712   _MAN_PATH="${_manpath}";
2713   eval "${return_ok}";
2714 } # manpath_set_from_path()
2715
2716
2717 ########################################################################
2718 landmark '9: path_*()';
2719 ########################################################################
2720
2721 ########################################################################
2722 # path_chop (<path>)
2723 #
2724 # Remove unnecessary colons from path.
2725 #
2726 # Argument: 1, a colon separated path.
2727 # Output:   path without leading, double, or trailing colons.
2728 #
2729 path_chop()
2730 {
2731   func_check path_chop = 1 "$@";
2732   local _res;
2733
2734   # replace multiple colons by a single colon `:'
2735   # remove leading and trailing colons
2736   echo -n "$1" | sed -e 's/:::*/:/g' |
2737                  sed -e 's/^:*//' |
2738                  sed -e 's/:*$//';
2739   eval "${return_ok}";
2740 }
2741
2742
2743 ########################################################################
2744 # path_clean (<path>)
2745 #
2746 # Remove non-existing directories from a colon-separated list.
2747 #
2748 # Argument: 1, a colon separated path.
2749 # Output:   colon-separated list of existing directories.
2750 #
2751 path_clean()
2752 {
2753   func_check path_clean = 1 "$@";
2754   local _arg;
2755   local _dir;
2756   local _res;
2757   local i;
2758   if test "$#" -ne 1; then
2759     error 'path_clean() needs 1 argument.';
2760   fi;
2761   _arg="$1";
2762   eval set -- "$(path_split "${_arg}")";
2763   _res="";
2764   for i in "$@"; do
2765     if is_not_empty "$i" \
2766        && path_not_contains "${_res}" "$i" \
2767        && is_dir "$i";
2768     then
2769       case "$i" in
2770         ?*/) _res="${_res}$(dirname_chop "$i")"; ;;
2771         *)  _res="${_res}:$i";
2772       esac;
2773     fi;
2774   done;
2775   if path_chop "${_res}"; then
2776     eval "${return_ok}";
2777   else
2778     eval "${return_badk}";
2779   fi;
2780 }
2781
2782
2783 ########################################################################
2784 # path_contains (<path> <dir>)
2785 #-
2786 # Test whether `dir' is contained in `path', a list separated by `:'.
2787 #
2788 # Arguments : 2 arguments.
2789 # Return    : `0' if arg2 is substring of arg1, `1' otherwise.
2790 #
2791 path_contains()
2792 {
2793   func_check path_contains = 2 "$@";
2794   case ":$1:" in
2795     *":$2:"*)
2796       eval "${return_yes}";
2797       ;;
2798     *)
2799       eval "${return_no}";
2800       ;;
2801   esac;
2802   eval "${return_ok}";
2803 }
2804
2805
2806 ########################################################################
2807 # path_not_contains (<path> <dir>)
2808 #-
2809 # Test whether `dir' is not contained in colon separated `path'.
2810 #
2811 # Arguments : 2 arguments.
2812 #
2813 path_not_contains()
2814 {
2815   func_check path_not_contains = 2 "$@";
2816   if path_contains "$1" "$2"; then
2817     eval "${return_no}";
2818   else
2819     eval "${return_yes}";
2820   fi;
2821   eval "${return_ok}";
2822 }
2823
2824
2825 ########################################################################
2826 # path_split (<path>)
2827 #
2828 # In `path' escape white space and replace each colon by a space.
2829 #
2830 # Arguments: 1: a colon-separated path
2831 # Output:    the resulting list, process with `eval set --'
2832 #
2833 path_split()
2834 {
2835   func_check path_split = 1 "$@";
2836   list_from_split "$1" ':';
2837   eval "${return_ok}";
2838 }
2839
2840
2841 ########################################################################
2842 # reset ()
2843 #
2844 # Reset the variables that can be affected by options to their default.
2845 #
2846 #
2847 # Defined in section `Preset' after the rudimentary shell tests.
2848
2849
2850 ########################################################################
2851 landmark '10: register_*()';
2852 ########################################################################
2853
2854 ########################################################################
2855 # register_file (<filename>)
2856 #
2857 # Write a found file and register the title element.
2858 #
2859 # Arguments: 1: a file name
2860 # Output: none
2861 #
2862 register_file()
2863 {
2864   func_check register_file = 1 "$@";
2865   if is_empty "$1"; then
2866     error 'register_file(): file name is empty';
2867   fi;
2868   if is_equal "$1" '-'; then
2869     to_tmp "${_TMP_STDIN}";
2870     register_title '-';
2871   else
2872     to_tmp "$1";
2873     register_title "$(base_name "$1")";
2874   fi;
2875   eval "${return_ok}";
2876 }
2877
2878
2879 ########################################################################
2880 # register_title (<filespec>)
2881 #
2882 # Create title element from <filespec> and append to $_REGISTERED_TITLE
2883 #
2884 # Globals: $_REGISTERED_TITLE (rw)
2885 #
2886 register_title()
2887 {
2888   func_check register_title = 1 "$@";
2889   local _title;
2890   if is_empty "$1"; then
2891     eval "${return_ok}";
2892   fi;
2893   _title="$(base_name "$1")";   # remove directory part
2894   
2895   # remove extension `.gz'
2896   _title="$(echo -n "${_title}" | sed -e 's/\.gz$//')";
2897   # remove extension `.Z'
2898   _title="$(echo -n "${_title}" | sed -e 's/\.Z$//')";
2899
2900   if is_empty "${_title}"; then
2901     eval "${return_ok}";
2902   fi;
2903   _REGISTERED_TITLE="${_REGISTERED_TITLE} ${_title}";
2904   eval "${return_ok}";
2905 }
2906
2907
2908 ########################################################################
2909 # save_stdin ()
2910 #
2911 # Store standard input to temporary file (with decompression).
2912 #
2913 if test "${_HAS_COMPRESSION}" = 'yes'; then
2914   save_stdin()
2915   {
2916     local _f;
2917     func_check save_stdin = 0 "$@";
2918     _f="$(tmp_create)";
2919     cat >"${_f}";
2920     catz "${_f}" >"${_TMP_STDIN}";
2921     rm -f "${_f}";
2922     eval "${return_ok}";
2923   }
2924 else
2925   save_stdin()
2926   {
2927     func_check save_stdin = 0 "$@";
2928     cat >"${_TMP_STDIN}";
2929     eval "${return_ok}";
2930   }
2931 fi;
2932
2933
2934 ########################################################################
2935 landmark '11: stack_*()';
2936 ########################################################################
2937
2938 ########################################################################
2939 # string_contains (<string> <part>)
2940 #
2941 # Test whether `part' is contained in `string'.
2942 #
2943 # Arguments : 2 text arguments.
2944 # Return    : `0' if arg2 is substring of arg1, `1' otherwise.
2945 #
2946 string_contains()
2947 {
2948   func_check string_contains = 2 "$@";
2949   case "$1" in
2950     *"$2"*)
2951       eval "${return_yes}";
2952       ;;
2953     *)
2954       eval "${return_no}";
2955       ;;
2956   esac;
2957   eval "${return_ok}";
2958 }
2959
2960
2961 ########################################################################
2962 # string_not_contains (<string> <part>)
2963 #
2964 # Test whether `part' is not substring of `string'.
2965 #
2966 # Arguments : 2 text arguments.
2967 # Return    : `0' if arg2 is substring of arg1, `1' otherwise.
2968 #
2969 string_not_contains()
2970 {
2971   func_check string_not_contains = 2 "$@";
2972   if string_contains "$1" "$2"; then
2973     eval "${return_no}";
2974   else
2975     eval "${return_yes}";
2976   fi;
2977   eval "${return_ok}";
2978 }
2979
2980
2981 ########################################################################
2982 landmark '12: tmp_*()';
2983 ########################################################################
2984
2985 ########################################################################
2986 # tmp_cat ()
2987 #
2988 # output the temporary cat file (the concatenation of all input)
2989 #
2990 tmp_cat()
2991 {
2992   cat "${_TMP_CAT}";
2993 }
2994
2995
2996 ########################################################################
2997 # tmp_create (<suffix>?)
2998 #
2999 # create temporary file
3000 #
3001 # It's safe to use the shell process ID together with a suffix to
3002 # have multiple temporary files.
3003 #
3004 # Output : name of created file
3005 #
3006 tmp_create()
3007 {
3008   func_check tmp_create '<=' 1 "$@";
3009   local _tmp;
3010   _tmp="${_TMP_PREFIX}${_PROCESS_ID}$1";
3011   echo -n >"${_tmp}";
3012   echo -n "${_tmp}";
3013   eval "${return_ok}";
3014 }
3015
3016
3017 ########################################################################
3018 # to_tmp (<filename>)
3019 #
3020 # print file (decompressed) to the temporary cat file
3021 #
3022 to_tmp()
3023 {
3024   func_check to_tmp = 1 "$@";
3025   if is_file "$1"; then
3026     if is_yes "${_OPT_LOCATION}"; then
3027       echo2 "$1";
3028     fi;
3029     if is_yes "${_OPT_WHATIS}"; then
3030       what_is "$1" >>"${_TMP_CAT}";
3031     else
3032       catz "$1" >>"${_TMP_CAT}";
3033     fi;
3034   else
3035     error "to_tmp(): could not read file \`$1'.";
3036   fi;
3037   eval "${return_ok}";
3038 }
3039
3040
3041 ########################################################################
3042 # usage ()
3043 #
3044 # print usage information to stderr
3045 #
3046 usage()
3047 {
3048   func_check usage = 0 "$@";
3049   echo2;
3050   version;
3051   cat >&2 <<EOF
3052 Copyright (C) 2001 Free Software Foundation, Inc.
3053 This is free software licensed under the GNU General Public License.
3054
3055 EOF
3056
3057   echo2 "Usage: ${_PROGRAM_NAME} ${_header} [option]... [filespec]...";
3058
3059   cat >&2 <<EOF
3060
3061 where "filespec" is one of
3062   "filename"       name of a readablefile
3063   "-"              for standard input
3064   "man:name.n"     man page "name" in section "n"
3065   "man:name"       man page "name" in first section found
3066   "name.n"         man page "name" in section "n"
3067   "name"           man page "name" in first section found
3068 and some more (see groff(1) for details).
3069
3070 Display roff files, standard input, and/or Unix manual pages with
3071 in a X window viewer or in a text pager.
3072 "-" stands for including standard input.
3073 "manpage" is the name of a man page, "x" its section.
3074 All input is decompressed on-the-fly (by gzip).
3075
3076 -h --help        print this usage message.
3077 -Q --source      output as roff source.
3078 -T --device=name pass to groff using output device "name".
3079 -v --version     print version information.
3080
3081 All other short options are interpreted as "groff" formatting
3082 parameters and are transferred unmodified.  The following groff
3083 options imply groff mode (groffer viewing disabled):
3084
3085 -X               display with "gxditview" using groff -X.
3086 -V               display the groff execution pipe instead of formatting.
3087 -Z --ditroff --intermediate-output
3088                  generate groff intermediate output without 
3089                  post-processing and viewing like groff -Z.
3090
3091 The most important long options are
3092
3093 --auto-modes=mode1,mode2,...
3094                  set sequence of automatically tried modes.
3095 --bg             set background color (not for all modes).
3096 --default        reset effects of option processing so far.
3097 --display        set the X device when displaying in X.
3098 --dpi=res        set resolution to "res" ("75" (default) or "100").
3099 --dvi            display in a viewer for TeX device independent format.
3100 --dvi-viewer     choose the viewer program for dvi mode.
3101 --extension=ext  restrict man pages to section suffix.
3102 --fg             set foreground color (not for all modes).
3103 --geometry=geom  set the window size and position when displaying in X.
3104 --groff          process like groff, disable viewing features.
3105 --local-file     same as --no-man.
3106 --locale=lang    preset the language for man pages.
3107 --location       print file locations additionally to standard error.
3108 --man            check file parameters first whether they are man pages.
3109 --mode=auto|dvi|groff|pdf|ps|source|tty|www|x
3110                  choose display mode.
3111 --no-location    disable former call to "--location".
3112 --no-man         disable man-page facility.
3113 --pager=program  preset the paging program for tty mode.
3114 --pdf            display in a PDF viewer.
3115 --pdf-viewer     choose the viewer program for pdf mode.
3116 --ps             display in a Postscript viewer.
3117 --ps-viewer      choose the viewer program for ps mode.
3118 --shell          specify shell under which to run this program.
3119 --systems=os1,os2,...
3120                  search man pages for different operating systems.
3121 --title='text'   set the title of the viewer window (not in all modes.
3122 --tty            force paging on text terminal even when in X.
3123 --www            display in a web browser.
3124 --www-viewer     choose the web browser for www mode.
3125 --x              display in an X roff viewer.
3126 --x-viewer       choose viewer program for x mode.
3127 --xrm='resource' set X resouce.
3128
3129 EOF
3130   eval "${return_ok}";
3131 }
3132
3133
3134 ########################################################################
3135 # version ()
3136 #
3137 # print version information to stderr
3138 #
3139 version()
3140 {
3141   echo2 "${_PROGRAM_NAME} ${_PROGRAM_VERSION} of ${_LAST_UPDATE}";
3142   # also display groff's version, but not the called subprograms
3143   groff -v 2>&1 | sed -e '/^ *$/q' | sed -e '1s/^/is part of /' >&2;  
3144 }
3145
3146
3147 ########################################################################
3148 # warning (<string>)
3149 #
3150 # Print warning to stderr
3151 #
3152 warning()
3153 {
3154   echo2 "warning: $*";
3155 }
3156
3157
3158 ########################################################################
3159 # what_is (<filename>)
3160 #
3161 # Interpret <filename> as a man page and display its `whatis'
3162 # information as a fragment written in the groff language.
3163 #
3164 what_is()
3165 {
3166   func_check what_is = 1 "$@";
3167   local _res;
3168   local _dot;
3169   if is_not_file "$1"; then
3170     error "what_is(): argument is not a readable file."
3171   fi;
3172   _dot='^\.['"${_SPACE}${_TAB}"']*';
3173   echo '.br';
3174   echo "$1: ";
3175     echo '.br';
3176   echo -n '  ';
3177   # grep the line containing `.TH' macro, if any
3178   _res="$(catz "$1" | sed -e '/'"${_dot}"'TH /p
3179 d')";
3180   if is_not_empty "${_res}"; then       # traditional man style
3181     # get the text between the first and the second `.SH' macro, by
3182     # - delete up to first .SH;
3183     # - of this, print everything up to next .SH, and delete the rest;
3184     # - of this, delete the final .SH line;
3185     catz "$1" | sed -e '1,/'"${_dot}"'SH/d' \
3186               | sed -e '1,/'"${_dot}"'SH/p
3187 d' \
3188               | sed -e '/'"${_dot}"'SH/d';
3189     eval "${return_ok}";
3190   fi;
3191   # grep the line containing `.Dd' macro, if any
3192   _res="$(catz "$1" | sed -e '/'"${_dot}"'Dd /p
3193 d')";
3194   if is_not_empty "${_res}"; then       # BSD doc style
3195     # get the text between the first and the second `.Nd' macro, by
3196     # - delete up to first .Nd;
3197     # - of this, print everything up to next .Nd, and delete the rest;
3198     # - of this, delete the final .Nd line;
3199     catz "$1" | sed -e '1,/'"${_dot}"'Nd/d' \
3200               | sed -e '1,/'"${_dot}"'Nd/p
3201 d' \
3202               | sed -e '/'"${_dot}"'Nd/d';
3203     eval "${return_ok}";
3204   fi;
3205   echo 'is not a man page.';
3206   eval "${return_bad}";
3207 }
3208
3209
3210 ########################################################################
3211 # where (<program>)
3212 #
3213 # Output path of a program if in $PATH.
3214 #
3215 # Arguments : >=1 (empty allowed)
3216 #   more args are ignored, this allows to specify progs with arguments
3217 # Return    : `0' if arg1 is a program in $PATH, `1' otherwise.
3218 #
3219 where()
3220 {
3221   func_check where '>=' 1 "$@";
3222   local _file;
3223   local _arg;
3224   local p;
3225   _arg="$1";
3226   if is_empty "${_arg}"; then
3227     eval "${return_bad}";
3228   fi;
3229   case "${_arg}" in
3230     /*)
3231       if test -f "${_arg}" && test -x "${_arg}"; then
3232         eval "${return_ok}";
3233       else
3234         eval "${return_bad}";
3235       fi;
3236       ;;
3237   esac;
3238   eval set -- "$(path_split "${PATH}")";
3239   for p in "$@"; do
3240     case "$p" in
3241       */) _file=$p${_arg}; ;;
3242       *)  _file=$p/${_arg}; ;;
3243     esac;
3244     if test -f "${_file}" && test -x "${_file}"; then
3245       echo -n "${_file}";
3246       eval "${return_ok}";
3247     fi;
3248   done;
3249   eval "${return_bad}";
3250 }
3251
3252
3253 ########################################################################
3254 #                              main
3255 ########################################################################
3256
3257 # The main area contains the following parts:
3258 # - main_init(): initialize temporary files and set exit trap
3259 # - parse $MANOPT
3260 # - main_parse_args(): argument parsing
3261 # - determine display mode
3262 # - process filespec arguments
3263 # - setup X resources
3264 # - do the displaying
3265
3266 # These parts are implemented as functions, being defined below in the
3267 # sequence they are called in the main() function.
3268
3269
3270 #######################################################################
3271 # main_init ()
3272 #
3273 # set exit trap and create temporary files
3274 #
3275 # Globals: $_TMP_CAT, $_TMP_STDIN
3276 #
3277 landmark '13: main_init()';
3278 main_init()
3279 {
3280   func_check main_init = 0 "$@";
3281   # call clean_up() on any signal
3282   trap clean_up  2>/dev/null || true;
3283
3284   for f in ${_CONFFILES}; do
3285     if is_file "$f"; then
3286       . "$f";
3287     fi;
3288   done;
3289
3290   # determine temporary directory
3291   for d in "${GROFF_TMPDIR}" "${TMPDIR}" "${TMP}" "${TEMP}" \
3292            "${TEMPDIR}" "${HOME}"'/tmp' '/tmp' "${HOME}" '.';
3293   do
3294     if test "$d" != ""; then
3295       if test -d "$d" && test -r "$d" && test -w "$d"; then
3296         _TMP_DIR="$d";
3297         break;
3298       fi;
3299     fi;
3300   done;
3301   unset d;
3302   if test "${_TMP_DIR}" = ""; then
3303     error "Couldn't find a directory for storing temorary files.";
3304   fi;
3305   _TMP_PREFIX="${_TMP_DIR}/${_PROGRAM_NAME}";
3306
3307   _TMP_CAT="$(tmp_create)";
3308   _TMP_STDIN="$(tmp_create i)";
3309   eval "${return_ok}";
3310 } # main_init()
3311
3312
3313 ########################################################################
3314 # main_parse_MANOPT ()
3315 #
3316 # Parse $MANOPT; this clobbered by the command line.
3317 #
3318 # Globals:
3319 #   in: $MANOPT, $_OPTS_MAN_*
3320 #   out: $_MANOPT_*
3321 #   in/out: $GROFFER_OPT
3322 #
3323 landmark '14: main_parse_MANOPT()';
3324 main_parse_MANOPT()
3325 {
3326   func_check main_parse_MANOPT = 0 "$@";
3327   local _opt;
3328   local _list;
3329   _list='';
3330   # feed in $MANOPT
3331   eval set -- "$(list_from_cmdline \
3332     "${_OPTS_MAN_SHORT_NA}" "${_OPTS_MAN_SHORT_ARG}" \
3333     "${_OPTS_MAN_LONG_NA}" "${_OPTS_MAN_LONG_ARG}" \
3334     "${MANOPT}")";
3335   until test "$#" -le 0 || is_equal "$1" '--'; do
3336     _opt="$1";
3337     shift;
3338     case "${_opt}" in
3339       -7|--ascii)
3340         _list="$(list_append "${_list}" '--ascii')";
3341         ;;
3342       -a|--all)
3343         _list="$(list_append "${_list}" '--all')";
3344         ;;
3345       -c|--catman)
3346         do_nothing;
3347         shift;
3348         ;;
3349       -d|--debug)
3350         _list="$(list_append "${_list}" '--debug')";
3351         ;;
3352       -D|--default)
3353         # undo all man options so far
3354         _list='';
3355         ;;
3356       -e|--extension)
3357         _list="$(list_append "${_list}" '--extension')";
3358         shift;
3359         ;;
3360       -f|--whatis)
3361         _list="$(list_append "${_list}" '--whatis')";
3362         shift;
3363         ;;
3364       -h|--help)
3365         do_nothing;
3366         shift;
3367         ;;
3368       -k|--apropos)
3369         _list="$(list_append "${_list}" '--apropos')";
3370         shift;
3371         ;;
3372       -l|--local-file)
3373         _list="$(list_append "${_list}" '--local-file')";
3374         ;;
3375       -L|--locale)
3376         _list="$(list_append "${_list}" '--locale' "$1")";
3377         shift;
3378         ;;
3379       -m|--systems)
3380         _list="$(list_append "${_list}" '--systems' "$1")";
3381         shift;
3382         ;;
3383       -M|--manpath)
3384         _list="$(list_append "${_list}" '--manpath' "$1")";
3385         shift;
3386         ;;
3387       -p|--preprocessor)
3388         do_nothing;
3389         shift;
3390         ;;
3391       -P|--pager)
3392         _list="$(list_append "${_list}" '--pager' "$1")";
3393         shift;
3394         ;;
3395       -r|--prompt)
3396         do_nothing;
3397         shift;
3398         ;;
3399       -S|--sections)
3400         _list="$(list_append "${_list}" '--sections' "$1")";
3401         shift;
3402         ;;
3403       -t|--troff)
3404         do_nothing;
3405         shift;
3406         ;;
3407       -T|--device)
3408         _list="$(list_append "${_list}" '-T' "$1")";
3409         shift;
3410         ;;
3411       -u|--update)
3412         do_nothing;
3413         shift;
3414         ;;
3415       -V|--version)
3416         do_nothing;
3417         ;;
3418       -w|--where|--location)
3419         _list="$(list_append "${_list}" '--location')";
3420         ;;
3421       -Z|--ditroff)
3422         _list="$(list_append "${_list}" '-Z' "$1")";
3423         shift;
3424         ;;
3425       # ignore all other options
3426     esac;
3427   done;
3428   GROFFER_OPT="$(list_from_lists "${_list}" "${GROFFER_OPT}")";
3429   eval "${return_ok}";
3430 } # main_parse_MANOPT()
3431
3432
3433 ########################################################################
3434 # main_parse_args (<command_line_args>*)
3435 #
3436 # Parse arguments; process options and filespec parameters
3437 #
3438 # Arguments: pass the command line arguments unaltered.
3439 # Globals:
3440 #   in:  $_OPTS_*
3441 #   out: $_OPT_*, $_ADDOPTS, $_FILEARGS
3442 #
3443 landmark '15: main_parse_args()';
3444 main_parse_args()
3445 {
3446   func_check main_parse_args '>=' 0 "$@";
3447   local _arg;
3448   local _code;
3449   local _dpi;
3450   local _longopt;
3451   local _mode;
3452   local _opt;
3453   local _optchar;
3454   local _optarg;
3455   local _opts;
3456   local _string;
3457
3458   eval set -- "${GROFFER_OPT}" '"$@"';
3459
3460   eval set -- "$(list_from_cmdline \
3461    "$_OPTS_CMDLINE_SHORT_NA" "$_OPTS_CMDLINE_SHORT_ARG" \
3462    "$_OPTS_CMDLINE_LONG_NA" "$_OPTS_CMDLINE_LONG_ARG" \
3463    "$@")";
3464
3465 # By the call of `eval', unnecessary quoting was removed.  So the
3466 # positional shell parameters ($1, $2, ...) are now guaranteed to
3467 # represent an option or an argument to the previous option, if any;
3468 # then a `--' argument for separating options and
3469 # parameters; followed by the filespec parameters if any.
3470
3471 # Note, the existence of arguments to options has already been checked.
3472 # So a check for `$#' or `--' should not be done for arguments.
3473
3474   until test "$#" -le 0 || is_equal "$1" '--'; do
3475     _opt="$1";                  # $_opt is fed into the option handler
3476     shift;
3477     case "${_opt}" in
3478       -h|--help)
3479         usage;
3480         leave;
3481         ;;
3482       -Q|--source)              # output source code (`Quellcode').
3483         _OPT_MODE='source';
3484         ;;
3485       -T|--device|--troff-device)
3486                                 # device; arg
3487         _OPT_DEVICE="$1";
3488         shift;
3489         ;;
3490       -v|--version)
3491         version;
3492         leave;
3493         ;;
3494       -V)
3495         _OPT_V='yes';
3496         ;;
3497       -X)
3498         _OPT_X='yes';
3499         ;;
3500       -Z|--ditroff|--intermediate-output)
3501                                 # groff intermediate output
3502         _OPT_Z='yes';
3503         ;;
3504       -?)
3505         # delete leading `-'
3506         _optchar="$(echo -n "${_opt}" | sed -e 's/^.//')";
3507         if list_has "${_OPTS_GROFF_SHORT_NA}" "${_optchar}";
3508         then
3509           _ADDOPTS_GROFF="$(list_append "${_ADDOPTS_GROFF}" \
3510                                         "${_opt}")";
3511         elif list_has "${_OPTS_GROFF_SHORT_ARG}" "${_optchar}";
3512         then
3513           _ADDOPTS_GROFF="$(list_append "${_ADDOPTS_GROFF}" \
3514                                         "${_opt}" "$1")";
3515            shift;
3516         else
3517           error "Unknown option : \`$1'";
3518         fi;
3519         ;;
3520       --all)
3521           _OPT_ALL="yes";
3522           ;;
3523       --ascii)
3524         _ADDOPTS_GROFF="$(list_append "${_ADDOPTS_GROFF}" \
3525                                       '-mtty-char')";
3526         ;;
3527       --apropos)
3528         _OPT_APROPOS="yes";
3529         ;;
3530       --auto)                   # the default automatic mode
3531         _mode='';
3532         ;;
3533       --bd)                     # border color for viewers, arg;
3534         _OPT_BD="$1";
3535         shift;
3536         ;;
3537       --bg|--backgroud)         # background color for viewers, arg;
3538         _OPT_BG="$1";
3539         shift;
3540         ;;
3541       --bw)                     # border width for viewers, arg;
3542         _OPT_BW="$1";
3543         shift;
3544         ;;
3545       --default)                # reset variables to default
3546         reset;
3547         ;;
3548       --default-modes)          # sequence of modes in auto mode; arg
3549         _OPT_DEFAULT_MODES="$1";
3550         shift;
3551         ;;
3552       --debug)          # sequence of modes in auto mode; arg
3553         _OPT_DEBUG='yes';
3554         ;;
3555       --display)                # set X display, arg
3556         _OPT_DISPLAY="$1";
3557         shift;
3558         ;;
3559       --dvi)
3560         _OPT_MODE='dvi';
3561         ;;
3562       --dvi-viewer)             # viewer program for dvi mode; arg
3563         _OPT_VIEWER_DVI="$1";
3564         shift;
3565         ;;
3566       --extension)              # the extension for man pages, arg
3567         _OPT_EXTENSION="$1";
3568         shift;
3569         ;;
3570       --fg|--foreground)        # foreground color for viewers, arg;
3571         _OPT_FG="$1";
3572         shift;
3573         ;;
3574       --fn|--font)              # set font for viewers, arg;
3575         _OPT_FN="$1";
3576         shift;
3577         ;;
3578       --geometry)               # window geometry for viewers, arg;
3579         _OPT_GEOMETRY="$1";
3580         shift;
3581         ;;
3582       --groff)
3583         _OPT_MODE='groff';
3584         ;;
3585       --locale)                 # set language for man pages, arg
3586         # argument is xx[_territory[.codeset[@modifier]]] (ISO 639,...)
3587         _OPT_LANG="$1";
3588         shift;
3589         ;;
3590       --local-file)             # force local files; same as `--no-man'
3591         _MAN_FORCE='no';
3592         _MAN_ENABLE='no';
3593         ;;
3594       --location|--where)       # print file locations to stderr
3595         _OPT_LOCATION='yes';
3596         ;;
3597       --man)                    # force all file params to be man pages
3598         _MAN_ENABLE='yes';
3599         _MAN_FORCE='yes';
3600         ;;
3601       --manpath)                # specify search path for man pages, arg
3602         # arg is colon-separated list of directories
3603         _OPT_MANPATH="$1";
3604         shift;
3605         ;;
3606       --mode)                   # display mode
3607         _arg="$1";
3608         shift;
3609         case "${_arg}" in
3610           auto|'')              # the default automatic mode
3611             _mode='';
3612             ;;
3613           groff)                # pass input to plain groff
3614             _mode='groff';
3615             ;;
3616           dvi)                  # display with xdvi viewer
3617             _mode='dvi';
3618             ;;
3619           pdf)                  # display with PDF viewer
3620             _mode='pdf';
3621             ;;
3622           ps)                   # display with Postscript viewer
3623             _mode='ps';
3624             ;;
3625           X|x)                  # output on X roff viewer
3626             _mode='x';
3627             ;;
3628           tty)                  # output on terminal
3629             _mode='tty';
3630             ;;
3631           Q|source)             # display source code
3632             _mode="source";
3633             ;;
3634           *)
3635             error "unknown mode ${_arg}";
3636             ;;
3637         esac;
3638         _OPT_MODE="${_mode}";
3639         ;;
3640       --no-location)            # disable former call to `--location'
3641         _OPT_LOCATION='yes';
3642         ;;
3643       --no-man)                 # disable search for man pages
3644         # the same as --local-file
3645         _MAN_FORCE="no";
3646         _MAN_ENABLE="no";
3647         ;;
3648       --pager)                  # set paging program for tty mode, arg
3649         _OPT_PAGER="$1";
3650         shift;
3651         ;;
3652       --pdf)
3653         _OPT_MODE='pdf';
3654         ;;
3655       --pdf-viewer)             # viewer program for ps mode; arg
3656         _OPT_VIEWER_PDF="$1";
3657         shift;
3658         ;;
3659       --ps)
3660         _OPT_MODE='ps';
3661         ;;
3662       --ps-viewer)              # viewer program for ps mode; arg
3663         _OPT_VIEWER_PS="$1";
3664         shift;
3665         ;;
3666       --resolution)             # set resolution for X devices, arg
3667         _arg="$1";
3668         shift;
3669         case "${_arg}" in
3670           75|75dpi)
3671             _dpi=75;
3672             ;;
3673           100|100dpi)
3674             _dpi=100;
3675             ;;
3676           *)
3677             error "only resoutions of 75 or 100 dpi are supported";
3678             ;;
3679         esac;
3680         _OPT_RESOLUTION="${_dpi}";
3681         ;;
3682       --rv)
3683         _OPT_RV='yes';
3684         ;;
3685       --sections)               # specify sections for man pages, arg
3686         # arg is colon-separated list of section names
3687         _OPT_SECTIONS="$1";
3688         shift;
3689         ;;
3690       --shell)
3691         shift;
3692         ;;
3693       --systems)                # man pages for different OS's, arg
3694         # argument is a comma-separated list
3695         _OPT_SYSTEMS="$1";
3696         shift;
3697         ;;
3698       --title)                  # title for X viewers; arg
3699         _OPT_TITLE="$1";
3700         shift;
3701         ;;
3702       --tty)
3703         _OPT_MODE="tty";
3704         ;;
3705       --tty-device)             # device for tty mode; arg
3706         _OPT_TTY_DEVICE="$1";
3707         shift;
3708         ;;
3709       --whatis)
3710         _OPT_WHATIS='yes';
3711         ;;
3712       --www)                    # display with web browser
3713         _OPT_MODE='www';
3714         ;;
3715       --www-viewer)             # viewer program for www mode; arg
3716         _OPT_VIEWER_WWW="$1";
3717         shift;
3718         ;;
3719       --x)
3720         _OPT_MODE='x';
3721         ;;
3722       --xrm)                    # pass X resource string, arg;
3723         _OPT_XRM="$(list_append "${_OPT_XRM}" "$1")";
3724         shift;
3725         ;;
3726       --x-viewer)               # viewer program for x mode; arg
3727         _OPT_VIEWER_X="$1";
3728         shift;
3729         ;;
3730       *)
3731         error 'error on argument parsing : '"\`$*'";
3732         ;;
3733     esac;
3734   done;
3735   shift;                        # remove `--' argument
3736
3737   if test "${_DEBUG}" != 'yes'; then
3738     if test "${_OPT_DEBUG}" = 'yes'; then
3739       _DEBUG='yes';
3740     fi;
3741   fi;
3742
3743   # Remaining arguments are file names (filespecs).
3744   # Save them to list $_FILEARGS
3745   if test "$#" -eq 0; then         # use "-" for standard input
3746     set -- '-';
3747   fi;
3748   _FILEARGS="$(list_from_args "$@")";
3749   if list_has "$_FILEARGS" '-'; then
3750     save_stdin;
3751   fi;
3752   # $_FILEARGS must be retrieved with `eval set -- "$_FILEARGS"'
3753   eval "${return_ok}";
3754 } # main_parse_args()
3755
3756
3757 ########################################################################
3758 # main_set_mode ()
3759 #
3760 # Determine the display mode.
3761 #
3762 # Globals:
3763 #   in:  $DISPLAY, $_OPT_MODE, $_OPT_DEVICE
3764 #   out: $_DISPLAY_MODE
3765 #
3766
3767 # _get_first_prog (<proglist>)
3768 #
3769 # Retrieve first argument that represents an existing program in $PATH.
3770 # Local function for main_set_mode().
3771 #
3772 # Arguments: 1; a comma-separated list of commands (with options),
3773 #               like $_VIEWER_*.
3774 #
3775 # Return  : `1' if none found, `0' if found.
3776 # Output  : the argument that succeded.
3777 #
3778 landmark '16: main_set_mode()';
3779 main_set_mode()
3780 {
3781   func_check main_set_mode = 0 "$@";
3782   local m;
3783   local _modes;
3784   local _viewer;
3785   local _viewers;
3786
3787   # handle apropos
3788   if is_yes "${_OPT_APROPOS}"; then
3789     apropos "$@";
3790     _code="$?";
3791     clean_up;
3792     exit "${_code}";
3793   fi;
3794
3795   # set display
3796   if is_not_empty "${_OPT_DISPLAY}"; then
3797     DISPLAY="${_OPT_DISPLAY}";
3798   fi;
3799
3800   if is_yes "${_OPT_V}"; then
3801     _DISPLAY_MODE='groff';
3802     _ADDOPTS_GROFF="$(list_append "${_ADDOPTS_GROFF}" '-V')";
3803   fi;
3804   if is_yes "${_OPT_X}"; then
3805     _DISPLAY_MODE='groff';
3806     _ADDOPTS_GROFF="$(list_append "${_ADDOPTS_GROFF}" '-X')";
3807   fi;
3808   if is_yes "${_OPT_Z}"; then
3809     _DISPLAY_MODE='groff';
3810     _ADDOPTS_GROFF="$(list_append "${_ADDOPTS_GROFF}" '-Z')";
3811   fi;
3812   if is_equal "${_OPT_MODE}" 'groff'; then
3813     _DISPLAY_MODE='groff';
3814   fi;
3815   if is_equal "${_DISPLAY_MODE}" 'groff'; then
3816     eval "${return_ok}";
3817   fi;
3818
3819   if is_equal "${_OPT_MODE}" 'source'; then
3820     _DISPLAY_MODE='source';
3821     eval "${return_ok}";
3822   fi;
3823
3824   case "${_OPT_MODE}" in
3825     '')                         # automatic mode
3826       case "${_OPT_DEVICE}" in
3827         X*)
3828           if is_empty "${DISPLAY}"; then
3829             error "no X display found for device ${_OPT_DEVICE}";
3830           fi;
3831           _DISPLAY_MODE='x';
3832           eval "${return_ok}";
3833           ;;
3834         ascii|cp1047|latin1|utf8)
3835           _DISPLAY_MODE='tty';
3836           eval "${return_ok}";
3837           ;;
3838       esac;
3839       if is_empty "${DISPLAY}"; then
3840         _DISPLAY_MODE='tty';
3841         eval "${return_ok}";
3842       fi;
3843
3844       if is_empty "${_OPT_DEFAULT_MODES}"; then
3845         _modes="${_DEFAULT_MODES}";
3846       else
3847         _modes="${_OPT_DEFAULT_MODES}";
3848       fi;
3849       ;;
3850     tty)
3851       _DISPLAY_MODE='tty';
3852       eval "${return_ok}";
3853       ;;
3854     *)                          # display mode was given
3855       if is_empty "${DISPLAY}"; then
3856         error "you must be in X Window for ${_OPT_MODE} mode.";
3857       fi;
3858       _modes="${_OPT_MODE}";
3859       ;;
3860   esac;
3861
3862   # only viewer modes are left
3863   eval set -- "$(list_from_split "${_modes}" ',')";
3864   while test "$#" -gt 0; do
3865     m="$1";
3866     shift;
3867     case "$m" in
3868       tty)
3869         _DISPLAY_MODE='tty';
3870         eval "${return_ok}";
3871         ;;
3872       x)
3873         if is_not_empty "${_OPT_VIEWER_X}"; then
3874           _viewers="${_OPT_VIEWER_X}";
3875         else
3876           _viewers="${_VIEWER_X}";
3877         fi;
3878         _viewer="$(_get_first_prog "${_viewers}")";
3879         if test "$?" -ne 0; then
3880           continue;
3881         fi;
3882         _DISPLAY_PROG="${_viewer}";
3883         _DISPLAY_MODE='x';
3884         eval "${return_ok}";
3885         ;;
3886       dvi)
3887         if is_not_empty "${_OPT_VIEWER_DVI}"; then
3888           _viewers="${_OPT_VIEWER_DVI}";
3889         else
3890           _viewers="${_VIEWER_DVI}";
3891         fi;
3892         _viewer="$(_get_first_prog "${_viewers}")";
3893         if test "$?" -ne 0; then
3894           continue;
3895         fi;
3896         _DISPLAY_PROG="${_viewer}";
3897         _DISPLAY_MODE="dvi";
3898         eval "${return_ok}";
3899         ;;
3900       pdf)
3901         if is_not_empty "${_OPT_VIEWER_PDF}"; then
3902           _viewers="${_OPT_VIEWER_PDF}";
3903         else
3904           _viewers="${_VIEWER_PDF}";
3905         fi;
3906         _viewer="$(_get_first_prog "${_viewers}")";
3907         if test "$?" -ne 0; then
3908           continue;
3909         fi;
3910         _DISPLAY_PROG="${_viewer}";
3911         _DISPLAY_MODE="pdf";
3912         eval "${return_ok}";
3913         ;;
3914       ps)
3915         if is_not_empty "${_OPT_VIEWER_PS}"; then
3916           _viewers="${_OPT_VIEWER_PS}";
3917         else
3918           _viewers="${_VIEWER_PS}";
3919         fi;
3920         _viewer="$(_get_first_prog "${_viewers}")";
3921         if test "$?" -ne 0; then
3922           continue;
3923         fi;
3924         _DISPLAY_PROG="${_viewer}";
3925         _DISPLAY_MODE="ps";
3926         eval "${return_ok}";
3927         ;;
3928       www)
3929         if is_not_empty "${_OPT_VIEWER_WWW}"; then
3930           _viewers="${_OPT_VIEWER_WWW}";
3931         else
3932           _viewers="${_VIEWER_WWW}";
3933         fi;
3934         _viewer="$(_get_first_prog "${_viewers}")";
3935         if test "$?" -ne 0; then
3936           continue;
3937         fi;
3938         _DISPLAY_PROG="${_viewer}";
3939         _DISPLAY_MODE="www";
3940         eval "${return_ok}";
3941         ;;
3942     esac;
3943   done;
3944   error "no suitable display mode found.";
3945 }
3946
3947 _get_first_prog()
3948 {
3949   local i;
3950   if test "$#" -eq 0; then
3951     error "_get_first_prog() needs 1 argument.";
3952   fi;
3953   if is_empty "$1"; then
3954     return "${_BAD}";
3955   fi;
3956   eval set -- "$(list_from_split "$1" ',')";
3957   for i in "$@"; do
3958     if is_empty "$i"; then
3959       continue;
3960     fi;
3961     if is_prog "$(get_first_essential $i)"; then
3962       echo -n "$i";
3963       return "${_GOOD}";
3964     fi;
3965   done;
3966   return "${_BAD}";
3967 } # main_set_mode()
3968
3969
3970 #######################################################################
3971 # main_do_fileargs ()
3972 #
3973 # Process filespec arguments in $_FILEARGS.
3974 #
3975 # Globals:
3976 #   in: $_FILEARGS (process with `eval set -- "$_FILEARGS"')
3977 #
3978 landmark '17: main_do_fileargs()';
3979 main_do_fileargs()
3980 {
3981   func_check main_do_fileargs = 0 "$@";
3982   local _exitcode;
3983   local _filespec;
3984   local _name;
3985   _exitcode="${_BAD}";
3986   eval set -- "${_FILEARGS}";
3987   unset _FILEARGS;
3988   # temporary storage of all input to $_TMP_CAT
3989   while test "$#" -ge 2; do
3990     # test for `s name' arguments, with `s' a 1-char standard section
3991     _filespec="$1";
3992     shift;
3993     case "${_filespec}" in
3994       '')
3995         continue;
3996         ;;
3997       '-')
3998         if register_file '-'; then
3999           _exitcode="${_GOOD}";
4000         fi;
4001         continue;
4002         ;;
4003       ?)
4004         if list_has_not "${_MAN_AUTO_SEC}" "${_filespec}"; then
4005           if do_filearg "${_filespec}"; then
4006             _exitcode="${_GOOD}";
4007           fi;
4008           continue;
4009         fi;
4010         _name="$1";
4011         case "${_name}" in
4012           */*|man:*|*\(*\)|*."${_filespec}")
4013             if do_filearg "${_filespec}"; then
4014               _exitcode="${_GOOD}";
4015             fi;
4016             continue;
4017             ;;
4018         esac;
4019         if do_filearg "man:${_name}(${_filespec})"; then
4020           _exitcode="${_GOOD}";
4021           shift;
4022           continue;
4023         else
4024           if do_filearg "${_filespec}"; then
4025             _exitcode="${_GOOD}";
4026           fi;
4027           continue;
4028         fi;
4029         ;;
4030       *)
4031         if do_filearg "${_filespec}"; then
4032           _exitcode="${_GOOD}";
4033         fi;
4034         continue;
4035         ;;
4036     esac;
4037   done;                         # end of `s name' test
4038   while test "$#" -gt 0; do
4039     _filespec="$1";
4040     shift;
4041     if do_filearg "${_filespec}"; then
4042       _exitcode="${_GOOD}";
4043     fi;
4044   done;
4045   clean_up_secondary;
4046   if is_equal "${_exitcode}" "${_BAD}"; then
4047     eval "${return_bad}";
4048   fi;
4049   eval "${return_ok}";
4050 } # main_do_fileargs()
4051
4052
4053 ########################################################################
4054 # main_set_resources ()
4055 #
4056 #  Determine options for setting X resources with $_DISPLAY_PROG.
4057 #
4058 landmark '18: main_set_resources()';
4059 main_set_resources()
4060 {
4061   func_check main_set_resources = 0 "$@";
4062   local _prog;                  # viewer program
4063   local _rl;                    # resource list
4064   _rl='';
4065   if is_empty "${_DISPLAY_PROG}"; then
4066     eval "${return_ok}";
4067   fi;
4068   set -- ${_DISPLAY_PROG};
4069   _prog="$(base_name "$1")";
4070   if is_not_empty "${_OPT_BD}"; then
4071     case "${_prog}" in
4072       ghostview|gv|gxditview|xditview|xdvi)
4073         _rl="$(list_append "$_rl" '-bd' "${_OPT_BD}")";
4074         ;;
4075     esac;
4076   fi;
4077   if is_not_empty "${_OPT_BG}"; then
4078     case "${_prog}" in
4079       ghostview|gv|gxditview|xditview|xdvi)
4080         _rl="$(list_append "$_rl" '-bg' "${_OPT_BG}")";
4081         ;;
4082       xpdf)
4083         _rl="$(list_append "$_rl" '-papercolor' "${_OPT_BG}")";
4084         ;;
4085     esac;
4086   fi;
4087   if is_not_empty "${_OPT_BW}"; then
4088     case "${_prog}" in
4089       ghostview|gv|gxditview|xditview|xdvi)
4090         _rl="$(list_append "$_rl" '-bw' "${_OPT_BW}")";
4091         ;;
4092     esac;
4093   fi;
4094   if is_not_empty "${_OPT_FG}"; then
4095     case "${_prog}" in
4096       ghostview|gv|gxditview|xditview|xdvi)
4097         _rl="$(list_append "$_rl" '-fg' "${_OPT_FG}")";
4098         ;;
4099     esac;
4100   fi;
4101   if is_not_empty "${_OPT_FN}"; then
4102     case "${_prog}" in
4103       ghostview|gv|gxditview|xditview|xdvi)
4104         _rl="$(list_append "$_rl" '-fn' "${_OPT_FN}")";
4105         ;;
4106     esac;
4107   fi;
4108   if is_not_empty "${_OPT_GEOMETRY}"; then
4109     case "${_prog}" in
4110       ghostview|gv|gxditview|xditview|xdvi|xpdf)
4111         _rl="$(list_append "$_rl" '-geometry' "${_OPT_GEOMETRY}")";
4112         ;;
4113     esac;
4114   fi;
4115   if is_empty "${_OPT_RESOLUTION}"; then
4116     case "${_prog}" in
4117       gxditview|xditview)
4118         _rl="$(list_append "$_rl" \
4119                '-resolution' "${_DEFAULT_RESOLUTION}")";
4120         ;;
4121       xpdf)
4122         case "${_DEFAULT_RESOLUTION}" in
4123           75)
4124             _rl="$(list_append "$_rl" '-z' '2')";
4125             ;;
4126           100)
4127             _rl="$(list_append "$_rl" '-z' '3')";
4128             ;;
4129         esac;
4130         ;;
4131     esac;
4132   else
4133     case "${_prog}" in
4134       ghostview|gv|gxditview|xditview|xdvi)
4135         _rl="$(list_append "$_rl" '-resolution' "${_OPT_RESOLUTION}")";
4136         ;;
4137       xpdf)
4138         case "${_OPT_RESOLUTION}" in
4139           75)
4140             _rl="$(list_append "$_rl" '-z' '2')";
4141             ;;
4142           100)
4143             _rl="$(list_append "$_rl" '-z' '3')";
4144             ;;
4145         esac;
4146         ;;
4147     esac;
4148   fi;
4149   if is_not_empty "${_OPT_RV}"; then
4150     case "${_prog}" in
4151       ghostview|gv|gxditview|xditview|xdvi)
4152         _rl="$(list_append "$_rl" '-rv')";
4153         ;;
4154     esac;
4155   fi;
4156   if is_not_empty "${_OPT_XRM}"; then
4157     case "${_prog}" in
4158       ghostview|gv|gxditview|xditview|xdvi|xpdf)
4159         eval set -- "{$_OPT_XRM}";
4160         for i in "$@"; do
4161           _rl="$(list_append "$_rl" '-xrm' "$i")";
4162         done;
4163         ;;
4164     esac;
4165   fi;
4166   _title="$(get_first_essential \
4167                 "${_OPT_TITLE}" "${_REGISTERED_TITLE}")";
4168   if is_not_empty "${_title}"; then
4169     case "${_prog}" in
4170       gxditview|xditview)
4171         _rl="$(list_append "$_rl" '-title' "${_title}")";
4172         ;;
4173     esac;
4174   fi;
4175   _DISPLAY_ARGS="${_rl}"; 
4176   eval "${return_ok}";
4177 } # main_set_resources
4178
4179
4180 ########################################################################
4181 # main_display ()
4182 #
4183 # Do the actual display of the whole thing.
4184 #
4185 # Globals:
4186 #   in: $_DISPLAY_MODE, $_OPT_DEVICE,
4187 #       $_ADDOPTS_GROFF, $_ADDOPTS_POST, $_ADDOPTS_X,
4188 #       $_REGISTERED_TITLE, $_TMP_PREFIX, $_TMP_CAT,
4189 #       $_OPT_PAGER $PAGER $_MANOPT_PAGER
4190 #
4191 landmark '19: main_display()';
4192 main_display()
4193 {
4194   func_check main_display = 0 "$@";
4195   local p;
4196   local _addopts;
4197   local _device;
4198   local _groggy;
4199   local _modefile;
4200   local _options;
4201   local _pager;
4202   local _title;
4203   export _addopts;
4204   export _groggy;
4205   export _modefile;
4206
4207   # Some display programs have trouble with empty input.  
4208   # This is avoided by feeding a space character in this case.
4209   # Test on non-empty file by tracking a line with at least 1 character.
4210   if is_empty "$(tmp_cat | sed -e '/./q')"; then
4211     echo ' ' > "${_TMP_CAT}";
4212   fi;
4213
4214   case "${_DISPLAY_MODE}" in
4215     groff)
4216       _ADDOPTS_GROFF="${_ADDOPTS_GROFF} ${_ADDOPTS_POST}";
4217       if is_not_empty "${_OPT_DEVICE}"; then
4218         _ADDOPTS_GROFF="${_ADDOPTS_GROFF} -T${_OPT_DEVICE}";
4219       fi;
4220       _groggy="$(tmp_cat | eval grog "${_options}")";
4221       trap "" EXIT 2>/dev/null || true;
4222       # start a new shell program to get another process ID.
4223       sh -c '
4224         set -e;
4225         _PROCESS_ID="$$";
4226         _modefile="${_TMP_DIR}/${_PROGRAM_NAME}${_PROCESS_ID}";
4227         rm -f "${_modefile}";
4228         mv "${_TMP_CAT}" "${_modefile}";
4229         rm -f "${_TMP_CAT}";
4230         cat "${_modefile}" | \
4231         (
4232           clean_up()
4233           {
4234             rm -f "${_modefile}";
4235           }
4236           trap clean_up EXIT 2>/dev/null || true;
4237           eval "${_groggy}" "${_ADDOPTS_GROFF}";
4238         ) &'
4239       ;;
4240     tty)
4241       case "${_OPT_DEVICE}" in
4242         '')
4243           _device="$(get_first_essential \
4244                      "${_OPT_TTY_DEVICE}" "${_DEFAULT_TTY_DEVICE}")";
4245           ;;
4246         ascii|cp1047|latin1|utf8)
4247           _device="${_OPT_DEVICE}";
4248           ;;
4249         *)
4250           warning \
4251             "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
4252           ;;
4253       esac;
4254       _addopts="${_ADDOPTS_GROFF} ${_ADDOPTS_POST}";
4255       _groggy="$(tmp_cat | grog -T${_device})";
4256       _pager='';
4257       for p in "${_OPT_PAGER}" "${PAGER}" "${_MANOPT_PAGER}" \
4258                'less' 'more' 'cat'; do
4259         if is_prog "$p"; then
4260           _pager="$p";
4261           break;
4262         fi;
4263       done;
4264       if is_empty "${_pager}"; then
4265         error 'no pager program found for tty mode';
4266       fi;
4267       tmp_cat | eval "${_groggy}" "${_addopts}" | \
4268                 eval "${_pager}";
4269       clean_up;
4270       ;;
4271
4272     #### viewer modes
4273
4274     dvi)
4275       case "${_OPT_DEVICE}" in
4276         ''|dvi) do_nothing; ;;
4277         *)
4278           warning \
4279             "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
4280           ;;
4281       esac;
4282       _groggy="$(tmp_cat | grog -Tdvi)";
4283       _do_display;
4284       ;;
4285     pdf)
4286       case "${_OPT_DEVICE}" in
4287         ''|ps)
4288           do_nothing;
4289           ;;
4290         *)
4291           warning \
4292             "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
4293           ;;
4294       esac;
4295       _groggy="$(tmp_cat | grog -Tps)";
4296       trap "" EXIT 2>/dev/null || true;
4297       # start a new shell program to get another process ID.
4298       sh -c '
4299         set -e;
4300         _PROCESS_ID="$$";
4301         _psfile="${_TMP_DIR}/${_PROGRAM_NAME}${_PROCESS_ID}";
4302         _modefile="${_TMP_DIR}/${_PROGRAM_NAME}${_PROCESS_ID}.pdf";
4303         rm -f "${_psfile}";
4304         rm -f "${_modefile}";
4305         cat "${_TMP_CAT}" | \
4306         eval "${_groggy}" "${_ADDOPTS_GROFF}" > "${_psfile}";
4307         gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite \
4308            -sOutputFile="${_modefile}" -c save pop -f "${_psfile}";
4309         rm -f "${_psfile}";
4310         rm -f "${_TMP_CAT}";
4311         (
4312           clean_up()
4313           {
4314             rm -f "${_modefile}";
4315           }
4316           trap clean_up EXIT 2>/dev/null || true;
4317           eval "${_DISPLAY_PROG}" ${_DISPLAY_ARGS} "${_modefile}";
4318         ) &'
4319       ;;
4320     ps)
4321       case "${_OPT_DEVICE}" in
4322         ''|ps)
4323           do_nothing;
4324           ;;
4325         *)
4326           warning \
4327             "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
4328           ;;
4329       esac;
4330       _groggy="$(tmp_cat | grog -Tps)";
4331       _do_display;
4332       ;;
4333     source)
4334       tmp_cat;
4335       clean_up;
4336       ;;
4337     www)
4338       case "${_OPT_DEVICE}" in
4339         ''|html) do_nothing; ;;
4340         *)
4341           warning \
4342             "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
4343           ;;
4344       esac;
4345       _groggy="$(tmp_cat | grog -Thtml)";
4346       _do_display;
4347       ;;
4348     x)
4349       case "${_OPT_DEVICE}" in
4350         '')
4351           _groggy="$(tmp_cat | grog -Z)";
4352           ;;
4353         X*|ps)
4354           _groggy="$(tmp_cat | grog -T"${_OPT_DEVICE}" -Z)";
4355           ;;
4356         *)
4357           warning \
4358             "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
4359           _groggy="$(tmp_cat | grog -Z)";
4360           ;;
4361       esac;
4362       _do_display;
4363       ;;
4364     *)
4365       error "unknown mode \`${_DISPLAY_MODE}'";
4366       ;;
4367   esac;
4368   eval "${return_ok}";
4369 } # main_display()
4370
4371 _do_display()
4372 {
4373   trap "" EXIT 2>/dev/null || true;
4374   # start a new shell program to get another process ID.
4375   sh -c '
4376     set -e;
4377     _PROCESS_ID="$$";
4378     _modefile="${_TMP_DIR}/${_PROGRAM_NAME}${_PROCESS_ID}";
4379     rm -f "${_modefile}";
4380     cat "${_TMP_CAT}" | \
4381       eval "${_groggy}" "${_ADDOPTS_GROFF}" > "${_modefile}";
4382     rm -f "${_TMP_CAT}";
4383     (
4384       clean_up()
4385       {
4386         rm -f "${_modefile}";
4387       }
4388       trap clean_up EXIT 2>/dev/null || true;
4389       eval "${_DISPLAY_PROG}" ${_DISPLAY_ARGS} "${_modefile}";
4390     ) &'
4391 }
4392
4393
4394 ########################################################################
4395 # main (<command_line_args>*)
4396 #
4397 # The main function for groffer.
4398 #
4399 # Arguments:
4400 #
4401 main()
4402 {
4403   func_check main '>=' 0 "$@";
4404   # Do not change the sequence of the following functions!
4405   main_init;
4406   main_parse_MANOPT;
4407   main_parse_args "$@";
4408   main_set_mode;
4409   main_do_fileargs;
4410   main_set_resources;
4411   main_display;
4412   eval "${return_ok}";
4413 }
4414
4415 landmark '20: end of function definitions';
4416
4417 ########################################################################
4418
4419 main "$@";