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