110047Ssam #ifndef lint 2*13489Ssam static char *sccsid = "@(#)mv.c 4.13 (Berkeley) 83/06/30"; 310047Ssam #endif 48839Smckusick 51216Sbill /* 61216Sbill * mv file1 file2 71216Sbill */ 810047Ssam #include <sys/param.h> 910047Ssam #include <sys/stat.h> 101216Sbill 111216Sbill #include <stdio.h> 12*13489Ssam #include <sys/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; 3710641Smckusick char *dest; 381216Sbill 391216Sbill if (argc < 2) 401216Sbill goto usage; 4110047Ssam while (argc > 1 && *argv[1] == '-') { 421216Sbill argc--; 431931Sroot arg = *++argv; 441216Sbill 451931Sroot /* 4610047Ssam * all files following a null option 4710047Ssam * are considered file names 481931Sroot */ 4910047Ssam if (*(arg+1) == '\0') 5010047Ssam break; 5110047Ssam while (*++arg != '\0') switch (*arg) { 521931Sroot 5310047Ssam case 'i': 5410047Ssam iflag++; 5510047Ssam break; 561931Sroot 5710047Ssam case 'f': 5810047Ssam fflag++; 5910047Ssam break; 601216Sbill 6110047Ssam default: 6210047Ssam goto usage; 6310047Ssam } 641216Sbill } 651216Sbill if (argc < 3) 661216Sbill goto usage; 6710641Smckusick dest = argv[argc-1]; 6810642Smckusick if (stat(dest, &s2) >= 0 && ISDIR(s2)) { 6910114Ssam r = 0; 7010047Ssam for (i = 1; i < argc-1; i++) 7110047Ssam r |= movewithshortname(argv[i], dest); 7210047Ssam exit(r); 731216Sbill } 7410641Smckusick if (argc > 3) 7510641Smckusick goto usage; 7610641Smckusick r = move(argv[1], argv[2]); 7710047Ssam exit(r); 7810047Ssam /*NOTREACHED*/ 791216Sbill usage: 8010047Ssam fprintf(stderr, 8110642Smckusick "usage: mv [-if] f1 f2 or mv [-if] f1 ... fn d1 (`fn' is a file or directory)\n"); 8210047Ssam return (1); 831216Sbill } 841216Sbill 8510047Ssam movewithshortname(src, dest) 8610047Ssam char *src, *dest; 8710047Ssam { 8810047Ssam register char *shortname; 8910047Ssam char target[MAXPATHLEN + 1]; 9010047Ssam 9110047Ssam shortname = dname(src); 9210047Ssam if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) { 9310047Ssam error("%s/%s: pathname too long", dest, 9410047Ssam shortname); 9510047Ssam return (1); 9610047Ssam } 9710047Ssam sprintf(target, "%s/%s", dest, shortname); 9810047Ssam return (move(src, target)); 9910047Ssam } 10010047Ssam 1011216Sbill move(source, target) 10210047Ssam char *source, *target; 1031216Sbill { 10411642Ssam int targetexists; 1051216Sbill 1068839Smckusick if (lstat(source, &s1) < 0) { 10710047Ssam error("cannot access %s", source); 10810047Ssam return (1); 1091216Sbill } 11010047Ssam /* 11110047Ssam * First, try to rename source to destination. 11210047Ssam * The only reason we continue on failure is if 11310047Ssam * the move is on a nondirectory and not across 11410047Ssam * file systems. 11510047Ssam */ 11611642Ssam targetexists = lstat(target, &s2) >= 0; 11711642Ssam if (targetexists) { 11810047Ssam if (iflag && !fflag && query("remove %s? ", target) == 0) 11910047Ssam return (1); 12010047Ssam if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) { 12110047Ssam error("%s and %s are identical", source, target); 12210047Ssam return (1); 1231216Sbill } 12410047Ssam if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) { 12510047Ssam if (query("override protection %o for %s? ", 12610047Ssam s2.st_mode & MODEBITS, target) == 0) 12710047Ssam return (1); 1281216Sbill } 1291216Sbill } 13011642Ssam if (rename(source, target) >= 0) 13111642Ssam return (0); 13211642Ssam if (errno != EXDEV) { 13311642Ssam Perror2(source, "rename"); 13411642Ssam return (1); 13511642Ssam } 13611642Ssam if (ISDIR(s1)) { 13711642Ssam error("can't mv directories across file systems"); 13811642Ssam return (1); 13911642Ssam } 14011642Ssam if (targetexists && unlink(target) < 0) { 14111642Ssam error("cannot unlink %s", target); 14211642Ssam return (1); 14311642Ssam } 14410047Ssam /* 14510047Ssam * File can't be renamed, try to recreate the symbolic 14610047Ssam * link or special device, or copy the file wholesale 14710047Ssam * between file systems. 14810047Ssam */ 14910047Ssam if (ISLNK(s1)) { 1508839Smckusick register m; 15110047Ssam char symln[MAXPATHLEN]; 1528839Smckusick 1538839Smckusick if (readlink(source, symln, sizeof (symln)) < 0) { 15410047Ssam Perror(source); 1558839Smckusick return (1); 1568839Smckusick } 1578839Smckusick m = umask(~(s1.st_mode & MODEBITS)); 1588839Smckusick if (symlink(symln, target) < 0) { 15910047Ssam Perror(target); 1608839Smckusick return (1); 1618839Smckusick } 16210047Ssam (void) umask(m); 16310047Ssam goto cleanup; 16410047Ssam } 16510047Ssam if (ISDEV(s1)) { 16610166Ssam time_t tv[2]; 16710166Ssam 16810047Ssam if (mknod(target, s1.st_mode, s1.st_rdev) < 0) { 16910047Ssam Perror(target); 17010047Ssam return (1); 17110047Ssam } 17210166Ssam /* kludge prior to utimes */ 17310166Ssam tv[0] = s1.st_atime; 17410166Ssam tv[1] = s1.st_mtime; 17510166Ssam (void) utime(target, tv); 17610047Ssam goto cleanup; 17710047Ssam } 17810047Ssam if (ISREG(s1)) { 17910047Ssam int i, c, status; 18010166Ssam time_t tv[2]; 18110047Ssam 1821216Sbill i = fork(); 1831216Sbill if (i == -1) { 18410047Ssam error("try again"); 18510047Ssam return (1); 1861216Sbill } 1871216Sbill if (i == 0) { 1881216Sbill execl("/bin/cp", "cp", source, target, 0); 18910047Ssam error("cannot exec /bin/cp"); 1901216Sbill exit(1); 1911216Sbill } 1921216Sbill while ((c = wait(&status)) != i && c != -1) 1931216Sbill ; 1941216Sbill if (status != 0) 19510047Ssam return (1); 19610166Ssam /* kludge prior to utimes */ 19710166Ssam tv[0] = s1.st_atime; 19810166Ssam tv[1] = s1.st_mtime; 19910166Ssam (void) utime(target, tv); 20010047Ssam goto cleanup; 2011216Sbill } 20210047Ssam error("%s: unknown file type %o", source, s1.st_mode); 20310047Ssam return (1); 2041216Sbill 20510047Ssam cleanup: 2061216Sbill if (unlink(source) < 0) { 20710047Ssam error("cannot unlink %s", source); 20810047Ssam return (1); 2091216Sbill } 21010047Ssam return (0); 2111216Sbill } 2121216Sbill 21310047Ssam /*VARARGS*/ 21410047Ssam query(prompt, a1, a2) 21510047Ssam char *a1; 2161216Sbill { 21710047Ssam register char i, c; 2181216Sbill 21910047Ssam fprintf(stderr, prompt, a1, a2); 22010047Ssam i = c = getchar(); 22110047Ssam while (c != '\n' && c != EOF) 22210047Ssam c = getchar(); 22310047Ssam return (i == 'y'); 2241216Sbill } 2251216Sbill 2261216Sbill char * 2271216Sbill dname(name) 22810047Ssam register char *name; 2291216Sbill { 2301216Sbill register char *p; 2311216Sbill 2321216Sbill p = name; 2331216Sbill while (*p) 2341216Sbill if (*p++ == DELIM && *p) 2351216Sbill name = p; 2361216Sbill return name; 2371216Sbill } 2381216Sbill 23910047Ssam /*VARARGS*/ 24010047Ssam error(fmt, a1, a2) 24110047Ssam char *fmt; 2421216Sbill { 2431216Sbill 24410047Ssam fprintf(stderr, "mv: "); 24510047Ssam fprintf(stderr, fmt, a1, a2); 24610047Ssam fprintf(stderr, "\n"); 24710047Ssam } 2481216Sbill 24910047Ssam Perror(s) 25010047Ssam char *s; 25110047Ssam { 25210047Ssam char buf[MAXPATHLEN + 10]; 25310047Ssam 25410047Ssam sprintf(buf, "mv: %s", s); 25510047Ssam perror(buf); 2561216Sbill } 2571216Sbill 25810047Ssam Perror2(s1, s2) 25910047Ssam char *s1, *s2; 2601216Sbill { 26110047Ssam char buf[MAXPATHLEN + 20]; 26210047Ssam 26310047Ssam sprintf(buf, "mv: %s: %s", s1, s2); 26410047Ssam perror(buf); 2651216Sbill } 266