xref: /inferno-os/appl/cmd/fcp.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Fcp;
2
3include "sys.m";
4	sys: Sys;
5include "draw.m";
6include "arg.m";
7include "readdir.m";
8	readdir: Readdir;
9
10Fcp: module
11{
12	init:	fn(nil: ref Draw->Context, argv: list of string);
13};
14
15stderr: ref Sys->FD;
16errors := 0;
17
18fdc: chan of (ref Sys->FD, ref Sys->FD);
19
20init(nil: ref Draw->Context, argv: list of string)
21{
22	sys = load Sys Sys->PATH;
23	stderr = sys->fildes(2);
24
25	arg := load Arg Arg->PATH;
26	if (arg == nil) {
27		sys->fprint(stderr, "fcp: cannot load %s: %r\n", Arg->PATH);
28		raise "fail:bad module";
29	}
30	recursive := 0;
31	nreaders := nwriters := 8;
32	arg->init(argv);
33	arg->setusage("\tfcp [-r] [-R nproc] [-W nproc] src target\n\tfcp [-r] [-R nproc] [-W nproc] src ... directory");
34	while ((opt := arg->opt()) != 0) {
35		case opt {
36		'R' =>
37			nreaders = int arg->earg();
38		'W' =>
39			nwriters = int arg->earg();
40		'r' =>
41			recursive = 1;
42		* =>
43			arg->usage();
44		}
45	}
46	if(nreaders < 1 || nwriters < 1)
47		arg->usage();
48	if(nreaders > 1 || nwriters > 1){
49		fdc = chan of (ref Sys->FD, ref Sys->FD);
50		spawn mstream(fdc, Sys->ATOMICIO, nreaders, nwriters);
51	}
52	argv = arg->argv();
53	argc := len argv;
54	if (argc < 2)
55		arg->usage();
56	arg = nil;
57
58	dst: string;
59	for (t := argv; t != nil; t = tl t)
60		dst = hd t;
61
62	(ok, dir) := sys->stat(dst);
63	todir := (ok != -1 && (dir.mode & Sys->DMDIR));
64	if (argc > 2 && !todir) {
65		sys->fprint(stderr, "fcp: %s  not a directory\n", dst);
66		raise "fail:error";
67	}
68	if (recursive)
69		cpdir(argv, dst);
70	else {
71		for (; tl argv != nil; argv = tl argv) {
72			if (todir)
73				cp(hd argv, dst, basename(hd argv));
74			else
75				cp(hd argv, dst, nil);
76		}
77	}
78	if(fdc != nil)
79		fdc <-= (nil, nil);
80	if (errors)
81		raise "fail:error";
82}
83
84basename(s: string): string
85{
86	for ((nil, ls) := sys->tokenize(s, "/"); ls != nil; ls = tl ls)
87		s = hd ls;
88	return s;
89}
90
91cp(src, dst: string, newname: string)
92{
93	ok: int;
94	ds, dd: Sys->Dir;
95
96	if (newname != nil)
97		dst += "/" + newname;
98	(ok, ds) = sys->stat(src);
99	if (ok < 0) {
100		warning(sys->sprint("%s: %r", src));
101		return;
102	}
103	if (ds.mode & Sys->DMDIR) {
104		warning(src + " is a directory");
105		return;
106	}
107	(ok, dd) = sys->stat(dst);
108	if (ok != -1 &&
109			ds.qid.path == dd.qid.path &&
110			ds.dev == dd.dev &&
111			ds.dtype == dd.dtype) {
112		warning(src + " and " + dst + " are the same file");
113		return;
114	}
115	sfd := sys->open(src, sys->OREAD);
116	if (sfd == nil) {
117		warning(sys->sprint("cannot open %s: %r", src));
118		return;
119	}
120	dfd := sys->create(dst, sys->OWRITE, ds.mode);
121	if (dfd == nil) {
122		warning(sys->sprint("cannot create %s: %r", dst));
123		return;
124	}
125	copy(sfd, dfd, src, dst);
126}
127
128mkdir(d: string, mode: int): int
129{
130	dfd := sys->create(d, sys->OREAD, sys->DMDIR | mode);
131	if (dfd == nil) {
132		warning(sys->sprint("cannot make directory %s: %r", d));
133		return -1;
134	}
135	return 0;
136}
137
138copy(sfd, dfd: ref Sys->FD, src, dst: string): int
139{
140	if(fdc != nil){
141		fdc <-= (sfd, dfd);
142		return 0;
143	}
144	buf := array[Sys->ATOMICIO] of byte;
145	for (;;) {
146		r := sys->read(sfd, buf, Sys->ATOMICIO);
147		if (r < 0) {
148			warning(sys->sprint("error reading %s: %r", src));
149			return -1;
150		}
151		if (r == 0)
152			return 0;
153		if (sys->write(dfd, buf, r) != r) {
154			warning(sys->sprint("error writing %s: %r", dst));
155			return -1;
156		}
157	}
158}
159
160cpdir(argv: list of string, dst: string)
161{
162	readdir = load Readdir Readdir->PATH;
163	if (readdir == nil) {
164		sys->fprint(stderr, "fcp: cannot load %s: %r\n", Readdir->PATH);
165		raise "fail:bad module";
166	}
167	cache = array[NCACHE] of list of ref Sys->Dir;
168	dexists := 0;
169	(ok, dd) := sys->stat(dst);
170	 # destination file exists
171	if (ok != -1) {
172		if ((dd.mode & Sys->DMDIR) == 0) {
173			warning(dst + ": destination not a directory");
174			return;
175		}
176		dexists = 1;
177	}
178	for (; tl argv != nil; argv = tl argv) {
179		ds: Sys->Dir;
180		src := hd argv;
181		(ok, ds) = sys->stat(src);
182		if (ok < 0) {
183			warning(sys->sprint("can't stat %s: %r", src));
184			continue;
185		}
186		if ((ds.mode & Sys->DMDIR) == 0) {
187			cp(hd argv, dst, basename(hd argv));
188		} else if (dexists) {
189			if (ds.qid.path==dd.qid.path &&
190					ds.dev==dd.dev &&
191					ds.dtype==dd.dtype) {
192				warning("cannot copy " + src + " into itself");
193				continue;
194			}
195			copydir(src, dst + "/" + basename(src), ds.mode);
196		} else {
197			copydir(src, dst, ds.mode);
198		}
199	}
200}
201
202copydir(src, dst: string, srcmode: int)
203{
204	(ok, nil) := sys->stat(dst);
205	if (ok != -1) {
206		warning("cannot copy " + src + " onto another directory");
207		return;
208	}
209	tmode := srcmode | 8r777;	# Fix for Nt
210	if (mkdir(dst, tmode) == -1)
211		return;
212	(entries, n) := readdir->init(src, Readdir->COMPACT);
213	for (i := 0; i < n; i++) {
214		e := entries[i];
215		path := src + "/" + e.name;
216		if ((e.mode & Sys->DMDIR) == 0)
217			cp(path, dst, e.name);
218		else if (seen(e))
219			warning(path + ": directory loop found");
220		else
221			copydir(path, dst + "/" + e.name, e.mode);
222	}
223	chmod(dst, srcmode);
224}
225
226# Avoid loops in tangled namespaces. (from du.b)
227NCACHE: con 64; # must be power of two
228cache: array of list of ref sys->Dir;
229
230seen(dir: ref sys->Dir): int
231{
232	savlist := cache[int dir.qid.path&(NCACHE-1)];
233	for(c := savlist; c!=nil; c = tl c){
234		sav := hd c;
235		if(dir.qid.path==sav.qid.path &&
236			dir.dtype==sav.dtype && dir.dev==sav.dev)
237			return 1;
238	}
239	cache[int dir.qid.path&(NCACHE-1)] = dir :: savlist;
240	return 0;
241}
242
243warning(e: string)
244{
245	sys->fprint(stderr, "fcp: %s\n", e);
246	errors++;
247}
248
249chmod(s: string, mode: int): int
250{
251	(ok, d) := sys->stat(s);
252	if (ok < 0)
253		return -1;
254
255	if(d.mode == mode)
256		return 0;
257	d = sys->nulldir;
258	d.mode = mode;
259	if (sys->wstat(s, d) < 0) {
260		warning(sys->sprint("cannot wstat %s: %r", s));
261		return -1;
262	}
263	return 0;
264}
265
266mstream(fdc: chan of (ref Sys->FD, ref Sys->FD), bufsize: int, nin, nout: int)
267{
268	inc := chan of (ref Sys->FD, big, int, ref Sys->FD);
269	outc := chan of (ref Sys->FD, big, array of byte);
270	for(i := 0; i < nin; i++)
271		spawn readproc(inc, outc);
272	for(i = 0; i < nout; i++)
273		spawn writeproc(outc);
274	while(((src, dst) := <-fdc).t0 != nil){
275		(ok, stat) := sys->fstat(src);
276		if(ok == -1)
277			continue;
278		tot := stat.length;
279		o := big 0;
280		while((n := tot - o) > big 0){
281			if(n < big bufsize)
282				inc <-= (src, o, int n, dst);
283			else
284				inc <-= (src, o, bufsize, dst);
285			o += big bufsize;
286		}
287	}
288	for(i = 0; i < nin; i++)
289		inc <-= (nil, big 0, 0, nil);
290	for(i = 0; i < nout; i++)
291		outc <-= (nil, big 0, nil);
292}
293
294readproc(inc: chan of (ref Sys->FD, big, int, ref Sys->FD), outc: chan of (ref Sys->FD, big, array of byte))
295{
296	buf: array of byte;
297	while(((src, o, nb, dst) := <-inc).t0 != nil){
298		if(len buf < nb)
299			buf = array[nb*2] of byte;
300		n := sys->pread(src, buf, nb, o);
301		if(n > 0){
302			outc <-= (dst, o, buf[0:n]);
303			buf = buf[n:];
304		}
305	}
306}
307
308writeproc(outc: chan of (ref Sys->FD, big, array of byte))
309{
310	while(((dst, o, buf) := <-outc).t0 != nil)
311		sys->pwrite(dst, buf, len buf, o);
312}
313