reubeno / brush

bash/POSIX-compatible shell implemented in Rust
MIT License
20 stars 4 forks source link

Lots of issues in Arch Linux's bash completion #222

Closed Quackdoc closed 4 days ago

Quackdoc commented 6 days ago

Syntax error in arch's bash_completion, the tar files can be found here for them https://archive.archlinux.org/packages/b/bash-completion/

one such issue is from below

                if [[ ! $OPTARG ]]; then
                    printf 'bash_completion: %s: -%s: invalid command name `%s'\''\n' "$FUNCNAME" "$_opt" "$OPTARG" >&2
                    return 2
                elif [[ $_icmd ]]; then
                    printf 'bash_completion: %s: -%s: `-i %s'\'' is already specified\n' "$FUNCNAME" "$_opt" "$_icmd" >&2
                    return 2
                elif [[ $_xcmd ]]; then
                    printf 'bash_completion: %s: -%s: `-x %s'\'' is already specified\n' "$FUNCNAME" "$_opt" "$_xcmd" >&2
                    return 2
                fi
                ;;&

the full function is

function ```sh _comp_compgen() { local _append= local _var= local _cur=${_comp_compgen__cur-${cur-}} local _dir="" local _ifs=$' \t\n' _has_ifs="" local _icmd="" _xcmd="" local -a _upvars=() local _old_nocasematch="" if shopt -q nocasematch; then _old_nocasematch=set shopt -u nocasematch fi local OPTIND=1 OPTARG="" OPTERR=0 _opt while getopts ':av:U:Rc:C:lF:i:x:' _opt "$@"; do case $_opt in a) _append=set ;; v) if [[ $OPTARG == @(*[^_a-zA-Z0-9]*|[0-9]*|''|_*|IFS|OPTIND|OPTARG|OPTERR|cur) ]]; then printf 'bash_completion: %s: -v: invalid array name `%s'\''\n' "$FUNCNAME" "$OPTARG" >&2 return 2 fi _var=$OPTARG ;; U) if [[ $OPTARG == @(*[^_a-zA-Z0-9]*|[0-9]*|'') ]]; then printf 'bash_completion: %s: -U: invalid variable name `%s'\''\n' "$FUNCNAME" "$OPTARG" >&2 return 2 elif [[ $OPTARG == @(_*|IFS|OPTIND|OPTARG|OPTERR|cur) ]]; then printf 'bash_completion: %s: -U: unnecessary to mark `%s'\'' as upvar\n' "$FUNCNAME" "$OPTARG" >&2 return 2 fi _upvars+=("$OPTARG") ;; c) _cur=$OPTARG ;; R) _cur="" ;; C) if [[ ! $OPTARG ]]; then printf 'bash_completion: %s: -C: invalid directory name `%s'\''\n' "$FUNCNAME" "$OPTARG" >&2 return 2 fi _dir=$OPTARG ;; l) _has_ifs=set _ifs=$'\n' ;; F) _has_ifs=set _ifs=$OPTARG ;; [ix]) if [[ ! $OPTARG ]]; then printf 'bash_completion: %s: -%s: invalid command name `%s'\''\n' "$FUNCNAME" "$_opt" "$OPTARG" >&2 return 2 elif [[ $_icmd ]]; then printf 'bash_completion: %s: -%s: `-i %s'\'' is already specified\n' "$FUNCNAME" "$_opt" "$_icmd" >&2 return 2 elif [[ $_xcmd ]]; then printf 'bash_completion: %s: -%s: `-x %s'\'' is already specified\n' "$FUNCNAME" "$_opt" "$_xcmd" >&2 return 2 fi ;;& i) _icmd=$OPTARG ;; x) _xcmd=$OPTARG ;; *) printf 'bash_completion: %s: usage error\n' "$FUNCNAME" >&2 return 2 ;; esac done [[ $_old_nocasematch ]] && shopt -s nocasematch shift "$((OPTIND - 1))" if (($# == 0)); then printf 'bash_completion: %s: unexpected number of arguments\n' "$FUNCNAME" >&2 printf 'usage: %s [-alR|-F SEP|-v ARR|-c CUR] -- ARGS...' "$FUNCNAME" >&2 return 2 fi if [[ ! $_var ]]; then # Inherit _append and _var only when -v var is unspecified. _var=${_comp_compgen__var-COMPREPLY} [[ $_append ]] || _append=${_comp_compgen__append-} fi if [[ $1 != -* ]]; then # usage: _comp_compgen [options] NAME args if [[ $_has_ifs ]]; then printf 'bash_completion: %s: `-l'\'' and `-F sep'\'' are not supported for generators\n' "$FUNCNAME" >&2 return 2 fi local -a _generator if [[ $_icmd ]]; then _generator=("_comp_cmd_${_icmd//[^a-zA-Z0-9_]/_}__compgen_$1") elif [[ $_xcmd ]]; then _generator=(_comp_xfunc "$_xcmd" "compgen_$1") else _generator=("_comp_compgen_$1") fi if ! declare -F -- "${_generator[0]}" &>/dev/null; then printf 'bash_completion: %s: unrecognized generator `%s'\'' (function %s not found)\n' "$FUNCNAME" "$1" "${_generator[0]}" >&2 return 2 fi ((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}" if [[ $_dir ]]; then local _original_pwd=$PWD local PWD=${PWD-} OLDPWD=${OLDPWD-} # Note: We also redirect stdout because `cd` may output the target # directory to stdout when CDPATH is set. command cd -- "$_dir" &>/dev/null || { _comp_compgen__error_fallback return } fi local _comp_compgen__append=$_append local _comp_compgen__var=$_var local _comp_compgen__cur=$_cur cur=$_cur # Note: we use $1 as a part of a function name, and we use $2... as # arguments to the function if any. # shellcheck disable=SC2145 "${_generator[@]}" "${@:2}" local _status=$? # Go back to the original directory. # Note: Failure of this line results in the change of the current # directory visible to the user. We intentionally do not redirect # stderr so that the error message appear in the terminal. # shellcheck disable=SC2164 [[ $_dir ]] && command cd -- "$_original_pwd" return "$_status" fi # usage: _comp_compgen [options] -- [compgen_options] if [[ $_icmd || $_xcmd ]]; then printf 'bash_completion: %s: generator name is unspecified for `%s'\''\n' "$FUNCNAME" "${_icmd:+-i $_icmd}${_xcmd:+x $_xcmd}" >&2 return 2 fi # Note: $* in the below checks would be affected by uncontrolled IFS in # bash >= 5.0, so we need to set IFS to the normal value. The behavior in # bash < 5.0, where unquoted $* in conditional command did not honor IFS, # was a bug. # Note: Also, ${_cur:+-- "$_cur"} and ${_append:+-a} would be affected by # uncontrolled IFS. local IFS=$' \t\n' # Note: extglob *\$?(\{)[0-9]* can be extremely slow when the string # "${*:2:_nopt}" becomes longer, so we test \$[0-9] and \$\{[0-9] # separately. if [[ $* == *\$[0-9]* || $* == *\$\{[0-9]* ]]; then printf 'bash_completion: %s: positional parameter $1, $2, ... do not work inside this function\n' "$FUNCNAME" >&2 return 2 fi local _result _result=$( if [[ $_dir ]]; then # Note: We also redirect stdout because `cd` may output the target # directory to stdout when CDPATH is set. command cd -- "$_dir" &>/dev/null || return fi IFS=$_ifs compgen "$@" ${_cur:+-- "$_cur"} ) || { _comp_compgen__error_fallback return } ((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}" _comp_split -l ${_append:+-a} "$_var" "$_result" } ```
reubeno commented 6 days ago

Thanks for reporting this, @Quackdoc . Our experience has been that the various versions of bash-completion contain the most complex bash logic around. Looking at the snippet you reference, I'm wondering if the & after the ;; is tripping us up.

Do you know which specific version of the bash-completion package you happened to test with? And what triggered the error (e.g., just loading bash-completion, completing a specific command)?

Thanks!

reubeno commented 5 days ago

FYI, I tried sourceing bash_completion from bash-completion 2.14.0; the error reproduced easily:

brush$ source ./bash_completion 
ERROR ./bash_completion: syntax error near token `&' (line 615 col 19)

Sure enough, that line + col identifies the ;;& that was added in https://github.com/scop/bash-completion/commit/39cc200f9e6d9b03d1e76a924371f85304e786d5; that made it into bash-completion 2.12. The latest testing we'd previously done was on bash-completion 2.11.

We'll need to add support for ;;& (as well as ;& while we're at it). Beyond that, it sounds like we should conduct additional testing on more recent releases of bash-completion.

reubeno commented 5 days ago

Well, this was a good test case! There were 2 parsing errors--1 for a non-POSIX bashism, and 1 for a case that was missed in parenthesized operator chars showing up in regexes (in extended tests). They should both be fixed now.

I continued on with testing and found (and addressed) a few runtime errors with the latest released version of bash-completion (2.14.0), which is the version that Arch is on (according to repology). I'm sure there could be additional issues lurking, but my one-off tests are yielding much better results.

@Quackdoc -- if you get a chance would you be willing to try this scenario again with the latest changes in this repo? If the original error is resolved, I'd like to close this issue and track any subsequent new issues separately.

Quackdoc commented 4 days ago

I installed git and the issues are all solved that I was hitting :D Closing issue.