summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--stdlib/doc-syms.tl1
-rw-r--r--stream.c48
-rw-r--r--stream.h1
-rw-r--r--tests/018/path.tl19
-rw-r--r--txr.180
5 files changed, 149 insertions, 0 deletions
diff --git a/stdlib/doc-syms.tl b/stdlib/doc-syms.tl
index c4a116c4..0d69f2a2 100644
--- a/stdlib/doc-syms.tl
+++ b/stdlib/doc-syms.tl
@@ -94,6 +94,7 @@
("acosh" "D-0041")
("add" "N-03244398")
("add*" "N-03244398")
+ ("add-suffix" "N-00AE9981")
("addrinfo" "N-0110E961")
("ado" "N-011CFC0C")
("af-inet" "N-0228EAE0")
diff --git a/stream.c b/stream.c
index f1a3deb0..63bc3988 100644
--- a/stream.c
+++ b/stream.c
@@ -5149,6 +5149,53 @@ val trim_long_suffix(val name)
}
}
+val add_suffix(val name, val suffix)
+{
+ val self = lit("add-suffix");
+ size_t len_n = c_unum(length_str(name), self);
+ size_t len_s = c_unum(length_str(suffix), self);
+ const wchar_t *psc = coerce(const wchar_t *, path_sep_chars);
+ const wchar_t *nam = c_str(name, self);
+ const wchar_t *suf = c_str(suffix, self);
+ const wchar_t *sl = wcspbrk(nam, psc);
+
+ if (psc[0] == '\\' || 1) {
+ const wchar_t *set = L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ L"abcdefghijklmnopqrstuvwxyz"
+ L"0123456789";
+ const wchar_t *drv = nam + wcsspn(nam, set);
+ if (drv[0] == ':' && sl == drv + 1) {
+ if (drv - nam > 1) {
+ if (wcschr(psc, drv[2]))
+ sl = wcspbrk(drv + 3, psc);
+ } else if (drv > nam) {
+ sl = wcspbrk(drv + 2, psc);
+ }
+ }
+ }
+
+ if (sl == nam)
+ sl = wcspbrk(nam + 1, psc);
+
+ while (sl) {
+ const wchar_t *nsl = sl + 1 + wcsspn(sl + 1, psc);
+
+ if (*nsl == 0) {
+ size_t nchar = len_n + len_s + 1;
+ size_t offs = sl - nam;
+ wchar_t *out = chk_wmalloc(nchar);
+ wmemcpy(out, nam, offs);
+ wmemcpy(out + offs, suf, len_s);
+ wcscpy(out + offs + len_s, sl);
+ return string_own(out);
+ }
+
+ sl = wcspbrk(nsl, psc);
+ }
+
+ return scat2(name, suffix);
+}
+
val path_cat(val dir_name, val base_name)
{
val dl = length_str(dir_name);
@@ -5474,6 +5521,7 @@ void stream_init(void)
reg_fun(intern(lit("trim-short-suffix"), user_package), func_n1(trim_short_suffix));
reg_fun(intern(lit("trim-long-suffix"), user_package), func_n1(trim_long_suffix));
reg_fun(intern(lit("path-cat"), user_package), func_n0v(path_vcat));
+ reg_fun(intern(lit("add-suffix"), user_package), func_n2(add_suffix));
reg_varl(intern(lit("path-sep-chars"), user_package), static_str(path_sep_chars));
reg_fun(intern(lit("get-indent-mode"), user_package), func_n1(get_indent_mode));
reg_fun(intern(lit("test-set-indent-mode"), user_package), func_n3(test_set_indent_mode));
diff --git a/stream.h b/stream.h
index 07c2bb80..e705c694 100644
--- a/stream.h
+++ b/stream.h
@@ -259,6 +259,7 @@ val long_suffix(val name, val alt_in);
val trim_short_suffix(val name);
val trim_long_suffix(val name);
val path_cat(val dir_name, val base_name);
+val add_suffix(val name, val suffix);
val make_byte_input_stream(val obj);
val iobuf_get(void);
void iobuf_put(val buf);
diff --git a/tests/018/path.tl b/tests/018/path.tl
index 62dbda6c..7761e0fe 100644
--- a/tests/018/path.tl
+++ b/tests/018/path.tl
@@ -134,6 +134,25 @@
(trim-long-suffix "/a.//") "/a//")
(mtest
+ (add-suffix "" "") ""
+ (add-suffix "" "a") "a"
+ (add-suffix "." "a") ".a"
+ (add-suffix "." ".a") "..a"
+ (add-suffix "/" ".b") "/.b"
+ (add-suffix "//" ".b") "/.b/"
+ (add-suffix "//" "b") "/b/"
+ (add-suffix "a" "") "a"
+ (add-suffix "a" ".b") "a.b"
+ (add-suffix "a/" ".b") "a.b/"
+ (add-suffix "a//" ".b") "a.b//"
+
+ (add-suffix "c://" "x") "c:/x/"
+ (add-suffix "0://" "x") "0:/x/"
+ (add-suffix "host://" "x") "host://x"
+ (add-suffix "host:///" "x") "host://x/"
+ (add-suffix "1234:///" "x") "1234://x/")
+
+(mtest
(base-name "") ""
(base-name "/") "/"
(base-name ".") "."
diff --git a/txr.1 b/txr.1
index 026df54d..c035ba39 100644
--- a/txr.1
+++ b/txr.1
@@ -57636,6 +57636,86 @@ characters, these are preserved in the return value.
(trim-long-suffix "a.b.c///") -> "a///"
.brev
+.coNP Function @ add-suffix
+.synb
+.mets (add-suffix < path << suffix )
+.syne
+.desc
+The
+.code add-suffix
+function combines the string arguments
+.meta path
+and
+.meta suffix
+in a way which harmonizes with the
+.code long-suffix
+and
+.code short-suffix
+functions.
+
+If
+.meta path
+does not end in a path separator character, that category being defined by the
+.code path-sep-chars
+variable, then
+.code add-suffix
+returns the trivial string catenation of
+.meta path
+and
+.metn suffix .
+
+Otherwise,
+.code add-suffix
+returns a string formed by inserting
+.meta suffix
+into
+.meta path
+just prior to the sequence of trailing path separator characters.
+The returned string is a catenation of that portion of
+.meta path
+which excludes the sequence of trailing path separators,
+followed by
+.metn suffix ,
+followed by the sequence of trailing path separators.
+
+A path separator which occurs as a part of syntax that indicates an absolute
+pathname is not considered a trailing separator. A path which begins with a
+separator is absolute. Other platform-specific path patterns may constitute
+an absolute pathname.
+
+Note: in cases when
+.meta suffix
+does not begin with a period, or is inserted in such a way
+that it is the start of a path component, then the functions
+.code long-suffix
+and
+.code short-suffix
+will not recognize
+.meta suffix
+in the resulting path.
+
+.TP* Examples:
+
+.verb
+ (add-suffix "" "") -> ""
+ (add-suffix "" "a") -> "a"
+ (add-suffix "." "a") -> ".a"
+ (add-suffix "." ".a") -> "..a"
+ (add-suffix "/" ".b") -> "/.b"
+ (add-suffix "//" ".b") -> "/.b/"
+ (add-suffix "//" "b") -> "/b/"
+ (add-suffix "a" "") -> "a"
+ (add-suffix "a" ".b") -> "a.b"
+ (add-suffix "a/" ".b") -> "a.b/"
+ (add-suffix "a//" ".b") -> "a.b//"
+
+ ;; On MS Windows
+ (add-suffix "c://" "x") -> "c:/x/"
+ (add-suffix "host://" "x") -> "host://x"
+ (add-suffix "host:///" "x") -> "host://x/"
+
+.brev
+
.coNP Function @ path-cat
.synb
.mets (path-cat >> [ dir-path <> { rel-path }*])