xref: /inferno-os/appl/lib/fsproto.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1*37da2899SCharles.Forsythimplement FSproto;
2*37da2899SCharles.Forsyth
3*37da2899SCharles.Forsythinclude "sys.m";
4*37da2899SCharles.Forsyth	sys: Sys;
5*37da2899SCharles.Forsyth	Dir: import Sys;
6*37da2899SCharles.Forsythinclude "draw.m";
7*37da2899SCharles.Forsythinclude "bufio.m";
8*37da2899SCharles.Forsyth	bufio: Bufio;
9*37da2899SCharles.Forsyth	Iobuf: import bufio;
10*37da2899SCharles.Forsythinclude "string.m";
11*37da2899SCharles.Forsyth	str: String;
12*37da2899SCharles.Forsythinclude "readdir.m";
13*37da2899SCharles.Forsyth	readdir: Readdir;
14*37da2899SCharles.Forsythinclude "fsproto.m";
15*37da2899SCharles.Forsyth
16*37da2899SCharles.ForsythFile: adt {
17*37da2899SCharles.Forsyth	new:	string;
18*37da2899SCharles.Forsyth	elem:	string;
19*37da2899SCharles.Forsyth	old:	string;
20*37da2899SCharles.Forsyth	uid:	string;
21*37da2899SCharles.Forsyth	gid:	string;
22*37da2899SCharles.Forsyth	mode:	int;
23*37da2899SCharles.Forsyth};
24*37da2899SCharles.Forsyth
25*37da2899SCharles.ForsythProto: adt {
26*37da2899SCharles.Forsyth	b:	ref Iobuf;
27*37da2899SCharles.Forsyth	doquote:	int;
28*37da2899SCharles.Forsyth	indent:	int;
29*37da2899SCharles.Forsyth	lineno:	int;
30*37da2899SCharles.Forsyth	newfile:	string;
31*37da2899SCharles.Forsyth	oldfile:	string;
32*37da2899SCharles.Forsyth	oldroot:	string;
33*37da2899SCharles.Forsyth	ec:	chan of Direntry;
34*37da2899SCharles.Forsyth	wc:	chan of (string, string);
35*37da2899SCharles.Forsyth
36*37da2899SCharles.Forsyth	walk:	fn(w: self ref Proto, f: ref File, level: int);
37*37da2899SCharles.Forsyth	entry:	fn(w: self ref Proto, old: string, new: string, d: ref Sys->Dir);
38*37da2899SCharles.Forsyth	warn:	fn(w: self ref Proto, s: string);
39*37da2899SCharles.Forsyth	fatal:	fn(w: self ref Proto, s: string);
40*37da2899SCharles.Forsyth};
41*37da2899SCharles.Forsyth
42*37da2899SCharles.Forsythinit(): string
43*37da2899SCharles.Forsyth{
44*37da2899SCharles.Forsyth	sys = load Sys Sys->PATH;
45*37da2899SCharles.Forsyth	bufio = load Bufio Bufio->PATH;
46*37da2899SCharles.Forsyth	if(bufio == nil)
47*37da2899SCharles.Forsyth		return sys->sprint("%r");
48*37da2899SCharles.Forsyth	str = load String String->PATH;
49*37da2899SCharles.Forsyth	if(str == nil)
50*37da2899SCharles.Forsyth		return sys->sprint("%r");
51*37da2899SCharles.Forsyth	readdir = load Readdir Readdir->PATH;
52*37da2899SCharles.Forsyth	if(readdir == nil)
53*37da2899SCharles.Forsyth		return sys->sprint("%r");
54*37da2899SCharles.Forsyth	return nil;
55*37da2899SCharles.Forsyth}
56*37da2899SCharles.Forsyth
57*37da2899SCharles.Forsythreadprotofile(proto: string, root: string, entries: chan of Direntry, warnings: chan of (string, string)): string
58*37da2899SCharles.Forsyth{
59*37da2899SCharles.Forsyth	b := bufio->open(proto, Sys->OREAD);
60*37da2899SCharles.Forsyth	if(b == nil)
61*37da2899SCharles.Forsyth		return sys->sprint("%r");
62*37da2899SCharles.Forsyth	rdproto(b, root, entries, warnings);
63*37da2899SCharles.Forsyth	return nil;
64*37da2899SCharles.Forsyth}
65*37da2899SCharles.Forsyth
66*37da2899SCharles.Forsythreadprotostring(proto: string, root: string, entries: chan of Direntry, warnings: chan of (string, string))
67*37da2899SCharles.Forsyth{
68*37da2899SCharles.Forsyth	rdproto(bufio->sopen(proto), root, entries, warnings);
69*37da2899SCharles.Forsyth}
70*37da2899SCharles.Forsyth
71*37da2899SCharles.Forsythrdproto(b: ref Iobuf, root: string, entries: chan of Direntry, warnings: chan of (string, string)): string
72*37da2899SCharles.Forsyth{
73*37da2899SCharles.Forsyth	w := ref Proto;
74*37da2899SCharles.Forsyth	w.b = b;
75*37da2899SCharles.Forsyth	w.doquote = 1;
76*37da2899SCharles.Forsyth	w.ec = entries;
77*37da2899SCharles.Forsyth	w.wc = warnings;
78*37da2899SCharles.Forsyth	w.oldroot = root;
79*37da2899SCharles.Forsyth	w.lineno = 0;
80*37da2899SCharles.Forsyth	w.indent = 0;
81*37da2899SCharles.Forsyth	file := ref File;
82*37da2899SCharles.Forsyth	file.mode = 0;
83*37da2899SCharles.Forsyth	spawn walker(w, file);
84*37da2899SCharles.Forsyth	return nil;
85*37da2899SCharles.Forsyth}
86*37da2899SCharles.Forsyth
87*37da2899SCharles.Forsythwalker(w: ref Proto, file: ref File)
88*37da2899SCharles.Forsyth{
89*37da2899SCharles.Forsyth	w.walk(file, -1);
90*37da2899SCharles.Forsyth	w.entry(nil, nil, nil);
91*37da2899SCharles.Forsyth}
92*37da2899SCharles.Forsyth
93*37da2899SCharles.ForsythProto.entry(w: self ref Proto, old: string, new: string, d: ref Sys->Dir)
94*37da2899SCharles.Forsyth{
95*37da2899SCharles.Forsyth	if(w.ec != nil)
96*37da2899SCharles.Forsyth		w.ec <-= (old, new, d);
97*37da2899SCharles.Forsyth}
98*37da2899SCharles.Forsyth
99*37da2899SCharles.ForsythProto.warn(w: self ref Proto, s: string)
100*37da2899SCharles.Forsyth{
101*37da2899SCharles.Forsyth	if(w.wc != nil)
102*37da2899SCharles.Forsyth		w.wc <-= (w.oldfile, s);
103*37da2899SCharles.Forsyth	else
104*37da2899SCharles.Forsyth		sys->fprint(sys->fildes(2), "warning: %s\n", s);
105*37da2899SCharles.Forsyth}
106*37da2899SCharles.Forsyth
107*37da2899SCharles.ForsythProto.fatal(w: self ref Proto, s: string)
108*37da2899SCharles.Forsyth{
109*37da2899SCharles.Forsyth	if(w.wc != nil)
110*37da2899SCharles.Forsyth		w.wc <-= (w.oldfile, s);
111*37da2899SCharles.Forsyth	else
112*37da2899SCharles.Forsyth		sys->fprint(sys->fildes(2), "fatal error: %s\n", s);
113*37da2899SCharles.Forsyth	w.ec <-= (nil, nil, nil);
114*37da2899SCharles.Forsyth	exit;
115*37da2899SCharles.Forsyth}
116*37da2899SCharles.Forsyth
117*37da2899SCharles.ForsythProto.walk(w: self ref Proto, me: ref File, level: int)
118*37da2899SCharles.Forsyth{
119*37da2899SCharles.Forsyth	(child, fp) := getfile(w, me);
120*37da2899SCharles.Forsyth	if(child == nil)
121*37da2899SCharles.Forsyth		return;
122*37da2899SCharles.Forsyth	if(child.elem == "+" || child.elem == "*" || child.elem == "%"){
123*37da2899SCharles.Forsyth		rec := child.elem[0] == '+';
124*37da2899SCharles.Forsyth		filesonly := child.elem[0] == '%';
125*37da2899SCharles.Forsyth		child.new = me.new;
126*37da2899SCharles.Forsyth		setnames(w, child);
127*37da2899SCharles.Forsyth		mktree(w, child, rec, filesonly);
128*37da2899SCharles.Forsyth		(child, fp) = getfile(w, me);
129*37da2899SCharles.Forsyth	}
130*37da2899SCharles.Forsyth	while(child != nil && w.indent > level){
131*37da2899SCharles.Forsyth		if(mkfile(w, child))
132*37da2899SCharles.Forsyth			w.walk(child, w.indent);
133*37da2899SCharles.Forsyth		(child, fp) = getfile(w, me);
134*37da2899SCharles.Forsyth	}
135*37da2899SCharles.Forsyth	if(child != nil){
136*37da2899SCharles.Forsyth		w.b.seek(big fp, 0);
137*37da2899SCharles.Forsyth		w.lineno--;
138*37da2899SCharles.Forsyth	}
139*37da2899SCharles.Forsyth}
140*37da2899SCharles.Forsyth
141*37da2899SCharles.Forsythmktree(w: ref Proto, me: ref File, rec: int, filesonly: int)
142*37da2899SCharles.Forsyth{
143*37da2899SCharles.Forsyth	fd := sys->open(w.oldfile, Sys->OREAD);
144*37da2899SCharles.Forsyth	if(fd == nil){
145*37da2899SCharles.Forsyth		w.warn(sys->sprint("can't open %s: %r", w.oldfile));
146*37da2899SCharles.Forsyth		return;
147*37da2899SCharles.Forsyth	}
148*37da2899SCharles.Forsyth	child := ref *me;
149*37da2899SCharles.Forsyth	(d, n) := readdir->init(w.oldfile, Readdir->NAME|Readdir->COMPACT);
150*37da2899SCharles.Forsyth	for(i := 0; i < n; i++) {
151*37da2899SCharles.Forsyth		if(filesonly && (d[i].mode & Sys->DMDIR))
152*37da2899SCharles.Forsyth			continue;
153*37da2899SCharles.Forsyth		child.new = mkpath(me.new, d[i].name);
154*37da2899SCharles.Forsyth		if(me.old != nil)
155*37da2899SCharles.Forsyth			child.old = mkpath(me.old, d[i].name);
156*37da2899SCharles.Forsyth		child.elem = d[i].name;
157*37da2899SCharles.Forsyth		setnames(w, child);
158*37da2899SCharles.Forsyth		if(copyfile(w, child, d[i]) && rec)
159*37da2899SCharles.Forsyth			mktree(w, child, rec, filesonly);
160*37da2899SCharles.Forsyth	}
161*37da2899SCharles.Forsyth}
162*37da2899SCharles.Forsyth
163*37da2899SCharles.Forsythmkfile(w: ref Proto, f: ref File): int
164*37da2899SCharles.Forsyth{
165*37da2899SCharles.Forsyth	(i, dir) := sys->stat(w.oldfile);
166*37da2899SCharles.Forsyth	if(i < 0){
167*37da2899SCharles.Forsyth		w.warn(sys->sprint("can't stat file %s: %r", w.oldfile));
168*37da2899SCharles.Forsyth		skipdir(w);
169*37da2899SCharles.Forsyth		return 0;
170*37da2899SCharles.Forsyth	}
171*37da2899SCharles.Forsyth	return copyfile(w, f, ref dir);
172*37da2899SCharles.Forsyth}
173*37da2899SCharles.Forsyth
174*37da2899SCharles.Forsythcopyfile(w: ref Proto, f: ref File, d: ref Dir): int
175*37da2899SCharles.Forsyth{
176*37da2899SCharles.Forsyth	d.name = f.elem;
177*37da2899SCharles.Forsyth	if(f.mode != ~0){
178*37da2899SCharles.Forsyth		if((d.mode&Sys->DMDIR) != (f.mode&Sys->DMDIR))
179*37da2899SCharles.Forsyth			w.warn(sys->sprint("inconsistent mode for %s", f.new));
180*37da2899SCharles.Forsyth		else
181*37da2899SCharles.Forsyth			d.mode = f.mode;
182*37da2899SCharles.Forsyth	}
183*37da2899SCharles.Forsyth	w.entry(w.oldfile, w.newfile, d);
184*37da2899SCharles.Forsyth	return (d.mode & Sys->DMDIR) != 0;
185*37da2899SCharles.Forsyth}
186*37da2899SCharles.Forsyth
187*37da2899SCharles.Forsythsetnames(w: ref Proto, f: ref File)
188*37da2899SCharles.Forsyth{
189*37da2899SCharles.Forsyth	w.newfile = f.new;
190*37da2899SCharles.Forsyth	if(f.old != nil){
191*37da2899SCharles.Forsyth		if(f.old[0] == '/')
192*37da2899SCharles.Forsyth			w.oldfile = mkpath(w.oldroot, f.old);
193*37da2899SCharles.Forsyth		else
194*37da2899SCharles.Forsyth			w.oldfile = f.old;
195*37da2899SCharles.Forsyth	}else
196*37da2899SCharles.Forsyth		w.oldfile = mkpath(w.oldroot, f.new);
197*37da2899SCharles.Forsyth}
198*37da2899SCharles.Forsyth
199*37da2899SCharles.Forsyth#
200*37da2899SCharles.Forsyth# skip all files in the proto that
201*37da2899SCharles.Forsyth# could be in the current dir
202*37da2899SCharles.Forsyth#
203*37da2899SCharles.Forsythskipdir(w: ref Proto)
204*37da2899SCharles.Forsyth{
205*37da2899SCharles.Forsyth	if(w.indent < 0)
206*37da2899SCharles.Forsyth		return;
207*37da2899SCharles.Forsyth	b := w.b;
208*37da2899SCharles.Forsyth	level := w.indent;
209*37da2899SCharles.Forsyth	for(;;){
210*37da2899SCharles.Forsyth		w.indent = 0;
211*37da2899SCharles.Forsyth		fp := b.offset();
212*37da2899SCharles.Forsyth		p := b.gets('\n');
213*37da2899SCharles.Forsyth		if(p != nil && p[len p - 1] != '\n')
214*37da2899SCharles.Forsyth			p += "\n";
215*37da2899SCharles.Forsyth		w.lineno++;
216*37da2899SCharles.Forsyth		if(p == nil){
217*37da2899SCharles.Forsyth			w.indent = -1;
218*37da2899SCharles.Forsyth			return;
219*37da2899SCharles.Forsyth		}
220*37da2899SCharles.Forsyth		for(j := 0; (c := p[j++]) != '\n';)
221*37da2899SCharles.Forsyth			if(c == ' ')
222*37da2899SCharles.Forsyth				w.indent++;
223*37da2899SCharles.Forsyth			else if(c == '\t')
224*37da2899SCharles.Forsyth				w.indent += 8;
225*37da2899SCharles.Forsyth			else
226*37da2899SCharles.Forsyth				break;
227*37da2899SCharles.Forsyth		if(w.indent <= level){
228*37da2899SCharles.Forsyth			b.seek(fp, 0);
229*37da2899SCharles.Forsyth			w.lineno--;
230*37da2899SCharles.Forsyth			return;
231*37da2899SCharles.Forsyth		}
232*37da2899SCharles.Forsyth	}
233*37da2899SCharles.Forsyth}
234*37da2899SCharles.Forsyth
235*37da2899SCharles.Forsythgetfile(w: ref Proto, old: ref File): (ref File, int)
236*37da2899SCharles.Forsyth{
237*37da2899SCharles.Forsyth	p, elem: string;
238*37da2899SCharles.Forsyth	c: int;
239*37da2899SCharles.Forsyth
240*37da2899SCharles.Forsyth	if(w.indent < 0)
241*37da2899SCharles.Forsyth		return (nil, 0);
242*37da2899SCharles.Forsyth	b := w.b;
243*37da2899SCharles.Forsyth	fp := int b.offset();
244*37da2899SCharles.Forsyth	do {
245*37da2899SCharles.Forsyth		w.indent = 0;
246*37da2899SCharles.Forsyth		p = b.gets('\n');
247*37da2899SCharles.Forsyth		if(p != nil && p[len p - 1] != '\n')
248*37da2899SCharles.Forsyth			p += "\n";
249*37da2899SCharles.Forsyth		w.lineno++;
250*37da2899SCharles.Forsyth		if(p == nil){
251*37da2899SCharles.Forsyth			w.indent = -1;
252*37da2899SCharles.Forsyth			return (nil, 0);
253*37da2899SCharles.Forsyth		}
254*37da2899SCharles.Forsyth		for(; (c = p[0]) != '\n'; p = p[1:])
255*37da2899SCharles.Forsyth			if(c == ' ')
256*37da2899SCharles.Forsyth				w.indent++;
257*37da2899SCharles.Forsyth			else if(c == '\t')
258*37da2899SCharles.Forsyth				w.indent += 8;
259*37da2899SCharles.Forsyth			else
260*37da2899SCharles.Forsyth				break;
261*37da2899SCharles.Forsyth	} while(c == '\n' || c == '#');
262*37da2899SCharles.Forsyth	(elem, p) = getname(w, p);
263*37da2899SCharles.Forsyth	if(p == nil)
264*37da2899SCharles.Forsyth		return (nil, 0);
265*37da2899SCharles.Forsyth	f := ref File;
266*37da2899SCharles.Forsyth	f.new = mkpath(old.new, elem);
267*37da2899SCharles.Forsyth	(nil, f.elem) = str->splitr(f.new, "/");
268*37da2899SCharles.Forsyth	if(f.elem == nil)
269*37da2899SCharles.Forsyth		w.fatal(sys->sprint("can't find file name component of %s", f.new));
270*37da2899SCharles.Forsyth	(f.mode, p) = getmode(w, p);
271*37da2899SCharles.Forsyth	if(p == nil)
272*37da2899SCharles.Forsyth		return (nil, 0);
273*37da2899SCharles.Forsyth	(f.uid, p) = getname(w, p);
274*37da2899SCharles.Forsyth	if(p == nil)
275*37da2899SCharles.Forsyth		return (nil, 0);
276*37da2899SCharles.Forsyth	if(f.uid == nil)
277*37da2899SCharles.Forsyth		f.uid = "-";
278*37da2899SCharles.Forsyth	(f.gid, p) = getname(w, p);
279*37da2899SCharles.Forsyth	if(p == nil)
280*37da2899SCharles.Forsyth		return (nil, 0);
281*37da2899SCharles.Forsyth	if(f.gid == nil)
282*37da2899SCharles.Forsyth		f.gid = "-";
283*37da2899SCharles.Forsyth	f.old = getpath(p);
284*37da2899SCharles.Forsyth	if(f.old == "-")
285*37da2899SCharles.Forsyth		f.old = nil;
286*37da2899SCharles.Forsyth	if(f.old == nil && old.old != nil)
287*37da2899SCharles.Forsyth		f.old = mkpath(old.old, elem);
288*37da2899SCharles.Forsyth	setnames(w, f);
289*37da2899SCharles.Forsyth	return (f, fp);
290*37da2899SCharles.Forsyth}
291*37da2899SCharles.Forsyth
292*37da2899SCharles.Forsythgetpath(p: string): string
293*37da2899SCharles.Forsyth{
294*37da2899SCharles.Forsyth	for(; (c := p[0]) == ' ' || c == '\t'; p = p[1:])
295*37da2899SCharles.Forsyth		;
296*37da2899SCharles.Forsyth	for(n := 0; (c = p[n]) != '\n' && c != ' ' && c != '\t'; n++)
297*37da2899SCharles.Forsyth		;
298*37da2899SCharles.Forsyth	return p[0:n];
299*37da2899SCharles.Forsyth}
300*37da2899SCharles.Forsyth
301*37da2899SCharles.Forsythgetname(w: ref Proto, p: string): (string, string)
302*37da2899SCharles.Forsyth{
303*37da2899SCharles.Forsyth	for(; (c := p[0]) == ' ' || c == '\t'; p = p[1:])
304*37da2899SCharles.Forsyth		;
305*37da2899SCharles.Forsyth	i := 0;
306*37da2899SCharles.Forsyth	s := "";
307*37da2899SCharles.Forsyth	quoted := 0;
308*37da2899SCharles.Forsyth	for(; (c = p[0]) != '\n' && (c != ' ' && c != '\t' || quoted); p = p[1:]){
309*37da2899SCharles.Forsyth		if(quoted && c == '\'' && p[1] == '\'')
310*37da2899SCharles.Forsyth			p = p[1:];
311*37da2899SCharles.Forsyth		else if(c == '\'' && w.doquote){
312*37da2899SCharles.Forsyth			quoted = !quoted;
313*37da2899SCharles.Forsyth			continue;
314*37da2899SCharles.Forsyth		}
315*37da2899SCharles.Forsyth		s[i++] = c;
316*37da2899SCharles.Forsyth	}
317*37da2899SCharles.Forsyth	if(len s > 0 && s[0] == '$'){
318*37da2899SCharles.Forsyth		s = getenv(s[1:]);
319*37da2899SCharles.Forsyth		if(s == nil)
320*37da2899SCharles.Forsyth			w.warn(sys->sprint("can't read environment variable %s", s));
321*37da2899SCharles.Forsyth	}
322*37da2899SCharles.Forsyth	return (s, p);
323*37da2899SCharles.Forsyth}
324*37da2899SCharles.Forsyth
325*37da2899SCharles.Forsythgetenv(s: string): string
326*37da2899SCharles.Forsyth{
327*37da2899SCharles.Forsyth	if(s == "user")
328*37da2899SCharles.Forsyth		return readfile("/dev/user");	# more accurate?
329*37da2899SCharles.Forsyth	return readfile("/env/"+s);
330*37da2899SCharles.Forsyth}
331*37da2899SCharles.Forsyth
332*37da2899SCharles.Forsythreadfile(f: string): string
333*37da2899SCharles.Forsyth{
334*37da2899SCharles.Forsyth	fd := sys->open(f, Sys->OREAD);
335*37da2899SCharles.Forsyth	if(fd != nil){
336*37da2899SCharles.Forsyth		a := array[256] of byte;
337*37da2899SCharles.Forsyth		n := sys->read(fd, a, len a);
338*37da2899SCharles.Forsyth		if(n > 0)
339*37da2899SCharles.Forsyth			return string a[0:n];
340*37da2899SCharles.Forsyth	}
341*37da2899SCharles.Forsyth	return nil;
342*37da2899SCharles.Forsyth}
343*37da2899SCharles.Forsyth
344*37da2899SCharles.Forsythgetmode(w: ref Proto, p: string): (int, string)
345*37da2899SCharles.Forsyth{
346*37da2899SCharles.Forsyth	s: string;
347*37da2899SCharles.Forsyth
348*37da2899SCharles.Forsyth	(s, p) = getname(w, p);
349*37da2899SCharles.Forsyth	if(s == nil || s == "-")
350*37da2899SCharles.Forsyth		return (~0, p);
351*37da2899SCharles.Forsyth	m := 0;
352*37da2899SCharles.Forsyth	if(s[0] == 'd'){
353*37da2899SCharles.Forsyth		m |= Sys->DMDIR;
354*37da2899SCharles.Forsyth		s = s[1:];
355*37da2899SCharles.Forsyth	}
356*37da2899SCharles.Forsyth	if(s[0] == 'a'){
357*37da2899SCharles.Forsyth		m |= Sys->DMAPPEND;
358*37da2899SCharles.Forsyth		s = s[1:];
359*37da2899SCharles.Forsyth	}
360*37da2899SCharles.Forsyth	if(s[0] == 'l'){
361*37da2899SCharles.Forsyth		m |= Sys->DMEXCL;
362*37da2899SCharles.Forsyth		s = s[1:];
363*37da2899SCharles.Forsyth	}
364*37da2899SCharles.Forsyth	for(i:=0; i<len s || i < 3; i++)
365*37da2899SCharles.Forsyth		if(i >= len s || !(s[i]>='0' && s[i]<='7')){
366*37da2899SCharles.Forsyth			w.warn(sys->sprint("bad mode specification %s", s));
367*37da2899SCharles.Forsyth			return (~0, p);
368*37da2899SCharles.Forsyth		}
369*37da2899SCharles.Forsyth	(v, nil) := str->toint(s, 8);
370*37da2899SCharles.Forsyth	return (m|v, p);
371*37da2899SCharles.Forsyth}
372*37da2899SCharles.Forsyth
373*37da2899SCharles.Forsythmkpath(prefix, elem: string): string
374*37da2899SCharles.Forsyth{
375*37da2899SCharles.Forsyth	slash1 := slash2 := 0;
376*37da2899SCharles.Forsyth	if(len prefix > 0)
377*37da2899SCharles.Forsyth		slash1 = prefix[len prefix - 1] == '/';
378*37da2899SCharles.Forsyth	if(len elem > 0)
379*37da2899SCharles.Forsyth		slash2 = elem[0] == '/';
380*37da2899SCharles.Forsyth	if(slash1 && slash2)
381*37da2899SCharles.Forsyth		return prefix+elem[1:];
382*37da2899SCharles.Forsyth	if(!slash1 && !slash2)
383*37da2899SCharles.Forsyth		return prefix+"/"+elem;
384*37da2899SCharles.Forsyth	return prefix+elem;
385*37da2899SCharles.Forsyth}
386