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"; 11*34045Sbostic #endif /* not lint */ 128839Smckusick 1319845Sdist #ifndef lint 14*34045Sbostic static char sccsid[] = "@(#)mv.c 5.7 (Berkeley) 04/21/88"; 15*34045Sbostic #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> 2333382Sbostic #include <sys/file.h> 241216Sbill #include <stdio.h> 2510047Ssam #include <errno.h> 261216Sbill 271216Sbill #define DELIM '/' 281216Sbill #define MODEBITS 07777 291216Sbill 3010047Ssam #define ISDIR(st) (((st).st_mode&S_IFMT) == S_IFDIR) 3110047Ssam #define ISLNK(st) (((st).st_mode&S_IFMT) == S_IFLNK) 3210047Ssam #define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG) 3310047Ssam #define ISDEV(st) \ 3410047Ssam (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK) 3510047Ssam 361216Sbill char *dname(); 3710047Ssam int iflag = 0; /* interactive mode */ 3810047Ssam int fflag = 0; /* force overwriting */ 3910047Ssam extern unsigned errno; 401216Sbill 411216Sbill main(argc, argv) 42*34045Sbostic register int argc; 43*34045Sbostic register char **argv; 441216Sbill { 45*34045Sbostic extern int optind; 46*34045Sbostic struct stat st; 47*34045Sbostic int ch, r; 4810641Smckusick char *dest; 491216Sbill 50*34045Sbostic while ((ch = getopt(argc, argv, "-fi")) != EOF) 51*34045Sbostic switch((char)ch) { 52*34045Sbostic case '-': 53*34045Sbostic goto endarg; 54*34045Sbostic case 'f': 55*34045Sbostic fflag++; 5610047Ssam break; 5710047Ssam case 'i': 5810047Ssam iflag++; 5910047Ssam break; 60*34045Sbostic case '?': 6110047Ssam default: 62*34045Sbostic usage(); 6310047Ssam } 64*34045Sbostic endarg: argv += optind; 65*34045Sbostic argc -= optind; 66*34045Sbostic 67*34045Sbostic if (argc < 2) 68*34045Sbostic usage(); 69*34045Sbostic dest = argv[argc - 1]; 70*34045Sbostic if (stat(dest, &st) >= 0 && ISDIR(st)) { 71*34045Sbostic for (r = 0; --argc; ++argv) 72*34045Sbostic r |= movewithshortname(*argv, dest); 7310047Ssam exit(r); 741216Sbill } 75*34045Sbostic if (argc != 2) 76*34045Sbostic usage(); 77*34045Sbostic r = move(argv[0], argv[1]); 7810047Ssam exit(r); 791216Sbill } 801216Sbill 8110047Ssam movewithshortname(src, dest) 8210047Ssam char *src, *dest; 8310047Ssam { 8410047Ssam register char *shortname; 8510047Ssam char target[MAXPATHLEN + 1]; 8610047Ssam 8710047Ssam shortname = dname(src); 8810047Ssam if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) { 8910047Ssam error("%s/%s: pathname too long", dest, 9010047Ssam shortname); 9110047Ssam return (1); 9210047Ssam } 9332415Sbostic (void)sprintf(target, "%s/%s", dest, shortname); 9410047Ssam return (move(src, target)); 9510047Ssam } 9610047Ssam 971216Sbill move(source, target) 9810047Ssam char *source, *target; 991216Sbill { 10011642Ssam int targetexists; 101*34045Sbostic struct stat s1, s2; 1021216Sbill 1038839Smckusick if (lstat(source, &s1) < 0) { 10422601Sserge Perror2(source, "Cannot access"); 10510047Ssam return (1); 1061216Sbill } 10710047Ssam /* 10810047Ssam * First, try to rename source to destination. 10910047Ssam * The only reason we continue on failure is if 11010047Ssam * the move is on a nondirectory and not across 11110047Ssam * file systems. 11210047Ssam */ 11311642Ssam targetexists = lstat(target, &s2) >= 0; 11411642Ssam if (targetexists) { 11510047Ssam if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) { 11610047Ssam error("%s and %s are identical", source, target); 11710047Ssam return (1); 1181216Sbill } 11933382Sbostic if (!fflag && isatty(fileno(stdin))) 12033382Sbostic if (iflag) { 12133382Sbostic if (!query("remove %s? ", target)) 12233382Sbostic return (1); 12333382Sbostic } 12433382Sbostic else if (access(target, W_OK) < 0 && 12533382Sbostic !query("override protection %o for %s? ", 12633382Sbostic s2.st_mode & MODEBITS, target)) 12710047Ssam return (1); 1281216Sbill } 12911642Ssam if (rename(source, target) >= 0) 13011642Ssam return (0); 13111642Ssam if (errno != EXDEV) { 13228202Slepreau Perror2(errno == ENOENT && targetexists == 0 ? target : source, 13328202Slepreau "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) { 14122601Sserge Perror2(target, "Cannot unlink"); 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; 15122601Sserge char symln[MAXPATHLEN + 1]; 1528839Smckusick 15322601Sserge m = readlink(source, symln, sizeof (symln) - 1); 15422601Sserge if (m < 0) { 15510047Ssam Perror(source); 1568839Smckusick return (1); 1578839Smckusick } 15822601Sserge symln[m] = '\0'; 15922601Sserge 16030907Slepreau (void) umask(~(s1.st_mode & MODEBITS)); 1618839Smckusick if (symlink(symln, target) < 0) { 16210047Ssam Perror(target); 1638839Smckusick return (1); 1648839Smckusick } 16510047Ssam goto cleanup; 16610047Ssam } 16730907Slepreau (void) umask(0); 16810047Ssam if (ISDEV(s1)) { 16922601Sserge struct timeval tv[2]; 17010166Ssam 17110047Ssam if (mknod(target, s1.st_mode, s1.st_rdev) < 0) { 17210047Ssam Perror(target); 17310047Ssam return (1); 17410047Ssam } 17522601Sserge 17622601Sserge tv[0].tv_sec = s1.st_atime; 17722601Sserge tv[0].tv_usec = 0; 17822601Sserge tv[1].tv_sec = s1.st_mtime; 17922601Sserge tv[1].tv_usec = 0; 18022601Sserge (void) utimes(target, tv); 18110047Ssam goto cleanup; 18210047Ssam } 18310047Ssam if (ISREG(s1)) { 18422601Sserge register int fi, fo, n; 18522601Sserge struct timeval tv[2]; 18622601Sserge char buf[MAXBSIZE]; 18710047Ssam 18822601Sserge fi = open(source, 0); 18922601Sserge if (fi < 0) { 19022601Sserge Perror(source); 19110047Ssam return (1); 1921216Sbill } 19322601Sserge 19422601Sserge fo = creat(target, s1.st_mode & MODEBITS); 19522601Sserge if (fo < 0) { 19622601Sserge Perror(target); 19722601Sserge close(fi); 19822601Sserge return (1); 1991216Sbill } 20022601Sserge 20122601Sserge for (;;) { 20222601Sserge n = read(fi, buf, sizeof buf); 20322601Sserge if (n == 0) { 20422601Sserge break; 20522601Sserge } else if (n < 0) { 20622601Sserge Perror2(source, "read"); 20722601Sserge close(fi); 20822601Sserge close(fo); 20922601Sserge return (1); 21022601Sserge } else if (write(fo, buf, n) != n) { 21122601Sserge Perror2(target, "write"); 21222601Sserge close(fi); 21322601Sserge close(fo); 21422601Sserge return (1); 21522601Sserge } 21622601Sserge } 21722601Sserge 21822601Sserge close(fi); 21922601Sserge close(fo); 22022601Sserge 22122601Sserge tv[0].tv_sec = s1.st_atime; 22222601Sserge tv[0].tv_usec = 0; 22322601Sserge tv[1].tv_sec = s1.st_mtime; 22422601Sserge tv[1].tv_usec = 0; 22522601Sserge (void) utimes(target, tv); 22610047Ssam goto cleanup; 2271216Sbill } 22810047Ssam error("%s: unknown file type %o", source, s1.st_mode); 22910047Ssam return (1); 2301216Sbill 23110047Ssam cleanup: 2321216Sbill if (unlink(source) < 0) { 23322601Sserge Perror2(source, "Cannot unlink"); 23410047Ssam return (1); 2351216Sbill } 23610047Ssam return (0); 2371216Sbill } 2381216Sbill 23910047Ssam /*VARARGS*/ 24010047Ssam query(prompt, a1, a2) 24110047Ssam char *a1; 2421216Sbill { 24322601Sserge register int i, c; 2441216Sbill 24510047Ssam fprintf(stderr, prompt, a1, a2); 24610047Ssam i = c = getchar(); 24710047Ssam while (c != '\n' && c != EOF) 24810047Ssam c = getchar(); 24910047Ssam return (i == 'y'); 2501216Sbill } 2511216Sbill 2521216Sbill char * 2531216Sbill dname(name) 25410047Ssam register char *name; 2551216Sbill { 2561216Sbill register char *p; 2571216Sbill 2581216Sbill p = name; 2591216Sbill while (*p) 2601216Sbill if (*p++ == DELIM && *p) 2611216Sbill name = p; 2621216Sbill return name; 2631216Sbill } 2641216Sbill 26510047Ssam /*VARARGS*/ 26610047Ssam error(fmt, a1, a2) 26710047Ssam char *fmt; 2681216Sbill { 2691216Sbill 27010047Ssam fprintf(stderr, "mv: "); 27110047Ssam fprintf(stderr, fmt, a1, a2); 27210047Ssam fprintf(stderr, "\n"); 27310047Ssam } 2741216Sbill 27510047Ssam Perror(s) 27610047Ssam char *s; 27710047Ssam { 27810047Ssam char buf[MAXPATHLEN + 10]; 27932415Sbostic 28032415Sbostic (void)sprintf(buf, "mv: %s", s); 28110047Ssam perror(buf); 2821216Sbill } 2831216Sbill 28410047Ssam Perror2(s1, s2) 28510047Ssam char *s1, *s2; 2861216Sbill { 28710047Ssam char buf[MAXPATHLEN + 20]; 28810047Ssam 28932415Sbostic (void)sprintf(buf, "mv: %s: %s", s1, s2); 29010047Ssam perror(buf); 2911216Sbill } 292*34045Sbostic 293*34045Sbostic usage() 294*34045Sbostic { 295*34045Sbostic fputs("usage: mv [-if] file1 file2 or mv [-if] file/directory ... directory\n", stderr); 296*34045Sbostic exit(1); 297*34045Sbostic } 298