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;}
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;
}