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