mv.c revision 1.7
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.7 1993/11/09 18:58:03 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; 61int stdin_ok; 62 63main(argc, argv) 64 int argc; 65 char **argv; 66{ 67 extern char *optarg; 68 extern int optind; 69 register int baselen, exitval, len; 70 register char *p, *endp; 71 struct stat sb; 72 int ch; 73 char path[MAXPATHLEN + 1]; 74 75 while (((ch = getopt(argc, argv, "if")) != -1)) 76 switch((char)ch) { 77 case 'i': 78 fflg = 0; 79 iflg = 1; 80 break; 81 case 'f': 82 iflg = 0; 83 fflg = 1; 84 break; 85 case '?': 86 default: 87 usage(); 88 } 89 argc -= optind; 90 argv += optind; 91 92 if (argc < 2) 93 usage(); 94 95 stdin_ok = isatty(STDIN_FILENO); 96 97 /* 98 * If the stat on the target fails or the target isn't a directory, 99 * try the move. More than 2 arguments is an error in this case. 100 */ 101 if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { 102 if (argc > 2) 103 usage(); 104 exit(do_move(argv[0], argv[1])); 105 } 106 107 /* It's a directory, move each file into it. */ 108 (void)strcpy(path, argv[argc - 1]); 109 baselen = strlen(path); 110 endp = &path[baselen]; 111 *endp++ = '/'; 112 ++baselen; 113 for (exitval = 0; --argc; ++argv) { 114 if ((p = rindex(*argv, '/')) == NULL) 115 p = *argv; 116 else 117 ++p; 118 if ((baselen + (len = strlen(p))) >= MAXPATHLEN) 119 (void)fprintf(stderr, 120 "mv: %s: destination pathname too long\n", *argv); 121 else { 122 bcopy(p, endp, len + 1); 123 exitval |= do_move(*argv, path); 124 } 125 } 126 exit(exitval); 127} 128 129do_move(from, to) 130 char *from, *to; 131{ 132 struct stat sb; 133 134 /* (1) If the destination path exists, the -f option is not specified 135 * and either of the following conditions are true: 136 * 137 * (a) The perimissions of the destination path do not permit 138 * writing and the standard input is a terminal. 139 * (b) The -i option is specified. 140 * 141 * the mv utility shall write a prompt to standard error and 142 * read a line from standard input. If the response is not 143 * affirmative, mv shall do nothing more with the current 144 * source file... 145 */ 146 if (!fflg && !access(to, F_OK)) { 147 int ask = 0; 148 int ch; 149 150 if (iflg) { 151 (void)fprintf(stderr, "overwrite %s? ", to); 152 ask = 1; 153 } else if (stdin_ok && access(to, W_OK) && !stat(to, &sb)) { 154 (void)fprintf(stderr, "override mode %o on %s? ", 155 sb.st_mode & 07777, to); 156 ask = 1; 157 } 158 if (ask) { 159 if ((ch = getchar()) != EOF && ch != '\n') 160 while (getchar() != '\n'); 161 if (ch != 'y' && ch != 'Y') 162 return(0); 163 } 164 } 165 166 167 /* (2) If rename() succeeds, mv shall do nothing more with the 168 * current source file. If it fails for any other reason than 169 * EXDEV, mv shall write a diagnostic message to the standard 170 * error and do nothing more with the current source file. 171 * 172 * (3) If the destination path exists, and it is a file of type 173 * directory and source_file is not a file of type directory, 174 * or it is a file not of type directory, and source file is 175 * a file of type directory, mv shall write a diagnostic 176 * message to standard error, and do nothing more with the 177 * current source file... 178 */ 179 if (!rename(from, to)) 180 return(0); 181 182 if (errno != EXDEV) { 183 (void)fprintf(stderr, 184 "mv: rename %s to %s: %s\n", from, to, strerror(errno)); 185 return(1); 186 } 187 188 189 /* (4) If the destination path exists, mv shall attempt to remove it. 190 * If this fails for any reason, mv shall write a diagnostic 191 * message to the standard error and do nothing more with the 192 * current source file... 193 */ 194 if (!stat(to, &sb)) { 195 if ((S_ISDIR(sb.st_mode)) ? rmdir(to) : unlink(to)) { 196 (void) fprintf(stderr, 197 "mv: can't remove %s: %s\n", to, strerror(errno)); 198 return (1); 199 } 200 } 201 202 203 /* (5) The file hierarchy rooted in source_file shall be duplicated 204 * as a file hiearchy rooted in the destination path... 205 */ 206 if (stat(from, &sb)) { 207 (void)fprintf(stderr, "mv: %s: %s\n", from, strerror(errno)); 208 return(1); 209 } 210 return(S_ISREG(sb.st_mode) ? 211 fastcopy(from, to, &sb) : copy(from, to)); 212} 213 214fastcopy(from, to, sbp) 215 char *from, *to; 216 struct stat *sbp; 217{ 218 struct timeval tval[2]; 219 static u_int blen; 220 static char *bp; 221 register int nread, from_fd, to_fd; 222 223 if ((from_fd = open(from, O_RDONLY, 0)) < 0) { 224 error(from); 225 return(1); 226 } 227 if ((to_fd = open(to, O_CREAT|O_TRUNC|O_WRONLY, sbp->st_mode)) < 0) { 228 error(to); 229 (void)close(from_fd); 230 return(1); 231 } 232 if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { 233 error(NULL); 234 return(1); 235 } 236 while ((nread = read(from_fd, bp, blen)) > 0) 237 if (write(to_fd, bp, nread) != nread) { 238 error(to); 239 goto err; 240 } 241 if (nread < 0) { 242 error(from); 243err: (void)unlink(to); 244 (void)close(from_fd); 245 (void)close(to_fd); 246 return(1); 247 } 248 (void)fchown(to_fd, sbp->st_uid, sbp->st_gid); 249 (void)fchmod(to_fd, sbp->st_mode); 250 251 (void)close(from_fd); 252 (void)close(to_fd); 253 254 tval[0].tv_sec = sbp->st_atime; 255 tval[1].tv_sec = sbp->st_mtime; 256 tval[0].tv_usec = tval[1].tv_usec = 0; 257 (void)utimes(to, tval); 258 (void)unlink(from); 259 return(0); 260} 261 262copy(from, to) 263 char *from, *to; 264{ 265 int pid, status; 266 267 if (!(pid = vfork())) { 268 execl(_PATH_CP, "mv", "-pr", from, to, NULL); 269 error(_PATH_CP); 270 _exit(1); 271 } 272 (void)waitpid(pid, &status, 0); 273 if (!WIFEXITED(status) || WEXITSTATUS(status)) 274 return(1); 275 if (!(pid = vfork())) { 276 execl(_PATH_RM, "mv", "-rf", from, NULL); 277 error(_PATH_RM); 278 _exit(1); 279 } 280 (void)waitpid(pid, &status, 0); 281 return(!WIFEXITED(status) || WEXITSTATUS(status)); 282} 283 284error(s) 285 char *s; 286{ 287 if (s) 288 (void)fprintf(stderr, "mv: %s: %s\n", s, strerror(errno)); 289 else 290 (void)fprintf(stderr, "mv: %s\n", strerror(errno)); 291} 292 293usage() 294{ 295 (void)fprintf(stderr, 296 "usage: mv [-fi] source_file target_file\n" 297 " mv [-fi] source_file ... target_dir\n"); 298 exit(1); 299} 300