code: purgatorio

ref: d3da2e1b89f30f404c3d11053680098f1b7bf677
dir: /appl/lib/names.b/

View raw version
implement Names;

include "sys.m";

include "names.m";

# return name rewritten to compress /+, eliminate ., and interpret ..

cleanname(name: string): string
{
	if(name == nil)
		return ".";

	p := rooted := name[0]=='/';
	if(name[0] == '#'){	# special
		if(len name < 2)
			return name;
		p += 2;	# character after # whatever it is, is the name (including /)
		for(; p < len name; p++)
			if(name[p] == '/')
				break;
		rooted = p;
	}
	dotdot := rooted;

	#
	# invariants:
	#	p points at beginning of path element we're considering.
	#	out records up to the last path element (no trailing slash unless root or #/).
	#	dotdot points in out just past the point where .. cannot backtrack
	#		any further (no slash).
	#
	out := name[0:rooted];
	while(p < len name){
		for(q := p; p < len name && name[p] != '/'; p++){
			# skip
		}
		n := name[q:p];	# path element
		p++;
		case n {
		"" or "." =>
			;	# null effect
		".." =>
			if(len out > dotdot){	# can backtrack
				for(q = len out; --q > dotdot && out[q] != '/';)
					;
				out = out[:q];
			}else if(!rooted){	# /.. is / but ./../ is ..
				if(out != nil)
					out += "/..";
				else
					out += "..";
				dotdot = len out;
			}
		* =>
			if(rooted > 1 || len out > rooted)
				out[len out] = '/';
			out += n;
		}
	}
	if(out == nil)
		return ".";
	return out;
}

dirname(name: string): string
{
	for(i := len name; --i >= 0;)
		if(name[i] == '/')
			break;
	if(i < 0)
		return nil;
	d := name[0:i];
	if(d != nil)
		return d;
	if(name[0] == '/')
		return "/";
	return nil;
}

basename(name: string, suffix: string): string
{
	for(i := len name; --i >= 0;)
		if(name[i] == '/')
			break;
	if(i >= 0)
		name = name[i+1:];
	if(suffix != nil){
		o := len name - len suffix;
		if(o >= 0 && name[o:] == suffix)
			return name[0:o];
	}
	return name;
}

relative(name: string, root: string): string
{
	if(root == nil || name == nil)
		return name;
	if(isprefix(root, name)){
		name = name[len root:];
		while(name != nil && name[0] == '/')
			name = name[1:];
	}
	return name;
}

rooted(root: string, name: string): string
{
	if(name == nil)
		return root;
	if(root == nil || name[0] == '/' || name[0] == '#')
		return name;
	if(root[len root-1] != '/' && name[0] != '/')
		return root+"/"+name;
	return root+name;
}

isprefix(a: string, b: string): int
{
	la := len a;
	if(la == 0)
		return 0;	# "" isn't a pathname
	while(la > 1 && a[la-1] == '/')
		a = a[0:--la];
	lb := len b;
	if(la > lb)
		return 0;
	if(la == lb)
		return a == b;
	return a == b[0:la] && (a == "/" || b[la] == '/');
}

elements(name: string): list of string
{
	sys := load Sys Sys->PATH;
	(nil, fld) := sys->tokenize(name, "/");
	if(name != nil && name[0] == '/')
		fld = "/" :: fld;
	return fld;
}

pathname(els: list of string): string
{
	name: string;
	sl := els != nil && hd els == "/";
	for(; els != nil; els = tl els){
		if(!sl)
			name += "/";
		name += hd els;
		sl = 0;
	}
	return name;
}