commit:     dfa50503f76f3e70bac50f959a9c7248e6ea880d
Author:     Ulrich Müller <ulm <AT> gentoo <DOT> org>
AuthorDate: Mon Nov 23 09:06:49 2020 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Mon May 24 05:55:41 2021 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=dfa50503

dosym: Implement -r option for EAPI 8.

"dosym -r <target> <link>" will expand the (apparent) path of <target>
relative to the (apparent) path of the directory containing <link>.
The main aim of this is to allow for an absolute path to be specified
as the link target, and the function will count path components and
convert it into a relative path.

Since we're inside ED at this point but the image will finally be
installed in EROOT, we don't try to resolve any pre-existing symlinks
in <target> or <link>. In other words, path expansion only looks at
the specified apparent paths, without touching any actual files in ED
or EROOT.

Bug: https://bugs.gentoo.org/708360
Signed-off-by: Ulrich Müller <ulm <AT> gentoo.org>
Signed-off-by: Michał Górny <mgorny <AT> gentoo.org>
Signed-off-by: Zac Medico <zmedico <AT> gentoo.org>

 bin/eapi.sh              |  4 +++
 bin/ebuild-helpers/dosym | 67 +++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 70 insertions(+), 1 deletion(-)

diff --git a/bin/eapi.sh b/bin/eapi.sh
index 80bade87e..362cc07c0 100644
--- a/bin/eapi.sh
+++ b/bin/eapi.sh
@@ -266,6 +266,10 @@ ___eapi_has_DESTTREE_INSDESTTREE() {
        [[ ${1-${EAPI-0}} =~ ^(0|1|2|3|4|4-python|4-slot-abi|5|5-progress|6)$ ]]
 }
 
+___eapi_has_dosym_r() {
+       [[ ! ${1-${EAPI-0}} =~ 
^(0|1|2|3|4|4-python|4-slot-abi|5|5-progress|6|7)$ ]]
+}
+
 ___eapi_usev_has_second_arg() {
        [[ ! ${1-${EAPI-0}} =~ 
^(0|1|2|3|4|4-python|4-slot-abi|5|5-progress|6|7)$ ]]
 }

diff --git a/bin/ebuild-helpers/dosym b/bin/ebuild-helpers/dosym
index abd4da4f0..69d38956f 100755
--- a/bin/ebuild-helpers/dosym
+++ b/bin/ebuild-helpers/dosym
@@ -4,6 +4,12 @@
 
 source "${PORTAGE_BIN_PATH}"/isolated-functions.sh || exit 1
 
+option_r=
+if [[ ___eapi_has_dosym_r && $1 == -r ]]; then
+       option_r=t
+       shift
+fi
+
 if [[ $# -ne 2 ]] ; then
        __helpers_die "${0##*/}: two arguments needed"
        exit 1
@@ -18,9 +24,68 @@ if [[ ${2} == */ ]] || [[ -d ${ED%/}/${2#/} && ! -L 
${ED%/}/${2#/} ]] ; then
        __helpers_die "${0##*/}: dosym target omits basename: '${2}'"
 fi
 
+target=$1
+
+if [[ ${option_r} ]]; then
+       # Transparent bash-only replacement for GNU "realpath -m -s".
+       # Resolve references to "/./", "/../" and remove extra "/" characters
+       # from <path>, without touching any actual file.
+       dosym_canonicalize() {
+               local path slash i prev out IFS=/
+
+               path=( $1 )
+               [[ $1 == /* ]] && slash=/
+
+               while true; do
+                       # Find first instance of non-".." path component 
followed by "..",
+                       # or as a special case, "/.." at the beginning of the 
path.
+                       # Also drop empty and "." path components as we go 
along.
+                       prev=
+                       for i in ${!path[@]}; do
+                               if [[ -z ${path[i]} || ${path[i]} == . ]]; then
+                                       unset "path[i]"
+                               elif [[ ${path[i]} != .. ]]; then
+                                       prev=${i}
+                               elif [[ ${prev} || ${slash} ]]; then
+                                       # Found, remove path components and 
reiterate
+                                       [[ ${prev} ]] && unset "path[prev]"
+                                       unset "path[i]"
+                                       continue 2
+                               fi
+                       done
+                       # No (further) instance found, so we're done
+                       break
+               done
+
+               out="${slash}${path[*]}"
+               echo "${out:-.}"
+       }
+
+       # Expansion makes sense only for an absolute target path
+       [[ ${target} == /* ]] || __helpers_die \
+               "${0##*/}: -r specified but no absolute target path: 
'${target}'"
+
+       target=$(dosym_canonicalize "${target}")
+       linkdir=$(dosym_canonicalize "/${2#/}")
+       linkdir=${linkdir%/*}   # poor man's dirname(1)
+       linkdir=${linkdir:-/}   # always keep the initial "/"
+
+       IFS=/
+       for comp in ${linkdir}; do
+               if [[ ${target%%/*} == "${comp}" ]]; then
+                       target=${target#"${comp}"}
+                       target=${target#/}
+               else
+                       target=..${target:+/}${target}
+               fi
+       done
+       unset IFS
+       target=${target:-.}
+fi
+
 destdir=${2%/*}
 [[ ! -d ${ED%/}/${destdir#/} ]] && dodir "${destdir}"
-ln -snf "${1}" "${ED%/}/${2#/}"
+ln -snf "${target}" "${ED%/}/${2#/}"
 
 ret=$?
 [[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed"

Reply via email to