Commit | Line | Data |
---|---|---|
d53fe8dd | 1 | #ifndef lint |
98288a4a | 2 | static char *sccsid = "@(#)mv.c 4.13 (Berkeley) 83/06/30"; |
d53fe8dd | 3 | #endif |
2a7e674d | 4 | |
3880b4a9 BJ |
5 | /* |
6 | * mv file1 file2 | |
7 | */ | |
d53fe8dd SL |
8 | #include <sys/param.h> |
9 | #include <sys/stat.h> | |
3880b4a9 BJ |
10 | |
11 | #include <stdio.h> | |
98288a4a | 12 | #include <sys/dir.h> |
d53fe8dd | 13 | #include <errno.h> |
3880b4a9 BJ |
14 | #include <signal.h> |
15 | ||
3880b4a9 | 16 | #define DELIM '/' |
3880b4a9 | 17 | #define MODEBITS 07777 |
3880b4a9 | 18 | |
d53fe8dd SL |
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 | ||
3880b4a9 BJ |
25 | char *sprintf(); |
26 | char *dname(); | |
27 | struct stat s1, s2; | |
d53fe8dd SL |
28 | int iflag = 0; /* interactive mode */ |
29 | int fflag = 0; /* force overwriting */ | |
30 | extern unsigned errno; | |
3880b4a9 BJ |
31 | |
32 | main(argc, argv) | |
d53fe8dd | 33 | register char *argv[]; |
3880b4a9 BJ |
34 | { |
35 | register i, r; | |
ca02483b | 36 | register char *arg; |
29ba52aa | 37 | char *dest; |
3880b4a9 | 38 | |
3880b4a9 BJ |
39 | if (argc < 2) |
40 | goto usage; | |
d53fe8dd | 41 | while (argc > 1 && *argv[1] == '-') { |
3880b4a9 | 42 | argc--; |
ca02483b BJ |
43 | arg = *++argv; |
44 | ||
45 | /* | |
d53fe8dd SL |
46 | * all files following a null option |
47 | * are considered file names | |
ca02483b | 48 | */ |
d53fe8dd SL |
49 | if (*(arg+1) == '\0') |
50 | break; | |
51 | while (*++arg != '\0') switch (*arg) { | |
3880b4a9 | 52 | |
d53fe8dd SL |
53 | case 'i': |
54 | iflag++; | |
55 | break; | |
3880b4a9 | 56 | |
d53fe8dd SL |
57 | case 'f': |
58 | fflag++; | |
59 | break; | |
3880b4a9 | 60 | |
d53fe8dd SL |
61 | default: |
62 | goto usage; | |
63 | } | |
3880b4a9 BJ |
64 | } |
65 | if (argc < 3) | |
66 | goto usage; | |
29ba52aa | 67 | dest = argv[argc-1]; |
a7d0170a | 68 | if (stat(dest, &s2) >= 0 && ISDIR(s2)) { |
66a05c49 | 69 | r = 0; |
d53fe8dd SL |
70 | for (i = 1; i < argc-1; i++) |
71 | r |= movewithshortname(argv[i], dest); | |
72 | exit(r); | |
73 | } | |
29ba52aa KM |
74 | if (argc > 3) |
75 | goto usage; | |
76 | r = move(argv[1], argv[2]); | |
d53fe8dd SL |
77 | exit(r); |
78 | /*NOTREACHED*/ | |
3880b4a9 | 79 | usage: |
d53fe8dd | 80 | fprintf(stderr, |
a7d0170a | 81 | "usage: mv [-if] f1 f2 or mv [-if] f1 ... fn d1 (`fn' is a file or directory)\n"); |
d53fe8dd SL |
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)); | |
3880b4a9 BJ |
99 | } |
100 | ||
101 | move(source, target) | |
d53fe8dd | 102 | char *source, *target; |
3880b4a9 | 103 | { |
b07dfbb8 | 104 | int targetexists; |
3880b4a9 | 105 | |
2a7e674d | 106 | if (lstat(source, &s1) < 0) { |
d53fe8dd SL |
107 | error("cannot access %s", source); |
108 | return (1); | |
3880b4a9 | 109 | } |
d53fe8dd SL |
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 | */ | |
b07dfbb8 SL |
116 | targetexists = lstat(target, &s2) >= 0; |
117 | if (targetexists) { | |
d53fe8dd SL |
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); | |
3880b4a9 | 123 | } |
d53fe8dd SL |
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 | } | |
b07dfbb8 SL |
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); | |
3880b4a9 | 143 | } |
d53fe8dd SL |
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)) { | |
2a7e674d | 150 | register m; |
d53fe8dd | 151 | char symln[MAXPATHLEN]; |
2a7e674d KM |
152 | |
153 | if (readlink(source, symln, sizeof (symln)) < 0) { | |
d53fe8dd | 154 | Perror(source); |
2a7e674d KM |
155 | return (1); |
156 | } | |
157 | m = umask(~(s1.st_mode & MODEBITS)); | |
158 | if (symlink(symln, target) < 0) { | |
d53fe8dd | 159 | Perror(target); |
2a7e674d KM |
160 | return (1); |
161 | } | |
d53fe8dd SL |
162 | (void) umask(m); |
163 | goto cleanup; | |
164 | } | |
165 | if (ISDEV(s1)) { | |
e99dc6b8 SL |
166 | time_t tv[2]; |
167 | ||
d53fe8dd SL |
168 | if (mknod(target, s1.st_mode, s1.st_rdev) < 0) { |
169 | Perror(target); | |
170 | return (1); | |
171 | } | |
e99dc6b8 SL |
172 | /* kludge prior to utimes */ |
173 | tv[0] = s1.st_atime; | |
174 | tv[1] = s1.st_mtime; | |
175 | (void) utime(target, tv); | |
d53fe8dd SL |
176 | goto cleanup; |
177 | } | |
178 | if (ISREG(s1)) { | |
179 | int i, c, status; | |
e99dc6b8 | 180 | time_t tv[2]; |
d53fe8dd | 181 | |
3880b4a9 BJ |
182 | i = fork(); |
183 | if (i == -1) { | |
d53fe8dd SL |
184 | error("try again"); |
185 | return (1); | |
3880b4a9 BJ |
186 | } |
187 | if (i == 0) { | |
188 | execl("/bin/cp", "cp", source, target, 0); | |
d53fe8dd | 189 | error("cannot exec /bin/cp"); |
3880b4a9 BJ |
190 | exit(1); |
191 | } | |
192 | while ((c = wait(&status)) != i && c != -1) | |
193 | ; | |
194 | if (status != 0) | |
d53fe8dd | 195 | return (1); |
e99dc6b8 SL |
196 | /* kludge prior to utimes */ |
197 | tv[0] = s1.st_atime; | |
198 | tv[1] = s1.st_mtime; | |
199 | (void) utime(target, tv); | |
d53fe8dd | 200 | goto cleanup; |
3880b4a9 | 201 | } |
d53fe8dd SL |
202 | error("%s: unknown file type %o", source, s1.st_mode); |
203 | return (1); | |
3880b4a9 | 204 | |
d53fe8dd | 205 | cleanup: |
3880b4a9 | 206 | if (unlink(source) < 0) { |
d53fe8dd SL |
207 | error("cannot unlink %s", source); |
208 | return (1); | |
3880b4a9 | 209 | } |
d53fe8dd | 210 | return (0); |
3880b4a9 BJ |
211 | } |
212 | ||
d53fe8dd SL |
213 | /*VARARGS*/ |
214 | query(prompt, a1, a2) | |
215 | char *a1; | |
3880b4a9 | 216 | { |
d53fe8dd | 217 | register char i, c; |
3880b4a9 | 218 | |
d53fe8dd SL |
219 | fprintf(stderr, prompt, a1, a2); |
220 | i = c = getchar(); | |
221 | while (c != '\n' && c != EOF) | |
222 | c = getchar(); | |
223 | return (i == 'y'); | |
3880b4a9 BJ |
224 | } |
225 | ||
226 | char * | |
227 | dname(name) | |
d53fe8dd | 228 | register char *name; |
3880b4a9 BJ |
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 | ||
d53fe8dd SL |
239 | /*VARARGS*/ |
240 | error(fmt, a1, a2) | |
241 | char *fmt; | |
3880b4a9 | 242 | { |
3880b4a9 | 243 | |
d53fe8dd SL |
244 | fprintf(stderr, "mv: "); |
245 | fprintf(stderr, fmt, a1, a2); | |
246 | fprintf(stderr, "\n"); | |
247 | } | |
3880b4a9 | 248 | |
d53fe8dd SL |
249 | Perror(s) |
250 | char *s; | |
251 | { | |
252 | char buf[MAXPATHLEN + 10]; | |
253 | ||
254 | sprintf(buf, "mv: %s", s); | |
255 | perror(buf); | |
3880b4a9 BJ |
256 | } |
257 | ||
d53fe8dd SL |
258 | Perror2(s1, s2) |
259 | char *s1, *s2; | |
3880b4a9 | 260 | { |
d53fe8dd SL |
261 | char buf[MAXPATHLEN + 20]; |
262 | ||
263 | sprintf(buf, "mv: %s: %s", s1, s2); | |
264 | perror(buf); | |
3880b4a9 | 265 | } |