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.9 (Berkeley) 05/31/90"; 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 #include <sys/file.h> 26 #include <sys/errno.h> 27 #include <stdio.h> 28 #include <string.h> 29 #include "pathnames.h" 30 31 extern int errno; 32 int fflg, iflg; 33 34 main(argc, argv) 35 int argc; 36 char **argv; 37 { 38 extern char *optarg; 39 extern int optind; 40 register int baselen, exitval, len; 41 register char *p, *endp; 42 struct stat sbuf; 43 int ch; 44 char path[MAXPATHLEN + 1]; 45 46 while (((ch = getopt(argc, argv, "-if")) != EOF)) 47 switch((char)ch) { 48 case 'i': 49 ++iflg; 50 break; 51 case 'f': 52 ++fflg; 53 break; 54 case '-': /* undocumented; for compatibility */ 55 goto endarg; 56 case '?': 57 default: 58 usage(); 59 } 60 endarg: argc -= optind; 61 argv += optind; 62 63 if (argc < 2) 64 usage(); 65 66 /* 67 * if stat fails on target, it doesn't exist (or can't be accessed 68 * by the user, doesn't matter which) try the move. If target exists, 69 * and isn't a directory, try the move. More than 2 arguments is an 70 * error. 71 */ 72 if (stat(argv[argc - 1], &sbuf) || !S_ISDIR(sbuf.st_mode)) { 73 if (argc > 2) 74 usage(); 75 exit(do_move(argv[0], argv[1])); 76 } 77 78 /* got a directory, move each file into it */ 79 (void)strcpy(path, argv[argc - 1]); 80 baselen = strlen(path); 81 endp = &path[baselen]; 82 *endp++ = '/'; 83 ++baselen; 84 for (exitval = 0; --argc; ++argv) { 85 if ((p = rindex(*argv, '/')) == NULL) 86 p = *argv; 87 else 88 ++p; 89 if ((baselen + (len = strlen(p))) >= MAXPATHLEN) 90 (void)fprintf(stderr, 91 "mv: %s: destination pathname too long\n", *argv); 92 else { 93 bcopy(p, endp, len + 1); 94 exitval |= do_move(*argv, path); 95 } 96 } 97 exit(exitval); 98 } 99 100 do_move(from, to) 101 char *from, *to; 102 { 103 struct stat sbuf; 104 int ask, ch; 105 106 /* 107 * Check access. If interactive and file exists ask user if it 108 * should be replaced. Otherwise if file exists but isn't writable 109 * make sure the user wants to clobber it. 110 */ 111 if (!fflg && !access(to, F_OK)) { 112 ask = 0; 113 if (iflg) { 114 (void)fprintf(stderr, "overwrite %s? ", to); 115 ask = 1; 116 } 117 else if (access(to, W_OK) && !stat(to, &sbuf)) { 118 (void)fprintf(stderr, "override mode %o on %s? ", 119 sbuf.st_mode & 07777, to); 120 ask = 1; 121 } 122 if (ask) { 123 if ((ch = getchar()) != EOF && ch != '\n') 124 while (getchar() != '\n'); 125 if (ch != 'y') 126 return(0); 127 } 128 } 129 if (!rename(from, to)) 130 return(0); 131 if (errno != EXDEV) { 132 (void)fprintf(stderr, 133 "mv: rename %s to %s: %s\n", from, to, strerror(errno)); 134 return(1); 135 } 136 /* 137 * if rename fails, and it's a regular file, do the copy 138 * internally; otherwise, use cp and rm. 139 */ 140 if (stat(from, &sbuf)) { 141 (void)fprintf(stderr, 142 "mv: %s: %s\n", from, strerror(errno)); 143 return(1); 144 } 145 return(S_ISREG(sbuf.st_mode) ? 146 fastcopy(from, to, &sbuf) : copy(from, to)); 147 } 148 149 fastcopy(from, to, sbp) 150 char *from, *to; 151 struct stat *sbp; 152 { 153 struct timeval tval[2]; 154 static u_int blen; 155 static char *bp; 156 register int nread, from_fd, to_fd; 157 char *malloc(); 158 159 if ((from_fd = open(from, O_RDONLY, 0)) < 0) { 160 (void)fprintf(stderr, 161 "mv: %s: %s\n", from, strerror(errno)); 162 return(1); 163 } 164 if ((to_fd = open(to, O_WRONLY|O_CREAT|O_TRUNC, sbp->st_mode)) < 0) { 165 (void)fprintf(stderr, 166 "mv: %s: %s\n", to, strerror(errno)); 167 (void)close(from_fd); 168 return(1); 169 } 170 if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { 171 (void)fprintf(stderr, "mv: %s: out of memory.\n", from); 172 return(1); 173 } 174 while ((nread = read(from_fd, bp, blen)) > 0) 175 if (write(to_fd, bp, nread) != nread) { 176 (void)fprintf(stderr, "mv: %s: %s\n", 177 to, strerror(errno)); 178 goto err; 179 } 180 if (nread < 0) { 181 (void)fprintf(stderr, "mv: %s: %s\n", from, strerror(errno)); 182 err: (void)unlink(to); 183 (void)close(from_fd); 184 (void)close(to_fd); 185 return(1); 186 } 187 (void)fchown(to_fd, sbp->st_uid, sbp->st_gid); 188 (void)fchmod(to_fd, sbp->st_mode); 189 190 (void)close(from_fd); 191 (void)close(to_fd); 192 193 tval[0].tv_sec = sbp->st_atime; 194 tval[1].tv_sec = sbp->st_mtime; 195 tval[0].tv_usec = tval[1].tv_usec = 0; 196 (void)utimes(to, tval); 197 (void)unlink(from); 198 return(0); 199 } 200 201 copy(from, to) 202 char *from, *to; 203 { 204 int pid, status; 205 206 if (!(pid = vfork())) { 207 execlp(_PATH_CP, "mv", "-pr", from, to); 208 (void)fprintf(stderr, "mv: can't exec %s.\n", _PATH_CP); 209 _exit(1); 210 } 211 (void)waitpid(pid, &status, 0); 212 if (!WIFEXITED(status) || WEXITSTATUS(status)) 213 return(1); 214 if (!(pid = vfork())) { 215 execlp(_PATH_RM, "mv", "-rf", from); 216 (void)fprintf(stderr, "mv: can't exec %s.\n", _PATH_RM); 217 _exit(1); 218 } 219 (void)waitpid(pid, &status, 0); 220 return(!WIFEXITED(status) || WEXITSTATUS(status)); 221 } 222 223 usage() 224 { 225 (void)fprintf(stderr, 226 "usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n"); 227 exit(1); 228 } 229