ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/install/proto.b/
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;
}