diff options
author | Karen Arutyunov <karen@codesynthesis.com> | 2020-03-15 17:02:22 +0300 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2020-03-17 13:16:33 +0300 |
commit | 955afeb419ed02a078b45312949767692751274c (patch) | |
tree | 1f2190353eb6fd69b065ed0a0c11f8da9f39ef4e | |
parent | 56e49a09b4f1d268bfee83324bbcd44eb925815b (diff) |
Fix ln builtin not to complete relative target against working directory
Now it preserves the relative path when creates a symlink and completes it
against the link directory when creates a hardlink or a copy.
-rw-r--r-- | libbutl/builtin.cxx | 50 | ||||
-rw-r--r-- | tests/builtin/ln.testscript | 6 | ||||
-rw-r--r-- | tests/dir-iterator/testscript | 2 | ||||
-rw-r--r-- | tests/mventry/testscript | 2 | ||||
-rw-r--r-- | tests/wildcard/testscript | 2 |
5 files changed, 38 insertions, 24 deletions
diff --git a/libbutl/builtin.cxx b/libbutl/builtin.cxx index a041820..f1b6b63 100644 --- a/libbutl/builtin.cxx +++ b/libbutl/builtin.cxx @@ -242,16 +242,17 @@ namespace butl return ops; } - // Parse and normalize a path. Also, unless it is already absolute, make the - // path absolute using the specified directory (must be an absolute path). - // Fail if the path is empty, and on parsing and normalization errors. + // Parse and normalize a path. Also make a relative path absolute using the + // specified directory path if it is not empty (in which case it must be + // absolute). Fail if the path is empty, and on parsing and normalization + // errors. // static path parse_path (string s, const dir_path& d, const function<error_record ()>& fail) { - assert (d.absolute ()); + assert (d.empty () || d.absolute ()); try { @@ -260,7 +261,7 @@ namespace butl if (p.empty ()) throw invalid_path (""); - if (p.relative ()) + if (p.relative () && !d.empty ()) p = d / move (p); p.normalize (); @@ -775,26 +776,31 @@ namespace butl const builtin_callbacks& cbs, const function<error_record ()>& fail) { - assert (target.absolute () && target.normalized ()); assert (link.absolute () && link.normalized ()); - // Determine the target type, fail if the target doesn't exist. + // Determine the target type, fail if the target doesn't exist. Note that + // to do that we need to complete a relative target path against the link + // directory making the target path absolute. // + const path& atp (target.relative () + ? link.directory () / target + : target); + bool dir (false); try { - pair<bool, entry_stat> pe (path_entry (target)); + pair<bool, entry_stat> pe (path_entry (atp)); if (!pe.first) - fail () << "unable to create symlink to '" << target << "': no such " + fail () << "unable to create symlink to '" << atp << "': no such " << "file or directory"; dir = pe.second.type == entry_type::directory; } catch (const system_error& e) { - fail () << "unable to stat '" << target << "': " << e; + fail () << "unable to stat '" << atp << "': " << e; } // First we try to create a symlink. If that fails (e.g., "Windows @@ -824,11 +830,14 @@ namespace butl (c == ENOSYS || // Not implemented. c == EPERM))) // Not supported by the filesystem(s). fail () << "unable to create symlink '" << link << "' to '" - << target << "': " << e; + << atp << "': " << e; + // Note that for hardlinking/copying we need to use the complete + // (absolute) target path. + // try { - mkhardlink (target, link, dir); + mkhardlink (atp, link, dir); if (cbs.create) call (fail, cbs.create, link, false /* pre */); @@ -840,16 +849,16 @@ namespace butl (c == ENOSYS || // Not implemented. c == EPERM || // Not supported by the filesystem(s). c == EXDEV))) // On different filesystems. - fail () << "unable to create hardlink '" << link << "' to '" - << target << "': " << e; + fail () << "unable to create hardlink '" << link << "' to '" << atp + << "': " << e; if (dir) - cpdir (path_cast<dir_path> (target), path_cast<dir_path> (link), + cpdir (path_cast<dir_path> (atp), path_cast<dir_path> (link), false /* attrs */, cbs, fail); else - cpfile (target, link, + cpfile (atp, link, false /* overwrite */, true /* attrs */, cbs, @@ -924,7 +933,9 @@ namespace butl // if (!link.to_directory ()) { - path target (parse_path (move (*i++), wd, fail)); + // Don't complete a relative target and pass it to mksymlink() as is. + // + path target (parse_path (move (*i++), dir_path (), fail)); // If there are multiple targets but no trailing separator for the // link, then, most likelly, it is missing. @@ -940,7 +951,10 @@ namespace butl { for (; i != e; ++i) { - path target (parse_path (move (*i), wd, fail)); + // Don't complete a relative target and pass it to mksymlink() as + // is. + // + path target (parse_path (move (*i), dir_path (), fail)); // Synopsis 2: create a target path symlink in the specified // directory. diff --git a/tests/builtin/ln.testscript b/tests/builtin/ln.testscript index c12999f..051d0dc 100644 --- a/tests/builtin/ln.testscript +++ b/tests/builtin/ln.testscript @@ -112,7 +112,7 @@ $* -u >'option -u' 2>"ln: unknown option '-u'" == 1 { touch a; - $* -s a b/c >>/~%EOO% 2>>/~%EOE% != 0 + $* -s $~/a b/c >>/~%EOO% 2>>/~%EOE% != 0 %create .+/b/c true% EOO %( @@ -185,7 +185,7 @@ $* -u >'option -u' 2>"ln: unknown option '-u'" == 1 { mkdir a; - $* -s a b/c >>/~%EOO% 2>>/~%EOE% != 0 + $* -s $~/a b/c >>/~%EOO% 2>>/~%EOE% != 0 %create .+/b/c true% EOO %( @@ -205,7 +205,7 @@ $* -u >'option -u' 2>"ln: unknown option '-u'" == 1 touch a; mkdir b c; - $* -s a b c/ >>/~%EOO% &c/a &c/b; + $* -s ../a ../b c/ >>/~%EOO% &c/a &c/b; %create .+/c/a true% %create .+/c/a false% %create .+/c/b true% diff --git a/tests/dir-iterator/testscript b/tests/dir-iterator/testscript index ec7338d..03ed164 100644 --- a/tests/dir-iterator/testscript +++ b/tests/dir-iterator/testscript @@ -24,7 +24,7 @@ $* a >"dir b" if ($test.target == $build.host) { +if ($cxx.target.class != 'windows') - lnf = ln -s wd/t wd/l &wd/l + lnf = ^ln -s t wd/l &wd/l lnd = $lnf else echo 'yes' >=t diff --git a/tests/mventry/testscript b/tests/mventry/testscript index 54a3acc..61ef871 100644 --- a/tests/mventry/testscript +++ b/tests/mventry/testscript @@ -98,7 +98,7 @@ if ($test.target == $build.host) { +if ($cxx.target.class != 'windows') - lnf = ln -s t l &l + lnf = ^ln -s t l &l lnd = $lnf else echo 'yes' >=t diff --git a/tests/wildcard/testscript b/tests/wildcard/testscript index 3590aa3..5f6a767 100644 --- a/tests/wildcard/testscript +++ b/tests/wildcard/testscript @@ -650,7 +650,7 @@ { mkdir a; touch --no-cleanup a/b; - ln -s a/b a/l; + ^ln -s b a/l &a/l; rm a/b; touch a/c; |