implement Proto; include "sys.m"; sys: Sys; Dir : import Sys; include "draw.m"; include "bufio.m"; bufio : Bufio; Iobuf : import bufio; include "string.m"; str: String; include "readdir.m"; readdir : Readdir; include "proto.m"; include "protocaller.m"; WARN, ERROR, FATAL : import Protocaller; File: adt { new: string; elem: string; old: string; uid: string; gid: string; mode: int; }; indent: int; lineno := 0; newfile: string; oldfile: string; oldroot : string; b: ref Iobuf; cmod : Protocaller; rdproto(proto : string, root : string, pcmod : Protocaller) : int { if (sys == nil) { sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; str = load String String->PATH; readdir = load Readdir Readdir->PATH; } cmod = pcmod; oldroot = root; b = bufio->open(proto, Sys->OREAD); if(b == nil){ cmod->protoerr(FATAL, lineno, sys->sprint("can't open %s: %r: skipping\n", proto)); b.close(); return -1; } lineno = 0; indent = 0; file := ref File; file.mode = 0; mkfs(file, -1); b.close(); return 0; } mkfs(me: ref File, level: int) { (child, fp) := getfile(me); if(child == nil) return; if(child.elem == "+" || child.elem == "*" || child.elem == "%"){ rec := child.elem[0] == '+'; filesonly := child.elem[0] == '%'; child.new = me.new; setnames(child); mktree(child, rec, filesonly); (child, fp) = getfile(me); } while(child != nil && indent > level){ if(mkfile(child)) mkfs(child, indent); (child, fp) = getfile(me); } if(child != nil){ b.seek(fp, 0); lineno--; } } mktree(me: ref File, rec: int, filesonly : int) { fd := sys->open(oldfile, Sys->OREAD); if(fd == nil){ cmod->protoerr(WARN, lineno, sys->sprint("can't open %s: %r", oldfile)); return; } child := ref *me; (d, n) := readdir->init(oldfile, Readdir->NAME|Readdir->COMPACT); for (i := 0; i < n; i++) { if (filesonly && (d[i].mode & Sys->CHDIR)) continue; child.new = mkpath(me.new, d[i].name); if(me.old != nil) child.old = mkpath(me.old, d[i].name); child.elem = d[i].name; setnames(child); if(copyfile(child, d[i]) && rec) mktree(child, rec, filesonly); } } mkfile(f: ref File): int { (i, dir) := sys->stat(oldfile); if(i < 0){ cmod->protoerr(WARN, lineno, sys->sprint("can't stat file %s: %r", oldfile)); skipdir(); return 0; } return copyfile(f, ref dir); } copyfile(f: ref File, d: ref Dir): int { d.name = f.elem; if(f.mode != ~0){ if((d.mode&Sys->CHDIR) != (f.mode&Sys->CHDIR)) cmod->protoerr(WARN, lineno, sys->sprint("inconsistent mode for %s", f.new)); else d.mode = f.mode; } cmod->protofile(newfile, oldfile, d); return (d.mode & Sys->CHDIR) != 0; } setnames(f: ref File) { newfile = f.new; if(f.old != nil){ if(f.old[0] == '/') oldfile = mkpath(oldroot, f.old); else oldfile = f.old; }else oldfile = mkpath(oldroot, f.new); } # # skip all files in the proto that # could be in the current dir # skipdir() { if(indent < 0) return; level := indent; for(;;){ indent = 0; fp := b.offset(); p := b.gets('\n'); if (p != nil && p[len p - 1] != '\n') p += "\n"; lineno++; if(p == nil){ indent = -1; return; } for(j := 0; (c := p[j++]) != '\n';) if(c == ' ') indent++; else if(c == '\t') indent += 8; else break; if(indent <= level){ b.seek(fp, 0); lineno--; return; } } } getfile(old: ref File): (ref File, int) { f: ref File; p, elem: string; c: int; if(indent < 0) return (nil, 0); fp := b.offset(); do { indent = 0; p = b.gets('\n'); if (p != nil && p[len p - 1] != '\n') p += "\n"; lineno++; if(p == nil){ indent = -1; return (nil, 0); } for(; (c = p[0]) != '\n'; p = p[1:]) if(c == ' ') indent++; else if(c == '\t') indent += 8; else break; } while(c == '\n' || c == '#'); f = ref File; (elem, p) = getname(p, Sys->NAMELEN); f.new = mkpath(old.new, elem); (nil, f.elem) = str->splitr(f.new, "/"); if(f.elem == nil) cmod->protoerr(ERROR, lineno, sys->sprint("can't find file name component of %s", f.new)); (f.mode, p) = getmode(p); (f.uid, p) = getname(p, Sys->NAMELEN); if(f.uid == nil) f.uid = "-"; (f.gid, p) = getname(p, Sys->NAMELEN); if(f.gid == nil) f.gid = "-"; f.old = getpath(p); if(f.old == "-") f.old = nil; if(f.old == nil && old.old != nil) f.old = mkpath(old.old, elem); setnames(f); return (f, fp); } getpath(p: string): string { for(; (c := p[0]) == ' ' || c == '\t'; p = p[1:]) ; for(n := 0; (c = p[n]) != '\n' && c != ' ' && c != '\t'; n++) ; return p[0:n]; } getname(p: string, lim: int): (string, string) { for(; (c := p[0]) == ' ' || c == '\t'; p = p[1:]) ; i := 0; s := ""; for(; (c = p[0]) != '\n' && c != ' ' && c != '\t'; p = p[1:]) s[i++] = c; if(len s >= lim){ cmod->protoerr(WARN, lineno, sys->sprint("name %s too long; truncated", s)); s = s[0:lim-1]; } if(len s > 0 && s[0] == '$'){ s = getenv(s[1:]); if(s == nil) cmod->protoerr(ERROR, lineno, sys->sprint("can't read environment variable %s", s)); if(len s >= Sys->NAMELEN) s = s[0:Sys->NAMELEN-1]; } return (s, p); } getenv(s: string): string { if(s == "user") return getuser(); return nil; } getuser(): string { fd := sys->open("/dev/user", Sys->OREAD); if(fd != nil){ u := array [100] of byte; n := sys->read(fd, u, len u); if(n > 0) return string u[0:n]; } return nil; } getmode(p: string): (int, string) { s: string; (s, p) = getname(p, 7); if(s == nil || s == "-") return (~0, p); m := 0; if(s[0] == 'd'){ m |= Sys->CHDIR; s = s[1:]; } if(s[0] == 'a'){ #m |= CHAPPEND; s = s[1:]; } if(s[0] == 'l'){ #m |= CHEXCL; s = s[1:]; } if(s[0] < '0' || s[0] > '7' || s[1] < '0' || s[1] > '7' || s[2] < '0' || s[2] > '7' || s[3] < '0' || s[3] > '7') { cmod->protoerr(WARN, lineno, sys->sprint("bad mode specification %s", s)); return (~0, p); } (v, nil) := str->toint(s, 8); return (m|v, p); } mkpath(prefix, elem: string): string { slash1 := slash2 := 0; if (len prefix > 0) slash1 = prefix[len prefix - 1] == '/'; if (len elem > 0) slash2 = elem[0] == '/'; if (slash1 && slash2) return prefix+elem[1:]; if (!slash1 && !slash2) return prefix+"/"+elem; return prefix+elem; }