1implement Fsmodule; 2include "sys.m"; 3 sys: Sys; 4include "readdir.m"; 5 readdir: Readdir; 6include "bufio.m"; 7 bufio: Bufio; 8 Iobuf: import bufio; 9include "string.m"; 10 str: String; 11include "draw.m"; 12include "sh.m"; 13include "fslib.m"; 14 fslib: Fslib; 15 Report, Value, type2s, report, quit: import fslib; 16 Fschan, Fsdata, Entrychan, Entry, 17 Gatechan, Gatequery, Nilentry, Option, 18 Next, Down, Skip, Quit: import Fslib; 19 20File: adt { 21 name: string; 22 mode: int; 23 owner: string; 24 group: string; 25 old: string; 26 flags: int; 27 sub: cyclic array of ref File; 28}; 29 30Proto: adt { 31 indent: int; 32 lastline: string; 33 iob: ref Iobuf; 34}; 35 36Star, Plus: con 1<<iota; 37 38types(): string 39{ 40 return "ts-rs"; 41} 42 43badmod(p: string) 44{ 45 sys->fprint(sys->fildes(2), "fs: eproto: cannot load %s: %r\n", p); 46 raise "fail:bad module"; 47} 48 49init() 50{ 51 sys = load Sys Sys->PATH; 52 fslib = load Fslib Fslib->PATH; 53 if(fslib == nil) 54 badmod(Fslib->PATH); 55 readdir = load Readdir Readdir->PATH; 56 if(readdir == nil) 57 badmod(Readdir->PATH); 58 bufio = load Bufio Bufio->PATH; 59 if(bufio == nil) 60 badmod(Bufio->PATH); 61 str = load String String->PATH; 62 if(str == nil) 63 badmod(String->PATH); 64} 65 66run(nil: ref Draw->Context, report: ref Report, 67 opts: list of Option, args: list of ref Value): ref Value 68{ 69 protofile := (hd args).s().i; 70 rootpath: string; 71 if(opts != nil) 72 rootpath = (hd (hd opts).args).s().i; 73 if(rootpath == nil) 74 rootpath = "/"; 75 76 proto := ref Proto(0, nil, nil); 77 if((proto.iob = bufio->open(protofile, Sys->OREAD)) == nil){ 78 sys->fprint(sys->fildes(2), "fs: eproto: cannot open %q: %r\n", protofile); 79 return nil; 80 } 81 root := ref File(rootpath, ~0, nil, nil, nil, 0, nil); 82 (root.flags, root.sub) = readproto(proto, -1); 83 c := Entrychan(chan of int, chan of Entry); 84 spawn protowalk(c, root, report.start("proto")); 85 return ref Value.T(c); 86} 87 88protowalk(c: Entrychan, root: ref File, errorc: chan of string) 89{ 90 if(<-c.sync == 0){ 91 quit(errorc); 92 exit; 93 } 94 protowalk1(c, root.flags, root.name, file2dir(root, nil), root.sub, -1, errorc); 95 c.c <-= (nil, nil, 0); 96 quit(errorc); 97} 98 99protowalk1(c: Entrychan, flags: int, path: string, d: ref Sys->Dir, 100 sub: array of ref File, depth: int, errorc: chan of string): int 101{ 102 if(depth >= 0) 103 c.c <-= (d, path, depth); 104 depth++; 105 (a, n) := readdir->init(path, Readdir->NAME|Readdir->COMPACT); 106 j := 0; 107 prevsub: string; 108 for(i := 0; i < n; i++){ 109 for(; j < len sub; j++){ 110 s := sub[j].name; 111 if(s == prevsub){ 112 report(errorc, sys->sprint("duplicate entry %s", pathconcat(path, s))); 113 continue; # eliminate duplicates in proto 114 } 115 if(s >= a[i].name || sub[j].old != nil) 116 break; 117 report(errorc, sys->sprint("%s not found", pathconcat(path, s))); 118 } 119 foundsub := j < len sub && (sub[j].name == a[i].name || sub[j].old != nil); 120 if(foundsub || flags&Plus || 121 (flags&Star && (a[i].mode & Sys->DMDIR)==0)){ 122 f: ref File; 123 if(foundsub){ 124 f = sub[j++]; 125 prevsub = f.name; 126 } 127 p: string; 128 d: ref Sys->Dir; 129 if(foundsub && f.old != nil){ 130 p = f.old; 131 (ok, xd) := sys->stat(p); 132 if(ok == -1){ 133 report(errorc, sys->sprint("cannot stat %q: %r", p)); 134 continue; 135 } 136 d = ref xd; 137 }else{ 138 p = pathconcat(path, a[i].name); 139 d = a[i]; 140 } 141 142 d = file2dir(f, d); 143 r: int; 144 if((d.mode & Sys->DMDIR) == 0) 145 r = walkfile(c, p, d, depth, errorc); 146 else if(flags & Plus) 147 r = protowalk1(c, Plus, p, d, nil, depth, errorc); 148 else 149 r = protowalk1(c, f.flags, p, d, f.sub, depth, errorc); 150 if(r == Skip) 151 return Next; 152 } 153 } 154 return Next; 155} 156 157pathconcat(p, name: string): string 158{ 159 if(p != nil && p[len p - 1] != '/') 160 p[len p] = '/'; 161 return p+name; 162} 163 164# from(ish) walk.b 165walkfile(c: Entrychan, path: string, d: ref Sys->Dir, depth: int, errorc: chan of string): int 166{ 167 fd := sys->open(path, Sys->OREAD); 168 if(fd == nil) 169 report(errorc, sys->sprint("cannot open %q: %r", path)); 170 else 171 c.c <-= (d, path, depth); 172 return Next; 173} 174 175readproto(proto: ref Proto, indent: int): (int, array of ref File) 176{ 177 a := array[10] of ref File; 178 n := 0; 179 flags := 0; 180 while((f := readline(proto, indent)) != nil){ 181 if(f.name == "*") 182 flags |= Star; 183 else if(f.name == "+") 184 flags |= Plus; 185 else{ 186 (f.flags, f.sub) = readproto(proto, proto.indent); 187 if(n == len a) 188 a = (array[n * 2] of ref File)[0:] = a; 189 a[n++] = f; 190 } 191 } 192 if(n < len a) 193 a = (array[n] of ref File)[0:] = a[0:n]; 194 mergesort(a, array[n] of ref File); 195 return (flags, a); 196} 197 198readline(proto: ref Proto, indent: int): ref File 199{ 200 s: string; 201 if(proto.lastline != nil){ 202 s = proto.lastline; 203 proto.lastline = nil; 204 }else if(proto.indent == -1) 205 return nil; 206 else if((s = proto.iob.gets('\n')) == nil){ 207 proto.indent = -1; 208 return nil; 209 } 210 spc := 0; 211 for(i := 0; i < len s; i++){ 212 c := s[i]; 213 if(c == ' ') 214 spc++; 215 else if(c == '\t') 216 spc += 8; 217 else 218 break; 219 } 220 if(i == len s || s[i] == '#' || s[i] == '\n') 221 return readline(proto, indent); # XXX sort out tail recursion! 222 if(spc <= indent){ 223 proto.lastline = s; 224 return nil; 225 } 226 proto.indent = spc; 227 (nil, toks) := sys->tokenize(s, " \t\n"); 228 f := ref File(nil, ~0, nil, nil, nil, 0, nil); 229 (f.name, toks) = (getname(hd toks, 0), tl toks); 230 if(toks == nil) 231 return f; 232 (f.mode, toks) = (getmode(hd toks), tl toks); 233 if(toks == nil) 234 return f; 235 (f.owner, toks) = (getname(hd toks, 1), tl toks); 236 if(toks == nil) 237 return f; 238 (f.group, toks) = (getname(hd toks, 1), tl toks); 239 if(toks == nil) 240 return f; 241 (f.old, toks) = (hd toks, tl toks); 242 return f; 243} 244 245mergesort(a, b: array of ref File) 246{ 247 r := len a; 248 if (r > 1) { 249 m := (r-1)/2 + 1; 250 mergesort(a[0:m], b[0:m]); 251 mergesort(a[m:], b[m:]); 252 b[0:] = a; 253 for ((i, j, k) := (0, m, 0); i < m && j < r; k++) { 254 if(b[i].name > b[j].name) 255 a[k] = b[j++]; 256 else 257 a[k] = b[i++]; 258 } 259 if (i < m) 260 a[k:] = b[i:m]; 261 else if (j < r) 262 a[k:] = b[j:r]; 263 } 264} 265 266getname(s: string, allowminus: int): string 267{ 268 if(s == nil) 269 return nil; 270 if(allowminus && s == "-") 271 return nil; 272 if(s[0] == '$'){ 273 s = getenv(s[1:]); 274 if(s == nil) 275 ; # TO DO: w.warn(sys->sprint("can't read environment variable %s", s)); 276 return s; 277 } 278 return s; 279} 280 281getenv(s: string): string 282{ 283 if(s == "user") 284 return readfile("/dev/user"); # more accurate? 285 return readfile("/env/"+s); 286} 287 288readfile(f: string): string 289{ 290 fd := sys->open(f, Sys->OREAD); 291 if(fd != nil){ 292 a := array[256] of byte; 293 n := sys->read(fd, a, len a); 294 if(n > 0) 295 return string a[0:n]; 296 } 297 return nil; 298} 299 300getmode(s: string): int 301{ 302 s = getname(s, 1); 303 if(s == nil) 304 return ~0; 305 m := 0; 306 i := 0; 307 if(s[i] == 'd'){ 308 m |= Sys->DMDIR; 309 i++; 310 } 311 if(i < len s && s[i] == 'a'){ 312 m |= Sys->DMAPPEND; 313 i++; 314 } 315 if(i < len s && s[i] == 'l'){ 316 m |= Sys->DMEXCL; 317 i++; 318 } 319 (xmode, t) := str->toint(s, 8); 320 if(t != nil){ 321 # report(aux.errorc, "bad mode specification %q", s); 322 return ~0; 323 } 324 return xmode | m; 325} 326 327file2dir(f: ref File, old: ref Sys->Dir): ref Sys->Dir 328{ 329 d := ref Sys->nulldir; 330 if(old != nil){ 331 if(old.dtype != 'M'){ 332 d.uid = "sys"; 333 d.gid = "sys"; 334 xmode := (old.mode >> 6) & 7; 335 d.mode = old.mode | xmode | (xmode << 3); 336 }else{ 337 d.uid = old.uid; 338 d.gid = old.gid; 339 d.mode = old.mode; 340 } 341 d.length = old.length; 342 d.mtime = old.mtime; 343 d.atime = old.atime; 344 d.muid = old.muid; 345 d.name = old.name; 346 } 347 if(f != nil){ 348 d.name = f.name; 349 if(f.owner != nil) 350 d.uid = f.owner; 351 if(f.group != nil) 352 d.gid = f.group; 353 if(f.mode != ~0) 354 d.mode = f.mode; 355 } 356 return d; 357} 358