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.2 (Berkeley) 06/07/85"; 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 *sprintf(); 39 char *dname(); 40 struct stat s1, s2; 41 int iflag = 0; /* interactive mode */ 42 int fflag = 0; /* force overwriting */ 43 extern unsigned errno; 44 45 main(argc, argv) 46 register char *argv[]; 47 { 48 register i, r; 49 register char *arg; 50 char *dest; 51 52 if (argc < 2) 53 goto usage; 54 while (argc > 1 && *argv[1] == '-') { 55 argc--; 56 arg = *++argv; 57 58 /* 59 * all files following a null option 60 * are considered file names 61 */ 62 if (*(arg+1) == '\0') 63 break; 64 while (*++arg != '\0') switch (*arg) { 65 66 case 'i': 67 iflag++; 68 break; 69 70 case 'f': 71 fflag++; 72 break; 73 74 default: 75 goto usage; 76 } 77 } 78 if (argc < 3) 79 goto usage; 80 dest = argv[argc-1]; 81 if (stat(dest, &s2) >= 0 && ISDIR(s2)) { 82 r = 0; 83 for (i = 1; i < argc-1; i++) 84 r |= movewithshortname(argv[i], dest); 85 exit(r); 86 } 87 if (argc > 3) 88 goto usage; 89 r = move(argv[1], argv[2]); 90 exit(r); 91 /*NOTREACHED*/ 92 usage: 93 fprintf(stderr, 94 "usage: mv [-if] f1 f2 or mv [-if] f1 ... fn d1 (`fn' is a file or directory)\n"); 95 return (1); 96 } 97 98 movewithshortname(src, dest) 99 char *src, *dest; 100 { 101 register char *shortname; 102 char target[MAXPATHLEN + 1]; 103 104 shortname = dname(src); 105 if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) { 106 error("%s/%s: pathname too long", dest, 107 shortname); 108 return (1); 109 } 110 sprintf(target, "%s/%s", dest, shortname); 111 return (move(src, target)); 112 } 113 114 move(source, target) 115 char *source, *target; 116 { 117 int targetexists; 118 119 if (lstat(source, &s1) < 0) { 120 Perror2(source, "Cannot access"); 121 return (1); 122 } 123 /* 124 * First, try to rename source to destination. 125 * The only reason we continue on failure is if 126 * the move is on a nondirectory and not across 127 * file systems. 128 */ 129 targetexists = lstat(target, &s2) >= 0; 130 if (targetexists) { 131 if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) { 132 error("%s and %s are identical", source, target); 133 return (1); 134 } 135 if (iflag && !fflag && isatty(fileno(stdin)) && 136 query("remove %s? ", target) == 0) 137 return (1); 138 if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) { 139 if (query("override protection %o for %s? ", 140 s2.st_mode & MODEBITS, target) == 0) 141 return (1); 142 } 143 } 144 if (rename(source, target) >= 0) 145 return (0); 146 if (errno != EXDEV) { 147 Perror2(source, "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 m = umask(~(s1.st_mode & MODEBITS)); 175 if (symlink(symln, target) < 0) { 176 Perror(target); 177 return (1); 178 } 179 (void) umask(m); 180 goto cleanup; 181 } 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 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 sprintf(buf, "mv: %s: %s", s1, s2); 304 perror(buf); 305 } 306