1 #include <u.h> 2 #include <libc.h> 3 4 int copy1(int fdf, int fdt, char *from, char *to); 5 void hardremove(char *); 6 int mv(char *from, char *todir, char *toelem); 7 int mv1(char *from, Dir *dirb, char *todir, char *toelem); 8 int samefile(char *, char *); 9 void split(char *, char **, char **); 10 11 void 12 main(int argc, char *argv[]) 13 { 14 int i, failed; 15 Dir *dirto, *dirfrom; 16 char *todir, *toelem; 17 18 if(argc<3){ 19 fprint(2, "usage: mv fromfile tofile\n"); 20 fprint(2, " mv fromfile ... todir\n"); 21 exits("bad usage"); 22 } 23 24 /* prepass to canonicalise names before splitting, etc. */ 25 for(i=1; i < argc; i++) 26 cleanname(argv[i]); 27 28 if((dirto = dirstat(argv[argc-1])) != nil && (dirto->mode&DMDIR)){ 29 dirfrom = nil; 30 if(argc == 3 31 && (dirfrom = dirstat(argv[1])) != nil 32 && (dirfrom->mode & DMDIR)) 33 split(argv[argc-1], &todir, &toelem); /* mv dir1 dir2 */ 34 else{ /* mv file... dir */ 35 todir = argv[argc-1]; 36 toelem = nil; /* toelem will be fromelem */ 37 } 38 free(dirfrom); 39 }else 40 split(argv[argc-1], &todir, &toelem); /* mv file1 file2 */ 41 free(dirto); 42 if(argc>3 && toelem != nil){ 43 fprint(2, "mv: %s not a directory\n", argv[argc-1]); 44 exits("bad usage"); 45 } 46 47 failed = 0; 48 for(i=1; i < argc-1; i++) 49 if(mv(argv[i], todir, toelem) < 0) 50 failed++; 51 if(failed) 52 exits("failure"); 53 exits(0); 54 } 55 56 int 57 mv(char *from, char *todir, char *toelem) 58 { 59 int stat; 60 Dir *dirb; 61 62 dirb = dirstat(from); 63 if(dirb == nil){ 64 fprint(2, "mv: can't stat %s: %r\n", from); 65 return -1; 66 } 67 stat = mv1(from, dirb, todir, toelem); 68 free(dirb); 69 return stat; 70 } 71 72 int 73 mv1(char *from, Dir *dirb, char *todir, char *toelem) 74 { 75 int fdf, fdt, i, j, stat; 76 char toname[4096], fromname[4096]; 77 char *fromdir, *fromelem; 78 Dir *dirt, null; 79 80 strncpy(fromname, from, sizeof fromname); 81 split(from, &fromdir, &fromelem); 82 if(toelem == 0) 83 toelem = fromelem; 84 i = strlen(toelem); 85 if(i==0){ 86 fprint(2, "mv: null last name element moving %s\n", fromname); 87 return -1; 88 } 89 j = strlen(todir); 90 if(i + j + 2 > sizeof toname){ 91 fprint(2, "mv: path too big (max %d): %s/%s\n", 92 sizeof toname, todir, toelem); 93 return -1; 94 } 95 memmove(toname, todir, j); 96 toname[j] = '/'; 97 memmove(toname+j+1, toelem, i); 98 toname[i+j+1] = 0; 99 100 if(samefile(fromdir, todir)){ 101 if(samefile(fromname, toname)){ 102 fprint(2, "mv: %s and %s are the same\n", 103 fromname, toname); 104 return -1; 105 } 106 107 /* remove target if present */ 108 dirt = dirstat(toname); 109 if(dirt != nil) { 110 hardremove(toname); 111 free(dirt); 112 } 113 114 /* try wstat */ 115 nulldir(&null); 116 null.name = toelem; 117 if(dirwstat(fromname, &null) >= 0) 118 return 0; 119 if(dirb->mode & DMDIR){ 120 fprint(2, "mv: can't rename directory %s: %r\n", 121 fromname); 122 return -1; 123 } 124 } 125 /* 126 * Renaming won't work --- must copy 127 */ 128 if(dirb->mode & DMDIR){ 129 fprint(2, "mv: %s is a directory, not copied to %s\n", 130 fromname, toname); 131 return -1; 132 } 133 fdf = open(fromname, OREAD); 134 if(fdf < 0){ 135 fprint(2, "mv: can't open %s: %r\n", fromname); 136 return -1; 137 } 138 139 dirt = dirstat(toname); 140 if(dirt != nil && (dirt->mode & DMAPPEND)) 141 hardremove(toname); /* because create() won't truncate file */ 142 free(dirt); 143 144 fdt = create(toname, OWRITE, dirb->mode); 145 if(fdt < 0){ 146 fprint(2, "mv: can't create %s: %r\n", toname); 147 close(fdf); 148 return -1; 149 } 150 stat = copy1(fdf, fdt, fromname, toname); 151 close(fdf); 152 153 if(stat >= 0){ 154 nulldir(&null); 155 null.mtime = dirb->mtime; 156 null.mode = dirb->mode; 157 dirfwstat(fdt, &null); /* ignore errors; e.g. user none always fails */ 158 if(remove(fromname) < 0){ 159 fprint(2, "mv: can't remove %s: %r\n", fromname); 160 stat = -1; 161 } 162 } 163 close(fdt); 164 return stat; 165 } 166 167 int 168 copy1(int fdf, int fdt, char *from, char *to) 169 { 170 char buf[8192]; 171 long n, n1; 172 173 while ((n = read(fdf, buf, sizeof buf)) > 0) { 174 n1 = write(fdt, buf, n); 175 if(n1 != n){ 176 fprint(2, "mv: error writing %s: %r\n", to); 177 return -1; 178 } 179 } 180 if(n < 0){ 181 fprint(2, "mv: error reading %s: %r\n", from); 182 return -1; 183 } 184 return 0; 185 } 186 187 void 188 split(char *name, char **pdir, char **pelem) 189 { 190 char *s; 191 192 s = utfrrune(name, '/'); 193 if(s){ 194 *s = 0; 195 *pelem = s+1; 196 *pdir = name; 197 }else if(strcmp(name, "..") == 0){ 198 *pdir = ".."; 199 *pelem = "."; 200 }else{ 201 *pdir = "."; 202 *pelem = name; 203 } 204 } 205 206 int 207 samefile(char *a, char *b) 208 { 209 Dir *da, *db; 210 int ret; 211 212 if(strcmp(a, b) == 0) 213 return 1; 214 da = dirstat(a); 215 db = dirstat(b); 216 ret = (da != nil && db != nil && 217 da->qid.type==db->qid.type && 218 da->qid.path==db->qid.path && 219 da->qid.vers==db->qid.vers && 220 da->dev==db->dev && 221 da->type==db->type); 222 free(da); 223 free(db); 224 return ret; 225 } 226 227 void 228 hardremove(char *a) 229 { 230 if(remove(a) == -1){ 231 fprint(2, "mv: can't remove %s: %r\n", a); 232 exits("mv"); 233 } 234 while(remove(a) != -1) 235 ; 236 } 237