shellspec/shellspec

View on GitHub
lib/general.sh

Summary

Maintainability
Test Coverage
C
73%
#shellcheck shell=sh disable=SC2004,SC2016

# Workaround for ksh 2020
if [ "${KSH_VERSION:-}" ]; then
  ( exec 3>/dev/null 4>&3 5>&3 6>&3 7>&3 8>&3 9>&3
    eval "dummy() { $(printf %8192s :); }"
  ) 3>&- 4>&- 5>&- 6>&- 7>&- 8>&- 9>&-
fi

: "${SHELLSPEC_SHELL_TYPE:=}" "${SHELLSPEC_SHELL_VERSION:=}"
SHELLSPEC_SH_VERSION=$(eval 'echo "${.sh.version}"' 2>/dev/null) ||:
: "${SHELLSPEC_SH_VERSION:=${SH_VERSION:-}}"
: "${SHELLSPEC_SH_VERSION:=${NETBSD_SHELL:-}}"

shellspec_shell_info() {
  SHELLSPEC_SHELL_TYPE='sh'
  shellspec_shell_version bash BASH_VERSION && return 0
  shellspec_shell_version zsh  ZSH_VERSION  && return 0
  shellspec_shell_version yash YASH_VERSION && return 0
  shellspec_shell_version posh POSH_VERSION && return 0
  shellspec_shell_version oil OIL_VERSION && return 0
  if shellspec_shell_version ksh KSH_VERSION; then
    case $SHELLSPEC_SHELL_VERSION in
      *MIRBSD* ) SHELLSPEC_SHELL_TYPE=mksh ;;
      *LEGACY* ) SHELLSPEC_SHELL_TYPE=lksh ;;
      *PD\ KSH*) SHELLSPEC_SHELL_TYPE=pdksh ;;
    esac
  else
    SHELLSPEC_SHELL_VERSION=$SHELLSPEC_SH_VERSION
    case $SHELLSPEC_SHELL_VERSION in
      *BUILD*  ) SHELLSPEC_SHELL_TYPE="sh" ;;
      *PD\ KSH*) SHELLSPEC_SHELL_TYPE="pdksh" ;;
      *pbosh*  ) SHELLSPEC_SHELL_TYPE="pbosh" ;;
      *bosh*   ) SHELLSPEC_SHELL_TYPE="bosh" ;;
      ?*       ) SHELLSPEC_SHELL_TYPE="ksh" ;;
    esac
  fi
}

shellspec_shell_version() {
  SHELLSPEC_SHELL_TYPE='sh' SHELLSPEC_SHELL_VERSION=''
  eval "[ \${$2+x} ] &&:" || return 1
  eval "SHELLSPEC_SHELL_TYPE=\$1 SHELLSPEC_SHELL_VERSION=\${$2}"
}

shellspec_shell_info

shellspec_constants() {
  set -- "${1:-}"

  SHELLSPEC_EVAL="
    ${1}SOH='\\001' ${1}STX='\\002' ${1}ETX='\\003' ${1}EOT='\\004' \
    ${1}ENQ='\\005' ${1}ACK='\\006' ${1}BEL='\\007' ${1}BS='\\010' \
    ${1}HT='\\011'  ${1}TAB='\\011' ${1}LF='\\012'  ${1}VT='\\013' \
    ${1}FF='\\014'  ${1}CR='\\015'  ${1}SO='\\016'  ${1}SI='\\017' \
    ${1}DLE='\\020' ${1}DC1='\\021' ${1}DC2='\\022' ${1}DC3='\\023' \
    ${1}DC4='\\024' ${1}NAK='\\025' ${1}SYN='\\026' ${1}ETB='\\027' \
    ${1}CAN='\\030' ${1}EM='\\031'  ${1}SUB='\\032' ${1}ESC='\\033' \
    ${1}FS='\\034'  ${1}GS='\\035'  ${1}RS='\\036'  ${1}US='\\037' \
    "
  # shellcheck disable=SC2059
  eval "$("$SHELLSPEC_PRINTF" "$SHELLSPEC_EVAL")"

  # Workaround: Variable CR is empty on MSYS
  if eval "[ \${#${1}CR} -eq 0 ] &&:"; then
    set -- "$1" "$("$SHELLSPEC_PRINTF" '\015_')"
    eval "${1}CR=\${2%_}"
  fi
}

shellspec_constants SHELLSPEC_
# shellcheck disable=SC2153,SC2034
SHELLSPEC_IFS=" ${SHELLSPEC_TAB}${SHELLSPEC_LF}"

if [ "$SHELLSPEC_DEFECT_EMPTYPARAMS" ]; then
  shellspec_proxy() {
    eval "$1() { case \$# in (0) $2 ;; (*) $2 \"\$@\" ;; esac; }"
  }
else
  shellspec_proxy() {
    eval "$1() { $2 \"\$@\"; }"
  }
fi

shellspec_import() {
  shellspec_find_module "$SHELLSPEC_LOAD_PATH" "$1" "SHELLSPEC_SOURCE"
  shift
  if [ $# -eq 0 ]; then
    shellspec_source "$SHELLSPEC_SOURCE"
  else
    shellspec_source "$SHELLSPEC_SOURCE" "$@"
  fi
}

shellspec_resolve_module_path() {
  shellspec_find_module "$SHELLSPEC_LOAD_PATH" "$2" "$1"
}

shellspec_module_exists() {
  set -- "$1" "${2:-$SHELLSPEC_LOAD_PATH}"
  [ -e "${2%%$SHELLSPEC_PATHSEP*}/$1.sh" ] && return 0
  [ -e "${2%%$SHELLSPEC_PATHSEP*}/$1/$1.sh" ] && return 0
  case $2 in (*$SHELLSPEC_PATHSEP*)
    shellspec_module_exists "$1" "${2#*$SHELLSPEC_PATHSEP}" &&:
    return $?
  esac
  return 1
}

shellspec_find_module() {
  if [ -e "${1%%$SHELLSPEC_PATHSEP*}/$2.sh" ]; then
    eval "$3=\${1%%\$SHELLSPEC_PATHSEP*}/\$2.sh"
    return 0
  fi
  if [ -e "${1%%$SHELLSPEC_PATHSEP*}/$2/$2.sh" ]; then
    eval "$3=\${1%%\$SHELLSPEC_PATHSEP*}/\$2/\$2.sh"
    return 0
  fi
  case $1 in (*$SHELLSPEC_PATHSEP*)
    shellspec_find_module "${1#*$SHELLSPEC_PATHSEP}" "$2" "$3"
    return 0
  esac
  shellspec_error "The required module '$2' is not found in SHELLSPEC_LOAD_PATH."
}

shellspec_source() {
  SHELLSPEC_SOURCE=$1
  shift
  # shellcheck disable=SC1090
  . "$SHELLSPEC_SOURCE"
}

if [ "${ZSH_VERSION:-}" ]; then
  shellspec_find_files() {
    if eval '[[ -o nonomatch ]]'; then
      shellspec_find_files_ "$@"
    else
      setopt nonomatch
      shellspec_find_files_ "$@"
      unsetopt nonomatch
    fi
  }
else
  shellspec_proxy shellspec_find_files shellspec_find_files_
fi
shellspec_find_files_() {
  SHELLSPEC_EVAL="
    shift; \
    while [ \$# -gt 0 ]; do \
      case \$1 in (*/[*]) shift; continue; esac; \
      if [ -d \"\$1\" ]; then \
        shellspec_find_files__ \"$1\" \"\${1%/}\"; \
      else \
        \"$1\" \"\$1\"; \
      fi; \
      shift; \
    done \
  "
  eval "$SHELLSPEC_EVAL"
}
shellspec_find_files__() {
  SHELLSPEC_EVAL="
    SHELLSPEC_IFSORIG=\$IFS; IFS=''; set -- \$2/*; IFS=\$SHELLSPEC_IFSORIG; \
    if [ \$# -gt 0 ]; then shellspec_find_files_ \"$1\" \"\$@\"; fi
  "
  eval "$SHELLSPEC_EVAL"
}
# Workaround for posh 0.10.2. do not glob with set -u.
if [ "${POSH_VERSION:-}" ] && [ "$(set -u; echo /*)" = '/*' ]; then
  shellspec_find_files__() {
    SHELLSPEC_EVAL="
      SHELLSPEC_IFSORIG=\$IFS; IFS=''; \
      case \$- in (*u*) set +u ;; esac; \
      set -- \$2/*; set -u; IFS=\$SHELLSPEC_IFSORIG; \
      if [ \$# -gt 0 ]; then shellspec_find_files_ \"$1\" \"\$@\"; fi \
    "
    eval "$SHELLSPEC_EVAL"
  }
fi

shellspec_printf() {
  eval '"$SHELLSPEC_PRINTF"' ${1+'"$@"'}
}

# `echo` not has portability, and external 'printf' command is slow.
# Use shellspec_puts or shellspec_putsn replacement of 'echo'.
# Those commands output arguments as it. (not interpret -n and escape sequence)
shellspec_puts() {
  [ "$SHELLSPEC_BUILTIN_PRINTF" ] && return 0
  if [ "$SHELLSPEC_BUILTIN_PRINT" ]; then
    [ "${ZSH_VERSION:-}" ] && return 1 || return 2
  fi
  if [ "${POSH_VERSION:-}" ]; then
    [ "${1#*\\}" ] && return 3 || return 4
  fi
  return 9
}
shellspec_puts "\\" &&:
case $? in
  0 | 9)
    # Use built-in or external 'printf'.
    shellspec_puts() {
      IFS=" $IFS"; "$SHELLSPEC_PRINTF" '%s' "${*:-}"; IFS=${IFS#?}
    }
    shellspec_putsn() {
      IFS=" $IFS"; "$SHELLSPEC_PRINTF" '%s\n' "${*:-}"; IFS=${IFS#?}
    }
    ;;
  1)
    # zsh 3.1.9, 4.0.4
    shellspec_puts() { builtin print -nr -- "${@:-}"; }
    shellspec_putsn() { builtin print -r -- "${@:-}"; }
    ;;
  2)
    # ksh88, mksh (depends on compile option), OpenBSD ksh (loksh, oksh), pdksh
    shellspec_puts() { command print -nr -- "${@:-}"; }
    shellspec_putsn() { command print -r -- "${@:-}"; }
    ;;
  3)
    # posh 0.3.14, 0.5.4 + workaround for parameter expansion bug
    shellspec_puts() {
      if [ $# -eq 1 ] && [ "$1" = "-n" ]; then
        builtin echo -n -; builtin echo -n n; return 0
      fi
      IFS=" $IFS"; set -- "${*:-}\\" ""; IFS=${IFS#?}
      while [ "$1" ]; do set -- "${1#*\\\\}" "$2${2:+\\\\}${1%%\\\\*}"; done
      builtin echo -n "$2"
    }
    shellspec_putsn() { [ $# -gt 0 ] && shellspec_puts "$@"; builtin echo; }
    ;;
  4)
    # posh
    shellspec_puts() {
      if [ $# -eq 1 ] && [ "$1" = "-n" ]; then
        builtin echo -n -; builtin echo -n n; return 0
      fi
      IFS=" $IFS"; set -- "${*:-}\\" ""; IFS=${IFS#?}
      while [ "$1" ]; do set -- "${1#*\\}" "$2${2:+\\\\}${1%%\\*}"; done
      builtin echo -n "$2"
    }
    shellspec_putsn() { [ $# -gt 0 ] && shellspec_puts "$@"; builtin echo; }
    ;;
esac

shellspec_error() { shellspec_putsn "$*" >&2; exit 1; }

# $1: callback, $2-: parameters
shellspec_each() {
  [ $# -gt 1 ] || return 0
  eval "shift; shellspec_each_ $1 1 \"\$@\""
}
shellspec_each_() {
  eval "$1 \"\${$(($2 + 2))}\" $2 $(($# - 2))"
  [ "$2" -lt "$(($# - 2))" ] || return 0
  eval "shift 2; shellspec_each_ $1 $(($2 + 1)) \"\$@\""
}

# $1: callback, $2: from, $3: to $4: step
shellspec_sequence() {
  if [ "$2" -lt "$3" ]; then
    shellspec_sequence_ "$1" "$2" "$3" "${4:-1}" -le
  else
    shellspec_sequence_ "$1" "$2" "$3" "${4:--1}" -ge
  fi
}
shellspec_sequence_() {
  eval "[ \"$2\" \"$5\" \"$3\" ] &&:" || return 0
  "$1" "$2"
  shellspec_sequence_ "$1" $(($2 + $4)) "$3" "$4" "$5"
}

shellspec_loop() {
  [ "$2" -gt 0 ] || return 0
  "$1"
  shellspec_loop "$1" $(($2 - 1))
}

shellspec_get_line() {
  [ "$2" -le 0 ] && set -- "$1" "$2" ""
  while [ "$2" -gt 1 ]; do
    case $3 in
      *$SHELLSPEC_LF*) set -- "$1" $(($2 - 1)) "${3#*$SHELLSPEC_LF}" ;;
      *) set -- "$1" "$2" "" && break
    esac
  done
  [ "$3" ] && eval "$1=\${3%%\$SHELLSPEC_LF*}" && return 0
  unset "$1" ||:
}

shellspec_count_lines() {
  set -- "$1" "$2" 0
  while [ "$2" ]; do
    case $2 in
      *$SHELLSPEC_LF*) set -- "$1" "${2#*$SHELLSPEC_LF}" $(($3 + 1)) ;;
      *) set -- "$1" "$2" $(($3 + 1)) && break
    esac
  done
  eval "$1=\$3"
}

# $1: variable, $2: string, $3 N times
shellspec_padding() {
  eval "$1=''"
  shellspec_padding_ "$1" "$2" "$3"
}
shellspec_padding_() {
  [ "$3" -eq 0 ] && return 0
  eval "$1=\"\${$1:-}\$2\""
  shellspec_padding_ "$1" "$2" $(($3 - 1))
}

shellspec_wrap() {
  [ ! "$2" ] && eval "$1=" && return 0
  case $2 in
    *"$SHELLSPEC_LF") set -- "$1" "$2" "${3:-}" "${4:-}" "" "" ;;
    *) set -- "$1" "$2${SHELLSPEC_LF}" "${3:-}" "${4:-}" "" x ;;
  esac
  while [ "$2" ]; do
    set -- "$1" "${2#*$SHELLSPEC_LF}" "$3" "$4" \
      "$5$3${2%%$SHELLSPEC_LF*}$4${SHELLSPEC_LF}" "$6"
  done
  [ "$6" ] && set -- "$1" "$2" "$3" "$4" "${5%$SHELLSPEC_LF}"
  eval "$1=\$5"
}

if [ "$SHELLSPEC_BUILTIN_READARRAY" ]; then
  shellspec_readfile() { shellspec_readfile_bash_readarray "$@"; }
elif [ "$SHELLSPEC_SEEKABLE" ]; then
  shellspec_readfile() { shellspec_readfile_ksh_readall "$@"; }
elif [ "${ZSH_VERSION:-}" ]; then
  # shellcheck disable=SC2039
  shellspec_readfile() {
    if zmodload -e zsh/mapfile; then
      shellspec_readfile_zsh_mapfile "$@"
    elif [ "$SHELLSPEC_READ_DELIM" ]; then
      shellspec_readfile_read_delim "$@"
    elif [ "$SHELLSPEC_STRING_CONCAT" ]; then
      shellspec_readfile_zsh_concat "$@"
    else
      shellspec_readfile_zsh_array "$@"
    fi
  }
elif [ "$SHELLSPEC_READ_DELIM" ]; then
  shellspec_readfile() { shellspec_readfile_read_delim "$@"; }
elif [ "${YASH_VERSION:-}" ]; then
  shellspec_readfile() { shellspec_readfile_yash_array "$@"; }
else
  shellspec_readfile() { shellspec_readfile_posix "$@"; }
fi

# shellcheck disable=SC2039
shellspec_readfile_bash_readarray() {
  [ -e "$2" ] || { unset "$1" ||:; return 0; }
  [ -s "$2" ] || { eval "$1=''"; return 0; }
  local IFS=""
  readarray "$1" < "$2"
  eval "$1=\"\${$1[*]}\""
}

# shellcheck disable=SC2039
shellspec_readfile_ksh_readall() {
  [ -e "$2" ] || { unset "$1" ||:; return 0; }
  [ -s "$2" ] || { eval "$1=''"; return 0; }
  set -- "$1" "$2" "${LC_ALL:-}" "${LC_ALL+x}"
  LC_ALL=C
  eval '{ read -N "$(<#((EOF)))" "$1" <#((0)); } < "$2"'
  unset LC_ALL
  [ "$4" ] && LC_ALL="$3"
  return 0
}

# shellcheck disable=SC2039
shellspec_readfile_read_delim() {
  [ -e "$2" ] || { unset "$1" ||:; return 0; }
  [ -s "$2" ] || { eval "$1=''"; return 0; }
  IFS= read -r -d "" "$1" < "$2" ||:
}

# shellcheck disable=SC2039
shellspec_readfile_yash_array() {
  [ -e "$2" ] || { unset "$1" ||:; return 0; }
  [ -s "$2" ] || { eval "$1=''"; return 0; }
  typeset IFS="" shellspec_readfile_line=""
  array -- "$1"
  while read -r shellspec_readfile_line; do
    array -i -- "$1" -1 "${shellspec_readfile_line}${SHELLSPEC_LF}"
  done < "$2"
  array -i -- "$1" -1 "$shellspec_readfile_line"
  eval "$1=\"\${$1[*]}\""
}

shellspec_readfile_zsh_mapfile() {
  [ -e "$2" ] || { unset "$1" ||:; return 0; }
  [ -s "$2" ] || { eval "$1=''"; return 0; }
  eval "$1=\"\${mapfile[\$2]}\""
}

# shellcheck disable=SC2039
shellspec_readfile_zsh_concat() {
  [ -e "$2" ] || { unset "$1" ||:; return 0; }
  [ -s "$2" ] || { eval "$1=''"; return 0; }
  local shellspec_readfile_line='' shellspec_readfile_data=''
  while IFS= read -r shellspec_readfile_line; do
    shellspec_readfile_data+="${shellspec_readfile_line}${SHELLSPEC_LF}"
  done < "$2"
  shellspec_readfile_data+="$shellspec_readfile_line"
  eval "$1=\"\$shellspec_readfile_data\""
}

# shellcheck disable=SC2039
shellspec_readfile_zsh_array() {
  [ -e "$2" ] || { unset "$1" ||:; return 0; }
  [ -s "$2" ] || { eval "$1=''"; return 0; }
  local shellspec_readfile_i=1 IFS="$SHELLSPEC_LF"
  local -a shellspec_readfile_line
  while IFS= read -r "shellspec_readfile_line[$shellspec_readfile_i]"; do
    shellspec_readfile_i=$(($shellspec_readfile_i+1))
  done < "$2"
  eval "$1=\"\${shellspec_readfile_line[*]}\""
}

shellspec_readfile_posix() {
  [ -e "$2" ] || { unset "$1" ||:; return 0; }
  [ -s "$2" ] || { eval "$1=''"; return 0; }
  shellspec_readfile_var=$1 shellspec_readfile_data='' shellspec_readfile_i=1
  while IFS= read -r "shellspec_readfile_line_$shellspec_readfile_i"; do
    shellspec_readfile_data="$shellspec_readfile_data\$1_$shellspec_readfile_i\$2"
    shellspec_readfile_i=$(($shellspec_readfile_i+1))
  done < "$2"
  shellspec_readfile_data="$shellspec_readfile_data\$1_$shellspec_readfile_i"
  set -- '$shellspec_readfile_line' "$SHELLSPEC_LF" "$IFS"
  eval "shellspec_readfile_data=\"$shellspec_readfile_data\""

  IFS="\$$SHELLSPEC_LF "
  set -- "$3" $shellspec_readfile_data
  IFS="$1"
  shift 2

  eval "$shellspec_readfile_var=\"$shellspec_readfile_data\""
  IFS=" $IFS"
  eval "unset $* shellspec_readfile_var shellspec_readfile_data shellspec_readfile_i"
  IFS=${IFS#?}
}

shellspec_readfile_once() {
  eval "[ \${$1+x} ] &&:" && return 0
  shellspec_readfile "$@"
}

shellspec_capturefile() {
  shellspec_readfile "$@"
  shellspec_chomp "$@"
}

shellspec_head() {
  set -- "$1" "${2:-3}" ""
  eval "$1=''"
  while IFS= read -r "$1" && [ "$2" -gt 0 ]; do
    eval "set -- \"\$1\" \$((\$2 - 1)) \"\$3\${$1}\$SHELLSPEC_LF\""
  done &&:
  if eval "[ \"\${$1}\" ] &&:" && [ "$2" -eq 0 ]; then
    eval "$1=\"\$3...\""
  else
    eval "$1=\"\$3\${$1}\""
  fi
}

shellspec_trim() {
  SHELLSPEC_EVAL="
    $1=\${2:-}; [ \"\$$1\" ] || return 0; \
    $1=\${$1#\"\${$1%%[!\$SHELLSPEC_IFS]*}\"}; \
    $1=\${$1%\"\${$1##*[!\$SHELLSPEC_IFS]}\"}
  "
  eval "$SHELLSPEC_EVAL"
}

# $1: ret, $2: from, $3: to
# $1: ret, $2: value, $3: from, $4: to
shellspec_replace_all_fast() {
  if [ $# -lt 4 ]; then
    eval "$1=\${$1//\"\$2\"/\"\$3\"}"
  else
    eval "$1=\${2//\"\$3\"/\"\$4\"}"
  fi
}

# $1: ret, $2: from, $3: to
# $1: ret, $2: value, $3: from, $4: to
shellspec_replace_all_posix() {
  if [ $# -lt 4 ]; then
    eval "set -- \"\$1\" \"\${$1}\" \"\$2\" \"\$3\" \"\""
  else
    set -- "$1" "$2" "$3" "$4" ""
  fi
  while :; do
    case $2 in
      *"$3"*) set -- "$1" "${2#*"$3"}" "$3" "$4" "$5${2%%"$3"*}$4" ;;
      *) break ;;
    esac
  done
  eval "$1=\$5\$2"
}

# $1: ret, $2: from, $3: to
# $1: ret, $2: value, $3: from, $4: to
shellspec_replace_all_fallback() {
  [ $# -lt 4 ] && eval "set -- \"\$1\" \"\${$1}\" \"\$2\" \"\$3\""
  shellspec_meta_escape "$1" "$3"
  eval "set -- \"\$1\" \"\$2\" \"\${$1}\" \"\$4\" \"\""
  until eval "[ _\"\$2\" = _\"\${2#*$3}\" ] && $1=\$5\$2"; do
    eval "set -- \"\$1\" \"\${2#*$3}\" \"\$3\" \"\$4\" \"\$5\${2%%$3*}\$4\""
  done
}

shellspec_replace_all_multiline_() {
  [ $# -lt 5 ] && eval "set -- \"\$1\" \"\$2\" \"\${$2}\" \"\$3\" \"\$4\""
  shellspec_replace_all_multiline_check "$4"
  eval "$2=\$(shellspec_replace_all_multiline_lines \"\$@\"); $2=\${$2%_}"
}

shellspec_replace_all_multiline_check() {
  case $1 in (*$SHELLSPEC_LF*)
    echo "shellspec_replace_all_multiline: newline replacement is not supported" >&2
    exit 1
  esac
}

shellspec_replace_all_multiline_lines() {
  shellspec_puts "$3" | {
    while IFS= read -r line; do
      "$1" line "$line" "$4" "$5"
      shellspec_putsn "$line"
    done
    "$1" line "$line" "$4" "$5"
    shellspec_puts "${line}_"
  }
}

shellspec_includes_posix() {
  case $1 in (*"$2"*) ;; (*) false ;; esac
}

shellspec_starts_with_posix() {
  case $1 in ("$2"*) ;; (*) false ;; esac
}

shellspec_ends_with_posix() {
  case $1 in (*"$2") ;; (*) false ;; esac
}

# shellcheck disable=SC2154
shellspec_includes_fallback() {
  shellspec_meta_escape shellspec_includes_fallback "$2"
  eval "[ ! \"\${1#*$shellspec_includes_fallback}\" = \"\$1\" ] &&:" &&:
}

# shellcheck disable=SC2154
shellspec_starts_with_fallback() {
  shellspec_meta_escape shellspec_starts_with_fallback "$2"
  eval "[ ! \"\${1#$shellspec_starts_with_fallback*}\" = \"\$1\" ] &&:" &&:
}

# shellcheck disable=SC2154
shellspec_ends_with_fallback() {
  shellspec_meta_escape shellspec_ends_with_fallback "$2"
  eval "[ ! \"\${1%*$shellspec_ends_with_fallback}\" = \"\$1\" ] &&:" &&:
}

shellspec_meta_escape() {
  # shellcheck disable=SC1003
  if [ "${1#*\?}" ]; then
    # posh <= 0.5.4
    set -- '\\\\:\\\\\\\\' '\\\[:[[]' '\\\?:[?]' '\\\*:[*]' '\\\$:[$]'
  elif [ "${2%%\\*}" ]; then
    # bosh = all (>= 20181007), busybox <= 1.22.0
    set -- '\\\\:\\\\\\\\' '\[:[[]' '\?:[?]' '\*:[*]' '\$:[$]'
  else
    # POSIX compliant
    set -- '\\:\\\\' '\[:[[]' '\?:[?]' '\*:[*]' '\$:[$]' '\#:[#]' '\~:[~]'
  fi

  set "$@" '\(:\\(' '\):\\)' '\|:\\|' '\":\\\"' '\`:\\\`' '\{:\\{' '\}:\\}' \
    "\\':\\\\'" '\ :\\ ' '\&:\\&' '\=:\\=' '\<:\\<' '\>:\\>' '\;:\\;' '\^:\\^'\
    '${SHELLSPEC_LF}:\${SHELLSPEC_LF}' '${SHELLSPEC_TAB}:\${SHELLSPEC_TAB}' \
    '${SHELLSPEC_CR}:\${SHELLSPEC_CR}' '${SHELLSPEC_VT}:\${SHELLSPEC_VT}' end

  echo 'shellspec_meta_escape() { set -- "$1" "$2" ""'
  until [ "$1" = "end" ] && shift && "$SHELLSPEC_PRINTF" '%s\n' "$@"; do
    set -- "${1%:*}" "${1#*:}" "$@"
    set -- "$@" 'until [ _"$2" = _"${2#*'"$1"'}" ] && set -- "$1" "$3$2" ""; do'
    set -- "$@" '  set -- "$1" "${2#*'"$1"'}" "$3${2%%'"$1"'*}'"$2"'"'
    set -- "$@" 'done'
    shift 3
  done
  echo 'eval "$1=\"\$2\""; }'
}
eval "$(shellspec_meta_escape "a?" "\\")"

shellspec_replace_all() {
  (eval 'v="*#*/" p="#*/"; [ "${v//"$p"/-}" = "*-" ]') 2>/dev/null && return 0
  [ "${1#"$2"}" = "a*b" ] && return 1 || return 2
}
eval 'shellspec_replace_all "a*b" "a[*]" &&:' &&:
case $? in
  0)
    # Fast version (Not POSIX compliant)
    # ash(busybox)>=1.30.1, bash>=3.1.17, dash>=none, ksh>=93?, mksh>=54
    # yash>=?, zsh>=?, pdksh=none, posh=none, bosh=none
    shellspec_replace_all() { shellspec_replace_all_fast "$@"; }
    shellspec_includes() { shellspec_includes_posix "$@"; }
    shellspec_starts_with() { shellspec_starts_with_posix "$@"; }
    shellspec_ends_with() { shellspec_ends_with_posix "$@"; }
    shellspec_replace_all_multiline() {
      [ $# -lt 4 ] && eval "set -- \"\$1\" \"\${$1}\" \"\$2\" \"\$3\""
      shellspec_replace_all_multiline_check "$3"
      shellspec_replace_all_fast "$@"
    }
    ;;
  1)
    # POSIX version (POSIX compliant)
    # ash(busybox)>=1.1.3, bash>=2.05b, dash>=0.5.2, ksh>=93q, mksh>=40
    # yash>=2.30?, zsh>=3.1.9?, pdksh=none, posh=none, bosh>=2020/04/27
    shellspec_replace_all() { shellspec_replace_all_posix "$@"; }
    shellspec_includes() { shellspec_includes_posix "$@"; }
    shellspec_starts_with() { shellspec_starts_with_posix "$@"; }
    shellspec_ends_with() { shellspec_ends_with_posix "$@"; }
    shellspec_replace_all_multiline() {
      shellspec_replace_all_multiline_ shellspec_replace_all_posix "$@"
    }
    ;;
  2)
    # Fallback version
    shellspec_replace_all() { shellspec_replace_all_fallback "$@"; }
    shellspec_includes() { shellspec_includes_fallback "$@"; }
    shellspec_starts_with() { shellspec_starts_with_fallback "$@"; }
    shellspec_ends_with() { shellspec_ends_with_fallback "$@"; }
    shellspec_replace_all_multiline() {
      shellspec_replace_all_multiline_ shellspec_replace_all_fallback "$@"
    }
esac

# $2: pattern should be escaped
shellspec_match() {
  ( eval "export LC_ALL=C; case \${1:-} in ($2) :;; (*) false;; esac" ) &&:
}

shellspec_escape_quote() {
  [ $# -lt 2 ] && eval "set -- \"\$1\" \"\$${1}\""
  shellspec_replace_all "$1" "$2" "'" "'\"'\"'"
}

shellspec_pack() {
  SHELLSPEC_EVAL="
    $1=''; shift; \
    for $1; do \
      shellspec_escape_quote $1 \"\${$1}\"; \
      set -- \"\$@\" \"'\${$1}'\"; \
      shift; \
    done; \
    IFS=\" \$IFS\"; set -- \"\${*:-}\"; IFS=\${IFS#?}; unset $1; $1=\$1
  "
  eval "$SHELLSPEC_EVAL"
}

shellspec_match_pattern_escape() {
  shellspec_replace_all "$1" \\ \\\\
  shellspec_replace_all "$1" \" \\\"
  shellspec_replace_all "$1" \# \\\#
  shellspec_replace_all "$1" \$ \\\$
  shellspec_replace_all "$1" \& \\\&
  shellspec_replace_all "$1" \' \\\'
  shellspec_replace_all "$1" \( \\\(
  shellspec_replace_all "$1" \) \\\)
  shellspec_replace_all "$1" \; \\\;
  shellspec_replace_all "$1" \< \\\<
  shellspec_replace_all "$1" \> \\\>
  shellspec_replace_all "$1" \` \\\`
  shellspec_replace_all "$1" \~ \\\~
  shellspec_replace_all "$1" '=' '\='
  shellspec_replace_all "$1" '^' '\^'
  shellspec_replace_all "$1" ' ' '" "'
  shellspec_replace_all "$1" "$SHELLSPEC_TAB" '${SHELLSPEC_TAB}'
  shellspec_replace_all "$1" "$SHELLSPEC_LF" '${SHELLSPEC_LF}'
  shellspec_replace_all "$1" "$SHELLSPEC_CR" '${SHELLSPEC_CR}'
  shellspec_replace_all "$1" "$SHELLSPEC_VT" '${SHELLSPEC_VT}'
}

shellspec_match_pattern() {
  [ "${2:-}" ] || return 1
  shellspec_match_pattern=$2
  shellspec_match_pattern_escape shellspec_match_pattern
  set -- "$1" "$shellspec_match_pattern" /dev/null
  ( eval "export LC_ALL=C; case \$1 in ($2) :;; (*) false;; esac" ) 2>"$3" &&:
}

shellspec_join() {
  [ $# -le 3 ] && eval "$1=\${3:-}" && return 0
  SHELLSPEC_EVAL="
    $1=\$3 && shift 3; \
    while [ \$# -gt 0 ]; do $1=\"\${$1}$2\$1\" && shift; done \
  "
  eval "$SHELLSPEC_EVAL"
}

shellspec_shift10() {
  while [ "$3" -gt 0 ]; do
    case $2 in
      *.*)
        set -- "$1" "${2%.*}" "${2#*.}" "$3"
        set -- "$1" $(($2 * 10)) "${3%"${3#?}"}" "${3#?}" "$4"
        set -- "$1" "$(($2 + $3))${4:+.}$4" $(($5 - 1))
        ;;
      *) set -- "$1" $(($2 * 10)) $(($3 - 1))
    esac
  done

  while [ "$3" -lt 0 ]; do
    case $2 in
      *.*)
        set -- "$1" "${2%.*}" "${2#*.}" "$3"
        set -- "$1" "$(($2 / 10)).${2#"${2%?}"}$3" $(($4 + 1))
        ;;
      *) set -- "$1" "$(($2 / 10)).${2#"${2%?}"}" $(($3 + 1))
    esac
  done

  eval "$1=$2"
}

shellspec_chomp() {
  SHELLSPEC_EVAL="
    until case \$$1 in (*\$SHELLSPEC_LF) false; esac; do \
      $1=\${$1%\$SHELLSPEC_LF}; \
    done
  "
  eval "$SHELLSPEC_EVAL"
}

if [ "${ZSH_VERSION:-}" ]; then
  shellspec_get_nth() {
    set -- "$1" "$2" "${3#"${3%%[1-9]*}"}" "${4:-}" "$IFS"
    IFS=${4:-$SHELLSPEC_IFS}
    eval "set -- \"\$@\" \${=2}"
    IFS=$5
    [ $# -ge $(($3 + 5)) ] && eval "$1=\${$(($3 + 5))}"
  }
else
  shellspec_get_nth() {
    set -f -u -- "$1" "$2" "${3#"${3%%[1-9]*}"}" "${4:-}" "$IFS" "$-"
    IFS=${4:-$SHELLSPEC_IFS}
    # shellcheck disable=SC2086
    set -- "$@" $2
    IFS=$5
    [ "${6#*f}" = "$6" ] && set +f
    # Workaround for posh 0.10.2: glob does not expand when set -u
    [ "${6#*u}" = "$6" ] && set +u
    [ $# -ge $(($3 + 6)) ] && eval "$1=\${$(($3 + 6))} &&:"
  }
fi

shellspec_which() {
  set -- "$1" "${PATH%$SHELLSPEC_PATHSEP}${SHELLSPEC_PATHSEP}"
  while [ "${2%$SHELLSPEC_PATHSEP}" ]; do
    if [ -x "${2%%$SHELLSPEC_PATHSEP*}/$1" ]; then
      shellspec_putsn "${2%%$SHELLSPEC_PATHSEP*}/$1"
      return 0
    fi
    set -- "$1" "${2#*$SHELLSPEC_PATHSEP}"
  done
  return 1
}

shellspec_is_empty_file() {
  [ "${1:-}" ] && [ -f "${1:-}" ] && [ ! -s "${1:-}" ]
}

shellspec_is_empty_directory() {
  [ -d "$1" ] || return 1

  # This subshell is used to revert changes directory, variables and shell flags
  ( CDPATH=
    # set -- "$DIR"/* not working properly in posh 0.10.2
    cd "$1" || return 1

    # workaround for posh 0.10.2: glob does not expand when set -u
    set +o noglob +u
    # shellcheck disable=SC2039
    [ "${SHELLSPEC_FAILGLOB_AVAILABLE:-}" ] && shopt -u failglob
    [ "${SHELLSPEC_NOMATCH_AVAILABLE:-}" ] && setopt NO_NOMATCH

    for found in * .*; do
      case $found in (.|..) continue; esac
      [ -e "$found" ] && return 1
    done &&:
    return 0
  )
}

shellspec_pluralize() {
  [ $# -eq 2 ] && set -- "$1" "" "$2"
  [ "${3%% *}" ] || return 0
  [ "${3%% *}" -eq 1 ] && eval "$1=\${$1}\${2}\${3}\${4:-}" && return 0
  case $3 in
    *x) eval "$1=\${$1}\${2}\${3}es\${4:-}" ;;
    *) eval "$1=\${$1}\${2}\${3}s\${4:-}" ;;
  esac
}

shellspec_exists_file() {
  [ -e "$1" ]
}

shellspec_unsetf() {
  if shellspec_is_function "$1"; then
    # Workaround for posh.
    # Define function that run external command instead of unset function.
    # Unset the function cause not be able to run external command with posh.
    eval "$1() { case \$# in (0) command $1;; (*) command $1 \"\$@\"; esac; }"
    return 0
  fi
  if [ "$SHELLSPEC_BUILTIN_TYPESETF" ]; then
    # shellcheck disable=SC2039
    typeset -f "$1" >/dev/null 2>&1 || return 0
  elif [ "${POSH_VERSION:-}" ]; then
    ( unset -f "$1" 2>/dev/null ) || return 0
  fi
  unset -f "$1" 2>/dev/null ||:
  return 0
}

if [ "$SHELLSPEC_DEFECT_EXPORTP" ]; then
  shellspec_exportp() { export; }
else
  shellspec_exportp() { export -p; }
fi

shellspec_list_envkeys() {
  set -- "$1" "shellspec_list_envkeys"
  eval "$2_callback() { $1 \"\$@\"; }; $(shellspec_exportp | "$2_rework")" &&:
}
shellspec_list_envkeys_rework() {
  while IFS= read -r line; do
    shellspec_list_envkeys_sanitize line "$line"
    shellspec_putsn "shellspec_list_envkeys_parse $line || return \$?"
  done
}
shellspec_list_envkeys_parse() {
  while [ $# -gt 2 ]; do
    case $2 in
      *=*) break ;;
      *) shift ;;
    esac
  done
  [ $# -gt 1 ] && shift
  shellspec_list_envkeys_callback "${1%%[=]*}"
}

# Sanitize to avoid security risk when environment variable name
# contains meta characters
#
# e.g.
# $ env 'FOO; echo bad; # =aaa' ksh -c 'export -p | grep FOO'
# export FOO; echo bad; # =aaa
shellspec_list_envkeys_sanitize() {
  set -- "$IFS" "$1" "_$2_"
  IFS='!#&()*;<>?[]`{|}~'
  eval "set -- \"\$1\" \"\$2\" \${${ZSH_VERSION:+=}3}"
  IFS="_$1"
  eval "shift 2; $2=\"\$*\"; $2=\${$2%_}; $2=\${$2#_}"
  IFS=${IFS#_}
}

shellspec_exists_envkey() {
  (
    key="$1"
    callback() { [ ! "$1" = "$key" ] &&:; }
    shellspec_list_envkeys callback && return 1
    return 0
  ) &&:
}

shellspec_is_readonly() {
  eval "[ \"\${$1+x}\" ] &&:" || return 1
  eval "set -- \"\$1\" \"\${$1}\""
  ( eval "$1=_\$1 && [ ! \"\${$1}\" = \"\$2\" ]" ) 2>/dev/null && return 1
  return 0
}

shellspec_abspath() {
  case $PWD in
    [a-zA-Z]:* | //*) shellspec_abspath_win "$@" ;;
    *) shellspec_abspath_unix "$@" ;;
  esac
}

shellspec_abspath_unix() {
  case $2 in
    /*) set -- "$1" "$2/" "" "" ;;
    *) set -- "$1" "${3:-$PWD}/$2/" "" ""
  esac

  while [ "$2" ]; do
    case ${2%%/*} in
      "" | .) set -- "$1" "${2#*/}" "${2%%/*}" "$4" ;;
      ..) set -- "$1" "${2#*/}" "${2%%/*}" "${4%/*}" ;;
      *) set -- "$1" "${2#*/}" "${2%%/*}" "$4/${2%%/*}"
    esac
  done
  eval "$1=\"/\${4#/}\""
}

shellspec_chdrv() { cd "$1:" 2>/dev/null || exit; }

shellspec_abspath_win() {
  set -- "$1" "$2" "${3:-$PWD}"

  case $2 in (//*) # UNC path
    shellspec_abspath_unix "$1" "${2#/}"
    eval "$1=\"/\${$1}\""
    return 0
  esac

  case $2 in
    [a-zA-Z]:/*) set -- "$1" "/$2" ;;
    [a-zA-Z]:*)
      set -- "$1" "$2" "$(shellspec_chdrv "${2%%:*}" && echo "$3")"
      set -- "$1" "/${3%/}/${2#?:}" ;;
    /*) set -- "$1" "/${3%%:*}:$2" ;;
    *) set -- "$1" "/${3%/}/$2" ;;
  esac
  shellspec_abspath_unix "$@"
  eval "$1=\"\${$1#/}\""
}

shellspec_mv() { "$SHELLSPEC_MV" "$@"; }
shellspec_rm() { "$SHELLSPEC_RM" "$@"; }
shellspec_chmod() { "$SHELLSPEC_CHMOD" "$@"; }
shellspec_sleep() { "$SHELLSPEC_SLEEP" "$@"; }