shithub: mc

ref: ff88594df5c5df6fb8f227ab8e5df3529ba6e72f
dir: /lib/std/pathjoin.myr/

View raw version
use "alloc"
use "die"
use "extremum"
use "fmt"
use "hasprefix"
use "getcwd"
use "sleq"
use "slcp"
use "sldup"
use "sljoin"
use "strjoin"
use "strsplit"
use "traits"

pkg std =
	const pathcat	: (a : byte[:], b : byte[:] -> byte[:])
	const pathjoin	: (p : byte[:][:] -> byte[:])
	const pathnorm	: (p : byte[:] -> byte[:])
	const pathabs	: (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.
	*/
	for i = 0; i < comps.len; i++
		if eq(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 !eq(comps[i], "..")
			continue
		;;
		for del = 1; del <= i; del++
			if comps[i - del].len > 0 && !eq(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 && eq(p[:1], "/")
		for i = 0; i < comps.len; i++
			if !eq(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
}

const pathabs = {p
	if hasprefix(p, "/")
		-> pathnorm(p)
	else
		-> pathnorm(pathjoin([getcwd(), p][:]))
	;;
}