@@ -1189,10 +1189,52 @@ _comp_initialize()
11891189 return 0
11901190}
11911191
1192- # Helper function for _parse_help and _parse_usage.
1192+ # Helper function for _comp_compgen_help and _comp_compgen_usage.
1193+ # Obtain the help output based on the arguments.
1194+ # @param $@ args Arguments specified to the caller.
1195+ # @var[out] _lines
1196+ # @return 2 if the usage is wrong, 1 if no output is obtained, or otherwise 0.
1197+ _comp_compgen_help__get_help_lines ()
1198+ {
1199+ local -a help_cmd
1200+ case ${1-} in
1201+ -)
1202+ if (( $# > 1 )) ; then
1203+ printf ' bash_completion: %s -: extra arguments for -\n' " ${FUNCNAME[1]} " >&2
1204+ printf ' usage: %s -\n' " ${FUNCNAME[1]} " >&2
1205+ printf ' usage: %s -c cmd args...\n' " ${FUNCNAME[1]} " >&2
1206+ printf ' usage: %s [-- args...]\n' " ${FUNCNAME[1]} " >&2
1207+ return 2
1208+ fi
1209+ help_cmd=(exec cat)
1210+ ;;
1211+ -c)
1212+ if (( $# < 2 )) ; then
1213+ printf ' bash_completion: %s -c: no command is specified\n' " ${FUNCNAME[1]} " >&2
1214+ printf ' usage: %s -\n' " ${FUNCNAME[1]} " >&2
1215+ printf ' usage: %s -c cmd args...\n' " ${FUNCNAME[1]} " >&2
1216+ printf ' usage: %s [-- args...]\n' " ${FUNCNAME[1]} " >&2
1217+ return 2
1218+ fi
1219+ help_cmd=(" ${@: 2} " )
1220+ ;;
1221+ --) shift 1 ;&
1222+ * )
1223+ local ret
1224+ _comp_dequote " ${comp_args[0]-} " || ret=${comp_args[0]-}
1225+ help_cmd=(" ${ret:- false} " " $@ " )
1226+ ;;
1227+ esac
1228+
1229+ local ret
1230+ _comp_split -l ret " $( LC_ALL=C " ${help_cmd[@]} " 2>&1 ) " &&
1231+ _lines=(" ${ret[@]} " )
1232+ }
1233+
1234+ # Helper function for _comp_compgen_help and _comp_compgen_usage.
1235+ # @var[in,out] _options Add options
11931236# @return True (0) if an option was found, False (> 0) otherwise
1194- # TODO: rename per API conventions, rework to use vars rather than outputting
1195- __parse_options ()
1237+ _comp_compgen_help__parse ()
11961238{
11971239 local option option2 i
11981240
@@ -1218,108 +1260,89 @@ __parse_options()
12181260 if [[ $option =~ (\[ (( no| dont)-? )\]). ]]; then
12191261 option2 =${option/ " ${BASH_REMATCH[1]} " / }
12201262 option2 =${option2%% [<{().[]* }
1221- printf '%s\n' "${option2/ =*/ =} "
1263+ _options+=( "${option2/ =*/ =} ")
12221264 option=${option/ " ${BASH_REMATCH[1]} " / " ${BASH_REMATCH[2]} " }
12231265 fi
12241266
12251267 option=${option%% [<{().[]* }
12261268 option=${option/ =*/ =}
12271269 [[ $option ]] || return 1
12281270
1229- printf '%s\n' "$option "
1271+ _options+=( "$option ")
12301272}
12311273
1232- # Parse GNU style help output of the given command.
1233- # @param $1 command; if "-", read from stdin and ignore rest of args
1234- # @param $2 command options (default: --help)
1274+ # Parse GNU style help output of the given command and generate and store
1275+ # completions in an array. The help output is produced in the way depending on
1276+ # the usage:
1277+ # usage: _comp_compgen_help - # read from stdin
1278+ # usage: _comp_compgen_help -c cmd args... # run "cmd args..."
1279+ # usage: _comp_compgen_help [[--] args...] # run "${comp_args[0]} args..."
1280+ # When no arguments are specified, `--help` is assumed.
12351281#
1236- # TODO: rename per API conventions, rework to use vars rather than outputting
1237- _parse_help ()
1282+ # @var[in] comp_args[ 0 ]
1283+ _comp_compgen_help ()
12381284{
1239- local IFS=$' \t\n'
1240- local reset_monitor=$(shopt -po monitor) reset_lastpipe=$(shopt -p lastpipe) reset_noglob=$(shopt -po noglob)
1241- set +o monitor
1242- shopt -s lastpipe
1243- set -o noglob
1285+ (($# )) || set -- -- --help
12441286
1245- local cmd=$1
1246- local line rc=1
1247- (
1248- case $cmd in
1249- -) exec cat ;;
1250- *)
1251- # shellcheck disable=SC2086
1252- _comp_dequote "$cmd " && LC_ALL=C "$ret " ${2:- --help} 2 >& 1
1253- ;;
1254- esac
1255- ) |
1256- while read -r line; do
1257-
1258- [[ $line == *([[:blank:]])-* ]] || continue
1259- # transform "-f FOO, --foo=FOO" to "-f , --foo=FOO" etc
1260- while [[ $line =~ ((^|[^-])-[A-Za-z0 -9 ?][[:space:]]+)\[?[A-Z0 -9 ]+([,_-]+[A-Z0 -9 ]+)?(\.\.+)?\]? ]]; do
1261- line=${line/ " ${BASH_REMATCH[0]} " / " ${BASH_REMATCH[1]} " }
1262- done
1263- __parse_options "${line// or / , } " && rc= 0
1287+ local -a _lines
1288+ _comp_compgen_help__get_help_lines " $@ " || return " $? "
12641289
1290+ local -a _options=()
1291+ local _line
1292+ for _line in " ${_lines[@]} " ; do
1293+ [[ $_line == * ([[:blank:]])-* ]] || continue
1294+ # transform "-f FOO, --foo=FOO" to "-f , --foo=FOO" etc
1295+ while [[ $_line =~ (( ^| [^- ])- [A- Za- z0 - 9 ? ][[: space: ]]+ )\[? [A- Z0 - 9 ]+ ([, _- ]+ [A- Z0 - 9 ]+ )? (\.\.+ )? \]? ]]; do
1296+ _line=${_line/ " ${BASH_REMATCH[0]} " / " ${BASH_REMATCH[1]} " }
12651297 done
1298+ _comp_compgen_help__parse "${_line// or / , } "
1299+ done
1300+ ((${# _options[@]} )) || return 1
12661301
1267- $reset_monitor
1268- $reset_lastpipe
1269- $reset_noglob
1270- return $rc
1302+ _comp_compgen -- -W ' "${_options[@]}"'
1303+ return 0
12711304}
12721305
1273- # Parse BSD style usage output (options in brackets) of the given command.
1274- # @param $1 command; if "-", read from stdin and ignore rest of args
1275- # @param $2 command options (default: --usage)
1276- #
1277- # TODO: rename per API conventions, rework to use vars rather than outputting
1278- _parse_usage()
1279- {
1280- local IFS=$' \t\n'
1281- local reset_monitor=$(shopt -po monitor) reset_lastpipe=$(shopt -p lastpipe) reset_noglob=$(shopt -po noglob)
1282- set +o monitor
1283- shopt -s lastpipe
1284- set -o noglob
1285-
1286- local cmd=$1
1287- local line match option i char rc=1
1288- (
1289- case $cmd in
1290- -) exec cat ;;
1291- *)
1292- # shellcheck disable=SC2086
1293- _comp_dequote "$cmd " && LC_ALL=C "$ret " ${2:- --usage} 2 >& 1
1294- ;;
1295- esac
1296- ) |
1297- while read -r line; do
1298-
1299- while [[ $line =~ \[[[:space:]]*(-[^]]+)[[:space:]]*\] ]]; do
1300- match=${BASH_REMATCH[0]}
1301- option=${BASH_REMATCH[1]}
1302- case $option in
1303- -?(\[)+([a-zA-Z0 -9 ?]))
1304- # Treat as bundled short options
1305- for (( i = 1 ; i < ${# option} ; i++ )) ; do
1306- char=${option: i: 1}
1307- [[ $char != ' [' ]] && printf ' %s\n' -" $char " && rc=0
1308- done
1309- ;;
1310- * )
1311- __parse_options " $option " && rc= 0
1312- ;;
1313- esac
1314- line= ${line#* " $match " }
1315- done
1316-
1306+ # Parse BSD style usage output (options in brackets) of the given command. The
1307+ # help output is produced in the way depending on the usage:
1308+ # usage: _comp_compgen_usage - # read from stdin
1309+ # usage: _comp_compgen_usage -c cmd args... # run "cmd args..."
1310+ # usage: _comp_compgen_usage [[--] args...] # run "${comp_args[0]} args..."
1311+ # When no arguments are specified, `--usage` is assumed.
1312+ #
1313+ # @var[in] comp_args[0]
1314+ _comp_compgen_usage ()
1315+ {
1316+ (( $# )) || set -- -- --usage
1317+
1318+ local -a _lines
1319+ _comp_compgen_help__get_help_lines " $@ " || return " $? "
1320+
1321+ local -a _options=()
1322+ local _line _match _option _i _char
1323+ for _line in " ${_lines[@]} " ; do
1324+ while [[ $_line =~ \[ [[:space:]]* (-[^]]+)[[:space:]]* \] ]]; do
1325+ _match=${BASH_REMATCH[0]}
1326+ _option=${BASH_REMATCH[1]}
1327+ case $_option in
1328+ -? (\[ )+ ([a-zA-Z0-9? ]))
1329+ # Treat as bundled short options
1330+ for (( _i = 1 ; _i < ${# _option} ; _i++ )) ; do
1331+ _char=${_option: _i: 1}
1332+ [[ $_char != ' [' ]] && _options+=(" -$_char " )
1333+ done
1334+ ;;
1335+ * )
1336+ _comp_compgen_help__parse " $_option "
1337+ ;;
1338+ esac
1339+ _line=${_line#* " $_match " }
13171340 done
1341+ done
1342+ (( ${# _options[@]} )) || return 1
13181343
1319- $reset_monitor
1320- $reset_lastpipe
1321- $reset_noglob
1322- return $rc
1344+ _comp_compgen -- -W ' "${_options[@]}"'
1345+ return 0
13231346}
13241347
13251348# This function completes on signal names (minus the SIG prefix)
0 commit comments