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