This implements the dosym command proposed for EAPI 8 (called dosym8
because we cannot use the same name as the package-manager builtin).

"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.

Signed-off-by: Ulrich Müller <u...@gentoo.org>
---
 eclass/eapi8-dosym.eclass | 108 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 108 insertions(+)
 create mode 100644 eclass/eapi8-dosym.eclass

diff --git a/eclass/eapi8-dosym.eclass b/eclass/eapi8-dosym.eclass
new file mode 100644
index 000000000000..52f0ffe3e62b
--- /dev/null
+++ b/eclass/eapi8-dosym.eclass
@@ -0,0 +1,108 @@
+# Copyright 2020 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: eapi8-dosym.eclass
+# @MAINTAINER:
+# PMS team <p...@gentoo.org>
+# @AUTHOR:
+# Ulrich Müller <u...@gentoo.org>
+# @SUPPORTED_EAPIS: 5 6 7
+# @BLURB: Testing implementation of EAPI 8 dosym -r option
+# @DESCRIPTION:
+# A stand-alone implementation of the dosym command aimed for EAPI 8.
+# Intended to be used for wider testing of the proposed option and to
+# allow ebuilds to switch to the new model early, with minimal change
+# needed for actual EAPI 8.
+#
+# https://bugs.gentoo.org/708360
+
+case ${EAPI} in
+       5|6|7) ;;
+       *) die "${ECLASS}: EAPI=${EAPI:-0} not supported" ;;
+esac
+
+# @FUNCTION: _dosym8_canonicalize
+# @USAGE: <path>
+# @INTERNAL
+# @DESCRIPTION:
+# Transparent bash-only replacement for GNU "realpath -m -s".
+# Resolve references to "/./", "/../" and remove extra "/" characters
+# from <path>, without touching any actual file.
+_dosym8_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:-.}"
+}
+
+# @FUNCTION: dosym8
+# @USAGE: [-r] <target> <link>
+# @DESCRIPTION:
+# Create a symbolic link <link>, pointing to <target>.  If the
+# directory containing the new link does not exist, create it.
+#
+# If called with option -r, expand <target> relative to the apparent
+# path of the directory containing <link>.  For example, "dosym8 -r
+# /bin/foo /usr/bin/foo" will create a link named "../../bin/foo".
+dosym8() {
+       local option_r
+
+       case $1 in
+               -r) option_r=t; shift ;;
+       esac
+
+       [[ $# -eq 2 ]] || die "${FUNCNAME}: bad number of arguments"
+
+       local target=$1 link=$2
+
+       if [[ ${option_r} ]]; then
+               local linkdir comp
+
+               # Expansion makes sense only for an absolute target path
+               [[ ${target} == /* ]] \
+                       || die "${FUNCNAME}: -r specified but no absolute 
target path"
+
+               target=$(_dosym8_canonicalize "${target}")
+               linkdir=$(_dosym8_canonicalize "/${link#/}")
+               linkdir=${linkdir%/*}   # poor man's dirname(1)
+               linkdir=${linkdir:-/}   # always keep the initial "/"
+
+               local ifs_save=${IFS-$' \t\n'} IFS=/
+               for comp in ${linkdir}; do
+                       if [[ ${target%%/*} == "${comp}" ]]; then
+                               target=${target#"${comp}"}
+                               target=${target#/}
+                       else
+                               target=..${target:+/}${target}
+                       fi
+               done
+               IFS=${ifs_save}
+               target=${target:-.}
+       fi
+
+       dosym "${target}" "${link}"
+}
-- 
2.29.2


Reply via email to