1 /* 2 * Copyright (c) 1980 Regents of the University of California. 3 * All rights reserved. The Berkeley software License Agreement 4 * specifies the terms and conditions for redistribution. 5 */ 6 7 #ifndef lint 8 char copyright[] = 9 "@(#) Copyright (c) 1980 Regents of the University of California.\n\ 10 All rights reserved.\n"; 11 #endif not lint 12 13 #ifndef lint 14 static char sccsid[] = "@(#)mv.c 5.5 (Berkeley) 10/22/87"; 15 #endif not lint 16 17 /* 18 * mv file1 file2 19 */ 20 #include <sys/param.h> 21 #include <sys/stat.h> 22 #include <sys/time.h> 23 24 #include <stdio.h> 25 #include <sys/dir.h> 26 #include <errno.h> 27 #include <signal.h> 28 29 #define DELIM '/' 30 #define MODEBITS 07777 31 32 #define ISDIR(st) (((st).st_mode&S_IFMT) == S_IFDIR) 33 #define ISLNK(st) (((st).st_mode&S_IFMT) == S_IFLNK) 34 #define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG) 35 #define ISDEV(st) \ 36 (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK) 37 38 char *dname(); 39 struct stat s1, s2; 40 int iflag = 0; /* interactive mode */ 41 int fflag = 0; /* force overwriting */ 42 extern unsigned errno; 43 44 main(argc, argv) 45 register char *argv[]; 46 { 47 register i, r; 48 register char *arg; 49 char *dest; 50 51 if (argc < 2) 52 goto usage; 53 while (argc > 1 && *argv[1] == '-') { 54 argc--; 55 arg = *++argv; 56 57 /* 58 * all files following a null option 59 * are considered file names 60 */ 61 if (*(arg+1) == '\0') 62 break; 63 while (*++arg != '\0') switch (*arg) { 64 65 case 'i': 66 iflag++; 67 break; 68 69 case 'f': 70 fflag++; 71 break; 72 73 default: 74 goto usage; 75 } 76 } 77 if (argc < 3) 78 goto usage; 79 dest = argv[argc-1]; 80 if (stat(dest, &s2) >= 0 && ISDIR(s2)) { 81 r = 0; 82 for (i = 1; i < argc-1; i++) 83 r |= movewithshortname(argv[i], dest); 84 exit(r); 85 } 86 if (argc > 3) 87 goto usage; 88 r = move(argv[1], argv[2]); 89 exit(r); 90 /*NOTREACHED*/ 91 usage: 92 fprintf(stderr, 93 "usage: mv [-if] f1 f2 or mv [-if] f1 ... fn d1 (`fn' is a file or directory)\n"); 94 return (1); 95 } 96 97 movewithshortname(src, dest) 98 char *src, *dest; 99 { 100 register char *shortname; 101 char target[MAXPATHLEN + 1]; 102 103 shortname = dname(src); 104 if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) { 105 error("%s/%s: pathname too long", dest, 106 shortname); 107 return (1); 108 } 109 (void)sprintf(target, "%s/%s", dest, shortname); 110 return (move(src, target)); 111 } 112 113 move(source, target) 114 char *source, *target; 115 { 116 int targetexists; 117 118 if (lstat(source, &s1) < 0) { 119 Perror2(source, "Cannot access"); 120 return (1); 121 } 122 /* 123 * First, try to rename source to destination. 124 * The only reason we continue on failure is if 125 * the move is on a nondirectory and not across 126 * file systems. 127 */ 128 targetexists = lstat(target, &s2) >= 0; 129 if (targetexists) { 130 if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) { 131 error("%s and %s are identical", source, target); 132 return (1); 133 } 134 if (iflag && !fflag && isatty(fileno(stdin)) && 135 query("remove %s? ", target) == 0) 136 return (1); 137 if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) { 138 if (query("override protection %o for %s? ", 139 s2.st_mode & MODEBITS, target) == 0) 140 return (1); 141 } 142 } 143 if (rename(source, target) >= 0) 144 return (0); 145 if (errno != EXDEV) { 146 Perror2(errno == ENOENT && targetexists == 0 ? target : source, 147 "rename"); 148 return (1); 149 } 150 if (ISDIR(s1)) { 151 error("can't mv directories across file systems"); 152 return (1); 153 } 154 if (targetexists && unlink(target) < 0) { 155 Perror2(target, "Cannot unlink"); 156 return (1); 157 } 158 /* 159 * File can't be renamed, try to recreate the symbolic 160 * link or special device, or copy the file wholesale 161 * between file systems. 162 */ 163 if (ISLNK(s1)) { 164 register m; 165 char symln[MAXPATHLEN + 1]; 166 167 m = readlink(source, symln, sizeof (symln) - 1); 168 if (m < 0) { 169 Perror(source); 170 return (1); 171 } 172 symln[m] = '\0'; 173 174 (void) umask(~(s1.st_mode & MODEBITS)); 175 if (symlink(symln, target) < 0) { 176 Perror(target); 177 return (1); 178 } 179 goto cleanup; 180 } 181 (void) umask(0); 182 if (ISDEV(s1)) { 183 struct timeval tv[2]; 184 185 if (mknod(target, s1.st_mode, s1.st_rdev) < 0) { 186 Perror(target); 187 return (1); 188 } 189 190 tv[0].tv_sec = s1.st_atime; 191 tv[0].tv_usec = 0; 192 tv[1].tv_sec = s1.st_mtime; 193 tv[1].tv_usec = 0; 194 (void) utimes(target, tv); 195 goto cleanup; 196 } 197 if (ISREG(s1)) { 198 register int fi, fo, n; 199 struct timeval tv[2]; 200 char buf[MAXBSIZE]; 201 202 fi = open(source, 0); 203 if (fi < 0) { 204 Perror(source); 205 return (1); 206 } 207 208 fo = creat(target, s1.st_mode & MODEBITS); 209 if (fo < 0) { 210 Perror(target); 211 close(fi); 212 return (1); 213 } 214 215 for (;;) { 216 n = read(fi, buf, sizeof buf); 217 if (n == 0) { 218 break; 219 } else if (n < 0) { 220 Perror2(source, "read"); 221 close(fi); 222 close(fo); 223 return (1); 224 } else if (write(fo, buf, n) != n) { 225 Perror2(target, "write"); 226 close(fi); 227 close(fo); 228 return (1); 229 } 230 } 231 232 close(fi); 233 close(fo); 234 235 tv[0].tv_sec = s1.st_atime; 236 tv[0].tv_usec = 0; 237 tv[1].tv_sec = s1.st_mtime; 238 tv[1].tv_usec = 0; 239 (void) utimes(target, tv); 240 goto cleanup; 241 } 242 error("%s: unknown file type %o", source, s1.st_mode); 243 return (1); 244 245 cleanup: 246 if (unlink(source) < 0) { 247 Perror2(source, "Cannot unlink"); 248 return (1); 249 } 250 return (0); 251 } 252 253 /*VARARGS*/ 254 query(prompt, a1, a2) 255 char *a1; 256 { 257 register int i, c; 258 259 fprintf(stderr, prompt, a1, a2); 260 i = c = getchar(); 261 while (c != '\n' && c != EOF) 262 c = getchar(); 263 return (i == 'y'); 264 } 265 266 char * 267 dname(name) 268 register char *name; 269 { 270 register char *p; 271 272 p = name; 273 while (*p) 274 if (*p++ == DELIM && *p) 275 name = p; 276 return name; 277 } 278 279 /*VARARGS*/ 280 error(fmt, a1, a2) 281 char *fmt; 282 { 283 284 fprintf(stderr, "mv: "); 285 fprintf(stderr, fmt, a1, a2); 286 fprintf(stderr, "\n"); 287 } 288 289 Perror(s) 290 char *s; 291 { 292 char buf[MAXPATHLEN + 10]; 293 294 (void)sprintf(buf, "mv: %s", s); 295 perror(buf); 296 } 297 298 Perror2(s1, s2) 299 char *s1, *s2; 300 { 301 char buf[MAXPATHLEN + 20]; 302 303 (void)sprintf(buf, "mv: %s: %s", s1, s2); 304 perror(buf); 305 } 306