code: 9ferno

ref: 44ce0097b612a1fefd754065bdf8d9d2e5ef60c8
dir: /appl/lib/names.b/

View raw version
implement Names;

include "sys.m";
	sys: Sys;

include "names.m";
include "env.m";
include "string.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
{
	if(sys == nil)
		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;
}

# Is a given path an absolute path?
absolute(p: string): int
{
	if (len p < 2)
		return 0;
	if (p[0] == '/' || p[0] == '#')
		return 1;
	if (len p < 3 || p[0] != '.')
		return 0;
	if (p[1] == '/')
		return 1;
	if (p[1] == '.' && p[2] == '/')
		return 1;
	return 0;
}

# Search for name in $path or use sane default
# Return the full path found or the same name
searchname(progname: string): string
{
	if(sys == nil)
		sys = load Sys Sys->PATH;
	env := load Env Env->PATH;
	str := load String String->PATH;
	disfile := 0;
	pathlist: list of string;

	if (len progname >= 4 && progname[len progname-4:] == ".dis")
		disfile = 1;

	if (absolute(progname))
		pathlist = nil;

	else if ((pl := env->getenv("path")) != nil)
		pathlist = str->unquoted(pl);
	else
		pathlist = list of {"/dis", "."};

	for(; pathlist != nil; pathlist = tl pathlist)
	{
		npath := hd pathlist + "/" + progname;

		fd := sys->open(npath, sys->OREAD);
		if(fd != nil){
			return npath;
		}

		if (!disfile) {
			npath += ".dis";
			fd = sys->open(npath, sys->OREAD);
			if(fd != nil){
				return npath;
			}
		}
	}

	return progname;
}