#!/bin/sh # groffer - display groff files # Source file position: /contrib/groffer/groffer.sh # Copyright (C) 2001,2002,2003,2004 Free Software Foundation, Inc. # Written by Bernd Warken # This file is part of groff version @VERSION@. # 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. _PROGRAM_NAME='groffer'; _PROGRAM_VERSION='0.9.7'; _LAST_UPDATE='30 Apr 2004'; ######################################################################## # Determine the shell under which to run this script; # if `ash' is available restart the script using `ash'; # otherwise just go on. if test "${_groffer_run}" = ''; then # only reached during the first run of the script export _PROGRAM_NAME; export _PROGRAM_VERSION; export _LAST_UPDATE; export GROFFER_OPT; # option environment for groffer export _GROFFER_SH; # file name of this shell script export _OUTPUT_FILE_NAME; # output generated, see main_set_res..() export _groffer_run; # counter for the runs of groffer _groffer_run='first'; case "$0" in *${_PROGRAM_NAME}*) _GROFFER_SH="$0"; # was: _GROFFER_SH="@BINDIR@/${_PROGRAM_NAME}"; ;; *) echo "The ${_PROGRAM_NAME} script should be started directly." >&2 exit 1; ;; esac; ########################### # _get_opt_shell ("$@") # # Determine whether `--shell' was specified in $GROFF_OPT or in $*; # if so echo its argument. # _get_opt_shell() { local i; local _sh; case " ${GROFFER_OPT} $*" in *\ --shell\ *|*\ --shell=*) ( eval set -- "${GROFFER_OPT}" '"$@"'; _sh=''; for i in "$@"; do case "$1" in --shell) if test "$#" -ge 2; then _sh="$2"; shift; fi; ;; --shell=?*) # delete up to first `=' character _sh="$(echo -n "$1" | sed -e 's/^[^=]*=//')"; ;; esac; shift; done; echo -n "${_sh}"; ) ;; esac; } ########################### # _test_on_shell () # # Test whether is a shell program of Bourne type (POSIX sh). # _test_on_shell() { if test "$#" -le 0 || test "$1" = ''; then return 1; fi; # do not quote $1 to allow arguments test "$($1 -c 's=ok; echo -n "$s"' 2>/dev/null)" = 'ok'; } # do the shell determination _shell="$(_get_opt_shell "$@")"; if test "${_shell}" = ''; then _shell='ash'; fi; if _test_on_shell "${_shell}"; then _groffer_run='second'; # do not quote $_shell to allow arguments exec ${_shell} "${_GROFFER_SH}" "$@"; exit; fi; # clean-up of shell determination unset _shell; unset _GROFFER_SH; unset _groffer_run; _get_opt_shell() { return 0; } _test_on_shell() { return 0; } fi; # end of first run if test "${_groffer_run}" != 'second'; then echo "$_groffer_run should be 'second' here." >&2 exit 1 fi; unset _groffer_run ######################################################################## # diagnostic messages # export _DEBUG; _DEBUG='no'; # disable debugging information #_DEBUG='yes'; # enable debugging information export _DEBUG_LM; _DEBUG_LM='no'; # disable landmark messages #_DEBUG_LM='yes'; # enable landmark messages ######################################################################## # 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) ######################################################################## # characters export _BQUOTE; export _BSLASH; export _DQUOTE; export _NEWLINE; export _LBRACK; export _LPAR; export _RBRACK; export _RPAR; export _SPACE; export _SQUOTE; export _TAB; _BQUOTE='`'; _BSLASH='\'; _DQUOTE='"'; _NEWLINE=' '; _LBRACK='['; _LPAR='('; _RBRACK=']'; _RPAR=')'; _SPACE=' '; _SQUOTE="'"; _TAB=' '; # function return values; `0' means ok; other values are error codes export _ALL_EXIT; export _BAD; export _ERROR; export _GOOD; export _NO; export _OK; export _YES; _GOOD='0'; # return ok _BAD='1'; # return negatively, error code `1' _ERROR='7'; # for syntax errors; no `-1' in `ash' _ALL_EXIT="${_GOOD} ${_BAD} ${_ERROR}"; # all exit codes (for `trap_set') _NO="${_BAD}"; _YES="${_GOOD}"; _OK="${_GOOD}"; # quasi-functions, call with `eval' export return_ok; export return_good; export return_bad; export return_yes; export return_no; export return_error; 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}"; export _CONFFILES; _CONFFILES="/etc/groff/groffer.conf ${HOME}/.groff/groffer.conf"; 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_PS; # viewer program for ps mode export _VIEWER_HTML_X; # viewer program for html mode in X export _VIEWER_HTML_TTY; # viewer program for html mode in tty _VIEWER_DVI='xdvi,dvilx'; _VIEWER_PDF='xpdf,acroread'; _VIEWER_PS='gv,ghostview,gs_x11,gs'; _VIEWER_HTML='konqueror,mozilla,netscape,opera,amaya,arena,lynx'; _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; _MAN_AUTO_SEC="'1' '2' '3' '4' '5' '6' '7' '8' '9' 'n' 'o'" 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__[_] # # : program name GROFFER, GROFF, or CMDLINE (for all # command line options) # : LONG (long options) or SHORT (single character options) # : ARG for options with argument, NA for no argument; # without _ 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' 'debug' 'default' 'dvi' \ 'groff' 'help' 'intermediate-output' 'html' 'man' \ 'no-location' 'no-man' 'pdf' 'ps' 'rv' 'source' 'text' 'text-device' \ 'title' 'tty' 'tty-device' 'version' 'whatis' 'where' 'www' 'x' 'X'"; _OPTS_GROFFER_LONG_ARG="\ 'apropos' 'apropos-data' 'apropos-devel' 'apropos-progs' \ 'default-modes' 'dvi-viewer' 'extension' 'fg' 'fn' 'font' \ 'foreground' 'html-viewer' 'mode' 'pdf-viewer' 'ps-viewer' 'shell' \ 'tty-viewer' 'www-viewer' 'x-viewer' 'X-viewer'"; ##### 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' 'V' 'z'"; _OPTS_GROFF_SHORT_ARG="'d' 'f' 'F' 'I' 'L' 'm' 'M' 'n' 'o' 'P' 'r' \ 'w' 'W'"; _OPTS_GROFF_LONG_NA="'source'"; _OPTS_GROFF_LONG_ARG="'device' 'macro-file'"; ##### 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' 'debug' 'ditroff' 'help' \ 'local-file' 'location' 'pager' 'troff' 'update' 'version' \ 'whatis' 'where'"; _OPTS_MAN_LONG_ARG="'extension' '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' 'html' 'ignore-case' 'location-cat' \ 'match-case' 'troff' 'update' 'version' 'where-cat'"; _OPTS_MANOPT_LONG_ARG="${_OPTS_MAN_LONG_NA} \ 'config_file' 'encoding' '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 _ADDOPTS_GROFF; # Transp. options for groff (`eval'). export _ADDOPTS_POST; # Transp. options postproc (`eval'). export _ADDOPTS_X; # Transp. options X postproc (`eval'). 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 _FUNC_STACK; # Store debugging information. export _REGISTERED_TITLE; # Processed file names. # _HAS_* from availability tests export _HAS_COMPRESSION; # `yes' if compression is available export _HAS_OPTS_GNU; # `yes' if GNU `getopt' is available export _HAS_OPTS_POSIX; # `yes' if POSIX `getopts' is available # _MAN_* finally used configuration of man searching export _MAN_ALL; # search all man pages per filespec export _MAN_ENABLE; # enable search for man pages export _MAN_EXT; # extension for man pages export _MAN_FORCE; # force file parameter to be man pages export _MAN_IS_SETUP; # setup man variables only once export _MAN_LANG; # language for man pages export _MAN_LANG_DONE; # language dirs added to man path export _MAN_PATH; # search path for man pages export _MAN_SEC; # sections for man pages; sep. `:' export _MAN_SEC_DONE; # sections added to man path export _MAN_SYS; # system names for man pages; sep. `,' export _MAN_SYS; # system names added to man path # _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_APROPOS_DATA; # `apropos' for man sections 4, 5, 7 export _OPT_APROPOS_DEVEL; # `apropos' for man sections 2, 3, 9 export _OPT_APROPOS_PROGS; # `apropos' for man sections 1, 6, 8 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_DEBUG; # print debugging information on stderr. export _OPT_DEFAULT_MODES; # `,'-list of modes when no mode given. export _OPT_DEVICE; # device option. 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 one-liner man info export _OPT_XRM; # specify X resource. export _OPT_Z; # groff option -Z. # _TMP_* temporary files export _TMP_DIR; # groff directory for temporary files export _TMP_DIR_SUB; # 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 ######################################################################## # Test of rudimentary shell functionality ######################################################################## ######################################################################## # Test of `test'. # test "a" = "a" || exit 1; ######################################################################## # Test of `echo' and the `$()' construct. # echo -n '' >/dev/null || exit "${_ERROR}"; if test "$(echo -n 'te' && echo -n '' && echo -n 'st')" != "test"; then exit "${_ERROR}"; fi; ######################################################################## # Test of function definitions. # _t_e_s_t_f_u_n_c_() { return "${_OK}"; } if _t_e_s_t_f_u_n_c_ 2>/dev/null; then : else echo 'shell does not support function definitions.' >&2; exit "${_ERROR}"; fi; ######################################################################## # Preset and reset of read-write global variables ######################################################################## # For variables that can be reset by option `--default', see reset(). _FILEARGS=''; # _HAS_* from availability tests _HAS_COMPRESSION=''; _HAS_OPTS_GNU=''; _HAS_OPTS_POSIX=''; # _TMP_* temporary files _TMP_DIR=''; _TMP_DIR_SUB=''; _TMP_CAT=''; _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=''; _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_LANG_DONE='no'; _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=''; _OPT_APROPOS_DATA=''; _OPT_APROPOS_DEVEL=''; _OPT_APROPOS_PROGS=''; _OPT_BD=''; _OPT_BG=''; _OPT_BW=''; _OPT_DEBUG='no'; _OPT_DEFAULT_MODES=''; _OPT_DEVICE=''; _OPT_DISPLAY=''; _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'; } reset; ######################################################################## # Functions for error handling and debugging ######################################################################## ############## # landmark () # # Print to standard error as a debugging aid. # # Globals: $_DEBUG_LM # landmark() { if test "${_DEBUG_LM}" = 'yes'; then echo ">>> $*" >&2; fi; } landmark "1: debugging functions"; ############## # clean_up () # # Clean up at exit. # clean_up() { if test -d "${_TMP_DIR}"; then rm -f "${_TMP_DIR}"/*; rmdir "${_TMP_DIR}"; fi; } ############## # echo2 (*) # # Output to stderr. # # Arguments : arbitrary text. # echo2() { echo "$*" >&2; } ############## # echo2n (*) # # Output to stderr. # # Arguments : arbitrary text. # echo2n() { echo -n "$*" >&2; } ############# # diag (text>*) # # Output a diagnostic message to stderr # diag() { echo2 '>>>>>'"$*"; } ############# # error (*) # # Print an error message to standard error; exit with an error condition # error() { local i; local _code; _code="${_ERROR}"; case "$#" in 0) true; ;; 1) echo2 'groffer error: '"$1"; ;; 2) echo2 'groffer error: '"$1"; _code="$2"; ;; *) echo2 'groffer error: wrong number of arguments in error().'; ;; esac; if test "${_DEBUG}" = 'yes'; then func_stack_dump; fi; clean_up; kill "${_PROCESS_ID}" >/dev/null 2>&1; kill -9 "${_PROCESS_ID}" >/dev/null 2>&1; exit "${_code}"; } ############# # abort (*) # # Terminate program with error condition # abort() { error "Program aborted."; exit 1; } ############# # func_check ( "$@") # # Check number of arguments and register to _FUNC_STACK. # # Arguments: >=3 # : name of the calling function. # : a relational operator: = != < > <= >= # : number of arguments to be checked against # "$@": the arguments of the calling function. # func_check() { local _comp; local _fname; local _nargs; local _op; local _s; if test "$#" -lt 3; then error 'func_check() needs at least 3 arguments.'; fi; _fname="$1"; case "$3" in 1) _nargs="$3"; _s=''; ;; 0|[2-9]) _nargs="$3"; _s='s'; ;; *) error "func_check(): third argument must be a digit."; ;; esac; case "$2" in '='|'-eq') _op='-eq'; _comp='exactly'; ;; '>='|'-ge') _op='-ge'; _comp='at least'; ;; '<='|'-le') _op='-le'; _comp='at most'; ;; '<'|'-lt') _op='-lt'; _comp='less than'; ;; '>'|'-gt') _op='-gt'; _comp='more than'; ;; '!='|'-ne') _op='-ne'; _comp='not'; ;; *) error \ 'func_check(): second argument is not a relational operator.'; ;; esac; shift 3; if test "$#" "${_op}" "${_nargs}"; then do_nothing; else error \ "${_fname}"'() needs '"${_comp} ${_nargs}"' argument'"${_s}"'.'; fi; if test "${_DEBUG}" = 'yes'; then func_push "${_fname} $*"; fi; } ############# # 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 "${_DEBUG}" = 'yes'; then if test "$#" -ne 0; then error 'func_pop() does not have arguments.'; fi; case "${_FUNC_STACK}" in '') error 'func_pop(): stack is empty.'; ;; *!*) # split at first bang `!'. _FUNC_STACK="$(echo -n ${_FUNC_STACK} \ | sed -e 's/^[^!]*!//')"; ;; *) _FUNC_STACK=''; ;; esac; fi; } ############# # func_push () # # Store another element to stack. # # The stack elements are separated by `!'; if contains a `!' # it is removed first. # # Arguments: 1 # func_push() { local _element; if test "${_DEBUG}" = 'yes'; then if test "$#" -ne 1; then error 'func_push() needs 1 argument.'; fi; case "$1" in *'!'*) # remove all bangs `!'. _element="$(echo -n "$1" | sed -e 's/!//g')"; ;; *) _element="$1"; ;; esac; if test "${_FUNC_STACK}" = ''; then _FUNC_STACK="${_element}"; else _FUNC_STACK="${_element}!${_FUNC_STACK}"; fi; fi; } ############# # func_stack_dump () # # Print the content of the stack. Ignore the arguments. # func_stack_dump() { diag 'call stack:'; case "${_FUNC_STACK}" in *!*) _rest="${_FUNC_STACK}"; while test "${_rest}" != ''; do # get part before the first bang `!'. diag "$(echo -n "${_rest}" | sed -e 's/!.*$//')"; # delete part before and including the first bang `!'. _rest="$(echo -n "${_rest}" | sed -e 's/^[^!]*!//')"; done; ;; *) diag "${_FUNC_STACK}"; ;; esac; } ######################################################################## # System Test ######################################################################## landmark "2: system test"; # Test the availability of the system utilities used in this script. ######################################################################## # Test of `true'. # if true >/dev/null 2>&1; then true; else true() { return "${_GOOD}"; } false() { return "${_BAD}"; } fi; ######################################################################## # Test of `unset'. # _test='test'; if unset _test >/dev/null 2>&1 && test "${_test}" = ''; then true; else unset() { for v in "$@"; do eval "$v"=''; done; } fi; unset _test; ######################################################################## # Test of builtin `local' # _t_e_s_t_f_u_n_c_() { local _test >/dev/null 2>&1 || return "${_BAD}"; } if _t_e_s_t_f_u_n_c_; then : else local() { if test "$1" != ''; then error "overriding global variable \`$1' with local value."; fi; } fi; ######################################################################## # Test of global setting in functions # _global='outside'; _clobber='outside'; _t_e_s_t_f_u_n_c_() { local _clobber; _global='inside'; _clobber='inside'; } _t_e_s_t_f_u_n_c_; if test "${_global}" != 'inside' || test "${_clobber}" != 'outside'; then error "Cannot assign to global variables from within functions."; fi; unset _global; unset _clobber; ######################################################################## # Test of function `sed'. # if test "$(echo xTesTx \ | sed -e 's/^.\([Tt]e*x*sTT*\).*$/\1/' \ | sed -e '\|T|s||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>/dev/null)" = 'test'; then _HAS_COMPRESSION='yes'; if echo 'test' | bzip2 -c 2>/dev/null | bzip2 -t 2>/dev/null \ && test "$(echo 'test' | bzip2 -c 2>/dev/null \ | bzip2 -d -c 2>/dev/null)" \ = 'test'; then _HAS_BZIP='yes'; else _HAS_BZIP='no'; fi; else _HAS_COMPRESSION='no'; _HAS_BZIP='no'; fi; ######################################################################## _t_e_s_t_f_u_n_c_() { : } ######################################################################## # Definition of normal Functions ######################################################################## landmark "3: functions"; ######################################################################## # abort (*) # # Unconditionally terminate the program with error code; # useful for debugging. # # defined above ######################################################################## # apropos_run () # # apropos_run() { func_check apropos_run = 1 "$@"; if apropos apropos >/dev/null 2>/dev/null; then apropos "$1"; elif man --apropos man >/dev/null 2>/dev/null; then man --apropos "$1"; elif man -k man >/dev/null 2>/dev/null; then man -k "$1"; fi; } ######################################################################## # base_name () # # Get the file name part of , i.e. delete everything up to last # `/' from the beginning of . Remove final slashes, too, to get a # non-empty output. # # Arguments : 1 # Output : the file name part (without slashes) # base_name() { func_check base_name = 1 "$@"; local f; f="$1"; case "$f" in */) # delete all final slashes f="$(echo -n "$f" | sed -e '\|//*$|s|||')"; ;; esac; case "$f" in /|'') eval "${return_bad}"; ;; */*) # delete everything before and including the last slash `/'. echo -n "$f" | sed -e '\|^.*//*\([^/]*\)$|s||\1|'; ;; *) echo -n "$f"; ;; esac; eval "${return_ok}"; } ######################################################################## # catz () # # Decompress if possible or just print to standard output. # # gzip, bzip2, and .Z decompression is supported. # # Arguments: 1, a file name. # Output: the content of , possibly decompressed. # if test "${_HAS_COMPRESSION}" = 'yes'; then catz() { func_check catz = 1 "$@"; case "$1" in '') error 'catz(): empty file name'; ;; '-') error 'catz(): for standard input use save_stdin()'; ;; esac; if obj _HAS_BZIP is_yes; then if bzip2 -t "$1" 2>/dev/null; then bzip2 -c -d "$1" 2>/dev/null; eval "${return_ok}"; fi; fi; gzip -c -d -f "$1" 2>/dev/null; eval "${return_ok}"; } else catz() { func_check catz = 1 "$@"; cat "$1"; eval "${return_ok}"; } fi; ######################################################################## # clean_up () # # Do the final cleaning up before exiting; used by the trap calls. # # defined above ######################################################################## # diag (*) # # Print marked message to standard error; useful for debugging. # # defined above ######################################################################## landmark '4: dirname()*'; ######################################################################## ####################################################################### # dirname_append ( ) # # Append `name' to `dir' with clean handling of `/'. # # Arguments : 2 # Output : the generated new directory name / # dirname_append() { func_check dirname_append = 2 "$@"; local _res; if is_empty "$1"; then error "dir_append(): first argument is empty."; fi; if is_empty "$2"; then echo -n "$1"; else dirname_chop "$1"/"$2"; fi; eval "${return_ok}"; } ######################################################################## # dirname_chop () # # Remove unnecessary slashes from directory name. # # Argument: 1, a directory name. # Output: path without double, or trailing slashes. # dirname_chop() { func_check dirname_chop = 1 "$@"; local _arg; local _res; local _sep; # replace all multiple slashes by a single slash `/'. _res="$(echo -n "$1" | sed -e '\|///*|s||/|g')"; case "${_res}" in ?*/) # remove trailing slash '/'; echo -n "${_res}" | sed -e '\|/$|s|||'; ;; *) echo -n "${_res}"; ;; esac; eval "${return_ok}"; } ######################################################################## # do_filearg () # # Append the file, man-page, or standard input corresponding to the # argument to the temporary file. If this is compressed in the gzip # or Z format it is decompressed. A title element is generated. # # Argument either: # - name of an existing files. # - `-' to represent standard input (several times allowed). # - `man:name.(section)' the man-page for `name' in `section'. # - `man:name.section' the man-page for `name' in `section'. # - `man:name' the man-page for `name' in the lowest `section'. # - `name.section' the man-page for `name' in `section'. # - `name' the man-page for `name' in the lowest `section'. # Globals : # $_TMP_STDIN, $_MAN_ENABLE, $_MAN_IS_SETUP, $_OPT_MAN # # Output : none # Return : $_GOOD if found, ${_BAD} otherwise. # do_filearg() { func_check do_filearg = 1 "$@"; local _filespec; local i; _filespec="$1"; # store sequence into positional parameters case "${_filespec}" in '') eval "${return_good}"; ;; '-') register_file '-'; 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 in "$@"; do case "$i" in File) if test -f "${_filespec}"; then if test -r "${_filespec}"; then register_file "${_filespec}"; eval "${return_good}"; else echo2 "could not read \`${_filespec}'"; eval "${return_bad}"; fi; else continue; fi; ;; Manpage) # parse filespec as man page if obj _MAN_IS_SETUP is_not_yes; then man_setup; fi; if man_do_filespec "${_filespec}"; then eval "${return_good}"; else continue; fi; ;; esac; done; eval "${return_bad}"; } # do_filearg() ######################################################################## # do_nothing () # # Dummy function. # do_nothing() { return "${_OK}"; } ######################################################################## # echo2 (*) # # Print to standard error with final line break. # # defined above ######################################################################## # echo2n (*) # # Print to standard error without final line break. # # defined above ######################################################################## # error (*) # # Print error message and exit with error code. # # defined above ######################################################################## # func_check ( "$@") # # Check number of arguments and register to _FUNC_STACK. # # Arguments: >=3 # : name of the calling function. # : a relational operator: = != < > <= >= # : number of arguments to be checked against # "$@": the arguments of the calling function. # # defined above ######################################################################### # func_pop () # # Delete the top element from the function call stack. # # defined above ######################################################################## # func_push () # # Store another element to function call stack. # # defined above ######################################################################## # func_stack_dump () # # Print the content of the stack. # # defined above ######################################################################## # get_first_essential (*) # # Retrieve first non-empty argument. # # Return : `1' if all arguments are empty, `0' if found. # Output : the retrieved non-empty argument. # get_first_essential() { func_check get_first_essential '>=' 0 "$@"; local i; if is_equal "$#" 0; then eval "${return_ok}"; fi; for i in "$@"; do if obj i is_not_empty; then echo -n "$i"; eval "${return_ok}"; fi; done; eval "${return_bad}"; } ######################################################################## landmark '5: is_*()'; ######################################################################## ######################################################################## # is_dir () # # 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 -d "$1" && test -r "$1"; then eval "${return_yes}"; fi; eval "${return_no}"; } ######################################################################## # is_empty () # # 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 ( ) # # Test whether `string1' is equal to . # # 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_file () # # 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 test -f "$1" && test -r "$1"; then eval "${return_yes}"; fi; eval "${return_no}"; } ######################################################################## # is_non_empty_file () # # 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_empty = 1 "$@"; if is_file "$1"; then if is_not_empty "$(cat "$1" | sed -e '/./q')"; then eval "${return_yes}"; fi; fi; eval "${return_no}"; } ######################################################################## # is_not_dir () # # 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 () # # 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 ( ) # # 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 () # # Test whether `name' is a not readable file. # # Arguments : >=1 (empty allowed), more args are ignored # is_not_file() { func_check is_not_file '>=' 1 "$@"; if is_file "$1"; then eval "${return_no}"; fi; eval "${return_yes}"; } ######################################################################## # is_not_prog () # # Verify that arg is a not program in $PATH. # # Arguments : >=1 (empty allowed) # more args are ignored, this allows to specify progs with arguments # is_not_prog() { func_check is_not_prog '>=' 1 "$@"; if where "$1" >/dev/null; then eval "${return_no}"; fi; eval "${return_yes}"; } ######################################################################## # is_not_writable () # # 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_yes () # # 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 () # # Determine whether arg is a program in $PATH # # Arguments : >=0 (empty allowed) # more args are ignored, this allows to specify progs with arguments # is_prog() { func_check is_prog '>=' 0 "$@"; case "$#" in 0) eval "${return_no}"; ;; *) if where "$1" >/dev/null; then eval "${return_yes}"; fi; ;; esac eval "${return_no}"; } ######################################################################## # is_writable () # # 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 -r "$1"; then if test -w "$1"; then eval "${return_yes}"; fi; fi; eval "${return_no}"; } ######################################################################## # is_yes () # # Test whether `string' has value "yes". # # Arguments : <=1 # 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 () # # Clean exit without an error. # leave() { clean_up; exit "${_OK}"; } ######################################################################## 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 `set -- $list`. ######################################################################## # list_append ( ...) # # Arguments: >=2 # : a variable name for a list of single-quoted elements # : some sequence of characters. # Output: none, but $ is set to # if is empty: "'' '...'" # otherwise: "$list '' ..." # list_append() { func_check list_append '>=' 2 "$@"; local _element; local _list; local _name; _name="$1"; eval _list='"${'$1'}"'; shift; for s in "$@"; do case "$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' _element="$(echo -n "$s" | sed -e 's/'"${_SQUOTE}"'/&\\&&/g')"; ;; '') _element=""; ;; *) _element="$s"; ;; esac; if obj _list is_empty; then _list="'${_element}'"; else _list="${_list} '${_element}'"; fi; done; eval "${_name}"='"${_list}"'; eval "${return_ok}"; } ######################################################################## # list_from_cmdline ( [...]) # # 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: >=4 # : space-separated list of short options without an arg. # : space-separated list of short options that have an arg. # : space-separated list of long options without an arg. # : space-separated list of long options that have an arg. # ...: the arguments from a command line, such as "$@", # the content of a variable, or direct arguments. # # Globals: $POSIXLY_CORRECT (only kept for compatibility). # # Output: ['-[-]opt' ['optarg']]... '--' ['filename']... # # Example: # list_normalize 'a b' 'c' '' 'long' -a f1 -bcarg --long=larg f2 # will result in printing: # '-a' '-b' '-c' 'arg' '--long' 'larg' '--' 'f1' 'f2' # If $POSIXLY_CORRECT is not empty, the result will be: # '-a' '--' 'f1' '-bcarg' '--long=larg' 'f2' # # Rationale: # In POSIX, the first non-option ends the option processing. # In GNU mode, used by default, non-option arguments are sorted # behind the options. # # Use this function only in the following way: # eval set -- "$(args_norm '...' '...' '...' '...' "$@")"; # while test "$1" != '--'; do # case "$1" in # ... # esac; # shift; # done; # shift; #skip '--' # # all positional parameters ("$@") left are file name parameters. # list_from_cmdline() { func_check list_from_cmdline '>=' 4 "$@"; local _fparams; local _fn; local _result; local _long_a; local _long_n; local _short_a; local _short_n; _short_n="$(list_get "$1")"; # short options, no argument _short_a="$(list_get "$2")"; # short options with argument _long_n="$(list_get "$3")"; # long options, no argument _long_a="$(list_get "$4")"; # long options with argument shift 4; _fn='list_from_cmdline():'; # for error messages if is_equal "$#" 0; then echo -n "'--'"; eval "${return_ok}"; fi; _fparams=''; _result=''; while test "$#" -ge 1; do _arg="$1"; shift; case "$_arg" in --) break; ;; --?*) # delete leading '--'; _opt="$(echo -n "${_arg}" | sed -e 's/^..//')"; if list_has _long_n "${_opt}"; then # long option, no argument list_append _result "--${_opt}"; continue; fi; # test on `--opt=arg' if string_contains "${_opt}" '='; then # extract option by deleting from the first '=' to the end _lopt="$(echo -n "${_opt}" | sed -e 's/=.*$//')"; if list_has _long_a "${_lopt}"; then # get the option argument by deleting up to first `=' _optarg="$(echo -n "${_opt}" | sed -e 's/^[^=]*=//')"; list_append _result "--${_lopt}" "${_optarg}"; continue; fi; fi; if list_has _long_a "${_opt}"; then # long option with argument if test "$#" -le 0; then error "${_fn} no argument for option --${_opt}." fi; list_append _result "--${_opt}" "$1"; shift; continue; fi; error "${_fn} --${_opt} is not an option." ;; -?*) # short option (cluster) # delete leading `-'; _rest="$(echo -n "${_arg}" | sed -e 's/^-//')"; while obj _rest is_not_empty; do # get next short option from cluster (first char of $_rest) _optchar="$(echo -n "${_rest}" | sed -e 's/^\(.\).*$/\1/')"; # remove first character from ${_rest}; _rest="$(echo -n "${_rest}" | sed -e 's/^.//')"; if list_has _short_n "${_optchar}"; then list_append _result "-${_optchar}"; continue; elif list_has _short_a "${_optchar}"; then if obj _rest is_empty; then if test "$#" -ge 1; then list_append _result "-${_optchar}" "$1"; shift; continue; else error \ "${_fn}"' no argument for option -'"${_optchar}." fi; else # rest is the argument list_append _result "-${_optchar}" "${_rest}"; _rest=''; continue; fi; else error "${_fn} unknown option -${_optchar}." fi; done; ;; *) # Here, $_arg is not an option, so a file parameter. # When $POSIXLY_CORRECT is set this ends option parsing; # otherwise, the argument is stored as a file parameter and # option processing is continued. list_append _fparams "${_arg}"; if obj POSIXLY_CORRECT is_not_empty; then break; fi; ;; esac; done; list_append _result '--'; if obj _fparams is_not_empty; then _result="${_result} ${_fparams}"; fi; if test "$#" -gt 0; then list_append _result "$@"; fi; echo -n "$_result"; eval "${return_ok}"; } # list_from_cmdline() ######################################################################## # list_from_split ( ) # # In , escape all white space characters and replace each # by space. # # Arguments: 2: a that is to be split into parts divided by # # Output: the resulting list string # list_from_split() { func_check list_from_split = 2 "$@"; local _s; # precede each space or tab by a backslash `\' (doubled for `sed') _s="$(echo -n "$1" | sed -e 's/\(['"${_SPACE}${_TAB}"']\)/\\\1/g')"; # replace split character of string by the list separator ` ' (space). case "$2" in /) # cannot use normal `sed' separator echo -n "${_s}" | sed -e '\|'"$2"'|s|| |g'; ;; ?) # use normal `sed' separator echo -n "${_s}" | sed -e 's/'"$2"'/ /g'; ;; ??*) error 'list_from_split(): separator must be a single character.'; ;; esac; eval "${return_ok}"; } ######################################################################## # list_get () # # Check whether 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 unchanged, if the check succeeded. # list_get() { func_check list_get = 1 "$@"; local _list; eval _list='"${'$1'}"'; # remove leading and final space characters _list="$(echo -n "${_list}" | \ sed -e '/^['"${_SPACE}${_TAB}"']*/s///' | \ sed -e '/['"${_SPACE}${_TAB}"']*$/s///')"; case "${_list}" in '') eval "${return_ok}"; ;; \'*\') echo -n "${_list}"; eval "${return_ok}"; ;; *) error "list_get(): bad list: $1" ;; esac; eval "${return_ok}"; } ######################################################################## # list_has ( ) # # Arguments: 2 # : a variable name for a list of single-quoted elements # : some sequence of characters. # Output: # if is empty: "'' '...'" # otherwise: "list '' ..." # list_has() { func_check list_has = 2 "$@"; eval _list='"${'$1'}"'; if obj _list is_empty; then eval "${return_no}"; fi; _element="$2"; case "$2" in \'*\') _element="$2"; ;; *) _element="'$2'"; ;; esac; if string_contains "${_list}" "${_element}"; then eval "${return_yes}"; else eval "${return_no}"; fi; eval "${return_ok}"; } ######################################################################## # list_has_not ( ) # # Arguments: 2 # : a space-separated list of single-quoted elements. # : some sequence of characters. # Output: # if is empty: "'' '...'" # otherwise: " '' ..." # list_has_not() { func_check list_has_not = 2 "$@"; eval _list='"${'$1'}"'; if obj _list is_empty; then eval "${return_yes}"; fi; _element="$2"; case "$2" in \'*\') _element="$2"; ;; *) _element="'$2'"; ;; esac; if string_contains "${_list}" "${_element}"; then eval "${return_no}"; else eval "${return_yes}"; fi; eval "${return_ok}"; } ######################################################################## landmark '7: man_*()'; ######################################################################## ######################################################################## # man_do_filespec () # # Print suitable man page(s) for filespec to $_TMP_CAT. # # Arguments : 2 # : 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. # man_do_filespec() { func_check man_do_filespec = 1 "$@"; local _got_one; local _name; local _prevsec; local _res; local _section; local _spec; local _string; local s; if obj _MAN_PATH is_empty; then eval "${return_bad}"; fi; if is_empty "$1"; then eval "${return_bad}"; fi; _spec="$1"; _name=''; _section=''; case "${_spec}" in */*) # not a man spec when it contains '/' eval "${return_bad}"; ;; man:?*\(?*\)) # man:name(section) _name="$(echo -n "${_spec}" \ | sed -e 's/^man:\(..*\)(\(..*\))$/\1/')"; _section="$(echo -n "${_spec}" \ | sed -e 's/^man:\(..*\)(\(..*\))$/\2/')"; ;; man:?*.[0-9on]) # man:name.section _name="$(echo -n "${_spec}" \ | sed -e 's/^man:\(..*\)\..$/\1/')"; _section="$(echo -n "${_spec}" \ | sed -e 's/^.*\(.\)$/\1/')"; ;; man:?*) # man:name _name="$(echo -n "${_spec}" | sed -e 's/^man://')"; ;; ?*\(?*\)) # name(section) _name="$(echo -n "${_spec}" \ | sed -e 's/^\(..*\)(\(..*\))$/\1/')"; _section="$(echo -n "${_spec}" \ | sed -e 's/^\(..*\)(\(..*\))$/\2/')"; ;; ?*.[0-9on]) # name.section _name="$(echo -n "${_spec}" \ | sed -e 's/^\(..*\)\..$/\1/')"; _section="$(echo -n "${_spec}" \ | sed -e 's/^.*\(.\)$/\1/')"; ;; ?*) _name="${_filespec}"; ;; esac; if obj _name is_empty; then eval "${return_bad}"; fi; _got_one='no'; if obj _section is_empty; then eval set -- "${_MAN_AUTO_SEC}"; for s in "$@"; do if man_search_section "${_name}" "$s"; then # found if obj _MAN_ALL is_yes; then _got_one='yes'; else eval "${return_good}"; fi; fi; done; else if man_search_section "${_name}" "${_section}"; then eval "${return_good}"; else eval "${return_bad}"; fi; fi; if obj _MAN_ALL is_yes && is_yes "${_got_one}"; then eval "${return_good}"; fi; eval "${return_bad}"; } # man_do_filespec() ######################################################################## # man_register_file ( [
]) # # 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 (
) # # Retrieve man pages. # # Arguments : 2 # Globals : $_MAN_PATH, $_MAN_EXT # Return : 0 if found, 1 otherwise # man_search_section() { func_check man_search_section = 2 "$@"; local _dir; local _ext; local _got_one; local _name; local _prefix local _section; local d; local f; 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; _name="$1"; _section="$2"; eval set -- "$(path_split "${_MAN_PATH}")"; _got_one='no'; if obj _MAN_EXT is_empty; then for d in "$@"; do _dir="$(dirname_append "$d" "man${_section}")"; if obj _dir is_dir; then _prefix="$(dirname_append "${_dir}" "${_name}.${_section}")"; for f in $(echo -n ${_prefix}*); do if obj f is_file; then if is_yes "${_got_one}"; then register_file "$f"; elif obj _MAN_ALL is_yes; then man_register_file "$f" "${_name}"; else man_register_file "$f" "${_name}" "${_section}"; eval "${return_good}"; fi; _got_one='yes'; fi; done; fi; done; else _ext="${_MAN_EXT}"; # check for directory name having trailing extension for d in "$@"; do _dir="$(dirname_append $d man${_section}${_ext})"; if obj _dir is_dir; then _prefix="$(dirname_append "${_dir}" "${_name}.${_section}")"; for f in ${_prefix}*; do if obj f is_file; then if is_yes "${_got_one}"; then register_file "$f"; elif obj _MAN_ALL is_yes; then man_register_file "$f" "${_name}"; else man_register_file "$f" "${_name}" "${_section}"; eval "${return_good}"; fi; _got_one='yes'; fi; done; fi; done; # check for files with extension in directories without extension for d in "$@"; do _dir="$(dirname_append "$d" "man${_section}")"; if obj _dir is_dir; then _prefix="$(dirname_append "${_dir}" \ "${_name}.${_section}${_ext}")"; for f in ${_prefix}*; do if obj f is_file; then if is_yes "${_got_one}"; then register_file "$f"; elif obj _MAN_ALL is_yes; then man_register_file "$f" "${_name}"; else man_register_file "$f" "${_name}" "${_section}"; eval "${return_good}"; fi; _got_one='yes'; fi; done; fi; done; fi; if obj _MAN_ALL is_yes && is_yes "${_got_one}"; then eval "${return_good}"; fi; 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. # man_setup() { func_check main_man_setup = 0 "$@"; local _lang; 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}")"; if obj _MAN_PATH is_empty; then manpath_set_from_path; else _MAN_PATH="$(path_clean "${_MAN_PATH}")"; fi; if obj _MAN_PATH is_empty; then if is_prog 'manpath'; then _MAN_PATH="$(manpath 2>/dev/null)"; # not always available 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}")"; if obj _MAN_ALL is_empty; then _MAN_ALL='no'; fi; _MAN_SYS="$(get_first_essential \ "${_OPT_SYSTEMS}" "${_MANOPT_SYS}" "${SYSTEM}")"; _lang="$(get_first_essential \ "${_OPT_LANG}" "${LC_ALL}" "${LC_MESSAGES}" "${LANG}")"; case "${_lang}" in C|POSIX) _MAN_LANG=""; _MAN_LANG2=""; ;; ?) _MAN_LANG="${_lang}"; _MAN_LANG2=""; ;; *) _MAN_LANG="${_lang}"; # get first two characters of $_lang _MAN_LANG2="$(echo -n "${_lang}" | sed -e 's/^\(..\).*$/\1/')"; ;; esac; # from now on, use only $_LANG, forget about $_OPT_LANG, $LC_*. manpath_add_lang_sys; # this is very slow _MAN_SEC="$(get_first_essential \ "${_OPT_SECT}" "${_MANOPT_SEC}" "${MANSEC}")"; if obj _MAN_PATH is_empty; then _MAN_ENABLE="no"; eval "${return_ok}"; fi; _MAN_EXT="$(get_first_essential \ "${_OPT_EXTENSION}" "${_MANOPT_EXTENSION}")"; 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. # manpath_add_lang_sys() { func_check manpath_add_lang_sys = 0 "$@"; local p; local _mp; if obj _MAN_PATH is_empty; then eval "${return_ok}"; fi; # twice test both sys and lang eval set -- "$(path_split "${_MAN_PATH}")"; _mp=''; for p in "$@"; do # loop on man path directories _mp="$(_manpath_add_lang_sys_single "${_mp}" "$p")"; done; eval set -- "$(path_split "${_mp}")"; for p in "$@"; do # loop on man path directories _mp="$(_manpath_add_lang_sys_single "${_mp}" "$p")"; done; _MAN_PATH="$(path_chop "${_mp}")"; eval "${return_ok}"; } _manpath_add_lang_sys_single() { # To the directory in $1 append existing sys/lang subdirectories # Function is necessary to split the OS list. # # globals: in: $_MAN_SYS, $_MAN_LANG, $_MAN_LANG2 # argument: 2: `man_path' and `dir' # output: colon-separated path of the retrieved subdirectories # func_check _manpath_add_lang_sys_single = 2 "$@"; local d; _res="$1"; _parent="$2"; eval set -- "$(list_from_split "${_MAN_SYS}" ',')"; for d in "$@" "${_MAN_LANG}" "${_MAN_LANG2}"; do _dir="$(dirname_append "${_parent}" "$d")"; if obj _res path_not_contains "${_dir}" && obj _dir is_dir; then _res="${_res}:${_dir}"; fi; done; if path_not_contains "${_res}" "${_parent}"; then _res="${_res}:${_parent}"; fi; path_chop "${_res}"; } # 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 # manpath_set_from_path() { func_check manpath_set_from_path = 0 "$@"; local _base; local _mandir; local _manpath; local d; local e; _manpath=''; # get a basic man path from $PATH if obj PATH is_not_empty; then eval set -- "$(path_split "${PATH}")"; for d in "$@"; do # delete the final `/bin' part _base="$(echo -n "$d" | sed -e '\|//*bin/*$|s|||')"; for e in /share/man /man; do _mandir="${_base}$e"; if test -d "${_mandir}" && test -r "${_mandir}"; then _manpath="${_manpath}:${_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 if obj _manpath path_not_contains "$d" && obj d is_dir; then _manpath="${_manpath}:$d"; fi; done; _MAN_PATH="${_manpath}"; eval "${return_ok}"; } # manpath_set_from_path() ######################################################################## landmark '9: obj_*()'; ######################################################################## ######################################################################## # obj ( ...) # # This works like a method (object function) call for an object. # Run " $ ...". # # The first argument represents an object whose data is given as first # argument to (). # # Argument: >=2 # : variable name # : a program or function name # obj() { func_check obj '>=' 2 "$@"; local func; local var; if is_empty "$2"; then error "obj(): function name is empty." else func="$2"; fi; eval arg1='"${'$1'}"'; shift; shift; eval "${func}"' "${arg1}" "$@"'; } ######################################################################## # obj_data () # # Print the data of , i.e. the content of $. # For possible later extensions. # # Arguments: 1 # : a variable name # Output: the data of # obj_data() { func_check obj '=' 1 "$@"; if is_empty "$1"; then error "obj_data(): object name is empty." fi; eval echo -n '"${'$1'}"'; } ######################################################################## # obj_from_output ( ...) # # Run '$="$( ...)"' to set the result of a # function call to a global variable. # # Arguments: >=2 # : a variable name # : the name of a function or program # : optional argument to # Output: none # obj_from_output() { func_check obj_from_output '>=' 2 "$@"; local result_name; if is_empty "$1"; then error "res(): variable name is empty."; elif is_empty "$2"; then error "res(): function name is empty." else result_name="$1"; fi; shift; eval "${result_name}"'="$('"$@"')"'; } ######################################################################## # obj_set ( ) # # Set the data of , i.e. call "$=". # # Arguments: 2 # : a variable name # : 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"'; } ######################################################################## # path_chop () # # 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 "$@"; local _res; # replace multiple colons by a single colon `:' # remove leading and trailing colons echo -n "$1" | sed -e 's/:::*/:/g' | sed -e 's/^:*//' | sed -e 's/:*$//'; eval "${return_ok}"; } ######################################################################## # path_clean () # # Remove non-existing directories from a colon-separated list. # # Argument: 1, a colon separated path. # Output: colon-separated list of existing directories. # path_clean() { func_check path_clean = 1 "$@"; local _arg; local _dir; local _res; local i; if is_not_equal "$#" 1; then error 'path_clean() needs 1 argument.'; fi; _arg="$1"; eval set -- "$(path_split "${_arg}")"; _res=""; for i in "$@"; do if obj i is_not_empty \ && obj _res path_not_contains "$i" \ && obj i is_dir; then case "$i" in ?*/) _res="${_res}$(dirname_chop "$i")"; ;; *) _res="${_res}:$i"; esac; fi; done; if path_chop "${_res}"; then eval "${return_ok}"; else eval "${return_badk}"; fi; } ######################################################################## # path_contains ( ) #- # 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 ( ) #- # 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 () # # 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 () # # 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 '-'; else to_tmp "$1"; register_title "$(base_name "$1")"; fi; eval "${return_ok}"; } ######################################################################## # register_title () # # Create title element from and append to $_REGISTERED_TITLE # # Globals: $_REGISTERED_TITLE (rw) # register_title() { func_check register_title = 1 "$@"; local _title; if is_empty "$1"; then eval "${return_ok}"; fi; _title="$(base_name "$1")"; # remove directory part # remove extension `.gz' _title="$(echo -n "${_title}" | sed -e 's/\.gz$//')"; # remove extension `.Z' _title="$(echo -n "${_title}" | sed -e 's/\.Z$//')"; if obj _title is_empty; then eval "${return_ok}"; fi; _REGISTERED_TITLE="${_REGISTERED_TITLE} ${_title}"; eval "${return_ok}"; } ######################################################################## # reset () # # Reset the variables that can be affected by options to their default. # # # Defined in section `Preset' after the rudimentary shell tests. ######################################################################## # save_stdin () # # Store standard input to temporary file (with decompression). # if obj _HAS_COMPRESSION is_yes; then save_stdin() { local _f; func_check save_stdin = 0 "$@"; _f="${_TMP_DIR}"/INPUT; cat >"${_f}"; catz "${_f}" >"${_TMP_STDIN}"; rm -f "${_f}"; eval "${return_ok}"; } else save_stdin() { func_check save_stdin = 0 "$@"; cat >"${_TMP_STDIN}"; eval "${return_ok}"; } fi; ######################################################################## landmark '11: stack_*()'; ######################################################################## ######################################################################## # string_contains ( ) # # 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 ( ) # # 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() { cat "${_TMP_CAT}"; } ######################################################################## # tmp_create (?) # # create temporary file # # It's safe to use the shell process ID together with a suffix to # have multiple temporary files. # # Output : name of created file # tmp_create() { func_check tmp_create '<=' 1 "$@"; local _tmp; # the output file does not have `,' as first character _tmp="${_TMP_DIR}/,$1"; echo -n >"${_tmp}"; echo -n "${_tmp}"; # output file name eval "${return_ok}"; } ######################################################################## # to_tmp () # # print file (decompressed) to the temporary cat file # to_tmp() { func_check to_tmp = 1 "$@"; if is_file "$1"; then if obj _OPT_LOCATION is_yes; then echo2 "$1"; fi; if obj _OPT_WHATIS is_yes; then what_is "$1" >>"${_TMP_CAT}"; else catz "$1" >>"${_TMP_CAT}"; fi; else error "to_tmp(): could not read file \`$1'."; fi; eval "${return_ok}"; } ######################################################################## # trap_clean () # # disable trap on all exit codes ($_ALL_EXIT) # # Arguments: 0 # Globals: $_ALL_EXIT # trap_clean() { func_check trap_clean = 0 "$@"; local i; for i in ${_ALL_EXIT}; do trap "" "$i" 2>/dev/null || true; done; eval "${return_ok}"; } ######################################################################## # trap_set () # # call function on all exit codes ($_ALL_EXIT) # # Arguments: 1 (name of a shell function) # Globals: $_ALL_EXIT # trap_set() { func_check trap_set = 1 "$@"; local i; for i in ${_ALL_EXIT}; do trap "$1" "$i" 2>/dev/null || true; done; eval "${return_ok}"; } ######################################################################## # usage () # # print usage information to stderr; for groffer option --help. # usage() { func_check usage = 0 "$@"; echo; version; echo 'Usage: '"${_PROGRAM_NAME}"' [option]... [filespec]...'; cat <&1 | sed -e '/^ *$/q' | sed -e '1s/^/is part of /' >&2; } ######################################################################## # warning () # # Print warning to stderr # warning() { echo2 "warning: $*"; } ######################################################################## # what_is () # # Interpret as a man page and display its `whatis' # information as a fragment written in the groff language. # what_is() { func_check what_is = 1 "$@"; local _res; local _dot; if is_not_file "$1"; then error "what_is(): argument is not a readable file." fi; _dot='^\.['"${_SPACE}${_TAB}"']*'; echo '.br'; echo "$1: "; echo '.br'; echo -n ' '; # grep the line containing `.TH' macro, if any _res="$(catz "$1" | sed -e '/'"${_dot}"'TH /p d')"; if obj _res is_not_empty; then # traditional man style # get the text between the first and the second `.SH' macro, by # - delete up to first .SH; # - of this, print everything up to next .SH, and delete the rest; # - of this, delete the final .SH line; catz "$1" | sed -e '1,/'"${_dot}"'SH/d' \ | sed -e '1,/'"${_dot}"'SH/p d' \ | sed -e '/'"${_dot}"'SH/d'; eval "${return_ok}"; fi; # grep the line containing `.Dd' macro, if any _res="$(catz "$1" | sed -e '/'"${_dot}"'Dd /p d')"; if obj _res is_not_empty; then # BSD doc style # get the text between the first and the second `.Nd' macro, by # - delete up to first .Nd; # - of this, print everything up to next .Nd, and delete the rest; # - of this, delete the final .Nd line; catz "$1" | sed -e '1,/'"${_dot}"'Nd/d' \ | sed -e '1,/'"${_dot}"'Nd/p d' \ | sed -e '/'"${_dot}"'Nd/d'; eval "${return_ok}"; fi; echo 'is not a man page.'; eval "${return_bad}"; } ######################################################################## # where () # # 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. # where() { func_check where '>=' 1 "$@"; local _file; local _arg; local p; _arg="$1"; if obj _arg is_empty; then eval "${return_bad}"; fi; case "${_arg}" in /*) if test -f "${_arg}" && test -x "${_arg}"; then eval "${return_ok}"; else eval "${return_bad}"; fi; ;; esac; eval set -- "$(path_split "${PATH}")"; for p in "$@"; do case "$p" in */) _file=${p}${_arg}; ;; *) _file=${p}/${_arg}; ;; esac; if test -f "${_file}" && test -x "${_file}"; then echo -n "${_file}"; eval "${return_ok}"; fi; done; eval "${return_bad}"; } ######################################################################## # main ######################################################################## # The main area contains the following parts: # - main_init(): initialize temporary files and set exit trap # - parse $MANOPT # - main_parse_args(): argument parsing # - determine display mode # - process filespec arguments # - setup X resources # - do the displaying # These parts are implemented as functions, being defined below in the # sequence they are called in the main() function. ####################################################################### # main_init () # # set exit trap and create temporary files # # Globals: $_TMP_CAT, $_TMP_STDIN # landmark '13: main_init()'; main_init() { func_check main_init = 0 "$@"; # call clean_up() on any signal trap_set clean_up; # determine temporary directory umask 000; _TMP_DIR=''; for d in "${GROFF_TMPDIR}" "${TMPDIR}" "${TMP}" "${TEMP}" \ "${TEMPDIR}" "${HOME}"'/tmp' '/tmp' "${HOME}" '.'; do if is_not_empty "$d"; then if obj d is_dir && obj d is_writable; then _TMP_DIR="${d}/${_PROGRAM_NAME}${_PROCESS_ID}"; if obj _TMP_DIR is_dir; then rm -f "${_TMP_DIR}"/*; break; else mkdir "${_TMP_DIR}"; if obj _TMP_DIR is_not_dir; then _TMP_DIR=''; continue; fi; break; fi; fi; if obj _TMP_DIR is_not_writable; then _TMP_DIR=''; continue; fi; fi; done; unset d; if obj _TMP_DIR is_empty; then error "Couldn't create a directory for storing temporary files."; fi; _TMP_CAT="$(tmp_create groffer_cat)"; _TMP_STDIN="$(tmp_create groffer_input)"; # groffer configuration files for f in ${_CONFFILES}; do if obj f is_file; then echo '_groffer_opt=""' >>${_TMP_CAT}; # collect the lines starting with a minus cat "$f" | sed -e \ '/^[ ]*\(-.*\)$/s//_groffer_opt="${_groffer_opt} \1"'/ \ >>${_TMP_CAT}; # prepend the collected information to $GROFFER_OPT echo 'GROFFER_OPT="${_groffer_opt} ${GROFFER_OPT}"' >>${_TMP_CAT}; fi; done; . "${_TMP_CAT}"; _TMP_CAT="$(tmp_create groffer_cat)"; 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_* # in/out: $GROFFER_OPT # landmark '14: main_parse_MANOPT()'; main_parse_MANOPT() { func_check main_parse_MANOPT = 0 "$@"; local _opt; local _list; _list=''; if obj MANOPT is_not_empty; then MANOPT="$(echo -n "${MANOPT}" | \ sed -e 's/^'"${_SPACE}${_SPACE}"'*//')"; fi; if obj MANOPT is_empty; then eval "${return_ok}"; fi; # add arguments in $MANOPT by mapping them to groffer options eval set -- "$(list_from_cmdline \ _OPTS_MANOPT_SHORT_NA _OPTS_MANOPT_SHORT_ARG \ _OPTS_MANOPT_LONG_NA _OPTS_MANOPT_LONG_ARG \ "${MANOPT}")"; until test "$#" -le 0 || is_equal "$1" '--'; do _opt="$1"; shift; case "${_opt}" in -7|--ascii) list_append _list '--ascii'; ;; -a|--all) list_append _list '--all'; ;; -c|--catman) do_nothing; shift; ;; -d|--debug) list_append _list '--debug'; ;; -D|--default) # undo all man options so far _list=''; ;; -e|--extension) list_append _list '--extension'; shift; ;; -f|--whatis) list_append _list '--whatis'; shift; ;; -h|--help) do_nothing; shift; ;; -k|--apropos) # groffer's --apropos takes an argument, but man's does not, so do_nothing; shift; ;; -l|--local-file) list_append _list '--local-file'; ;; -L|--locale) list_append _list '--locale' "$1"; shift; ;; -m|--systems) list_append _list '--systems' "$1"; shift; ;; -M|--manpath) list_append _list '--manpath' "$1"; shift; ;; -p|--preprocessor) do_nothing; shift; ;; -P|--pager|--tty-viewer) list_append _list '--pager' "$1"; shift; ;; -r|--prompt) do_nothing; shift; ;; -S|--sections) list_append _list '--sections' "$1"; shift; ;; -t|--troff) do_nothing; shift; ;; -T|--device) list_append _list '-T' "$1"; shift; ;; -u|--update) do_nothing; shift; ;; -V|--version) do_nothing; ;; -w|--where|--location) list_append _list '--location'; ;; -Z|--ditroff) list_append _list '-Z' "$1"; shift; ;; # ignore all other options esac; done; # append the 2 lists in $_list and $GROFFER_OPT to $GROFFER_OPT if obj GROFFER_OPT is_empty; then GROFFER_OPT="${_list}"; elif obj _list is_not_empty; then GROFFER_OPT="${_list} ${GROFFER_OPT}"; fi; eval "${return_ok}"; } # main_parse_MANOPT() ######################################################################## # main_parse_args (*) # # Parse arguments; process options and filespec parameters # # Arguments: pass the command line arguments unaltered. # Globals: # in: $_OPTS_* # out: $_OPT_*, $_ADDOPTS, $_FILEARGS # landmark '15: main_parse_args()'; main_parse_args() { func_check main_parse_args '>=' 0 "$@"; local _arg; local _code; local _dpi; local _longopt; local _mode; local _opt; local _optchar; local _optarg; local _opts; local _string; eval set -- "${GROFFER_OPT}" '"$@"'; eval set -- "$(list_from_cmdline \ _OPTS_CMDLINE_SHORT_NA _OPTS_CMDLINE_SHORT_ARG \ _OPTS_CMDLINE_LONG_NA _OPTS_CMDLINE_LONG_ARG \ "$@")"; # By the call of `eval', unnecessary quoting was removed. So the # positional shell parameters ($1, $2, ...) are now guaranteed to # represent an option or an argument to the previous option, if any; # then a `--' argument for separating options and # parameters; followed by the filespec parameters if any. # Note, the existence of arguments to options has already been checked. # So a check for `$#' or `--' should not be done for arguments. until test "$#" -le 0 || is_equal "$1" '--'; do _opt="$1"; # $_opt is fed into the option handler shift; case "${_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|--X|--x) _OPT_MODE=x; ;; -?) # delete leading `-' _optchar="$(echo -n "${_opt}" | sed -e 's/^.//')"; if list_has _OPTS_GROFF_SHORT_NA "${_optchar}"; then list_append _ADDOPTS_GROFF "${_opt}"; elif list_has _OPTS_GROFF_SHORT_ARG "${_optchar}"; then list_append _ADDOPTS_GROFF "${_opt}" "$1"; shift; else error "Unknown option : \`$1'"; fi; ;; --all) _OPT_ALL="yes"; ;; --ascii) list_append _ADDOPTS_GROFF '-mtty-char'; if obj _mode is_empty; then _mode='text'; fi; ;; --apropos) # run `apropos' apropos_run "$1"; _code="$?"; clean_up; exit "${_code}"; ;; --apropos-data) # run `apropos' for data sections apropos_run "$1" | grep '^[^(]*([457])'; _code="$?"; clean_up; exit "${_code}"; ;; --apropos-devel) # run `apropos' for development sections apropos_run "$1" | grep '^[^(]*([239])'; _code="$?"; clean_up; exit "${_code}"; ;; --apropos-progs) # run `apropos' for program sections apropos_run "$1" | grep '^[^(]*([168])'; _code="$?"; clean_up; exit "${_code}"; ;; --auto) # the default automatic mode _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; ;; --default) # reset variables to default reset; ;; --default-modes) # sequence of modes in auto mode; arg _OPT_DEFAULT_MODES="$1"; shift; ;; --debug) # buggy, only for development _OPT_DEBUG='yes'; ;; --display) # set X display, arg _OPT_DISPLAY="$1"; shift; ;; --dvi) _OPT_MODE='dvi'; ;; --dvi-viewer) # viewer program for dvi mode; arg _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 _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 _arg="$1"; shift; case "${_arg}" in auto|'') # search mode automatically among default _mode=''; ;; groff) # pass input to plain groff _mode='groff'; ;; html|www) # display with a web browser _mode='html'; ;; dvi) # display with xdvi viewer _mode='dvi'; ;; pdf) # display with PDF viewer _mode='pdf'; ;; ps) # display with Postscript viewer _mode='ps'; ;; text) # output on terminal _mode='text'; ;; tty) # output on terminal _mode='tty'; ;; X|x) # output on X roff viewer _mode='x'; ;; Q|source) # display source code _mode="source"; ;; *) error "unknown mode ${_arg}"; ;; esac; _OPT_MODE="${_mode}"; ;; --no-location) # disable former call to `--location' _OPT_LOCATION='yes'; ;; --no-man) # disable search for man pages # the same as --local-file _MAN_FORCE="no"; _MAN_ENABLE="no"; ;; --pager) # set paging program for tty mode, arg _OPT_PAGER="$1"; shift; ;; --pdf) _OPT_MODE='pdf'; ;; --pdf-viewer) # viewer program for ps mode; arg _OPT_VIEWER_PDF="$1"; shift; ;; --ps) _OPT_MODE='ps'; ;; --ps-viewer) # viewer program for ps mode; arg _OPT_VIEWER_PS="$1"; shift; ;; --resolution) # set resolution for X devices, arg _arg="$1"; shift; case "${_arg}" in 75|75dpi) _dpi=75; ;; 100|100dpi) _dpi=100; ;; *) error "only resoutions of 75 or 100 dpi are supported"; ;; esac; _OPT_RESOLUTION="${_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) 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'; ;; --xrm) # pass X resource string, arg; list_append _OPT_XRM "$1"; shift; ;; --x-viewer|--X-viewer) # viewer program for x mode; arg _OPT_VIEWER_X="$1"; shift; ;; *) error 'error on argument parsing : '"\`$*'"; ;; esac; done; shift; # remove `--' argument if obj _DEBUG is_not_yes; then if obj _OPT_DEBUG is_yes; then _DEBUG='yes'; fi; fi; # Remaining arguments are file names (filespecs). # Save them to list $_FILEARGS if is_equal "$#" 0; then # use "-" for standard input set -- '-'; fi; _FILEARGS=''; list_append _FILEARGS "$@"; if list_has _FILEARGS '-'; then save_stdin; fi; # $_FILEARGS must be retrieved with `eval set -- "$_FILEARGS"' 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}"; } ######################################################################## # main_set_mode () # # Determine the display mode. # # Globals: # in: $DISPLAY, $_OPT_MODE, $_OPT_DEVICE # out: $_DISPLAY_MODE # # _get_first_prog () # # 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. # landmark '16: main_set_mode()'; main_set_mode() { func_check main_set_mode = 0 "$@"; local m; local _modes; local _viewer; local _viewers; # handle apropos if obj _OPT_APROPOS is_not_empty; then apropos "${_OPT_APROPOS}"; _code="$?"; clean_up; exit "${_code}"; fi; if obj _OPT_APROPOS_DATA is_not_empty; then apropos "$@" | grep '^[^(]*([457])'; _code="$?"; clean_up; exit "${_code}"; fi; if obj _OPT_APROPOS_DEVEL is_not_empty; then apropos "$@" | grep '^[^(]*([239])'; _code="$?"; clean_up; exit "${_code}"; fi; if obj _OPT_APROPOS_PROGS is_not_empty; then apropos "$@" | grep '^[^(]*([168])'; _code="$?"; clean_up; exit "${_code}"; fi; # set display if obj _OPT_DISPLAY is_not_empty; then DISPLAY="${_OPT_DISPLAY}"; fi; if obj _OPT_V is_yes; then _DISPLAY_MODE='groff'; 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 "${return_ok}"; fi; if obj _OPT_MODE is_equal 'source'; then _DISPLAY_MODE='source'; eval "${return_ok}"; fi; case "${_OPT_MODE}" in '') # automatic mode case "${_OPT_DEVICE}" in X*) if obj DISPLAY is_empty; then error "no X display found for device ${_OPT_DEVICE}"; fi; _DISPLAY_MODE='x'; eval "${return_ok}"; ;; ascii|cp1047|latin1|utf8) if obj _DISPLAY_MODE is_not_equal 'text'; then _DISPLAY_MODE='tty'; fi; eval "${return_ok}"; ;; esac; if obj DISPLAY is_empty; then _DISPLAY_MODE='tty'; eval "${return_ok}"; fi; if obj _OPT_DEFAULT_MODES is_empty; then _modes="${_DEFAULT_MODES}"; else _modes="${_OPT_DEFAULT_MODES}"; fi; ;; text) _DISPLAY_MODE='text'; eval "${return_ok}"; ;; tty) _DISPLAY_MODE='tty'; eval "${return_ok}"; ;; *) # display mode was given if obj DISPLAY is_empty; then error "you must be in X Window for ${_OPT_MODE} mode."; fi; _modes="${_OPT_MODE}"; ;; esac; # only viewer modes are left eval set -- "$(list_from_split "${_modes}" ',')"; while test "$#" -gt 0; do m="$1"; shift; case "$m" in text) _DISPLAY_MODE='text'; eval "${return_ok}"; ;; tty) _DISPLAY_MODE='tty'; eval "${return_ok}"; ;; x) if obj _OPT_VIEWER_X is_not_empty; then _viewers="${_OPT_VIEWER_X}"; else _viewers="${_VIEWER_X}"; fi; _viewer="$(_get_first_prog "${_viewers}")"; if is_not_equal "$?" 0; then continue; fi; _DISPLAY_PROG="${_viewer}"; _DISPLAY_MODE='x'; eval "${return_ok}"; ;; dvi) if obj _OPT_VIEWER_DVI is_not_empty; then _viewers="${_OPT_VIEWER_DVI}"; else _viewers="${_VIEWER_DVI}"; fi; _viewer="$(_get_first_prog "${_viewers}")"; if is_not_equal "$?" 0; then continue; fi; _DISPLAY_PROG="${_viewer}"; _DISPLAY_MODE="dvi"; eval "${return_ok}"; ;; pdf) if obj _OPT_VIEWER_PDF is_not_empty; then _viewers="${_OPT_VIEWER_PDF}"; else _viewers="${_VIEWER_PDF}"; fi; _viewer="$(_get_first_prog "${_viewers}")"; if is_not_equal "$?" 0; then continue; fi; _DISPLAY_PROG="${_viewer}"; _DISPLAY_MODE="pdf"; eval "${return_ok}"; ;; ps) if obj _OPT_VIEWER_PS is_not_empty; then _viewers="${_OPT_VIEWER_PS}"; else _viewers="${_VIEWER_PS}"; fi; _viewer="$(_get_first_prog "${_viewers}")"; if is_not_equal "$?" 0; then continue; fi; _DISPLAY_PROG="${_viewer}"; _DISPLAY_MODE="ps"; eval "${return_ok}"; ;; html) if obj _OPT_VIEWER_HTML is_not_empty; then _viewers="${_OPT_VIEWER_HTML}"; else _viewers="${_VIEWER_HTML}"; fi; _viewer="$(_get_first_prog "${_viewers}")"; if is_not_equal "$?" 0; then continue; fi; _DISPLAY_PROG="${_viewer}"; _DISPLAY_MODE=html; eval "${return_ok}"; ;; esac; done; error "no suitable display mode found."; } _get_first_prog() { local i; if is_equal "$#" 0; then error "_get_first_prog() needs 1 argument."; fi; if is_empty "$1"; then return "${_BAD}"; fi; eval set -- "$(list_from_split "$1" ',')"; for i in "$@"; do if obj i is_empty; then continue; fi; if is_prog "$(get_first_essential $i)"; then echo -n "$i"; return "${_GOOD}"; fi; done; return "${_BAD}"; } # main_set_mode() ####################################################################### # main_do_fileargs () # # Process filespec arguments in $_FILEARGS. # # Globals: # in: $_FILEARGS (process with `eval set -- "$_FILEARGS"') # landmark '17: main_do_fileargs()'; main_do_fileargs() { func_check main_do_fileargs = 0 "$@"; local _exitcode; local _filespec; local _name; _exitcode="${_BAD}"; eval set -- "${_FILEARGS}"; 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 _filespec="$1"; shift; case "${_filespec}" in '') continue; ;; '-') if register_file '-'; then _exitcode="${_GOOD}"; fi; continue; ;; ?) if list_has_not _MAN_AUTO_SEC "${_filespec}"; then if do_filearg "${_filespec}"; then _exitcode="${_GOOD}"; fi; continue; fi; _name="$1"; case "${_name}" in */*|man:*|*\(*\)|*."${_filespec}") if do_filearg "${_filespec}"; then _exitcode="${_GOOD}"; fi; continue; ;; esac; if do_filearg "man:${_name}(${_filespec})"; then _exitcode="${_GOOD}"; shift; continue; else if do_filearg "${_filespec}"; then _exitcode="${_GOOD}"; fi; continue; fi; ;; *) if do_filearg "${_filespec}"; then _exitcode="${_GOOD}"; fi; continue; ;; esac; done; # end of `s name' test while test "$#" -gt 0; do _filespec="$1"; shift; if do_filearg "${_filespec}"; then _exitcode="${_GOOD}"; fi; done; rm -f "${_TMP_STDIN}"; if is_equal "${_exitcode}" "${_BAD}"; then eval "${return_bad}"; fi; eval "${return_ok}"; } # main_do_fileargs() ######################################################################## # main_set_resources () # # Determine options for setting X resources with $_DISPLAY_PROG. # # Globals: $_DISPLAY_PROG, $_OUTPUT_FILE_NAME # landmark '18: main_set_resources()'; main_set_resources() { func_check main_set_resources = 0 "$@"; local _prog; # viewer program local _rl; # resource list local n; _title="$(get_first_essential \ "${_OPT_TITLE}" "${_REGISTERED_TITLE}")"; _OUTPUT_FILE_NAME=''; set -- ${_title}; until is_equal "$#" 0; do n="$1"; case "$n" in '') continue; ;; ,*) n="$(echo -n "$1" | sed -e '/^,,*/s///')"; ;; esac if obj 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}$n"; shift; done; case "${_OUTPUT_FILE_NAME}" in '') _OUTPUT_FILE_NAME='-'; ;; ,*) error "$_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 "${return_ok}"; fi; set -- ${_DISPLAY_PROG}; _prog="$(base_name "$1")"; _rl=''; if obj _OPT_BD is_not_empty; then case "${_prog}" in ghostview|gv|gxditview|xditview|xdvi) list_append _rl '-bd' "${_OPT_BD}"; ;; esac; fi; if obj _OPT_BG is_not_empty; then case "${_prog}" in ghostview|gv|gxditview|xditview|xdvi) list_append _rl '-bg' "${_OPT_BG}"; ;; xpdf) list_append _rl '-papercolor' "${_OPT_BG}"; ;; esac; fi; if obj _OPT_BW is_not_empty; then case "${_prog}" in ghostview|gv|gxditview|xditview|xdvi) _list_append _rl '-bw' "${_OPT_BW}"; ;; esac; fi; if obj _OPT_FG is_not_empty; then case "${_prog}" in ghostview|gv|gxditview|xditview|xdvi) list_append _rl '-fg' "${_OPT_FG}"; ;; esac; fi; if is_not_empty "${_OPT_FN}"; then case "${_prog}" in ghostview|gv|gxditview|xditview|xdvi) list_append _rl '-fn' "${_OPT_FN}"; ;; esac; fi; if is_not_empty "${_OPT_GEOMETRY}"; then case "${_prog}" in ghostview|gv|gxditview|xditview|xdvi|xpdf) list_append _rl '-geometry' "${_OPT_GEOMETRY}"; ;; esac; fi; if is_empty "${_OPT_RESOLUTION}"; then _OPT_RESOLUTION="${_DEFAULT_RESOLUTION}"; case "${_prog}" in gxditview|xditview) list_append _rl '-resolution' "${_DEFAULT_RESOLUTION}"; ;; xpdf) case "${_DEFAULT_RESOLUTION}" in 75) # 72dpi is '100' list_append _rl '-z' '104'; ;; 100) list_append _rl '-z' '139'; ;; esac; ;; esac; else case "${_prog}" in ghostview|gv|gxditview|xditview|xdvi) list_append _rl '-resolution' "${_OPT_RESOLUTION}"; ;; xpdf) case "${_OPT_RESOLUTION}" in 75) list_append _rl '-z' '104'; # '100' corresponds to 72dpi ;; 100) list_append _rl '-z' '139'; ;; esac; ;; esac; fi; if is_yes "${_OPT_ICONIC}"; then case "${_prog}" in ghostview|gv|gxditview|xditview|xdvi) list_append _rl '-iconic'; ;; esac; fi; if is_yes "${_OPT_RV}"; then case "${_prog}" in ghostview|gv|gxditview|xditview|xdvi) list_append _rl '-rv'; ;; esac; fi; if is_not_empty "${_OPT_XRM}"; then case "${_prog}" in ghostview|gv|gxditview|xditview|xdvi|xpdf) eval set -- "{$_OPT_XRM}"; for i in "$@"; do list_append _rl '-xrm' "$i"; done; ;; esac; fi; if is_not_empty "${_title}"; then case "${_prog}" in gxditview|xditview) list_append _rl '-title' "${_title}"; ;; esac; fi; _DISPLAY_ARGS="${_rl}"; 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, # $_REGISTERED_TITLE, $_TMP_CAT, # $_OPT_PAGER $PAGER $_MANOPT_PAGER # landmark '19: main_display()'; main_display() { func_check main_display = 0 "$@"; local p; local _addopts; local _device; local _groggy; local _modefile; local _options; local _pager; local _title; export _addopts; export _groggy; export _modefile; if obj _TMP_CAT is_non_empty_file; then _modefile="${_OUTPUT_FILE_NAME}"; else clean_up; eval "${return_ok}"; fi; 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; _groggy="$(tmp_cat | eval grog "${_options}")"; trap_clean; # start a new shell program to get another process ID. sh -c ' set -e; test -f "${_modefile}" && rm -f "${_modefile}"; mv "${_TMP_CAT}" "${_modefile}"; cat "${_modefile}" | \ ( clean_up() { if test -d "${_TMP_DIR}"; then rm -f "${_TMP_DIR}"/* || true; rmdir "${_TMP_DIR}"; fi; } trap clean_up 0 2>/dev/null || true; eval "${_groggy}" "${_ADDOPTS_GROFF}"; ) &' ;; text|tty) case "${_OPT_DEVICE}" in '') _device="$(get_first_essential \ "${_OPT_TEXT_DEVICE}" "${_DEFAULT_TTY_DEVICE}")"; ;; ascii|cp1047|latin1|utf8) _device="${_OPT_DEVICE}"; ;; *) warning \ "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}"; ;; esac; _addopts="${_ADDOPTS_GROFF} ${_ADDOPTS_POST}"; _groggy="$(tmp_cat | grog -T${_device})"; if obj _DISPLAY_MODE is_equal 'text'; then tmp_cat | eval "${_groggy}" "${_addopts}"; else _pager=''; for p in "${_OPT_PAGER}" "${PAGER}" "${_MANOPT_PAGER}" \ 'less -r -R' 'more' 'pager' 'cat'; do if is_prog $p; then # no "" for is_prog() allows args for $p _pager="$p"; break; fi; done; if obj _pager is_empty; then error 'no pager program found for tty mode'; fi; tmp_cat | eval "${_groggy}" "${_addopts}" | \ eval "${_pager}"; fi; clean_up; ;; #### viewer modes dvi) case "${_OPT_DEVICE}" in ''|dvi) do_nothing; ;; *) warning \ "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}"; ;; esac; _groggy="$(tmp_cat | grog -Tdvi)"; _do_display; ;; html) case "${_OPT_DEVICE}" in ''|html) do_nothing; ;; *) warning \ "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}"; ;; esac; _modefile="${_modefile}".html _groggy="$(tmp_cat | grog -Thtml)"; _do_display; ;; pdf) case "${_OPT_DEVICE}" in ''|ps) do_nothing; ;; *) warning \ "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}"; ;; esac; _modefile="${_modefile}" _groggy="$(tmp_cat | grog -Tps)"; trap_clean; # start a new shell program to get another process ID. sh -c ' set -e; _psfile="${_modefile}.ps"; _modefile="${_modefile}.pdf"; test -f "${_psfile}" && rm -f "${_psfile}"; test -f "${_modefile}" && rm -f "${_modefile}"; cat "${_TMP_CAT}" | \ eval "${_groggy}" "${_ADDOPTS_GROFF}" > "${_psfile}"; gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite \ -sOutputFile="${_modefile}" -c save pop -f "${_psfile}"; test -f "${_psfile}" && rm -f "${_psfile}"; test -f "${_TMP_CAT}" && rm -f "${_TMP_CAT}"; ( clean_up() { rm -f "${_modefile}"; if test -d "${_TMP_DIR}"; then rm -f "${_TMP_DIR}"/* || true; rmdir "${_TMP_DIR}"; fi; } trap clean_up 0 2>/dev/null || true; eval "${_DISPLAY_PROG}" ${_DISPLAY_ARGS} "${_modefile}"; ) &' ;; ps) case "${_OPT_DEVICE}" in ''|ps) do_nothing; ;; *) warning \ "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}"; ;; esac; _groggy="$(tmp_cat | grog -Tps)"; _do_display; ;; source) tmp_cat; clean_up; ;; x) case "${_OPT_DEVICE}" in '') _groggy="$(tmp_cat | grog -Z)"; ;; X*|ps) _groggy="$(tmp_cat | grog -T"${_OPT_DEVICE}" -Z)"; ;; *) warning \ "wrong device for ${_DISPLAY_MODE} mode: ${_OPT_DEVICE}"; _groggy="$(tmp_cat | grog -Z)"; ;; esac; _do_display; ;; *) error "unknown mode \`${_DISPLAY_MODE}'"; ;; esac; eval "${return_ok}"; } # main_display() _do_display() { func_check _do_display = 0 "$@"; trap_clean; # start a new shell program for another process ID and better # cleaning-up of the temporary files. sh -c ' set -e; test -f "${_modefile}" && rm -f "${_modefile}"; cat "${_TMP_CAT}" | \ eval "${_groggy}" "${_ADDOPTS_GROFF}" > "${_modefile}"; rm -f "${_TMP_CAT}"; ( clean_up() { if test -d "${_TMP_DIR}"; then rm -f "${_TMP_DIR}"/* || true; rmdir "${_TMP_DIR}"; fi; } trap clean_up 0 2>/dev/null || true; eval "${_DISPLAY_PROG}" ${_DISPLAY_ARGS} "${_modefile}"; ) &' } ######################################################################## # main (*) # # The main function for groffer. # # Arguments: # main() { func_check main '>=' 0 "$@"; # Do not change the sequence of the following functions! main_init; main_parse_MANOPT; main_parse_args "$@"; main_set_mode; main_do_fileargs; main_set_resources; main_display; eval "${return_ok}"; } landmark '20: end of function definitions'; ######################################################################## main "$@";