From bd1d554a35b543afa5a79dd483583bf2aad9cf47 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Fri, 15 Apr 2016 17:06:52 -0400 Subject: libremessages:flag: Fix several things (also librexgettext) - Fix a bug where it panicked if given an odd number of sub-headings. - Document the the ability to include sub-headings. - Fix librexgettext's handling of it. It only worked correctly for times when it was only called once in a program, or when it was only ever called with exactly one flag/description pair (and no headings). --- src/lib/libremessages.1.ronn | 8 +++- src/lib/librexgettext | 101 +++++++++++++++++++++++++++++++++---------- src/lib/messages.sh | 12 +++-- 3 files changed, 92 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/lib/libremessages.1.ronn b/src/lib/libremessages.1.ronn index 9a976a8..d4fac85 100644 --- a/src/lib/libremessages.1.ronn +++ b/src/lib/libremessages.1.ronn @@ -101,7 +101,7 @@ routines by default. Similar to `prose`, but prints a bullet point before the first line, and indents the remaining lines. - * `flag` [ ...]: + * `flag` [ |:]...: Print a flag and description formatted for `--help` text. For example:
`flag '-N' 'Disable networking in the chroot'`
@@ -113,7 +113,11 @@ routines by default. whitespace-collapsed (so newlines are stripped), then it is re-word-wrapped, in the same way as `prose` and `bullet`. If you pass in multiple flag/description pairs to the same invocation, - the descriptions are all aligned together. + the descriptions are all aligned together. The ability to do + insert headings without resetting the alignment is the motivation + for also allowing headings to be in the list. In order to tell + the difference between a flag and a heading, a heading must end + with a colon (':'), and a flag must not. ### NOTIFICATION ROUTINES diff --git a/src/lib/librexgettext b/src/lib/librexgettext index 22a6df2..c82b9ae 100755 --- a/src/lib/librexgettext +++ b/src/lib/librexgettext @@ -65,49 +65,104 @@ xgettext-sh() { xgettext-flag() { { - declare -i x=0 + # Stage 1: Generate + # + # Get all of the arguments to `flag`. Because `flag` + # takes an arbitrary number of arguments, just iterate + # through arg1, arg2, ... argN; until we've come up + # empty 3 times. Why 3? Because each flag takes 2 + # arguments, and because we don't keep track of which + # one of those we're on, waiting for 3 empties ensures + # us that we've had a complete "round" with nothing. + # + # Why can't I just do i+=2, and not have to keep track + # of empties? Because, we also allow for arguments + # ending in a colon to be headings, which changes the + # offsets. + declare -i empties=0 declare -i i - for (( i=1; x < 3; i++ )); do + for (( i=1; empties < 3; i++ )); do local out out="$(xgettext-sh --keyword="flag:$i,\"$i\"" "$@")" if [[ -n $out ]]; then printf -- '%s\n' "$out" else - x+=1 + empties+=1 fi done } | whitespace-collapse | sed '/^\#, sh-format/d' | { - declare -i i - - IFS='' - local segments=() + # Stage 2: Parse + # + # Read in the lines, and group them into an array of + # (multi-line) msgs. This just makes working with + # them easier. + local msgs=() + declare -i i=-1 local re='^#\. ([0-9]+)$' + IFS='' local line while read -r line; do if [[ $line =~ $re ]]; then - i=${BASH_REMATCH[1]} - else - segments[$i]+="$line"$'\n' + i+=1 fi + msgs[$i]+="$line"$'\n' done - - declare -i last=$(printf '%s\n' "${!segments[@]}"|sed -n '$p') - + # Stage 3: Sort + # + # Now, we have the `msgs` array, and it is + # sorted such that it is all of the arg1's to `flag`, + # then all of the arg2's, then all of the arg3's, and + # so on. We want to re-order them such that it's all + # of the args for the first invocation then all of the + # args for the second; and so on. + # + # We do this by simply sorting them by the location + # that they appear in the file. Then, when we see the + # argument number go back down to 1, we know that a + # new invocation has started! IFS=$'\n' - local flag=true - for (( i=1; i <= last; i++ )); do - if $flag; then - local lines=(${segments[$i]}) + local locations=($( + local i + for i in "${!msgs[@]}"; do + declare -i arg row + local lines=(${msgs[$i]}) + arg=${lines[0]#'#. '} + row=${lines[1]##*:} + printf '%d.%d %d\n' "$row" "$arg" "$i" + done | sort -n + )) + # Stage 4: Output + # + # Now, we prune out the arguments that aren't + # localizable. Also, remove the "#." comment lines. + # As explained above (in stage 3), when we see $arg go + # to 1, that's the beginning of a new invocation. + local expectflag=true + local location + for location in "${locations[@]}"; do + IFS=' .' + local row arg i + read -r row arg i <<<"$location" + local msg="${msgs[$i]#*$'\n'}" + # Now we operate based on $row, $arg, and $msg + if [[ $arg == 1 ]]; then + expectflag=true + fi + if $expectflag; then + IFS=$'\n' + local lines=(${msg}) if [[ ${lines[1]} == *':"' ]]; then - printf -- '%s\n' "${segments[$i]}" + # We expected a flag, but got + # a heading + printf -- '%s\n' "$msg" else - flag=false + # We expected a flag, and got + # one! + expectflag=false fi else - if [[ -n ${segments[$i]} ]]; then - printf -- '%s\n' "${segments[$i]}" - fi - flag=true + printf -- '%s\n' "$msg" + expectflag=true fi done } diff --git a/src/lib/messages.sh b/src/lib/messages.sh index 4b4897e..0125003 100644 --- a/src/lib/messages.sh +++ b/src/lib/messages.sh @@ -92,14 +92,15 @@ bullet() { printf -- "$mesg" "$@" | fmt -u -w 71 | sed -e '1s/^/ - /' -e '2,$s/^/ /' } -# Usage: flag FLAG DESCRIPTION [FLAG2 DESCRIPTION2...] +# Usage: flag [FLAG DESCRIPTION|HEADING:]... # # Print a flag and description formatted for --help text. # # ex: flag '-C ' 'Use this file instead of pacman.conf' # -# The description is fed through gettext, the flag is not, so if part -# of the flag needs to be translated, you must do that yourself: +# The descriptions and headings are fed through gettext, the flags ar +# not, so if part of a flag needs to be translated, you must do that +# yourself: # # ex: flag "-C <$(_ FILE)>" 'Use this file instead of pacman.conf' # @@ -107,8 +108,10 @@ bullet() { # crazy-long, feel free, it is reflowed/wrapped the same way as prose # and bullet. If you pass in multiple flag/description pairs at once, # the descriptions are all alligned together. +# +# A heading MUST end with a colon (':'), this is how it knows that it +# is a heading. Similarly, a flag MUST NOT end with a colon. flag() { - [[ $# == $(($#/2*2)) ]] || panic local args=("$@") declare -i flaglen=0 @@ -147,6 +150,7 @@ flag() { printf -- ' %s\n' "$(_ "$1")" shift else + [[ $# -gt 1 ]] || panic local flag=$1 local desc="$(_ "$(whitespace_collapse <<<"$2")")" shift 2 -- cgit v1.2.3