119845Sdist /* 239246Sbostic * Copyright (c) 1989 The Regents of the University of California. 339246Sbostic * 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 1219845Sdist char copyright[] = 1339246Sbostic "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 1419845Sdist All rights reserved.\n"; 1534045Sbostic #endif /* not lint */ 168839Smckusick 1719845Sdist #ifndef lint 18*59503Sbostic static char sccsid[] = "@(#)mv.c 5.13 (Berkeley) 04/29/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 26*59503Sbostic #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) { 91*59503Sbostic if ((p = strrchr(*argv, '/')) == NULL) 9239246Sbostic p = *argv; 9339246Sbostic else 9439246Sbostic ++p; 9556905Sbostic if ((baselen + (len = strlen(p))) >= MAXPATHLEN) { 96*59503Sbostic warnx("%s: destination pathname too long", *argv); 9756905Sbostic exitval = 1; 9856905Sbostic } else { 99*59503Sbostic 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) { 140*59503Sbostic 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)) { 149*59503Sbostic 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) { 167*59503Sbostic warn("%s", from); 16856905Sbostic return (1); 16910047Ssam } 17047751Sbostic if ((to_fd = open(to, O_CREAT|O_TRUNC|O_WRONLY, sbp->st_mode)) < 0) { 171*59503Sbostic warn("%s", to); 17239246Sbostic (void)close(from_fd); 17356905Sbostic return (1); 17439246Sbostic } 17539246Sbostic if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { 176*59503Sbostic warn(NULL); 17756905Sbostic return (1); 17839246Sbostic } 17939246Sbostic while ((nread = read(from_fd, bp, blen)) > 0) 18039246Sbostic if (write(to_fd, bp, nread) != nread) { 181*59503Sbostic warn("%s", to); 18239246Sbostic goto err; 1831216Sbill } 18439246Sbostic if (nread < 0) { 185*59503Sbostic warn("%s", from); 186*59503Sbostic err: if (unlink(to)) 187*59503Sbostic warn("%s: remove", to); 18839246Sbostic (void)close(from_fd); 18939246Sbostic (void)close(to_fd); 19056905Sbostic return (1); 1911216Sbill } 19239246Sbostic (void)close(from_fd); 1931216Sbill 194*59503Sbostic if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) 195*59503Sbostic warn("%s: set owner/group", to); 196*59503Sbostic if (fchmod(to_fd, sbp->st_mode)) 197*59503Sbostic warn("%s: set mode", to); 198*59503Sbostic 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; 202*59503Sbostic if (utimes(to, tval)) 203*59503Sbostic warn("%s: set times", to); 204*59503Sbostic 205*59503Sbostic if (close(to_fd)) { 206*59503Sbostic warn("%s", to); 207*59503Sbostic return (1); 208*59503Sbostic } 209*59503Sbostic 210*59503Sbostic if (unlink(from)) { 211*59503Sbostic warn("%s: remove", from); 212*59503Sbostic return (1); 213*59503Sbostic } 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); 225*59503Sbostic warn("%s", _PATH_CP); 22639246Sbostic _exit(1); 22739246Sbostic } 228*59503Sbostic if (waitpid(pid, &status, 0) == -1) { 229*59503Sbostic warn("%s: waitpid", _PATH_CP); 23056905Sbostic return (1); 231*59503Sbostic } 232*59503Sbostic if (!WIFEXITED(status)) { 233*59503Sbostic warn("%s: did not terminate normally", _PATH_CP); 234*59503Sbostic return (1); 235*59503Sbostic } 236*59503Sbostic if (WEXITSTATUS(status)) { 237*59503Sbostic warn("%s: terminated with %d (non-zero) status", 238*59503Sbostic _PATH_CP, WEXITSTATUS(status)); 239*59503Sbostic return (1); 240*59503Sbostic } 24139246Sbostic if (!(pid = vfork())) { 24247754Sbostic execl(_PATH_RM, "mv", "-rf", from, NULL); 243*59503Sbostic warn("%s", _PATH_RM); 24439246Sbostic _exit(1); 24539246Sbostic } 246*59503Sbostic if (waitpid(pid, &status, 0) == -1) { 247*59503Sbostic warn("%s: waitpid", _PATH_RM); 248*59503Sbostic return (1); 249*59503Sbostic } 250*59503Sbostic if (!WIFEXITED(status)) { 251*59503Sbostic warn("%s: did not terminate normally", _PATH_RM); 252*59503Sbostic return (1); 253*59503Sbostic } 254*59503Sbostic if (WEXITSTATUS(status)) { 255*59503Sbostic warn("%s: terminated with %d (non-zero) status", 256*59503Sbostic _PATH_RM, WEXITSTATUS(status)); 257*59503Sbostic return (1); 258*59503Sbostic } 259*59503Sbostic 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