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.12 (Berkeley) 11/23/92"; 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 <errno.h> 27 #include <fcntl.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 33 #include "pathnames.h" 34 35 int fflg, iflg; 36 37 int copy __P((char *, char *)); 38 int do_move __P((char *, char *)); 39 void err __P((const 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 = rindex(*argv, '/')) == NULL) 92 p = *argv; 93 else 94 ++p; 95 if ((baselen + (len = strlen(p))) >= MAXPATHLEN) { 96 err("%s: destination pathname too long", *argv); 97 exitval = 1; 98 } else { 99 bcopy(p, endp, 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 err("rename %s to %s: %s", from, to, strerror(errno)); 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 err("%s: %s", from, strerror(errno)); 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 err("%s: %s", from, strerror(errno)); 168 return (1); 169 } 170 if ((to_fd = open(to, O_CREAT|O_TRUNC|O_WRONLY, sbp->st_mode)) < 0) { 171 err("%s: %s", to, strerror(errno)); 172 (void)close(from_fd); 173 return (1); 174 } 175 if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { 176 err("%s", strerror(errno)); 177 return (1); 178 } 179 while ((nread = read(from_fd, bp, blen)) > 0) 180 if (write(to_fd, bp, nread) != nread) { 181 err("%s: %s", to, strerror(errno)); 182 goto err; 183 } 184 if (nread < 0) { 185 err("%s: %s", from, strerror(errno)); 186 err: (void)unlink(to); 187 (void)close(from_fd); 188 (void)close(to_fd); 189 return (1); 190 } 191 (void)fchown(to_fd, sbp->st_uid, sbp->st_gid); 192 (void)fchmod(to_fd, sbp->st_mode); 193 194 (void)close(from_fd); 195 (void)close(to_fd); 196 197 tval[0].tv_sec = sbp->st_atime; 198 tval[1].tv_sec = sbp->st_mtime; 199 tval[0].tv_usec = tval[1].tv_usec = 0; 200 (void)utimes(to, tval); 201 (void)unlink(from); 202 return (0); 203 } 204 205 int 206 copy(from, to) 207 char *from, *to; 208 { 209 int pid, status; 210 211 if (!(pid = vfork())) { 212 execl(_PATH_CP, "mv", "-pR", from, to, NULL); 213 err("%s: %s", _PATH_CP, strerror(errno)); 214 _exit(1); 215 } 216 (void)waitpid(pid, &status, 0); 217 if (!WIFEXITED(status) || WEXITSTATUS(status)) 218 return (1); 219 if (!(pid = vfork())) { 220 execl(_PATH_RM, "mv", "-rf", from, NULL); 221 err("%s: %s", _PATH_RM, strerror(errno)); 222 _exit(1); 223 } 224 (void)waitpid(pid, &status, 0); 225 return (!WIFEXITED(status) || WEXITSTATUS(status)); 226 } 227 228 void 229 usage() 230 { 231 (void)fprintf(stderr, 232 "usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n"); 233 exit(1); 234 } 235 236 #if __STDC__ 237 #include <stdarg.h> 238 #else 239 #include <varargs.h> 240 #endif 241 242 void 243 #if __STDC__ 244 err(const char *fmt, ...) 245 #else 246 err(fmt, va_alist) 247 char *fmt; 248 va_dcl 249 #endif 250 { 251 va_list ap; 252 #if __STDC__ 253 va_start(ap, fmt); 254 #else 255 va_start(ap); 256 #endif 257 (void)fprintf(stderr, "mv: "); 258 (void)vfprintf(stderr, fmt, ap); 259 va_end(ap); 260 (void)fprintf(stderr, "\n"); 261 } 262