# # Copyright (c) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 LucĂ­a Andrea Illanes Albornoz # set +o errexit -o noglob -o nounset is assumed. # RTL_NL=" "; # XXX optimise # implement %<...{...,...}...> # support multiple %<...*...> in same spec # split_ = unfold_ # impl 3/ 1## 1# 1%% 1% # # DSL functor implementation # # {{{ rtlp_install_v2_fmap($_rstatus, $_rparams, $_prefix, $_spec, $_fn, [$_param[...], --, [$_spec[...]]]) rtlp_install_v2_fmap() { local _ri2f_rstatus="${1#\$}" _ri2f_rparams="${2#\$}" _ri2f_prefix="${3}" _ri2f_spec="${4}" _ri2f_fn="${5}" \ _ri2f_IFS0="${IFS:- }" _ri2f_paramsc=0 _ri2f_spec_cur="" _ri2f_spec_list="" _ri2f_spec0="" \ _ri2f_specsc=0 _ri2f_nspec=0 _ri2f_rc=0 IFS; shift 5; while [ "${#}" -gt 0 ] && [ "x${1}" != "x--" ]; do : $((_ri2f_paramsc+=1)); local "${_ri2f_rparams}${_ri2f_paramsc}=${1}"; shift; done; shift; while [ "${#}" -gt 0 ] && [ "x${1}" != "x--" ]; do : $((_ri2f_specsc+=1)); local "_ri2f_specs${_ri2f_specsc}=${1}"; shift; done; if rtlp_install_v2_fmap_params "${_ri2f_rstatus}" "${_ri2f_rparams}" \$_ri2f_spec ""\ && rtlp_install_v2_fmap_patterns "${_ri2f_rstatus}" "${_ri2f_rparams}" "${_ri2f_prefix}" "${_ri2f_spec}" \$_ri2f_spec_list; then IFS="${RTL_NL}"; for _ri2f_spec_cur in ${_ri2f_spec_list}; do IFS="${_ri2f_IFS0}"; set --; _ri2f_nspec=1; while [ "${_ri2f_nspec}" -le "${_ri2f_specsc}" ]; do eval _ri2f_spec0=\"\${_ri2f_specs${_ri2f_nspec}}\"; rtlp_install_v2_fmap_params \ "${_ri2f_rstatus}" \ "${_ri2f_rparams}" \ \$_ri2f_spec0 "${_ri2f_spec_cur}"; eval set -- '"${@}"' '"${_ri2f_spec0}"'; : $((_ri2f_nspec+=1)); done; eval "${_ri2f_fn}" \"\$\{@\}\"; _ri2f_rc=$((${?} ? ${?} : ${_ri2f_rc})); [ "${_ri2f_rc}" -ne 0 ] && break; done; IFS="${_ri2f_IFS0}"; else _ri2f_rc=1; fi; return "${_ri2f_rc}"; }; # }}} # {{{ rtlp_install_v2_fmap_params($_rstatus, $_rparams, $_rspec, $_item) RTLP_INSTALL_FMAP_PARAMS_LEVEL=0; rtlp_install_v2_fmap_params() { local _ri2fp_rstatus="${1#\$}" _ri2fp_rparams="${2#\$}" _ri2fp_rspec="${3#\$}" _ri2fp_item="${4}" \ _ri2fp_expr="" _ri2fp_expr_="" _ri2fp_expr_sub="" _ri2fp_expr_op="" _ri2fp_lhs="" _ri2fp_rc=0 \ _ri2fp_rhs="" _ri2fp_subexpr=""; eval _ri2fp_lhs='${'"${_ri2fp_rspec}"'}'\; ${_ri2fp_rspec}=; while true; do if ! rtlp_install_v2_splitl_ref \$_ri2fp_expr \$_ri2fp_lhs \$_ri2fp_rhs '%[' ']'; then eval ${_ri2fp_rspec}='${'"${_ri2fp_rspec}"'}${_ri2fp_lhs}'; break; else case "${_ri2fp_expr}" in [0-9]*) eval _ri2fp_expr='${'"${_ri2fp_rparams}${_ri2fp_expr}"'}'; ;; @[0-9]*) : $((RTLP_INSTALL_FMAP_PARAMS_LEVEL+=1)); eval _ri2fp_expr${RTLP_INSTALL_FMAP_PARAMS_LEVEL}='${'"${_ri2fp_rparams}${_ri2fp_expr#@}"'}'; rtlp_install_v2_fmap_params "${_ri2fp_rstatus}" "${_ri2fp_rparams}" \ \$_ri2fp_expr${RTLP_INSTALL_FMAP_PARAMS_LEVEL} \ "${_ri2fp_item}"; _ri2fp_rc="${?}"; eval _ri2fp_expr='${_ri2fp_expr'"${RTLP_INSTALL_FMAP_PARAMS_LEVEL}"'}'; unset '_ri2fp_expr'"${RTLP_INSTALL_FMAP_PARAMS_LEVEL}"; : $((RTLP_INSTALL_FMAP_PARAMS_LEVEL-=1)); [ "${_ri2fp_rc}" -eq 1 ] && break; ;; [_0-9a-zA-Z]*) case "${_ri2fp_expr}" in DNAME*) _ri2fp_subexpr="${_ri2fp_expr#DNAME}"; _ri2fp_expr="${_ri2fp_item%/*}"; ;; FNAME*) _ri2fp_subexpr="${_ri2fp_expr#FNAME}"; _ri2fp_expr="${_ri2fp_item##*/}"; ;; ITEM*) _ri2fp_subexpr="${_ri2fp_expr#ITEM}"; _ri2fp_expr="${_ri2fp_item}"; ;; "") _ri2fp_rc=1; rtl_setrstatus "${_ri2fp_rstatus}" 'zero-length parameter name in expression'; ;; *) _ri2fp_expr_="${_ri2fp_expr%%[!_0-9a-zA-Z]*}"; _ri2fp_subexpr="${_ri2fp_expr#${_ri2fp_expr_}}"; _ri2fp_expr="${_ri2fp_expr_}"; if eval [ '"${'"${_ri2fp_rparams}${_ri2fp_expr}"':+1}"' = 1 ]; then eval _ri2fp_expr='${'"${_ri2fp_rparams}${_ri2fp_expr}"'}'; else _ri2fp_rc=1; rtl_setrstatus "${_ri2fp_rstatus}" 'unknown parameter name \`'"${_ri2fp_expr}"''\'' in expression'; fi; ;; esac; if [ "${_ri2fp_rc}" -eq 0 ]; then while true; do if ! rtlp_install_v2_splitl_subexpr \ \$_ri2fp_subexpr_ \ \$_ri2fp_subexpr \ '## # %% %'; then break; else case "${_ri2fp_subexpr_}" in /*) ;; \#\#*) _ri2fp_expr="${_ri2fp_expr##${_ri2fp_subexpr_#\#\#}}"; ;; \#*) _ri2fp_expr="${_ri2fp_expr#${_ri2fp_subexpr_#\#}}"; ;; %%*) _ri2fp_expr="${_ri2fp_expr%%${_ri2fp_subexpr_#%%}}"; ;; %*) _ri2fp_expr="${_ri2fp_expr%${_ri2fp_subexpr_#%}}"; ;; "") _ri2fp_rc=1; rtl_setrstatus "${_ri2fp_rstatus}" 'zero-length subexpression in expression'; ;; *) _ri2fp_rc=1; rtl_setrstatus "${_ri2fp_rstatus}" 'invalid subexpression \`'"${_ri2fp_subexpr_}"''\'' in expression'; ;; esac; fi; done; fi; ;; "") _ri2fp_rc=1; rtl_setrstatus "${_ri2fp_rstatus}" 'zero-length expression'; break; ;; *) _ri2fp_rc=1; rtl_setrstatus "${_ri2fp_rstatus}" 'invalid expression \`'"${_ri2fp_expr}"''\'; break; ;; esac; fi; eval ${_ri2fp_rspec}='${'"${_ri2fp_rspec}"'}${_ri2fp_lhs}${_ri2fp_expr}'; _ri2fp_lhs="${_ri2fp_rhs}"; done; return "${_ri2fp_rc}"; }; # }}} # {{{ rtlp_install_v2_fmap_patterns($_rstatus, $_rparams, $_prefix, $_spec, $_rspec_list) rtlp_install_v2_fmap_patterns() { local _ri2fp2_rstatus="${1#\$}" _ri2fp2_rparams="${2#\$}" _ri2fp2_prefix="${3}" \ _ri2fp2_spec="${4}" _ri2fp2_rspec_list="${5#\$}" \ _ri2fp2_expr="" _ri2fp2_lhs="" _ri2fp2_rc=0 _ri2fp2_rhs="" \ IFS="${RTL_NL}"; _ri2fp2_lhs="${_ri2fp2_spec}"; while true; do if rtlp_install_v2_splitl_ref \$_ri2fp2_expr \$_ri2fp2_lhs \$_ri2fp2_rhs '%<' '>'; then case "${_ri2fp2_expr}" in *\**) if [ "${_ri2fp2_lhs#/}" = "${_ri2fp2_lhs}" ]; then _ri2fp2_lhs_full="${_ri2fp2_prefix:+${_ri2fp2_prefix%}/}${_ri2fp2_lhs:+${_ri2fp2_lhs%/}/}"; else _ri2fp2_lhs_full="${_ri2fp2_lhs:+${_ri2fp2_lhs%/}/}"; fi; set +o noglob; for _ri2fp2_pname in ${_ri2fp2_lhs_full}${_ri2fp2_expr}; do set -o noglob; _ri2fp2_pname="${_ri2fp2_pname%/}${_ri2fp2_rhs:+/${_ri2fp2_rhs}}"; if [ -e "${_ri2fp2_pname}" ]; then eval ${_ri2fp2_rspec_list}='${'"${_ri2fp2_rspec_list}"':+${'"${_ri2fp2_rspec_list}"'}${RTL_NL}}${_ri2fp2_pname}'; fi; done; set -o noglob; ;; "") _ri2fp2_rc=1; rtl_setrstatus "${_ri2fp2_rstatus}" 'zero-length pattern'; break; ;; *) _ri2fp2_rc=1; rtl_setrstatus "${_ri2fp2_rstatus}" 'invalid pattern \`'"${_ri2fp2_expr}"''\'; break; ;; esac; _ri2fp2_lhs="${_ri2fp2_rhs}"; else eval ${_ri2fp2_rspec_list}='${'"${_ri2fp2_rspec_list}"':+${'"${_ri2fp2_rspec_list}"'}${RTL_NL}}${_ri2fp2_lhs}'; break; fi; done; return "${_ri2fp2_rc}"; }; # }}} # # Install OPeration functions # # {{{ rtlp_install_v2_op_chmod($_nflag, $_prefix, $_vflag, $_fname, $_mode) rtlp_install_v2_op_chmod() { local _ri2oc_nflag="${1}" _ri2oc_prefix="${2}" _ri2oc_vflag="${3}" _ri2oc_fname="${4}" _ri2oc_mode="${5}"; rtlp_install_v2_fixup_fname \$_ri2oc_fname "${_ri2oc_prefix}"; rtlp_install_v2_rc "${_ri2oc_nflag}" "${_ri2oc_vflag}" rtl_fileop chmod "${_ri2oc_mode}" "${_ri2oc_fname}"; }; # }}} # {{{ rtlp_install_v2_op_chgrp($_nflag, $_prefix, $_vflag, $_fname, $_group) rtlp_install_v2_op_chgrp() { local _ri2oc2_nflag="${1}" _ri2oc2_prefix="${2}" _ri2oc2_vflag="${3}" _ri2oc2_fname="${4}" _ri2oc2_group="${5}"; rtlp_install_v2_fixup_fname \$_ri2oc2_fname "${_ri2oc2_prefix}"; rtlp_install_v2_rc "${_ri2oc2_nflag}" "${_ri2oc2_vflag}" rtl_fileop chgrp "${_ri2oc2_group}" "${_ri2oc2_fname}"; }; # }}} # {{{ rtlp_install_v2_op_chown($_nflag, $_prefix, $_vflag, $_fname, $_owner) rtlp_install_v2_op_chown() { local _ri2oc3_nflag="${1}" _ri2oc3_prefix="${2}" _ri2oc3_vflag="${3}" _ri2oc3_fname="${4}" _ri2oc3_owner="${5}"; rtlp_install_v2_fixup_fname \$_ri2oc3_fname "${_ri2oc3_prefix}"; rtlp_install_v2_rc "${_ri2oc3_nflag}" "${_ri2oc3_vflag}" rtl_fileop chown "${_ri2oc3_owner}" "${_ri2oc3_fname}"; }; # }}} # {{{ rtlp_install_v2_op_cp($_nflag, $_prefix, $_vflag, $_file_fname_dst, $_file_fname_src) rtlp_install_v2_op_cp() { local _ri2oc4_nflag="${1}" _ri2oc4_prefix="${2}" _ri2oc4_vflag="${3}" _ri2oc4_fname_dst="${4}" _ri2oc4_fname_src="${5}"; rtlp_install_v2_fixup_fname \$_ri2oc4_fname_dst "${_ri2oc4_prefix}"; rtlp_install_v2_fixup_fname \$_ri2oc4_fname_src "${_ri2oc4_prefix}"; rtlp_install_v2_rc "${_ri2oc4_nflag}" "${_ri2oc4_vflag}" rtl_fileop cp "${_ri2oc4_fname_src}" "${_ri2oc4_fname_dst}"; }; # }}} # {{{ rtlp_install_v2_op_cp_follow_if_newer($_nflag, $_prefix, $_vflag, $_file_fname_dst, $_file_fname_src) rtlp_install_v2_op_cp_follow_if_newer() { local _ri2ocfin_nflag="${1}" _ri2ocfin_prefix="${2}" _ri2ocfin_vflag="${3}" _ri2ocfin_fname_dst="${4}" _ri2ocfin_fname_src="${5}"; rtlp_install_v2_fixup_fname \$_ri2ocfin_fname_dst "${_ri2ocfin_prefix}"; rtlp_install_v2_fixup_fname \$_ri2ocfin_fname_src "${_ri2ocfin_prefix}"; if [ -e "${_ri2ocfin_fname_dst}" ]\ && rtl_is_newer "${_ri2ocfin_fname_src}" "${_ri2ocfin_fname_dst}"; then return 0; else rtlp_install_v2_rc "${_ri2ocfin_nflag}" "${_ri2ocfin_vflag}" rtl_fileop cp_follow "${_ri2ocfin_fname_src}" "${_ri2ocfin_fname_dst}"; fi; }; # }}} # {{{ rtlp_install_v2_op_ln_symbolic($_nflag, $_prefix, $_vflag, $_ln_fname, $_ln_target) rtlp_install_v2_op_ln_symbolic() { local _ri2ols_nflag="${1}" _ri2ols_prefix="${2}" _ri2ols_vflag="${3}" _ri2ols_ln_fname="${4}" _ri2ols_ln_target="${5}"; rtlp_install_v2_fixup_fname \$_ri2ols_ln_fname "${_ri2ols_prefix}"; if [ -e "${_ri2ols_ln_fname}" ]; then rtlp_install_v2_rc "${_ri2ols_nflag}" "${_ri2ols_vflag}" rtl_fileop rm "${_ri2ols_ln_fname}"; fi; rtlp_install_v2_rc "${_ri2ols_nflag}" "${_ri2ols_vflag}" rtl_fileop ln_symbolic "${_ri2ols_ln_target}" "${_ri2ols_ln_fname}"; }; # }}} # {{{ rtlp_install_v2_op_mkdir($_nflag, $_prefix, $_vflag, $_dname) rtlp_install_v2_op_mkdir() { local _ri2om_nflag="${1}" _ri2om_prefix="${2}" _ri2om_vflag="${3}" _ri2om_dname="${4}"; rtlp_install_v2_fixup_fname \$_ri2om_dname "${_ri2om_prefix}"; rtlp_install_v2_rc "${_ri2om_nflag}" "${_ri2om_vflag}" rtl_fileop mkdir "${_ri2om_dname}"; }; # }}} # {{{ rtlp_install_v2_op_mv($_nflag, $_prefix, $_vflag, $_file_fname_dst, $_file_fname_src) rtlp_install_v2_op_mv() { local _ri2om2_nflag="${1}" _ri2om2_prefix="${2}" _ri2om2_vflag="${3}" _ri2om2_fname_dst="${4}" _ri2om2_fname_src="${5}"; rtlp_install_v2_fixup_fname \$_ri2om2_fname_dst "${_ri2om2_prefix}"; rtlp_install_v2_fixup_fname \$_ri2om2_fname_src "${_ri2om2_prefix}"; rtlp_install_v2_rc "${_ri2om2_nflag}" "${_ri2om2_vflag}" rtl_fileop mv "${_ri2om2_fname_src}" "${_ri2om2_fname_dst}"; }; # }}} # {{{ rtlp_install_v2_op_rm($_nflag, $_prefix, $_vflag, $_pname) rtlp_install_v2_op_rm() { local _ri2or_nflag="${1}" _ri2or_prefix="${2}" _ri2or_vflag="${3}" _ri2or_pname="${4}"; rtlp_install_v2_fixup_fname \$_ri2or_pname "${_ri2or_prefix}"; rtlp_install_v2_rc "${_ri2or_nflag}" "${_ri2or_vflag}" rtl_fileop rm "${_ri2or_pname}"; }; # }}} # {{{ rtlp_install_v2_op_touch($_nflag, $_prefix, $_vflag, $_fname, $_ts) rtlp_install_v2_op_touch() { local _ri2ot_nflag="${1}" _ri2ot_prefix="${2}" _ri2ot_vflag="${3}" _ri2ot_fname="${4}" _ri2ot_ts="${5:-}"; rtlp_install_v2_fixup_fname \$_ri2ot_fname "${_ri2ot_prefix}"; rtlp_install_v2_rc "${_ri2ot_nflag}" "${_ri2ot_vflag}" rtl_fileop touch "${_ri2ot_fname}" "${_ri2ot_ts}"; }; # }}} # # Ancillary functions # # {{{ rtlp_install_v2_fixup_fname($_rfname, $_prefix) rtlp_install_v2_fixup_fname() { local _ri2ff_rfname="${1#\$}" _ri2ff_prefix="${2}" _ri2ff_fname=""; eval _ri2ff_fname='${'"${_ri2ff_rfname}"'}'; if [ "${_ri2ff_fname#/}" = "${_ri2ff_fname}" ]; then eval ${_ri2ff_rfname}='${_ri2ff_prefix:+${_ri2ff_prefix}/}${_ri2ff_fname}'; fi; }; # }}} # {{{ rtlp_install_v2_rc($_nflag, $_vflag, $_fn, [...]) rtlp_install_v2_rc() { local _ri2r_nflag="${1}" _ri2r_vflag="${2}" _ri2r_fn="${3}" _ri2r_rc=0; shift 3; if [ "${_ri2r_nflag}" -eq 1 ]\ || [ "${_ri2r_vflag}" -gt 0 ]; then rtl_log_msgV "install" "${MSG_rtl_install_v2_rc}" "${_ri2r_fn}${_ri2r_fn:+ ${*}}"; fi; if [ "${_ri2r_nflag}" -eq 0 ]; then "${_ri2r_fn}" "${@}"; _ri2r_rc="${?}"; fi; return "${_ri2r_rc}"; }; # }}} # {{{ rtlp_install_v2_splitl($_rlhs, $_rrhs, $_sep) # # rtlp_install_v2_splitl() # Split @_rlhs from left-hand side into left-hand and right-hand side # according to @_sep w/ backslash escaping # # @_rlhs: inout reference to string and left-hand side result # @_rrhs: out reference to right-hand side result # @_sep: single non-zero, possibly multi-character, separator # # Calling convention: inout ref. @_rlhs, out ref. @_rrhs # Notate bene: @_sep is a shell pattern # Returns: zero (0) on success, non-zero (>0) on absence of unescaped @_sep in @_rlhs # rtlp_install_v2_splitl() { local _ri2s_rlhs="${1#\$}" _ri2s_rrhs="${2#\$}" _ri2s_sep="${3}" _ri2s_lhs="" \ _ri2s_lhs_new="" _ri2s_rc=1 _ri2s_rhs="" _ri2s_rhs_new=""; eval _ri2s_rhs='${'"${_ri2s_rlhs}"'}'; while [ "${_ri2s_rhs:+1}" = 1 ]; do _ri2s_lhs_new="${_ri2s_rhs%%${_ri2s_sep}*}"; if [ "${_ri2s_lhs_new}" != "${_ri2s_rhs}" ]; then _ri2s_rhs_new="${_ri2s_rhs#*${_ri2s_sep}}"; if [ "${_ri2s_lhs_new%\\}" = "${_ri2s_lhs_new}" ]; then eval ${_ri2s_rlhs}='${_ri2s_lhs}${_ri2s_lhs_new}' \ ${_ri2s_rrhs}='${_ri2s_rhs_new}'; _ri2s_rc=0; break; else _ri2s_lhs="${_ri2s_lhs}${_ri2s_lhs_new%\\}${_ri2s_sep}"; _ri2s_rhs="${_ri2s_rhs_new}"; fi; else break; fi; done; return "${_ri2s_rc}"; }; # }}} # {{{ rtlp_install_v2_splitl_ref($_ritem, $_rlhs, $_rrhs, $_sepl, $_sepr) # # rtlp_install_v2_splitl_ref() # Split @_rlhs from left-hand side into left-hand, reference, and # right-hand side according to left-hand (beginning) and right-hand # (ending) side separators w/ backslash escaping # # @_rref: out reference to reference # @_rlhs: inout reference to string and left-hand side result # @_rrhs: out reference to right-hand side result # @_sepl: single non-zero, possibly multi-character, left-hand side separator # @_sepr: single non-zero, possibly multi-character, right-hand side separator # # Calling convention: out ref. @_rref, inout ref. @_rlhs, out ref. @_rrhs # Notate bene: @_sepl and @_sepr are shell patterns # Returns: zero (0) on success, non-zero (>0) on absence of unescaped references in @_rlhs # rtlp_install_v2_splitl_ref() { local _ri2sr_rref="${1#\$}" _ri2sr_rlhs="${2#\$}" _ri2sr_rrhs="${3#\$}" _ri2sr_sepl="${4}" \ _ri2sr_sepr="${5}" _ri2sr_item="" _ri2sr_item_lhs="" _ri2sr_item_lhs_new="" \ _ri2sr_item_rhs="" _ri2sr_item_rhs_new="" _ri2sr_lhs="" _ri2sr_lhs_new="" _ri2sr_rc=1 \ _ri2sr_rhs="" _ri2sr_rhs_new=""; eval _ri2sr_rhs='${'"${_ri2sr_rlhs}"'}'; while [ "${_ri2sr_rhs:+1}" = 1 ]; do _ri2sr_lhs_new="${_ri2sr_rhs%%${_ri2sr_sepl}*}"; if [ "${_ri2sr_lhs_new}" != "${_ri2sr_rhs}" ]; then _ri2sr_rhs_new="${_ri2sr_rhs#*${_ri2sr_sepl}}"; if [ "${_ri2sr_lhs_new%\\}" = "${_ri2sr_lhs_new}" ]; then _ri2sr_item=""; _ri2sr_item_lhs=""; _ri2sr_item_rhs="${_ri2sr_rhs_new}"; while [ "${_ri2sr_item_rhs:+1}" = 1 ]; do _ri2sr_item_lhs_new="${_ri2sr_item_rhs%%${_ri2sr_sepr}*}"; if [ "${_ri2sr_item_lhs_new}" != "${_ri2sr_item_rhs}" ]; then _ri2sr_item_rhs_new="${_ri2sr_item_rhs#*${_ri2sr_sepr}}"; if [ "${_ri2sr_item_lhs_new%\\}" = "${_ri2sr_item_lhs_new}" ]; then _ri2sr_item="${_ri2sr_item_lhs}${_ri2sr_item_lhs_new}"; _ri2sr_item_rhs="${_ri2sr_item_rhs_new}"; _ri2sr_rc=0; break; else _ri2sr_item_lhs="${_ri2sr_item_lhs}${_ri2sr_item_lhs_new%\\}${_ri2sr_sepr}"; _ri2sr_item_rhs="${_ri2sr_item_rhs_new}"; fi; else break; fi; done; break; else _ri2sr_lhs="${_ri2sr_lhs}${_ri2sr_lhs_new%\\}${_ri2sr_sepl}"; _ri2sr_rhs="${_ri2sr_rhs_new}"; fi; else break; fi; done; eval ${_ri2sr_rref}='${_ri2sr_item}' \ ${_ri2sr_rlhs}='${_ri2sr_lhs}${_ri2sr_lhs_new}' \ ${_ri2sr_rrhs}='${_ri2sr_item_rhs}'; return "${_ri2sr_rc}"; }; # }}} # {{{ rtlp_install_v2_splitl_subexpr($_rexpr, $_rlhs, $_lsep) # # rtlp_install_v2_splitl_subexpr() # Split @_rlhs from left-hand side into left-hand (subexpression) and right-hand # side according to list of expression operator prefixes w/ backslash escaping # # @_rexpr: out reference to right-hand (expression) side result # @_rlhs: inout reference to string and left-hand side result # @_lsep: non-zero SP-separated list of non-zero, possibly multi-character, expression operator prefixes # # Calling convention: out ref. @_rexpr, inout ref. @_rlhs # Notate bene: @_lsep list items are shell patterns # Returns: zero (0) on success, non-zero (>0) on absence of unescaped expressions in @_rlhs # rtlp_install_v2_splitl_subexpr() { local _ri2ss_rexpr="${1#\$}" _ri2ss_rlhs="${2#\$}" _ri2ss_lsep="${3}" _ri2ss_lhs="" \ _ri2ss_matchfl="" _ri2ss_nsep="" _ri2ss_rc=1 _ri2ss_sep="" _ri2ss_sexpr="" \ _ri2ss_sexpr_lhs="" _ri2ss_sexpr_lhs_new="" _ri2ss_sexpr_rhs="" \ _ri2ss_sexpr_rhs_new="" _ri2ss_sexpr_rhs_new_min="" _ri2ss_sexpr_rhs_new_min_new="" \ _ri2ss_sexpr_sep="" _ri2ss_sexpr_sep_new="" eval _ri2ss_lhs='${'"${_ri2ss_rlhs}"'}'; set -- ${_ri2ss_lsep}; if [ "${_ri2ss_lhs:+1}" = 1 ]; then _ri2ss_matchfl=0; _ri2ss_nsep=1; while [ "${_ri2ss_nsep}" -le "${#}" ]; do eval _ri2ss_sep='${'"${_ri2ss_nsep}"'}'; case "${_ri2ss_lhs}" in ${_ri2ss_sep}*) _ri2ss_matchfl=1; break; ;; *) : $((_ri2ss_nsep+=1)); ;; esac; done; if [ "${_ri2ss_matchfl}" -eq 1 ]; then _ri2ss_sexpr=""; _ri2ss_sexpr_lhs="${_ri2ss_sep}"; _ri2ss_sexpr_rhs="${_ri2ss_lhs#${_ri2ss_sep}}"; while [ "${_ri2ss_sexpr_rhs:+1}" = 1 ]; do _ri2ss_sexpr_rhs_new_min=-1; _ri2ss_sexpr_sep=""; _ri2ss_nsep=1; while [ "${_ri2ss_nsep}" -le "${#}" ]; do eval _ri2ss_sexpr_sep_new='${'"${_ri2ss_nsep}"'}'; _ri2ss_sexpr_rhs_new="${_ri2ss_sexpr_rhs%%${_ri2ss_sexpr_sep_new}*}"; if [ "${_ri2ss_sexpr_rhs_new}" != "${_ri2ss_sexpr_rhs}" ]; then _ri2ss_sexpr_rhs_new_min_new="${#_ri2ss_sexpr_rhs_new}"; if [ "${_ri2ss_sexpr_rhs_new_min_new}" -le "${_ri2ss_sexpr_rhs_new_min}" ]\ || [ "${_ri2ss_sexpr_rhs_new_min}" -eq -1 ]; then _ri2ss_sexpr_rhs_new_min="${_ri2ss_sexpr_rhs_new_min_new}"; _ri2ss_sexpr_sep="${_ri2ss_sexpr_sep_new}"; fi; fi; : $((_ri2ss_nsep+=1)); done; if [ "${_ri2ss_sexpr_sep:+1}" = 1 ]; then _ri2ss_sexpr_lhs_new="${_ri2ss_sexpr_rhs%%${_ri2ss_sexpr_sep}*}"; if [ "${_ri2ss_sexpr_lhs_new%\\}" = "${_ri2ss_sexpr_lhs_new}" ]; then _ri2ss_sexpr_lhs="${_ri2ss_sexpr_lhs}${_ri2ss_sexpr_lhs_new}"; _ri2ss_sexpr_rhs="${_ri2ss_sexpr_sep}${_ri2ss_sexpr_rhs#*${_ri2ss_sexpr_sep}}"; break; else _ri2ss_sexpr_lhs="${_ri2ss_sexpr_lhs}${_ri2ss_sexpr_rhs%%\\${_ri2ss_sexpr_sep}*}${_ri2ss_sexpr_sep}"; _ri2ss_sexpr_rhs="${_ri2ss_sexpr_rhs#*\\${_ri2ss_sexpr_sep}}"; fi; else _ri2ss_sexpr_lhs="${_ri2ss_sexpr_lhs}${_ri2ss_sexpr_rhs}"; _ri2ss_sexpr_rhs=""; break; fi; done; eval ${_ri2ss_rexpr}='${_ri2ss_sexpr_lhs}' ${_ri2ss_rlhs}='${_ri2ss_sexpr_rhs}'; _ri2ss_rc=0; fi; fi; return "${_ri2ss_rc}"; }; # }}} # # rtl_install_v2() - install files # @_rstatus: reference to out variable of status string on failure # @[-i]: continue on soft errors # @[-I ifs]: process spec_list with ifs instead of NL # @[-n]: perform dry run # @[-p name=val]: set named parameter # @[-v]: increase verbosity # @_prefix: pathname prefix # @_spec_list: ifs-separated list of specs # # {{{ File installation DSL # # ``` # (* # SH_GLOB_PATTERN = any valid portable shell pattern (see sh(1)); superset of PATHNAME # SH_SUBSTRING_PATTERN = any valid portable substring processing shell pattern (see sh(1)); # superset of PATHNAME # PARAMETER = any valid portable shell variable name except that [0-9] may occur # the beginning # PATHNAME = any valid filename, directory name, relative or absolute pathname # excluding the characters NUL and NL # *) # # spec = { op_flag, } op_unary, "=", op_spec, "\n", { spec } ; # | { op_flag, } op_binary, op_spec, "=" op_spec, "\n", { spec } ; # | "#" COMMENT ; # op_unary = "-" | "/" | "t" ; # op_binary = ":" | "!" | "@" | "+" | "g" | "m" | "o" | "T" ; # op_flag = "?" ; # op_spec = pattern_spec | PATHNAME | expr_spec | op_spec ; # # pattern_spec = "%<", SH_GLOB_PATTERN, ">" ; # # expr_spec = "%[", expr, { sexpr_spec }, "%]" ; # expr = [ "@" ], "0" .. "9" | "DNAME" | "FNAME" | "ITEM" | PARAMETER ; # # sexpr_spec = sexpr_op_unary, SH_SUBSTRING_PATTERN, { sexpr } ; # sexpr_op_unary = "##" | "#" | "%%" | "%" ; # ``` # # Single ``"="`` characters in ``spec``, the ``"%<"`` and ``"%["`` character # sequences in ``pattern_spec`` and ``expr_spec``, resp., and the ``sexpr_op_unary`` # as well as ``sexpr_op_binary`` characters or character sequences may be # escaped with a single backslash (``"\"``.) ``SH_SUBSTRING_PATTERN`` differs # from ``SH_GLOB_PATTERN`` solely in that any of ``sexpr_op_unary`` and # ``sexpr_op_binary`` occuring at the beginning of or in the former must # be escaped with a single backslash (``"\"``,) e.g. ``"#\#pattern"`` and # ``"%\%pattern"``, etc. and ``"#pat\%ern"`` and ``%patt\#ern", etc., resp. # # Named parameters (``PARAMETER``) are supplied via the ``-p name=value`` # argument to ``rtl_install()``, whereas numbered parameters are for # internal usage only; the ``"DNAME"``, ``"FNAME"``, and ``"ITEM"`` parameters # lazily evaluate to the directory name, file (aka base) name, and full # pathname of the current item being processed relative to a specification # with a pattern in it. # # The following operation flags are defined: # # | Flag | Description | # | --------- | ------------------------ | # | ``?`` | Continue on soft failure | # # The following operations are defined: # # | Operation | Arity | Description | # | -------------- | ------ | ---------------------------------------------------------------- | # | ``-`` | Unary | Remove directories and/or files | # | ``/`` | Unary | Create directories or trees thereof | # | ``t`` | Unary | touch(1) files and/or directories | # | ``:`` | Binary | Copy directories and/or files | # | ``!`` | Binary | Move/rename directories and/or files | # | ``@`` | Binary | Create/update symbolic links | # | ``+`` | Binary | Copy directories and/or files if newer and follow symbolic links | # | ``g`` | Binary | Set group owner of files and/or directories | # | ``m`` | Binary | Set mode bits of files and/or directories | # | ``o`` | Binary | Set user and/or group owner of files and/or directories | # | ``T`` | Binary | touch(1) files and/or directories with timestamp | # # The following expression modifiers are defined: # # | Modifier | Description | # | -------------- | ----------------------------------------- | # | ``@`` | Recursively reevaluate after substituting | # # The following subexpression operators are defined: # # | Operation | Arity | Description | # | -------------- | ------ | ---------------------------------------------------------------- | # | ``##`` | Unary | Remove largest prefix from left-hand side | # | ``#`` | Unary | Remove prefix from left-hand side | # | ``%%`` | Unary | Remove largest postfix from right-hand side | # | ``%`` | Unary | Remove postfix from right-hand side | # # ```shell # # # # Examples: # # # # # # # Create directory %[_minipix]/bin and copy all files # # in %[_minipix_dist]/bin/ to %[_minipix]/bin/ with # # identical file names. # /=%[_minipix]/bin # ?%[_minipix_dist]/bin/%<*>=%[_minipix]/bin/%[FNAME] # # # # # Rename all files in share/info/ matching *.info to # # their filenames with the `.info' postfix removed and # # `-2.64.info' appended and all files in share/man/man1/ # # matching *.1 with the `.1' postfix removed and -2.64.1 # # appended. # !share/info/%<*.info>=share/info/%[FNAME%.info]-2.64.info # !share/man/man1/%<*.1>=share/man/man1/%[FNAME%.1]-2.64.1 # # # # # Create/update symbolic links named include/ffi.h and # # include/ffitarget.h with ../lib/libffi-3.2.1/include/ffi.h # # and ../lib/libffi-3.2.1/include/ffitarget.h as targets, resp. # @../lib/libffi-3.2.1/include/ffi.h=include/ffi.h # @../lib/libffi-3.2.1/include/ffitarget.h=include/ffitarget.h # # ``` # # }}} # # Returns: zero (0) on success, non-zero (>0) on failure # rtl_install_v2() { local _ri2_rstatus="${1#\$}" \ _ri2_prefix="" _ri2_spec_flag="" _ri2_spec_list="" _ri2_iflag=0 _ri2_IFS="${IFS:- }" \ _ri2_nflag=0 _ri2_paramsc=0 _ri2_vflag=0 _ri2_IFS0 _ri2_nparam=0 _ri2_opt="" _ri2_param="" \ _ri2_rc=0 _ri2_spec="" _ri2_spec_dst="" _ri2_spec_src="" IFS OPTARG="" OPTIND=1; shift; while true; do if [ "${1:-}" = "--" ]; then : $((OPTIND+=1)); break; elif ! getopts hiI:np:v _ri2_opt; then break; else case "${_ri2_opt}" in h) printf "usage: rtl_install [-i] [-I ifs] [-n] [-p name=val] [-v] prefix spec_list\n" >&2; printf " -i...........: continue on soft errors\n" >&2; printf " -I ifs.......: process spec_list with ifs instead of NL\n" >&2; printf " -n...........: perform dry run\n" >&2; printf " -p name=val..: set named parameter\n" >&2; printf " -v...........: increase verbosity\n" >&2; printf " prefix.......: pathname prefix\n" >&2; printf " spec_list....: ifs-separated list of specs\n" >&2; return 1; ;; i) _ri2_iflag=1; ;; I) _ri2_IFS="${OPTARG}"; ;; n) _ri2_nflag=1; ;; p) : $((_ri2_paramsc+=1)); local _ri2_params${OPTARG%%=*}="${OPTARG#*=}"; ;; v) : $((_ri2_vflag+=1)); ;; *) return 1; ;; esac; fi; done; shift $((${OPTIND}-1)); _ri2_prefix="${1:-}"; _ri2_spec_list="${2:-}"; shift 2; _ri2_IFS0="${IFS:- }"; IFS="${_ri2_IFS}"; set -- ${_ri2_spec_list}; IFS="${_ri2_IFS0}"; while [ ${#} -gt 0 ]; do _ri2_spec_src="${1}"; case "${_ri2_spec_src}" in \?*) _ri2_spec_flag="?"; ;; *) _ri2_spec_flag=""; ;; esac; if ! rtlp_install_v2_splitl \$_ri2_spec_src \$_ri2_spec_dst "="; then _ri2_rc=1; rtl_setrstatus "${_ri2_rstatus}" 'zero-length or invalid specification \`'"${1}"''\'; else case "${_ri2_spec_src}" in -) rtlp_install_v2_fmap "${_ri2_rstatus}" \$_ri2_params "${_ri2_prefix}" "${_ri2_spec_dst}" \ rtlp_install_v2_op_rm "${_ri2_nflag}" "${_ri2_prefix}" "${_ri2_vflag}" \ "" \ -- "%[1]" "%[2]" "%[3]" "%[ITEM]"; ;; /) rtlp_install_v2_fmap "${_ri2_rstatus}" \$_ri2_params "${_ri2_prefix}" "${_ri2_spec_dst}" \ rtlp_install_v2_op_mkdir "${_ri2_nflag}" "${_ri2_prefix}" "${_ri2_vflag}" \ "" \ -- "%[1]" "%[2]" "%[3]" "%[ITEM]"; ;; t*) rtlp_install_v2_fmap "${_ri2_rstatus}" \$_ri2_params "${_ri2_prefix}" "${_ri2_spec_dst}" \ rtlp_install_v2_op_touch "${_ri2_nflag}" "${_ri2_prefix}" "${_ri2_vflag}" \ "" "" \ -- "%[1]" "%[2]" "%[3]" "%[ITEM]" "%[5]"; ;; :*) rtlp_install_v2_fmap "${_ri2_rstatus}" \$_ri2_params "${_ri2_prefix}" "${_ri2_spec_src#:}" \ rtlp_install_v2_op_cp "${_ri2_nflag}" "${_ri2_prefix}" "${_ri2_vflag}" \ "${_ri2_spec_dst}" "" \ -- "%[1]" "%[2]" "%[3]" "%[@4]" "%[ITEM]"; ;; !*) rtlp_install_v2_fmap "${_ri2_rstatus}" \$_ri2_params "${_ri2_prefix}" "${_ri2_spec_src#!}" \ rtlp_install_v2_op_mv "${_ri2_nflag}" "${_ri2_prefix}" "${_ri2_vflag}" \ "${_ri2_spec_dst}" "" \ -- "%[1]" "%[2]" "%[3]" "%[@4]" "%[ITEM]"; ;; @*) rtlp_install_v2_fmap "${_ri2_rstatus}" \$_ri2_params "${_ri2_prefix}" "${_ri2_spec_dst}" \ rtlp_install_v2_op_ln_symbolic "${_ri2_nflag}" "${_ri2_prefix}" \ "${_ri2_vflag}" "" "${_ri2_spec_src#@}" "" \ -- "%[1]" "%[2]" "%[3]" "%[ITEM]" "%[@5]"; ;; +*) rtlp_install_v2_fmap "${_ri2_rstatus}" \$_ri2_params "${_ri2_prefix}" "${_ri2_spec_src#+}" \ rtlp_install_v2_op_cp_follow_if_newer "${_ri2_nflag}" "${_ri2_prefix}" \ "${_ri2_vflag}" "${_ri2_spec_dst}" "" \ -- "%[1]" "%[2]" "%[3]" "%[@4]" "%[ITEM]"; ;; g*) rtlp_install_v2_fmap "${_ri2_rstatus}" \$_ri2_params "${_ri2_prefix}" "${_ri2_spec_dst}" \ rtlp_install_v2_op_chgrp "${_ri2_nflag}" "${_ri2_prefix}" "${_ri2_vflag}" \ "" "${_ri2_spec_src#g}" \ -- "%[1]" "%[2]" "%[3]" "%[ITEM]" "%[5]"; ;; m[0-7][0-7][0-7][0-7]) rtlp_install_v2_fmap "${_ri2_rstatus}" \$_ri2_params "${_ri2_prefix}" "${_ri2_spec_dst}" \ rtlp_install_v2_op_chmod "${_ri2_nflag}" "${_ri2_prefix}" "${_ri2_vflag}" \ "" "${_ri2_spec_src#m}" \ -- "%[1]" "%[2]" "%[3]" "%[ITEM]" "%[5]"; ;; o*) rtlp_install_v2_fmap "${_ri2_rstatus}" \$_ri2_params "${_ri2_prefix}" "${_ri2_spec_dst}" \ rtlp_install_v2_op_chown "${_ri2_nflag}" "${_ri2_prefix}" "${_ri2_vflag}" \ "" "${_ri2_spec_src#o}" \ -- "%[1]" "%[2]" "%[3]" "%[ITEM]" "%[5]"; ;; T*) rtlp_install_v2_fmap "${_ri2_rstatus}" \$_ri2_params "${_ri2_prefix}" "${_ri2_spec_dst}" \ rtlp_install_v2_op_touch "${_ri2_nflag}" "${_ri2_prefix}" "${_ri2_vflag}" \ "" "${_ri2_spec_src#T}" \ -- "%[1]" "%[2]" "%[3]" "%[ITEM]" "%[5]"; ;; \#*|"") ;; esac; _ri2_rc="${?}"; fi; shift; if [ "${_ri2_rc}" -ne 0 ]\ && [ "${_ri2_iflag}" -eq 0 ]; then break; fi; done; return "${_ri2_rc}"; }; # vim:filetype=sh textwidth=0