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