110047Ssam #ifndef lint 2*10166Ssam static char *sccsid = "@(#)mv.c 4.9 (Berkeley) 83/01/05"; 310047Ssam #endif 48839Smckusick 51216Sbill /* 61216Sbill * mv file1 file2 71216Sbill */ 810047Ssam #include <sys/param.h> 910047Ssam #include <sys/stat.h> 101216Sbill 111216Sbill #include <stdio.h> 1210047Ssam #include <dir.h> 1310047Ssam #include <errno.h> 141216Sbill #include <signal.h> 151216Sbill 161216Sbill #define DELIM '/' 171216Sbill #define MODEBITS 07777 181216Sbill 1910047Ssam #define ISDIR(st) (((st).st_mode&S_IFMT) == S_IFDIR) 2010047Ssam #define ISLNK(st) (((st).st_mode&S_IFMT) == S_IFLNK) 2110047Ssam #define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG) 2210047Ssam #define ISDEV(st) \ 2310047Ssam (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK) 2410047Ssam 251216Sbill char *sprintf(); 261216Sbill char *dname(); 271216Sbill struct stat s1, s2; 2810047Ssam int iflag = 0; /* interactive mode */ 2910047Ssam int fflag = 0; /* force overwriting */ 3010047Ssam extern unsigned errno; 311216Sbill 321216Sbill main(argc, argv) 3310047Ssam register char *argv[]; 341216Sbill { 351216Sbill register i, r; 361931Sroot register char *arg; 371216Sbill 381216Sbill if (argc < 2) 391216Sbill goto usage; 4010047Ssam while (argc > 1 && *argv[1] == '-') { 411216Sbill argc--; 421931Sroot arg = *++argv; 431216Sbill 441931Sroot /* 4510047Ssam * all files following a null option 4610047Ssam * are considered file names 471931Sroot */ 4810047Ssam if (*(arg+1) == '\0') 4910047Ssam break; 5010047Ssam while (*++arg != '\0') switch (*arg) { 511931Sroot 5210047Ssam case 'i': 5310047Ssam iflag++; 5410047Ssam break; 551931Sroot 5610047Ssam case 'f': 5710047Ssam fflag++; 5810047Ssam break; 591216Sbill 6010047Ssam default: 6110047Ssam goto usage; 6210047Ssam } 631216Sbill } 641216Sbill if (argc < 3) 651216Sbill goto usage; 6610047Ssam if (argc > 3) { 6710047Ssam register char *dest; 6810047Ssam 6910047Ssam dest = argv[argc-1]; 7010047Ssam if (stat(dest, &s2) < 0 || !ISDIR(s2)) 711216Sbill goto usage; 7210114Ssam r = 0; 7310047Ssam for (i = 1; i < argc-1; i++) 7410047Ssam r |= movewithshortname(argv[i], dest); 7510047Ssam exit(r); 761216Sbill } 7710114Ssam if (lstat(argv[2], &s2) >= 0 && ISDIR(s2)) { 7810047Ssam struct stat s1; 7910047Ssam 8010114Ssam if (lstat(argv[1], &s1) >= 0 && ISDIR(s1)) 8110047Ssam r = move(argv[1], argv[2]); 8210047Ssam else 8310047Ssam r = movewithshortname(argv[1], argv[2]); 8410047Ssam } else 8510047Ssam r = move(argv[1], argv[2]); 8610047Ssam exit(r); 8710047Ssam /*NOTREACHED*/ 881216Sbill usage: 8910047Ssam fprintf(stderr, 9010047Ssam "usage: mv [-if] f1 ... fn d1 (where `fn' is a file or directory)\n"); 9110047Ssam return (1); 921216Sbill } 931216Sbill 9410047Ssam movewithshortname(src, dest) 9510047Ssam char *src, *dest; 9610047Ssam { 9710047Ssam register char *shortname; 9810047Ssam char target[MAXPATHLEN + 1]; 9910047Ssam 10010047Ssam shortname = dname(src); 10110047Ssam if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) { 10210047Ssam error("%s/%s: pathname too long", dest, 10310047Ssam shortname); 10410047Ssam return (1); 10510047Ssam } 10610047Ssam sprintf(target, "%s/%s", dest, shortname); 10710047Ssam return (move(src, target)); 10810047Ssam } 10910047Ssam 1101216Sbill move(source, target) 11110047Ssam char *source, *target; 1121216Sbill { 1131216Sbill 1148839Smckusick if (lstat(source, &s1) < 0) { 11510047Ssam error("cannot access %s", source); 11610047Ssam return (1); 1171216Sbill } 11810047Ssam /* 11910047Ssam * First, try to rename source to destination. 12010047Ssam * The only reason we continue on failure is if 12110047Ssam * the move is on a nondirectory and not across 12210047Ssam * file systems. 12310047Ssam */ 12410047Ssam if (lstat(target, &s2) >= 0) { 12510047Ssam if (iflag && !fflag && query("remove %s? ", target) == 0) 12610047Ssam return (1); 12710047Ssam if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) { 12810047Ssam error("%s and %s are identical", source, target); 12910047Ssam return (1); 1301216Sbill } 13110047Ssam if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) { 13210047Ssam if (query("override protection %o for %s? ", 13310047Ssam s2.st_mode & MODEBITS, target) == 0) 13410047Ssam return (1); 1351216Sbill } 13610047Ssam if (rename(source, target) >= 0) 13710047Ssam return (0); 13810047Ssam if (errno != EXDEV) { 13910047Ssam Perror2(source, "rename"); 14010047Ssam return (1); 14110047Ssam } 14210047Ssam if (ISDIR(s1)) { 14310047Ssam error("can't mv directories across file systems"); 14410047Ssam return (1); 14510047Ssam } 14610047Ssam if (unlink(target) < 0) { 14710047Ssam error("cannot unlink %s", target); 14810047Ssam return (1); 14910047Ssam } 15010047Ssam } else { 15110047Ssam if (rename(source, target) >= 0) 15210047Ssam return (0); 15310047Ssam if (ISDIR(s1)) { 15410047Ssam Perror2(source, "rename"); 15510047Ssam return (1); 15610047Ssam } 1571216Sbill } 15810047Ssam /* 15910047Ssam * File can't be renamed, try to recreate the symbolic 16010047Ssam * link or special device, or copy the file wholesale 16110047Ssam * between file systems. 16210047Ssam */ 16310047Ssam if (ISLNK(s1)) { 1648839Smckusick register m; 16510047Ssam char symln[MAXPATHLEN]; 1668839Smckusick 1678839Smckusick if (readlink(source, symln, sizeof (symln)) < 0) { 16810047Ssam Perror(source); 1698839Smckusick return (1); 1708839Smckusick } 1718839Smckusick m = umask(~(s1.st_mode & MODEBITS)); 1728839Smckusick if (symlink(symln, target) < 0) { 17310047Ssam Perror(target); 1748839Smckusick return (1); 1758839Smckusick } 17610047Ssam (void) umask(m); 17710047Ssam goto cleanup; 17810047Ssam } 17910047Ssam if (ISDEV(s1)) { 180*10166Ssam time_t tv[2]; 181*10166Ssam 18210047Ssam if (mknod(target, s1.st_mode, s1.st_rdev) < 0) { 18310047Ssam Perror(target); 18410047Ssam return (1); 18510047Ssam } 186*10166Ssam /* kludge prior to utimes */ 187*10166Ssam tv[0] = s1.st_atime; 188*10166Ssam tv[1] = s1.st_mtime; 189*10166Ssam (void) utime(target, tv); 19010047Ssam goto cleanup; 19110047Ssam } 19210047Ssam if (ISREG(s1)) { 19310047Ssam int i, c, status; 194*10166Ssam time_t tv[2]; 19510047Ssam 1961216Sbill i = fork(); 1971216Sbill if (i == -1) { 19810047Ssam error("try again"); 19910047Ssam return (1); 2001216Sbill } 2011216Sbill if (i == 0) { 2021216Sbill execl("/bin/cp", "cp", source, target, 0); 20310047Ssam error("cannot exec /bin/cp"); 2041216Sbill exit(1); 2051216Sbill } 2061216Sbill while ((c = wait(&status)) != i && c != -1) 2071216Sbill ; 2081216Sbill if (status != 0) 20910047Ssam return (1); 210*10166Ssam /* kludge prior to utimes */ 211*10166Ssam tv[0] = s1.st_atime; 212*10166Ssam tv[1] = s1.st_mtime; 213*10166Ssam (void) utime(target, tv); 21410047Ssam goto cleanup; 2151216Sbill } 21610047Ssam error("%s: unknown file type %o", source, s1.st_mode); 21710047Ssam return (1); 2181216Sbill 21910047Ssam cleanup: 2201216Sbill if (unlink(source) < 0) { 22110047Ssam error("cannot unlink %s", source); 22210047Ssam return (1); 2231216Sbill } 22410047Ssam return (0); 2251216Sbill } 2261216Sbill 22710047Ssam /*VARARGS*/ 22810047Ssam query(prompt, a1, a2) 22910047Ssam char *a1; 2301216Sbill { 23110047Ssam register char i, c; 2321216Sbill 23310047Ssam fprintf(stderr, prompt, a1, a2); 23410047Ssam i = c = getchar(); 23510047Ssam while (c != '\n' && c != EOF) 23610047Ssam c = getchar(); 23710047Ssam return (i == 'y'); 2381216Sbill } 2391216Sbill 2401216Sbill char * 2411216Sbill dname(name) 24210047Ssam register char *name; 2431216Sbill { 2441216Sbill register char *p; 2451216Sbill 2461216Sbill p = name; 2471216Sbill while (*p) 2481216Sbill if (*p++ == DELIM && *p) 2491216Sbill name = p; 2501216Sbill return name; 2511216Sbill } 2521216Sbill 25310047Ssam /*VARARGS*/ 25410047Ssam error(fmt, a1, a2) 25510047Ssam char *fmt; 2561216Sbill { 2571216Sbill 25810047Ssam fprintf(stderr, "mv: "); 25910047Ssam fprintf(stderr, fmt, a1, a2); 26010047Ssam fprintf(stderr, "\n"); 26110047Ssam } 2621216Sbill 26310047Ssam Perror(s) 26410047Ssam char *s; 26510047Ssam { 26610047Ssam char buf[MAXPATHLEN + 10]; 26710047Ssam 26810047Ssam sprintf(buf, "mv: %s", s); 26910047Ssam perror(buf); 2701216Sbill } 2711216Sbill 27210047Ssam Perror2(s1, s2) 27310047Ssam char *s1, *s2; 2741216Sbill { 27510047Ssam char buf[MAXPATHLEN + 20]; 27610047Ssam 27710047Ssam sprintf(buf, "mv: %s: %s", s1, s2); 27810047Ssam perror(buf); 2791216Sbill } 280