# # Copyright (c) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 LucĂ­a Andrea Illanes Albornoz # set +o errexit -o noglob -o nounset is assumed. # N.B. Lists separated by SP, when passed to rtl_l*() w/ explicit IFS, # must specify "" as IFS. # # # rtl_lassignV() - assigns list of variables to list of values w/ separator # @_vnames: list of variable names # @_sep: single non-zero, possibly multi-character, separator # @...: list as positional parameters # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_lassignV() { local _rla_vnames="${1}" _rla_sep="${2}" \ _rla_head="" _rla_tail="" _rla_vname="" _rla_vval="" IFS; IFS="${_rla_sep}"; shift 2; for _rla_vval in ${@}; do if ! rtl_lfirst \$_rla_head \$_rla_tail "${_rla_vnames}" " "; then return 1; else _rla_vname="${_rla_head}"; _rla_vnames="${_rla_tail}"; rtl_set_var_unsafe "${_rla_vname}" "${_rla_vval}"; fi; done; return 0; }; # # rtl_lconcat() - concatenate list with item(s) # @_rlist: inout reference to list # @_litem_new: list item(s) to concatenate with # @[_sep]: optional single non-zero, possibly multi-character, separator # N.B.: If @_litem_new is a list of multiple items, the separator of that list # must be the same as @[_sep] # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_lconcat() { rtl_lconcat2 "${1}" "${1}" "${2}" "${3:-}"; }; # # rtl_lconcat2() - concatenate list with item(s) # @_rlist_new: out reference to new list # @_rlist: in reference to list # @_litem_new: list item(s) to concatenate with # @[_sep]: optional single non-zero, possibly multi-character, separator # # Returns: zero (0) on success, non-zero (>0) on failure # N.B.: If @_litem_new is a list of multiple items, the separator of that list # must be the same as @[_sep] # rtl_lconcat2() { local _rlc2_rlist_new="${1#\$}" _rlc2_rlist="${2#\$}" _rlc2_litem_new="${3}" \ _rlc2_sep="${4:- }" IFS="${4:-${IFS:- }}"; if eval [ \"'${'${_rlc2_rlist}':+1}'\" = 1 ]; then eval ${_rlc2_rlist_new}='${'"${_rlc2_rlist}"'}${_rlc2_sep}${_rlc2_litem_new}'; else eval ${_rlc2_rlist_new}='${_rlc2_litem_new}'; fi; return 0; }; # # rtl_lfilter() - remove item(s) from list # @_rlist: inout reference to list # @_filter: item(s) to remove # @[_sep]: optional single non-zero, possibly multi-character, separator # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_lfilter() { rtl_lfilter2 "${1}" "${1}" "${2}" "${3:-}"; }; # # rtl_lfilter2() - remove item(s) from list # @_rlist: in reference to list # @_rlist_new: out reference to new list # @_filter: item(s) to remove # @[_sep]: optional single non-zero, possibly multi-character, separator # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_lfilter2() { local _rlf2_rlist="${1#\$}" _rlf2_rlist_new="${2#\$}" _rlf2_filter="${3}" _rlf2_sep="${4:-}"\ _rlf2_filterfl="" _rlf2_list="" _rlf2_litem="" _rlf2_litem_filter="" _rlf2_lnew=""; if [ "${_rlf2_sep:+1}" = 1 ]; then local IFS="${_rlf2_sep}"; else local _rlf2_sep=" " IFS="${RTL_IFS_ORIG}"; fi; if [ "${_rlf2_filter:+1}" != 1 ]; then eval ${_rlf2_rlist_new}="\${${_rlf2_rlist}}"; else eval _rlf2_list='${'"${_rlf2_rlist}"'}'; eval ${_rlf2_rlist_new}=; for _rlf2_litem in ${_rlf2_list}; do _rlf2_filterfl=0; for _rlf2_litem_filter in ${_rlf2_filter}; do if [ "${_rlf2_litem_filter}" = "${_rlf2_litem}" ]; then _rlf2_filterfl=1; break; fi; done; if [ "${_rlf2_filterfl:-0}" -eq 0 ]; then eval ${_rlf2_rlist_new}='${'"${_rlf2_rlist_new}"':+${'"${_rlf2_rlist_new}"'}${_rlf2_sep}}${_rlf2_litem}'; fi; done; fi; return 0; }; # # rtl_lfirst() - obtain first (head) and remaining (tail) item(s) in list # @_rhead: out reference to first item (head) in list, if any # @_rtail: out reference to remaining items (tail) in list, if any # @_list: list # @_sep: single non-zero, possibly multi-character, separator # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_lfirst() { local _rlf_rhead="${1#\$}" _rlf_rtail="${2#\$}" _rlf_list="${3}" _rlf_sep="${4}" IFS="${4}"; set -- ${_rlf_list}; eval ${_rlf_rhead}='${1}'; if [ "${#}" -ge 0 ]; then shift; eval ${_rlf_rtail}=\"'${*}'\"; else eval ${_rlf_rtail}=; fi; return 0; }; # # rtl_lindexV() - retrieve item from list # @_rlout: out reference to list item # @_idx: zero-based (from start of list) or negative (from end of list) index of list item # @...: list as positional parameters # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_lindexV() { local _rliV_rlout="${1#\$}" _rliV_idx="${2}" \ _rliV_limit=0; shift 2; if [ "${_rliV_idx#-}" != "${_rliV_idx}" ]; then _rliV_idx="${_rliV_idx#-}"; while [ "${#}" -gt "${_rliV_idx}" ]; do shift; done; _rliV_limit=$((${#} + 1)); else _rliV_limit="${#}"; shift "${_rliV_idx}"; fi; if [ "${#}" -ge 1 ]\ && [ "${_rliV_idx}" -lt "${_rliV_limit}" ]; then eval ${_rliV_rlout}='${1}'; else eval ${_rliV_rlout}=; fi; return 0; }; # # rtl_llength() - obtain count of items in list # @_rlen: out reference to count of items in list # @_rlist: in reference to list # @[_sep]: optional single non-zero, possibly multi-character, separator # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_llength() { local _rll_rlen="${1#\$}" _rll_rlist="${2#\$}" _rll_sep="${3:- }" IFS="${3:-${IFS:- }}"; eval set -- '${'"${_rll_rlist}"'}'\; ${_rll_rlen}='${#}'; return 0; }; # # rtl_llift() - convert list w/ separator to new list w/ new separator # @_rlist: inout reference to list # @_sep: single non-zero, possibly multi-character, separator # @_sep_new: single non-zero, possibly multi-character, separator of new list # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_llift() { rtl_llift2 "${1}" "${1}" "${2}" "${3}"; }; # # rtl_llift2() - convert list w/ separator to new list w/ new separator # @_rlist: in reference to list # @_rlist_new: out reference to new list # @_sep: single non-zero, possibly multi-character, separator # @_sep_new: single non-zero, possibly multi-character, separator of new list # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_llift2() { local _rl2_rlist="${1#\$}" _rl2_rlist_new="${2#\$}" _rl2_sep="${3:-}" \ _rl2_sep_new="${4}"; if [ "${_rl2_sep:+1}" = 1 ]; then local IFS="${_rl2_sep}"; else local _rl2_sep=" " IFS="${RTL_IFS_ORIG}"; fi; eval set -- '${'"${_rl2_rlist}"'}'; IFS="${_rl2_sep_new}"; eval ${_rl2_rlist_new}='"${*}"'; return 0; }; # # rtl_llift_set() - convert list w/ separator to set # @_rlist: in reference to list # @_rset_new: out reference to new set # @_sep: single non-zero, possibly multi-character, separator # # Notate bene: all list items in @_rlist must form valid shell parameter name substrings; # no list item must be named "__000000__" # Returns: zero (0) on success, non-zero (>0) on failure # rtl_llift_set() { local _rls3_rlist="${1#\$}" _rls3_rset_new="${2#\$}" _rls3_sep="${3:-}" \ _rls3_litem=""; if [ "${_rls3_sep:+1}" = 1 ]; then local IFS="${_rls3_sep}"; else local _rls3_sep=" " IFS="${RTL_IFS_ORIG}"; fi; eval set -- '${'"${_rls3_rlist}"'}'; eval "${_rls3_rset_new}___000000__=\${${_rls3_rlist}}"; for _rls3_litem in "${@}"; do eval "${_rls3_rset_new}_${_rls3_litem}=1"; done; return 0; }; # # rtl_llift_unset() - unset set previously created w/ rtl_llift_set() # @_rset: in reference to set # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_llift_unset() { local _rlu_rset="${1#\$}" \ _rlu_index="" _rlu_item=""; if rtl_get_var_unsafe \$_rlu_index "${_rlu_rset}___000000__"; then for _rlu_item in ${_rlu_index}; do unset "${_rlu_rset}_${_rlu_item}"; done; unset "${_rlu_rset}___000000__"; fi; return 0; }; # # rtl_lmatch() - check if item(s) in list # @_rlist: in reference to list # @_item: item(s) to search for # @[_sep]: optional single non-zero, possibly multi-character, separator # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_lmatch() { local _rlm_rlist="${1#\$}" _rlm_item="${2}" _rlm_sep="${3:-}" \ _rlm_list_=""; rtl_lsearch2 "${_rlm_rlist}" \$_rlm_list_ "${_rlm_item}" "${_rlm_sep}"; [ "${_rlm_list_:+1}" = 1 ]; }; # # rtl_lmax() - obtain maximum length of items in list # @_rlist: in reference to list # @_rmax: out reference to maximum length of items variable # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_lmax() { local _rlm2_rlist="${1#\$}" _rlm2_rmax="${2#\$}" \ _rlm2_len=0 _rlm2_len_max=0; eval set -- '${'"${_rlm2_rlist}"'}'; while [ "${#}" -gt 0 ]; do _rlm2_len="${#1}"; if [ "${_rlm2_len}" -gt "${_rlm2_len_max}" ]; then _rlm2_len_max="${_rlm2_len}"; fi; shift; done; eval ${_rlm2_rmax}='${_rlm2_len_max}'; return 0; }; # # rtl_lrangeV() - retrieve item(s) from list w/ range # @_rlout: out reference to list item # @_idx0: zero-based (from start of list) or negative (from end of list) start index of list item # @_idx1: zero-based (from start of list) or negative (from end of list) end index of list item # @_sep: single non-zero, possibly multi-character, separator # @...: list as positional parameters # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_lrangeV() { local _rlrV_rlout="${1#\$}" _rlrV_idx0="${2}" _rlrV_idx1="${3}" _rlrV_sep="${4}" \ _rlrV_limit=0; shift 4; if [ "${_rlrV_idx0#-}" != "${_rlrV_idx0}" ]; then eval ${_rlrV_rlout}=; return 1; elif [ "${_rlrV_idx1#-}" != "${_rlrV_idx1}" ]; then _rlrV_idx1="${_rlrV_idx1#-}"; _rlrV_limit="${#}"; shift "${_rlrV_idx0}"; eval ${_rlrV_rlout}=; while [ "${#}" -gt 0 ]\ && [ "${_rlrV_idx1}" -lt "${#}" ]; do eval ${_rlrV_rlout}='${'"${_rlrV_rlout}"':+${'"${_rlrV_rlout}"'}${_rlrV_sep}}${1}'; shift; done; return 0; elif [ "${_rlrV_idx1}" -ge "${_rlrV_idx0}" ]; then _rlrV_limit="${#}"; shift "${_rlrV_idx0}"; : $((_rlrV_idx1 -= ${_rlrV_idx0})); : $((_rlrV_idx1 += 1)); eval ${_rlrV_rlout}=; while [ "${_rlrV_idx1}" -gt 0 ]\ && [ "${#}" -gt 0 ]; do eval ${_rlrV_rlout}='${'"${_rlrV_rlout}"':+${'"${_rlrV_rlout}"'}${_rlrV_sep}}${1}'; : $((_rlrV_idx1 -= 1)); shift; done; return 0; fi; }; # # rtl_lsearch() - obtains item(s) from list # @_rlist: inout reference to list # @_filter: item(s) to search for and include in new list # @[_sep]: optional single non-zero, possibly multi-character, separator # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_lsearch() { rtl_lsearch2 "${1}" "${1}" "${2}" "${3:-}"; } # # rtl_lsearch2() - obtains item(s) from list # @_rlist: in reference to list # @_rlist_new: out reference to new list # @_filter: item(s) to search for and include in new list # @[_sep]: optional single non-zero, possibly multi-character, separator # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_lsearch2() { local _rls2_rlist="${1#\$}" _rls2_rlist_new="${2#\$}" \ _rls2_filter="${3}" _rls2_sep="${4:-}" \ _rls2_list="" _rls2_litem="" _rls2_litem_filter="" \ _rls2_lnew=""; if [ "${_rls2_sep:+1}" = 1 ]; then local IFS="${_rls2_sep}"; else local _rls2_sep=" " IFS="${RTL_IFS_ORIG}"; fi; if [ "${_rls2_filter:+1}" != 1 ]; then eval ${_rls2_rlist_new}='${_rls2_list}'; return 0; else eval _rls2_list='${'"${_rls2_rlist}"'}'; for _rls2_litem in ${_rls2_list}; do for _rls2_litem_filter in ${_rls2_filter}; do if [ "${_rls2_litem_filter}" = "${_rls2_litem}" ]; then _rls2_lnew="${_rls2_lnew:+${_rls2_lnew}${_rls2_sep}}${_rls2_litem}"; break; fi; done; done; fi; eval ${_rls2_rlist_new}='${_rls2_lnew}'; return 0; }; # # rtl_lsearch_patternl() - obtain item(s) from list matching pattern # @_rlist: inout reference to list # @_pattern: pattern of item(s) to search for and include in list # @[_sep]: optional single non-zero, possibly multi-character, separator # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_lsearch_patternl() { rtl_lsearch_patternl2 "${1}" "${1}" "${2}" "${3:-}"; }; # # rtl_lsearch_patternl2() - obtain item(s) from list matching pattern # @_rlist: in reference to list # @_rlist_new: out reference to new list # @_pattern: pattern of item(s) to search for and include in new list # @[_sep]: optional single non-zero, possibly multi-character, separator # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_lsearch_patternl2() { local _rlsp2_rlist="${1#\$}" _rlsp2_rlist_new="${2#\$}" \ _rlsp2_pattern="${3}" _rlsp2_sep="${4:- }" \ IFS="${4:-${IFS:- }}" \ _rlsp2_litem="" _rlsp2_lnew=""; if [ "${_rlsp2_pattern:+1}" = 1 ]; then eval _rlsp2_list='${'"${_rlsp2_rlist}"'}'; for _rlsp2_litem in ${_rlsp2_list}; do if [ "${_rlsp2_litem#${_rlsp2_pattern}}" != "${_rlsp2_litem}" ]; then _rlsp2_lnew="${_rlsp2_lnew:+${_rlsp2_lnew}${_rlsp2_sep}}${_rlsp2_litem}"; fi; done; fi; eval ${_rlsp2_rlist_new}='${_rlsp2_lnew}'; return 0; }; # # rtl_lsortV() - sort list w/ sort(1) # @...: list as positional parameters # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_lsortV() { printf "%s" "${*}" | tr " " "\n" | sort | paste -s -d " "; return 0; }; # # rtl_lunfold_dependsV() - expand list into list w/ unfolded dependencies for each list item # @_vname_template: template of variable names containing dependencies for each list item # @_rlist: out reference to list w/ unfolded dependencies # @...: list as positional parameters # # Returns: zero (0) on success, non-zero (>0) on failure # RTL_LUNFOLD_DEPENDSV_LEVEL=0; rtl_lunfold_dependsV() { local _rld_vname_template="${1}" _rld_rlist="${2#\$}" \ _rld_depends="" _rld_list="" _rld_name="" _rld_names="" \ _rld_vname_template_=""; shift 2; for _rld_name in "${@}"; do eval _rld_vname_template_=\"${_rld_vname_template}\"; if rtl_get_var_unsafe \$_rld_depends -u "${_rld_vname_template_}"\ && [ "${_rld_depends:+1}" = 1 ]; then : $((RTL_LUNFOLD_DEPENDSV_LEVEL+=1)); eval _rld_depends${RTL_LUNFOLD_DEPENDSV_LEVEL}='${_rld_depends}'; rtl_lunfold_dependsV "${_rld_vname_template}" \$_rld_depends${RTL_LUNFOLD_DEPENDSV_LEVEL} ${_rld_depends}; eval _rld_depends='${_rld_depends'"${RTL_LUNFOLD_DEPENDSV_LEVEL}"'}'; unset '_rld_depends'"${RTL_LUNFOLD_DEPENDSV_LEVEL}"; : $((RTL_LUNFOLD_DEPENDSV_LEVEL-=1)); rtl_lconcat \$_rld_names "${_rld_depends}"; fi; rtl_lconcat \$_rld_names "${_rld_name}"; done; eval ${_rld_rlist}='${_rld_names}'; return 0; }; # vim:filetype=sh textwidth=0