mv.c revision 1.5
1/* 2 * Copyright (c) 1989 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Ken Smith of The State University of New York at Buffalo. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#ifndef lint 38char copyright[] = 39"@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 40 All rights reserved.\n"; 41#endif /* not lint */ 42 43#ifndef lint 44/*static char sccsid[] = "from: @(#)mv.c 5.11 (Berkeley) 4/3/91";*/ 45static char rcsid[] = "$Id: mv.c,v 1.5 1993/09/22 00:34:27 jtc Exp $"; 46#endif /* not lint */ 47 48#include <sys/param.h> 49#include <sys/time.h> 50#include <sys/wait.h> 51#include <sys/stat.h> 52#include <fcntl.h> 53#include <errno.h> 54#include <unistd.h> 55#include <stdio.h> 56#include <stdlib.h> 57#include <string.h> 58#include "pathnames.h" 59 60int fflg, iflg; 61 62main(argc, argv) 63 int argc; 64 char **argv; 65{ 66 extern char *optarg; 67 extern int optind; 68 register int baselen, exitval, len; 69 register char *p, *endp; 70 struct stat sb; 71 int ch; 72 char path[MAXPATHLEN + 1]; 73 74 while (((ch = getopt(argc, argv, "if")) != -1)) 75 switch((char)ch) { 76 case 'i': 77 fflg = 0; 78 iflg = 1; 79 break; 80 case 'f': 81 iflg = 0; 82 fflg = 1; 83 break; 84 case '?': 85 default: 86 usage(); 87 } 88 argc -= optind; 89 argv += optind; 90 91 if (argc < 2) 92 usage(); 93 94 /* 95 * If the stat on the target fails or the target isn't a directory, 96 * try the move. More than 2 arguments is an error in this case. 97 */ 98 if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { 99 if (argc > 2) 100 usage(); 101 exit(do_move(argv[0], argv[1])); 102 } 103 104 /* It's a directory, move each file into it. */ 105 (void)strcpy(path, argv[argc - 1]); 106 baselen = strlen(path); 107 endp = &path[baselen]; 108 *endp++ = '/'; 109 ++baselen; 110 for (exitval = 0; --argc; ++argv) { 111 if ((p = rindex(*argv, '/')) == NULL) 112 p = *argv; 113 else 114 ++p; 115 if ((baselen + (len = strlen(p))) >= MAXPATHLEN) 116 (void)fprintf(stderr, 117 "mv: %s: destination pathname too long\n", *argv); 118 else { 119 bcopy(p, endp, len + 1); 120 exitval |= do_move(*argv, path); 121 } 122 } 123 exit(exitval); 124} 125 126do_move(from, to) 127 char *from, *to; 128{ 129 struct stat sb; 130 int ask, ch; 131 132 /* 133 * Check access. If interactive and file exists, ask user if it 134 * should be replaced. Otherwise if file exists but isn't writable 135 * make sure the user wants to clobber it. 136 */ 137 if (!fflg && !access(to, F_OK)) { 138 ask = 0; 139 if (iflg) { 140 (void)fprintf(stderr, "overwrite %s? ", to); 141 ask = 1; 142 } 143 else if (access(to, W_OK) && !stat(to, &sb)) { 144 (void)fprintf(stderr, "override mode %o on %s? ", 145 sb.st_mode & 07777, to); 146 ask = 1; 147 } 148 if (ask) { 149 if ((ch = getchar()) != EOF && ch != '\n') 150 while (getchar() != '\n'); 151 if (ch != 'y') 152 return(0); 153 } 154 } 155 if (!rename(from, to)) 156 return(0); 157 158 if (errno != EXDEV) { 159 (void)fprintf(stderr, 160 "mv: rename %s to %s: %s\n", from, to, strerror(errno)); 161 return(1); 162 } 163 164 /* 165 * If rename fails, and it's a regular file, do the copy internally; 166 * otherwise, use cp and rm. 167 */ 168 if (stat(from, &sb)) { 169 (void)fprintf(stderr, "mv: %s: %s\n", from, strerror(errno)); 170 return(1); 171 } 172 return(S_ISREG(sb.st_mode) ? 173 fastcopy(from, to, &sb) : copy(from, to)); 174} 175 176fastcopy(from, to, sbp) 177 char *from, *to; 178 struct stat *sbp; 179{ 180 struct timeval tval[2]; 181 static u_int blen; 182 static char *bp; 183 register int nread, from_fd, to_fd; 184 185 if ((from_fd = open(from, O_RDONLY, 0)) < 0) { 186 error(from); 187 return(1); 188 } 189 if ((to_fd = open(to, O_CREAT|O_TRUNC|O_WRONLY, sbp->st_mode)) < 0) { 190 error(to); 191 (void)close(from_fd); 192 return(1); 193 } 194 if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { 195 error(NULL); 196 return(1); 197 } 198 while ((nread = read(from_fd, bp, blen)) > 0) 199 if (write(to_fd, bp, nread) != nread) { 200 error(to); 201 goto err; 202 } 203 if (nread < 0) { 204 error(from); 205err: (void)unlink(to); 206 (void)close(from_fd); 207 (void)close(to_fd); 208 return(1); 209 } 210 (void)fchown(to_fd, sbp->st_uid, sbp->st_gid); 211 (void)fchmod(to_fd, sbp->st_mode); 212 213 (void)close(from_fd); 214 (void)close(to_fd); 215 216 tval[0].tv_sec = sbp->st_atime; 217 tval[1].tv_sec = sbp->st_mtime; 218 tval[0].tv_usec = tval[1].tv_usec = 0; 219 (void)utimes(to, tval); 220 (void)unlink(from); 221 return(0); 222} 223 224copy(from, to) 225 char *from, *to; 226{ 227 int pid, status; 228 229 if (!(pid = vfork())) { 230 execl(_PATH_CP, "mv", "-pr", from, to, NULL); 231 error(_PATH_CP); 232 _exit(1); 233 } 234 (void)waitpid(pid, &status, 0); 235 if (!WIFEXITED(status) || WEXITSTATUS(status)) 236 return(1); 237 if (!(pid = vfork())) { 238 execl(_PATH_RM, "mv", "-rf", from, NULL); 239 error(_PATH_RM); 240 _exit(1); 241 } 242 (void)waitpid(pid, &status, 0); 243 return(!WIFEXITED(status) || WEXITSTATUS(status)); 244} 245 246error(s) 247 char *s; 248{ 249 if (s) 250 (void)fprintf(stderr, "mv: %s: %s\n", s, strerror(errno)); 251 else 252 (void)fprintf(stderr, "mv: %s\n", strerror(errno)); 253} 254 255usage() 256{ 257 (void)fprintf(stderr, 258 "usage: mv [-fi] source_file target_file\n" 259 " mv [-fi] source_file ... target_dir\n"); 260 exit(1); 261} 262