1*19845Sdist /* 2*19845Sdist * Copyright (c) 1980 Regents of the University of California. 3*19845Sdist * All rights reserved. The Berkeley software License Agreement 4*19845Sdist * specifies the terms and conditions for redistribution. 5*19845Sdist */ 6*19845Sdist 710047Ssam #ifndef lint 8*19845Sdist char copyright[] = 9*19845Sdist "@(#) Copyright (c) 1980 Regents of the University of California.\n\ 10*19845Sdist All rights reserved.\n"; 11*19845Sdist #endif not lint 128839Smckusick 13*19845Sdist #ifndef lint 14*19845Sdist static char sccsid[] = "@(#)mv.c 5.1 (Berkeley) 04/30/85"; 15*19845Sdist #endif not lint 16*19845Sdist 171216Sbill /* 181216Sbill * mv file1 file2 191216Sbill */ 2010047Ssam #include <sys/param.h> 2110047Ssam #include <sys/stat.h> 221216Sbill 231216Sbill #include <stdio.h> 2413489Ssam #include <sys/dir.h> 2510047Ssam #include <errno.h> 261216Sbill #include <signal.h> 271216Sbill 281216Sbill #define DELIM '/' 291216Sbill #define MODEBITS 07777 301216Sbill 3110047Ssam #define ISDIR(st) (((st).st_mode&S_IFMT) == S_IFDIR) 3210047Ssam #define ISLNK(st) (((st).st_mode&S_IFMT) == S_IFLNK) 3310047Ssam #define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG) 3410047Ssam #define ISDEV(st) \ 3510047Ssam (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK) 3610047Ssam 371216Sbill char *sprintf(); 381216Sbill char *dname(); 391216Sbill struct stat s1, s2; 4010047Ssam int iflag = 0; /* interactive mode */ 4110047Ssam int fflag = 0; /* force overwriting */ 4210047Ssam extern unsigned errno; 431216Sbill 441216Sbill main(argc, argv) 4510047Ssam register char *argv[]; 461216Sbill { 471216Sbill register i, r; 481931Sroot register char *arg; 4910641Smckusick char *dest; 501216Sbill 511216Sbill if (argc < 2) 521216Sbill goto usage; 5310047Ssam while (argc > 1 && *argv[1] == '-') { 541216Sbill argc--; 551931Sroot arg = *++argv; 561216Sbill 571931Sroot /* 5810047Ssam * all files following a null option 5910047Ssam * are considered file names 601931Sroot */ 6110047Ssam if (*(arg+1) == '\0') 6210047Ssam break; 6310047Ssam while (*++arg != '\0') switch (*arg) { 641931Sroot 6510047Ssam case 'i': 6610047Ssam iflag++; 6710047Ssam break; 681931Sroot 6910047Ssam case 'f': 7010047Ssam fflag++; 7110047Ssam break; 721216Sbill 7310047Ssam default: 7410047Ssam goto usage; 7510047Ssam } 761216Sbill } 771216Sbill if (argc < 3) 781216Sbill goto usage; 7910641Smckusick dest = argv[argc-1]; 8010642Smckusick if (stat(dest, &s2) >= 0 && ISDIR(s2)) { 8110114Ssam r = 0; 8210047Ssam for (i = 1; i < argc-1; i++) 8310047Ssam r |= movewithshortname(argv[i], dest); 8410047Ssam exit(r); 851216Sbill } 8610641Smckusick if (argc > 3) 8710641Smckusick goto usage; 8810641Smckusick r = move(argv[1], argv[2]); 8910047Ssam exit(r); 9010047Ssam /*NOTREACHED*/ 911216Sbill usage: 9210047Ssam fprintf(stderr, 9310642Smckusick "usage: mv [-if] f1 f2 or mv [-if] f1 ... fn d1 (`fn' is a file or directory)\n"); 9410047Ssam return (1); 951216Sbill } 961216Sbill 9710047Ssam movewithshortname(src, dest) 9810047Ssam char *src, *dest; 9910047Ssam { 10010047Ssam register char *shortname; 10110047Ssam char target[MAXPATHLEN + 1]; 10210047Ssam 10310047Ssam shortname = dname(src); 10410047Ssam if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) { 10510047Ssam error("%s/%s: pathname too long", dest, 10610047Ssam shortname); 10710047Ssam return (1); 10810047Ssam } 10910047Ssam sprintf(target, "%s/%s", dest, shortname); 11010047Ssam return (move(src, target)); 11110047Ssam } 11210047Ssam 1131216Sbill move(source, target) 11410047Ssam char *source, *target; 1151216Sbill { 11611642Ssam int targetexists; 1171216Sbill 1188839Smckusick if (lstat(source, &s1) < 0) { 11910047Ssam error("cannot access %s", source); 12010047Ssam return (1); 1211216Sbill } 12210047Ssam /* 12310047Ssam * First, try to rename source to destination. 12410047Ssam * The only reason we continue on failure is if 12510047Ssam * the move is on a nondirectory and not across 12610047Ssam * file systems. 12710047Ssam */ 12811642Ssam targetexists = lstat(target, &s2) >= 0; 12911642Ssam if (targetexists) { 13010047Ssam if (iflag && !fflag && query("remove %s? ", target) == 0) 13110047Ssam return (1); 13210047Ssam if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) { 13310047Ssam error("%s and %s are identical", source, target); 13410047Ssam return (1); 1351216Sbill } 13610047Ssam if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) { 13710047Ssam if (query("override protection %o for %s? ", 13810047Ssam s2.st_mode & MODEBITS, target) == 0) 13910047Ssam return (1); 1401216Sbill } 1411216Sbill } 14211642Ssam if (rename(source, target) >= 0) 14311642Ssam return (0); 14411642Ssam if (errno != EXDEV) { 14511642Ssam Perror2(source, "rename"); 14611642Ssam return (1); 14711642Ssam } 14811642Ssam if (ISDIR(s1)) { 14911642Ssam error("can't mv directories across file systems"); 15011642Ssam return (1); 15111642Ssam } 15211642Ssam if (targetexists && unlink(target) < 0) { 15311642Ssam error("cannot unlink %s", target); 15411642Ssam return (1); 15511642Ssam } 15610047Ssam /* 15710047Ssam * File can't be renamed, try to recreate the symbolic 15810047Ssam * link or special device, or copy the file wholesale 15910047Ssam * between file systems. 16010047Ssam */ 16110047Ssam if (ISLNK(s1)) { 1628839Smckusick register m; 16310047Ssam char symln[MAXPATHLEN]; 1648839Smckusick 1658839Smckusick if (readlink(source, symln, sizeof (symln)) < 0) { 16610047Ssam Perror(source); 1678839Smckusick return (1); 1688839Smckusick } 1698839Smckusick m = umask(~(s1.st_mode & MODEBITS)); 1708839Smckusick if (symlink(symln, target) < 0) { 17110047Ssam Perror(target); 1728839Smckusick return (1); 1738839Smckusick } 17410047Ssam (void) umask(m); 17510047Ssam goto cleanup; 17610047Ssam } 17710047Ssam if (ISDEV(s1)) { 17810166Ssam time_t tv[2]; 17910166Ssam 18010047Ssam if (mknod(target, s1.st_mode, s1.st_rdev) < 0) { 18110047Ssam Perror(target); 18210047Ssam return (1); 18310047Ssam } 18410166Ssam /* kludge prior to utimes */ 18510166Ssam tv[0] = s1.st_atime; 18610166Ssam tv[1] = s1.st_mtime; 18710166Ssam (void) utime(target, tv); 18810047Ssam goto cleanup; 18910047Ssam } 19010047Ssam if (ISREG(s1)) { 19110047Ssam int i, c, status; 19210166Ssam time_t tv[2]; 19310047Ssam 1941216Sbill i = fork(); 1951216Sbill if (i == -1) { 19610047Ssam error("try again"); 19710047Ssam return (1); 1981216Sbill } 1991216Sbill if (i == 0) { 2001216Sbill execl("/bin/cp", "cp", source, target, 0); 20110047Ssam error("cannot exec /bin/cp"); 2021216Sbill exit(1); 2031216Sbill } 2041216Sbill while ((c = wait(&status)) != i && c != -1) 2051216Sbill ; 2061216Sbill if (status != 0) 20710047Ssam return (1); 20810166Ssam /* kludge prior to utimes */ 20910166Ssam tv[0] = s1.st_atime; 21010166Ssam tv[1] = s1.st_mtime; 21110166Ssam (void) utime(target, tv); 21210047Ssam goto cleanup; 2131216Sbill } 21410047Ssam error("%s: unknown file type %o", source, s1.st_mode); 21510047Ssam return (1); 2161216Sbill 21710047Ssam cleanup: 2181216Sbill if (unlink(source) < 0) { 21910047Ssam error("cannot unlink %s", source); 22010047Ssam return (1); 2211216Sbill } 22210047Ssam return (0); 2231216Sbill } 2241216Sbill 22510047Ssam /*VARARGS*/ 22610047Ssam query(prompt, a1, a2) 22710047Ssam char *a1; 2281216Sbill { 22910047Ssam register char i, c; 2301216Sbill 23110047Ssam fprintf(stderr, prompt, a1, a2); 23210047Ssam i = c = getchar(); 23310047Ssam while (c != '\n' && c != EOF) 23410047Ssam c = getchar(); 23510047Ssam return (i == 'y'); 2361216Sbill } 2371216Sbill 2381216Sbill char * 2391216Sbill dname(name) 24010047Ssam register char *name; 2411216Sbill { 2421216Sbill register char *p; 2431216Sbill 2441216Sbill p = name; 2451216Sbill while (*p) 2461216Sbill if (*p++ == DELIM && *p) 2471216Sbill name = p; 2481216Sbill return name; 2491216Sbill } 2501216Sbill 25110047Ssam /*VARARGS*/ 25210047Ssam error(fmt, a1, a2) 25310047Ssam char *fmt; 2541216Sbill { 2551216Sbill 25610047Ssam fprintf(stderr, "mv: "); 25710047Ssam fprintf(stderr, fmt, a1, a2); 25810047Ssam fprintf(stderr, "\n"); 25910047Ssam } 2601216Sbill 26110047Ssam Perror(s) 26210047Ssam char *s; 26310047Ssam { 26410047Ssam char buf[MAXPATHLEN + 10]; 26510047Ssam 26610047Ssam sprintf(buf, "mv: %s", s); 26710047Ssam perror(buf); 2681216Sbill } 2691216Sbill 27010047Ssam Perror2(s1, s2) 27110047Ssam char *s1, *s2; 2721216Sbill { 27310047Ssam char buf[MAXPATHLEN + 20]; 27410047Ssam 27510047Ssam sprintf(buf, "mv: %s: %s", s1, s2); 27610047Ssam perror(buf); 2771216Sbill } 278