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, Empty: con 1<<iota; 37 38types(): string 39{ 40 return "xs-rs"; 41} 42 43badmod(p: string) 44{ 45 sys->fprint(sys->fildes(2), "fs: proto: 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: proto: 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 := chan of (Fsdata, chan of int); 84 spawn protowalk(c, root, report.start("proto")); 85 return ref Value.X(c); 86} 87 88protowalk(c: Fschan, root: ref File, errorc: chan of string) 89{ 90 protowalk1(c, root.flags, root.name, file2dir(root, nil), root.sub, errorc); 91 quit(errorc); 92} 93 94protowalk1(c: Fschan, flags: int, path: string, d: ref Sys->Dir, 95 sub: array of ref File, errorc: chan of string): int 96{ 97 reply := chan of int; 98 c <-= ((d, nil), reply); 99 case r := <-reply { 100 Quit => 101 quit(errorc); 102 Next or 103 Skip => 104 return r; 105 } 106 a: array of ref Sys->Dir; 107 n := 0; 108 if((flags&Empty)==0) 109 (a, n) = readdir->init(path, Readdir->NAME|Readdir->COMPACT); 110 i := j := 0; 111 prevsub: string; 112 while(i < n || j < len sub){ 113 for(; j < len sub; j++){ 114 s := sub[j].name; 115 if(s == prevsub){ 116 report(errorc, sys->sprint("duplicate entry %s", pathconcat(path, s))); 117 continue; # eliminate duplicates in proto 118 } 119 # if we're copying from an old file, and there's a matching 120 # entry in the directory, then skip it. 121 if(sub[j].old != nil && i < n && s == a[i].name) 122 i++; 123 if(sub[j].old != nil || i < n && s >= a[i].name) 124 break; 125 report(errorc, sys->sprint("%s not found", pathconcat(path, s))); 126 } 127 foundsub := j < len sub && (sub[j].old != nil || sub[j].name == a[i].name); 128 129 if(foundsub || flags&(Plus|Star)){ 130 f: ref File; 131 if(foundsub){ 132 f = sub[j++]; 133 prevsub = f.name; 134 } 135 p: string; 136 d: ref Sys->Dir; 137 if(foundsub && f.old != nil){ 138 p = f.old; 139 (ok, xd) := sys->stat(p); 140 if(ok == -1){ 141 report(errorc, sys->sprint("cannot stat %q: %r", p)); 142 continue; 143 } 144 d = ref xd; 145 }else{ 146 p = pathconcat(path, a[i].name); 147 d = a[i++]; 148 } 149 150 d = file2dir(f, d); 151 r: int; 152 if((d.mode & Sys->DMDIR) == 0) 153 r = walkfile(c, p, d, errorc); 154 else if(flags & Plus) 155 r = protowalk1(c, Plus, p, d, nil, errorc); 156 else if((flags&Star) && !foundsub) 157 r = protowalk1(c, Empty, p, d, nil, errorc); 158 else 159 r = protowalk1(c, f.flags, p, d, f.sub, errorc); 160 if(r == Skip) 161 return Next; 162 }else 163 i++; 164 } 165 166 c <-= ((nil, nil), reply); 167 if(<-reply == Quit) 168 quit(errorc); 169 return Next; 170} 171 172pathconcat(p, name: string): string 173{ 174 if(p != nil && p[len p - 1] != '/') 175 p[len p] = '/'; 176 p += name; 177 return p; 178} 179 180# from(ish) walk.b 181walkfile(c: Fschan, path: string, d: ref Sys->Dir, errorc: chan of string): int 182{ 183 reply := chan of int; 184 fd := sys->open(path, Sys->OREAD); 185 if(fd == nil){ 186 report(errorc, sys->sprint("cannot open %q: %r", path)); 187 return Next; 188 } 189 c <-= ((d, nil), reply); 190 case r := <-reply { 191 Quit => 192 quit(errorc); 193 Next or 194 Skip => 195 return r; 196 } 197 length := d.length; 198 for(n := big 0; n < length; ){ 199 nr := Sys->ATOMICIO; 200 if(n + big Sys->ATOMICIO > length) 201 nr = int (length - n); 202 buf := array[nr] of byte; 203 nr = sys->read(fd, buf, nr); 204 if(nr <= 0){ 205 if(nr < 0) 206 report(errorc, sys->sprint("error reading %q: %r", path)); 207 else 208 report(errorc, sys->sprint("%q is shorter than expected (%bd/%bd)", 209 path, n, length)); 210 break; 211 }else if(nr < len buf) 212 buf = buf[0:nr]; 213 c <-= ((nil, buf), reply); 214 case <-reply { 215 Quit => 216 quit(errorc); 217 Skip => 218 return Next; 219 } 220 n += big nr; 221 } 222 c <-= ((nil, nil), reply); 223 if(<-reply == Quit) 224 quit(errorc); 225 return Next; 226} 227 228readproto(proto: ref Proto, indent: int): (int, array of ref File) 229{ 230 a := array[10] of ref File; 231 n := 0; 232 flags := 0; 233 while((f := readline(proto, indent)) != nil){ 234 if(f.name == "*") 235 flags |= Star; 236 else if(f.name == "+") 237 flags |= Plus; 238 else{ 239 (f.flags, f.sub) = readproto(proto, proto.indent); 240 if(n == len a) 241 a = (array[n * 2] of ref File)[0:] = a; 242 a[n++] = f; 243 } 244 } 245 if(n < len a) 246 a = (array[n] of ref File)[0:] = a[0:n]; 247 mergesort(a, array[n] of ref File); 248 return (flags, a); 249} 250 251readline(proto: ref Proto, indent: int): ref File 252{ 253 s: string; 254 if(proto.lastline != nil){ 255 s = proto.lastline; 256 proto.lastline = nil; 257 }else if(proto.indent == -1) 258 return nil; 259 else if((s = proto.iob.gets('\n')) == nil){ 260 proto.indent = -1; 261 return nil; 262 } 263 spc := 0; 264 for(i := 0; i < len s; i++){ 265 c := s[i]; 266 if(c == ' ') 267 spc++; 268 else if(c == '\t') 269 spc += 8; 270 else 271 break; 272 } 273 if(i == len s || s[i] == '#' || s[i] == '\n') 274 return readline(proto, indent); # XXX sort out tail recursion! 275 if(spc <= indent){ 276 proto.lastline = s; 277 return nil; 278 } 279 proto.indent = spc; 280 (nil, toks) := sys->tokenize(s, " \t\n"); 281 f := ref File(nil, ~0, nil, nil, nil, 0, nil); 282 (f.name, toks) = (getname(hd toks, 0), tl toks); 283 if(toks == nil) 284 return f; 285 (f.mode, toks) = (getmode(hd toks), tl toks); 286 if(toks == nil) 287 return f; 288 (f.owner, toks) = (getname(hd toks, 1), tl toks); 289 if(toks == nil) 290 return f; 291 (f.group, toks) = (getname(hd toks, 1), tl toks); 292 if(toks == nil) 293 return f; 294 (f.old, toks) = (hd toks, tl toks); 295 return f; 296} 297 298mergesort(a, b: array of ref File) 299{ 300 r := len a; 301 if (r > 1) { 302 m := (r-1)/2 + 1; 303 mergesort(a[0:m], b[0:m]); 304 mergesort(a[m:], b[m:]); 305 b[0:] = a; 306 for ((i, j, k) := (0, m, 0); i < m && j < r; k++) { 307 if(b[i].name > b[j].name) 308 a[k] = b[j++]; 309 else 310 a[k] = b[i++]; 311 } 312 if (i < m) 313 a[k:] = b[i:m]; 314 else if (j < r) 315 a[k:] = b[j:r]; 316 } 317} 318 319getname(s: string, allowminus: int): string 320{ 321 if(s == nil) 322 return nil; 323 if(allowminus && s == "-") 324 return nil; 325 if(s[0] == '$') 326 return getenv(s[1:]); 327 return s; 328} 329 330getenv(s: string): string 331{ 332 # XXX implement env variables 333 return nil; 334} 335 336getmode(s: string): int 337{ 338 s = getname(s, 1); 339 if(s == nil) 340 return ~0; 341 m := 0; 342 i := 0; 343 if(s[i] == 'd'){ 344 m |= Sys->DMDIR; 345 i++; 346 } 347 if(i < len s && s[i] == 'a'){ 348 m |= Sys->DMAPPEND; 349 i++; 350 } 351 if(i < len s && s[i] == 'l'){ 352 m |= Sys->DMEXCL; 353 i++; 354 } 355 (xmode, t) := str->toint(s, 8); 356 if(t != nil){ 357 # report(aux.errorc, "bad mode specification %q", s); 358 return ~0; 359 } 360 return xmode | m; 361} 362 363file2dir(f: ref File, old: ref Sys->Dir): ref Sys->Dir 364{ 365 d := ref Sys->nulldir; 366 if(old != nil){ 367 if(old.dtype != 'M'){ 368 d.uid = "sys"; 369 d.gid = "sys"; 370 xmode := (old.mode >> 6) & 7; 371 d.mode = old.mode | xmode | (xmode << 3); 372 }else{ 373 d.uid = old.uid; 374 d.gid = old.gid; 375 d.mode = old.mode; 376 } 377 d.length = old.length; 378 d.mtime = old.mtime; 379 d.atime = old.atime; 380 d.muid = old.muid; 381 d.name = old.name; 382 } 383 if(f != nil){ 384 d.name = f.name; 385 if(f.owner != nil) 386 d.uid = f.owner; 387 if(f.group != nil) 388 d.gid = f.group; 389 if(f.mode != ~0) 390 d.mode = f.mode; 391 } 392 return d; 393} 394