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 * %sccs.include.redist.c% 9 */ 10 11 #ifndef lint 12 char copyright[] = 13 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 14 All rights reserved.\n"; 15 #endif /* not lint */ 16 17 #ifndef lint 18 static char sccsid[] = "@(#)mv.c 5.13 (Berkeley) 04/29/93"; 19 #endif /* not lint */ 20 21 #include <sys/param.h> 22 #include <sys/time.h> 23 #include <sys/wait.h> 24 #include <sys/stat.h> 25 26 #include <err.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #include "pathnames.h" 35 36 int fflg, iflg; 37 38 int copy __P((char *, char *)); 39 int do_move __P((char *, char *)); 40 int fastcopy __P((char *, char *, struct stat *)); 41 void usage __P((void)); 42 43 int 44 main(argc, argv) 45 int argc; 46 char *argv[]; 47 { 48 register int baselen, exitval, len; 49 register char *p, *endp; 50 struct stat sb; 51 int ch; 52 char path[MAXPATHLEN + 1]; 53 54 while (((ch = getopt(argc, argv, "-if")) != EOF)) 55 switch((char)ch) { 56 case 'i': 57 iflg = 1; 58 break; 59 case 'f': 60 fflg = 1; 61 break; 62 case '-': /* Undocumented; for compatibility. */ 63 goto endarg; 64 case '?': 65 default: 66 usage(); 67 } 68 endarg: argc -= optind; 69 argv += optind; 70 71 if (argc < 2) 72 usage(); 73 74 /* 75 * If the stat on the target fails or the target isn't a directory, 76 * try the move. More than 2 arguments is an error in this case. 77 */ 78 if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { 79 if (argc > 2) 80 usage(); 81 exit(do_move(argv[0], argv[1])); 82 } 83 84 /* It's a directory, move each file into it. */ 85 (void)strcpy(path, argv[argc - 1]); 86 baselen = strlen(path); 87 endp = &path[baselen]; 88 *endp++ = '/'; 89 ++baselen; 90 for (exitval = 0; --argc; ++argv) { 91 if ((p = strrchr(*argv, '/')) == NULL) 92 p = *argv; 93 else 94 ++p; 95 if ((baselen + (len = strlen(p))) >= MAXPATHLEN) { 96 warnx("%s: destination pathname too long", *argv); 97 exitval = 1; 98 } else { 99 memmove(endp, p, len + 1); 100 exitval |= do_move(*argv, path); 101 } 102 } 103 exit(exitval); 104 } 105 106 int 107 do_move(from, to) 108 char *from, *to; 109 { 110 struct stat sb; 111 int ask, ch; 112 113 /* 114 * Check access. If interactive and file exists, ask user if it 115 * should be replaced. Otherwise if file exists but isn't writable 116 * make sure the user wants to clobber it. 117 */ 118 if (!fflg && !access(to, F_OK)) { 119 ask = 0; 120 if (iflg) { 121 (void)fprintf(stderr, "overwrite %s? ", to); 122 ask = 1; 123 } 124 else if (access(to, W_OK) && !stat(to, &sb)) { 125 (void)fprintf(stderr, "override mode %o on %s? ", 126 sb.st_mode & 07777, to); 127 ask = 1; 128 } 129 if (ask) { 130 if ((ch = getchar()) != EOF && ch != '\n') 131 while (getchar() != '\n'); 132 if (ch != 'y') 133 return (0); 134 } 135 } 136 if (!rename(from, to)) 137 return (0); 138 139 if (errno != EXDEV) { 140 warn("rename %s to %s", from, to); 141 return (1); 142 } 143 144 /* 145 * If rename fails, and it's a regular file, do the copy internally; 146 * otherwise, use cp and rm. 147 */ 148 if (stat(from, &sb)) { 149 warn("%s", from); 150 return (1); 151 } 152 return (S_ISREG(sb.st_mode) ? 153 fastcopy(from, to, &sb) : copy(from, to)); 154 } 155 156 int 157 fastcopy(from, to, sbp) 158 char *from, *to; 159 struct stat *sbp; 160 { 161 struct timeval tval[2]; 162 static u_int blen; 163 static char *bp; 164 register int nread, from_fd, to_fd; 165 166 if ((from_fd = open(from, O_RDONLY, 0)) < 0) { 167 warn("%s", from); 168 return (1); 169 } 170 if ((to_fd = open(to, O_CREAT|O_TRUNC|O_WRONLY, sbp->st_mode)) < 0) { 171 warn("%s", to); 172 (void)close(from_fd); 173 return (1); 174 } 175 if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { 176 warn(NULL); 177 return (1); 178 } 179 while ((nread = read(from_fd, bp, blen)) > 0) 180 if (write(to_fd, bp, nread) != nread) { 181 warn("%s", to); 182 goto err; 183 } 184 if (nread < 0) { 185 warn("%s", from); 186 err: if (unlink(to)) 187 warn("%s: remove", to); 188 (void)close(from_fd); 189 (void)close(to_fd); 190 return (1); 191 } 192 (void)close(from_fd); 193 194 if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) 195 warn("%s: set owner/group", to); 196 if (fchmod(to_fd, sbp->st_mode)) 197 warn("%s: set mode", to); 198 199 tval[0].tv_sec = sbp->st_atime; 200 tval[1].tv_sec = sbp->st_mtime; 201 tval[0].tv_usec = tval[1].tv_usec = 0; 202 if (utimes(to, tval)) 203 warn("%s: set times", to); 204 205 if (close(to_fd)) { 206 warn("%s", to); 207 return (1); 208 } 209 210 if (unlink(from)) { 211 warn("%s: remove", from); 212 return (1); 213 } 214 return (0); 215 } 216 217 int 218 copy(from, to) 219 char *from, *to; 220 { 221 int pid, status; 222 223 if (!(pid = vfork())) { 224 execl(_PATH_CP, "mv", "-pR", from, to, NULL); 225 warn("%s", _PATH_CP); 226 _exit(1); 227 } 228 if (waitpid(pid, &status, 0) == -1) { 229 warn("%s: waitpid", _PATH_CP); 230 return (1); 231 } 232 if (!WIFEXITED(status)) { 233 warn("%s: did not terminate normally", _PATH_CP); 234 return (1); 235 } 236 if (WEXITSTATUS(status)) { 237 warn("%s: terminated with %d (non-zero) status", 238 _PATH_CP, WEXITSTATUS(status)); 239 return (1); 240 } 241 if (!(pid = vfork())) { 242 execl(_PATH_RM, "mv", "-rf", from, NULL); 243 warn("%s", _PATH_RM); 244 _exit(1); 245 } 246 if (waitpid(pid, &status, 0) == -1) { 247 warn("%s: waitpid", _PATH_RM); 248 return (1); 249 } 250 if (!WIFEXITED(status)) { 251 warn("%s: did not terminate normally", _PATH_RM); 252 return (1); 253 } 254 if (WEXITSTATUS(status)) { 255 warn("%s: terminated with %d (non-zero) status", 256 _PATH_RM, WEXITSTATUS(status)); 257 return (1); 258 } 259 return (0); 260 } 261 262 void 263 usage() 264 { 265 (void)fprintf(stderr, 266 "usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n"); 267 exit(1); 268 } 269