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*47754Sbostic static char sccsid[] = "@(#)mv.c 5.11 (Berkeley) 04/03/91"; 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> 2547751Sbostic #include <fcntl.h> 2647751Sbostic #include <errno.h> 2747751Sbostic #include <unistd.h> 281216Sbill #include <stdio.h> 2947751Sbostic #include <stdlib.h> 3039246Sbostic #include <string.h> 3139246Sbostic #include "pathnames.h" 321216Sbill 3339246Sbostic int fflg, iflg; 341216Sbill 351216Sbill main(argc, argv) 3639246Sbostic int argc; 3739246Sbostic char **argv; 381216Sbill { 3939246Sbostic extern char *optarg; 4034045Sbostic extern int optind; 4139246Sbostic register int baselen, exitval, len; 4239246Sbostic register char *p, *endp; 4347751Sbostic struct stat sb; 4439246Sbostic int ch; 4539246Sbostic char path[MAXPATHLEN + 1]; 461216Sbill 4739246Sbostic while (((ch = getopt(argc, argv, "-if")) != EOF)) 4834045Sbostic switch((char)ch) { 4939246Sbostic case 'i': 5047751Sbostic iflg = 1; 5139246Sbostic break; 5234045Sbostic case 'f': 5347751Sbostic fflg = 1; 5410047Ssam break; 5539246Sbostic case '-': /* undocumented; for compatibility */ 5639246Sbostic goto endarg; 5734045Sbostic case '?': 5810047Ssam default: 5934045Sbostic usage(); 6010047Ssam } 6139246Sbostic endarg: argc -= optind; 6239246Sbostic argv += optind; 6334045Sbostic 6434045Sbostic if (argc < 2) 6534045Sbostic usage(); 6639246Sbostic 6739246Sbostic /* 6847751Sbostic * If the stat on the target fails or the target isn't a directory, 6947751Sbostic * try the move. More than 2 arguments is an error in this case. 7039246Sbostic */ 7147751Sbostic if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { 7239246Sbostic if (argc > 2) 7339246Sbostic usage(); 7439246Sbostic exit(do_move(argv[0], argv[1])); 751216Sbill } 761216Sbill 7747751Sbostic /* It's a directory, move each file into it. */ 7839246Sbostic (void)strcpy(path, argv[argc - 1]); 7939246Sbostic baselen = strlen(path); 8039246Sbostic endp = &path[baselen]; 8139246Sbostic *endp++ = '/'; 8239246Sbostic ++baselen; 8339246Sbostic for (exitval = 0; --argc; ++argv) { 8439246Sbostic if ((p = rindex(*argv, '/')) == NULL) 8539246Sbostic p = *argv; 8639246Sbostic else 8739246Sbostic ++p; 8839246Sbostic if ((baselen + (len = strlen(p))) >= MAXPATHLEN) 8939246Sbostic (void)fprintf(stderr, 9039246Sbostic "mv: %s: destination pathname too long\n", *argv); 9139246Sbostic else { 9239246Sbostic bcopy(p, endp, len + 1); 9339246Sbostic exitval |= do_move(*argv, path); 9439246Sbostic } 9510047Ssam } 9639246Sbostic exit(exitval); 9710047Ssam } 9810047Ssam 9939246Sbostic do_move(from, to) 10039246Sbostic char *from, *to; 1011216Sbill { 10247751Sbostic struct stat sb; 10339246Sbostic int ask, ch; 1041216Sbill 10510047Ssam /* 10647751Sbostic * Check access. If interactive and file exists, ask user if it 10739246Sbostic * should be replaced. Otherwise if file exists but isn't writable 10839246Sbostic * make sure the user wants to clobber it. 10910047Ssam */ 11039246Sbostic if (!fflg && !access(to, F_OK)) { 11139246Sbostic ask = 0; 11239246Sbostic if (iflg) { 11339246Sbostic (void)fprintf(stderr, "overwrite %s? ", to); 11439246Sbostic ask = 1; 1151216Sbill } 11647751Sbostic else if (access(to, W_OK) && !stat(to, &sb)) { 11739246Sbostic (void)fprintf(stderr, "override mode %o on %s? ", 11847751Sbostic sb.st_mode & 07777, to); 11939246Sbostic ask = 1; 12039246Sbostic } 12139246Sbostic if (ask) { 12239246Sbostic if ((ch = getchar()) != EOF && ch != '\n') 12339246Sbostic while (getchar() != '\n'); 12439246Sbostic if (ch != 'y') 12539246Sbostic return(0); 12639246Sbostic } 1271216Sbill } 12839246Sbostic if (!rename(from, to)) 12939246Sbostic return(0); 13047751Sbostic 13111642Ssam if (errno != EXDEV) { 13239246Sbostic (void)fprintf(stderr, 13339246Sbostic "mv: rename %s to %s: %s\n", from, to, strerror(errno)); 13439246Sbostic return(1); 13511642Ssam } 13647751Sbostic 13710047Ssam /* 13847751Sbostic * If rename fails, and it's a regular file, do the copy internally; 13947751Sbostic * otherwise, use cp and rm. 14010047Ssam */ 14147751Sbostic if (stat(from, &sb)) { 14247751Sbostic (void)fprintf(stderr, "mv: %s: %s\n", from, strerror(errno)); 14339246Sbostic return(1); 14410047Ssam } 14547751Sbostic return(S_ISREG(sb.st_mode) ? 14647751Sbostic fastcopy(from, to, &sb) : copy(from, to)); 14739246Sbostic } 14810166Ssam 14939246Sbostic fastcopy(from, to, sbp) 15039246Sbostic char *from, *to; 15139246Sbostic struct stat *sbp; 15239246Sbostic { 15339246Sbostic struct timeval tval[2]; 15439246Sbostic static u_int blen; 15539246Sbostic static char *bp; 15639246Sbostic register int nread, from_fd, to_fd; 15722601Sserge 15839246Sbostic if ((from_fd = open(from, O_RDONLY, 0)) < 0) { 15947751Sbostic error(from); 16039246Sbostic return(1); 16110047Ssam } 16247751Sbostic if ((to_fd = open(to, O_CREAT|O_TRUNC|O_WRONLY, sbp->st_mode)) < 0) { 16347751Sbostic error(to); 16439246Sbostic (void)close(from_fd); 16539246Sbostic return(1); 16639246Sbostic } 16739246Sbostic if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { 16847751Sbostic error(NULL); 16939246Sbostic return(1); 17039246Sbostic } 17139246Sbostic while ((nread = read(from_fd, bp, blen)) > 0) 17239246Sbostic if (write(to_fd, bp, nread) != nread) { 17347751Sbostic error(to); 17439246Sbostic goto err; 1751216Sbill } 17639246Sbostic if (nread < 0) { 17747751Sbostic error(from); 17839246Sbostic err: (void)unlink(to); 17939246Sbostic (void)close(from_fd); 18039246Sbostic (void)close(to_fd); 18139246Sbostic return(1); 1821216Sbill } 18339246Sbostic (void)fchown(to_fd, sbp->st_uid, sbp->st_gid); 18439246Sbostic (void)fchmod(to_fd, sbp->st_mode); 1851216Sbill 18639246Sbostic (void)close(from_fd); 18739246Sbostic (void)close(to_fd); 1881216Sbill 18939246Sbostic tval[0].tv_sec = sbp->st_atime; 19039246Sbostic tval[1].tv_sec = sbp->st_mtime; 19139246Sbostic tval[0].tv_usec = tval[1].tv_usec = 0; 19239246Sbostic (void)utimes(to, tval); 19339246Sbostic (void)unlink(from); 19439246Sbostic return(0); 1951216Sbill } 1961216Sbill 19739246Sbostic copy(from, to) 19839246Sbostic char *from, *to; 1991216Sbill { 20039246Sbostic int pid, status; 2011216Sbill 20239246Sbostic if (!(pid = vfork())) { 203*47754Sbostic execl(_PATH_CP, "mv", "-pr", from, to, NULL); 20447751Sbostic error(_PATH_CP); 20539246Sbostic _exit(1); 20639246Sbostic } 20739246Sbostic (void)waitpid(pid, &status, 0); 20839246Sbostic if (!WIFEXITED(status) || WEXITSTATUS(status)) 20939246Sbostic return(1); 21039246Sbostic if (!(pid = vfork())) { 211*47754Sbostic execl(_PATH_RM, "mv", "-rf", from, NULL); 21247751Sbostic error(_PATH_RM); 21339246Sbostic _exit(1); 21439246Sbostic } 21539246Sbostic (void)waitpid(pid, &status, 0); 21639246Sbostic return(!WIFEXITED(status) || WEXITSTATUS(status)); 2171216Sbill } 2181216Sbill 21947751Sbostic error(s) 22047751Sbostic char *s; 22147751Sbostic { 22247751Sbostic if (s) 22347751Sbostic (void)fprintf(stderr, "mv: %s: %s\n", s, strerror(errno)); 22447751Sbostic else 22547751Sbostic (void)fprintf(stderr, "mv: %s\n", strerror(errno)); 22647751Sbostic } 22747751Sbostic 22834045Sbostic usage() 22934045Sbostic { 23039246Sbostic (void)fprintf(stderr, 23139246Sbostic "usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n"); 23234045Sbostic exit(1); 23334045Sbostic } 234