summaryrefslogtreecommitdiff
path: root/lib/std/pathjoin.myr
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std/pathjoin.myr')
-rw-r--r--lib/std/pathjoin.myr105
1 files changed, 105 insertions, 0 deletions
diff --git a/lib/std/pathjoin.myr b/lib/std/pathjoin.myr
new file mode 100644
index 0000000..f9df8aa
--- /dev/null
+++ b/lib/std/pathjoin.myr
@@ -0,0 +1,105 @@
+use "alloc.use"
+use "extremum.use"
+use "strjoin.use"
+use "strsplit.use"
+use "sleq.use"
+use "sljoin.use"
+use "sldup.use"
+use "slcp.use"
+use "die.use"
+use "fmt.use"
+
+pkg std =
+ const pathcat : (a : byte[:], b : byte[:] -> byte[:])
+ const pathjoin : (p : byte[:][:] -> byte[:])
+ const pathnorm : (p : byte[:] -> byte[:])
+;;
+
+const pathcat = {a, b
+ -> pathjoin([a, b][:])
+}
+
+const pathjoin = {l
+ var p, i, q
+
+ for i = 0; i < l.len; i++
+ if l[i].len != 0
+ break
+ ;;
+ ;;
+ p = strjoin(l[i:], "/")
+ q = pathnorm(p)
+ slfree(p)
+ -> q
+}
+
+const pathnorm = {p
+ var comps
+ var i, del, dst
+ var s, ret
+
+ comps = strsplit(p, "/")
+ /*
+ "." is a no-op component, so we drop it. In order
+ to drop a component, we set it to the empty string,
+ and remove it later on in the code.
+ */
+ for i = 0; i < comps.len; i++
+ if sleq(comps[i], ".")
+ comps[i] = ""
+ ;;
+ ;;
+
+ /*
+ then, resolve '..' by cancelling out previous components. Scan
+ backwards in the component list for the first real component,
+ and delete both it and the '..' that lead to it.
+
+ Leave in extra '..' components, so that, eg, ../foo doesn't
+ get mangled.
+ */
+ for i = 0; i < comps.len; i++
+ if !sleq(comps[i], "..")
+ continue
+ ;;
+ for del = 1; del <= i; del++
+ if comps[i - del].len > 0 && !sleq(comps[i-del], "..")
+ comps[i - del] = ""
+ comps[i] = ""
+ break
+ ;;
+ ;;
+ ;;
+
+ /* clear out the path nodes we decided to drop */
+ dst = 0
+ for i = 0; i < comps.len; i++
+ if comps[i].len > 0
+ comps[dst++] = comps[i]
+ ;;
+ ;;
+ comps = comps[:dst]
+
+ /*
+ and reassemble. If we have an absolute path,
+ make it absolute. If we have an empty path, return
+ ".". Otherwise, just return the path.
+ */
+ if p.len > 0 && sleq(p[:1], "/")
+ for i = 0; i < comps.len; i++
+ if !sleq(comps[i], "..")
+ break
+ ;;
+ ;;
+ s = strjoin(comps[i:], "/")
+ ret = fmt("/{}", s)
+ slfree(s)
+ elif comps.len == 0
+ ret = sldup(".")
+ else
+ ret = strjoin(comps, "/")
+ ;;
+ slfree(comps)
+ -> ret
+}
+