X-Git-Url: http://git.tuebingen.mpg.de/?p=gsu.git;a=blobdiff_plain;f=subcommand;h=956fb5c865ad3ae780ac7344917345879380aa8e;hp=b3dc0493905a2eb0a0bcb016bc99ed8e4799d981;hb=46d356bc055978057542e999892ec6e0cf1c0153;hpb=8ab2321bcdb1d7f018bdd398c1b7b1a4da5d22e5 diff --git a/subcommand b/subcommand index b3dc049..956fb5c 100644 --- a/subcommand +++ b/subcommand @@ -187,6 +187,8 @@ _gsu_print_available_commands() # Print all options of the given optstring to stdout if the word in the current # command line begins with a hyphen character. +# +# Returns 0 if the current word does not start with a hyphen, one otherwise. gsu_complete_options() { local opts="$1" cword="$2" cur opt @@ -198,13 +200,12 @@ gsu_complete_options() ret=0 [[ ! "$cur" == -* ]] && return - ret=0 for ((i=0; i < ${#opts}; i++)); do opt="${opts:$i:1}" [[ "$opt" == ":" ]] && continue printf "%s" "-$opt " - let ret++ done + ret=1 } com_prefs_options='e' @@ -278,29 +279,310 @@ complete_prefs() gsu_complete_options "$com_prefs_options" "$@" } +_gsu_man_options='m:b:' + +complete_man() +{ + gsu_complete_options "$_gsu_man_options" "$@" + ((ret > 0)) && return + gsu_cword_is_option_parameter "$_gsu_man_options" "$@" + [[ "$result" == 'm' ]] && printf 'roff\ntext\nhtml\n' +} + _gsu_man_txt=' Print the manual. -Usage: man +Usage: man [-m ] [-b ] -If stdout associated with a terminal device, output is piped to -$PAGER. If $PAGER is unset, less(1) is assumed. +-m: Set output format (text, roff or html). Default: text. +-b: Use the specified browser. Implies html mode. + +If stdout is not associated with a terminal device, the command +dumps the man page to stdout and exits. + +Otherwise, it tries to display the manual page as follows. In text +mode, plain text is piped to $PAGER. In roff mode, the roff output +is filtered through nroff, then piped to $PAGER. For both formats, +if $PAGER is unset, less(1) is assumed. + +In html mode, html output is written to a temporary file, and this +file is displayed as a page in the web browser. If -b is not given, +the command stored in the $BROWSER environment variable is executed +with the path to the temporary file as an argument. If $BROWSER is +unset, elinks(1) is assumed. + +It is recommended to specify the output format with -m as the default +mode might change in future versions of gsu. ' +_gsu_read_line() +{ + local -n p="$1" + local l OIFS="$IFS" + + IFS= + read -r l || return + IFS="$OIFS" + p="$l" +} + +_gsu_change_roffify_state() +{ + local -n statep="$1" + local new_state="$2" + local old_state="$statep" + + [[ "$old_state" == "$new_state" ]] && return 0 + + case "$old_state" in + text);; + example) printf '.EE\n';; + enum) printf '.RE\n';; + esac + case "$new_state" in + text);; + example) printf '.EX\n';; + enum) printf '.RS 2\n';; + esac + + statep="$new_state" + return 1 +} + +_gsu_print_protected_roff_line() +{ + local line="$1" + local -i n=0 + + while [[ "${line:$n:1}" == ' ' ]]; do + let n++ + done + line="${line:$n}" + printf '\\&%s\n' "${line//\\/\\\\}" +} + +_gsu_roffify_maindoc() +{ + local state='text' TAB=' ' + local line next_line + local -i n + + _gsu_read_line 'line' || return + while _gsu_read_line next_line; do + if [[ "$next_line" =~ ^(----|====|~~~~) ]]; then # heading + printf '.SS %s\n' "$line" + _gsu_read_line line || return + _gsu_change_roffify_state 'state' 'text' + continue + fi + if [[ "${line:0:1}" == "$TAB" ]]; then # example + _gsu_change_roffify_state 'state' 'example' + printf '%s\n' "$line" + line="$next_line" + continue + fi + n=0 + while [[ "${line:$n:1}" == ' ' ]]; do + let n++ + done + line=${line:$n}; + if [[ "${line:0:1}" == '*' ]]; then # enum + line=${line#\*} + _gsu_change_roffify_state 'state' 'enum' + printf '\n\(bu %s\n' "$line" + line="$next_line" + continue + fi + if [[ "$line" =~ ^$ ]]; then # new paragraph + _gsu_change_roffify_state 'state' 'text' + printf '.PP\n' + else + _gsu_print_protected_roff_line "$line" + fi + line="$next_line" + done + _gsu_print_protected_roff_line "$line" +} + +_gsu_extract_maindoc() +{ + sed -e '1,/^#\{70,\}/d' -e '/^#\{70,\}/,$d' -e 's/^# *//' -e 's/^#//g' "$0" +} + +_gsu_roffify_cmds() +{ + local line cmd= desc= state='text' TAB=' ' + + while _gsu_read_line line; do + if [[ "${line:0:1}" != '#' ]]; then # com_foo() + line="${line#com_}" + cmd="${line%()}" + continue + fi + line="${line####}" + if [[ "$line" =~ ^[[:space:]]*$ ]]; then + printf '.PP\n' + _gsu_change_roffify_state 'state' 'text' + continue + fi + if [[ -n "$cmd" ]]; then # desc or usage + if [[ -z "$desc" ]]; then # desc + desc="$line" + continue + fi + # usage + _gsu_change_roffify_state 'state' 'text' + printf '\n.SS %s \\- %s\n' "$cmd" "$desc" + printf '\n.I %s\n' "$line" + cmd= + desc= + continue + fi + line="${line# }" + if [[ "${line:0:1}" == "$TAB" ]]; then + _gsu_change_roffify_state 'state' 'example' + _gsu_print_protected_roff_line "$line" + continue + fi + if [[ "$line" == -*:* ]]; then + _gsu_change_roffify_state 'state' 'enum' + printf '.PP\n.B %s:\n' "${line%%:*}" + _gsu_print_protected_roff_line "${line#*:}" + continue + fi + _gsu_print_protected_roff_line "$line" + done +} + +_gsu_roffify_autocmd() +{ + local cmd="$1" help_txt="$2" + + { + printf 'com_%s()\n' "$cmd" + sed -e 's/^/## /g' <<< "$help_txt" + } | _gsu_roffify_cmds +} + +_gsu_roff_man() +{ + local name="$1" sect_num="$2" month_year="$3" pkg="$4" sect_name="$5" + local purpose="$6" + local ere + + cat << EOF +.TH "${name^^}" "$sect_num" "$month_year" "$pkg" "$sect_name" +.SH NAME +$name \- $purpose +.SH SYNOPSIS +.B $name +\fI\,\/\fR [\fI\,\/\fR] [\fI\,\/\fR] +.SH DESCRIPTION +EOF + _gsu_extract_maindoc | _gsu_roffify_maindoc + + printf '\n.SH "GENERIC SUBCOMMANDS"\n' + printf 'The following commands are automatically created by gsu\n' + _gsu_roffify_autocmd "help" "$_gsu_help_txt" + _gsu_roffify_autocmd "man" "$_gsu_man_txt" + _gsu_roffify_autocmd "prefs" "$_gsu_prefs_txt" + _gsu_roffify_autocmd "complete" "$_gsu_complete_txt" + + printf '\n.SH "LIST OF SUBCOMMANDS"\n' + printf 'Each command has its own set of options as described below.\n' + + _gsu_get_command_regex + ere="$result" + # only consider lines in the comment of the function + sed -nEe '/'"$ere"'/,/^[^#]/p' "$0" | _gsu_roffify_cmds +} + +_gsu_file_mtime() +{ + local file="$1" + result="$(find "$file" -printf '%TB %TY' 2>/dev/null)" # GNU + (($? == 0)) && [[ -n "$result" ]] && return + result="$(stat -f %Sm -t '%B %Y' "$file" 2>/dev/null)" # BSD + (($? == 0)) && [[ -n "$result" ]] && return + result="$(date '+%B %Y' 2>/dev/null)" # POSIX + (($? == 0)) && [[ -n "$result" ]] && return + result='[unknown date]' +} + com_man() { local equal_signs="==================================================" local minus_signs="--------------------------------------------------" - local com num pager='cat' + local filter='cat' pager='cat' browser=${BROWSER:-elinks} tmpfile= + local com num isatty pipeline - _gsu_isatty && pager="${PAGER:-less}" + gsu_getopts "$_gsu_man_options" + eval "$result" + ((ret < 0)) && return + if [[ -n "$o_b" ]]; then + o_m='html' + browser="$o_b" + elif [[ -z "$o_m" ]]; then + o_m='text' + fi + + _gsu_isatty && isatty='true' || isatty='false' + if [[ "$o_m" == 'roff' ]]; then + if [[ "$isatty" == 'true' ]]; then + filter='nroff -Tutf8 -mandoc' + pager="${PAGER:-less}" + fi + elif [[ "$o_m" == 'text' ]]; then + if [[ "$isatty" == 'true' ]]; then + pager="${PAGER:-less}" + fi + elif [[ "$o_m" == 'html' ]]; then + filter='groff -T html -m man' + if [[ "$isatty" == 'true' ]]; then + gsu_make_tempfile "gsu_html_man.XXXXXX.html" + ((ret < 0)) && return || tmpfile="$result" + trap "rm -f $tmpfile" RETURN EXIT + fi + fi [[ "$pager" == 'less' ]] && export LESS=${LESS-RI} + case "$o_m" in + roff|html) + _gsu_file_mtime "$0" + _gsu_roff_man "$gsu_name" '1' "$result" \ + "${gsu_package-${gsu_name^^}(1)}" \ + "User Commands" "${gsu_banner_txt}" \ + | $filter | { + if [[ -n "$tmpfile" ]]; then + cat > "$tmpfile" + else + $pager + fi + } + if (($? != 0)); then + ret=-$E_GSU_XCMD + result="filter: $filter" + return + fi + if [[ -n "$tmpfile" ]]; then + ret=-$E_GSU_XCMD + result="$browser" + "$browser" "$tmpfile" || return + fi + ret=$GSU_SUCCESS + return + ;; + text) ;; + "") ;; + *) + ret=-$E_GSU_INVAL + result="$o_m" + return + esac { echo "$gsu_name (_${gsu_banner_txt}_) manual" echo "${equal_signs:0:${#gsu_name} + ${#gsu_banner_txt} + 16}" echo - - sed -e '1,/^#\{70,\}/d' -e '/^#\{70,\}/,$d' "$0" -e 's/^# *//' + _gsu_extract_maindoc echo "----" echo echo "$gsu_name usage" @@ -493,7 +775,8 @@ EOF # gsu_cword_is_option_parameter() { - local opts="$1" cword="$2" prev i n + local opts="$1" cword="$2" + local opt prev i n local -a words result= @@ -510,7 +793,7 @@ gsu_cword_is_option_parameter() opt="${opts:$i:1}" [[ "${opts:$(($i + 1)):1}" != ":" ]] && continue let i++ - [[ "$prev" != "-$opt" ]] && continue + [[ ! "$prev" =~ ^-.*$opt$ ]] && continue result="$opt" return done