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