1implement Cp; 2 3include "sys.m"; 4 sys: Sys; 5 6include "draw.m"; 7include "arg.m"; 8 9include "readdir.m"; 10 readdir: Readdir; 11 12Cp: module 13{ 14 init: fn(nil: ref Draw->Context, args: list of string); 15}; 16 17stderr: ref Sys->FD; 18errors := 0; 19gflag := 0; 20uflag := 0; 21xflag := 0; 22 23init(nil: ref Draw->Context, args: list of string) 24{ 25 sys = load Sys Sys->PATH; 26 stderr = sys->fildes(2); 27 28 arg := load Arg Arg->PATH; 29 recursive := 0; 30 arg->init(args); 31 arg->setusage("\tcp [-gux] src target\n\tcp [-r] [-gux] src ... directory"); 32 while((opt := arg->opt()) != 0) 33 case opt { 34 'r' => recursive = 1; 35 'g' => gflag = 1; 36 'u' => uflag = gflag = 1; 37 'x' => xflag = 1; 38 * => arg->usage(); 39 } 40 args = arg->argv(); 41 argc := len args; 42 if(argc < 2) 43 arg->usage(); 44 arg = nil; 45 46 dst: string; 47 for(t := args; t != nil; t = tl t) 48 dst = hd t; 49 50 (ok, dir) := sys->stat(dst); 51 todir := (ok != -1 && (dir.mode & Sys->DMDIR)); 52 if(argc > 2 && !todir){ 53 sys->fprint(stderr, "cp: %s not a directory\n", dst); 54 raise "fail:error"; 55 } 56 if(recursive) 57 cpdir(args, dst); 58 else{ 59 for(; tl args != nil; args = tl args){ 60 if(todir) 61 cp(hd args, dst, basename(hd args)); 62 else 63 cp(hd args, dst, nil); 64 } 65 } 66 if(errors) 67 raise "fail:error"; 68} 69 70basename(s: string): string 71{ 72 for((nil, ls) := sys->tokenize(s, "/"); ls != nil; ls = tl ls) 73 s = hd ls; 74 return s; 75} 76 77cp(src, dst: string, newname: string) 78{ 79 dd: Sys->Dir; 80 81 if(newname != nil) 82 dst += "/" + newname; 83 (ok, ds) := sys->stat(src); 84 if(ok < 0){ 85 warning(sys->sprint("%s: %r", src)); 86 return; 87 } 88 if(ds.mode & Sys->DMDIR){ 89 warning(src + " is a directory"); 90 return; 91 } 92 (ok, dd) = sys->stat(dst); 93 if(ok != -1 && samefile(ds, dd)){ 94 warning(src + " and " + dst + " are the same file"); 95 return; 96 } 97 sfd := sys->open(src, Sys->OREAD); 98 if(sfd == nil){ 99 warning(sys->sprint("cannot open %s: %r", src)); 100 return; 101 } 102 dfd := sys->create(dst, Sys->OWRITE, ds.mode & 8r777); 103 if(dfd == nil){ 104 warning(sys->sprint("cannot create %s: %r", dst)); 105 return; 106 } 107 if(copy(sfd, dfd, src, dst)!=0) 108 return; 109 if(wstat(dfd, ds, 0) < 0) 110 warning(sys->sprint("can't wstat %s: %r", src)); 111} 112 113copy(sfd, dfd: ref Sys->FD, src, dst: string): int 114{ 115 buf := array[Sys->ATOMICIO] of byte; 116 while((r := sys->read(sfd, buf, len buf)) > 0){ 117 if(sys->write(dfd, buf, r) != r){ 118 warning(sys->sprint("error writing %s: %r", dst)); 119 return -1; 120 } 121 } 122 if(r < 0){ 123 warning(sys->sprint("error reading %s: %r", src)); 124 return -1; 125 } 126 return 0; 127} 128 129cpdir(args: list of string, dst: string) 130{ 131 readdir = load Readdir Readdir->PATH; 132 if(readdir == nil){ 133 sys->fprint(stderr, "cp: cannot load %s: %r\n", Readdir->PATH); 134 raise "fail:bad module"; 135 } 136 cache = array[NCACHE] of list of ref Sys->Dir; 137 dexists := 0; 138 (ok, dd) := sys->stat(dst); 139 # destination file exists 140 if(ok != -1){ 141 if((dd.mode & Sys->DMDIR) == 0){ 142 warning(dst + ": destination not a directory"); 143 return; 144 } 145 dexists = 1; 146 } 147 for(; tl args != nil; args = tl args){ 148 ds: Sys->Dir; 149 src := hd args; 150 (ok, ds) = sys->stat(src); 151 if(ok < 0){ 152 warning(sys->sprint("can't stat %s: %r", src)); 153 continue; 154 } 155 if((ds.mode & Sys->DMDIR) == 0){ 156 cp(hd args, dst, basename(hd args)); 157 } else if(dexists){ 158 if(samefile(ds, dd)){ 159 warning("cannot copy " + src + " into itself"); 160 continue; 161 } 162 copydir(src, dst + "/" + basename(src), ds); 163 } else 164 copydir(src, dst, ds); 165 } 166} 167 168copydir(src, dst: string, srcd: Sys->Dir) 169{ 170 (ok, nil) := sys->stat(dst); 171 if(ok != -1){ 172 warning("cannot copy " + src + " onto another directory"); 173 return; 174 } 175 tmode := srcd.mode | 8r777; # Fix for Nt 176 dfd := sys->create(dst, Sys->OREAD, Sys->DMDIR | tmode); 177 if(dfd == nil){ 178 warning(sys->sprint("cannot make directory %s: %r", dst)); 179 return; 180 } 181 (entries, n) := readdir->init(src, Readdir->COMPACT); 182 for(i := 0; i < n; i++){ 183 e := entries[i]; 184 path := src + "/" + e.name; 185 if((e.mode & Sys->DMDIR) == 0) 186 cp(path, dst, e.name); 187 else if(seen(e)) 188 warning(path + ": directory loop found"); 189 else 190 copydir(path, dst + "/" + e.name, *e); 191 } 192 if(wstat(dfd, srcd, 1) < 0) 193 warning(sys->sprint("can't wstat %s: %r", dst)); 194} 195 196wstat(dfd: ref Sys->FD, ds: Sys->Dir, mflag: int): int 197{ 198 if(!xflag && !gflag && !uflag && !mflag) 199 return 0; 200 d := sys->nulldir; 201 if(xflag) 202 d.mtime = ds.mtime; 203 if(xflag || mflag) 204 d.mode = ds.mode; 205 if(uflag) 206 d.uid = ds.uid; 207 if(gflag) 208 d.gid = ds.gid; 209 return sys->fwstat(dfd, d); 210} 211 212samefile(d1: Sys->Dir, d2: Sys->Dir): int 213{ 214 return d1.dtype == d2.dtype && d1.dev == d2.dev && 215 d1.qid.qtype == d2.qid.qtype && d1.qid.path == d2.qid.path && 216 d1.qid.vers == d2.qid.vers; 217} 218 219# Avoid loops in tangled namespaces. (from du.b) 220NCACHE: con 64; # must be power of two 221cache: array of list of ref sys->Dir; 222 223seen(dir: ref sys->Dir): int 224{ 225 savlist := cache[int dir.qid.path&(NCACHE-1)]; 226 for(c := savlist; c!=nil; c = tl c) 227 if(samefile(*dir, *hd c)) 228 return 1; 229 cache[int dir.qid.path&(NCACHE-1)] = dir :: savlist; 230 return 0; 231} 232 233warning(e: string) 234{ 235 sys->fprint(stderr, "cp: %s\n", e); 236 errors++; 237} 238