Skip to content

Latest commit

 

History

History
166 lines (126 loc) · 4.16 KB

Workarounds.md

File metadata and controls

166 lines (126 loc) · 4.16 KB

Workarounds

The generated option parser code contains workarounds for several shell bugs. The reason why those codes are needed is not easy to understand and is explained here. If you do not need these workarounds, you can remove them manually.

# e.g.
eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'}
eval '[ ${OPTARG+x} ] &&:' && OPTARG=1 || OPTARG=-1
$1*) OPTARG="$OPTARG --flag"
VERBOSE="$((${VERBOSE:-0}+$OPTARG))"

1. ${1+'"$@"'}

eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'}

# Equivalent to the following
set -- "${OPTARG%%\=*}" "${OPTARG#*\=}" "$@"

This is a well-known workaround the problem of using set -u for some shells. See What does ${1+"$@"} mean.

Oh wait, there's another meaning It's also a workaround for a problem in some shells if you want to use the flag u ("error upon using empty variables") and "$@" (or $@, "$*", $*) is used without arguments. Example:

$ shell -cu 'echo     "$@";  echo not reached'
@: parameter not set
$ shell -cu 'echo ${1+"$@"}; echo ok'
ok

At least these shells need that workaround:

  • bash-4.0.0 ... -4.0.27
  • dash-0.4.6 ... -0.4.18
  • all ksh88
  • ksh93 until release t+20090501
  • pdksh-5.1.3, -5.2.14
  • mksh before R39 (as pdksh descendant)
  • posh < 0.10 (as pdksh descendant)
  • NetBSD 2 ff. /bin/sh
  • all traditional Bourne shells

NOTE: In NetBSD ksh on NetBSD 9.0 and posh 0.14.1, this problem still exists.

${1+"$@"} vs ${1+'"$@"'}

However, this workaround does not work properly with old zsh 3.x, so we are using a modified version.

$ echo $ZSH_VERSION
3.1.9
$ set -- a b c; printf '[%s] ' ${1+"$@"}; echo
[a b c]
# correct: [a] [b] [c]

Modified version

eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'}

# if $1 is present, then
=> eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' '"$@"'
=> set -- "${OPTARG%%\=*}" "${OPTARG#*\=}" "$@"

# if $1 is not present, then
=> eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"'
=> set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"

2. eval '[ ${OPTARG+x} ]'

eval '[ ${OPTARG+x} ]' && OPTARG=1 || OPTARG=-1

This is a workaround for ksh 93u+. The code without the workaround is the following.

[ ${OPTARG+x} ] && OPTARG=1 || OPTARG=-1

The reproduction code is as follows. The eval avoids this problem.

#!/usr/bin/ksh
set -eu 1 2
VAR='var'
while [ $# -gt 0 ]; do
  [ $# -eq 1 ] && unset VAR  # unset VAR in the last loop
  if [ "${VAR+x}" ]; then    # if variable VAR is set

    # The last loop will be executed even though the variable VAR is not set
    echo "$VAR" # => [error] VAR: parameter not set

  fi
  shift
done

3. eval '[ ... ] &&:'

eval '[ ${OPTARG+x} ] &&:' && OPTARG=1 || OPTARG=-1

This &&: is a workaround for OpenBSD ksh.

The reproduction code is as follows.

set -eu
eval "[ ]"     && v=1 || v=2 # it should not exit with failure, but it does
eval "[ ] &&:" && v=1 || v=2 # it works as expected

4. $1*)

The $1 in this code should be enclosed in double quotes. If $1 contains a pattern string, it will cause incorrect behavior.

set -- "???"
case foo in
  "$1"*) echo "it doesn't match" ;; # correct
  $1*) echo "it matches" ;; # wrong
esac

However, posh 0.10.2, 0.12.6 doesn't work properly when variables are enclosed in double quotes.

#!/usr/bin/posh
set -- "foo"
case foo in
  "$1"*) echo "ok" ;;
  *) echo "not ok" >&2; exit 1 ;; # posh 0.10.2, 0.12.6
esac

Therefore, it cannot be enclosed in double quotes, but it would be in trouble if $1 contains a pattern, so we check it beforehand.

case $1 in (*[!a-zA-Z0-9_-]*) break; esac

This workaround has a very limited number of affected shells and limits the characters that can be used as long options, so it may be removed in the future.

5. $((${VERBOSE:-0}+$OPTARG))

There is no need to use $ to refer to simple variables in arithmetic expressions.

VERBOSE="$((${VERBOSE:-0}+OPTARG))"

However, older shells (e.g. dash 0.5.2) require $. The affected shells are limited and quite old, but we use $ because it doesn't cause any problems.