From 98392b9a9c340fb5c0ad86310e0a29a2b15e493c Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sun, 17 Nov 2019 12:58:49 -0600 Subject: [PATCH] Add function relative_path This function will create a path relative to another, without the use of an external program like dirname. --- test/test_unit_relative_path.py | 31 +++++++++++++++++ yadm | 60 +++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 test/test_unit_relative_path.py diff --git a/test/test_unit_relative_path.py b/test/test_unit_relative_path.py new file mode 100644 index 0000000..f723c84 --- /dev/null +++ b/test/test_unit_relative_path.py @@ -0,0 +1,31 @@ +"""Unit tests: relative_path""" +import pytest + + +@pytest.mark.parametrize( + 'base,full_path,expected', + [ + ("/A/B/C", "/A", "../.."), + ("/A/B/C", "/A/B", ".."), + ("/A/B/C", "/A/B/C", ""), + ("/A/B/C", "/A/B/C/D", "D"), + ("/A/B/C", "/A/B/C/D/E", "D/E"), + ("/A/B/C", "/A/B/D", "../D"), + ("/A/B/C", "/A/B/D/E", "../D/E"), + ("/A/B/C", "/A/D", "../../D"), + ("/A/B/C", "/A/D/E", "../../D/E"), + ("/A/B/C", "/D/E/F", "../../../D/E/F"), + ], +) +def test_relative_path(runner, paths, base, full_path, expected): + """Test translate_to_relative""" + + script = f""" + YADM_TEST=1 source {paths.pgm} + relative_path "{base}" "{full_path}" + """ + + run = runner(command=['bash'], inp=script) + assert run.success + assert run.err == '' + assert run.out.strip() == expected diff --git a/yadm b/yadm index 800b097..dc79359 100755 --- a/yadm +++ b/yadm @@ -1592,6 +1592,66 @@ function parse_encrypt() { } +function builtin_dirname() { + # dirname is not builtin, and universally available, this is a built-in + # replacement using parameter expansion + path="$1" + dname="${path%/*}" + if ! [[ "$path" =~ / ]]; then + echo "." + elif [ "$dname" = "" ]; then + echo "/" + else + echo "$dname" + fi +} + +function relative_path() { + # Output a path to $2/full, relative to $1/base + # + # This fucntion created with ideas from + # https://stackoverflow.com/questions/2564634 + base="$1" + full="$2" + + common_part="$base" + result="" + + count=0 + while [ "${full#$common_part}" == "${full}" ]; do + [ "$count" = "500" ] && return # this is a failsafe + # no match, means that candidate common part is not correct + # go up one level (reduce common part) + common_part="$(builtin_dirname "$common_part")" + # and record that we went back, with correct / handling + if [[ -z $result ]]; then + result=".." + else + result="../$result" + fi + count=$((count+1)) + done + + if [[ $common_part == "/" ]]; then + # special case for root (no common path) + result="$result/" + fi + + # since we now have identified the common part, + # compute the non-common part + forward_part="${full#$common_part}" + + # and now stick all parts together + if [[ -n $result ]] && [[ -n $forward_part ]]; then + result="$result$forward_part" + elif [[ -n $forward_part ]]; then + # extra slash removal + result="${forward_part:1}" + fi + + echo "$result" +} + # ****** Auto Functions ****** function auto_alt() {