diff options
Diffstat (limited to 'appl/cmd/install/proto.b')
| -rw-r--r-- | appl/cmd/install/proto.b | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/appl/cmd/install/proto.b b/appl/cmd/install/proto.b new file mode 100644 index 00000000..c25ee220 --- /dev/null +++ b/appl/cmd/install/proto.b @@ -0,0 +1,320 @@ +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"; + +NAMELEN: con 8192; + +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(big 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->DMDIR)) + 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->DMDIR) != (f.mode&Sys->DMDIR)) + 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->DMDIR) != 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 := int 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, 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, NAMELEN); + if(f.uid == nil) + f.uid = "-"; + (f.gid, p) = getname(p, 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 >= NAMELEN) + s = s[0: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->DMDIR; + s = s[1:]; + } + if(s[0] == 'a'){ + #m |= CHAPPEND; + s = s[1:]; + } + if(s[0] == 'l'){ + #m |= CHEXCL; + s = s[1:]; + } + for(i:=0; i<len s || i < 3; i++) + if(i >= len s || !(s[i]>='0' && s[i]<='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; +} |
