shellspec/shellspec

View on GitHub
lib/libexec.sh

Summary

Maintainability
Test Coverage
A
98%
#shellcheck shell=sh disable=SC2004

if [ "${ZSH_VERSION:-}" ]; then
  setopt shwordsplit
  zmodload zsh/mapfile 2>/dev/null ||:
fi

# shellcheck source=lib/general.sh
. "${SHELLSPEC_LIB:-./lib}/general.sh"

use() {
  while [ $# -gt 0 ]; do
    case $1 in
      constants) shellspec_constants ;;
      *) shellspec_proxy "$1" "shellspec_$1" ;;
    esac
    shift
  done
}

load() {
  while [ "$#" -gt 0 ]; do
    shellspec_source "${SHELLSPEC_LIB:-./lib}/libexec/$1.sh"
    shift
  done
}

unixtime() {
  IFS=" $IFS"
  #shellcheck disable=SC2046
  set -- $("$SHELLSPEC_DATE" -u +'%Y %m %d %H %M %S') "$1"
  IFS=${IFS# }
  set -- "$1" "${2#0}" "${3#0}" "${4#0}" "${5#0}" "${6#0}" "$7"
  [ "$2" -lt 3 ] && set -- $(( $1-1 )) $(( $2+12 )) "$3" "$4" "$5" "$6" "$7"
  set -- $(( 365*$1 + $1/4 - ($1/100) + $1/400 )) "$2" "$3" "$4" "$5" "$6" "$7"
  set -- "$1" $(( (306 * ($2 + 1) / 10) - 428 )) "$3" "$4" "$5" "$6" "$7"
  set -- $(( ($1 + $2 + $3 - 719163) * 86400 + $4 * 3600 + $5 * 60 + $6 )) "$7"
  eval "$2=$1"
}

is_specfile() {
  shellspec_match "${1%%:*}" "$SHELLSPEC_PATTERN"
}

found_specfile() {
  set -- "$1" "${2#./}"
  set -- "$@" "${2%%:*}"
  case $2 in (*:*)
    set -- "$@" "${2#*:}"
    until case $4 in (*:*) false; esac; do
      set -- "$1" "$2" "$3" "${4%%:*} ${4#*:}"
    done
  esac
  "$@"
}

find_specfiles() {
  eval "found_() { ! is_specfile \"\$1\" || found_specfile \"$1\" \"\$1\"; }"
  shift
  if [ "${1:-}" = "-" ]; then
    [ -e "$SHELLSPEC_INFILE" ] || return 0
    while IFS= read -r line || [ "$line" ]; do
      [ "$line" ] || continue
      eval shellspec_find_files found_ "$line"
    done < "$SHELLSPEC_INFILE" &&:
  else
    eval shellspec_find_files found_ ${1+'"$@"'}
  fi
}

edit_in_place() {
  [ -e "$1" ] || return 0
  set -- "$1" "$(eval 'shift; "$@"' < "$1")"
  putsn "$2" > "$1"
}

info() {
  IFS=" $IFS"
  if [ "$SHELLSPEC_COLOR" ]; then
    set -- '\033[36m%s\033[m\n' "${*:-}"
  else
    set -- '%s\n' "${*:-}"
  fi
  IFS=${IFS#?}
  "$SHELLSPEC_PRINTF" "$@"
}

warn() {
  IFS=" $IFS"
  if [ "$SHELLSPEC_COLOR" ]; then
    set -- '\033[33m%s\033[m\n' "${*:-}"
  else
    set -- '%s\n' "${*:-}"
  fi
  IFS=${IFS#?}
  "$SHELLSPEC_PRINTF" "$@" >&2
}

error() {
  IFS=" $IFS"
  if [ "$SHELLSPEC_COLOR" ]; then
    set -- '\033[1;31m%s\033[m\n' "${*:-}"
  else
    set -- '%s\n' "${*:-}"
  fi
  IFS=${IFS#?}
  "$SHELLSPEC_PRINTF" "$@" >&2
}

abort() {
  error "$@"
  exit 1
}

set_exit_status() {
  return "$1"
}

nap() {
  if [ "$SHELLSPEC_MSLEEP" ]; then
    sleep 0.1
  else
    sleep 0
  fi
}

sleep_wait() {
  while "$@"; do nap; done
}

sleep_wait_until() {
  until "$@"; do nap; done
}

signal() { kill -"$1" "$2"; }
if kill -s 0 $$ 2>/dev/null; then
  signal() { kill -s "$1" "$2"; }
  # workaround for posh 0.8.5. broken signal
  if [ "$SHELLSPEC_DEFECT_SIGNAL" ]; then
    kill() { env kill "$@"; }
  fi
fi

timeout() {
  sleep 0
  set -- "$(t=0; unixtime t; echo "$t")" "$@"
  while [ "$(t=0; unixtime t; echo "$t")" -le "$(($1 + $2))" ]; do
    ( shift 2; "$@" ) && return 0
    nap
  done
  return 0
}

read_quickfile() {
  set -- "$1" "${2:-}" "${3:-}"
  while eval "{ IFS= read -r $1 || [ \"\$$1\" ]; } &&:"; do
    eval "set -- \"\$@\" \"\${$1##*:}\" \"\${$1%:*}\""
    case $4 in (todo | fixed) [ "$3" ] && continue; esac
    eval "$1=\$5"
    [ "$2" ] && eval "$2=\$4"
    return 0
  done
  return 1
}

includes_path() {
  [ "$2" = "./" ] && return 0
  set -- "/${1%/}" "/${2%/}"
  while [ "$1" ]; do
    [ "$1" = "$2" ] && return 0
    set -- "${1%/*}" "$2"
  done
  return 1
}

# $1: @ID, $2: [:@IDs...]
match_quick_data_range() {
  set -- "${1%:*}" "${1#*:}" "$2:"
  while [ "$3" ] && set -- "$1" "$2" "${3#*:}" "${3%%:*}"; do
    case $4 in (@*)
      [ "$1" = "$4" ] || starts_with "$1" "$4-" && return 0
    esac
  done
  return 1
}

# $1: line of quick data (PATH:@ID), $2-: path[:IDs...]
match_quick_data() {
  set -- "$@" "$1"
  while [ $# -gt 2 ] && shift; do
    case $1 in (*:*)
      eval "includes_path \"\${$#%%:*}\" \"\${1%%:*}\" &&:" || continue
      eval "match_quick_data_range \"\${$##*:}\" \"\${1#*:}\" &&:" || continue
      return 0
    esac
    eval "includes_path \"\${$#%%:*}\" \"\$1\" &&:" || continue
    return 0
  done
  return 1
}

use puts putsn starts_with