119845Sdist /* 2*39246Sbostic * Copyright (c) 1989 The Regents of the University of California. 3*39246Sbostic * All rights reserved. 4*39246Sbostic * 5*39246Sbostic * This code is derived from software contributed to Berkeley by 6*39246Sbostic * Ken Smith of The State University of New York at Buffalo. 7*39246Sbostic * 8*39246Sbostic * Redistribution and use in source and binary forms are permitted 9*39246Sbostic * provided that the above copyright notice and this paragraph are 10*39246Sbostic * duplicated in all such forms and that any documentation, 11*39246Sbostic * advertising materials, and other materials related to such 12*39246Sbostic * distribution and use acknowledge that the software was developed 13*39246Sbostic * by the University of California, Berkeley. The name of the 14*39246Sbostic * University may not be used to endorse or promote products derived 15*39246Sbostic * from this software without specific prior written permission. 16*39246Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 17*39246Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 18*39246Sbostic * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1919845Sdist */ 2019845Sdist 2110047Ssam #ifndef lint 2219845Sdist char copyright[] = 23*39246Sbostic "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 2419845Sdist All rights reserved.\n"; 2534045Sbostic #endif /* not lint */ 268839Smckusick 2719845Sdist #ifndef lint 28*39246Sbostic static char sccsid[] = "@(#)mv.c 5.8 (Berkeley) 10/01/89"; 2934045Sbostic #endif /* not lint */ 3019845Sdist 3110047Ssam #include <sys/param.h> 32*39246Sbostic #include <sys/time.h> 33*39246Sbostic #include <sys/wait.h> 3410047Ssam #include <sys/stat.h> 3533382Sbostic #include <sys/file.h> 36*39246Sbostic #include <sys/errno.h> 371216Sbill #include <stdio.h> 38*39246Sbostic #include <string.h> 39*39246Sbostic #include "pathnames.h" 401216Sbill 41*39246Sbostic extern int errno; 42*39246Sbostic int fflg, iflg; 431216Sbill 441216Sbill main(argc, argv) 45*39246Sbostic int argc; 46*39246Sbostic char **argv; 471216Sbill { 48*39246Sbostic extern char *optarg; 4934045Sbostic extern int optind; 50*39246Sbostic register int baselen, exitval, len; 51*39246Sbostic register char *p, *endp; 52*39246Sbostic struct stat sbuf; 53*39246Sbostic int ch; 54*39246Sbostic char path[MAXPATHLEN + 1]; 551216Sbill 56*39246Sbostic while (((ch = getopt(argc, argv, "-if")) != EOF)) 5734045Sbostic switch((char)ch) { 58*39246Sbostic case 'i': 59*39246Sbostic ++iflg; 60*39246Sbostic break; 6134045Sbostic case 'f': 62*39246Sbostic ++fflg; 6310047Ssam break; 64*39246Sbostic case '-': /* undocumented; for compatibility */ 65*39246Sbostic goto endarg; 6634045Sbostic case '?': 6710047Ssam default: 6834045Sbostic usage(); 6910047Ssam } 70*39246Sbostic endarg: argc -= optind; 71*39246Sbostic argv += optind; 7234045Sbostic 7334045Sbostic if (argc < 2) 7434045Sbostic usage(); 75*39246Sbostic 76*39246Sbostic /* 77*39246Sbostic * if stat fails on target, it doesn't exist (or can't be accessed 78*39246Sbostic * by the user, doesn't matter which) try the move. If target exists, 79*39246Sbostic * and isn't a directory, try the move. More than 2 arguments is an 80*39246Sbostic * error. 81*39246Sbostic */ 82*39246Sbostic if (stat(argv[argc - 1], &sbuf) || !S_ISDIR(sbuf.st_mode)) { 83*39246Sbostic if (argc > 2) 84*39246Sbostic usage(); 85*39246Sbostic exit(do_move(argv[0], argv[1])); 861216Sbill } 871216Sbill 88*39246Sbostic /* got a directory, move each file into it */ 89*39246Sbostic (void)strcpy(path, argv[argc - 1]); 90*39246Sbostic baselen = strlen(path); 91*39246Sbostic endp = &path[baselen]; 92*39246Sbostic *endp++ = '/'; 93*39246Sbostic ++baselen; 94*39246Sbostic for (exitval = 0; --argc; ++argv) { 95*39246Sbostic if ((p = rindex(*argv, '/')) == NULL) 96*39246Sbostic p = *argv; 97*39246Sbostic else 98*39246Sbostic ++p; 99*39246Sbostic if ((baselen + (len = strlen(p))) >= MAXPATHLEN) 100*39246Sbostic (void)fprintf(stderr, 101*39246Sbostic "mv: %s: destination pathname too long\n", *argv); 102*39246Sbostic else { 103*39246Sbostic bcopy(p, endp, len + 1); 104*39246Sbostic exitval |= do_move(*argv, path); 105*39246Sbostic } 10610047Ssam } 107*39246Sbostic exit(exitval); 10810047Ssam } 10910047Ssam 110*39246Sbostic do_move(from, to) 111*39246Sbostic char *from, *to; 1121216Sbill { 113*39246Sbostic struct stat sbuf; 114*39246Sbostic int ask, ch; 1151216Sbill 11610047Ssam /* 117*39246Sbostic * Check access. If interactive and file exists ask user if it 118*39246Sbostic * should be replaced. Otherwise if file exists but isn't writable 119*39246Sbostic * make sure the user wants to clobber it. 12010047Ssam */ 121*39246Sbostic if (!fflg && !access(to, F_OK)) { 122*39246Sbostic ask = 0; 123*39246Sbostic if (iflg) { 124*39246Sbostic (void)fprintf(stderr, "overwrite %s? ", to); 125*39246Sbostic ask = 1; 1261216Sbill } 127*39246Sbostic else if (access(to, W_OK) && !stat(to, &sbuf)) { 128*39246Sbostic (void)fprintf(stderr, "override mode %o on %s? ", 129*39246Sbostic sbuf.st_mode & 07777, to); 130*39246Sbostic ask = 1; 131*39246Sbostic } 132*39246Sbostic if (ask) { 133*39246Sbostic if ((ch = getchar()) != EOF && ch != '\n') 134*39246Sbostic while (getchar() != '\n'); 135*39246Sbostic if (ch != 'y') 136*39246Sbostic return(0); 137*39246Sbostic } 1381216Sbill } 139*39246Sbostic if (!rename(from, to)) 140*39246Sbostic return(0); 14111642Ssam if (errno != EXDEV) { 142*39246Sbostic (void)fprintf(stderr, 143*39246Sbostic "mv: rename %s to %s: %s\n", from, to, strerror(errno)); 144*39246Sbostic return(1); 14511642Ssam } 14610047Ssam /* 147*39246Sbostic * if rename fails, and it's a regular file, do the copy 148*39246Sbostic * internally; otherwise, use cp and rm. 14910047Ssam */ 150*39246Sbostic if (stat(from, &sbuf)) { 151*39246Sbostic (void)fprintf(stderr, 152*39246Sbostic "mv: %s: %s\n", from, strerror(errno)); 153*39246Sbostic return(1); 15410047Ssam } 155*39246Sbostic return(S_ISREG(sbuf.st_mode) ? 156*39246Sbostic fastcopy(from, to, &sbuf) : copy(from, to)); 157*39246Sbostic } 15810166Ssam 159*39246Sbostic fastcopy(from, to, sbp) 160*39246Sbostic char *from, *to; 161*39246Sbostic struct stat *sbp; 162*39246Sbostic { 163*39246Sbostic struct timeval tval[2]; 164*39246Sbostic static u_int blen; 165*39246Sbostic static char *bp; 166*39246Sbostic register int nread, from_fd, to_fd; 167*39246Sbostic char *malloc(); 16822601Sserge 169*39246Sbostic if ((from_fd = open(from, O_RDONLY, 0)) < 0) { 170*39246Sbostic (void)fprintf(stderr, 171*39246Sbostic "mv: %s: %s\n", from, strerror(errno)); 172*39246Sbostic return(1); 17310047Ssam } 174*39246Sbostic if ((to_fd = open(to, O_WRONLY|O_CREAT|O_TRUNC, sbp->st_mode)) < 0) { 175*39246Sbostic (void)fprintf(stderr, 176*39246Sbostic "mv: %s: %s\n", to, strerror(errno)); 177*39246Sbostic (void)close(from_fd); 178*39246Sbostic return(1); 179*39246Sbostic } 180*39246Sbostic if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { 181*39246Sbostic (void)fprintf(stderr, "mv: %s: out of memory.\n", from); 182*39246Sbostic return(1); 183*39246Sbostic } 184*39246Sbostic while ((nread = read(from_fd, bp, blen)) > 0) 185*39246Sbostic if (write(to_fd, bp, nread) != nread) { 186*39246Sbostic (void)fprintf(stderr, "mv: %s: %s\n", 187*39246Sbostic to, strerror(errno)); 188*39246Sbostic goto err; 1891216Sbill } 190*39246Sbostic if (nread < 0) { 191*39246Sbostic (void)fprintf(stderr, "mv: %s: %s\n", from, strerror(errno)); 192*39246Sbostic err: (void)unlink(to); 193*39246Sbostic (void)close(from_fd); 194*39246Sbostic (void)close(to_fd); 195*39246Sbostic return(1); 1961216Sbill } 197*39246Sbostic (void)fchown(to_fd, sbp->st_uid, sbp->st_gid); 198*39246Sbostic (void)fchmod(to_fd, sbp->st_mode); 1991216Sbill 200*39246Sbostic (void)close(from_fd); 201*39246Sbostic (void)close(to_fd); 2021216Sbill 203*39246Sbostic tval[0].tv_sec = sbp->st_atime; 204*39246Sbostic tval[1].tv_sec = sbp->st_mtime; 205*39246Sbostic tval[0].tv_usec = tval[1].tv_usec = 0; 206*39246Sbostic (void)utimes(to, tval); 207*39246Sbostic (void)unlink(from); 208*39246Sbostic return(0); 2091216Sbill } 2101216Sbill 211*39246Sbostic copy(from, to) 212*39246Sbostic char *from, *to; 2131216Sbill { 214*39246Sbostic int pid, status; 2151216Sbill 216*39246Sbostic if (!(pid = vfork())) { 217*39246Sbostic execlp(_PATH_CP, "mv", "-pr", from, to); 218*39246Sbostic (void)fprintf(stderr, "mv: can't exec %s.\n", _PATH_CP); 219*39246Sbostic _exit(1); 220*39246Sbostic } 221*39246Sbostic (void)waitpid(pid, &status, 0); 222*39246Sbostic if (!WIFEXITED(status) || WEXITSTATUS(status)) 223*39246Sbostic return(1); 224*39246Sbostic if (!(pid = vfork())) { 225*39246Sbostic execlp(_PATH_RM, "mv", "-rf", from); 226*39246Sbostic (void)fprintf(stderr, "mv: can't exec %s.\n", _PATH_RM); 227*39246Sbostic _exit(1); 228*39246Sbostic } 229*39246Sbostic (void)waitpid(pid, &status, 0); 230*39246Sbostic return(!WIFEXITED(status) || WEXITSTATUS(status)); 2311216Sbill } 2321216Sbill 23334045Sbostic usage() 23434045Sbostic { 235*39246Sbostic (void)fprintf(stderr, 236*39246Sbostic "usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n"); 23734045Sbostic exit(1); 23834045Sbostic } 239