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;
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] && (b[la] == '/' || a == "/");
}
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;
}
|