#! /bin/sh

# groffer - display groff files

# Source file position: <groff-source>/contrib/groffer/groffer2.sh
# Installed position: <prefix>/lib/groff/groffer/groffer2.sh

# This file should not be run independently.  It is called by
# `groffer.sh' in the source or by the installed `groffer' program.

# Copyright (C) 2001,2002,2003,2004,2005
# Free Software Foundation, Inc.
# Written by Bernd Warken

# Last update: 22 August 2005

# This file is part of `groffer', which is part of `groff'.

# `groff' is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.

# `groff' is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with `groff'; see the files COPYING and LICENSE in the top
# directory of the `groff' source.  If not, write to the Free Software
# Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301,
# USA.


########################################################################
#             Test of rudimentary shell functionality
########################################################################


########################################################################
# Test of `unset'
#
export _UNSET;
export _foo;
_foo=bar;
_res="$(unset _foo 2>&1)";
if unset _foo >${_NULL_DEV} 2>&1 && \
  test _"${_res}"_ = __ && test _"${_foo}"_ = __
then
 _UNSET='unset';
 eval "${_UNSET}" _foo;
 eval "${_UNSET}" _res;
else
 _UNSET=':';
fi;


########################################################################
# Test of `test'.
#
if test a = a && test a != b && test -f "${_GROFFER_SH}"
then
 :;
else
 echo '"test" did not work.' >&2;
 exit "${_ERROR}";
fi;


########################################################################
# Test of `echo' and the `$()' construct.
#
if echo '' >${_NULL_DEV}
then
 :;
else
 echo '"echo" did not work.' >&2;
 exit "${_ERROR}";
fi;
if test _"$(t1="$(echo te)" &&
           t2="$(echo '')" &&
           t3="$(echo 'st')" &&
           echo "${t1}${t2}${t3}")"_ \
    != _test_
then
 echo 'The "$()" construct did not work' >&2;
 exit "${_ERROR}";
fi;


########################################################################
# Test of sed program; test in groffer.sh is not valid here.
#
if test _"$(echo red | sed -e 's/r/s/')"_ != _sed_
then
 echo 'The sed program did not work.' >&2;
 exit "${_ERROR}";
fi;


########################################################################
# Test of function definitions.
#
_t_e_s_t_f_u_n_c_()
{
 return 0;
}

if _t_e_s_t_f_u_n_c_ 2>${_NULL_DEV}
then
 :;
else
 echo 'Shell '"${_SHELL}"' does not support function definitions.' >&2;
 exit "${_ERROR}";
fi;


########################################################################
#                    debug - diagnostic messages
########################################################################

export _DEBUG_STACKS;
_DEBUG_STACKS='no';             # disable stack output in each function
#_DEBUG_STACKS='yes';           # enable stack output in each function

export _DEBUG_LM;
_DEBUG_LM='no';                 # disable landmark messages
#_DEBUG_LM='yes';               # enable landmark messages

export _DEBUG_KEEP_FILES;
_DEBUG_KEEP_FILES='no'          # disable file keeping in temporary dir
#_DEBUG_KEEP_FILES='yes'        # enable file keeping in temporary dir

export _DEBUG_PRINT_PARAMS;
_DEBUG_PRINT_PARAMS='no';       # disable printing of all parameters
#_DEBUG_PRINT_PARAMS='yes';     # enable printing of all parameters

export _DEBUG_PRINT_SHELL;
_DEBUG_PRINT_SHELL='no';        # disable printing of the shell name
#_DEBUG_PRINT_SHELL='yes';      # enable printing of the shell name

export _DEBUG_PRINT_TMPDIR;
_DEBUG_PRINT_TMPDIR='no';       # disable printing of the temporary dir
#_DEBUG_PRINT_TMPDIR='yes';     # enable printing of the temporary dir

export _DEBUG_USER_WITH_STACK;
_DEBUG_USER_WITH_STACK='no';    # disable stack dump in error_user()
#_DEBUG_USER_WITH_STACK='yes';  # enable stack dump in error_user()

# determine all --debug* options
case " $*" in
*\ --debug*)
 case " $* " in
 *' --debug '*)
   # _DEBUG_STACKS='yes';
   # _DEBUG_LM='yes';
   _DEBUG_KEEP_FILES='yes';
   _DEBUG_PRINT_PARAMS='yes';
   _DEBUG_PRINT_SHELL='yes';
   _DEBUG_PRINT_TMPDIR='yes';
   _DEBUG_USER_WITH_STACK='yes';
   ;;
 esac;
 d=' --debug-all --debug-keep --debug-lm --debug-params --debug-shell '\
'--debug-stacks --debug-tmpdir --debug-user ';
 for i
 do
   case "$i" in
   --debug-s)
     echo 'The abbreviation --debug-s has multiple options: '\
'--debug-shell and --debug-stacks.' >&2
     exit "${_ERROR}";
     ;;
   esac;
   case "$d" in
   *\ ${i}*)
     # extract whole word of abbreviation $i
     s="$(cat <<EOF | sed -n -e 's/^.* \('"$i"'[^ ]*\) .*/\1/p'
$d
EOF
)"
     case "$s" in
     '') continue; ;;
     --debug-all)
       _DEBUG_STACKS='yes';
       _DEBUG_LM='yes';
       _DEBUG_KEEP_FILES='yes';
       _DEBUG_PRINT_PARAMS='yes';
       _DEBUG_PRINT_SHELL='yes';
       _DEBUG_PRINT_TMPDIR='yes';
       _DEBUG_USER_WITH_STACK='yes';
       ;;
     --debug-keep)
       _DEBUG_PRINT_TMPDIR='yes';
       _DEBUG_KEEP_FILES='yes';
       ;;
     --debug-lm)
       _DEBUG_LM='yes';
       ;;
     --debug-params)
       _DEBUG_PRINT_PARAMS='yes';
       ;;
     --debug-shell)
       _DEBUG_PRINT_SHELL='yes';
       ;;
     --debug-stacks)
       _DEBUG_STACKS='yes';
       ;;
     --debug-tmpdir)
       _DEBUG_PRINT_TMPDIR='yes';
       ;;
     --debug-user)
       _DEBUG_USER_WITH_STACK='yes';
       ;;
     esac;
     ;;
   esac;
 done
 ;;
esac;

if test _"${_DEBUG_PRINT_PARAMS}"_ = _yes_
then
 echo "parameters: $@" >&2;
fi;

if test _"${_DEBUG_PRINT_SHELL}"_ = _yes_
then
 if test _"${_SHELL}"_ = __
 then
   if test _"${POSIXLY_CORRECT}"_ = _y_
   then
     echo 'shell: bash as /bin/sh (none specified)' >&2;
   else
     echo 'shell: /bin/sh (none specified)' >&2;
   fi;
 else
   echo "shell: ${_SHELL}" >&2;
 fi;
fi;


########################################################################
#                       Environment Variables
########################################################################

# Environment variables that exist only for this file start with an
# underscore letter.  Global variables to this file are written in
# upper case letters, e.g. $_GLOBAL_VARIABLE; temporary variables
# start with an underline and use only lower case letters and
# underlines, e.g.  $_local_variable .

#   [A-Z]*     system variables,      e.g. $MANPATH
#   _[A-Z_]*   global file variables, e.g. $_MAN_PATH
#   _[a-z_]*   temporary variables,   e.g. $_manpath

# Due to incompatibilities of the `ash' shell, the name of loop
# variables in `for' must be single character
#   [a-z]      local loop variables,   e.g. $i


########################################################################
# read-only variables (global to this file)
########################################################################

# function return values; `0' means ok; other values are error codes
export _ALL_EXIT;
export _BAD;
export _GOOD;
export _NO;
export _OK;
export _YES;

_GOOD='0';                      # return ok
_BAD='1';                       # return negatively, error code `1'
# $_ERROR was already defined as `7' in groffer.sh.

_NO="${_BAD}";
_YES="${_GOOD}";
_OK="${_GOOD}";

# quasi-functions, call with `eval', e.g `eval "${return_ok}"'
export return_ok;
export return_good;
export return_bad;
export return_yes;
export return_no;
export return_error;
export return_var;
return_ok="func_pop; return ${_OK}";
return_good="func_pop; return ${_GOOD}";
return_bad="func_pop; return ${_BAD}";
return_yes="func_pop; return ${_YES}";
return_no="func_pop; return ${_NO}";
return_error="func_pop; return ${_ERROR}";
return_var="func_pop; return";  # add number, e.g. `eval "${return_var} $n'


export _DEFAULT_MODES;
_DEFAULT_MODES='x,ps,tty';
export _DEFAULT_RESOLUTION;
_DEFAULT_RESOLUTION='75';

export _DEFAULT_TTY_DEVICE;
_DEFAULT_TTY_DEVICE='latin1';

# _VIEWER_* viewer programs for different modes (only X is necessary)
# _VIEWER_* a comma-separated list of viewer programs (with options)
export _VIEWER_DVI;             # viewer program for dvi mode
export _VIEWER_HTML_TTY;        # viewer program for html mode in tty
export _VIEWER_HTML_X;          # viewer program for html mode in X
export _VIEWER_PDF;             # viewer program for pdf mode
export _VIEWER_PS;              # viewer program for ps mode
export _VIEWER_X;               # viewer program for X mode
_VIEWER_DVI='kdvi,xdvi,dvilx';
_VIEWER_HTML_TTY='lynx';
_VIEWER_HTML_X='konqueror,mozilla,netscape,galeon,opera,amaya,arena';
_VIEWER_PDF='kghostview --scale 1.45,ggv,xpdf,acroread,kpdf';
_VIEWER_PS='kghostview --scale 1.45,ggv,gv,ghostview,gs_x11,gs';
_VIEWER_X='gxditview,xditview';

# Search automatically in standard sections `1' to `8', and in the
# traditional sections `9', `n', and `o'.  On many systems, there
# exist even more sections, mostly containing a set of man pages
# special to a specific program package.  These aren't searched for
# automatically, but must be specified on the command line.
export _MAN_AUTO_SEC_LIST;
_MAN_AUTO_SEC_LIST="'1' '2' '3' '4' '5' '6' '7' '8' '9' 'n' 'o'";
export _MAN_AUTO_SEC_CHARS;
_MAN_AUTO_SEC_CHARS='[123456789no]';

export _SPACE_SED;
_SPACE_SED='['"${_SP}${_TAB}"']';

export _SPACE_CASE;
_SPACE_CASE='[\'"${_SP}"'\'"${_TAB}"']';

export _PROCESS_ID;             # for shutting down the program
_PROCESS_ID="$$";


############ the command line options of the involved programs
#
# The naming scheme for the options environment names is
# $_OPTS_<prog>_<length>[_<argspec>]
#
# <prog>:    program name GROFFER, GROFF, or CMDLINE (for all
#            command line options)
# <length>:  LONG (long options) or SHORT (single character options)
# <argspec>: ARG for options with argument, NA for no argument;
#            without _<argspec> both the ones with and without arg.
#
# Each option that takes an argument must be specified with a
# trailing : (colon).

# exports
export _OPTS_GROFFER_SHORT_NA;
export _OPTS_GROFFER_SHORT_ARG;
export _OPTS_GROFFER_LONG_NA;
export _OPTS_GROFFER_LONG_ARG;
export _OPTS_GROFF_SHORT_NA;
export _OPTS_GROFF_SHORT_ARG;
export _OPTS_GROFF_LONG_NA;
export _OPTS_GROFF_LONG_ARG;
export _OPTS_X_SHORT_ARG;
export _OPTS_X_SHORT_NA;
export _OPTS_X_LONG_ARG;
export _OPTS_X_LONG_NA;
export _OPTS_MAN_SHORT_ARG;
export _OPTS_MAN_SHORT_NA;
export _OPTS_MAN_LONG_ARG;
export _OPTS_MAN_LONG_NA;
export _OPTS_MANOPT_SHORT_ARG;
export _OPTS_MANOPT_SHORT_NA;
export _OPTS_MANOPT_LONG_ARG;
export _OPTS_MANOPT_LONG_NA;
export _OPTS_CMDLINE_SHORT_NA;
export _OPTS_CMDLINE_SHORT_ARG;
export _OPTS_CMDLINE_LONG_NA;
export _OPTS_CMDLINE_LONG_ARG;

###### groffer native options

_OPTS_GROFFER_SHORT_NA="'h' 'Q' 'v' 'V' 'X' 'Z'";
_OPTS_GROFFER_SHORT_ARG="'T'";

_OPTS_GROFFER_LONG_NA="'auto' \
'apropos' 'apropos-data' 'apropos-devel' 'apropos-progs' \
'debug' 'debug-all' 'debug-keep' 'debug-lm' 'debug-params' 'debug-shell' \
'debug-stacks' 'debug-tmpdir' 'debug-user' 'default' 'do-nothing' 'dvi' \
'groff' 'help' 'intermediate-output' 'html' 'man' \
'no-location' 'no-man' 'no-special' 'pdf' 'ps' 'rv' 'source' \
'text' 'text-device' \
'tty' 'tty-device' 'version' 'whatis' 'where' 'www' 'x' 'X'";

_OPTS_GROFFER_LONG_ARG="\
'default-modes' 'device' 'dvi-viewer' 'dvi-viewer-tty' 'extension' 'fg' \
'fn' 'font' 'foreground' 'html-viewer' 'html-viewer-tty' 'mode' \
'pdf-viewer' 'pdf-viewer-tty' 'print' 'ps-viewer' 'ps-viewer-tty' 'shell' \
'title' 'tty-viewer' 'tty-viewer-tty' 'www-viewer' 'www-viewer-tty' \
'x-viewer' 'x-viewer-tty' 'X-viewer' 'X-viewer-tty'";

##### groffer options inhereted from groff

_OPTS_GROFF_SHORT_NA="'a' 'b' 'c' 'C' 'e' 'E' 'g' 'G' 'i' 'l' 'N' 'p' \
'R' 's' 'S' 't' 'U' 'z'";
_OPTS_GROFF_SHORT_ARG="'d' 'f' 'F' 'I' 'L' 'm' 'M' 'n' 'o' 'P' 'r' \
'w' 'W'";
_OPTS_GROFF_LONG_NA="";
_OPTS_GROFF_LONG_ARG="";

##### groffer options inhereted from the X Window toolkit

_OPTS_X_SHORT_NA="";
_OPTS_X_SHORT_ARG="";

_OPTS_X_LONG_NA="'iconic' 'rv'";

_OPTS_X_LONG_ARG="'background' 'bd' 'bg' 'bordercolor' 'borderwidth' \
'bw' 'display' 'fg' 'fn' 'font' 'foreground' 'ft' 'geometry' \
'resolution' 'title' 'xrm'";

###### groffer options inherited from man

_OPTS_MAN_SHORT_NA="";
_OPTS_MAN_SHORT_ARG="";

_OPTS_MAN_LONG_NA="'all' 'ascii' 'catman' 'ditroff' \
'local-file' 'location' 'troff' 'update'";

_OPTS_MAN_LONG_ARG="'locale' 'manpath' \
'pager' 'preprocessor' 'prompt' 'sections' 'systems' 'troff-device'";

###### additional options for parsing $MANOPT only

_OPTS_MANOPT_SHORT_NA="'7' 'a' 'c' 'd' 'D' 'f' 'h' 'k' 'l' 't' 'u' \
'V' 'w' 'Z'";
_OPTS_MANOPT_SHORT_ARG="'e' 'L' 'm' 'M' 'p' 'P' 'r' 'S' 'T'";

_OPTS_MANOPT_LONG_NA="${_OPTS_MAN_LONG_NA} \
'apropos' 'debug' 'default' 'help' 'html' 'ignore-case' 'location-cat' \
'match-case' 'troff' 'update' 'version' 'whatis' 'where' 'where-cat'";

_OPTS_MANOPT_LONG_ARG="${_OPTS_MAN_LONG_NA} \
'config_file' 'encoding' 'extension' 'locale'";

###### collections of command line options

_OPTS_CMDLINE_SHORT_NA="${_OPTS_GROFFER_SHORT_NA} \
${_OPTS_GROFF_SHORT_NA} ${_OPTS_X_SHORT_NA} ${_OPTS_MAN_SHORT_NA}";
_OPTS_CMDLINE_SHORT_ARG="${_OPTS_GROFFER_SHORT_ARG} \
${_OPTS_GROFF_SHORT_ARG} ${_OPTS_X_SHORT_ARG} ${_OPTS_MAN_SHORT_ARG}";

_OPTS_CMDLINE_LONG_NA="${_OPTS_GROFFER_LONG_NA} \
${_OPTS_GROFF_LONG_NA} ${_OPTS_X_LONG_NA} ${_OPTS_MAN_LONG_NA}";
_OPTS_CMDLINE_LONG_ARG="${_OPTS_GROFFER_LONG_ARG} \
${_OPTS_GROFF_LONG_ARG} ${_OPTS_MAN_LONG_ARG} ${_OPTS_X_LONG_ARG}";


########################################################################
# read-write variables (global to this file)
########################################################################

export _ALL_PARAMS;             # All options and file name parameters
export _ADDOPTS_GROFF;          # Transp. options for groff (`eval').
export _ADDOPTS_POST;           # Transp. options postproc (`eval').
export _ADDOPTS_X;              # Transp. options X postproc (`eval').
export _APROPOS_PROG;           # Program to run apropos.
export _APROPOS_SECTIONS;       # Sections for different --apropos-*.
export _DEFAULT_MODES;          # Set default modes.
export _DISPLAY_MODE;           # Display mode.
export _DISPLAY_PROG;           # Viewer program to be used for display.
export _DISPLAY_ARGS;           # X resources for the viewer program.
export _FILEARGS;               # Stores filespec parameters.
export _FILESPEC_ARG;           # Stores the actual filespec parameter.
export _FUNC_STACK;             # Store debugging information.
export _REGISTERED_TITLE;       # Processed file names.
# _HAS_* from availability tests
export _HAS_COMPRESSION;        # `yes' if gzip compression is available
export _HAS_BZIP;               # `yes' if bzip2 compression is available
# _MAN_* finally used configuration of man searching
export _MAN_ALL;                # search all man pages per filespec
export _MAN_ENABLE;             # enable search for man pages
export _MAN_EXT;                # extension for man pages
export _MAN_FORCE;              # force file parameter to be man pages
export _MAN_IS_SETUP;           # setup man variables only once
export _MAN_LANG;               # language for man pages
export _MAN_LANG2;              # language for man pages
export _MAN_LANG_DONE;          # language dirs added to man path
export _MAN_PATH;               # search path for man pages
export _MAN_SEC;                # sections for man pages; sep. `:'
export _MAN_SEC_DONE;           # sections added to man path
export _MAN_SYS;                # system names for man pages; sep. `,'
export _MAN_SYS;                # system names added to man path
# _MANOPT_* as parsed from $MANOPT
export _MANOPT_ALL;             # $MANOPT --all
export _MANOPT_EXTENSION;       # $MANOPT --extension
export _MANOPT_LANG;            # $MANOPT --locale
export _MANOPT_PATH;            # $MANOPT --manpath
export _MANOPT_PAGER;           # $MANOPT --pager
export _MANOPT_SEC;             # $MANOPT --sections
export _MANOPT_SYS;             # $MANOPT --systems
# _OPT_* as parsed from groffer command line
export _OPT_ALL;                # display all suitable man pages.
export _OPT_APROPOS;            # call `apropos' program.
export _OPT_BD;                 # set border color in some modes.
export _OPT_BG;                 # set background color in some modes.
export _OPT_BW;                 # set border width in some modes.
export _OPT_DEFAULT_MODES;      # `,'-list of modes when no mode given.
export _OPT_DEVICE;             # device option.
export _OPT_DO_NOTHING;         # do nothing in main_display().
export _OPT_DISPLAY;            # set X display.
export _OPT_FG;                 # set foreground color in some modes.
export _OPT_FN;                 # set font in some modes.
export _OPT_GEOMETRY;           # set size and position of viewer in X.
export _OPT_ICONIC;             # -iconic option for X viewers.
export _OPT_LANG;               # set language for man pages
export _OPT_LOCATION;           # print processed file names to stderr
export _OPT_MODE;               # values: X, tty, Q, Z, ""
export _OPT_MANPATH;            # manual setting of path for man-pages
export _OPT_PAGER;              # specify paging program for tty mode
export _OPT_RESOLUTION;         # set X resolution in dpi
export _OPT_RV;                 # reverse fore- and background colors.
export _OPT_SECTIONS;           # sections for man page search
export _OPT_SYSTEMS;            # man pages of different OS's
export _OPT_TITLE;              # title for gxditview window
export _OPT_TEXT_DEVICE;        # set device for tty mode.
export _OPT_V;                  # groff option -V.
export _OPT_VIEWER_DVI;         # viewer program for dvi mode
export _OPT_VIEWER_PDF;         # viewer program for pdf mode
export _OPT_VIEWER_PS;          # viewer program for ps mode
export _OPT_VIEWER_HTML;        # viewer program for html mode
export _OPT_VIEWER_X;           # viewer program for x mode
export _OPT_WHATIS;             # print the man description
export _OPT_XRM;                # specify X resource.
export _OPT_Z;                  # groff option -Z.
export _OUTPUT_FILE_NAME;       # output generated, see main_set_res..()
export _VIEWER_TERMINAL;        # viewer options for terminal (--*-viewer-tty)
# _TMP_* temporary directory and files
export _TMP_DIR;                # groffer directory for temporary files
export _TMP_CAT;                # stores concatenation of everything
export _TMP_STDIN;              # stores stdin, if any

# these variables are preset in section `Preset' after the rudim. test


########################################################################
# Preset and reset of read-write global variables
########################################################################


export _START_DIR;              # directory at start time of the script
_START_DIR="$(pwd)";

# For variables that can be reset by option `--default', see reset().

_FILEARGS='';

# _HAS_* from availability tests
_HAS_COMPRESSION='';
_HAS_BZIP='';

# _TMP_* temporary files
_TMP_DIR='';
_TMP_CAT='';
_TMP_CONF='';
_TMP_STDIN='';


########################################################################
# reset ()
#
# Reset the variables that can be affected by options to their default.
#
reset()
{
 if test "$#" -ne 0
 then
   error "reset() does not have arguments.";
 fi;

 _ADDOPTS_GROFF='';
 _ADDOPTS_POST='';
 _ADDOPTS_X='';
 _APROPOS_PROG='';
 _APROPOS_SECTIONS='';
 _DISPLAY_ARGS='';
 _DISPLAY_MODE='';
 _DISPLAY_PROG='';
 _REGISTERED_TITLE='';

 # _MAN_* finally used configuration of man searching
 _MAN_ALL='no';
 _MAN_ENABLE='yes';            # do search for man-pages
 _MAN_EXT='';
 _MAN_FORCE='no';              # first local file, then search man page
 _MAN_IS_SETUP='no';
 _MAN_LANG='';
 _MAN_LANG2='';
 _MAN_PATH='';
 _MAN_SEC='';
 _MAN_SEC_DONE='no';
 _MAN_SYS='';
 _MAN_SYS_DONE='no';

 # _MANOPT_* as parsed from $MANOPT
 _MANOPT_ALL='no';
 _MANOPT_EXTENSION='';
 _MANOPT_LANG='';
 _MANOPT_PATH='';
 _MANOPT_PAGER='';
 _MANOPT_SEC='';
 _MANOPT_SYS='';

 # _OPT_* as parsed from groffer command line
 _OPT_ALL='no';
 _OPT_APROPOS='no';
 _OPT_BD='';
 _OPT_BG='';
 _OPT_BW='';
 _OPT_DEFAULT_MODES='';
 _OPT_DEVICE='';
 _OPT_DISPLAY='';
 _OPT_DO_NOTHING='no';
 _OPT_FG='';
 _OPT_FN='';
 _OPT_GEOMETRY='';
 _OPT_ICONIC='no';
 _OPT_LANG='';
 _OPT_LOCATION='no';
 _OPT_MODE='';
 _OPT_MANPATH='';
 _OPT_PAGER='';
 _OPT_RESOLUTION='';
 _OPT_RV='no';
 _OPT_SECTIONS='';
 _OPT_SYSTEMS='';
 _OPT_TITLE='';
 _OPT_TEXT_DEVICE='';
 _OPT_V='no';
 _OPT_VIEWER_DVI='';
 _OPT_VIEWER_PDF='';
 _OPT_VIEWER_PS='';
 _OPT_VIEWER_HTML='';
 _OPT_VIEWER_X='';
 _OPT_WHATIS='no';
 _OPT_XRM='';
 _OPT_Z='no';
 _VIEWER_TERMINAL='no';
}

reset;


########################################################################
#          Functions for error handling and debugging
########################################################################


##############
# echo1 (<text>*)
#
# Output to stdout.
#
# Arguments : arbitrary text including `-'.
#
echo1()
{
 cat <<EOF
$@
EOF
}


##############
# echo2 (<text>*)
#
# Output to stderr.
#
# Arguments : arbitrary text.
#
echo2()
{
 cat >&2 <<EOF
$@
EOF
}


##############
# landmark (<text>)
#
# Print <text> to standard error as a debugging aid.
#
# Globals: $_DEBUG_LM
#
landmark()
{
 if test _"${_DEBUG_LM}"_ = _yes_
 then
   echo2 "LM: $*";
 fi;
}

landmark "1: debugging functions";


##############
# clean_up ()
#
# Clean up at exit.
#
clean_up()
{
 cd "${_START_DIR}" >"${_NULL_DEV}" 2>&1;
 if test _${_DEBUG_KEEP_FILES}_ = _yes_
 then
   echo2 "Kept temporary directory ${_TMP_DIR}."
 else
   if test _"${_TMP_DIR}"_ != __
   then
     if test -d "${_TMP_DIR}" || test -f "${_TMP_DIR}"
     then
       rm -f -r "${_TMP_DIR}" >${_NULL_DEV} 2>&1;
     fi;
   fi;
 fi;
}


#############
# diag (text>*)
#
# Output a diagnostic message to stderr
#
diag()
{
 echo2 '>>>>>'"$*";
}


#############
# error (<text>*)
#
# Print an error message to standard error, print the function stack,
# exit with an error condition.  The argument should contain the name
# of the function from which it was called.  This is for system errors.
#
error()
{
 case "$#" in
   1) echo2 'groffer error: '"$1"; ;;
   *) echo2 'groffer error: wrong number of arguments in error().'; ;;
 esac;
 func_stack_dump;
 if test _"${_TMP_DIR}"_ != __ && test -d "${_TMP_DIR}"
 then
   : >"${_TMP_DIR}"/,error;
 fi;
 exit "${_ERROR}";
}


#############
# error_user (<text>*)
#
# Print an error message to standard error; exit with an error condition.
# The error is supposed to be produce by the user.  So the funtion stack
# is omitted.
#
error_user()
{
 case "$#" in
   1)
     echo2 'groffer error: '"$1";
      ;;
   *)
     echo2 'groffer error: wrong number of arguments in error_user().';
     ;;
 esac;
 if test _"${_DEBUG_USER_WITH_STACK}"_ = _yes_
 then
   func_stack_dump;
 fi;
 if test _"${_TMP_DIR}"_ != __ && test -d "${_TMP_DIR}"
 then
   : >"${_TMP_DIR}"/,error;
 fi;
 exit "${_ERROR}";
}


#############
# exit_test ()
#
# Test whether the former command ended with error().  Exit again.
#
# Globals: $_ERROR
#
exit_test()
{
 if test "$?" = "${_ERROR}"
 then
   exit ${_ERROR};
 fi;
 if test _"${_TMP_DIR}"_ != __ && test -f "${_TMP_DIR}"/,error
 then
   exit ${_ERROR};
 fi;
}


#############
# func_check (<func_name> <rel_op> <nr_args> "$@")
#
# Check number of arguments and register to _FUNC_STACK.
#
# Arguments: >=3
#   <func_name>: name of the calling function.
#   <rel_op>:    a relational operator: = != < > <= >=
#   <nr_args>:   number of arguments to be checked against <operator>
#   "$@":        the arguments of the calling function.
#
# Variable prefix: fc
#
func_check()
{
 if test "$#" -lt 3
 then
   error 'func_check() needs at least 3 arguments.';
 fi;
 fc_fname="$1";
 case "$3" in
   1)
     fc_nargs="$3";
     fc_s='';
     ;;
   0|[2-9])
     fc_nargs="$3";
     fc_s='s';
     ;;
   *)
     error "func_check(): third argument must be a digit.";
     ;;
 esac;
 case "$2" in
   '='|'-eq')
     fc_op='-eq';
     fc_comp='exactly';
     ;;
   '>='|'-ge')
     fc_op='-ge';
     fc_comp='at least';
     ;;
   '<='|'-le')
     fc_op='-le';
     fc_comp='at most';
     ;;
   '<'|'-lt')
     fc_op='-lt';
     fc_comp='less than';
     ;;
   '>'|'-gt')
     fc_op='-gt';
     fc_comp='more than';
     ;;
   '!='|'-ne')
     fc_op='-ne';
     fc_comp='not';
     ;;
   *)
     error \
       'func_check(): second argument is not a relational operator.';
     ;;
 esac;
 shift;
 shift;
 shift;
 if test "$#" "${fc_op}" "${fc_nargs}"
 then
   do_nothing;
 else
   error "func_check(): \
${fc_fname}"'() needs '"${fc_comp} ${fc_nargs}"' argument'"${fc_s}"'.';
 fi;
 func_push "${fc_fname}";
 if test _"${_DEBUG_STACKS}"_ = _yes_
 then
   echo2 '+++ '"${fc_fname} $@";
   echo2 '>>> '"${_FUNC_STACK}";
 fi;
 eval ${_UNSET} fc_comp;
 eval ${_UNSET} fc_fname;
 eval ${_UNSET} fc_nargs;
 eval ${_UNSET} fc_op;
 eval ${_UNSET} fc_s;
}


#############
# func_pop ()
#
# Retrieve the top element from the stack.
#
# The stack elements are separated by `!'; the popped element is
# identical to the original element, except that all `!' characters
# were removed.
#
# Arguments: 1
#
func_pop()
{
 if test "$#" -ne 0
 then
   error 'func_pop() does not have arguments.';
 fi;
 case "${_FUNC_STACK}" in
 '')
   if test _"${_DEBUG_STACKS}"_ = _yes_
   then
     error 'func_pop(): stack is empty.';
   fi;
   ;;
 *!*)
   # split at first bang `!'.
   _FUNC_STACK="$(echo1 "${_FUNC_STACK}" | sed -e 's/^[^!]*!//')";
   exit_test;
   ;;
 *)
   _FUNC_STACK='';
   ;;
 esac;
 if test _"${_DEBUG_STACKS}"_ = _yes_
 then
   echo2 '<<< '"${_FUNC_STACK}";
 fi;
}


#############
# func_push (<element>)
#
# Store another element to stack.
#
# The stack elements are separated by `!'; if <element> contains a `!'
# it is removed first.
#
# Arguments: 1
#
# Variable prefix: fp
#
func_push()
{
 if test "$#" -ne 1
 then
   error 'func_push() needs 1 argument.';
 fi;
 case "$1" in
 *'!'*)
   # remove all bangs `!'.
   fp_element="$(echo1 "$1" | sed -e 's/!//g')";
   exit_test;
   ;;
 *)
   fp_element="$1";
   ;;
 esac;
 if test _"${_FUNC_STACK}"_ = __
 then
   _FUNC_STACK="${fp_element}";
 else
   _FUNC_STACK="${fp_element}!${_FUNC_STACK}";
 fi;
 eval ${_UNSET} fp_element;
}


#############
# func_stack_dump ()
#
# Print the content of the stack.  Ignore the arguments.
#
func_stack_dump()
{
 diag 'call stack: '"${_FUNC_STACK}";
}


########################################################################
#                        System Test
########################################################################

landmark "2: system test";

# Test the availability of the system utilities used in this script.


########################################################################
# Test of function `sed'.
#

if test _"$(echo xTesTx \
          | sed -e 's/^.\([Tt]e*x*sTT*\).*$/\1/' \
          | sed -e 's|T|t|g')"_ != _test_
then
 error 'Test of "sed" command failed.';
fi;


########################################################################
# Test of function `cat'.
#
if test _"$(echo test | cat)"_ != _test_
then
 error 'Test of "cat" command failed.';
fi;


########################################################################
# Test for compression.
#
if test _"$(echo 'test' | gzip -c -d -f - 2>${_NULL_DEV})"_ = _test_
then
 _HAS_COMPRESSION='yes';
 if echo1 'test' | bzip2 -c 2>${_NULL_DEV} | bzip2 -t 2>${_NULL_DEV} \
    && test _"$(echo 'test' | bzip2 -c 2>${_NULL_DEV} \
                            | bzip2 -d -c 2>${_NULL_DEV})"_ \
            = _test_
 then
   _HAS_BZIP='yes';
 else
   _HAS_BZIP='no';
 fi;
else
 _HAS_COMPRESSION='no';
 _HAS_BZIP='no';
fi;


########################################################################
#       Definition of normal Functions in alphabetical order
########################################################################
landmark "3: functions";

########################################################################
# apropos_filespec ()
#
# Setup for the --apropos* options
#
apropos_filespec()
{

 func_check apropos_filespec '=' 0 "$@";
 if obj _OPT_APROPOS is_yes
 then
   eval to_tmp_line \
     "'.SH $(echo1 "${_FILESPEC_ARG}" | sed 's/[^\\]-/\\-/g')'";
   exit_test;
   if obj _APROPOS_PROG is_empty
   then
     error 'apropos_filespec: apropos_setup() must be run first.';
   fi;
   if obj _APROPOS_SECTIONS is_empty
   then
     if obj _OPT_SECTIONS is_empty
     then
       s='^.*(.*).*$';
     else
       s='^.*(['"$(echo1 "${_OPT_SECTIONS}" | sed -e 's/://g')"']';
     fi;
   else
     s='^.*(['"${_APROPOS_SECTIONS}"']';
   fi;
   eval "${_APROPOS_PROG}" "'${_FILESPEC_ARG}'" | \
     sed -n -e '
/^'"${_FILESPEC_ARG}"': /p
/'"$s"'/p
' | \
     sort |\
     sed -e '
s/^\(.* (..*)\)  *-  *\(.*\)$/\.br\n\.TP 15\n\.BR \1\n\2/
' >>"${_TMP_CAT}";
 fi;
 eval "${return_ok}";
}


########################################################################
# apropos_setup ()
#
# Setup for the --apropos* options
#
apropos_setup()
{
 func_check apropos_setup '=' 0 "$@";
 if obj _OPT_APROPOS is_yes
 then
   if is_prog apropos
   then
     _APROPOS_PROG='apropos';
   elif is_prog man
   then
     if man --apropos man >${_NULL_DEV} 2>${_NULL_DEV}
     then
       _APROPOS_PROG='man --apropos';
     elif man -k man >${_NULL_DEV} 2>${_NULL_DEV}
     then
       _APROPOS_PROG='man -k';
     fi;
   fi;
   if obj _APROPOS_PROG is_empty
   then
     error 'apropos_setup: no apropos program available.';
   fi;
   to_tmp_line '.TH GROFFER APROPOS';
 fi;
 eval "${return_ok}";
}


########################################################################
# base_name (<path>)
#
# Get the file name part of <path>, i.e. delete everything up to last
# `/' from the beginning of <path>.  Remove final slashes, too, to get a
# non-empty output.
#
# Arguments : 1
# Output    : the file name part (without slashes)
#
# Variable prefix: bn
#
base_name()
{
 func_check base_name = 1 "$@";
 bn_name="$1";
 case "${bn_name}" in
   */)
     # delete all final slashes
     bn_name="$(echo1 "${bn_name}" | sed -e 's|//*$||')";
     exit_test;
     ;;
 esac;
 case "${bn_name}" in
   /|'')
     eval ${_UNSET} bn_name;
     eval "${return_bad}";
     ;;
   */*)
     # delete everything before and including the last slash `/'.
     echo1 "${bn_name}" | sed -e 's|^.*//*\([^/]*\)$|\1|';
     ;;
   *)
     obj bn_name echo1;
     ;;
 esac;
 eval ${_UNSET} bn_name;
 eval "${return_ok}";
}


########################################################################
# cat_z (<file>)
#
# Decompress if possible or just print <file> to standard output.
#
# gzip, bzip2, and .Z decompression is supported.
#
# Arguments: 1, a file name.
# Output: the content of <file>, possibly decompressed.
#
if test _"${_HAS_COMPRESSION}"_ = _yes_
then
 cat_z()
 {
   func_check cat_z = 1 "$@";
   case "$1" in
     '')
       error 'cat_z(): empty file name';
       ;;
     '-')
       error 'cat_z(): for standard input use save_stdin()';
       ;;
   esac;
   if obj _HAS_BZIP is_yes
   then
     if bzip2 -t "$1" 2>${_NULL_DEV}
     then
       bzip2 -c -d "$1" 2>${_NULL_DEV};
       eval "${return_ok}";
     fi;
   fi;
   gzip -c -d -f "$1" 2>${_NULL_DEV};
   eval "${return_ok}";
 }
else
 cat_z()
 {
   func_check cat_z = 1 "$@";
   cat "$1";
   eval "${return_ok}";
 }
fi;


########################################################################
# clean_up ()
#
# Do the final cleaning up before exiting; used by the trap calls.
#
# defined above


########################################################################
# diag (<text>*)
#
# Print marked message to standard error; useful for debugging.
#
# defined above


########################################################################
landmark '4: dirname()*';
########################################################################

#######################################################################
# dirname_append (<dir> <name>)
#
# Append `name' to `dir' with clean handling of `/'.
#
# Arguments : 2
# Output    : the generated new directory name <dir>/<name>
#
dirname_append()
{
 func_check dirname_append = 2 "$@";
 if is_empty "$1"
 then
   error "dir_append(): first argument is empty.";
 fi;
 if is_empty "$2"
 then
   echo1 "$1";
 else
   dirname_chop "$1"/"$2";
 fi;
 eval "${return_ok}";
}


########################################################################
# dirname_chop (<name>)
#
# Remove unnecessary slashes from directory name.
#
# Argument: 1, a directory name.
# Output:   path without double, or trailing slashes.
#
# Variable prefix: dc
#
dirname_chop()
{
 func_check dirname_chop = 1 "$@";
 # replace all multiple slashes by a single slash `/'.
 dc_res="$(echo1 "$1" | sed -e 's|///*|/|g')";
 exit_test;
 case "${dc_res}" in
 ?*/)
   # remove trailing slash '/';
   echo1 "${dc_res}" | sed -e 's|/$||';
   ;;
 *)
   obj dc_res echo1
   ;;
 esac;
 eval ${_UNSET} dc_res;
 eval "${return_ok}";
}


########################################################################
# do_filearg (<filearg>)
#
# Append the file, man-page, or standard input corresponding to the
# argument to the temporary file.  If this is compressed in the gzip
# or Z format it is decompressed.  A title element is generated.
#
# Argument either:
#   - name of an existing file.
#   - `-' to represent standard input (several times allowed).
#   - `man:name.(section)' the man-page for `name' in `section'.
#   - `man:name.section' the man-page for `name' in `section'.
#   - `man:name' the man-page for `name' in the lowest `section'.
#   - `name.section' the man-page for `name' in `section'.
#   - `name' the man-page for `name' in the lowest `section'.
# Globals :
#   $_TMP_STDIN, $_MAN_ENABLE, $_MAN_IS_SETUP, $_OPT_MAN
#
# Output  : none
# Return  : $_GOOD if found, ${_BAD} otherwise.
#
# Variable prefix: df
#
do_filearg()
{
 func_check do_filearg = 1 "$@";
 df_filespec="$1";
 # store sequence into positional parameters
 case "${df_filespec}" in
 '')
   eval ${_UNSET} df_filespec;
   eval "${return_good}";
   ;;
 '-')
   register_file '-';
   eval ${_UNSET} df_filespec;
   eval "${return_good}";
   ;;
 */*)                         # with directory part; so no man search
   set 'File';
   ;;
 *)
   if obj _MAN_ENABLE is_yes
   then
     if obj _MAN_FORCE is_yes
     then
       set 'Manpage' 'File';
     else
       set 'File' 'Manpage';
     fi;
     else
     set 'File';
   fi;
   ;;
 esac;
 for i
 do
   case "$i" in
   File)
     if test -f "${df_filespec}"
     then
       if test -r "${df_filespec}"
       then
         register_file "${df_filespec}";
         eval ${_UNSET} df_filespec;
         eval ${_UNSET} df_no_man;
         eval "${return_good}";
       else
         echo2 "could not read \`${df_filespec}'";
         eval ${_UNSET} df_filespec;
         eval ${_UNSET} df_no_man;
         eval "${return_bad}";
       fi;
     else
       if obj df_no_man is_not_empty
       then
         if obj _OPT_WHATIS is_yes
         then
           to_tmp_line "This is neither a file nor a man page."
         else
           echo2 "\`${df_filespec}' is neither a file nor a man page."
         fi;
       fi;
       df_no_file=yes;
       continue;
     fi;
     ;;
   Manpage)                    # parse filespec as man page
     if obj _MAN_IS_SETUP is_not_yes
     then
       man_setup;
     fi;
     if man_do_filespec "${df_filespec}"
     then
       eval ${_UNSET} df_filespec;
       eval ${_UNSET} df_no_file;
       eval "${return_good}";
     else
       if obj df_no_file is_not_empty
       then
         if obj _OPT_WHATIS is_yes
         then
           to_tmp_line "This is neither a file nor a man page."
         else
           echo2 "\`${df_filespec}' is neither a file nor a man page."
         fi;
       fi;
       df_no_man=yes;
       continue;
     fi;
     ;;
   esac;
 done;
 eval ${_UNSET} df_filespec;
 eval ${_UNSET} df_no_file;
 eval ${_UNSET} df_no_man;
 eval "${return_bad}";
} # do_filearg()


########################################################################
# do_nothing ()
#
# Dummy function.
#
do_nothing()
{
 eval return "${_OK}";
}


########################################################################
# echo2 (<text>*)
#
# Print to standard error with final line break.
#
# defined above


########################################################################
# error (<text>*)
#
# Print error message and exit with error code.
#
# defined above


########################################################################
# exit_test ()
#
# Test whether the former command ended with error().  Exit again.
#
# defined above


########################################################################
# func_check (<func_name> <rel_op> <nr_args> "$@")
#
# Check number of arguments and register to _FUNC_STACK.
#
# Arguments: >=3
#   <func_name>: name of the calling function.
#   <rel_op>:    a relational operator: = != < > <= >=
#   <nr_args>:   number of arguments to be checked against <operator>
#   "$@":        the arguments of the calling function.
#
# defined above

#########################################################################
# func_pop ()
#
# Delete the top element from the function call stack.
#
# defined above


########################################################################
# func_push (<element>)
#
# Store another element to function call stack.
#
# defined above


########################################################################
# func_stack_dump ()
#
# Print the content of the stack.
#
# defined above


########################################################################
# get_first_essential (<arg>*)
#
# Retrieve first non-empty argument.
#
# Return  : `1' if all arguments are empty, `0' if found.
# Output  : the retrieved non-empty argument.
#
# Variable prefix: gfe
#
get_first_essential()
{
 func_check get_first_essential '>=' 0 "$@";
 if is_equal "$#" 0
 then
   eval "${return_ok}";
 fi;
 for i
 do
   gfe_var="$i";
   if obj gfe_var is_not_empty
   then
     obj gfe_var echo1;
     eval ${_UNSET} gfe_var;
     eval "${return_ok}";
   fi;
 done;
 eval ${_UNSET} gfe_var;
 eval "${return_bad}";
}


########################################################################
landmark '5: is_*()';
########################################################################

########################################################################
# is_dir (<name>)
#
# Test whether `name' is a directory.
#
# Arguments : 1
# Return    : `0' if arg1 is a directory, `1' otherwise.
#
is_dir()
{
 func_check is_dir '=' 1 "$@";
 if test _"$1"_ != __ && test -d "$1" && test -r "$1"
 then
   eval "${return_yes}";
 fi;
 eval "${return_no}";
}


########################################################################
# is_empty (<string>)
#
# Test whether `string' is empty.
#
# Arguments : <=1
# Return    : `0' if arg1 is empty or does not exist, `1' otherwise.
#
is_empty()
{
 func_check is_empty '=' 1 "$@";
 if test _"$1"_ = __
 then
   eval "${return_yes}";
 fi;
 eval "${return_no}";
}


########################################################################
# is_equal (<string1> <string2>)
#
# Test whether `string1' is equal to <string2>.
#
# Arguments : 2
# Return    : `0' both arguments are equal strings, `1' otherwise.
#
is_equal()
{
 func_check is_equal '=' 2 "$@";
 if test _"$1"_ = _"$2"_
 then
   eval "${return_yes}";
 fi;
 eval "${return_no}";
}


########################################################################
# is_existing (<name>)
#
# Test whether `name' is an existing file or directory.  Solaris 2.5 does
# not have `test -e'.
#
# Arguments : 1
# Return    : `0' if arg1 exists, `1' otherwise.
#
is_existing()
{
 func_check is_existing '=' 1 "$@";
 if test _"$1"_ = __
 then
   eval "${return_no}";
 fi;
 if test -f "$1" || test -d "$1" || test -c "$1"
 then
   eval "${return_yes}";
 fi;
 eval "${return_no}";
}


########################################################################
# is_file (<name>)
#
# Test whether `name' is a readable file.
#
# Arguments : 1
# Return    : `0' if arg1 is a readable file, `1' otherwise.
#
is_file()
{
 func_check is_file '=' 1 "$@";
 if is_not_empty "$1" && test -f "$1" && test -r "$1"
 then
   eval "${return_yes}";
 fi;
 eval "${return_no}";
}


########################################################################
# is_non_empty_file (<file_name>)
#
# Test whether `file_name' is a non-empty existing file.
#
# Arguments : <=1
# Return    :
#   `0' if arg1 is a non-empty existing file
#   `1' otherwise
#
is_non_empty_file()
{
 func_check is_non_empty_file '=' 1 "$@";
 if is_file "$1" && test -s "$1"
 then
   eval "${return_yes}";
 fi;
 eval "${return_no}";
}


########################################################################
# is_not_dir (<name>)
#
# Test whether `name' is not a readable directory.
#
# Arguments : 1
# Return    : `0' if arg1 is a directory, `1' otherwise.
#
is_not_dir()
{
 func_check is_not_dir '=' 1 "$@";
 if is_dir "$1"
 then
   eval "${return_no}";
 fi;
 eval "${return_yes}";
}


########################################################################
# is_not_empty (<string>)
#
# Test whether `string' is not empty.
#
# Arguments : <=1
# Return    : `0' if arg1 exists and is not empty, `1' otherwise.
#
is_not_empty()
{
 func_check is_not_empty '=' 1 "$@";
 if is_empty "$1"
 then
   eval "${return_no}";
 fi;
 eval "${return_yes}";
}


########################################################################
# is_not_equal (<string1> <string2>)
#
# Test whether `string1' differs from `string2'.
#
# Arguments : 2
#
is_not_equal()
{
 func_check is_not_equal '=' 2 "$@";
 if is_equal "$1" "$2"
 then
   eval "${return_no}";
 fi
 eval "${return_yes}";
}


########################################################################
# is_not_file (<filename>)
#
# Test whether `name' is a not readable file.
#
# Arguments : 1 (empty allowed)
#
is_not_file()
{
 func_check is_not_file '=' 1 "$@";
 if is_file "$1"
 then
   eval "${return_no}";
 fi;
 eval "${return_yes}";
}


########################################################################
# is_not_prog ([<name> [<arg>*]])
#
# Verify that arg is a not program in $PATH.
#
# Arguments : >=0 (empty allowed)
#   more args are ignored, this allows to specify progs with arguments
#
is_not_prog()
{
 func_check is_not_prog '>=' 0 "$@";
 case "$#" in
 0)
   eval "${return_yes}";
   ;;
 *)
   if where_is "$1" >${_NULL_DEV}
   then
     eval "${return_no}";
   fi;
   ;;
 esac
 eval "${return_yes}";
}


########################################################################
# is_not_writable (<name>)
#
# Test whether `name' is a not a writable file or directory.
#
# Arguments : >=1 (empty allowed), more args are ignored
#
is_not_writable()
{
 func_check is_not_writable '>=' 1 "$@";
 if is_writable "$1"
 then
   eval "${return_no}";
 fi;
 eval "${return_yes}";
}


########################################################################
# is_not_X ()
#
# Test whether not running in X Window by checking $DISPLAY
#
is_not_X()
{
 func_check is_X '=' 0 "$@";
 if obj DISPLAY is_empty
 then
   eval "${return_yes}";
 fi;
 eval "${return_no}";
}


########################################################################
# is_not_yes (<string>)
#
# Test whether `string' is not "yes".
#
# Arguments : 1
#
is_not_yes()
{
 func_check is_not_yes = 1 "$@";
 if is_yes "$1"
 then
   eval "${return_no}";
 fi;
 eval "${return_yes}";
}


########################################################################
# is_prog ([<name> [<arg>*]])
#
# Determine whether <name> is a program in $PATH
#
# Arguments : >=0 (empty allowed)
#   <arg>* are ignored, this allows to specify progs with arguments.
#
is_prog()
{
 func_check is_prog '>=' 0 "$@";
 case "$#" in
 0)
   eval "${return_no}";
   ;;
 *)
   if where_is "$1" >${_NULL_DEV}
   then
     eval "${return_yes}";
   fi;
   ;;
 esac
 eval "${return_no}";
}


########################################################################
# is_writable (<name>)
#
# Test whether `name' is a writable file or directory.
#
# Arguments : >=1 (empty allowed), more args are ignored
#
is_writable()
{
 func_check is_writable '>=' 1 "$@";
 if test _"$1"_ = __
 then
   eval "${return_no}";
 fi;
 if test -r "$1"
 then
   if test -w "$1"
   then
     eval "${return_yes}";
   fi;
 fi;
 eval "${return_no}";
}


########################################################################
# is_X ()
#
# Test whether running in X Window by checking $DISPLAY
#
is_X()
{
 func_check is_X '=' 0 "$@";
 if obj DISPLAY is_not_empty
 then
   eval "${return_yes}";
 fi;
 eval "${return_no}";
}


########################################################################
# is_yes (<string>)
#
# Test whether `string' has value "yes".
#
# Return    : `0' if arg1 is `yes', `1' otherwise.
#
is_yes()
{
 func_check is_yes '=' 1 "$@";
 if is_equal "$1" 'yes'
 then
   eval "${return_yes}";
 fi;
 eval "${return_no}";
}


########################################################################
# landmark ()
#
# Print debugging information on standard error if $_DEBUG_LM is `yes'.
#
# Globals: $_DEBUG_LM
#
# Defined in section `Debugging functions'.


########################################################################
# leave ([<code>])
#
# Clean exit without an error or with <code>.
#
leave()
{
 clean_up;
 if test $# = 0
 then
   exit "${_OK}";
 else
   exit "$1";
 fi;
}


########################################################################
landmark '6: list_*()';
########################################################################
#
# `list' is an object class that represents an array or list.  Its
# data consists of space-separated single-quoted elements.  So a list
# has the form "'first' 'second' '...' 'last'".  See list_append() for
# more details on the list structure.  The array elements of `list'
# can be get by `eval set x "$list"; shift`.


########################################################################
# list_append (<list> <element>...)
#
# Arguments: >=2
#   <list>: a variable name for a list of single-quoted elements
#   <element>:  some sequence of characters.
# Output: none, but $<list> is set to
#   if <list> is empty:  "'<element>' '...'"
#   otherwise:           "$list '<element>' ..."
#
# Variable prefix: la
#
list_append()
{
 func_check list_append '>=' 2 "$@";
 la_name="$1";
 eval la_list='"${'$1'}"';
 shift;
 for s
 do
   la_s="$s";
   case "${la_s}" in
   *\'*)
     # escape each single quote by replacing each
     # "'" (squote) by "'\''" (squote bslash squote squote);
     # note that the backslash must be doubled in the following `sed'
     la_element="$(echo1 "${la_s}" | sed -e 's/'"${_SQ}"'/&\\&&/g')";
     exit_test;
     ;;
   '')
     la_element="";
     ;;
   *)
     la_element="${la_s}";
     ;;
   esac;
   if obj la_list is_empty
   then
     la_list="'${la_element}'";
   else
     la_list="${la_list} '${la_element}'";
   fi;
 done;
 eval "${la_name}"='"${la_list}"';
 eval ${_UNSET} la_element;
 eval ${_UNSET} la_list;
 eval ${_UNSET} la_name;
 eval ${_UNSET} la_s;
 eval "${return_ok}";
}


########################################################################
# list_from_cmdline (<pre_name_of_opt_lists> [<cmdline_arg>...])
#
# Transform command line arguments into a normalized form.
#
# Options, option arguments, and file parameters are identified and
# output each as a single-quoted argument of its own.  Options and
# file parameters are separated by a '--' argument.
#
# Arguments: >=1
#   <pre_name>: common part of a set of 4 environment variable names:
#     $<pre_name>_SHORT_NA:  list of short options without an arg.
#     $<pre_name>_SHORT_ARG: list of short options that have an arg.
#     $<pre_name>_LONG_NA:   list of long options without an arg.
#     $<pre_name>_LONG_ARG:  list of long options that have an arg.
#   <cmdline_arg>...: the arguments from a command line, such as "$@",
#                     the content of a variable, or direct arguments.
#
# Output: ['-[-]opt' ['optarg']]... '--' ['filename']...
#
# Example:
#   list_from_cmdline PRE 'a b' 'c' '' 'long' -a f1 -bcarg --long=larg f2
# If $PRE_SHORT_NA, $PRE_SHORT_ARG, $PRE_LONG_NA, and $PRE_LONG_ARG are
# none-empty option lists, this will result in printing:
#     '-a' '-b' '-c' 'arg' '--long' 'larg' '--' 'f1' 'f2'
#
#   Use this function in the following way:
#     eval set x "$(args_norm PRE_NAME "$@")";
#     shift;
#     while test "$1" != '--'; do
#       case "$1" in
#       ...
#       esac;
#       shift;
#     done;
#     shift;         #skip '--'
#     # all positional parameters ("$@") left are file name parameters.
#
# Variable prefix: lfc
#
list_from_cmdline()
{
 func_check list_from_cmdline '>=' 1 "$@";
 lfc_short_n="$(obj_data "$1"_SHORT_NA)";  # short options, no argument
 lfc_short_a="$(obj_data "$1"_SHORT_ARG)"; # short options, with argument
 lfc_long_n="$(obj_data "$1"_LONG_NA)";    # long options, no argument
 lfc_long_a="$(obj_data "$1"_LONG_ARG)";   # long options, with argument
 exit_test;
 if obj lfc_short_n is_empty
 then
   error 'list_from_cmdline(): no $'"$1"'_SHORT_NA options.';
 fi;
 if obj lfc_short_a is_empty
 then
   error 'list_from_cmdline(): no $'"$1"'_SHORT_ARG options.';
 fi;
 if obj lfc_long_n is_empty
 then
   error 'list_from_cmdline(): no $'"$1"'_LONG_NA options.';
 fi;
 if obj lfc_long_a is_empty
 then
   error 'list_from_cmdline(): no $'"$1"'_LONG_ARG options.';
 fi;

 shift;
 if is_equal "$#" 0
 then
   echo1 --
   eval ${_UNSET} lfc_fparams;
   eval ${_UNSET} lfc_short_a;
   eval ${_UNSET} lfc_short_n;
   eval ${_UNSET} lfc_long_a;
   eval ${_UNSET} lfc_long_n;
   eval ${_UNSET} lfc_result;
   eval "${return_ok}";
 fi;

 lfc_fparams='';
 lfc_result='';
 while test "$#" -ge 1
 do
   lfc_arg="$1";
   shift;
   case "${lfc_arg}" in
   --) break; ;;
   --*=*)
     # delete leading '--';
     lfc_abbrev="$(echo1 "${lfc_arg}" | sed -e 's/^--//')";
     lfc_with_equal="${lfc_abbrev}";
     # extract option by deleting from the first '=' to the end
     lfc_abbrev="$(echo1 "${lfc_with_equal}" | \
                   sed -e 's/^\([^=]*\)=.*$/\1/')";
     lfc_opt="$(list_single_from_abbrev lfc_long_a "${lfc_abbrev}")";
     exit_test;
     if obj lfc_opt is_empty
     then
       error_user "--${lfc_abbrev} is not an option.";
     else
       # get the option argument by deleting up to first `='
       lfc_optarg="$(echo1 "${lfc_with_equal}" | sed -e 's/^[^=]*=//')";
       exit_test;
       list_append lfc_result "--${lfc_opt}" "${lfc_optarg}";
       continue;
     fi;
     ;;
   --*)
     # delete leading '--';
     lfc_abbrev="$(echo1 "${lfc_arg}" | sed -e 's/^--//')";
     if list_has lfc_long_n "${lfc_abbrev}"
     then
       lfc_opt="${lfc_abbrev}";
     else
       exit_test;
       lfc_opt="$(list_single_from_abbrev lfc_long_n "${lfc_abbrev}")";
       exit_test;
       if obj lfc_opt is_not_empty && is_not_equal "$#" 0
       then
         a="$(list_single_from_abbrev lfc_long_a "${lfc_abbrev}")";
         exit_test;
         if obj a is_not_empty
         then
           error_user "The abbreviation ${lfc_arg} \
has multiple options: --${lfc_opt} and --${a}.";
         fi;
       fi;
     fi;
     if obj lfc_opt is_not_empty
     then
       # long option, no argument
       list_append lfc_result "--${lfc_opt}";
       continue;
     fi;
     lfc_opt="$(list_single_from_abbrev lfc_long_a "${lfc_abbrev}")";
     exit_test;
     if obj lfc_opt is_not_empty
     then
       # long option with argument
       if test "$#" -le 0
       then
         error_user "no argument for option --${lfc_opt}."
       fi;
       list_append lfc_result "--${lfc_opt}" "$1";
       shift;
       continue;
     fi;
     error_user "${lfc_arg} is not an option.";
     ;;
   -?*)                        # short option (cluster)
     # delete leading `-';
     lfc_rest="$(echo1 "${lfc_arg}" | sed -e 's/^-//')";
     exit_test;
     while obj lfc_rest is_not_empty
     do
       # get next short option from cluster (first char of $lfc_rest)
       lfc_optchar="$(echo1 "${lfc_rest}" | sed -e 's/^\(.\).*$/\1/')";
       # remove first character from ${lfc_rest};
       lfc_rest="$(echo1 "${lfc_rest}" | sed -e 's/^.//')";
       exit_test;
       if list_has lfc_short_n "${lfc_optchar}"
       then
         list_append lfc_result "-${lfc_optchar}";
         continue;
       elif list_has lfc_short_a "${lfc_optchar}"
       then
         if obj lfc_rest is_empty
         then
           if test "$#" -ge 1
           then
             list_append lfc_result "-${lfc_optchar}" "$1";
             shift;
             continue;
           else
             error_user "no argument for option -${lfc_optchar}.";
           fi;
         else                  # rest is the argument
           list_append lfc_result "-${lfc_optchar}" "${lfc_rest}";
           lfc_rest='';
           continue;
         fi;
       else
         error_user "unknown option -${lfc_optchar}.";
       fi;
     done;
     ;;
   *)
     # Here, $lfc_arg is not an option, so a file parameter.
     list_append lfc_fparams "${lfc_arg}";

     # Ignore the strange POSIX option handling to end option
     # parsing after the first file name argument.  To reuse it, do
     # a `break' here if $POSIXLY_CORRECT of `bash' is not empty.
     # When `bash' is called as `sh' $POSIXLY_CORRECT is set
     # automatically to `y'.
     ;;
   esac;
 done;
 list_append lfc_result '--';
 if obj lfc_fparams is_not_empty
 then
   lfc_result="${lfc_result} ${lfc_fparams}";
 fi;
 if test "$#" -gt 0
 then
   list_append lfc_result "$@";
 fi;
 obj lfc_result echo1;
 eval ${_UNSET} lfc_abbrev;
 eval ${_UNSET} lfc_fparams;
 eval ${_UNSET} lfc_short_a;
 eval ${_UNSET} lfc_short_n;
 eval ${_UNSET} lfc_long_a;
 eval ${_UNSET} lfc_long_n;
 eval ${_UNSET} lfc_result;
 eval ${_UNSET} lfc_arg;
 eval ${_UNSET} lfc_opt;
 eval ${_UNSET} lfc_opt_arg;
 eval ${_UNSET} lfc_opt_char;
 eval ${_UNSET} lfc_with_equal;
 eval ${_UNSET} lfc_rest;
 eval "${return_ok}";
} # list_from_cmdline()


########################################################################
# list_from_split (<string> <separator>)
#
# In <string>, escape all white space characters and replace each
# <separator> by space.
#
# Arguments: 2: a <string> that is to be split into parts divided by
#               <separator>
# Output:    the resulting list string
#
# Variable prefix: lfs
#
list_from_split()
{
 func_check list_from_split = 2 "$@";

 # precede each space or tab by a backslash `\' (doubled for `sed')
 lfs_s="$(echo1 "$1" | sed -e 's/\('"${_SPACE_SED}"'\)/\\\1/g')";
 exit_test;

 # replace split character of string by the list separator ` ' (space).
 case "$2" in
   /)                          # cannot use normal `sed' separator
     echo1 "${lfs_s}" | sed -e 's|'"$2"'| |g';
     ;;
   ?)                          # use normal `sed' separator
     echo1 "${lfs_s}" | sed -e 's/'"$2"'/ /g';
     ;;
   ??*)
     error 'list_from_split(): separator must be a single character.';
     ;;
 esac;
 eval ${_UNSET} lfs_s;
 eval "${return_ok}";
}


########################################################################
# list_get (<list>)
#
# Check whether <list> is a space-separated list of '-quoted elements.
#
# If the test fails an error is raised.
# If the test succeeds the argument is echoed.
#
# Testing criteria:
#   A list has the form "'first' 'second' '...' 'last'".  So it has a
#   leading and a final quote and the elements are separated by "' '"
#   constructs.  If these are all removed there should not be any
#   unescaped single-quotes left.  Watch out for escaped single
#   quotes; they have the form '\'' (sq bs sq sq).

# Arguments: 1
# Output: the argument <list> unchanged, if the check succeeded.
#
# Variable prefix: lg
#
list_get()
{
 func_check list_get = 1 "$@";
 eval lg_list='"${'$1'}"';
 # remove leading and final space characters
 lg_list="$(echo1 "${lg_list}" | sed -e '
s/^'"${_SPACE_SED}"'*//
s/'"${_SPACE_SED}"'*$//
')";
 exit_test;
 case "${lg_list}" in
 '')
   eval ${_UNSET} lg_list;
   eval "${return_ok}";
   ;;
 \'*\')
   obj lg_list echo1;
   eval ${_UNSET} lg_list;
   eval "${return_ok}";
   ;;
 *)
   error "list_get(): bad list: $1"
   ;;
 esac;
 eval ${_UNSET} lg_list;
 eval "${return_ok}";
}


########################################################################
# list_has (<var_name> <element>)
#
# Test whether the list <var_name> has the element <element>.
#
# Arguments: 2
#   <var_name>: a variable name for a list of single-quoted elements
#   <element>:  some sequence of characters.
#
# Variable prefix: lh
#
list_has()
{
 func_check list_has = 2 "$@";
 eval lh_list='"${'$1'}"';
 if obj lh_list is_empty
 then
   eval "${_UNSET}" lh_list;
   eval "${return_no}";
 fi;
 case "$2" in
   \'*\')  lh_element=" $2 "; ;;
   *)      lh_element=" '$2' "; ;;
 esac;
 if string_contains " ${lh_list} " "${lh_element}"
 then
   eval "${_UNSET}" lh_list;
   eval "${_UNSET}" lh_element;
   eval "${return_yes}";
 else
   eval "${_UNSET}" lh_list;
   eval "${_UNSET}" lh_element;
   eval "${return_no}";
 fi;
}


########################################################################
# list_has_abbrev (<var_name> <abbrev>)
#
# Test whether the list <var_name> has an element starting with <abbrev>.
#
# Arguments: 2
#   <var_name>: a variable name for a list of single-quoted elements
#   <abbrev>:   some sequence of characters.
#
# Variable prefix: lha
#
list_has_abbrev()
{
 func_check list_has_abbrev = 2 "$@";
 eval lha_list='"${'$1'}"';
 if obj lha_list is_empty
 then
   eval "${_UNSET}" lha_list;
   eval "${return_no}";
 fi;
 case "$2" in
   \'*)
     lha_element="$(echo1 "$2" | sed -e 's/'"${_SQ}"'$//')";
     exit_test;
     ;;
   *) lha_element="'$2"; ;;
 esac;
 if string_contains " ${lha_list}" " ${lha_element}"
 then
   eval "${_UNSET}" lha_list;
   eval "${_UNSET}" lha_element;
   eval "${return_yes}";
 else
   eval "${_UNSET}" lha_list;
   eval "${_UNSET}" lha_element;
   eval "${return_no}";
 fi;
 eval "${return_ok}";
}


########################################################################
# list_has_not (<list> <element>)
#
# Test whether <list> has no <element>.
#
# Arguments: 2
#   <list>:    a space-separated list of single-quoted elements.
#   <element>: some sequence of characters.
#
# Variable prefix: lhn
#
list_has_not()
{
 func_check list_has_not = 2 "$@";
 eval lhn_list='"${'$1'}"';
 if obj lhn_list is_empty
 then
   eval "${_UNSET}" lhn_list;
   eval "${return_yes}";
 fi;
 case "$2" in
   \'*\') lhn_element=" $2 "; ;;
   *)     lhn_element=" '$2' "; ;;
 esac;
 if string_contains " ${lhn_list} " "${lhn_element}"
 then
   eval "${_UNSET}" lhn_list;
   eval "${_UNSET}" lhn_element;
   eval "${return_no}";
 else
   eval "${_UNSET}" lhn_list;
   eval "${_UNSET}" lhn_element;
   eval "${return_yes}";
 fi;
}


########################################################################
# list_single_from_abbrev (<list> <abbrev>)
#
# Check whether the list has an element starting with <abbrev>.  If
# there are more than a single element an error is created.
#
# Arguments: 2
#   <list>:   a variable name for a list of single-quoted elements
#   <abbrev>: some sequence of characters.
#
# Output: the found element.
#
# Variable prefix: lsfa
#
list_single_from_abbrev()
{
 func_check list_single_from_abbrev = 2 "$@";
 eval lsfa_list='"${'$1'}"';
 if obj lsfa_list is_empty
 then
   eval "${_UNSET}" lsfa_list;
   eval "${return_no}";
 fi;
 lsfa_abbrev="$2";
 if list_has lsfa_list "${lsfa_abbrev}"
 then
   obj lsfa_abbrev echo1;
   eval "${_UNSET}" lsfa_abbrev;
   eval "${_UNSET}" lsfa_list;
   eval "${return_yes}";
 fi;
 if list_has_abbrev lsfa_list "${lsfa_abbrev}"
 then
   lsfa_element='';
   eval set x "${lsfa_list}";
   shift;
   for i
   do
     case "$i" in
     ${lsfa_abbrev}*)
       if obj lsfa_element is_not_empty
       then
         error_user "The abbreviation --${lsfa_abbrev} \
has multiple options: --${lsfa_element} and --${i}.";
       fi;
       lsfa_element="$i";
       ;;
     esac;
   done;
   obj lsfa_element echo1;
   eval "${_UNSET}" lsfa_abbrev;
   eval "${_UNSET}" lsfa_element;
   eval "${_UNSET}" lsfa_list;
   eval "${return_yes}";
 else
   eval "${_UNSET}" lsfa_abbrev;
   eval "${_UNSET}" lsfa_element;
   eval "${_UNSET}" lsfa_list;
   eval "${return_no}";
 fi;
}


########################################################################
landmark '7: man_*()';
########################################################################

########################################################################
# man_do_filespec (<filespec>)
#
# Print suitable man page(s) for filespec to $_TMP_CAT.
#
# Arguments : 2
#   <filespec>: argument of the form `man:name.section', `man:name',
#               `man:name(section)', `name.section', `name'.
#
# Globals   : $_OPT_ALL
#
# Output    : none.
# Return    : `0' if man page was found, `1' else.
#
# Only called from do_fileargs(), checks on $MANPATH and $_MAN_ENABLE
# are assumed (see man_setup()).
#
# Variable prefix: mdf
#
man_do_filespec()
{
 func_check man_do_filespec = 1 "$@";
 if obj _MAN_PATH is_empty
 then
   eval "${return_bad}";
 fi;
 if is_empty "$1"
 then
   eval "${return_bad}";
 fi;
 mdf_spec="$1";
 mdf_name='';
 mdf_section='';
 case "${mdf_spec}" in
 */*)                          # not a man spec with containing '/'
   eval ${_UNSET} mdf_got_one;
   eval ${_UNSET} mdf_name;
   eval ${_UNSET} mdf_section;
   eval ${_UNSET} mdf_spec;
   eval "${return_bad}";
   ;;
 man:?*\(?*\))                 # man:name(section)
   mdf_name="$(echo1 "${mdf_spec}" \
               | sed -e 's/^man:\(..*\)(\(..*\))$/\1/')";
   mdf_section="$(echo1 "${mdf_spec}" \
                  | sed -e 's/^man:\(..*\)(\(..*\))$/\2/')";
   exit_test;
   ;;
 man:?*.${_MAN_AUTO_SEC_CHARS}) # man:name.section
   mdf_name="$(echo1 "${mdf_spec}" \
               | sed -e 's/^man:\(..*\)\..$/\1/')";
   mdf_section="$(echo1 "${mdf_spec}" \
                  | sed -e 's/^.*\(.\)$/\1/')";
   exit_test;
   ;;
 man:?*)                       # man:name
   mdf_name="$(echo1 "${mdf_spec}" | sed -e 's/^man://')";
   exit_test;
   ;;
 ?*\(?*\))                     # name(section)
   mdf_name="$(echo1 "${mdf_spec}" \
               | sed -e 's/^\(..*\)(\(..*\))$/\1/')";
   mdf_section="$(echo1 "${mdf_spec}" \
                  | sed -e 's/^\(..*\)(\(..*\))$/\2/')";
   exit_test;
   ;;
 ?*.${_MAN_AUTO_SEC_CHARS})    # name.section
   mdf_name="$(echo1 "${mdf_spec}" \
               | sed -e 's/^\(..*\)\..$/\1/')";
   mdf_section="$(echo1 "${mdf_spec}" \
                  | sed -e 's/^.*\(.\)$/\1/')";
   exit_test;
   ;;
 ?*)
   mdf_name="${mdf_spec}";
   ;;
 esac;
 if obj mdf_name is_empty
 then
   eval ${_UNSET} mdf_got_one;
   eval ${_UNSET} mdf_name;
   eval ${_UNSET} mdf_section;
   eval ${_UNSET} mdf_spec;
   eval "${return_bad}";
 fi;
 mdf_got_one='no';
 if obj mdf_section is_empty
 then
   if obj _OPT_SECTIONS is_empty
   then
     eval set x "${_MAN_AUTO_SEC_LIST}";
   else
     # use --sections when no section is given to filespec
     eval set x "$(echo1 "${_OPT_SECTIONS}" | sed -e 's/:/ /g')";
   fi;
   shift;
   for s
   do
     mdf_s="$s";
     if man_search_section "${mdf_name}" "${mdf_s}"
     then                      # found
       if obj _MAN_ALL is_yes
       then
         mdf_got_one='yes';
       else
         eval ${_UNSET} mdf_got_one;
         eval ${_UNSET} mdf_name;
         eval ${_UNSET} mdf_s;
         eval ${_UNSET} mdf_section;
         eval ${_UNSET} mdf_spec;
         eval "${return_good}";
       fi;
     fi;
   done;
 else
   if man_search_section "${mdf_name}" "${mdf_section}"
   then
     eval ${_UNSET} mdf_got_one;
     eval ${_UNSET} mdf_name;
     eval ${_UNSET} mdf_s;
     eval ${_UNSET} mdf_section;
     eval ${_UNSET} mdf_spec;
     eval "${return_good}";
   else
     eval ${_UNSET} mdf_got_one;
     eval ${_UNSET} mdf_name;
     eval ${_UNSET} mdf_section;
     eval ${_UNSET} mdf_spec;
     eval "${return_bad}";
   fi;
 fi;
 if obj _MAN_ALL is_yes && obj mdf_got_one is_yes
 then
   eval ${_UNSET} mdf_got_one;
   eval ${_UNSET} mdf_name;
   eval ${_UNSET} mdf_s;
   eval ${_UNSET} mdf_section;
   eval ${_UNSET} mdf_spec;
   eval "${return_good}";
 fi;
 eval ${_UNSET} mdf_got_one;
 eval ${_UNSET} mdf_name;
 eval ${_UNSET} mdf_s;
 eval ${_UNSET} mdf_section;
 eval ${_UNSET} mdf_spec;
 eval "${return_bad}";
} # man_do_filespec()


########################################################################
# man_register_file (<file> <name> [<section>])
#
# Write a found man page file and register the title element.
#
# Arguments: 1, 2, or 3; maybe empty
# Output: none
#
man_register_file()
{
 func_check man_register_file '>=' 2 "$@";
 case "$#" in
   2|3) do_nothing; ;;
   *)
     error "man_register_file() expects 2 or 3 arguments.";
     ;;
 esac;
 if is_empty "$1"
 then
   error 'man_register_file(): file name is empty';
 fi;
 to_tmp "$1";
 case "$#" in
   2)
      register_title "man:$2";
      eval "${return_ok}";
      ;;
   3)
      register_title "$2.$3";
      eval "${return_ok}";
      ;;
 esac;
 eval "${return_ok}";
}


########################################################################
# man_search_section (<name> <section>)
#
# Retrieve man pages.
#
# Arguments : 2
# Globals   : $_MAN_PATH, $_MAN_EXT
# Return    : 0 if found, 1 otherwise
#
# Variable prefix: mss
#
man_search_section()
{
 func_check man_search_section = 2 "$@";
 if obj _MAN_PATH is_empty
 then
   eval "${return_bad}";
 fi;
 if is_empty "$1"
 then
   eval "${return_bad}";
 fi;
 if is_empty "$2"
 then
   eval "${return_bad}";
 fi;
 mss_name="$1";
 mss_section="$2";
 eval set x "$(path_split "${_MAN_PATH}")";
 exit_test;
 shift;
 mss_got_one='no';
 if obj _MAN_EXT is_empty
 then
   for d
   do
     mss_dir="$(dirname_append "$d" "man${mss_section}")";
     exit_test;
     if obj mss_dir is_dir
     then
       mss_prefix="$(\
         dirname_append "${mss_dir}" "${mss_name}.${mss_section}")";
       if obj _OPT_WHATIS is_yes
       then
         mss_files="$(eval ls "${mss_prefix}"'*' 2>${_NULL_DEV} |
                      sed -e '\| found|s|.*||'
                      )";
       else
         mss_files="$(eval ls "'${mss_prefix}'"'*' 2>${_NULL_DEV} |
                      sed -e '\| found|s|.*||'
                      )";
       fi;
       exit_test;
       if obj mss_files is_not_empty
       then
         # for f in $mss_files
         for f in $(eval set x ${mss_files}; shift; echo1 "$@")
         do
           exit_test;
           mss_f="$f";
           if obj mss_f is_file
           then
             if is_yes "${mss_got_one}"
             then
               register_file "${mss_f}";
             elif obj _MAN_ALL is_yes
             then
               man_register_file "${mss_f}" "${mss_name}";
             else
               man_register_file "${mss_f}" "${mss_name}" "${mss_section}";
               eval ${_UNSET} mss_dir;
               eval ${_UNSET} mss_ext;
               eval ${_UNSET} mss_f;
               eval ${_UNSET} mss_files;
               eval ${_UNSET} mss_got_one;
               eval ${_UNSET} mss_name;
               eval ${_UNSET} mss_prefix;
               eval ${_UNSET} mss_section;
               eval "${return_good}";
             fi;
             mss_got_one='yes';
           fi;
         done;
       fi;
     fi;
   done;
 else
   mss_ext="${_MAN_EXT}";
   # check for directory name having trailing extension
   for d
   do
     mss_dir="$(dirname_append $d man${mss_section}${mss_ext})";
     exit_test;
     if obj mss_dir is_dir
     then
       mss_prefix=\
         "$(dirname_append "${mss_dir}" "${mss_name}.${mss_section}")";
       mss_files="$( eval ls "${mss_prefix}"'*' 2>${_NULL_DEV} |
                    sed -e '\|not found|s|.*||'
                    )";
       exit_test;
       if obj mss_files is_not_empty
       then
         # for f in $mss_files
         for f in $(eval set x ${mss_files}; shift; echo1 "$@")
         do
           mss_f="$f";
           if obj mss_f is_file
           then
             if is_yes "${mss_got_one}"
             then
               register_file "${mss_f}";
             elif obj _MAN_ALL is_yes
             then
               man_register_file "${mss_f}" "${mss_name}";
             else
               man_register_file "${mss_f}" "${mss_name}" "${mss_section}";
               eval ${_UNSET} mss_dir;
               eval ${_UNSET} mss_ext;
               eval ${_UNSET} mss_f;
               eval ${_UNSET} mss_files;
               eval ${_UNSET} mss_got_one;
               eval ${_UNSET} mss_name;
               eval ${_UNSET} mss_prefix;
               eval ${_UNSET} mss_section;
               eval "${return_good}";
             fi;
             mss_got_one='yes';
           fi;
         done;
       fi;
     fi;
   done;
   # check for files with extension in directories without extension
   for d
   do
     mss_dir="$(dirname_append "$d" "man${mss_section}")";
     exit_test;
     if obj mss_dir is_dir
     then
       mss_prefix="$(dirname_append "${mss_dir}" \
                       "${mss_name}.${mss_section}${mss_ext}")";
       mss_files="$(eval ls "${mss_prefix}"'*' 2>${_NULL_DEV} |
                    sed -e '\|not found|s|.*||'
                    )";
       exit_test;
       if obj mss_files is_not_empty
       then
         # for f in $mss_files
         for f in $(eval set x ${mss_files}; shift; echo1 "$@")
         do
           mss_f="$f";
           if obj mss_f is_file
           then
             if is_yes "${mss_got_one}"
             then
               register_file "${mss_f}";
             elif obj _MAN_ALL is_yes
             then
               man_register_file "${mss_f}" "${mss_name}";
             else
               man_register_file "${mss_f}" "${mss_name}" "${mss_section}";
               eval ${_UNSET} mss_dir;
               eval ${_UNSET} mss_ext;
               eval ${_UNSET} mss_f;
               eval ${_UNSET} mss_files;
               eval ${_UNSET} mss_got_one;
               eval ${_UNSET} mss_name;
               eval ${_UNSET} mss_prefix;
               eval ${_UNSET} mss_section;
               eval "${return_good}";
             fi;
             mss_got_one='yes';
           fi;
         done;
       fi;
     fi;
   done;
 fi;
 if obj _MAN_ALL is_yes && is_yes "${mss_got_one}"
 then
   eval ${_UNSET} mss_dir;
   eval ${_UNSET} mss_ext;
   eval ${_UNSET} mss_f;
   eval ${_UNSET} mss_files;
   eval ${_UNSET} mss_got_one;
   eval ${_UNSET} mss_name;
   eval ${_UNSET} mss_prefix;
   eval ${_UNSET} mss_section;
   eval "${return_good}";
 fi;
 eval ${_UNSET} mss_dir;
 eval ${_UNSET} mss_ext;
 eval ${_UNSET} mss_f;
 eval ${_UNSET} mss_files;
 eval ${_UNSET} mss_got_one;
 eval ${_UNSET} mss_name;
 eval ${_UNSET} mss_prefix;
 eval ${_UNSET} mss_section;
 eval "${return_bad}";
} # man_search_section()


########################################################################
# man_setup ()
#
# Setup the variables $_MAN_* needed for man page searching.
#
# Globals:
#   in:     $_OPT_*, $_MANOPT_*, $LANG, $LC_MESSAGES, $LC_ALL,
#           $MANPATH, $MANROFFSEQ, $MANSEC, $PAGER, $SYSTEM, $MANOPT.
#   out:    $_MAN_PATH, $_MAN_LANG, $_MAN_SYS, $_MAN_LANG, $_MAN_LANG2,
#           $_MAN_SEC, $_MAN_ALL
#   in/out: $_MAN_ENABLE
#
# The precedence for the variables related to `man' is that of GNU
# `man', i.e.
#
# $LANG; overridden by
# $LC_MESSAGES; overridden by
# $LC_ALL; this has the same precedence as
# $MANPATH, $MANROFFSEQ, $MANSEC, $PAGER, $SYSTEM; overridden by
# $MANOPT; overridden by
# the groffer command line options.
#
# Variable prefix: ms
#
man_setup()
{
 func_check main_man_setup = 0 "$@";

 if obj _MAN_IS_SETUP is_yes
 then
   eval "${return_ok}";
 fi;
 _MAN_IS_SETUP='yes';

 if obj _MAN_ENABLE is_not_yes
 then
   eval "${return_ok}";
 fi;

 # determine basic path for man pages
 _MAN_PATH="$(get_first_essential \
              "${_OPT_MANPATH}" "${_MANOPT_PATH}" "${MANPATH}")";
 exit_test;
 if obj _MAN_PATH is_empty
 then
   manpath_set_from_path;
 else
   _MAN_PATH="$(path_clean "${_MAN_PATH}")";
   exit_test;
 fi;
 if obj _MAN_PATH is_empty
 then
   if is_prog 'manpath'
   then
     _MAN_PATH="$(manpath 2>${_NULL_DEV})"; # not always available
     exit_test;
   fi;
 fi;
 if obj _MAN_PATH is_empty
 then
   _MAN_ENABLE="no";
   eval "${return_ok}";
 fi;

 _MAN_ALL="$(get_first_essential "${_OPT_ALL}" "${_MANOPT_ALL}")";
 exit_test;
 if obj _MAN_ALL is_empty
 then
   _MAN_ALL='no';
 fi;

 _MAN_SYS="$(get_first_essential \
             "${_OPT_SYSTEMS}" "${_MANOPT_SYS}" "${SYSTEM}")";
 ms_lang="$(get_first_essential \
          "${_OPT_LANG}" "${LC_ALL}" "${LC_MESSAGES}" "${LANG}")";
 exit_test;
 case "${ms_lang}" in
   C|POSIX)
     _MAN_LANG="";
     _MAN_LANG2="";
     ;;
   ?)
     _MAN_LANG="${ms_lang}";
     _MAN_LANG2="";
     ;;
   *)
     _MAN_LANG="${ms_lang}";
     # get first two characters of $ms_lang
     _MAN_LANG2="$(echo1 "${ms_lang}" | sed -e 's/^\(..\).*$/\1/')";
     exit_test;
     ;;
 esac;
 # from now on, use only $_LANG, forget about $_OPT_LANG, $LC_*.

 manpath_add_lang_sys;         # this is very slow

 _MAN_SEC="$(get_first_essential \
             "${_OPT_SECT}" "${_MANOPT_SEC}" "${MANSEC}")";
 exit_test;
 if obj _MAN_PATH is_empty
 then
   _MAN_ENABLE="no";
   eval ${_UNSET} ms_lang;
   eval "${return_ok}";
 fi;

 _MAN_EXT="$(get_first_essential \
             "${_OPT_EXTENSION}" "${_MANOPT_EXTENSION}")";
 exit_test;
 eval ${_UNSET} ms_lang;
 eval "${return_ok}";
} # man_setup()


########################################################################
landmark '8: manpath_*()';
########################################################################

########################################################################
# manpath_add_lang_sys ()
#
# Add language and operating system specific directories to man path.
#
# Arguments : 0
# Output    : none
# Globals:
#   in:     $_MAN_SYS: has the form `os1,os2,...', a comma separated
#             list of names of operating systems.
#           $_MAN_LANG and $_MAN_LANG2: each a single name
#   in/out: $_MAN_PATH: has the form `dir1:dir2:...', a colon
#             separated list of directories.
#
# Variable prefix: mals
#
manpath_add_lang_sys()
{
 func_check manpath_add_lang_sys = 0 "$@";
 if obj _MAN_PATH is_empty
 then
   eval "${return_ok}";
 fi;
 # twice test both sys and lang
 eval set x "$(path_split "${_MAN_PATH}")";
 shift;
 exit_test;
 mals_mp='';
 for p
 do                            # loop on man path directories
   mals_mp="$(_manpath_add_lang_sys_single "${mals_mp}" "$p")";
   exit_test;
 done;
 eval set x "$(path_split "${mals_mp}")";
 shift;
 exit_test;
 for p
 do                            # loop on man path directories
   mals_mp="$(_manpath_add_lang_sys_single "${mals_mp}" "$p")";
   exit_test;
 done;
 _MAN_PATH="$(path_chop "${mals_mp}")";
 exit_test;
 eval ${_UNSET} mals_mp;
 eval "${return_ok}";
}


# To the directory in $1 append existing sys/lang subdirectories
# Function is necessary to split the OS list.
#
# globals: in: $_MAN_SYS, $_MAN_LANG, $_MAN_LANG2
# argument: 2: `man_path' and `dir'
# output: colon-separated path of the retrieved subdirectories
#
# Variable prefix: _mals
#
_manpath_add_lang_sys_single()
{
 func_check _manpath_add_lang_sys_single = 2 "$@";
 _mals_res="$1";
 _mals_parent="$2";
 eval set x "$(list_from_split "${_MAN_SYS}" ',')";
 shift;
 exit_test;
 for d in "$@" "${_MAN_LANG}" "${_MAN_LANG2}"
 do
   _mals_dir="$(dirname_append "${_mals_parent}" "$d")";
   exit_test;
   if obj _mals_res path_not_contains "${_mals_dir}" && \
      obj _mals_dir is_dir
   then
     _mals_res="${_mals_res}:${_mals_dir}";
   fi;
 done;
 if path_not_contains "${_mals_res}" "${_mals_parent}"
 then
   _mals_res="${_mals_res}:${_mals_parent}";
 fi;
 path_chop "${_mals_res}";
 eval ${_UNSET} _mals_dir;
 eval ${_UNSET} _mals_parent;
 eval ${_UNSET} _mals_res;
 eval "${return_ok}";
}

# end manpath_add_lang_sys ()


########################################################################
# manpath_set_from_path ()
#
# Determine basic search path for man pages from $PATH.
#
# Return:    `0' if a valid man path was retrieved.
# Output:    none
# Globals:
#   in:  $PATH
#   out: $_MAN_PATH
#
# Variable prefix: msfp
#
manpath_set_from_path()
{
 func_check manpath_set_from_path = 0 "$@";

 msfp_manpath='';

 # get a basic man path from $PATH
 if obj PATH is_not_empty
 then
   eval set x "$(path_split "${PATH}")";
   shift;
   exit_test;
   for d
   do
     # delete the final `/bin' part
     msfp_base="$(echo1 "$d" | sed -e 's|//*bin/*$||')";
     exit_test;
     for e in /share/man /man
     do
       msfp_mandir="${msfp_base}$e";
       if test -d "${msfp_mandir}" && test -r "${msfp_mandir}"
       then
         msfp_manpath="${msfp_manpath}:${msfp_mandir}";
       fi;
     done;
   done;
 fi;

 # append some default directories
 for d in /usr/local/share/man /usr/local/man \
          /usr/share/man /usr/man \
          /usr/X11R6/man /usr/openwin/man \
          /opt/share/man /opt/man \
          /opt/gnome/man /opt/kde/man
 do
   msfp_d="$d";
   if obj msfp_manpath path_not_contains "${msfp_d}" && obj mfsp_d is_dir
   then
     msfp_manpath="${msfp_manpath}:${mfsp_d}";
   fi;
 done;

 _MAN_PATH="${msfp_manpath}";
 eval ${_UNSET} msfp_base;
 eval ${_UNSET} msfp_d;
 eval ${_UNSET} msfp_mandir;
 eval ${_UNSET} msfp_manpath;
 eval "${return_ok}";
} # manpath_set_from_path()


########################################################################
landmark '9: obj_*()';
########################################################################

########################################################################
# obj (<object> <call_name> <arg>...)
#
# This works like a method (object function) call for an object.
# Run "<call_name> $<object> <arg> ...".
#
# The first argument represents an object whose data is given as first
# argument to <call_name>().
#
# Argument: >=2
#           <object>: variable name
#           <call_name>: a program or function name
#
# Variable prefix: o
#
obj()
{
 func_check obj '>=' 2 "$@";
 eval o_arg1='"${'$1'}"';
 if is_empty "$2"
 then
   error "obj(): function name is empty."
 else
   o_func="$2";
 fi;
 shift;
 shift;
 eval "${o_func}"' "${o_arg1}" "$@"';
 n="$?";
 eval ${_UNSET} o_arg1;
 eval ${_UNSET} o_func;
 eval "${return_var} $n";
} # obj()


########################################################################
# obj_data (<object>)
#
# Print the data of <object>, i.e. the content of $<object>.
# For possible later extensions.
#
# Arguments: 1
#            <object>: a variable name
# Output:    the data of <object>
#
# Variable prefix: od
#
obj_data()
{
 func_check obj '=' 1 "$@";
 if is_empty "$1"
 then
   error "obj_data(): object name is empty."
 fi;
 eval od_res='"${'$1'}"';
 obj od_res echo1;
 eval ${_UNSET} od_res;
 eval "${return_ok}";
}


########################################################################
# obj_from_output (<object> <call_name> <arg>...)
#
# Run '$<object>="$(<call_name> <arg>...)"' to set the result of a
# function call to a global variable.
#
# Arguments: >=2
#            <object>: a variable name
#            <call_name>: the name of a function or program
#            <arg>: optional argument to <call_name>
# Output:    none
#
# Variable prefix: ofo
#
obj_from_output()
{
 func_check obj_from_output '>=' 2 "$@";
 if is_empty "$1"
 then
   error "res(): variable name is empty.";
 elif is_empty "$2"
 then
   error "res(): function name is empty."
 else
   ofo_result_name="$1";
 fi;
 shift;
 eval "${ofo_result_name}"'="$('"$@"')"';
 exit_test;
 eval "${return_ok}";
}


########################################################################
# obj_set (<object> <data>)
#
# Set the data of <object>, i.e. call "$<object>=<data>".
#
# Arguments: 2
#            <object>: a variable name
#            <data>: a string
# Output::   none
#
obj_set()
{
 func_check obj_set '=' 2 "$@";
 if is_empty "$1"
 then
   error "obj_set(): object name is empty."
 fi;
 eval "$1"='"$2"';
 eval "${return_ok}";
}


########################################################################
# path_chop (<path>)
#
# Remove unnecessary colons from path.
#
# Argument: 1, a colon separated path.
# Output:   path without leading, double, or trailing colons.
#
path_chop()
{
 func_check path_chop = 1 "$@";

 # replace multiple colons by a single colon `:'
 # remove leading and trailing colons
 echo1 "$1" | sed -e '
s/^:*//
s/:::*/:/g
s/:*$//
';
 eval "${return_ok}";
}


########################################################################
# path_clean (<path>)
#
# Remove non-existing directories from a colon-separated list.
#
# Argument: 1, a colon separated path.
# Output:   colon-separated list of existing directories.
#
# Variable prefix: pc
#
path_clean()
{
 func_check path_clean = 1 "$@";
 if is_not_equal "$#" 1
 then
   error 'path_clean() needs 1 argument.';
 fi;
 pc_arg="$1";
 eval set x "$(path_split "${pc_arg}")";
 exit_test;
 shift;
 pc_res="";
 for i
 do
   pc_i="$i";
   if obj pc_i is_not_empty \
      && obj pc_res path_not_contains "${pc_i}" \
      && obj pc_i is_dir
   then
     case "${pc_i}" in
     ?*/)
       pc_res="${pc_res}$(dirname_chop "${pc_i}")";
       exit_test;
       ;;
     *)
       pc_res="${pc_res}:${pc_i}";
       exit_test;
       ;;
     esac;
   fi;
 done;
 eval ${_UNSET} pc_arg;
 eval ${_UNSET} pc_i;
 eval ${_UNSET} pc_res;
 if path_chop "${pc_res}"
 then
   eval "${return_ok}";
 else
   eval "${return_bad}";
 fi;
}


########################################################################
# path_contains (<path> <dir>)
#-
# Test whether `dir' is contained in `path', a list separated by `:'.
#
# Arguments : 2 arguments.
# Return    : `0' if arg2 is substring of arg1, `1' otherwise.
#
path_contains()
{
 func_check path_contains = 2 "$@";
 case ":$1:" in
   *":$2:"*)
     eval "${return_yes}";
     ;;
   *)
     eval "${return_no}";
     ;;
 esac;
 eval "${return_ok}";
}


########################################################################
# path_not_contains (<path> <dir>)
#
# Test whether `dir' is not contained in colon separated `path'.
#
# Arguments : 2 arguments.
#
path_not_contains()
{
 func_check path_not_contains = 2 "$@";
 if path_contains "$1" "$2"
 then
   eval "${return_no}";
 else
   eval "${return_yes}";
 fi;
 eval "${return_ok}";
}


########################################################################
# path_split (<path>)
#
# In `path' escape white space and replace each colon by a space.
#
# Arguments: 1: a colon-separated path
# Output:    the resulting list, process with `eval set'
#
path_split()
{
 func_check path_split = 1 "$@";
 list_from_split "$1" ':';
 eval "${return_ok}";
}


########################################################################
landmark '10: register_*()';
########################################################################

########################################################################
# register_file (<filename>)
#
# Write a found file and register the title element.
#
# Arguments: 1: a file name
# Output: none
#
register_file()
{
 func_check register_file = 1 "$@";
 if is_empty "$1"
 then
   error 'register_file(): file name is empty';
 fi;
 if is_equal "$1" '-'
 then
   to_tmp "${_TMP_STDIN}";
   register_title 'stdin';
 else
   to_tmp "$1";
   register_title "$(base_name "$1")";
   exit_test;
 fi;
 eval "${return_ok}";
} # register_file()


########################################################################
# register_title (<filespec>)
#
# Create title element from <filespec> and append to $_REGISTERED_TITLE
#
# Globals: $_REGISTERED_TITLE (rw)
#
# Variable prefix: rt
#
register_title()
{
 func_check register_title '=' 1 "$@";
 if is_empty "$1"
 then
   eval "${return_ok}";
 fi;

 case "${_REGISTERED_TITLE}" in
 *\ *\ *\ *)
   eval "${return_ok}";
   ;;
 esac;

 # remove directory part
 rt_title="$(base_name "$1")";
 # replace space characters by `_'
 rt_title="$(echo1 "${rt_title}" | sed -e 's/[         ]/_/g')";
 # remove extension `.bz2'
 rt_title="$(echo1 "${rt_title}" | sed -e 's/\.bz2$//')";
 # remove extension `.gz'
 rt_title="$(echo1 "${rt_title}" | sed -e 's/\.gz$//')";
 # remove extension `.Z'
 rt_title="$(echo1 "${rt_title}" | sed -e 's/\.Z$//')";
 exit_test;

 if obj rt_title is_empty
 then
   eval ${_UNSET} rt_title;
   eval "${return_ok}";
 fi;
 if obj _REGISTERED_TITLE is_empty
 then
   _REGISTERED_TITLE="${rt_title}";
 else
   _REGISTERED_TITLE="${_REGISTERED_TITLE} ${rt_title}";
 fi;
 eval ${_UNSET} rt_title;
 eval "${return_ok}";
} # register_title()


########################################################################
# reset ()
#
# Reset the variables that can be affected by options to their default.
#
#
# Defined in section `Preset' after the rudimentary shell tests.


########################################################################
# rm_file (<file_name>)
#
# Remove file if $_DEBUG_KEEP_FILES allows it.
#
# Globals: $_DEBUG_KEEP_FILES
#
rm_file()
{
 func_check rm_file '=' 1 "$@";
 if is_file "$1"
 then
   rm -f "$1" >${_NULL_DEV} 2>&1;
 fi;
 if is_existing "$1"
 then
   eval "${return_bad}";
 else
   eval "${return_good}";
 fi;
}


########################################################################
# rm_file_with_debug (<file_name>)
#
# Remove file if $_DEBUG_KEEP_FILES allows it.
#
# Globals: $_DEBUG_KEEP_FILES
#
rm_file_with_debug()
{
 func_check rm_file_with_debug '=' 1 "$@";
 if obj _DEBUG_KEEP_FILES is_not_yes
 then
   if is_file "$1"
   then
     rm -f "$1" >${_NULL_DEV} 2>&1;
   fi;
 fi;
 if is_existing "$1"
 then
   eval "${return_bad}";
 else
   eval "${return_good}";
 fi;
}


########################################################################
# rm_tree (<dir_name>)
#
# Remove file if $_DEBUG_KEEP_FILES allows it.
#
# Globals: $_DEBUG_KEEP_FILES
#
rm_tree()
{
 func_check rm_tree '=' 1 "$@";
 if is_existing "$1"
 then
   rm -f -r "$1" >${_NULL_DEV} 2>&1;
 fi;
 if is_existing "$1"
 then
   eval "${return_bad}";
 else
   eval "${return_good}";
 fi;
}


########################################################################
# save_stdin ()
#
# Store standard input to temporary file (with decompression).
#
# Variable prefix: ss
#
if obj _HAS_COMPRESSION is_yes
then
 save_stdin()
 {
   func_check save_stdin '=' 0 "$@";
   ss_f="${_TMP_DIR}"/INPUT;
   cat >"${ss_f}";
   cat_z "${ss_f}" >"${_TMP_STDIN}";
   rm_file "${ss_f}";
   eval ${_UNSET} ss_f;
   eval "${return_ok}";
 }
else
 save_stdin()
 {
   func_check save_stdin = 0 "$@";
   cat >"${_TMP_STDIN}";
   eval "${return_ok}";
 }
fi;


########################################################################
# special_filespec ()
#
# Handle special modes like whatis and apropos.
#
special_filespec()
{
 func_check special_setup '=' 0 "$@";
 if obj _OPT_APROPOS is_yes
 then
   if obj _OPT_WHATIS is_yes
   then
     error \
       'special_setup: $_OPT_APROPOS and $_OPT_WHATIS are both "yes"';
   fi;
   apropos_filespec;
   eval "${return_ok}";
 fi;
 if obj _OPT_WHATIS is_yes
 then
   whatis_filespec;
 fi;
 eval "${return_ok}";
}


########################################################################
# special_setup ()
#
# Handle special modes like whatis and apropos.
#
special_setup()
{
 func_check special_setup '=' 0 "$@";
 if obj _OPT_APROPOS is_yes
 then
   if obj _OPT_WHATIS is_yes
   then
     error \
       'special_setup: $_OPT_APROPOS and $_OPT_WHATIS are both "yes"';
   fi;
   apropos_setup;
   eval "${return_ok}";
 fi;
 if obj _OPT_WHATIS is_yes
 then
   whatis_header;
 fi;
 eval "${return_ok}";
}


########################################################################
landmark '11: stack_*()';
########################################################################

########################################################################
# string_contains (<string> <part>)
#
# Test whether `part' is contained in `string'.
#
# Arguments : 2 text arguments.
# Return    : `0' if arg2 is substring of arg1, `1' otherwise.
#
string_contains()
{
 func_check string_contains '=' 2 "$@";
 case "$1" in
   *"$2"*)
     eval "${return_yes}";
     ;;
   *)
     eval "${return_no}";
     ;;
 esac;
 eval "${return_ok}";
}


########################################################################
# string_not_contains (<string> <part>)
#
# Test whether `part' is not substring of `string'.
#
# Arguments : 2 text arguments.
# Return    : `0' if arg2 is substring of arg1, `1' otherwise.
#
string_not_contains()
{
 func_check string_not_contains '=' 2 "$@";
 if string_contains "$1" "$2"
 then
   eval "${return_no}";
 else
   eval "${return_yes}";
 fi;
 eval "${return_ok}";
}


########################################################################
landmark '12: tmp_*()';
########################################################################

########################################################################
# tmp_cat ()
#
# output the temporary cat file (the concatenation of all input)
#
tmp_cat()
{
 func_check tmp_cat '=' 0 "$@";
 cat "${_TMP_CAT}";
 eval "${return_var}" "$?";
}


########################################################################
# tmp_create (<suffix>?)
#
# Create temporary file.
#
# It's safe to use the shell process ID together with a suffix to
# have multiple temporary files.
#
# Globals: $_TMP_DIR
#
# Output : name of created file
#
# Variable prefix: tc
#
tmp_create()
{
 func_check tmp_create '<=' 1 "$@";
 # the output file does not have `,' as first character, so these are
 # different names from the output file.
 tc_tmp="${_TMP_DIR}/,$1";
 : >"${tc_tmp}"
 obj tc_tmp echo1;
 eval ${_UNSET} tc_tmp;
 eval "${return_ok}";
}


########################################################################
# to_tmp (<filename>)
#
# print file (decompressed) to the temporary cat file
#
to_tmp()
{
 func_check to_tmp '=' 1 "$@";
 if obj _TMP_CAT is_empty
 then
   error 'to_tmp_line: $_TMP_CAT is not yet set';
 fi;
 if is_file "$1"
 then
   if obj _OPT_LOCATION is_yes
   then
     echo2 "$1";
   fi;
   if obj _OPT_WHATIS is_yes
   then
     whatis_filename "$1" >>"${_TMP_CAT}";
   else
     cat_z "$1" >>"${_TMP_CAT}";
   fi;
 else
   error "to_tmp(): could not read file \`$1'.";
 fi;
 eval "${return_ok}";
}


########################################################################
# to_tmp_line ([<text>])
#
# print line to the temporary cat file
#
to_tmp_line()
{
 func_check to_tmp '>=' 0 "$@";
 if obj _TMP_CAT is_empty
 then
   error 'to_tmp_line: $_TMP_CAT is not yet set';
 fi;
 echo1 "$*" >>"${_TMP_CAT}";
 eval "${return_ok}";
}


########################################################################
# trap_set
#
# call function on signal 0
#
trap_set()
{
 func_check trap_set '=' 0 "$@";
 trap 'clean_up' 0 2>${_NULL_DEV} || :;
 eval "${return_ok}";
}


########################################################################
# trap_unset ()
#
# disable trap on signal 0.
#
trap_unset()
{
 func_check trap_unset '=' 0 "$@";
 trap '' 0 2>${_NULL_DEV} || :;
 eval "${return_ok}";
}


########################################################################
# usage ()
#
# print usage information to stderr; for groffer option --help.
#
usage()
{
 func_check usage = 0 "$@";
 echo;
 version;
 echo1 'Usage: groffer [option]... [filespec]...';
 cat <<EOF

Display roff files, standard input, and/or Unix manual pages with a X
Window viewer or in several text modes.  All input is decompressed
on-the-fly with all formats that gzip can handle.

"filespec" is one of
 "filename"       name of a readable file
 "-"              for standard input
 "man:name.n"     man page "name" in section "n"
 "man:name"       man page "name" in first section found
 "name.n"         man page "name" in section "n"
 "name"           man page "name" in first section found
and some more (see groffer(1) for details).

-h --help         print this usage message.
-Q --source       output as roff source.
-T --device=name  pass to groff using output device "name".
-v --version      print version information.
-V                display the groff execution pipe instead of formatting.
-X                display with "gxditview" using groff -X.
-Z --ditroff --intermediate-output
                 generate groff intermediate output without
                 post-processing and viewing, like groff -Z.
All other short options are interpreted as "groff" formatting options.

The most important groffer long options are

--apropos=name    start man's "apropos" program for "name".
--apropos-data=name
                 "apropos" for "name" in man's data sections 4, 5, 7.
--apropos-devel=name
                 "apropos" for "name" in development sections 2, 3, 9.
--apropos-progs=name
                 "apropos" for "name" in man's program sections 1, 6, 8.
--auto            choose mode automatically from the default mode list.
--default         reset all options to the default value.
--default-modes=mode1,mode2,...
                 set sequence of automatically tried modes.
--dvi             display in a viewer for TeX device independent format.
--dvi-viewer=prog choose the viewer program for dvi mode.
--groff           process like groff, disable viewing features.
--help            display this helping output.
--html            display in a web browser.
--html-viewer=program
                 choose the web browser for html mode.
--man             check file parameters first whether they are man pages.
--mode=auto|dvi|groff|html|pdf|ps|source|text|tty|www|x|X
                 choose display mode.
--no-man          disable man-page facility.
--no-special      disable --all, --apropos*, and --whatis
--pager=program   preset the paging program for tty mode.
--pdf             display in a PDF viewer.
--pdf-viewer=prog choose the viewer program for pdf mode.
--ps              display in a Postscript viewer.
--ps-viewer=prog  choose the viewer program for ps mode.
--shell=program   specify a shell under which to run groffer2.sh.
--text            output in a text device without a pager.
--tty             display with a pager on text terminal even when in X.
--tty-viewer=prog select a pager for tty mode; same as --pager.
--whatis          display the file name and description of man pages
--www             same as --html.
--www-viewer=prog same as --html-viewer
--x --X           display with "gxditview" using an X* device.
--x-viewer=prog   choose viewer program for x mode (X mode).
--X-viewer=prog   same as "--xviewer".

Development options that are not useful for normal usage:
--debug, --debug-all, --debug-keep, --debug-lm, --debug-params,
--debug-shell, --debug-stacks, --debug-tmpdir, --debug-user,
--do-nothing, --print=text

Viewer programs for the different modes that run on the terminal:
--dvi-viewer-tty=prog, --html-viewer-tty=prog, --pdf-viewer-tty=prog,
--ps-viewer-tty=prog, --tty-viewer-tty, --X-viewer-tty=prog,
--x-viewer-tty=prog, --www-viewer-tty=prog

The usual X Windows toolkit options transformed into GNU long options:
--background=color, --bd=size, --bg=color, --bordercolor=color,
--borderwidth=size, --bw=size, --display=Xdisplay, --fg=color,
--fn=font, --font=font, --foreground=color, --geometry=geom, --iconic,
--resolution=dpi, --rv, --title=text, --xrm=resource

Long options of GNU "man":
--all, --ascii, --ditroff, --extension=suffix, --locale=language,
--local-file=name, --location, --manpath=dir1:dir2:...,
--sections=s1:s2:..., --systems=s1,s2,..., --where, ...

EOF
 eval "${return_ok}";
}


########################################################################
# version ()
#
# print version information to stderr
#
version()
{
 func_check version = 0 "$@";
 echo1 "groffer ${_PROGRAM_VERSION} of ${_LAST_UPDATE}";
 # also display groff's version, but not the called subprograms
 groff -v 2>&1 | sed -e '/^ *$/q' | sed -e '1s/^/is part of /';
 eval "${return_ok}";
}


########################################################################
# warning (<string>)
#
# Print warning to stderr
#
warning()
{
 echo2 "warning: $*";
}


########################################################################
# whatis_filename (<filename>)
#
# Interpret <filename> as a man page and display its `whatis'
# information as a fragment written in the groff language.
#
# Variable prefix: wf
#
whatis_filename()
{
 func_check whatis_filename = 1 "$@";
 wf_arg="$1";
 if obj wf_arg is_not_file
 then
   error "whatis_filename(): argument is not a readable file."
 fi;
 wf_dot='^\.'"${_SPACE_SED}"'*';
 if obj _FILESPEC_ARG is_equal '-'
 then
   wf_arg='stdin';
 fi;
 cat <<EOF
\f[CR]${wf_arg}\f[]:
br
EOF

 # get the parts of the file name
 wf_name="$(base_name $1)";
 wf_section="$(echo1 $1 | sed -n -e '
s|^.*/man\('"${_MAN_AUTO_SEC_CHARS}"'\).*$|\1|p
')";
 if obj wf_section is_not_empty
 then
   case "${wf_name}" in
   *.${wf_section}*)
     s='yes';
     ;;
   *)
     s='';
     wf_section='';
     ;;
   esac
   if obj s is_yes
   then
     wf_name="$(echo1 ${wf_name} | sed -e '
s/^\(.*\)\.'${wf_section}'.*$/\1/
')";
   fi;
 fi;

 # traditional man style; grep the line containing `.TH' macro, if any
 wf_res="$(cat_z "$1" | sed -e '
/'"${wf_dot}"'TH /p
d
')";
 exit_test;
 if obj wf_res is_not_empty
 then                          # traditional man style
   # get the first line after the first `.SH' macro, by
   # - delete up to first .SH;
   # - print all lines before the next .SH;
   # - quit.
   wf_res="$(cat_z "$1" | sed -n -e '
1,/'"${wf_dot}"'SH/d
/'"${wf_dot}"'SH/q
p
')";

   if obj wf_section is_not_empty
   then
     case "${wf_res}" in
     ${wf_name}${_SPACE_CASE}*-${_SPACE_CASE}*)
       s='yes';
       ;;
     *)
       s='';
       ;;
     esac;
     if obj s is_yes
     then
       wf_res="$(obj wf_res echo1 | sed -e '
s/^'"${wf_name}${_SPACE_SED}"'[^-]*-'"${_SPACE_SED}"'*\(.*\)$/'"${wf_name}"' ('"${wf_section}"') \\[em] \1/
')";
     fi;
   fi;
   obj wf_res echo1;
   echo;
   eval ${_UNSET} wf_arg;
   eval ${_UNSET} wf_dot;
   eval ${_UNSET} wf_name;
   eval ${_UNSET} wf_res;
   eval ${_UNSET} wf_section;
   eval "${return_ok}";
 fi;

 # mdoc style (BSD doc); grep the line containing `.Nd' macro, if any
 wf_res="$(cat_z "$1" | sed -n -e '/'"${wf_dot}"'Nd /s///p')";
 exit_test;
 if obj wf_res is_not_empty
 then                          # BSD doc style
   if obj wf_section is_not_empty
   then
     wf_res="$(obj wf_res echo1 | sed -n -e '
s/^\(.*\)$/'"${wf_name}"' ('"${wf_section}"') \\[em] \1/p
')";
   fi;
   obj wf_res echo1;
   echo;
   eval ${_UNSET} wf_arg;
   eval ${_UNSET} wf_dot;
   eval ${_UNSET} wf_name;
   eval ${_UNSET} wf_res;
   eval ${_UNSET} wf_section;
   eval "${return_ok}";
 fi;
 echo1 'is not a man page';
 echo;
 eval ${_UNSET} wf_arg;
 eval ${_UNSET} wf_dot;
 eval ${_UNSET} wf_name;
 eval ${_UNSET} wf_res;
 eval ${_UNSET} wf_section;
 eval "${return_bad}";
}


########################################################################
# whatis_filespec ()
#
# Print the filespec name as .SH to the temporary cat file.
#
whatis_filespec()
{
 func_check whatis_filespec '=' 0 "$@";
 if obj _OPT_WHATIS is_yes
 then
   eval to_tmp_line \
     "'.SH $(echo1 "${_FILESPEC_ARG}" | sed 's/[^\\]-/\\-/g')'";
   exit_test;
 fi;
 eval "${return_ok}";
}


########################################################################
# whatis_header ()
#
# Print the whatis header to the temporary cat file.
#
whatis_header()
{
 func_check whatis_header '=' 0 "$@";
 if obj _OPT_WHATIS is_yes
 then
    to_tmp_line '.TH GROFFER WHATIS';
 fi;
 eval "${return_ok}";
}


########################################################################
# where_is (<program>)
#
# Output path of a program if in $PATH.
#
# Arguments : >=1 (empty allowed)
#   more args are ignored, this allows to specify progs with arguments
# Return    : `0' if arg1 is a program in $PATH, `1' otherwise.
#
# Variable prefix: w
#
where_is()
{
 func_check where_is '>=' 1 "$@";
 w_arg="$1";
 if obj w_arg is_empty
 then
   eval ${_UNSET} w_arg;
   eval "${return_bad}";
 fi;
 case "${w_arg}" in
   /*)
     eval ${_UNSET} w_arg;
     eval ${_UNSET} w_file;
     if test -f "${w_arg}" && test -x "${w_arg}"
     then
       eval "${return_ok}";
     else
       eval "${return_bad}";
     fi;
     ;;
 esac;
 eval set x "$(path_split "${PATH}")";
 exit_test;
 shift;
 for p
 do
   case "$p" in
     */) w_file=${p}${w_arg}; ;;
     *)  w_file=${p}/${w_arg}; ;;
   esac;
   if test -f "${w_file}" && test -x "${w_file}"
   then
     obj w_file echo1;
     eval ${_UNSET} w_arg;
     eval ${_UNSET} w_file;
     eval "${return_ok}";
   fi;
 done;
 eval ${_UNSET} w_arg;
 eval ${_UNSET} w_file;
 eval "${return_bad}";
}


########################################################################
#                        main* Functions
########################################################################

# The main area contains the following parts:
# - main_init(): initialize temporary files and set exit trap
# - main_parse_MANOPT(): parse $MANOPT
# - main_parse_args(): argument parsing
# - main_set_mode (): determine the display mode
# - main_do_fileargs(): process filespec arguments
# - main_set_resources(): setup X resources
# - main_display(): do the displaying
# - main(): the main function that calls all main_*()


#######################################################################
# main_init ()
#
# set exit trap and create temporary files
#
# Globals: $_TMP_DIR, $_TMP_CAT, $_TMP_STDIN
#
# Variable prefix: mi
#
main_init()
{
 func_check main_init = 0 "$@";
 # call clean_up() on shell termination.
 trap_set;

 # create temporary directory
 umask 0022;
 _TMP_DIR='';
 for d in "${GROFF_TMPDIR}" "${TMPDIR}" "${TMP}" "${TEMP}" \
          "${TEMPDIR}" "${HOME}"'/tmp' '/tmp' "${HOME}" '.'
 do
   mi_dir="$d";
   if obj mi_dir is_empty || obj mi_dir is_not_dir || \
      obj mi_dir is_not_writable
   then
     continue;
   fi;

   case "${mi_dir}" in
   */)
     _TMP_DIR="${mi_dir}";
     ;;
   *)
     _TMP_DIR="${mi_dir}"'/';
     ;;
   esac;
   _TMP_DIR="${_TMP_DIR}groffer${_PROCESS_ID}";
   if obj _TMP_DIR rm_tree
   then
     :
   else
     mi_tdir_="${_TMP_DIR}"_;
     mi_n=1;
     mi_tdir_n="${mi_tdir_}${mi_n}";
     while obj mi_tdir_n is_existing
     do
       if obj mi_tdir_n rm_tree
       then
         # directory could not be removed
         mi_n="$(expr "${mi_n}" + 1)";
         mi_tdir_n="${mi_tdir_}${mi_n}";
         continue;
       fi;
     done;
     _TMP_DIR="${mi_tdir_n}";
   fi;
   eval mkdir "${_TMP_DIR}";
   if is_not_equal "$?" 0
   then
     obj _TMP_DIR rm_tree;
     _TMP_DIR='';
     continue;
   fi;
   if obj _TMP_DIR is_dir && obj _TMP_DIR is_writable
   then
     # $_TMP_DIR can now be used as temporary directory
     break;
   fi;
   obj _TMP_DIR rm_tree;
   _TMP_DIR='';
   continue;
 done;
 if obj _TMP_DIR is_empty
 then
   error "main_init: \
Couldn't create a directory for storing temporary files.";
 fi;
 if obj _DEBUG_PRINT_TMPDIR is_yes
 then
   echo2 "temporary directory: ${_TMP_DIR}";
 fi;

 _TMP_CAT="$(tmp_create groffer_cat)";
 _TMP_STDIN="$(tmp_create groffer_input)";
 exit_test;

 eval ${_UNSET} mi_dir;
 eval ${_UNSET} mi_n;
 eval ${_UNSET} mi_tdir_;
 eval ${_UNSET} mi_tdir_n;
 eval "${return_ok}";
} # main_init()


########################################################################
# main_parse_MANOPT ()
#
# Parse $MANOPT to retrieve man options, but only if it is a non-empty
# string; found man arguments can be overwritten by the command line.
#
# Globals:
#   in: $MANOPT, $_OPTS_MANOPT_*
#   out: $_MANOPT_*
#
# Variable prefix: mpm
#
main_parse_MANOPT()
{
 func_check main_parse_MANOPT = 0 "$@";

 if obj MANOPT is_not_empty
 then
   # Delete leading and final spaces
   MANOPT="$(echo1 "${MANOPT}" | sed -e '
s/^'"${_SPACE_SED}"'*//
s/'"${_SPACE_SED}"'*$//
')";
 exit_test;
 fi;
 if obj MANOPT is_empty
 then
   eval "${return_ok}";
 fi;

 mpm_list='';
 # add arguments in $MANOPT by mapping them to groffer options
 eval set x "$(list_from_cmdline _OPTS_MANOPT "${MANOPT}")";
 exit_test;
 shift;
 until test "$#" -le 0 || is_equal "$1" '--'
 do
   mpm_opt="$1";
   shift;
   case "${mpm_opt}" in
   -7|--ascii)
     list_append mpm_list '--ascii';
     ;;
   -a|--all)
     list_append mpm_list '--all';
     ;;
   -c|--catman)
     do_nothing;
     shift;
     ;;
   -d|--debug)
     do_nothing;
     ;;
   -D|--default)
     # undo all man options so far
     mpm_list='';
     ;;
   -e|--extension)
     list_append mpm_list '--extension';
     shift;
     ;;
   -f|--whatis)
     list_append mpm_list '--whatis';
     shift;
     ;;
   -h|--help)
     do_nothing;
     shift;
     ;;
   -k|--apropos)
     # groffer's --apropos takes an argument, but man's does not, so
     do_nothing;
     ;;
   -l|--local-file)
     do_nothing;
     ;;
   -L|--locale)
     list_append mpm_list '--locale' "$1";
     shift;
     ;;
   -m|--systems)
     list_append mpm_list '--systems' "$1";
     shift;
     ;;
   -M|--manpath)
     list_append mpm_list '--manpath' "$1";
     shift;
     ;;
   -p|--preprocessor)
     do_nothing;
     shift;
     ;;
   -P|--pager)
     list_append mpm_list '--pager' "$1";
     shift;
     ;;
   -r|--prompt)
     do_nothing;
     shift;
     ;;
   -S|--sections)
     list_append mpm_list '--sections' "$1";
     shift;
     ;;
   -t|--troff)
     do_nothing;
     shift;
     ;;
   -T|--device)
     list_append mpm_list '-T' "$1";
     shift;
     ;;
   -u|--update)
     do_nothing;
     shift;
     ;;
   -V|--version)
     do_nothing;
     ;;
   -w|--where|--location)
     list_append mpm_list '--location';
     ;;
   -Z|--ditroff)
     do_nothing;
     ;;
   # ignore all other options
   esac;
 done;

 # prepend $mpm_list to the command line
 if obj mpm_list is_not_empty
 then
   eval set x "${mpm_list}" '"$@"';
   shift;
 fi;

 eval ${_UNSET} mpm_list;
 eval ${_UNSET} mpm_opt;
 eval "${return_ok}";
} # main_parse_MANOPT()


########################################################################
# main_parse_args (<command_line_args>*)
#
# Parse arguments; process options and filespec parameters
#
# Arguments: pass the command line arguments unaltered.
# Globals:
#   in:  $_OPTS_*
#   out: $_OPT_*, $_ADDOPTS, $_FILEARGS
#
# Variable prefix: mpa
#
main_parse_args()
{
 func_check main_parse_args '>=' 0 "$@";
 _ALL_PARAMS="$(list_from_cmdline _OPTS_CMDLINE "$@")";
 exit_test;
 if obj _DEBUG_PRINT_PARAMS is_yes
 then
   echo2 "parameters: ${_ALL_PARAMS}";
 fi;
 eval set x "${_ALL_PARAMS}";
 shift;

 # By the call of `eval', unnecessary quoting was removed.  So the
 # positional shell parameters ($1, $2, ...) are now guaranteed to
 # represent an option or an argument to the previous option, if any;
 # then a `--' argument for separating options and
 # parameters; followed by the filespec parameters if any.

 # Note, the existence of arguments to options has already been checked.
 # So a check for `$#' or `--' should not be done for arguments.

 until test "$#" -le 0 || is_equal "$1" '--'
 do
   mpa_opt="$1";               # $mpa_opt is fed into the option handler
   shift;
   case "${mpa_opt}" in
   -h|--help)
     usage;
     leave;
     ;;
   -Q|--source)                # output source code (`Quellcode').
     _OPT_MODE='source';
     ;;
   -T|--device|--troff-device) # device; arg
     _OPT_DEVICE="$1";
     _check_device_with_mode;
     shift;
     ;;
   -v|--version)
     version;
     leave;
     ;;
   -V)
     _OPT_V='yes';
     ;;
   -Z|--ditroff|--intermediate-output) # groff intermediate output
     _OPT_Z='yes';
     ;;
   -X)
     if is_X
     then
       _OPT_MODE=X;
     fi;
     ;;
   -?)
     # delete leading `-'
     mpa_optchar="$(echo1 "${mpa_opt}" | sed -e 's/^-//')";
     exit_test;
     if list_has _OPTS_GROFF_SHORT_NA "${mpa_optchar}"
     then
       list_append _ADDOPTS_GROFF "${mpa_opt}";
     elif list_has _OPTS_GROFF_SHORT_ARG "${mpa_optchar}"
     then
       list_append _ADDOPTS_GROFF "${mpa_opt}" "$1";
       shift;
     else
       error "main_parse_args(): Unknown option : \`$1'";
     fi;
     ;;
   --all)
       _OPT_ALL='yes';
       ;;
   --apropos)                  # run `apropos'
     _OPT_APROPOS='yes';
     _APROPOS_SECTIONS='';
     _OPT_WHATIS='no';
     ;;
   --apropos-data)             # run `apropos' for data sections
     _OPT_APROPOS='yes';
     _APROPOS_SECTIONS='457';
     _OPT_WHATIS='no';
     ;;
   --apropos-devel)            # run `apropos' for development sections
     _OPT_APROPOS='yes';
     _APROPOS_SECTIONS='239';
     _OPT_WHATIS='no';
     ;;
   --apropos-progs)            # run `apropos' for program sections
     _OPT_APROPOS='yes';
     _APROPOS_SECTIONS='168';
     _OPT_WHATIS='no';
     ;;
   --ascii)
     list_append _ADDOPTS_GROFF '-mtty-char';
     if obj _OPT_MODE is_empty
     then
       _OPT_MODE='text';
     fi;
     ;;
   --auto)                     # the default automatic mode
     _OPT_MODE='';
     ;;
   --bd)                       # border color for viewers, arg;
     _OPT_BD="$1";
     shift;
     ;;
   --bg|--backgroud)           # background color for viewers, arg;
     _OPT_BG="$1";
     shift;
     ;;
   --bw)                       # border width for viewers, arg;
     _OPT_BW="$1";
     shift;
     ;;
   --debug|--debug-all|--debug-keep|--debug-lm|--debug-params|\
--debug-shell|--debug-stacks|--debug-tmpdir|--debug-user)
     # debug is handled at the beginning
     :;
     ;;
   --default)                  # reset variables to default
     reset;
     ;;
   --default-modes)            # sequence of modes in auto mode; arg
     _OPT_DEFAULT_MODES="$1";
     shift;
     ;;
   --display)                  # set X display, arg
     _OPT_DISPLAY="$1";
     shift;
     ;;
   --do-nothing)
     _OPT_DO_NOTHING='yes';
     ;;
   --dvi)
     if is_X
     then
       _OPT_MODE='dvi';
     fi;
     ;;
   --dvi-viewer)               # viewer program for dvi mode; arg
     _VIEWER_TERMINAL='no';
     _OPT_VIEWER_DVI="$1";
     shift;
     ;;
   --dvi-viewer-tty)           # viewer program for dvi mode in tty; arg
     _VIEWER_TERMINAL='yes';
     _OPT_VIEWER_DVI="$1";
     shift;
     ;;
   --extension)                # the extension for man pages, arg
     _OPT_EXTENSION="$1";
     shift;
     ;;
   --fg|--foreground)          # foreground color for viewers, arg;
     _OPT_FG="$1";
     shift;
     ;;
   --fn|--font)                # set font for viewers, arg;
     _OPT_FN="$1";
     shift;
     ;;
   --geometry)                 # window geometry for viewers, arg;
     _OPT_GEOMETRY="$1";
     shift;
     ;;
   --groff)
     _OPT_MODE='groff';
     ;;
   --html|--www)               # display with web browser
     _OPT_MODE=html;
     ;;
   --html-viewer|--www-viewer) # viewer program for html mode; arg
     _VIEWER_TERMINAL='no';
     _OPT_VIEWER_HTML="$1";
     shift;
     ;;
   --html-viewer-tty|--www-viewer-tty) # viewer for html mode in tty; arg
     _VIEWER_TERMINAL='yes';
     _OPT_VIEWER_HTML="$1";
     shift;
     ;;
   --iconic)                   # start viewers as icons
     _OPT_ICONIC='yes';
     ;;
   --locale)                   # set language for man pages, arg
     # argument is xx[_territory[.codeset[@modifier]]] (ISO 639,...)
     _OPT_LANG="$1";
     shift;
     ;;
   --local-file)               # force local files; same as `--no-man'
     _MAN_FORCE='no';
     _MAN_ENABLE='no';
     ;;
   --location|--where)         # print file locations to stderr
     _OPT_LOCATION='yes';
     ;;
   --man)                     # force all file params to be man pages
     _MAN_ENABLE='yes';
     _MAN_FORCE='yes';
     ;;
   --manpath)                # specify search path for man pages, arg
     # arg is colon-separated list of directories
     _OPT_MANPATH="$1";
     shift;
     ;;
   --mode)                     # display mode
     mpa_arg="$1";
     shift;
     case "${mpa_arg}" in
     auto|'')               # search mode automatically among default
       _OPT_MODE='';
       ;;
     groff)                    # pass input to plain groff
       _OPT_MODE='groff';
       ;;
     html|www)                 # display with a web browser
       _OPT_MODE='html';
       ;;
     dvi)                      # display with xdvi viewer
       if is_X
       then
         _OPT_MODE='dvi';
       fi;
       ;;
     pdf)                      # display with PDF viewer
       if is_X
       then
         _OPT_MODE='pdf';
       fi;
       ;;
     ps)                       # display with Postscript viewer
       if is_X
       then
         _OPT_MODE='ps';
       fi;
       ;;
     text)                     # output on terminal
       _OPT_MODE='text';
       ;;
     tty)                      # output on terminal
       _OPT_MODE='tty';
       ;;
     X|x)                      # output on X roff viewer
       if is_X
       then
         _OPT_MODE='x';
       fi;
       ;;
     Q|source)                 # display source code
       _OPT_MODE="source";
       ;;
     *)
       error "main_parse_args(): unknown mode ${mpa_arg}";
       ;;
     esac;
     ;;
   --no-location)              # disable former call to `--location'
     _OPT_LOCATION='yes';
     ;;
   --no-man)                   # disable search for man pages
     # the same as --local-file
     _MAN_FORCE='no';
     _MAN_ENABLE='no';
     ;;
   --no-special)               # disable some special former calls
     _OPT_ALL='no'
     _OPT_APROPOS='no'
     _OPT_WHATIS='no'
     ;;
   --pager|--tty-viewer|--tty-viewer-tty)
     # set paging program for tty mode, arg
     _VIEWER_TERMINAL='yes';
     _OPT_PAGER="$1";
     shift;
     ;;
   --pdf)
     if is_X
     then
       _OPT_MODE='pdf';
     fi;
     ;;
   --pdf-viewer)               # viewer program for ps mode; arg
     _VIEWER_TERMINAL='no';
     _OPT_VIEWER_PDF="$1";
     shift;
     ;;
   --pdf-viewer-tty)           # viewer program for ps mode in tty; arg
     _VIEWER_TERMINAL='yes';
     _OPT_VIEWER_PDF="$1";
     shift;
     ;;
   --print)                    # for argument test
     echo2 "$1";
     shift;
     ;;
   --ps)
     if is_X
     then
       _OPT_MODE='ps';
     fi;
     ;;
   --ps-viewer)                # viewer program for ps mode; arg
     _VIEWER_TERMINAL='no';
     _OPT_VIEWER_PS="$1";
     shift;
     ;;
   --ps-viewer-tty)            # viewer program for ps mode in tty; arg
     _VIEWER_TERMINAL='yes';
     _OPT_VIEWER_PS="$1";
     shift;
     ;;
   --resolution)               # set resolution for X devices, arg
     mpa_arg="$1";
     shift;
     case "${mpa_arg}" in
     75|75dpi)
       mpa_dpi=75;
       ;;
     100|100dpi)
       mpa_dpi=100;
       ;;
     *)
       error "main_parse_args(): \
only resoutions of 75 or 100 dpi are supported";
       ;;
     esac;
     _OPT_RESOLUTION="${mpa_dpi}";
     ;;
   --rv)
     _OPT_RV='yes';
     ;;
   --sections)                 # specify sections for man pages, arg
     # arg is colon-separated list of section names
     _OPT_SECTIONS="$1";
     shift;
     ;;
   --shell)
     # already done during the first run; so ignore the argument
     shift;
     ;;
   --systems)                  # man pages for different OS's, arg
     # argument is a comma-separated list
     _OPT_SYSTEMS="$1";
     shift;
     ;;
   --text)                     # text mode without pager
     _OPT_MODE=text;
     ;;
   --title)                    # title for X viewers; arg
     _OPT_TITLE="$1";
     shift;
     ;;
   --tty)                      # tty mode, text with pager
     _OPT_MODE=tty;
     ;;
   --text-device|--tty-device) # device for tty mode; arg
     _OPT_TEXT_DEVICE="$1";
     shift;
     ;;
   --whatis)
     _OPT_WHATIS='yes';
     _OPT_ALL='yes';
     _OPT_APROPOS='no';
     ;;
   --X|--x)
     if is_X
     then
       _OPT_MODE=x;
     fi;
     ;;
   --xrm)                      # pass X resource string, arg;
     list_append _OPT_XRM "$1";
     shift;
     ;;
   --x-viewer|--X-viewer)      # viewer program for x mode; arg
     _VIEWER_TERMINAL='no';
     _OPT_VIEWER_X="$1";
     shift;
     ;;
   --x-viewer-tty|--X-viewer-tty) # viewer program for x mode in tty; arg
     _VIEWER_TERMINAL='yes';
     _OPT_VIEWER_X="$1";
     shift;
     ;;
   *)
     error 'main_parse_args(): error on argument parsing : '"\`$*'";
     ;;
   esac;
 done;
 shift;                        # remove `--' argument

 if obj _OPT_DO_NOTHING is_yes
 then
   leave;
 fi;

 # Remaining arguments are file names (filespecs).
 # Save them to list $_FILEARGS
 if is_equal "$#" 0
 then                          # use "-" for standard input
   set x '-';
   shift;
 fi;
 _FILEARGS='';
 list_append _FILEARGS "$@";
 if list_has _FILEARGS '-'
 then
   save_stdin;
 fi;
 # $_FILEARGS must be retrieved with `eval set x "$_FILEARGS"; shift;'
 eval ${_UNSET} mpa_arg;
 eval ${_UNSET} mpa_dpi;
 eval ${_UNSET} mpa_opt;
 eval ${_UNSET} mpa_optchar;
 eval "${return_ok}";
} # main_parse_args()


# Called from main_parse_args() because double `case' is not possible.
# Globals: $_OPT_DEVICE, $_OPT_MODE
_check_device_with_mode()
{
 func_check _check_device_with_mode = 0 "$@";
 case "${_OPT_DEVICE}" in
 dvi)
   _OPT_MODE=dvi;
   eval "${return_ok}";
   ;;
 html)
   _OPT_MODE=html;
   eval "${return_ok}";
   ;;
 lbp|lj4)
   _OPT_MODE=groff;
   eval "${return_ok}";
   ;;
 ps)
   _OPT_MODE=ps;
   eval "${return_ok}";
   ;;
 ascii|cp1047|latin1|utf8)
   if obj _OPT_MODE is_not_equal text
   then
     _OPT_MODE=tty;            # default text mode
   fi;
   eval "${return_ok}";
   ;;
 X*)
   _OPT_MODE=x;
   eval "${return_ok}";
   ;;
 *)                            # unknown device, go to groff mode
   _OPT_MODE=groff;
   eval "${return_ok}";
   ;;
 esac;
 eval "${return_error}";
} # _check_device_with_mode() of main_parse_args()


########################################################################
# main_set_mode ()
#
# Determine the display mode.
#
# Globals:
#   in:  $DISPLAY, $_OPT_MODE, $_OPT_DEVICE
#   out: $_DISPLAY_MODE
#
# Variable prefix: msm
#
main_set_mode()
{
 func_check main_set_mode = 0 "$@";

 # set display
 if obj _OPT_DISPLAY is_not_empty
 then
   DISPLAY="${_OPT_DISPLAY}";
 fi;

 if obj _OPT_V is_yes
 then
   list_append _ADDOPTS_GROFF '-V';
 fi;
 if obj _OPT_Z is_yes
 then
   _DISPLAY_MODE='groff';
   list_append _ADDOPTS_GROFF '-Z';
 fi;
 if obj _OPT_MODE is_equal 'groff'
 then
   _DISPLAY_MODE='groff';
 fi;
 if obj _DISPLAY_MODE is_equal 'groff'
 then
   eval ${_UNSET} msm_modes;
   eval ${_UNSET} msm_viewer;
   eval ${_UNSET} msm_viewers;
   eval "${return_ok}";
 fi;

 if obj _OPT_MODE is_equal 'source'
 then
   _DISPLAY_MODE='source';
   eval ${_UNSET} msm_modes;
   eval ${_UNSET} msm_viewer;
   eval ${_UNSET} msm_viewers;
   eval "${return_ok}";
 fi;

 case "${_OPT_MODE}" in
 '')                           # automatic mode
   case "${_OPT_DEVICE}" in
   X*)
    if is_not_X
     then
       error_user "no X display found for device ${_OPT_DEVICE}";
     fi;
     _DISPLAY_MODE='x';
     eval ${_UNSET} msm_modes;
     eval ${_UNSET} msm_viewer;
     eval ${_UNSET} msm_viewers;
     eval "${return_ok}";
     ;;
   ascii|cp1047|latin1|utf8)
     if obj _DISPLAY_MODE is_not_equal 'text'
     then
       _DISPLAY_MODE='tty';
     fi;
     eval ${_UNSET} msm_modes;
     eval ${_UNSET} msm_viewer;
     eval ${_UNSET} msm_viewers;
     eval "${return_ok}";
     ;;
   esac;
   if is_not_X
   then
     _DISPLAY_MODE='tty';
     eval ${_UNSET} msm_modes;
     eval ${_UNSET} msm_viewer;
     eval ${_UNSET} msm_viewers;
     eval "${return_ok}";
   fi;

   if obj _OPT_DEFAULT_MODES is_empty
   then
     msm_modes="${_DEFAULT_MODES}";
   else
     msm_modes="${_OPT_DEFAULT_MODES}";
   fi;
   ;;
 text)
   _DISPLAY_MODE='text';
   eval ${_UNSET} msm_modes;
   eval ${_UNSET} msm_viewer;
   eval ${_UNSET} msm_viewers;
   eval "${return_ok}";
   ;;
 tty)
   _DISPLAY_MODE='tty';
   eval ${_UNSET} msm_modes;
   eval ${_UNSET} msm_viewer;
   eval ${_UNSET} msm_viewers;
   eval "${return_ok}";
   ;;
 html)
   _DISPLAY_MODE='html';
   msm_modes="${_OPT_MODE}";
   ;;
 *)                            # display mode was given
   if is_not_X
   then
     error_user "You must be in X Window for ${_OPT_MODE} mode.";
   fi;
   msm_modes="${_OPT_MODE}";
   ;;
 esac;

 # only viewer modes are left
 eval set x "$(list_from_split "${msm_modes}" ',')";
 exit_test;
 shift;
 while test "$#" -gt 0
 do
   m="$1";
   shift;
   case "$m" in
   dvi)
     if obj _OPT_VIEWER_DVI is_not_empty
     then
       msm_viewer="${_OPT_VIEWER_DVI}";
     else
       msm_viewer="$(_get_first_prog "$_VIEWER_DVI}")";
       exit_test;
     fi;
     if obj msm_viewer is_empty
     then
       error 'No viewer for dvi mode available.';
     fi;
     if is_not_equal "$?" 0
     then
       continue;
     fi;
     _DISPLAY_PROG="${msm_viewer}";
     _DISPLAY_MODE="dvi";
     eval ${_UNSET} msm_modes;
     eval ${_UNSET} msm_viewer;
     eval ${_UNSET} msm_viewers;
     eval "${return_ok}";
     ;;
   html)
     if obj _OPT_VIEWER_HTML is_not_empty
     then
       msm_viewer="${_OPT_VIEWER_HTML}";
     else
       if is_X
       then
         msm_viewers="${_VIEWER_HTML_X}";
       else
         msm_viewers="${_VIEWER_HTML_TTY}";
       fi;
       msm_viewer="$(_get_first_prog "${msm_viewers}")";
       exit_test;
     fi;
     if obj msm_viewer is_empty
     then
       error 'No viewer for html mode available.';
     fi;
     if is_not_equal "$?" 0
     then
       continue;
     fi;
     _DISPLAY_PROG="${msm_viewer}";
     _DISPLAY_MODE=html;
     eval ${_UNSET} msm_modes;
     eval ${_UNSET} msm_viewer;
     eval ${_UNSET} msm_viewers;
     eval "${return_ok}";
     ;;
   pdf)
     if obj _OPT_VIEWER_PDF is_not_empty
     then
       msm_viewer="${_OPT_VIEWER_PDF}";
     else
       msm_viewer="$(_get_first_prog "${_VIEWER_PDF}")";
       exit_test;
     fi;
     if obj msm_viewer is_empty
     then
       error 'No viewer for pdf mode available.';
     fi;
     if is_not_equal "$?" 0
     then
       continue;
     fi;
     _DISPLAY_PROG="${msm_viewer}";
     _DISPLAY_MODE="pdf";
     eval ${_UNSET} msm_modes;
     eval ${_UNSET} msm_viewer;
     eval ${_UNSET} msm_viewers;
     eval "${return_ok}";
     ;;
   ps)
     if obj _OPT_VIEWER_PS is_not_empty
     then
       msm_viewer="${_OPT_VIEWER_PS}";
     else
       msm_viewer="$(_get_first_prog "${_VIEWER_PS}")";
       exit_test;
     fi;
     if obj msm_viewer is_empty
     then
       error 'No viewer for ps mode available.';
     fi;
     if is_not_equal "$?" 0
     then
       continue;
     fi;
     _DISPLAY_PROG="${msm_viewer}";
     _DISPLAY_MODE="ps";
    eval ${_UNSET} msm_modes;
     eval ${_UNSET} msm_viewer;
     eval ${_UNSET} msm_viewers;
     eval "${return_ok}";
     ;;
   text)
     _DISPLAY_MODE='text';
     eval ${_UNSET} msm_modes;
     eval ${_UNSET} msm_viewer;
     eval ${_UNSET} msm_viewers;
     eval "${return_ok}";
     ;;
   tty)
     _DISPLAY_MODE='tty';
     eval ${_UNSET} msm_modes;
     eval ${_UNSET} msm_viewer;
     eval ${_UNSET} msm_viewers;
     eval "${return_ok}";
     ;;
   x)
     if obj _OPT_VIEWER_X is_not_empty
     then
       msm_viewer="${_OPT_VIEWER_X}";
     else
       msm_viewer="$(_get_first_prog "${_VIEWER_X}")";
       exit_test;
     fi;
     if obj msm_viewer is_empty
     then
       error 'No viewer for x mode available.';
     fi;
     if is_not_equal "$?" 0
     then
       continue;
     fi;
     _DISPLAY_PROG="${msm_viewer}";
     _DISPLAY_MODE='x';
     eval ${_UNSET} msm_modes;
     eval ${_UNSET} msm_viewer;
     eval ${_UNSET} msm_viewers;
     eval "${return_ok}";
     ;;
   X)
     _DISPLAY_MODE='X';
     eval ${_UNSET} msm_modes;
     eval ${_UNSET} msm_viewer;
     eval ${_UNSET} msm_viewers;
     eval "${return_ok}";
     ;;
   esac;
 done;
 eval ${_UNSET} msm_modes;
 eval ${_UNSET} msm_viewer;
 eval ${_UNSET} msm_viewers;
 error_user "No suitable display mode found.";
} # main_set_mode()


# _get_first_prog (<proglist>)
#
# Retrieve first argument that represents an existing program in $PATH.
# Local function for main_set_mode().
#
# Arguments: 1; a comma-separated list of commands (with options),
#               like $_VIEWER_*.
#
# Return  : `1' if none found, `0' if found.
# Output  : the argument that succeded.
#
# Variable prefix: _gfp
#
_get_first_prog()
{
 if is_equal "$#" 0
 then
   error "_get_first_prog() needs 1 argument.";
 fi;
 if is_empty "$1"
 then
   return "${_BAD}";
 fi;
 eval set x "$(list_from_split "$1" ',')";
 exit_test;
 shift;
 for i
 do
   _gfp_i="$i";
   if obj _gfp_i is_empty
   then
     continue;
   fi;
   if eval is_prog "$(get_first_essential ${_gfp_i})"
   then
     exit_test;
     obj _gfp_i echo1;
     eval ${_UNSET} _gfp_i;
     return "${_GOOD}";
   fi;
 done;
 eval ${_UNSET} _gfp_i;
 return "${_BAD}";
} # _get_first_prog() of main_set_mode()


#######################################################################
# main_do_fileargs ()
#
# Process filespec arguments in $_FILEARGS.
#
# Globals:
#   in: $_FILEARGS (process with `eval set x "$_FILEARGS"; shift;')
#
# Variable prefix: mdfa
#
main_do_fileargs()
{
 func_check main_do_fileargs = 0 "$@";
 special_setup;
 eval set x "${_FILEARGS}";
 shift;
 eval ${_UNSET} _FILEARGS;
 # temporary storage of all input to $_TMP_CAT
 while test "$#" -ge 2
 do
   # test for `s name' arguments, with `s' a 1-char standard section
   mdfa_filespec="$1";
   _FILESPEC_ARG="$1";
   shift;
   case "${mdfa_filespec}" in
   '')
     continue;
     ;;
   '-')
     special_filespec;
     if obj _OPT_APROPOS is_yes
     then
       continue;
     fi;
     register_file '-'
     continue;
     ;;
   ?)
     if obj _OPT_APROPOS is_yes
     then
       special_filespec;
       continue;
     fi;
     if list_has_not _MAN_AUTO_SEC_LIST "${mdfa_filespec}"
     then
       special_filespec;
       do_filearg "${mdfa_filespec}"
       continue;
     fi;
     mdfa_name="$1";
     _FILESPEC_ARG="${_FILESPEC_ARG} $1";
     special_filespec;
     case "${mdfa_name}" in
     */*|man:*|*\(*\)|*."${mdfa_filespec}")
       do_filearg "${mdfa_filespec}"
       continue;
       ;;
     esac;
     shift;
     if do_filearg "man:${mdfa_name}(${mdfa_filespec})"
     then
       continue;
     else
       do_filearg "${mdfa_filespec}"
       continue;
     fi;
     ;;
   *)
     special_filespec;
     if obj _OPT_APROPOS is_yes
     then
       continue;
     fi;
     do_filearg "${mdfa_filespec}"
     continue;
     ;;
   esac;
 done;                         # end of `s name' test
 while test "$#" -gt 0
 do
   mdfa_filespec="$1";
   _FILESPEC_ARG="$1";
   shift;
   special_filespec;
   if obj _OPT_APROPOS is_yes
   then
     continue;
   fi;
   do_filearg "${mdfa_filespec}"
 done;
 obj _TMP_STDIN rm_file_with_debug;
 eval ${_UNSET} mdfa_filespec;
 eval ${_UNSET} mdfa_name;
 eval "${return_ok}";
} # main_do_fileargs()


########################################################################
# main_set_resources ()
#
# Determine options for setting X resources with $_DISPLAY_PROG.
#
# Globals: $_DISPLAY_PROG, $_OUTPUT_FILE_NAME
#
# Variable prefix: msr
#
main_set_resources()
{
 func_check main_set_resources = 0 "$@";
 # $msr_prog   viewer program
 # $msr_rl     resource list
 msr_title="$(get_first_essential \
                "${_OPT_TITLE}" "${_REGISTERED_TITLE}")";
 exit_test;
 _OUTPUT_FILE_NAME='';
 eval set x "${msr_title}";
 shift;
 until is_equal "$#" 0
 do
   msr_n="$1";
   case "${msr_n}" in
   '')
     continue;
     ;;
   ,*)
     msr_n="$(echo1 "$1" | sed -e 's/^,,*//')";
     exit_test;
     ;;
   esac
   if obj msr_n is_empty
   then
     continue;
   fi;
   if obj _OUTPUT_FILE_NAME is_not_empty
   then
     _OUTPUT_FILE_NAME="${_OUTPUT_FILE_NAME}"',';
   fi;
   _OUTPUT_FILE_NAME="${_OUTPUT_FILE_NAME}${msr_n}";
   shift;
 done;
 case "${_OUTPUT_FILE_NAME}" in
 '')
   _OUTPUT_FILE_NAME='-';
   ;;
 ,*)
   error "main_set_resources(): ${_OUTPUT_FILE_NAME} starts with a comma.";
   ;;
 esac;
 _OUTPUT_FILE_NAME="${_TMP_DIR}/${_OUTPUT_FILE_NAME}";

 if obj _DISPLAY_PROG is_empty
 then                          # for example, for groff mode
   _DISPLAY_ARGS='';
   eval ${_UNSET} msr_n;
   eval ${_UNSET} msr_prog;
   eval ${_UNSET} msr_rl;
   eval ${_UNSET} msr_title;
   eval "${return_ok}";
 fi;

 eval set x "${_DISPLAY_PROG}";
 shift;
 msr_prog="$(base_name "$1")";
 exit_test;
 shift;
 if test $# != 0
 then
   if obj _DISPLAY_PROG is_empty
   then
     _DISPLAY_ARGS="$*";
   else
     _DISPLAY_ARGS="$* ${_DISPLAY_ARGS}";
   fi;
 fi;
 msr_rl='';
 if obj _OPT_BD is_not_empty
 then
   case "${msr_prog}" in
   ghostview|gv|gxditview|xditview|xdvi)
     list_append msr_rl '-bd' "${_OPT_BD}";
     ;;
   esac;
 fi;
 if obj _OPT_BG is_not_empty
 then
   case "${msr_prog}" in
   ghostview|gv|gxditview|xditview|xdvi)
     list_append msr_rl '-bg' "${_OPT_BG}";
     ;;
   kghostview)
     list_append msr_rl '--bg' "${_OPT_BG}";
     ;;
   xpdf)
     list_append msr_rl '-papercolor' "${_OPT_BG}";
     ;;
   esac;
 fi;
 if obj _OPT_BW is_not_empty
 then
   case "${msr_prog}" in
   ghostview|gv|gxditview|xditview|xdvi)
     _list_append msr_rl '-bw' "${_OPT_BW}";
     ;;
   esac;
 fi;
 if obj _OPT_FG is_not_empty
 then
   case "${msr_prog}" in
   ghostview|gv|gxditview|xditview|xdvi)
     list_append msr_rl '-fg' "${_OPT_FG}";
     ;;
   kghostview)
     list_append msr_rl '--fg' "${_OPT_FG}";
     ;;
   esac;
 fi;
 if is_not_empty "${_OPT_FN}"
 then
   case "${msr_prog}" in
   ghostview|gv|gxditview|xditview|xdvi)
     list_append msr_rl '-fn' "${_OPT_FN}";
     ;;
   kghostview)
     list_append msr_rl '--fn' "${_OPT_FN}";
     ;;
   esac;
 fi;
 if is_not_empty "${_OPT_GEOMETRY}"
 then
   case "${msr_prog}" in
   ghostview|gv|gxditview|xditview|xdvi|xpdf)
     list_append msr_rl '-geometry' "${_OPT_GEOMETRY}";
     ;;
   kghostview)
     list_append msr_rl '--geometry' "${_OPT_GEOMETRY}";
     ;;
   esac;
 fi;
 if is_empty "${_OPT_RESOLUTION}"
 then
   _OPT_RESOLUTION="${_DEFAULT_RESOLUTION}";
   case "${msr_prog}" in
   gxditview|xditview)
     list_append msr_rl '-resolution' "${_DEFAULT_RESOLUTION}";
     ;;
   xpdf)
     case "${_DEFAULT_RESOLUTION}" in
     75)
       # 72dpi is '100'
       list_append msr_rl '-z' '104';
       ;;
     100)
       list_append msr_rl '-z' '139';
       ;;
     esac;
     ;;
   esac;
 else
   case "${msr_prog}" in
   ghostview|gv|gxditview|xditview|xdvi)
     list_append msr_rl '-resolution' "${_OPT_RESOLUTION}";
     ;;
   xpdf)
     case "${_OPT_RESOLUTION}" in
     75)
       list_append msr_rl '-z' '104';
       # '100' corresponds to 72dpi
       ;;
     100)
       list_append msr_rl '-z' '139';
       ;;
     esac;
     ;;
   esac;
 fi;
 if is_yes "${_OPT_ICONIC}"
 then
   case "${msr_prog}" in
   ghostview|gv|gxditview|xditview|xdvi)
     list_append msr_rl '-iconic';
     ;;
   esac;
 fi;
 if is_yes "${_OPT_RV}"
 then
   case "${msr_prog}" in
   ghostview|gv|gxditview|xditview|xdvi)
     list_append msr_rl '-rv';
     ;;
   esac;
 fi;
 if is_not_empty "${_OPT_XRM}"
 then
   case "${msr_prog}" in
   ghostview|gv|gxditview|xditview|xdvi|xpdf)
     eval set x "${_OPT_XRM}";
     shift;
     for i
     do
       list_append msr_rl '-xrm' "$i";
     done;
     ;;
   esac;
 fi;
 if is_not_empty "${msr_title}"
 then
   case "${msr_prog}" in
   gxditview|xditview)
     list_append msr_rl '-title' "${msr_title}";
     ;;
   esac;
 fi;
 _DISPLAY_ARGS="${msr_rl}";
 eval ${_UNSET} msr_n;
 eval ${_UNSET} msr_prog;
 eval ${_UNSET} msr_rl;
 eval ${_UNSET} msr_title;
 eval "${return_ok}";
} # main_set_resources


########################################################################
# main_display ()
#
# Do the actual display of the whole thing.
#
# Globals:
#   in: $_DISPLAY_MODE, $_OPT_DEVICE,
#       $_ADDOPTS_GROFF, $_ADDOPTS_POST, $_ADDOPTS_X,
#       $_TMP_CAT, $_OPT_PAGER, $PAGER, $_MANOPT_PAGER,
#       $_OUTPUT_FILE_NAME
#
# Variable prefix: md
#
main_display()
{
 func_check main_display = 0 "$@";

 export md_addopts;
 export md_groggy;
 export md_modefile;

 if obj _TMP_CAT is_non_empty_file
 then
   md_modefile="${_OUTPUT_FILE_NAME}";
 else
   echo2 'groffer: empty input.';
   clean_up;
   eval ${_UNSET} md_modefile;
   eval "${return_ok}";
 fi;

 # go to the temporary directory to be able to access internal data files
 cd "${_TMP_DIR}" >"${_NULL_DEV}" 2>&1;

 case "${_DISPLAY_MODE}" in
 groff)
   _ADDOPTS_GROFF="${_ADDOPTS_GROFF} ${_ADDOPTS_POST}";
   if obj _OPT_DEVICE is_not_empty
   then
     _ADDOPTS_GROFF="${_ADDOPTS_GROFF} -T${_OPT_DEVICE}";
   fi;
   md_groggy="$(tmp_cat | eval grog "${md_options}")";
   exit_test;
   _do_opt_V;

   obj md_modefile rm_file;
   mv "${_TMP_CAT}" "${md_modefile}";
   trap_unset;
   cat "${md_modefile}" | \
   {
     trap_set;
     eval "${md_groggy}" "${_ADDOPTS_GROFF}";
   } &
   ;;
 text|tty)
   case "${_OPT_DEVICE}" in
   '')
     md_device="$(get_first_essential \
                    "${_OPT_TEXT_DEVICE}" "${_DEFAULT_TTY_DEVICE}")";
     exit_test;
     ;;
   ascii|cp1047|latin1|utf8)
     md_device="${_OPT_DEVICE}";
     ;;
   *)
     warning "main_display(): \
wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
     ;;
   esac;
   md_addopts="${_ADDOPTS_GROFF} ${_ADDOPTS_POST}";
   md_groggy="$(tmp_cat | grog -T${md_device})";
   exit_test;
   if obj _DISPLAY_MODE is_equal 'text'
   then
     _do_opt_V;
     tmp_cat | eval "${md_groggy}" "${md_addopts}";
   else
     md_pager='';
     for p in "${_OPT_PAGER}" "${PAGER}" "${_MANOPT_PAGER}" \
              'less -r -R' 'more' 'pager' 'cat'
     do
       md_p="$p";
       if eval is_prog ${md_p}
       then                  # no "" for is_prog() allows args for $p
         md_pager="${md_p}";
         break;
       fi;
     done;
     if obj md_pager is_empty
     then
       error 'main_display(): no pager program found for tty mode';
     fi;
     _do_opt_V;
     tmp_cat | eval "${md_groggy}" "${md_addopts}" | \
               eval "${md_pager}";
   fi;
   clean_up;
   ;;
 source)
   tmp_cat;
   clean_up;
   ;;

 #### viewer modes

 dvi)
   case "${_OPT_DEVICE}" in
   ''|dvi) do_nothing; ;;
   *)
     warning "main_display(): \
wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}"
     ;;
   esac;
   md_modefile="${md_modefile}".dvi;
   md_groggy="$(tmp_cat | grog -Tdvi)";
   exit_test;
   _do_display;
   ;;
 html)
   case "${_OPT_DEVICE}" in
   ''|html) do_nothing; ;;
   *)
     warning "main_display(): \
wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
     ;;
   esac;
   md_modefile="${md_modefile}".html;
   md_groggy="$(tmp_cat | grog -Thtml)";
   exit_test;
   _do_display;
   ;;
 pdf)
   case "${_OPT_DEVICE}" in
   ''|ps)
     do_nothing;
     ;;
   *)
     warning "main_display(): \
wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
     ;;
   esac;
   md_groggy="$(tmp_cat | grog -Tps)";
   exit_test;
   _do_display _make_pdf;
   ;;
 ps)
   case "${_OPT_DEVICE}" in
   ''|ps)
     do_nothing;
     ;;
   *)
     warning "main_display(): \
wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
     ;;
   esac;
   md_modefile="${md_modefile}".ps;
   md_groggy="$(tmp_cat | grog -Tps)";
   exit_test;
   _do_display;
   ;;
 x)
   case "${_OPT_DEVICE}" in
   X*)
     md_device="${_OPT_DEVICE}"
     ;;
   *)
     case "${_OPT_RESOLUTION}" in
     100)
       md_device='X100';
       if obj _OPT_GEOMETRY is_empty
       then
         case "${_DISPLAY_PROG}" in
         gxditview|xditview)
           # add width of 800dpi for resolution of 100dpi to the args
           list_append _DISPLAY_ARGS '-geometry' '800';
           ;;
         esac;
       fi;
       ;;
     *)
       md_device='X75-12';
       ;;
     esac
   esac;
   md_groggy="$(tmp_cat | grog -T${md_device} -Z)";
   exit_test;
   _do_display;
   ;;
 X)
   case "${_OPT_DEVICE}" in
   '')
     md_groggy="$(tmp_cat | grog -X)";
     exit_test;
     ;;
   X*|dvi|html|lbp|lj4|ps)
     # these devices work with
     md_groggy="$(tmp_cat | grog -T"${_OPT_DEVICE}" -X)";
     exit_test;
     ;;
   *)
     warning "main_display(): \
wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}";
     md_groggy="$(tmp_cat | grog -Z)";
     exit_test;
     ;;
   esac;
   _do_display;
   ;;
 *)
   error "main_display(): unknown mode \`${_DISPLAY_MODE}'";
   ;;
 esac;
 eval ${_UNSET} md_addopts;
 eval ${_UNSET} md_device;
 eval ${_UNSET} md_groggy;
 eval ${_UNSET} md_modefile;
 eval ${_UNSET} md_options;
 eval ${_UNSET} md_p;
 eval ${_UNSET} md_pager;
 eval "${return_ok}";
} # main_display()


########################
# _do_display ([<prog>])
#
# Perform the generation of the output and view the result.  If an
# argument is given interpret it as a function name that is called in
# the midst (actually only for `pdf').
#
# Globals: $md_modefile, $md_groggy (from main_display())
#
_do_display()
{
 func_check _do_display '>=' 0 "$@";
 _do_opt_V;
 if obj _DISPLAY_PROG is_empty
 then
   trap_unset;
   {
     trap_set;
     eval "${md_groggy}" "${_ADDOPTS_GROFF}" "${_TMP_CAT}";
   } &
 else
   obj md_modefile rm_file;
   cat "${_TMP_CAT}" | \
     eval "${md_groggy}" "${_ADDOPTS_GROFF}" > "${md_modefile}";
   if is_not_empty "$1"
   then
     eval "$1";
   fi;
   obj _TMP_CAT rm_file_with_debug;
   if obj _VIEWER_TERMINAL is_yes # for programs that run on tty
   then
     eval "${_DISPLAY_PROG}" ${_DISPLAY_ARGS} "\"${md_modefile}\"";
   else
     case "${_DISPLAY_PROG}" in
#      lynx\ *|less\ *|more\ *) # programs known to run on the terminal
#        eval "${_DISPLAY_PROG}" ${_DISPLAY_ARGS} "\"${md_modefile}\"";
#        ;;
     *)
       trap_unset;
       {
         trap_set;
         eval "${_DISPLAY_PROG}" ${_DISPLAY_ARGS} "\"${md_modefile}\"";
       } &
       ;;
     esac;
   fi;
 fi;
 eval "${return_ok}";
} # _do_display() of main_display()


#############
# _do_opt_V ()
#
# Check on option `-V'; if set print the corresponding output and leave.
#
# Globals: $_ALL_PARAMS, $_ADDOPTS_GROFF, $_DISPLAY_MODE, $_DISPLAY_PROG,
#          $_DISPLAY_ARGS, $md_groggy,  $md_modefile
#
# Variable prefix: _doV
#
_do_opt_V()
{
 func_check _do_opt_V '=' 0 "$@";
 if obj _OPT_V is_yes
 then
   _OPT_V='no';
   echo1 "Parameters:     ${_ALL_PARAMS}";
   echo1 "Display mode:   ${_DISPLAY_MODE}";
   echo1 "Output file:    ${md_modefile}";
   echo1 "Display prog:   ${_DISPLAY_PROG} ${_DISPLAY_ARGS}";
   a="$(eval echo1 "'${_ADDOPTS_GROFF}'")";
   exit_test;
   echo1 "Output of grog: ${md_groggy} $a";
   _doV_res="$(eval "${md_groggy}" "${_ADDOPTS_GROFF}")";
   exit_test;
   echo1 "groff -V:       ${_doV_res}"
   leave;
 fi;
 eval "${return_ok}";
} # _do_opt_V() of main_display()


##############
# _make_pdf ()
#
# Transform to pdf format; for pdf mode in _do_display().
#
# Globals: $md_modefile (from main_display())
#
# Variable prefix: _mp
#
_make_pdf()
{
 func_check _do_display '=' 0 "$@";
 _mp_psfile="${md_modefile}";
 md_modefile="${md_modefile}.pdf";
 obj md_modefile rm_file;
 if gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite \
       -sOutputFile="${md_modefile}" -c save pop -f "${_mp_psfile}"
 then
   :;
 else
   error '_make_pdf: could not transform into pdf format.';
 fi;
 obj _mp_psfile rm_file_with_debug;
 eval ${_UNSET} _mp_psfile;
 eval "${return_ok}";
} # _make_pdf() of main_display()


########################################################################
# main (<command_line_args>*)
#
# The main function for groffer.
#
# Arguments:
#
main()
{
 func_check main '>=' 0 "$@";
 # Do not change the sequence of the following functions!
 landmark '13: main_init()';
 main_init;
 landmark '14: main_parse_MANOPT()';
 main_parse_MANOPT;
 landmark '15: main_parse_args()';
 main_parse_args "$@";
 landmark '16: main_set_mode()';
 main_set_mode;
 landmark '17: main_do_fileargs()';
 main_do_fileargs;
 landmark '18: main_set_resources()';
 main_set_resources;
 landmark '19: main_display()';
 main_display;
 eval "${return_ok}";
}


########################################################################

main "$@";