1implement Names; 2 3include "sys.m"; 4 5include "names.m"; 6 7# return name rewritten to compress /+, eliminate ., and interpret .. 8 9cleanname(name: string): string 10{ 11 if(name == nil) 12 return "."; 13 14 p := rooted := name[0]=='/'; 15 if(name[0] == '#'){ # special 16 if(len name < 2) 17 return name; 18 p += 2; # character after # whatever it is, is the name (including /) 19 for(; p < len name; p++) 20 if(name[p] == '/') 21 break; 22 rooted = p; 23 } 24 dotdot := rooted; 25 26 # 27 # invariants: 28 # p points at beginning of path element we're considering. 29 # out records up to the last path element (no trailing slash unless root or #/). 30 # dotdot points in out just past the point where .. cannot backtrack 31 # any further (no slash). 32 # 33 out := name[0:rooted]; 34 while(p < len name){ 35 for(q := p; p < len name && name[p] != '/'; p++){ 36 # skip 37 } 38 n := name[q:p]; # path element 39 p++; 40 case n { 41 "" or "." => 42 ; # null effect 43 ".." => 44 if(len out > dotdot){ # can backtrack 45 for(q = len out; --q > dotdot && out[q] != '/';) 46 ; 47 out = out[:q]; 48 }else if(!rooted){ # /.. is / but ./../ is .. 49 if(out != nil) 50 out += "/.."; 51 else 52 out += ".."; 53 dotdot = len out; 54 } 55 * => 56 if(rooted > 1 || len out > rooted) 57 out[len out] = '/'; 58 out += n; 59 } 60 } 61 if(out == nil) 62 return "."; 63 return out; 64} 65 66dirname(name: string): string 67{ 68 for(i := len name; --i >= 0;) 69 if(name[i] == '/') 70 break; 71 if(i < 0) 72 return nil; 73 d := name[0:i]; 74 if(d != nil) 75 return d; 76 if(name[0] == '/') 77 return "/"; 78 return nil; 79} 80 81basename(name: string, suffix: string): string 82{ 83 for(i := len name; --i >= 0;) 84 if(name[i] == '/') 85 break; 86 if(i >= 0) 87 name = name[i+1:]; 88 if(suffix != nil){ 89 o := len name - len suffix; 90 if(o >= 0 && name[o:] == suffix) 91 return name[0:o]; 92 } 93 return name; 94} 95 96relative(name: string, root: string): string 97{ 98 if(root == nil || name == nil) 99 return name; 100 if(isprefix(root, name)){ 101 name = name[len root:]; 102 while(name != nil && name[0] == '/') 103 name = name[1:]; 104 } 105 return name; 106} 107 108rooted(root: string, name: string): string 109{ 110 if(name == nil) 111 return root; 112 if(root == nil || name[0] == '/' || name[0] == '#') 113 return name; 114 if(root[len root-1] != '/' && name[0] != '/') 115 return root+"/"+name; 116 return root+name; 117} 118 119isprefix(a: string, b: string): int 120{ 121 la := len a; 122 if(la == 0) 123 return 0; # "" isn't a pathname 124 while(la > 1 && a[la-1] == '/') 125 a = a[0:--la]; 126 lb := len b; 127 if(la > lb) 128 return 0; 129 if(la == lb) 130 return a == b; 131 return a == b[0:la] && (a == "/" || b[la] == '/'); 132} 133 134elements(name: string): list of string 135{ 136 sys := load Sys Sys->PATH; 137 (nil, fld) := sys->tokenize(name, "/"); 138 if(name != nil && name[0] == '/') 139 fld = "/" :: fld; 140 return fld; 141} 142 143pathname(els: list of string): string 144{ 145 name: string; 146 sl := els != nil && hd els == "/"; 147 for(; els != nil; els = tl els){ 148 if(!sl) 149 name += "/"; 150 name += hd els; 151 sl = 0; 152 } 153 return name; 154} 155