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