119845Sdist /* 2*60673Sbostic * Copyright (c) 1989, 1993 3*60673Sbostic * The Regents of the University of California. All rights reserved. 439246Sbostic * 539246Sbostic * This code is derived from software contributed to Berkeley by 639246Sbostic * Ken Smith of The State University of New York at Buffalo. 739246Sbostic * 842536Sbostic * %sccs.include.redist.c% 919845Sdist */ 1019845Sdist 1110047Ssam #ifndef lint 12*60673Sbostic static char copyright[] = 13*60673Sbostic "@(#) Copyright (c) 1989, 1993\n\ 14*60673Sbostic The Regents of the University of California. All rights reserved.\n"; 1534045Sbostic #endif /* not lint */ 168839Smckusick 1719845Sdist #ifndef lint 18*60673Sbostic static char sccsid[] = "@(#)mv.c 8.1 (Berkeley) 05/31/93"; 1934045Sbostic #endif /* not lint */ 2019845Sdist 2110047Ssam #include <sys/param.h> 2239246Sbostic #include <sys/time.h> 2339246Sbostic #include <sys/wait.h> 2410047Ssam #include <sys/stat.h> 2556905Sbostic 2659503Sbostic #include <err.h> 2756905Sbostic #include <errno.h> 2847751Sbostic #include <fcntl.h> 291216Sbill #include <stdio.h> 3047751Sbostic #include <stdlib.h> 3139246Sbostic #include <string.h> 3256905Sbostic #include <unistd.h> 3356905Sbostic 3439246Sbostic #include "pathnames.h" 351216Sbill 3639246Sbostic int fflg, iflg; 371216Sbill 3856905Sbostic int copy __P((char *, char *)); 3956905Sbostic int do_move __P((char *, char *)); 4056905Sbostic int fastcopy __P((char *, char *, struct stat *)); 4156905Sbostic void usage __P((void)); 4256905Sbostic 4356905Sbostic int 441216Sbill main(argc, argv) 4539246Sbostic int argc; 4656905Sbostic char *argv[]; 471216Sbill { 4839246Sbostic register int baselen, exitval, len; 4939246Sbostic register char *p, *endp; 5047751Sbostic struct stat sb; 5139246Sbostic int ch; 5239246Sbostic char path[MAXPATHLEN + 1]; 531216Sbill 5439246Sbostic while (((ch = getopt(argc, argv, "-if")) != EOF)) 5534045Sbostic switch((char)ch) { 5639246Sbostic case 'i': 5747751Sbostic iflg = 1; 5839246Sbostic break; 5934045Sbostic case 'f': 6047751Sbostic fflg = 1; 6110047Ssam break; 6256905Sbostic case '-': /* Undocumented; for compatibility. */ 6339246Sbostic goto endarg; 6434045Sbostic case '?': 6510047Ssam default: 6634045Sbostic usage(); 6710047Ssam } 6839246Sbostic endarg: argc -= optind; 6939246Sbostic argv += optind; 7034045Sbostic 7134045Sbostic if (argc < 2) 7234045Sbostic usage(); 7339246Sbostic 7439246Sbostic /* 7547751Sbostic * If the stat on the target fails or the target isn't a directory, 7647751Sbostic * try the move. More than 2 arguments is an error in this case. 7739246Sbostic */ 7847751Sbostic if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { 7939246Sbostic if (argc > 2) 8039246Sbostic usage(); 8139246Sbostic exit(do_move(argv[0], argv[1])); 821216Sbill } 831216Sbill 8447751Sbostic /* It's a directory, move each file into it. */ 8539246Sbostic (void)strcpy(path, argv[argc - 1]); 8639246Sbostic baselen = strlen(path); 8739246Sbostic endp = &path[baselen]; 8839246Sbostic *endp++ = '/'; 8939246Sbostic ++baselen; 9039246Sbostic for (exitval = 0; --argc; ++argv) { 9159503Sbostic if ((p = strrchr(*argv, '/')) == NULL) 9239246Sbostic p = *argv; 9339246Sbostic else 9439246Sbostic ++p; 9556905Sbostic if ((baselen + (len = strlen(p))) >= MAXPATHLEN) { 9659503Sbostic warnx("%s: destination pathname too long", *argv); 9756905Sbostic exitval = 1; 9856905Sbostic } else { 9959503Sbostic memmove(endp, p, len + 1); 10039246Sbostic exitval |= do_move(*argv, path); 10139246Sbostic } 10210047Ssam } 10339246Sbostic exit(exitval); 10410047Ssam } 10510047Ssam 10656905Sbostic int 10739246Sbostic do_move(from, to) 10839246Sbostic char *from, *to; 1091216Sbill { 11047751Sbostic struct stat sb; 11139246Sbostic int ask, ch; 1121216Sbill 11310047Ssam /* 11447751Sbostic * Check access. If interactive and file exists, ask user if it 11539246Sbostic * should be replaced. Otherwise if file exists but isn't writable 11639246Sbostic * make sure the user wants to clobber it. 11710047Ssam */ 11839246Sbostic if (!fflg && !access(to, F_OK)) { 11939246Sbostic ask = 0; 12039246Sbostic if (iflg) { 12139246Sbostic (void)fprintf(stderr, "overwrite %s? ", to); 12239246Sbostic ask = 1; 1231216Sbill } 12447751Sbostic else if (access(to, W_OK) && !stat(to, &sb)) { 12539246Sbostic (void)fprintf(stderr, "override mode %o on %s? ", 12647751Sbostic sb.st_mode & 07777, to); 12739246Sbostic ask = 1; 12839246Sbostic } 12939246Sbostic if (ask) { 13039246Sbostic if ((ch = getchar()) != EOF && ch != '\n') 13139246Sbostic while (getchar() != '\n'); 13239246Sbostic if (ch != 'y') 13356905Sbostic return (0); 13439246Sbostic } 1351216Sbill } 13639246Sbostic if (!rename(from, to)) 13756905Sbostic return (0); 13847751Sbostic 13911642Ssam if (errno != EXDEV) { 14059503Sbostic warn("rename %s to %s", from, to); 14156905Sbostic return (1); 14211642Ssam } 14347751Sbostic 14410047Ssam /* 14547751Sbostic * If rename fails, and it's a regular file, do the copy internally; 14647751Sbostic * otherwise, use cp and rm. 14710047Ssam */ 14847751Sbostic if (stat(from, &sb)) { 14959503Sbostic warn("%s", from); 15056905Sbostic return (1); 15110047Ssam } 15256905Sbostic return (S_ISREG(sb.st_mode) ? 15347751Sbostic fastcopy(from, to, &sb) : copy(from, to)); 15439246Sbostic } 15510166Ssam 15656905Sbostic int 15739246Sbostic fastcopy(from, to, sbp) 15839246Sbostic char *from, *to; 15939246Sbostic struct stat *sbp; 16039246Sbostic { 16139246Sbostic struct timeval tval[2]; 16239246Sbostic static u_int blen; 16339246Sbostic static char *bp; 16439246Sbostic register int nread, from_fd, to_fd; 16522601Sserge 16639246Sbostic if ((from_fd = open(from, O_RDONLY, 0)) < 0) { 16759503Sbostic warn("%s", from); 16856905Sbostic return (1); 16910047Ssam } 17047751Sbostic if ((to_fd = open(to, O_CREAT|O_TRUNC|O_WRONLY, sbp->st_mode)) < 0) { 17159503Sbostic warn("%s", to); 17239246Sbostic (void)close(from_fd); 17356905Sbostic return (1); 17439246Sbostic } 17539246Sbostic if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { 17659503Sbostic warn(NULL); 17756905Sbostic return (1); 17839246Sbostic } 17939246Sbostic while ((nread = read(from_fd, bp, blen)) > 0) 18039246Sbostic if (write(to_fd, bp, nread) != nread) { 18159503Sbostic warn("%s", to); 18239246Sbostic goto err; 1831216Sbill } 18439246Sbostic if (nread < 0) { 18559503Sbostic warn("%s", from); 18659503Sbostic err: if (unlink(to)) 18759503Sbostic warn("%s: remove", to); 18839246Sbostic (void)close(from_fd); 18939246Sbostic (void)close(to_fd); 19056905Sbostic return (1); 1911216Sbill } 19239246Sbostic (void)close(from_fd); 1931216Sbill 19459503Sbostic if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) 19559503Sbostic warn("%s: set owner/group", to); 19659503Sbostic if (fchmod(to_fd, sbp->st_mode)) 19759503Sbostic warn("%s: set mode", to); 19859503Sbostic 19939246Sbostic tval[0].tv_sec = sbp->st_atime; 20039246Sbostic tval[1].tv_sec = sbp->st_mtime; 20139246Sbostic tval[0].tv_usec = tval[1].tv_usec = 0; 20259503Sbostic if (utimes(to, tval)) 20359503Sbostic warn("%s: set times", to); 20459503Sbostic 20559503Sbostic if (close(to_fd)) { 20659503Sbostic warn("%s", to); 20759503Sbostic return (1); 20859503Sbostic } 20959503Sbostic 21059503Sbostic if (unlink(from)) { 21159503Sbostic warn("%s: remove", from); 21259503Sbostic return (1); 21359503Sbostic } 21456905Sbostic return (0); 2151216Sbill } 2161216Sbill 21756905Sbostic int 21839246Sbostic copy(from, to) 21939246Sbostic char *from, *to; 2201216Sbill { 22139246Sbostic int pid, status; 2221216Sbill 22339246Sbostic if (!(pid = vfork())) { 22456905Sbostic execl(_PATH_CP, "mv", "-pR", from, to, NULL); 22559503Sbostic warn("%s", _PATH_CP); 22639246Sbostic _exit(1); 22739246Sbostic } 22859503Sbostic if (waitpid(pid, &status, 0) == -1) { 22959503Sbostic warn("%s: waitpid", _PATH_CP); 23056905Sbostic return (1); 23159503Sbostic } 23259503Sbostic if (!WIFEXITED(status)) { 23359503Sbostic warn("%s: did not terminate normally", _PATH_CP); 23459503Sbostic return (1); 23559503Sbostic } 23659503Sbostic if (WEXITSTATUS(status)) { 23759503Sbostic warn("%s: terminated with %d (non-zero) status", 23859503Sbostic _PATH_CP, WEXITSTATUS(status)); 23959503Sbostic return (1); 24059503Sbostic } 24139246Sbostic if (!(pid = vfork())) { 24247754Sbostic execl(_PATH_RM, "mv", "-rf", from, NULL); 24359503Sbostic warn("%s", _PATH_RM); 24439246Sbostic _exit(1); 24539246Sbostic } 24659503Sbostic if (waitpid(pid, &status, 0) == -1) { 24759503Sbostic warn("%s: waitpid", _PATH_RM); 24859503Sbostic return (1); 24959503Sbostic } 25059503Sbostic if (!WIFEXITED(status)) { 25159503Sbostic warn("%s: did not terminate normally", _PATH_RM); 25259503Sbostic return (1); 25359503Sbostic } 25459503Sbostic if (WEXITSTATUS(status)) { 25559503Sbostic warn("%s: terminated with %d (non-zero) status", 25659503Sbostic _PATH_RM, WEXITSTATUS(status)); 25759503Sbostic return (1); 25859503Sbostic } 25959503Sbostic return (0); 2601216Sbill } 2611216Sbill 26256905Sbostic void 26334045Sbostic usage() 26434045Sbostic { 26539246Sbostic (void)fprintf(stderr, 26639246Sbostic "usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n"); 26734045Sbostic exit(1); 26834045Sbostic } 269