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