date and time created 80/10/07 02:26:43 by bill
[unix-history] / usr / src / bin / mv / mv.c
CommitLineData
3880b4a9
BJ
1static char *sccsid = "@(#)mv.c 4.1 (Berkeley) %G%";
2/*
3 * mv file1 file2
4 */
5
6#include <stdio.h>
7#include <sys/types.h>
8#include <sys/stat.h>
9#include <sys/dir.h>
10#include <signal.h>
11
12#define DOT "."
13#define DOTDOT ".."
14#define DELIM '/'
15#define SDELIM "/"
16#define MAXN 100
17#define MODEBITS 07777
18#define ROOTINO 2
19
20char *pname();
21char *sprintf();
22char *dname();
23struct stat s1, s2;
24int iflag = 0; /* interactive flag. If this flag is set,
25 * the user is queried before files are
26 * destroyed by cp.
27 */
28int fflag = 0; /* force flag. supercedes all restrictions */
29
30main(argc, argv)
31register char *argv[];
32{
33 register i, r;
34
35 /* get the flag(s) */
36
37 if (argc < 2)
38 goto usage;
39 if (*argv[1] == '-') {
40 argc--;
41 while (*++argv[1] != '\0')
42 switch (*argv[1]) {
43
44 /* interactive mode */
45 case 'i':
46 iflag++;
47 break;
48
49 /* force moves */
50 case 'f':
51 fflag++;
52 break;
53
54 /* don't live with bad options */
55 default:
56 goto usage;
57 }
58 argv++;
59 }
60 if (argc < 3)
61 goto usage;
62 if (stat(argv[1], &s1) < 0) {
63 fprintf(stderr, "mv: cannot access %s\n", argv[1]);
64 return(1);
65 }
66 if ((s1.st_mode & S_IFMT) == S_IFDIR) {
67 if (argc != 3)
68 goto usage;
69 return mvdir(argv[1], argv[2]);
70 }
71 setuid(getuid());
72 if (argc > 3)
73 if (stat(argv[argc-1], &s2) < 0 || (s2.st_mode & S_IFMT) != S_IFDIR)
74 goto usage;
75 r = 0;
76 for (i=1; i<argc-1; i++)
77 r |= move(argv[i], argv[argc-1]);
78 return(r);
79usage:
80 fprintf(stderr, "usage: mv f1 f2; or mv d1 d2; or mv f1 ... fn d1\n");
81 return(1);
82}
83
84move(source, target)
85char *source, *target;
86{
87 register c, i;
88 int status;
89 char buf[MAXN];
90
91 if (stat(source, &s1) < 0) {
92 fprintf(stderr, "mv: cannot access %s\n", source);
93 return(1);
94 }
95 if ((s1.st_mode & S_IFMT) == S_IFDIR) {
96 fprintf(stderr, "mv: directory rename only\n");
97 return(1);
98 }
99 if (stat(target, &s2) >= 0) {
100 if ((s2.st_mode & S_IFMT) == S_IFDIR) {
101 sprintf(buf, "%s/%s", target, dname(source));
102 target = buf;
103 }
104 if (stat(target, &s2) >= 0) {
105 if ((s2.st_mode & S_IFMT) == S_IFDIR) {
106 fprintf(stderr, "mv: %s is a directory\n", target);
107 return(1);
108 } else if (iflag && !fflag) {
109 fprintf(stderr, "remove %s? ", target);
110 i = c = getchar();
111 while (c != '\n' && c != EOF)
112 c = getchar();
113 if (i != 'y')
114 return(1);
115 }
116 if (s1.st_dev==s2.st_dev && s1.st_ino==s2.st_ino) {
117 fprintf(stderr, "mv: %s and %s are identical\n",
118 source, target);
119 return(1);
120 }
121 if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) {
122 fprintf(stderr, "override protection %o for %s? ",
123 s2.st_mode & MODEBITS, target);
124 i = c = getchar();
125 while (c != '\n' && c != EOF)
126 c = getchar();
127 if (i != 'y')
128 return(1);
129 }
130 if (unlink(target) < 0) {
131 fprintf(stderr, "mv: cannot unlink %s\n", target);
132 return(1);
133 }
134 }
135 }
136 if (link(source, target) < 0) {
137 i = fork();
138 if (i == -1) {
139 fprintf(stderr, "mv: try again\n");
140 return(1);
141 }
142 if (i == 0) {
143 execl("/bin/cp", "cp", source, target, 0);
144 fprintf(stderr, "mv: cannot exec cp\n");
145 exit(1);
146 }
147 while ((c = wait(&status)) != i && c != -1)
148 ;
149 if (status != 0)
150 return(1);
151 utime(target, &s1.st_atime);
152 }
153 if (unlink(source) < 0) {
154 fprintf(stderr, "mv: cannot unlink %s\n", source);
155 return(1);
156 }
157 return(0);
158}
159
160mvdir(source, target)
161char *source, *target;
162{
163 register char *p;
164 register i;
165 char buf[MAXN];
166 char c,cc;
167
168 if (stat(target, &s2) >= 0) {
169 if ((s2.st_mode&S_IFMT) != S_IFDIR) {
170 fprintf(stderr, "mv: %s exists\n", target);
171 return(1);
172 } else if (iflag && !fflag) {
173 fprintf(stderr, "remove %s? ", target);
174 cc = c = getchar();
175 while (c != '\n' && c != EOF)
176 c = getchar();
177 if (cc != 'y')
178 return(1);
179 }
180 if (strlen(target) > MAXN-DIRSIZ-2) {
181 fprintf(stderr, "mv :target name too long\n");
182 return(1);
183 }
184 strcpy(buf, target);
185 target = buf;
186 strcat(buf, SDELIM);
187 strcat(buf, dname(source));
188 if (stat(target, &s2) >= 0) {
189 fprintf(stderr, "mv: %s exists\n", buf);
190 return(1);
191 }
192 }
193 if (strcmp(source, target) == 0) {
194 fprintf(stderr, "mv: ?? source == target, source exists and target doesnt\n");
195 return(1);
196 }
197 p = dname(source);
198 if (!strcmp(p, DOT) || !strcmp(p, DOTDOT) || !strcmp(p, "") || p[strlen(p)-1]=='/') {
199 fprintf(stderr, "mv: cannot rename %s\n", p);
200 return(1);
201 }
202 if (stat(pname(source), &s1) < 0 || stat(pname(target), &s2) < 0) {
203 fprintf(stderr, "mv: cannot locate parent\n");
204 return(1);
205 }
206 if (access(pname(target), 2) < 0) {
207 fprintf(stderr, "mv: no write access to %s\n", pname(target));
208 return(1);
209 }
210 if (access(pname(source), 2) < 0) {
211 fprintf(stderr, "mv: no write access to %s\n", pname(source));
212 return(1);
213 }
214 if (access(source, 2) < 0) {
215 fprintf(stderr, "mv: no write access to %s\n", source);
216 return(1);
217 }
218 if (s1.st_dev != s2.st_dev) {
219 fprintf(stderr, "mv: cannot move directories across devices\n");
220 return(1);
221 }
222 if (s1.st_ino != s2.st_ino) {
223 char dst[MAXN+5];
224
225 if (chkdot(source) || chkdot(target)) {
226 fprintf(stderr, "mv: Sorry, path names including %s aren't allowed\n", DOTDOT);
227 return(1);
228 }
229 stat(source, &s1);
230 if (check(pname(target), s1.st_ino))
231 return(1);
232 for (i = 1; i <= NSIG; i++)
233 signal(i, SIG_IGN);
234 if (link(source, target) < 0) {
235 fprintf(stderr, "mv: cannot link %s to %s\n", target, source);
236 return(1);
237 }
238 if (unlink(source) < 0) {
239 fprintf(stderr, "mv: %s: cannot unlink\n", source);
240 unlink(target);
241 return(1);
242 }
243 strcat(dst, target);
244 strcat(dst, "/");
245 strcat(dst, DOTDOT);
246 if (unlink(dst) < 0) {
247 fprintf(stderr, "mv: %s: cannot unlink\n", dst);
248 if (link(target, source) >= 0)
249 unlink(target);
250 return(1);
251 }
252 if (link(pname(target), dst) < 0) {
253 fprintf(stderr, "mv: cannot link %s to %s\n",
254 dst, pname(target));
255 if (link(pname(source), dst) >= 0)
256 if (link(target, source) >= 0)
257 unlink(target);
258 return(1);
259 }
260 return(0);
261 }
262 if (link(source, target) < 0) {
263 fprintf(stderr, "mv: cannot link %s and %s\n",
264 source, target);
265 return(1);
266 }
267 if (unlink(source) < 0) {
268 fprintf(stderr, "mv: ?? cannot unlink %s\n", source);
269 return(1);
270 }
271 return(0);
272}
273
274char *
275pname(name)
276register char *name;
277{
278 register c;
279 register char *p, *q;
280 static char buf[MAXN];
281
282 p = q = buf;
283 while (c = *p++ = *name++)
284 if (c == DELIM)
285 q = p-1;
286 if (q == buf && *q == DELIM)
287 q++;
288 *q = 0;
289 return buf[0]? buf : DOT;
290}
291
292char *
293dname(name)
294register char *name;
295{
296 register char *p;
297
298 p = name;
299 while (*p)
300 if (*p++ == DELIM && *p)
301 name = p;
302 return name;
303}
304
305check(spth, dinode)
306char *spth;
307ino_t dinode;
308{
309 char nspth[MAXN];
310 struct stat sbuf;
311
312 sbuf.st_ino = 0;
313
314 strcpy(nspth, spth);
315 while (sbuf.st_ino != ROOTINO) {
316 if (stat(nspth, &sbuf) < 0) {
317 fprintf(stderr, "mv: cannot access %s\n", nspth);
318 return(1);
319 }
320 if (sbuf.st_ino == dinode) {
321 fprintf(stderr, "mv: cannot move a directory into itself\n");
322 return(1);
323 }
324 if (strlen(nspth) > MAXN-2-sizeof(DOTDOT)) {
325 fprintf(stderr, "mv: name too long\n");
326 return(1);
327 }
328 strcat(nspth, SDELIM);
329 strcat(nspth, DOTDOT);
330 }
331 return(0);
332}
333
334chkdot(s)
335register char *s;
336{
337 do {
338 if (strcmp(dname(s), DOTDOT) == 0)
339 return(1);
340 s = pname(s);
341 } while (strcmp(s, DOT) != 0 && strcmp(s, SDELIM) != 0);
342 return(0);
343}