mv.c revision 11298
1237651Sbschmidt/* 2237651Sbschmidt * Copyright (c) 1989, 1993, 1994 3237651Sbschmidt * The Regents of the University of California. All rights reserved. 4237651Sbschmidt * 5237651Sbschmidt * This code is derived from software contributed to Berkeley by 6237651Sbschmidt * Ken Smith of The State University of New York at Buffalo. 7237651Sbschmidt * 8237651Sbschmidt * Redistribution and use in source and binary forms, with or without 9237651Sbschmidt * modification, are permitted provided that the following conditions 10237651Sbschmidt * are met: 11237651Sbschmidt * 1. Redistributions of source code must retain the above copyright 12237651Sbschmidt * notice, this list of conditions and the following disclaimer. 13237651Sbschmidt * 2. Redistributions in binary form must reproduce the above copyright 14237651Sbschmidt * notice, this list of conditions and the following disclaimer in the 15237651Sbschmidt * documentation and/or other materials provided with the distribution. 16237651Sbschmidt * 3. All advertising materials mentioning features or use of this software 17237651Sbschmidt * must display the following acknowledgement: 18237651Sbschmidt * This product includes software developed by the University of 19237651Sbschmidt * California, Berkeley and its contributors. 20237651Sbschmidt * 4. Neither the name of the University nor the names of its contributors 21237651Sbschmidt * may be used to endorse or promote products derived from this software 22237651Sbschmidt * without specific prior written permission. 23237651Sbschmidt * 24237651Sbschmidt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25237651Sbschmidt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26237651Sbschmidt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27237651Sbschmidt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28237651Sbschmidt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29237651Sbschmidt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30237651Sbschmidt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31237651Sbschmidt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32237651Sbschmidt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33237651Sbschmidt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34237651Sbschmidt * SUCH DAMAGE. 35237651Sbschmidt * 36237651Sbschmidt * $Id: mv.c,v 1.2 1994/09/24 02:56:07 davidg Exp $ 37237651Sbschmidt */ 38237651Sbschmidt 39237651Sbschmidt#ifndef lint 40237651Sbschmidtstatic char copyright[] = 41237651Sbschmidt"@(#) Copyright (c) 1989, 1993, 1994\n\ 42237651Sbschmidt The Regents of the University of California. All rights reserved.\n"; 43237651Sbschmidt#endif /* not lint */ 44237651Sbschmidt 45237651Sbschmidt#ifndef lint 46237651Sbschmidtstatic char sccsid[] = "@(#)mv.c 8.2 (Berkeley) 4/2/94"; 47237651Sbschmidt#endif /* not lint */ 48237651Sbschmidt 49237651Sbschmidt#include <sys/param.h> 50237651Sbschmidt#include <sys/time.h> 51237651Sbschmidt#include <sys/wait.h> 52237651Sbschmidt#include <sys/stat.h> 53237651Sbschmidt 54237651Sbschmidt#include <err.h> 55237651Sbschmidt#include <errno.h> 56237651Sbschmidt#include <fcntl.h> 57237651Sbschmidt#include <stdio.h> 58237651Sbschmidt#include <stdlib.h> 59237651Sbschmidt#include <string.h> 60237651Sbschmidt#include <unistd.h> 61237651Sbschmidt 62237651Sbschmidt#include "pathnames.h" 63237651Sbschmidt 64237651Sbschmidtint fflg, iflg; 65237651Sbschmidt 66237651Sbschmidtint copy __P((char *, char *)); 67237651Sbschmidtint do_move __P((char *, char *)); 68237651Sbschmidtint fastcopy __P((char *, char *, struct stat *)); 69237651Sbschmidtvoid usage __P((void)); 70237651Sbschmidt 71237651Sbschmidtint 72237651Sbschmidtmain(argc, argv) 73237651Sbschmidt int argc; 74237651Sbschmidt char *argv[]; 75237651Sbschmidt{ 76237651Sbschmidt register int baselen, len, rval; 77237651Sbschmidt register char *p, *endp; 78237651Sbschmidt struct stat sb; 79237651Sbschmidt int ch; 80237651Sbschmidt char path[MAXPATHLEN + 1]; 81237651Sbschmidt 82237651Sbschmidt while ((ch = getopt(argc, argv, "-if")) != EOF) 83237651Sbschmidt switch (ch) { 84237651Sbschmidt case 'i': 85237651Sbschmidt iflg = 1; 86237651Sbschmidt break; 87237651Sbschmidt case 'f': 88237651Sbschmidt fflg = 1; 89237651Sbschmidt break; 90237651Sbschmidt case '-': /* Undocumented; for compatibility. */ 91237651Sbschmidt goto endarg; 92237651Sbschmidt case '?': 93237651Sbschmidt default: 94237651Sbschmidt usage(); 95237651Sbschmidt } 96237651Sbschmidtendarg: argc -= optind; 97237651Sbschmidt argv += optind; 98237651Sbschmidt 99237651Sbschmidt if (argc < 2) 100237651Sbschmidt usage(); 101237651Sbschmidt 102237651Sbschmidt /* 103237651Sbschmidt * If the stat on the target fails or the target isn't a directory, 104237651Sbschmidt * try the move. More than 2 arguments is an error in this case. 105237651Sbschmidt */ 106237651Sbschmidt if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { 107237651Sbschmidt if (argc > 2) 108237651Sbschmidt usage(); 109237651Sbschmidt exit(do_move(argv[0], argv[1])); 110237651Sbschmidt } 111237651Sbschmidt 112237651Sbschmidt /* It's a directory, move each file into it. */ 113237651Sbschmidt (void)strcpy(path, argv[argc - 1]); 114237651Sbschmidt baselen = strlen(path); 115237651Sbschmidt endp = &path[baselen]; 116237651Sbschmidt *endp++ = '/'; 117237651Sbschmidt ++baselen; 118237651Sbschmidt for (rval = 0; --argc; ++argv) { 119237651Sbschmidt /* 120237651Sbschmidt * Find the last component of the source pathname. It 121237651Sbschmidt * may have trailing slashes. 122237651Sbschmidt */ 123237651Sbschmidt p = *argv + strlen(*argv); 124237651Sbschmidt while (p != *argv && p[-1] == '/') 125237651Sbschmidt --p; 126237651Sbschmidt while (p != *argv && p[-1] != '/') 127237651Sbschmidt --p; 128237651Sbschmidt 129237651Sbschmidt if ((baselen + (len = strlen(p))) >= MAXPATHLEN) { 130237651Sbschmidt warnx("%s: destination pathname too long", *argv); 131237651Sbschmidt rval = 1; 132237651Sbschmidt } else { 133237651Sbschmidt memmove(endp, p, len + 1); 134237651Sbschmidt if (do_move(*argv, path)) 135237651Sbschmidt rval = 1; 136237651Sbschmidt } 137237651Sbschmidt } 138237651Sbschmidt exit(rval); 139237651Sbschmidt} 140237651Sbschmidt 141237651Sbschmidtint 142237651Sbschmidtdo_move(from, to) 143237651Sbschmidt char *from, *to; 144237651Sbschmidt{ 145237651Sbschmidt struct stat sb; 146237651Sbschmidt int ask, ch; 147237651Sbschmidt char modep[15]; 148237651Sbschmidt 149237651Sbschmidt /* 150237651Sbschmidt * Check access. If interactive and file exists, ask user if it 151237651Sbschmidt * should be replaced. Otherwise if file exists but isn't writable 152237651Sbschmidt * make sure the user wants to clobber it. 153237651Sbschmidt */ 154237651Sbschmidt if (!fflg && !access(to, F_OK)) { 155237651Sbschmidt ask = 0; 156237651Sbschmidt if (iflg) { 157237651Sbschmidt (void)fprintf(stderr, "overwrite %s? ", to); 158237651Sbschmidt ask = 1; 159237651Sbschmidt } else if (access(to, W_OK) && !stat(to, &sb)) { 160237651Sbschmidt strmode(sb.st_mode, modep); 161237651Sbschmidt (void)fprintf(stderr, "override %s%s%s/%s for %s? ", 162237651Sbschmidt modep + 1, modep[9] == ' ' ? "" : " ", 163237651Sbschmidt user_from_uid(sb.st_uid, 0), 164237651Sbschmidt group_from_gid(sb.st_gid, 0), to); 165237651Sbschmidt ask = 1; 166237651Sbschmidt } 167237651Sbschmidt if (ask) { 168237651Sbschmidt if ((ch = getchar()) != EOF && ch != '\n') 169237651Sbschmidt while (getchar() != '\n'); 170237651Sbschmidt if (ch != 'y') 171237651Sbschmidt return (0); 172237651Sbschmidt } 173237651Sbschmidt } 174237651Sbschmidt if (!rename(from, to)) 175237651Sbschmidt return (0); 176237651Sbschmidt 177237651Sbschmidt if (errno != EXDEV) { 178237651Sbschmidt warn("rename %s to %s", from, to); 179237651Sbschmidt return (1); 180237651Sbschmidt } 181237651Sbschmidt 182237651Sbschmidt /* 183237651Sbschmidt * If rename fails because we're trying to cross devices, and 184237651Sbschmidt * it's a regular file, do the copy internally; otherwise, use 185237651Sbschmidt * cp and rm. 186237651Sbschmidt */ 187237651Sbschmidt if (stat(from, &sb)) { 188237651Sbschmidt warn("%s", from); 189237651Sbschmidt return (1); 190237651Sbschmidt } 191237651Sbschmidt return (S_ISREG(sb.st_mode) ? 192237651Sbschmidt fastcopy(from, to, &sb) : copy(from, to)); 193237651Sbschmidt} 194237651Sbschmidt 195237651Sbschmidtint 196237651Sbschmidtfastcopy(from, to, sbp) 197237651Sbschmidt char *from, *to; 198237651Sbschmidt struct stat *sbp; 199237651Sbschmidt{ 200237651Sbschmidt struct timeval tval[2]; 201237651Sbschmidt static u_int blen; 202237651Sbschmidt static char *bp; 203237651Sbschmidt register int nread, from_fd, to_fd; 204237651Sbschmidt 205237651Sbschmidt if ((from_fd = open(from, O_RDONLY, 0)) < 0) { 206237651Sbschmidt warn("%s", from); 207237651Sbschmidt return (1); 208237651Sbschmidt } 209237651Sbschmidt if ((to_fd = 210237651Sbschmidt open(to, O_CREAT | O_TRUNC | O_WRONLY, sbp->st_mode)) < 0) { 211237651Sbschmidt warn("%s", to); 212237651Sbschmidt (void)close(from_fd); 213237651Sbschmidt return (1); 214237651Sbschmidt } 215237651Sbschmidt if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { 216237651Sbschmidt warn(NULL); 217237651Sbschmidt return (1); 218237651Sbschmidt } 219237651Sbschmidt while ((nread = read(from_fd, bp, blen)) > 0) 220237651Sbschmidt if (write(to_fd, bp, nread) != nread) { 221237651Sbschmidt warn("%s", to); 222237651Sbschmidt goto err; 223237651Sbschmidt } 224237651Sbschmidt if (nread < 0) { 225237651Sbschmidt warn("%s", from); 226237651Sbschmidterr: if (unlink(to)) 227237651Sbschmidt warn("%s: remove", to); 228237651Sbschmidt (void)close(from_fd); 229237651Sbschmidt (void)close(to_fd); 230237651Sbschmidt return (1); 231237651Sbschmidt } 232237651Sbschmidt (void)close(from_fd); 233237651Sbschmidt 234237651Sbschmidt if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) 235237651Sbschmidt warn("%s: set owner/group", to); 236237651Sbschmidt if (fchmod(to_fd, sbp->st_mode)) 237237651Sbschmidt warn("%s: set mode", to); 238237651Sbschmidt 239237651Sbschmidt tval[0].tv_sec = sbp->st_atime; 240237651Sbschmidt tval[1].tv_sec = sbp->st_mtime; 241237651Sbschmidt tval[0].tv_usec = tval[1].tv_usec = 0; 242237651Sbschmidt if (utimes(to, tval)) 243237651Sbschmidt warn("%s: set times", to); 244237651Sbschmidt 245237651Sbschmidt if (close(to_fd)) { 246237651Sbschmidt warn("%s", to); 247237651Sbschmidt return (1); 248237651Sbschmidt } 249237651Sbschmidt 250237651Sbschmidt if (unlink(from)) { 251237651Sbschmidt warn("%s: remove", from); 252237651Sbschmidt return (1); 253237651Sbschmidt } 254237651Sbschmidt return (0); 255237651Sbschmidt} 256237651Sbschmidt 257237651Sbschmidtint 258237651Sbschmidtcopy(from, to) 259237651Sbschmidt char *from, *to; 260237651Sbschmidt{ 261237651Sbschmidt int pid, status; 262237651Sbschmidt 263237651Sbschmidt if ((pid = vfork()) == 0) { 264237651Sbschmidt execl(_PATH_CP, "mv", "-PRp", from, to, NULL); 265237651Sbschmidt warn("%s", _PATH_CP); 266237651Sbschmidt _exit(1); 267237651Sbschmidt } 268237651Sbschmidt if (waitpid(pid, &status, 0) == -1) { 269237651Sbschmidt warn("%s: waitpid", _PATH_CP); 270237651Sbschmidt return (1); 271237651Sbschmidt } 272237651Sbschmidt if (!WIFEXITED(status)) { 273237651Sbschmidt warn("%s: did not terminate normally", _PATH_CP); 274237651Sbschmidt return (1); 275237651Sbschmidt } 276237651Sbschmidt if (WEXITSTATUS(status)) { 277237651Sbschmidt warn("%s: terminated with %d (non-zero) status", 278237651Sbschmidt _PATH_CP, WEXITSTATUS(status)); 279237651Sbschmidt return (1); 280237651Sbschmidt } 281237651Sbschmidt if (!(pid = vfork())) { 282237651Sbschmidt execl(_PATH_RM, "mv", "-rf", from, NULL); 283237651Sbschmidt warn("%s", _PATH_RM); 284237651Sbschmidt _exit(1); 285237651Sbschmidt } 286237651Sbschmidt if (waitpid(pid, &status, 0) == -1) { 287237651Sbschmidt warn("%s: waitpid", _PATH_RM); 288237651Sbschmidt return (1); 289237651Sbschmidt } 290237651Sbschmidt if (!WIFEXITED(status)) { 291237651Sbschmidt warn("%s: did not terminate normally", _PATH_RM); 292237651Sbschmidt return (1); 293237651Sbschmidt } 294237651Sbschmidt if (WEXITSTATUS(status)) { 295237651Sbschmidt warn("%s: terminated with %d (non-zero) status", 296237651Sbschmidt _PATH_RM, WEXITSTATUS(status)); 297237651Sbschmidt return (1); 298237651Sbschmidt } 299237651Sbschmidt return (0); 300237651Sbschmidt} 301237651Sbschmidt 302237651Sbschmidtvoid 303237651Sbschmidtusage() 304237651Sbschmidt{ 305237651Sbschmidt (void)fprintf(stderr, 306237651Sbschmidt"usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n"); 307237651Sbschmidt exit(1); 308237651Sbschmidt} 309237651Sbschmidt