Mercurial > hgrepos > FreeBSD > ports > sysutils > local-bsdtools
changeset 516:1d1a4c217e38
array.sh: Implement an alist (aka map, dict) for key-pair values.
Implementation is straightforward and not optimized.
Is uses two arrays to implement keys and values respectively.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Sun, 01 Sep 2024 16:57:25 +0200 |
| parents | 7c2ffcc92df6 |
| children | 2e2ba2203117 |
| files | share/local-bsdtools/array.sh |
| diffstat | 1 files changed, 429 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/share/local-bsdtools/array.sh Sun Sep 01 16:55:10 2024 +0200 +++ b/share/local-bsdtools/array.sh Sun Sep 01 16:57:25 2024 +0200 @@ -31,10 +31,33 @@ #: error normally and forces an immediate error ``exit``. #: Exceptions to this rule are documented. #: +#: This module also contains a very rough implementation of an alist +#: (aka dict, aka map). +#: This is implemented with two corresponding arrays: one for the keys +#: and one for the values. +#: +#: If the map has the name NAME then the array naming is as such: +#: +#: - The keys are held in an array with name alist_key_<NAME> +#: - The values are held in an array with name alist_val_<NAME> +#: +#: This makes _farr_array_alist_key_<NAME> and _farr_array_alist_val_<NAME> +#: as effective shell level variable name prefixes. +#: +#: An alist exists iff both of the underlying arrays exist. +#: +#: Important: +#: All names that start with ``_farr_`` or ``__farr_`` are reserved +#: for private use. Try to do not use such names in your scripts. +#: +#: Public functions start with ``array_`` or ``alist_``. +#: _farr_global_prefix=_farr_array_ _farr_unset=_farr__UNSET_d646c21167a611efa78174d435fd3892__ +_farr_alist_key_infix=alist_key_ +_farr_alist_value_infix=alist_val_ #: @@ -694,3 +717,409 @@ printf "DEBUG: %s: \`%s'\\n" "$2" "$3" 1>&2 return 0 } + + +#: +#: Create a new alist. +#: +#: Args: +#: $1 (str): The name of the alist. +#: Must conform to shell variable naming conventions +#: +#: Exit: +#: Iff the alist already exists. +#: +alist_create() { + local __farr_name + + local __farr_key_array __farr_val_array + + [ $# -lt 1 ] && _farr_fatal "missing alist name" + __farr_name=$1 + __farr_key_array=${_farr_alist_key_infix}${__farr_name} + __farr_val_array=${_farr_alist_value_infix}${__farr_name} + + if array_create ${__farr_key_array}; then + if ! array_create ${__farr_val_array}; then + array_destroy ${__farr_key_array} || true + fi + fi +} + + +#: +#: Get the length of an alist and put it into a variable. +#: +#: Args: +#: $1 (str): The name of the alist. +#: +#: Returns: +#: 0 (truthy) if the array exists, +#: 1 (falsy) if the array does not exist +#: +alist_length() { + local __farr_varname __farr_name + + local __farr_key_array + + [ $# -lt 1 ] && _farr_fatal "missing variable name" + __farr_varname=$1 + [ $# -lt 2 ] && _farr_fatal "missing array name" + __farr_name=$2 + __farr_key_array=${_farr_alist_key_infix}${__farr_name} + + array_length ${__farr_varname} ${__farr_key_array} + return $? +} + + +#: +#: Get the length of an alist. +#: +#: Args: +#: $1 (str): The name of the variable to put the length into +#: $2 (str): The name of the alist. +#: +#: Output (stdout): +#: The number of elements in the alist. +#: If the array does not exist the output is -1. +#: +#: Returns: +#: 0 (truthy) +#: +alist_print_length() { + local __farr_name + + local __farr_pl_l __farr_key_array + + [ $# -lt 1 ] && _farr_fatal "missing array name" + __farr_name=$1 + __farr_key_array=${_farr_alist_key_infix}${__farr_name} + + array_print_length ${__farr_key_array} + return 0 +} + + +#: +#: Empty an existing alist. +#: +#: Args: +#: $1 (str): The name of the existing alist +#: +alist_clear() { + local __farr_name + + local __farr_key_array __farr_val_array + + [ $# -lt 1 ] && _farr_fatal "missing array name" + __farr_name=$1 + __farr_key_array=${_farr_alist_key_infix}${__farr_name} + __farr_val_array=${_farr_alist_value_infix}${__farr_name} + + array_clear ${__farr_val_array} + array_clear ${__farr_key_array} +} + + +#: +#: Destroy and unset an alist +#: +#: Args: +#: $1 (str): The name of an alist. The alist may exist or not. +#: +#: Returns: +#: - A truthy value if the alist existed and has been deleted +#: - A falsy value if the alist does not exist +#: +alist_destroy() { + local __farr_name + + local __farr_key_array __farr_val_array __farr_rc_key __farr_rc_val + + [ $# -lt 1 ] && _farr_fatal "missing alist name" + __farr_name=$1 + __farr_key_array=${_farr_alist_key_infix}${__farr_name} + __farr_val_array=${_farr_alist_value_infix}${__farr_name} + + __farr_rc_key=0 + __farr_rc_val=0 + array_destroy ${__farr_val_array} || __farr_rc_val=$? + array_destroy ${__farr_key_array} || __farr_rc_key=$? + + [ ${__farr_rc_key} -gt 0 ] && return ${__farr_rc_key} + [ ${__farr_rc_val} -gt 0 ] && return ${__farr_rc_val} + return 0 +} + + +#: +#: Map a key to a value +#: +#: Args: +#: $1 (str): The name of an existing alist +#: $2: The key +#: $3: The value +#: +#: Exit: +#: If one of the underlying arrays that implement the alist does not exist +#: or if an internal inconsistency will be detected. +#: +alist_set() { + local __farr_name __farr_key __farr_value + + local __farr_key_array __farr_val_array __farr_idx + + [ $# -lt 1 ] && _farr_fatal "missing array name" + __farr_name=$1 + __farr_key_array=${_farr_alist_key_infix}${__farr_name} + __farr_val_array=${_farr_alist_value_infix}${__farr_name} + [ $# -lt 2 ] && _farr_fatal "missing key" + __farr_key="$2" + [ $# -lt 3 ] && _farr_fatal "missing value" + __farr_value="$3" + + if array_find __farr_idx ${__farr_key_array} "${__farr_key}"; then + # Replace existing + array_set ${__farr_key_array} ${__farr_idx} "${__farr_key}" + array_set ${__farr_val_array} ${__farr_idx} "${__farr_value}" + else + # append new + array_append ${__farr_key_array} "${__farr_key}" + array_append ${__farr_val_array} "${__farr_value}" + if [ "$(array_print_length ${__farr_key_array})" -ne "$(array_print_length ${__farr_val_array})" ]; then + _farr_fatal "alist \`${__farr_name}': could not set (append error)" + fi + fi +} + + +#: +#: Get the value that is associated with a key and put it into a variable. +#: +#: Args: +#: $1 (str): The name of the variable to put the value into +#: $2 (str): The name of an existing alist +#: $3: The key +#: +#: Exit: +#: - If the key is not found. +#: - If one of the underlying arrays that implement the alist does not exist +#: or if an internal inconsistency will be detected. +#: +alist_get() { + local __farr_varname __farr_name __farr_key + + local __farr_key_array __farr_val_array __farr_idx + + [ $# -lt 1 ] && _farr_fatal "missing variable name" + __farr_varname=$1 + [ $# -lt 2 ] && _farr_fatal "missing array name" + __farr_name=$2 + __farr_key_array=${_farr_alist_key_infix}${__farr_name} + __farr_val_array=${_farr_alist_value_infix}${__farr_name} + [ $# -lt 3 ] && _farr_fatal "missing key" + __farr_key="$3" + + if array_find __farr_idx ${__farr_key_array} "${__farr_key}"; then + # Yes, we found an index + #printf "%s" "$(array_get ${__farr_val_array} ${__farr_idx})" + array_get ${__farr_varname} ${__farr_val_array} ${__farr_idx} + else + _farr_fatal "alist \`${__farr_name}': key not found" + fi +} + + +#: +#: Try to get the value that is associated with a key and store it in a variable +#: +#: Args: +#: $1 (str): The name of the variable where to put the value into +#: $2 (str): The name of an existing alist +#: $3: The key +#: +#: Returns: +#: 0 (truthy) on success, +#: 1 (falsy) if the given key is not found +#: +alist_tryget() { + local __farr_varname __farr_name __farr_key + + local __farr_key_array __farr_val_array __farr_idx + + [ $# -lt 1 ] && _farr_fatal "missing variable name" + __farr_varname=$1 + [ $# -lt 2 ] && _farr_fatal "missing array name" + __farr_name=$2 + __farr_key_array=${_farr_alist_key_infix}${__farr_name} + __farr_val_array=${_farr_alist_value_infix}${__farr_name} + [ $# -lt 3 ] && _farr_fatal "missing key" + __farr_key="$3" + + if array_find __farr_idx ${__farr_key_array} "${__farr_key}"; then + # Yes, we found an index + #printf "%s" "$(array_get ${__farr_val_array} ${__farr_idx})" + array_get ${__farr_varname} ${__farr_val_array} ${__farr_idx} + return 0 + else + return 1 + fi +} + + +#: +#: Try to get the key that is associated with a storage index and store it in a variable. +#: +#: Use this for iteration over values. +#: +#: Args: +#: $1 (str): The name of the variable where to put the key into +#: $2 (str): The name of an existing alist +#: $3 (int): The index +#: +#: Returns: +#: 0 (truthy) on success, +#: 1 (falsy) if the given index is out of bounds +#: +#: Exit: +#: Other errors (missing array name, missing index value) are considered +#: fatal and call `_farr_fatal` (i.e. `exit`). +#: +alist_tryget_key_at_index() { + local __farr_varname __farr_name __farr_index + + local __farr_key_array __farr_val_array __farr_idx + + [ $# -lt 1 ] && _farr_fatal "missing variable name" + __farr_varname=$1 + [ $# -lt 2 ] && _farr_fatal "missing array name" + __farr_name=$2 + __farr_key_array=${_farr_alist_key_infix}${__farr_name} + [ $# -lt 3 ] && _farr_fatal "missing index" + __farr_index=$3 + + array_tryget ${__farr_varname} ${__farr_key_array} ${__farr_index} +} + + +#: +#: Try to get the value that is associated with a storage index and store it in a variable. +#: +#: Use this for iteration over keys. +#: +#: Args: +#: $1 (str): The name of the variable where to put the value into +#: $2 (str): The name of an existing alist +#: $3 (int): The index +#: +#: Returns: +#: 0 (truthy) on success, +#: 1 (falsy) if the given index is out of bounds +#: +#: Exit: +#: Other errors (missing array name, missing index value) are considered +#: fatal and call `_farr_fatal` (i.e. `exit`). +#: +alist_tryget_value_at_index() { + local __farr_varname __farr_name __farr_index + + local __farr_key_array __farr_val_array __farr_idx + + [ $# -lt 1 ] && _farr_fatal "missing variable name" + __farr_varname=$1 + [ $# -lt 2 ] && _farr_fatal "missing array name" + __farr_name=$2 + __farr_val_array=${_farr_alist_value_infix}${__farr_name} + [ $# -lt 3 ] && _farr_fatal "missing index" + __farr_index=$3 + + array_tryget ${__farr_varname} ${__farr_val_array} ${__farr_index} +} + + +#: +#: Determine whether a key is found within the alist +#: +#: Args: +#: $1 (str): The name of an existing alist +#: $2: The key +#: +#: Returns: +#: 0 (truthy) on success if the key is found, +#: 1 (falsy) if the given key is not found +#: +alist_contains() { + local __farr_name __farr_key + + local __farr_key_array __farr_idx + + [ $# -lt 1 ] && _farr_fatal "missing array name" + __farr_name=$1 + __farr_key_array=${_farr_alist_key_infix}${__farr_name} + [ $# -lt 2 ] && _farr_fatal "missing key" + __farr_key="$2" + + if array_find __farr_idx ${__farr_key_array} "${__farr_key}"; then + return 0 + else + return 1 + fi +} + + +#: +#: Print the contents of an alist to stderr. +#: +#: Args: +#: $1 (str): The name of an alist. The array may exist or not. +#: +#: Returns: +#: 0 +#: +#: This function must know some of the basics of the array implementation +#: also. +#: +alist_debug() { + local __farr_name + + local __farr_key_array __farr_val_array + local __farr_key_gvrname __farr_val_gvrname + local __farr_key_l __farr_val_l __farr_idx __farr_i_k __farr_i_v + + [ $# -lt 1 ] && _farr_fatal "missing array name" + __farr_name=$1 + __farr_key_array=${_farr_alist_key_infix}${__farr_name} + __farr_val_array=${_farr_alist_value_infix}${__farr_name} + + __farr_key_gvrname=${_farr_global_prefix}${__farr_key_array} + __farr_val_gvrname=${_farr_global_prefix}${__farr_val_array} + + # Check whether the variables alread exist + eval __farr_key_l=\${${__farr_key_gvrname}__:-${_farr_unset}} + if [ "${__farr_key_l}" = ${_farr_unset} ]; then + echo "DEBUG: alist \`${__farr_name}' does not exist (key list)" 1>&2 + return 0 + fi + eval __farr_val_l=\${${__farr_val_gvrname}__:-${_farr_unset}} + if [ "${__farr_val_l}" = ${_farr_unset} ]; then + echo "DEBUG: alist \`${__farr_name}' does not exist (value list)" 1>&2 + return 0 + fi + if [ ${__farr_key_l} -ne ${__farr_val_l} ]; then + echo "DEBUG: length mismatch between keys and values in alist \`${__farr_name}' encountered" 1>&2 + return 0 + fi + echo "DEBUG: alist \`${__farr_name}' has length ${__farr_key_l}" 1>&2 + if [ "${__farr_key_l}" -gt 0 ]; then + echo "DEBUG: its contents:" 1>&2 + __farr_idx=1 + while [ ${__farr_idx} -le ${__farr_key_l} ]; do + eval __farr_i_k=\"\${${__farr_key_gvrname}_${__farr_idx}}\" + eval __farr_i_v=\"\${${__farr_val_gvrname}_${__farr_idx}}\" + printf "DEBUG: \`%s' -> \`%s'\\n" "${__farr_i_k}" "${__farr_i_v}" 1>&2 + __farr_idx=$((${__farr_idx} + 1)) + done + fi + return 0 +}
