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