ref: c57f6ab5442e2be1500b5e8e280de537eced19d7
dir: /lib/std/pathjoin.myr/
use "alloc" use "extremum" use "strjoin" use "strsplit" use "sleq" use "sljoin" use "sldup" use "slcp" use "die" use "fmt" 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 }