xref: /inferno-os/appl/cmd/install/eproto.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
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