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 are permitted 9 * provided that the above copyright notice and this paragraph are 10 * duplicated in all such forms and that any documentation, 11 * advertising materials, and other materials related to such 12 * distribution and use acknowledge that the software was developed 13 * by the University of California, Berkeley. The name of the 14 * University may not be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 19 */ 20 21 #ifndef lint 22 char copyright[] = 23 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 24 All rights reserved.\n"; 25 #endif /* not lint */ 26 27 #ifndef lint 28 static char sccsid[] = "@(#)mv.c 5.8 (Berkeley) 10/01/89"; 29 #endif /* not lint */ 30 31 #include <sys/param.h> 32 #include <sys/time.h> 33 #include <sys/wait.h> 34 #include <sys/stat.h> 35 #include <sys/file.h> 36 #include <sys/errno.h> 37 #include <stdio.h> 38 #include <string.h> 39 #include "pathnames.h" 40 41 extern int errno; 42 int fflg, iflg; 43 44 main(argc, argv) 45 int argc; 46 char **argv; 47 { 48 extern char *optarg; 49 extern int optind; 50 register int baselen, exitval, len; 51 register char *p, *endp; 52 struct stat sbuf; 53 int ch; 54 char path[MAXPATHLEN + 1]; 55 56 while (((ch = getopt(argc, argv, "-if")) != EOF)) 57 switch((char)ch) { 58 case 'i': 59 ++iflg; 60 break; 61 case 'f': 62 ++fflg; 63 break; 64 case '-': /* undocumented; for compatibility */ 65 goto endarg; 66 case '?': 67 default: 68 usage(); 69 } 70 endarg: argc -= optind; 71 argv += optind; 72 73 if (argc < 2) 74 usage(); 75 76 /* 77 * if stat fails on target, it doesn't exist (or can't be accessed 78 * by the user, doesn't matter which) try the move. If target exists, 79 * and isn't a directory, try the move. More than 2 arguments is an 80 * error. 81 */ 82 if (stat(argv[argc - 1], &sbuf) || !S_ISDIR(sbuf.st_mode)) { 83 if (argc > 2) 84 usage(); 85 exit(do_move(argv[0], argv[1])); 86 } 87 88 /* got a directory, move each file into it */ 89 (void)strcpy(path, argv[argc - 1]); 90 baselen = strlen(path); 91 endp = &path[baselen]; 92 *endp++ = '/'; 93 ++baselen; 94 for (exitval = 0; --argc; ++argv) { 95 if ((p = rindex(*argv, '/')) == NULL) 96 p = *argv; 97 else 98 ++p; 99 if ((baselen + (len = strlen(p))) >= MAXPATHLEN) 100 (void)fprintf(stderr, 101 "mv: %s: destination pathname too long\n", *argv); 102 else { 103 bcopy(p, endp, len + 1); 104 exitval |= do_move(*argv, path); 105 } 106 } 107 exit(exitval); 108 } 109 110 do_move(from, to) 111 char *from, *to; 112 { 113 struct stat sbuf; 114 int ask, ch; 115 116 /* 117 * Check access. If interactive and file exists ask user if it 118 * should be replaced. Otherwise if file exists but isn't writable 119 * make sure the user wants to clobber it. 120 */ 121 if (!fflg && !access(to, F_OK)) { 122 ask = 0; 123 if (iflg) { 124 (void)fprintf(stderr, "overwrite %s? ", to); 125 ask = 1; 126 } 127 else if (access(to, W_OK) && !stat(to, &sbuf)) { 128 (void)fprintf(stderr, "override mode %o on %s? ", 129 sbuf.st_mode & 07777, to); 130 ask = 1; 131 } 132 if (ask) { 133 if ((ch = getchar()) != EOF && ch != '\n') 134 while (getchar() != '\n'); 135 if (ch != 'y') 136 return(0); 137 } 138 } 139 if (!rename(from, to)) 140 return(0); 141 if (errno != EXDEV) { 142 (void)fprintf(stderr, 143 "mv: rename %s to %s: %s\n", from, to, strerror(errno)); 144 return(1); 145 } 146 /* 147 * if rename fails, and it's a regular file, do the copy 148 * internally; otherwise, use cp and rm. 149 */ 150 if (stat(from, &sbuf)) { 151 (void)fprintf(stderr, 152 "mv: %s: %s\n", from, strerror(errno)); 153 return(1); 154 } 155 return(S_ISREG(sbuf.st_mode) ? 156 fastcopy(from, to, &sbuf) : copy(from, to)); 157 } 158 159 fastcopy(from, to, sbp) 160 char *from, *to; 161 struct stat *sbp; 162 { 163 struct timeval tval[2]; 164 static u_int blen; 165 static char *bp; 166 register int nread, from_fd, to_fd; 167 char *malloc(); 168 169 if ((from_fd = open(from, O_RDONLY, 0)) < 0) { 170 (void)fprintf(stderr, 171 "mv: %s: %s\n", from, strerror(errno)); 172 return(1); 173 } 174 if ((to_fd = open(to, O_WRONLY|O_CREAT|O_TRUNC, sbp->st_mode)) < 0) { 175 (void)fprintf(stderr, 176 "mv: %s: %s\n", to, strerror(errno)); 177 (void)close(from_fd); 178 return(1); 179 } 180 if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { 181 (void)fprintf(stderr, "mv: %s: out of memory.\n", from); 182 return(1); 183 } 184 while ((nread = read(from_fd, bp, blen)) > 0) 185 if (write(to_fd, bp, nread) != nread) { 186 (void)fprintf(stderr, "mv: %s: %s\n", 187 to, strerror(errno)); 188 goto err; 189 } 190 if (nread < 0) { 191 (void)fprintf(stderr, "mv: %s: %s\n", from, strerror(errno)); 192 err: (void)unlink(to); 193 (void)close(from_fd); 194 (void)close(to_fd); 195 return(1); 196 } 197 (void)fchown(to_fd, sbp->st_uid, sbp->st_gid); 198 (void)fchmod(to_fd, sbp->st_mode); 199 200 (void)close(from_fd); 201 (void)close(to_fd); 202 203 tval[0].tv_sec = sbp->st_atime; 204 tval[1].tv_sec = sbp->st_mtime; 205 tval[0].tv_usec = tval[1].tv_usec = 0; 206 (void)utimes(to, tval); 207 (void)unlink(from); 208 return(0); 209 } 210 211 copy(from, to) 212 char *from, *to; 213 { 214 int pid, status; 215 216 if (!(pid = vfork())) { 217 execlp(_PATH_CP, "mv", "-pr", from, to); 218 (void)fprintf(stderr, "mv: can't exec %s.\n", _PATH_CP); 219 _exit(1); 220 } 221 (void)waitpid(pid, &status, 0); 222 if (!WIFEXITED(status) || WEXITSTATUS(status)) 223 return(1); 224 if (!(pid = vfork())) { 225 execlp(_PATH_RM, "mv", "-rf", from); 226 (void)fprintf(stderr, "mv: can't exec %s.\n", _PATH_RM); 227 _exit(1); 228 } 229 (void)waitpid(pid, &status, 0); 230 return(!WIFEXITED(status) || WEXITSTATUS(status)); 231 } 232 233 usage() 234 { 235 (void)fprintf(stderr, 236 "usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n"); 237 exit(1); 238 } 239