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