Commit | Line | Data |
---|---|---|
d53fe8dd | 1 | #ifndef lint |
a7d0170a | 2 | static char *sccsid = "@(#)mv.c 4.11 (Berkeley) 83/01/31"; |
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> | |
d53fe8dd SL |
12 | #include <dir.h> |
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 | { |
3880b4a9 | 104 | |
2a7e674d | 105 | if (lstat(source, &s1) < 0) { |
d53fe8dd SL |
106 | error("cannot access %s", source); |
107 | return (1); | |
3880b4a9 | 108 | } |
d53fe8dd SL |
109 | /* |
110 | * First, try to rename source to destination. | |
111 | * The only reason we continue on failure is if | |
112 | * the move is on a nondirectory and not across | |
113 | * file systems. | |
114 | */ | |
115 | if (lstat(target, &s2) >= 0) { | |
116 | if (iflag && !fflag && query("remove %s? ", target) == 0) | |
117 | return (1); | |
118 | if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) { | |
119 | error("%s and %s are identical", source, target); | |
120 | return (1); | |
3880b4a9 | 121 | } |
d53fe8dd SL |
122 | if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) { |
123 | if (query("override protection %o for %s? ", | |
124 | s2.st_mode & MODEBITS, target) == 0) | |
125 | return (1); | |
126 | } | |
127 | if (rename(source, target) >= 0) | |
128 | return (0); | |
129 | if (errno != EXDEV) { | |
130 | Perror2(source, "rename"); | |
131 | return (1); | |
132 | } | |
133 | if (ISDIR(s1)) { | |
134 | error("can't mv directories across file systems"); | |
135 | return (1); | |
136 | } | |
137 | if (unlink(target) < 0) { | |
138 | error("cannot unlink %s", target); | |
139 | return (1); | |
140 | } | |
141 | } else { | |
142 | if (rename(source, target) >= 0) | |
143 | return (0); | |
144 | if (ISDIR(s1)) { | |
145 | Perror2(source, "rename"); | |
146 | return (1); | |
3880b4a9 BJ |
147 | } |
148 | } | |
d53fe8dd SL |
149 | /* |
150 | * File can't be renamed, try to recreate the symbolic | |
151 | * link or special device, or copy the file wholesale | |
152 | * between file systems. | |
153 | */ | |
154 | if (ISLNK(s1)) { | |
2a7e674d | 155 | register m; |
d53fe8dd | 156 | char symln[MAXPATHLEN]; |
2a7e674d KM |
157 | |
158 | if (readlink(source, symln, sizeof (symln)) < 0) { | |
d53fe8dd | 159 | Perror(source); |
2a7e674d KM |
160 | return (1); |
161 | } | |
162 | m = umask(~(s1.st_mode & MODEBITS)); | |
163 | if (symlink(symln, target) < 0) { | |
d53fe8dd | 164 | Perror(target); |
2a7e674d KM |
165 | return (1); |
166 | } | |
d53fe8dd SL |
167 | (void) umask(m); |
168 | goto cleanup; | |
169 | } | |
170 | if (ISDEV(s1)) { | |
e99dc6b8 SL |
171 | time_t tv[2]; |
172 | ||
d53fe8dd SL |
173 | if (mknod(target, s1.st_mode, s1.st_rdev) < 0) { |
174 | Perror(target); | |
175 | return (1); | |
176 | } | |
e99dc6b8 SL |
177 | /* kludge prior to utimes */ |
178 | tv[0] = s1.st_atime; | |
179 | tv[1] = s1.st_mtime; | |
180 | (void) utime(target, tv); | |
d53fe8dd SL |
181 | goto cleanup; |
182 | } | |
183 | if (ISREG(s1)) { | |
184 | int i, c, status; | |
e99dc6b8 | 185 | time_t tv[2]; |
d53fe8dd | 186 | |
3880b4a9 BJ |
187 | i = fork(); |
188 | if (i == -1) { | |
d53fe8dd SL |
189 | error("try again"); |
190 | return (1); | |
3880b4a9 BJ |
191 | } |
192 | if (i == 0) { | |
193 | execl("/bin/cp", "cp", source, target, 0); | |
d53fe8dd | 194 | error("cannot exec /bin/cp"); |
3880b4a9 BJ |
195 | exit(1); |
196 | } | |
197 | while ((c = wait(&status)) != i && c != -1) | |
198 | ; | |
199 | if (status != 0) | |
d53fe8dd | 200 | return (1); |
e99dc6b8 SL |
201 | /* kludge prior to utimes */ |
202 | tv[0] = s1.st_atime; | |
203 | tv[1] = s1.st_mtime; | |
204 | (void) utime(target, tv); | |
d53fe8dd | 205 | goto cleanup; |
3880b4a9 | 206 | } |
d53fe8dd SL |
207 | error("%s: unknown file type %o", source, s1.st_mode); |
208 | return (1); | |
3880b4a9 | 209 | |
d53fe8dd | 210 | cleanup: |
3880b4a9 | 211 | if (unlink(source) < 0) { |
d53fe8dd SL |
212 | error("cannot unlink %s", source); |
213 | return (1); | |
3880b4a9 | 214 | } |
d53fe8dd | 215 | return (0); |
3880b4a9 BJ |
216 | } |
217 | ||
d53fe8dd SL |
218 | /*VARARGS*/ |
219 | query(prompt, a1, a2) | |
220 | char *a1; | |
3880b4a9 | 221 | { |
d53fe8dd | 222 | register char i, c; |
3880b4a9 | 223 | |
d53fe8dd SL |
224 | fprintf(stderr, prompt, a1, a2); |
225 | i = c = getchar(); | |
226 | while (c != '\n' && c != EOF) | |
227 | c = getchar(); | |
228 | return (i == 'y'); | |
3880b4a9 BJ |
229 | } |
230 | ||
231 | char * | |
232 | dname(name) | |
d53fe8dd | 233 | register char *name; |
3880b4a9 BJ |
234 | { |
235 | register char *p; | |
236 | ||
237 | p = name; | |
238 | while (*p) | |
239 | if (*p++ == DELIM && *p) | |
240 | name = p; | |
241 | return name; | |
242 | } | |
243 | ||
d53fe8dd SL |
244 | /*VARARGS*/ |
245 | error(fmt, a1, a2) | |
246 | char *fmt; | |
3880b4a9 | 247 | { |
3880b4a9 | 248 | |
d53fe8dd SL |
249 | fprintf(stderr, "mv: "); |
250 | fprintf(stderr, fmt, a1, a2); | |
251 | fprintf(stderr, "\n"); | |
252 | } | |
3880b4a9 | 253 | |
d53fe8dd SL |
254 | Perror(s) |
255 | char *s; | |
256 | { | |
257 | char buf[MAXPATHLEN + 10]; | |
258 | ||
259 | sprintf(buf, "mv: %s", s); | |
260 | perror(buf); | |
3880b4a9 BJ |
261 | } |
262 | ||
d53fe8dd SL |
263 | Perror2(s1, s2) |
264 | char *s1, *s2; | |
3880b4a9 | 265 | { |
d53fe8dd SL |
266 | char buf[MAXPATHLEN + 20]; |
267 | ||
268 | sprintf(buf, "mv: %s: %s", s1, s2); | |
269 | perror(buf); | |
3880b4a9 | 270 | } |