119845Sdist /* 219845Sdist * Copyright (c) 1980 Regents of the University of California. 319845Sdist * All rights reserved. The Berkeley software License Agreement 419845Sdist * specifies the terms and conditions for redistribution. 519845Sdist */ 619845Sdist 710047Ssam #ifndef lint 819845Sdist char copyright[] = 919845Sdist "@(#) Copyright (c) 1980 Regents of the University of California.\n\ 1019845Sdist All rights reserved.\n"; 1119845Sdist #endif not lint 128839Smckusick 1319845Sdist #ifndef lint 14*33382Sbostic static char sccsid[] = "@(#)mv.c 5.6 (Berkeley) 01/21/88"; 1519845Sdist #endif not lint 1619845Sdist 171216Sbill /* 181216Sbill * mv file1 file2 191216Sbill */ 2010047Ssam #include <sys/param.h> 2110047Ssam #include <sys/stat.h> 2222601Sserge #include <sys/time.h> 23*33382Sbostic #include <sys/file.h> 241216Sbill 251216Sbill #include <stdio.h> 2613489Ssam #include <sys/dir.h> 2710047Ssam #include <errno.h> 281216Sbill #include <signal.h> 291216Sbill 301216Sbill #define DELIM '/' 311216Sbill #define MODEBITS 07777 321216Sbill 3310047Ssam #define ISDIR(st) (((st).st_mode&S_IFMT) == S_IFDIR) 3410047Ssam #define ISLNK(st) (((st).st_mode&S_IFMT) == S_IFLNK) 3510047Ssam #define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG) 3610047Ssam #define ISDEV(st) \ 3710047Ssam (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK) 3810047Ssam 391216Sbill char *dname(); 401216Sbill struct stat s1, s2; 4110047Ssam int iflag = 0; /* interactive mode */ 4210047Ssam int fflag = 0; /* force overwriting */ 4310047Ssam extern unsigned errno; 441216Sbill 451216Sbill main(argc, argv) 4610047Ssam register char *argv[]; 471216Sbill { 481216Sbill register i, r; 491931Sroot register char *arg; 5010641Smckusick char *dest; 511216Sbill 521216Sbill if (argc < 2) 531216Sbill goto usage; 5410047Ssam while (argc > 1 && *argv[1] == '-') { 551216Sbill argc--; 561931Sroot arg = *++argv; 571216Sbill 581931Sroot /* 5910047Ssam * all files following a null option 6010047Ssam * are considered file names 611931Sroot */ 6210047Ssam if (*(arg+1) == '\0') 6310047Ssam break; 6410047Ssam while (*++arg != '\0') switch (*arg) { 651931Sroot 6610047Ssam case 'i': 6710047Ssam iflag++; 6810047Ssam break; 691931Sroot 7010047Ssam case 'f': 7110047Ssam fflag++; 7210047Ssam break; 731216Sbill 7410047Ssam default: 7510047Ssam goto usage; 7610047Ssam } 771216Sbill } 781216Sbill if (argc < 3) 791216Sbill goto usage; 8010641Smckusick dest = argv[argc-1]; 8110642Smckusick if (stat(dest, &s2) >= 0 && ISDIR(s2)) { 8210114Ssam r = 0; 8310047Ssam for (i = 1; i < argc-1; i++) 8410047Ssam r |= movewithshortname(argv[i], dest); 8510047Ssam exit(r); 861216Sbill } 8710641Smckusick if (argc > 3) 8810641Smckusick goto usage; 8910641Smckusick r = move(argv[1], argv[2]); 9010047Ssam exit(r); 9110047Ssam /*NOTREACHED*/ 921216Sbill usage: 9310047Ssam fprintf(stderr, 9410642Smckusick "usage: mv [-if] f1 f2 or mv [-if] f1 ... fn d1 (`fn' is a file or directory)\n"); 9510047Ssam return (1); 961216Sbill } 971216Sbill 9810047Ssam movewithshortname(src, dest) 9910047Ssam char *src, *dest; 10010047Ssam { 10110047Ssam register char *shortname; 10210047Ssam char target[MAXPATHLEN + 1]; 10310047Ssam 10410047Ssam shortname = dname(src); 10510047Ssam if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) { 10610047Ssam error("%s/%s: pathname too long", dest, 10710047Ssam shortname); 10810047Ssam return (1); 10910047Ssam } 11032415Sbostic (void)sprintf(target, "%s/%s", dest, shortname); 11110047Ssam return (move(src, target)); 11210047Ssam } 11310047Ssam 1141216Sbill move(source, target) 11510047Ssam char *source, *target; 1161216Sbill { 11711642Ssam int targetexists; 1181216Sbill 1198839Smckusick if (lstat(source, &s1) < 0) { 12022601Sserge Perror2(source, "Cannot access"); 12110047Ssam return (1); 1221216Sbill } 12310047Ssam /* 12410047Ssam * First, try to rename source to destination. 12510047Ssam * The only reason we continue on failure is if 12610047Ssam * the move is on a nondirectory and not across 12710047Ssam * file systems. 12810047Ssam */ 12911642Ssam targetexists = lstat(target, &s2) >= 0; 13011642Ssam if (targetexists) { 13110047Ssam if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) { 13210047Ssam error("%s and %s are identical", source, target); 13310047Ssam return (1); 1341216Sbill } 135*33382Sbostic if (!fflag && isatty(fileno(stdin))) 136*33382Sbostic if (iflag) { 137*33382Sbostic if (!query("remove %s? ", target)) 138*33382Sbostic return (1); 139*33382Sbostic } 140*33382Sbostic else if (access(target, W_OK) < 0 && 141*33382Sbostic !query("override protection %o for %s? ", 142*33382Sbostic s2.st_mode & MODEBITS, target)) 14310047Ssam return (1); 1441216Sbill } 14511642Ssam if (rename(source, target) >= 0) 14611642Ssam return (0); 14711642Ssam if (errno != EXDEV) { 14828202Slepreau Perror2(errno == ENOENT && targetexists == 0 ? target : source, 14928202Slepreau "rename"); 15011642Ssam return (1); 15111642Ssam } 15211642Ssam if (ISDIR(s1)) { 15311642Ssam error("can't mv directories across file systems"); 15411642Ssam return (1); 15511642Ssam } 15611642Ssam if (targetexists && unlink(target) < 0) { 15722601Sserge Perror2(target, "Cannot unlink"); 15811642Ssam return (1); 15911642Ssam } 16010047Ssam /* 16110047Ssam * File can't be renamed, try to recreate the symbolic 16210047Ssam * link or special device, or copy the file wholesale 16310047Ssam * between file systems. 16410047Ssam */ 16510047Ssam if (ISLNK(s1)) { 1668839Smckusick register m; 16722601Sserge char symln[MAXPATHLEN + 1]; 1688839Smckusick 16922601Sserge m = readlink(source, symln, sizeof (symln) - 1); 17022601Sserge if (m < 0) { 17110047Ssam Perror(source); 1728839Smckusick return (1); 1738839Smckusick } 17422601Sserge symln[m] = '\0'; 17522601Sserge 17630907Slepreau (void) umask(~(s1.st_mode & MODEBITS)); 1778839Smckusick if (symlink(symln, target) < 0) { 17810047Ssam Perror(target); 1798839Smckusick return (1); 1808839Smckusick } 18110047Ssam goto cleanup; 18210047Ssam } 18330907Slepreau (void) umask(0); 18410047Ssam if (ISDEV(s1)) { 18522601Sserge struct timeval tv[2]; 18610166Ssam 18710047Ssam if (mknod(target, s1.st_mode, s1.st_rdev) < 0) { 18810047Ssam Perror(target); 18910047Ssam return (1); 19010047Ssam } 19122601Sserge 19222601Sserge tv[0].tv_sec = s1.st_atime; 19322601Sserge tv[0].tv_usec = 0; 19422601Sserge tv[1].tv_sec = s1.st_mtime; 19522601Sserge tv[1].tv_usec = 0; 19622601Sserge (void) utimes(target, tv); 19710047Ssam goto cleanup; 19810047Ssam } 19910047Ssam if (ISREG(s1)) { 20022601Sserge register int fi, fo, n; 20122601Sserge struct timeval tv[2]; 20222601Sserge char buf[MAXBSIZE]; 20310047Ssam 20422601Sserge fi = open(source, 0); 20522601Sserge if (fi < 0) { 20622601Sserge Perror(source); 20710047Ssam return (1); 2081216Sbill } 20922601Sserge 21022601Sserge fo = creat(target, s1.st_mode & MODEBITS); 21122601Sserge if (fo < 0) { 21222601Sserge Perror(target); 21322601Sserge close(fi); 21422601Sserge return (1); 2151216Sbill } 21622601Sserge 21722601Sserge for (;;) { 21822601Sserge n = read(fi, buf, sizeof buf); 21922601Sserge if (n == 0) { 22022601Sserge break; 22122601Sserge } else if (n < 0) { 22222601Sserge Perror2(source, "read"); 22322601Sserge close(fi); 22422601Sserge close(fo); 22522601Sserge return (1); 22622601Sserge } else if (write(fo, buf, n) != n) { 22722601Sserge Perror2(target, "write"); 22822601Sserge close(fi); 22922601Sserge close(fo); 23022601Sserge return (1); 23122601Sserge } 23222601Sserge } 23322601Sserge 23422601Sserge close(fi); 23522601Sserge close(fo); 23622601Sserge 23722601Sserge tv[0].tv_sec = s1.st_atime; 23822601Sserge tv[0].tv_usec = 0; 23922601Sserge tv[1].tv_sec = s1.st_mtime; 24022601Sserge tv[1].tv_usec = 0; 24122601Sserge (void) utimes(target, tv); 24210047Ssam goto cleanup; 2431216Sbill } 24410047Ssam error("%s: unknown file type %o", source, s1.st_mode); 24510047Ssam return (1); 2461216Sbill 24710047Ssam cleanup: 2481216Sbill if (unlink(source) < 0) { 24922601Sserge Perror2(source, "Cannot unlink"); 25010047Ssam return (1); 2511216Sbill } 25210047Ssam return (0); 2531216Sbill } 2541216Sbill 25510047Ssam /*VARARGS*/ 25610047Ssam query(prompt, a1, a2) 25710047Ssam char *a1; 2581216Sbill { 25922601Sserge register int i, c; 2601216Sbill 26110047Ssam fprintf(stderr, prompt, a1, a2); 26210047Ssam i = c = getchar(); 26310047Ssam while (c != '\n' && c != EOF) 26410047Ssam c = getchar(); 26510047Ssam return (i == 'y'); 2661216Sbill } 2671216Sbill 2681216Sbill char * 2691216Sbill dname(name) 27010047Ssam register char *name; 2711216Sbill { 2721216Sbill register char *p; 2731216Sbill 2741216Sbill p = name; 2751216Sbill while (*p) 2761216Sbill if (*p++ == DELIM && *p) 2771216Sbill name = p; 2781216Sbill return name; 2791216Sbill } 2801216Sbill 28110047Ssam /*VARARGS*/ 28210047Ssam error(fmt, a1, a2) 28310047Ssam char *fmt; 2841216Sbill { 2851216Sbill 28610047Ssam fprintf(stderr, "mv: "); 28710047Ssam fprintf(stderr, fmt, a1, a2); 28810047Ssam fprintf(stderr, "\n"); 28910047Ssam } 2901216Sbill 29110047Ssam Perror(s) 29210047Ssam char *s; 29310047Ssam { 29410047Ssam char buf[MAXPATHLEN + 10]; 29532415Sbostic 29632415Sbostic (void)sprintf(buf, "mv: %s", s); 29710047Ssam perror(buf); 2981216Sbill } 2991216Sbill 30010047Ssam Perror2(s1, s2) 30110047Ssam char *s1, *s2; 3021216Sbill { 30310047Ssam char buf[MAXPATHLEN + 20]; 30410047Ssam 30532415Sbostic (void)sprintf(buf, "mv: %s: %s", s1, s2); 30610047Ssam perror(buf); 3071216Sbill } 308