summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lisplib.c1
-rw-r--r--stdlib/copy-file.tl14
-rw-r--r--tests/018/path-equal.tl18
-rw-r--r--txr.168
4 files changed, 101 insertions, 0 deletions
diff --git a/lisplib.c b/lisplib.c
index 63ef9b1d..5e2ff629 100644
--- a/lisplib.c
+++ b/lisplib.c
@@ -829,6 +829,7 @@ static val copy_file_set_entries(val dlt, val fun)
lit("copy-path-opts"), lit("copy-file"), lit("copy-files"),
lit("copy-path-rec"), lit("remove-path-rec"),
lit("chown-rec"), lit("chmod-rec"), lit("touch"), lit("rel-path"),
+ lit("path-equal"),
nil
};
set_dlt_entries(dlt, name, fun);
diff --git a/stdlib/copy-file.tl b/stdlib/copy-file.tl
index fcb7f40c..c8cbe59f 100644
--- a/stdlib/copy-file.tl
+++ b/stdlib/copy-file.tl
@@ -246,3 +246,17 @@
(down [tcomp ncommon..:]))
(cat-str (append (repeat '("..") nup) down)
[path-sep-chars 0]))))))
+
+(defun path-equal (left right)
+ (cond
+ ((and (stringp left) (equal left right)))
+ ((neq (abs-path-p left) (abs-path-p right)) nil)
+ ((and (macro-time (find #\\ path-sep-chars))
+ (if-match `@{fdrv #/[A-Za-z0-9]+:/}@nil` left
+ (if-match `@{tdrv #/[A-Za-z0-9]+:/}@nil` right
+ (nequal fdrv tdrv))))
+ nil)
+ (t (let* ((fcomp (path-simplify (spl path-sep-chars left)))
+ (tcomp (path-simplify (spl path-sep-chars right)))
+ (ncommon (mismatch fcomp tcomp)))
+ (null ncommon)))))
diff --git a/tests/018/path-equal.tl b/tests/018/path-equal.tl
new file mode 100644
index 00000000..cf1fbb63
--- /dev/null
+++ b/tests/018/path-equal.tl
@@ -0,0 +1,18 @@
+(load "../common")
+
+(mtest
+ (path-equal "a" "a") t
+ (path-equal "a" "b") nil
+ (path-equal "/a" "a") nil
+
+ (path-equal "a" "a/") t
+ (path-equal "a/" "a/") t
+
+ (path-equal "a/b/../c" "a/c") t
+
+ (path-equal "a" "a/././.") t
+ (path-equal "a/." "a/././.") t
+
+ ;;(path-equal "/.." "/") t
+ ;;(path-equal "/../a" "/a/") t
+ )
diff --git a/txr.1 b/txr.1
index a466de95..5d285a5c 100644
--- a/txr.1
+++ b/txr.1
@@ -58302,6 +58302,74 @@ there are no files, then read from standard input:
@(end)
.brev
+.coNP Function @ path-equal
+.synb
+.mets (path-equal < left-path << right-path )
+.syne
+.desc
+The
+.code path-equal
+function determines whether the two paths
+.meta left-path
+and
+.meta right-path
+are equal under a certain definition of equivalence, whose requirements are given below.
+The function returns
+.code t
+if the paths are equal, otherwise
+.codn nil .
+
+If
+.meta left-path
+and
+.meta right-path
+are strings which are identical under the
+.code equal
+function, then they are considered equal paths.
+
+Otherwise, the two paths are equal if the relative path from
+.meta left-path
+to
+.meta right-path
+is
+.str .
+(dot), as would be determined by the
+.code path-rel
+function, if it were applied to
+.meta left-path
+and
+.meta right-path
+as its arguments. If
+.code path-rel
+would return the dot path, then the two paths are equal. If
+.code path-rel
+would return any other value, or throw an exception, then the paths are unequal.
+
+.TP* Examples:
+
+.verb
+ ;; simple case
+ (path-equal "a" "a") -> t
+ (path-equal "a" "b") -> nil
+
+ ;; trailing slashes don't matter
+ (path-equal "a" "a/") -> t
+ (path-equal "a/" "a/") -> t
+
+ ;; .. components resolved:
+ (path-equal "a/b/../c" "a/c") -> t
+
+ ;; . components resolved:
+ (path-equal "a" "a/././.") -> t
+ (path-equal "a/." "a/././.") -> t
+
+ ;; (On Microsoft Windows)
+ ;; different drive:
+ (path-equal "c:/a" "d:/b/../a") -> nil
+ ;; same drive:
+ (path-equal "c:/a" "c:/b/../a") -> t
+.brev
+
.coNP Functions @ abs-path-p and @ portable-abs-path-p
.synb
.mets (abs-path-p << path )