Mercurial > hgrepos > FreeBSD > ports > sysutils > local-bsdtools
changeset 582:22d35878f6f8
farray.sh: implement farray_splice().
This is modeled after Perl's "splice()" function.
BUGS:
- Docs could be improved
- Tests
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Tue, 17 Sep 2024 23:03:28 +0200 |
| parents | fac8ca743e40 |
| children | 55c024c809ca |
| files | share/local-bsdtools/farray.sh |
| diffstat | 1 files changed, 202 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/share/local-bsdtools/farray.sh Tue Sep 17 23:02:30 2024 +0200 +++ b/share/local-bsdtools/farray.sh Tue Sep 17 23:03:28 2024 +0200 @@ -291,6 +291,46 @@ #: +#: Internal helper to try to get all the metadata for an array. +#: +#: This function is similar to `_farr_array_tryget_meta` but normally fatal +#: errors are reported with a "normal" error return. +#: +#: All output variables are properly initialized to the `null` value. +#: +#: Args: +#: $1 (str): The name of the array +#: +#: Output (Globals): +#: __farr_name (str): The name of the array. +#: __farr_token (str): The token that is the value of the name. +#: __farr_gvrname (str): The variable prefix for all items. +#: __farr_len (int): The length of the array +#: +#: Returns: +#: 0 if the array exists, 1 if something is missing or `$1` is not an array +#: +#: Important: +#: No fatal error exits should happen. +#: +_farr_array_tryget_meta_nonfatal() { + __farr_token='' + __farr_gvrname='' + __farr_len='' + __farr_name="${1-}" + [ -z "${__farr_name}" ] && return 1 + eval __farr_token=\"\$\{"${__farr_name}"-\}\" + [ -z "${__farr_token}" ] && return 1 + __farr_gvrname="${_farr_array_prefix}${__farr_token}" + eval __farr_len=\$\{${__farr_gvrname}__:+SET\} + [ -z "${__farr_len}" ] && return 1 + # eval __farr_len="\$((\${${__farr_gvrname}__} + 0))" + eval __farr_len="\$((${__farr_gvrname}__ + 0))" + return 0 +} + + +#: #: Get the length of an array and put it into a variable #: #: Args: @@ -520,6 +560,168 @@ #: +#: Remove the elements designated by an index and a length from an array, +#: and replace them with the elements of another array list, if any. +#: +#: Args: +#: $1 (str, null): The name of an array where the deleted items will be +#: appended to. May be the `null` value if this is not +#: desired: nothing will be done with the deleted elements. +#: $2 (str): The name of the array that is to be spliced +#: $3 (int, null): Index, where to remove and/or insert elements. +#: - `null` is len+1. +#: - 0, -1, -2, ... -(len-1) are len + given index. +#: $4 (int, null): The length -- the number of elements to delete at given +#: index. +#: If `null` then the length from index to the end of the +#: array is applied ("automatic"). +#: $5 (str, null, optional): An array whose elements will be inserted at +#: given index. +#: +farray_splice() { + local __farr_del_name __farr_del_token __farr_del_gvrname __farr_del_len + local __farr_l_name __farr_l_token __farr_l_gvrname __farr_l_len + local __farr_index __farr_length + local __farr_r_name __farr_r_token __farr_r_gvrname __farr_r_len + + # + # Dynamically scoped variables for _farr_array_tryget_meta_nonfatal() + # and _farr_array_tryget_meta(). + # + local __farr_name __farr_token __farr_gvrname __farr_len + + local __farr_off __farr_v __farr_delta __farr_src_idx __farr_dst_idx + + [ $# -lt 4 ] && _farr_fatal "missing required arguments" + + __farr_del_name='' + if [ -n "${1}" ] && _farr_array_tryget_meta "${1}"; then + __farr_del_name="${__farr_name}" + __farr_del_token="${__farr_token}" + __farr_del_gvrname="${__farr_gvrname}" + __farr_del_len="${__farr_len}" + fi + _farr_array_get_meta "${2}" + __farr_l_name="${__farr_name}" + __farr_l_token="${__farr_token}" + __farr_l_gvrname="${__farr_gvrname}" + __farr_l_len="${__farr_len}" + + __farr_index="${3}" + __farr_length="${4}" + + if _farr_array_tryget_meta_nonfatal "${5-}"; then + __farr_r_name="${__farr_name}" + __farr_r_token="${__farr_token}" + __farr_r_gvrname="${__farr_gvrname}" + __farr_r_len="${__farr_len}" + else + __farr_r_name='' + __farr_r_len=0 + fi + + if [ -z "${__farr_index}" ]; then + __farr_index=$((__farr_l_len + 1)) + else + if [ "${__farr_index}" -le 0 ]; then + __farr_index=$((__farr_l_len + __farr_index)) + fi + # NOTE: index value length + 1 is allowed: splice at the end + [ \( "${__farr_index}" -lt 1 \) -o \( "${__farr_index}" -gt "$((__farr_l_len + 1))" \) ] && _farr_fatal "index out of range" + fi + # also check the given length + if [ -z "${__farr_length}" ]; then + __farr_length="$((__farr_l_len - __farr_index + 1))" + else + [ \( "${__farr_length}" -lt 0 \) -o \( "${__farr_length}" -gt "$((__farr_l_len - __farr_index + 1))" \) ] && _farr_fatal "length out of valid range" + fi + if [ ${__farr_length} -eq ${__farr_r_len} ]; then + # Just replace + __farr_off=0 + while [ ${__farr_off} -lt ${__farr_length} ]; do + if [ -n "${__farr_del_name}" ]; then + eval __farr_v=\"\$\{${__farr_l_gvrname}_$((__farr_index + __farr_off))\}\" + farray_append "${__farr_del_name}" "${__farr_v}" + fi + eval __farr_v=\"\$\{${__farr_r_gvrname}_$((__farr_off + 1))\}\" + eval ${__farr_l_gvrname}_$((__farr_index + __farr_off))=\"\$\{__farr_v\}\" + __farr_off=$((__farr_off + 1)) + done + elif [ ${__farr_length} -gt ${__farr_r_len} ]; then + # More to delete than to copy: the resulting array shrinks + __farr_delta=$((__farr_length - __farr_r_len)) + __farr_off=0 + while [ ${__farr_off} -lt ${__farr_r_len} ]; do + if [ -n "${__farr_del_name}" ]; then + eval __farr_v=\"\$\{${__farr_l_gvrname}_$((__farr_index + __farr_off))\}\" + farray_append "${__farr_del_name}" "${__farr_v}" + fi + eval __farr_v=\"\$\{${__farr_r_gvrname}_$((__farr_off + 1))\}\" + eval ${__farr_l_gvrname}_$((__farr_index + __farr_off))=\"\$\{__farr_v\}\" + __farr_off=$((__farr_off + 1)) + done + # Copy / unset the rest that is to delete + while [ ${__farr_off} -lt ${__farr_length} ]; do + if [ -n "${__farr_del_name}" ]; then + eval __farr_v=\"\$\{${__farr_l_gvrname}_$((__farr_index + __farr_off))\}\" + farray_append "${__farr_del_name}" "${__farr_v}" + fi + eval unset ${__farr_l_gvrname}_$((__farr_index + __farr_off)) + __farr_off=$((__farr_off + 1)) + done + # Move the rest + __farr_src_idx=$((__farr_index + __farr_length)) + __farr_dst_idx=$((__farr_index + __farr_r_len)) + while [ ${__farr_src_idx} -le ${__farr_l_len} ]; do + eval __farr_v=\"\$\{${__farr_l_gvrname}_${__farr_src_idx}\}\" + eval ${__farr_l_gvrname}_${__farr_dst_idx}=\"\$\{__farr_v\}\" + eval unset ${__farr_l_gvrname}_${__farr_src_idx} + __farr_src_idx=$((__farr_src_idx + 1)) + __farr_dst_idx=$((__farr_dst_idx + 1)) + done + # Adjust the length + eval ${__farr_l_gvrname}__=$((__farr_l_len - __farr_delta)) + else + # More to copy than to delete: the resulting array grows + __farr_delta=$((__farr_r_len - __farr_length)) + __farr_off=0 + while [ ${__farr_off} -lt ${__farr_length} ]; do + if [ -n "${__farr_del_name}" ]; then + eval __farr_v=\"\$\{${__farr_l_gvrname}_$((__farr_index + __farr_off))\}\" + farray_append "${__farr_del_name}" "${__farr_v}" + fi + eval __farr_v=\"\$\{${__farr_r_gvrname}_$((__farr_off + 1))\}\" + eval ${__farr_l_gvrname}_$((__farr_index + __farr_off))=\"\$\{__farr_v\}\" + __farr_off=$((__farr_off + 1)) + done + # Make room from last item to the first non-deleted + __farr_src_idx=${__farr_l_len} + __farr_dst_idx=$((__farr_src_idx + __farr_delta)) + while [ ${__farr_src_idx} -ge $((__farr_index + __farr_length)) ]; do + eval __farr_v=\"\$\{${__farr_l_gvrname}_${__farr_src_idx}\}\" + eval ${__farr_l_gvrname}_${__farr_dst_idx}=\"\$\{__farr_v\}\" + __farr_src_idx=$((__farr_src_idx - 1)) + __farr_dst_idx=$((__farr_dst_idx - 1)) + done + # + # Copy the rest of the given data to be inserted + # + # NOTE: The offset variable __farr_off from above is NOT changed in + # between and valid here! + # + while [ ${__farr_off} -lt ${__farr_r_len} ]; do + eval __farr_v=\"\$\{${__farr_r_gvrname}_$((__farr_off + 1))\}\" + eval ${__farr_l_gvrname}_$((__farr_index + __farr_off))=\"\$\{__farr_v\}\" + __farr_off=$((__farr_off + 1)) + done + # Adjust the length + eval ${__farr_l_gvrname}__=$((__farr_l_len + __farr_delta)) + fi + return 0 +} + + +#: #: Empty an existing array. #: #: Args:
