1*10047Ssam #ifndef lint 2*10047Ssam static char *sccsid = "@(#)mv.c 4.7 (Berkeley) 83/01/01"; 3*10047Ssam #endif 48839Smckusick 51216Sbill /* 61216Sbill * mv file1 file2 71216Sbill */ 8*10047Ssam #include <sys/param.h> 9*10047Ssam #include <sys/stat.h> 101216Sbill 111216Sbill #include <stdio.h> 12*10047Ssam #include <dir.h> 13*10047Ssam #include <errno.h> 141216Sbill #include <signal.h> 151216Sbill 161216Sbill #define DELIM '/' 171216Sbill #define MODEBITS 07777 181216Sbill 19*10047Ssam #define ISDIR(st) (((st).st_mode&S_IFMT) == S_IFDIR) 20*10047Ssam #define ISLNK(st) (((st).st_mode&S_IFMT) == S_IFLNK) 21*10047Ssam #define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG) 22*10047Ssam #define ISDEV(st) \ 23*10047Ssam (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK) 24*10047Ssam 251216Sbill char *sprintf(); 261216Sbill char *dname(); 271216Sbill struct stat s1, s2; 28*10047Ssam int iflag = 0; /* interactive mode */ 29*10047Ssam int fflag = 0; /* force overwriting */ 30*10047Ssam extern unsigned errno; 311216Sbill 321216Sbill main(argc, argv) 33*10047Ssam register char *argv[]; 341216Sbill { 351216Sbill register i, r; 361931Sroot register char *arg; 371216Sbill 381216Sbill if (argc < 2) 391216Sbill goto usage; 40*10047Ssam while (argc > 1 && *argv[1] == '-') { 411216Sbill argc--; 421931Sroot arg = *++argv; 431216Sbill 441931Sroot /* 45*10047Ssam * all files following a null option 46*10047Ssam * are considered file names 471931Sroot */ 48*10047Ssam if (*(arg+1) == '\0') 49*10047Ssam break; 50*10047Ssam while (*++arg != '\0') switch (*arg) { 511931Sroot 52*10047Ssam case 'i': 53*10047Ssam iflag++; 54*10047Ssam break; 551931Sroot 56*10047Ssam case 'f': 57*10047Ssam fflag++; 58*10047Ssam break; 591216Sbill 60*10047Ssam default: 61*10047Ssam goto usage; 62*10047Ssam } 631216Sbill } 641216Sbill if (argc < 3) 651216Sbill goto usage; 66*10047Ssam setuid(getuid()); 67*10047Ssam r = 0; 68*10047Ssam if (argc > 3) { 69*10047Ssam register char *dest; 70*10047Ssam 71*10047Ssam dest = argv[argc-1]; 72*10047Ssam if (stat(dest, &s2) < 0 || !ISDIR(s2)) 731216Sbill goto usage; 74*10047Ssam for (i = 1; i < argc-1; i++) 75*10047Ssam r |= movewithshortname(argv[i], dest); 76*10047Ssam exit(r); 771216Sbill } 78*10047Ssam if (stat(argv[2], &s2) >= 0 && ISDIR(s2)) { 79*10047Ssam struct stat s1; 80*10047Ssam 81*10047Ssam if (stat(argv[1], &s1) >= 0 && ISDIR(s1)) 82*10047Ssam r = move(argv[1], argv[2]); 83*10047Ssam else 84*10047Ssam r = movewithshortname(argv[1], argv[2]); 85*10047Ssam } else 86*10047Ssam r = move(argv[1], argv[2]); 87*10047Ssam exit(r); 88*10047Ssam /*NOTREACHED*/ 891216Sbill usage: 90*10047Ssam fprintf(stderr, 91*10047Ssam "usage: mv [-if] f1 ... fn d1 (where `fn' is a file or directory)\n"); 92*10047Ssam return (1); 931216Sbill } 941216Sbill 95*10047Ssam movewithshortname(src, dest) 96*10047Ssam char *src, *dest; 97*10047Ssam { 98*10047Ssam register char *shortname; 99*10047Ssam char target[MAXPATHLEN + 1]; 100*10047Ssam 101*10047Ssam shortname = dname(src); 102*10047Ssam if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) { 103*10047Ssam error("%s/%s: pathname too long", dest, 104*10047Ssam shortname); 105*10047Ssam return (1); 106*10047Ssam } 107*10047Ssam sprintf(target, "%s/%s", dest, shortname); 108*10047Ssam return (move(src, target)); 109*10047Ssam } 110*10047Ssam 1111216Sbill move(source, target) 112*10047Ssam char *source, *target; 1131216Sbill { 1141216Sbill 1158839Smckusick if (lstat(source, &s1) < 0) { 116*10047Ssam error("cannot access %s", source); 117*10047Ssam return (1); 1181216Sbill } 119*10047Ssam /* 120*10047Ssam * First, try to rename source to destination. 121*10047Ssam * The only reason we continue on failure is if 122*10047Ssam * the move is on a nondirectory and not across 123*10047Ssam * file systems. 124*10047Ssam */ 125*10047Ssam if (lstat(target, &s2) >= 0) { 126*10047Ssam if (iflag && !fflag && query("remove %s? ", target) == 0) 127*10047Ssam return (1); 128*10047Ssam if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) { 129*10047Ssam error("%s and %s are identical", source, target); 130*10047Ssam return (1); 1311216Sbill } 132*10047Ssam if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) { 133*10047Ssam if (query("override protection %o for %s? ", 134*10047Ssam s2.st_mode & MODEBITS, target) == 0) 135*10047Ssam return (1); 1361216Sbill } 137*10047Ssam if (rename(source, target) >= 0) 138*10047Ssam return (0); 139*10047Ssam if (errno != EXDEV) { 140*10047Ssam Perror2(source, "rename"); 141*10047Ssam return (1); 142*10047Ssam } 143*10047Ssam if (ISDIR(s1)) { 144*10047Ssam error("can't mv directories across file systems"); 145*10047Ssam return (1); 146*10047Ssam } 147*10047Ssam if (unlink(target) < 0) { 148*10047Ssam error("cannot unlink %s", target); 149*10047Ssam return (1); 150*10047Ssam } 151*10047Ssam } else { 152*10047Ssam if (rename(source, target) >= 0) 153*10047Ssam return (0); 154*10047Ssam if (ISDIR(s1)) { 155*10047Ssam Perror2(source, "rename"); 156*10047Ssam return (1); 157*10047Ssam } 1581216Sbill } 159*10047Ssam /* 160*10047Ssam * File can't be renamed, try to recreate the symbolic 161*10047Ssam * link or special device, or copy the file wholesale 162*10047Ssam * between file systems. 163*10047Ssam */ 164*10047Ssam if (ISLNK(s1)) { 1658839Smckusick register m; 166*10047Ssam char symln[MAXPATHLEN]; 1678839Smckusick 1688839Smckusick if (readlink(source, symln, sizeof (symln)) < 0) { 169*10047Ssam Perror(source); 1708839Smckusick return (1); 1718839Smckusick } 1728839Smckusick m = umask(~(s1.st_mode & MODEBITS)); 1738839Smckusick if (symlink(symln, target) < 0) { 174*10047Ssam Perror(target); 1758839Smckusick return (1); 1768839Smckusick } 177*10047Ssam (void) umask(m); 178*10047Ssam goto cleanup; 179*10047Ssam } 180*10047Ssam if (ISDEV(s1)) { 181*10047Ssam if (mknod(target, s1.st_mode, s1.st_rdev) < 0) { 182*10047Ssam Perror(target); 183*10047Ssam return (1); 184*10047Ssam } 185*10047Ssam (void) utime(target, &s1.st_atime); 186*10047Ssam goto cleanup; 187*10047Ssam } 188*10047Ssam if (ISREG(s1)) { 189*10047Ssam int i, c, status; 190*10047Ssam 1911216Sbill i = fork(); 1921216Sbill if (i == -1) { 193*10047Ssam error("try again"); 194*10047Ssam return (1); 1951216Sbill } 1961216Sbill if (i == 0) { 1971216Sbill execl("/bin/cp", "cp", source, target, 0); 198*10047Ssam error("cannot exec /bin/cp"); 1991216Sbill exit(1); 2001216Sbill } 2011216Sbill while ((c = wait(&status)) != i && c != -1) 2021216Sbill ; 2031216Sbill if (status != 0) 204*10047Ssam return (1); 205*10047Ssam (void) utime(target, &s1.st_atime); 206*10047Ssam goto cleanup; 2071216Sbill } 208*10047Ssam error("%s: unknown file type %o", source, s1.st_mode); 209*10047Ssam return (1); 2101216Sbill 211*10047Ssam cleanup: 2121216Sbill if (unlink(source) < 0) { 213*10047Ssam error("cannot unlink %s", source); 214*10047Ssam return (1); 2151216Sbill } 216*10047Ssam return (0); 2171216Sbill } 2181216Sbill 219*10047Ssam /*VARARGS*/ 220*10047Ssam query(prompt, a1, a2) 221*10047Ssam char *a1; 2221216Sbill { 223*10047Ssam register char i, c; 2241216Sbill 225*10047Ssam fprintf(stderr, prompt, a1, a2); 226*10047Ssam i = c = getchar(); 227*10047Ssam while (c != '\n' && c != EOF) 228*10047Ssam c = getchar(); 229*10047Ssam return (i == 'y'); 2301216Sbill } 2311216Sbill 2321216Sbill char * 2331216Sbill dname(name) 234*10047Ssam register char *name; 2351216Sbill { 2361216Sbill register char *p; 2371216Sbill 2381216Sbill p = name; 2391216Sbill while (*p) 2401216Sbill if (*p++ == DELIM && *p) 2411216Sbill name = p; 2421216Sbill return name; 2431216Sbill } 2441216Sbill 245*10047Ssam /*VARARGS*/ 246*10047Ssam error(fmt, a1, a2) 247*10047Ssam char *fmt; 2481216Sbill { 2491216Sbill 250*10047Ssam fprintf(stderr, "mv: "); 251*10047Ssam fprintf(stderr, fmt, a1, a2); 252*10047Ssam fprintf(stderr, "\n"); 253*10047Ssam } 2541216Sbill 255*10047Ssam Perror(s) 256*10047Ssam char *s; 257*10047Ssam { 258*10047Ssam char buf[MAXPATHLEN + 10]; 259*10047Ssam 260*10047Ssam sprintf(buf, "mv: %s", s); 261*10047Ssam perror(buf); 2621216Sbill } 2631216Sbill 264*10047Ssam Perror2(s1, s2) 265*10047Ssam char *s1, *s2; 2661216Sbill { 267*10047Ssam char buf[MAXPATHLEN + 20]; 268*10047Ssam 269*10047Ssam sprintf(buf, "mv: %s: %s", s1, s2); 270*10047Ssam perror(buf); 2711216Sbill } 272