date and time created 87/02/23 14:01:59 by karels
[unix-history] / usr / src / bin / mv / mv.c
CommitLineData
bcf1365c
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
8char copyright[] =
9"@(#) Copyright (c) 1980 Regents of the University of California.\n\
10 All rights reserved.\n";
11#endif not lint
12
d53fe8dd 13#ifndef lint
73d60db3 14static char sccsid[] = "@(#)mv.c 5.3 (Berkeley) %G%";
bcf1365c 15#endif not lint
2a7e674d 16
3880b4a9
BJ
17/*
18 * mv file1 file2
19 */
d53fe8dd
SL
20#include <sys/param.h>
21#include <sys/stat.h>
465786e2 22#include <sys/time.h>
3880b4a9
BJ
23
24#include <stdio.h>
98288a4a 25#include <sys/dir.h>
d53fe8dd 26#include <errno.h>
3880b4a9
BJ
27#include <signal.h>
28
3880b4a9 29#define DELIM '/'
3880b4a9 30#define MODEBITS 07777
3880b4a9 31
d53fe8dd
SL
32#define ISDIR(st) (((st).st_mode&S_IFMT) == S_IFDIR)
33#define ISLNK(st) (((st).st_mode&S_IFMT) == S_IFLNK)
34#define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG)
35#define ISDEV(st) \
36 (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK)
37
3880b4a9
BJ
38char *sprintf();
39char *dname();
40struct stat s1, s2;
d53fe8dd
SL
41int iflag = 0; /* interactive mode */
42int fflag = 0; /* force overwriting */
43extern unsigned errno;
3880b4a9
BJ
44
45main(argc, argv)
d53fe8dd 46 register char *argv[];
3880b4a9
BJ
47{
48 register i, r;
ca02483b 49 register char *arg;
29ba52aa 50 char *dest;
3880b4a9 51
3880b4a9
BJ
52 if (argc < 2)
53 goto usage;
d53fe8dd 54 while (argc > 1 && *argv[1] == '-') {
3880b4a9 55 argc--;
ca02483b
BJ
56 arg = *++argv;
57
58 /*
d53fe8dd
SL
59 * all files following a null option
60 * are considered file names
ca02483b 61 */
d53fe8dd
SL
62 if (*(arg+1) == '\0')
63 break;
64 while (*++arg != '\0') switch (*arg) {
3880b4a9 65
d53fe8dd
SL
66 case 'i':
67 iflag++;
68 break;
3880b4a9 69
d53fe8dd
SL
70 case 'f':
71 fflag++;
72 break;
3880b4a9 73
d53fe8dd
SL
74 default:
75 goto usage;
76 }
3880b4a9
BJ
77 }
78 if (argc < 3)
79 goto usage;
29ba52aa 80 dest = argv[argc-1];
a7d0170a 81 if (stat(dest, &s2) >= 0 && ISDIR(s2)) {
66a05c49 82 r = 0;
d53fe8dd
SL
83 for (i = 1; i < argc-1; i++)
84 r |= movewithshortname(argv[i], dest);
85 exit(r);
86 }
29ba52aa
KM
87 if (argc > 3)
88 goto usage;
89 r = move(argv[1], argv[2]);
d53fe8dd
SL
90 exit(r);
91 /*NOTREACHED*/
3880b4a9 92usage:
d53fe8dd 93 fprintf(stderr,
a7d0170a 94"usage: mv [-if] f1 f2 or mv [-if] f1 ... fn d1 (`fn' is a file or directory)\n");
d53fe8dd
SL
95 return (1);
96}
97
98movewithshortname(src, dest)
99 char *src, *dest;
100{
101 register char *shortname;
102 char target[MAXPATHLEN + 1];
103
104 shortname = dname(src);
105 if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) {
106 error("%s/%s: pathname too long", dest,
107 shortname);
108 return (1);
109 }
110 sprintf(target, "%s/%s", dest, shortname);
111 return (move(src, target));
3880b4a9
BJ
112}
113
114move(source, target)
d53fe8dd 115 char *source, *target;
3880b4a9 116{
b07dfbb8 117 int targetexists;
3880b4a9 118
2a7e674d 119 if (lstat(source, &s1) < 0) {
465786e2 120 Perror2(source, "Cannot access");
d53fe8dd 121 return (1);
3880b4a9 122 }
d53fe8dd
SL
123 /*
124 * First, try to rename source to destination.
125 * The only reason we continue on failure is if
126 * the move is on a nondirectory and not across
127 * file systems.
128 */
b07dfbb8
SL
129 targetexists = lstat(target, &s2) >= 0;
130 if (targetexists) {
d53fe8dd
SL
131 if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) {
132 error("%s and %s are identical", source, target);
133 return (1);
3880b4a9 134 }
465786e2
S
135 if (iflag && !fflag && isatty(fileno(stdin)) &&
136 query("remove %s? ", target) == 0)
137 return (1);
d53fe8dd
SL
138 if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) {
139 if (query("override protection %o for %s? ",
140 s2.st_mode & MODEBITS, target) == 0)
141 return (1);
142 }
b07dfbb8
SL
143 }
144 if (rename(source, target) >= 0)
145 return (0);
146 if (errno != EXDEV) {
73d60db3
JL
147 Perror2(errno == ENOENT && targetexists == 0 ? target : source,
148 "rename");
b07dfbb8
SL
149 return (1);
150 }
151 if (ISDIR(s1)) {
152 error("can't mv directories across file systems");
153 return (1);
154 }
155 if (targetexists && unlink(target) < 0) {
465786e2 156 Perror2(target, "Cannot unlink");
b07dfbb8 157 return (1);
3880b4a9 158 }
d53fe8dd
SL
159 /*
160 * File can't be renamed, try to recreate the symbolic
161 * link or special device, or copy the file wholesale
162 * between file systems.
163 */
164 if (ISLNK(s1)) {
2a7e674d 165 register m;
465786e2 166 char symln[MAXPATHLEN + 1];
2a7e674d 167
465786e2
S
168 m = readlink(source, symln, sizeof (symln) - 1);
169 if (m < 0) {
d53fe8dd 170 Perror(source);
2a7e674d
KM
171 return (1);
172 }
465786e2
S
173 symln[m] = '\0';
174
2a7e674d
KM
175 m = umask(~(s1.st_mode & MODEBITS));
176 if (symlink(symln, target) < 0) {
d53fe8dd 177 Perror(target);
2a7e674d
KM
178 return (1);
179 }
d53fe8dd
SL
180 (void) umask(m);
181 goto cleanup;
182 }
183 if (ISDEV(s1)) {
465786e2 184 struct timeval tv[2];
e99dc6b8 185
d53fe8dd
SL
186 if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
187 Perror(target);
188 return (1);
189 }
465786e2
S
190
191 tv[0].tv_sec = s1.st_atime;
192 tv[0].tv_usec = 0;
193 tv[1].tv_sec = s1.st_mtime;
194 tv[1].tv_usec = 0;
195 (void) utimes(target, tv);
d53fe8dd
SL
196 goto cleanup;
197 }
198 if (ISREG(s1)) {
465786e2
S
199 register int fi, fo, n;
200 struct timeval tv[2];
201 char buf[MAXBSIZE];
d53fe8dd 202
465786e2
S
203 fi = open(source, 0);
204 if (fi < 0) {
205 Perror(source);
d53fe8dd 206 return (1);
3880b4a9 207 }
465786e2
S
208
209 fo = creat(target, s1.st_mode & MODEBITS);
210 if (fo < 0) {
211 Perror(target);
212 close(fi);
d53fe8dd 213 return (1);
465786e2
S
214 }
215
216 for (;;) {
217 n = read(fi, buf, sizeof buf);
218 if (n == 0) {
219 break;
220 } else if (n < 0) {
221 Perror2(source, "read");
222 close(fi);
223 close(fo);
224 return (1);
225 } else if (write(fo, buf, n) != n) {
226 Perror2(target, "write");
227 close(fi);
228 close(fo);
229 return (1);
230 }
231 }
232
233 close(fi);
234 close(fo);
235
236 tv[0].tv_sec = s1.st_atime;
237 tv[0].tv_usec = 0;
238 tv[1].tv_sec = s1.st_mtime;
239 tv[1].tv_usec = 0;
240 (void) utimes(target, tv);
d53fe8dd 241 goto cleanup;
3880b4a9 242 }
d53fe8dd
SL
243 error("%s: unknown file type %o", source, s1.st_mode);
244 return (1);
3880b4a9 245
d53fe8dd 246cleanup:
3880b4a9 247 if (unlink(source) < 0) {
465786e2 248 Perror2(source, "Cannot unlink");
d53fe8dd 249 return (1);
3880b4a9 250 }
d53fe8dd 251 return (0);
3880b4a9
BJ
252}
253
d53fe8dd
SL
254/*VARARGS*/
255query(prompt, a1, a2)
256 char *a1;
3880b4a9 257{
465786e2 258 register int i, c;
3880b4a9 259
d53fe8dd
SL
260 fprintf(stderr, prompt, a1, a2);
261 i = c = getchar();
262 while (c != '\n' && c != EOF)
263 c = getchar();
264 return (i == 'y');
3880b4a9
BJ
265}
266
267char *
268dname(name)
d53fe8dd 269 register char *name;
3880b4a9
BJ
270{
271 register char *p;
272
273 p = name;
274 while (*p)
275 if (*p++ == DELIM && *p)
276 name = p;
277 return name;
278}
279
d53fe8dd
SL
280/*VARARGS*/
281error(fmt, a1, a2)
282 char *fmt;
3880b4a9 283{
3880b4a9 284
d53fe8dd
SL
285 fprintf(stderr, "mv: ");
286 fprintf(stderr, fmt, a1, a2);
287 fprintf(stderr, "\n");
288}
3880b4a9 289
d53fe8dd
SL
290Perror(s)
291 char *s;
292{
293 char buf[MAXPATHLEN + 10];
294
295 sprintf(buf, "mv: %s", s);
296 perror(buf);
3880b4a9
BJ
297}
298
d53fe8dd
SL
299Perror2(s1, s2)
300 char *s1, *s2;
3880b4a9 301{
d53fe8dd
SL
302 char buf[MAXPATHLEN + 20];
303
304 sprintf(buf, "mv: %s: %s", s1, s2);
305 perror(buf);
3880b4a9 306}