display the file modes, not just the octal value on overwrite
[unix-history] / usr / src / bin / mv / mv.c
CommitLineData
bcf1365c 1/*
dfb144fc 2 * Copyright (c) 1989, 1993, 1994
59d11f9a 3 * The Regents of the University of California. All rights reserved.
50da9e1a
KB
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Ken Smith of The State University of New York at Buffalo.
7 *
27c71911 8 * %sccs.include.redist.c%
bcf1365c
DF
9 */
10
11#ifndef lint
59d11f9a 12static char copyright[] =
dfb144fc 13"@(#) Copyright (c) 1989, 1993, 1994\n\
59d11f9a 14 The Regents of the University of California. All rights reserved.\n";
8161a03a 15#endif /* not lint */
bcf1365c 16
d53fe8dd 17#ifndef lint
dfb144fc 18static char sccsid[] = "@(#)mv.c 8.2 (Berkeley) %G%";
8161a03a 19#endif /* not lint */
2a7e674d 20
d53fe8dd 21#include <sys/param.h>
465786e2 22#include <sys/time.h>
50da9e1a
KB
23#include <sys/wait.h>
24#include <sys/stat.h>
e96ee99d 25
2041d53d 26#include <err.h>
52b78a7d 27#include <errno.h>
e96ee99d 28#include <fcntl.h>
3880b4a9 29#include <stdio.h>
52b78a7d 30#include <stdlib.h>
50da9e1a 31#include <string.h>
e96ee99d
KB
32#include <unistd.h>
33
50da9e1a 34#include "pathnames.h"
3880b4a9 35
50da9e1a 36int fflg, iflg;
3880b4a9 37
e96ee99d
KB
38int copy __P((char *, char *));
39int do_move __P((char *, char *));
e96ee99d
KB
40int fastcopy __P((char *, char *, struct stat *));
41void usage __P((void));
42
43int
3880b4a9 44main(argc, argv)
50da9e1a 45 int argc;
e96ee99d 46 char *argv[];
3880b4a9 47{
dfb144fc 48 register int baselen, len, rval;
50da9e1a 49 register char *p, *endp;
52b78a7d 50 struct stat sb;
50da9e1a
KB
51 int ch;
52 char path[MAXPATHLEN + 1];
3880b4a9 53
dfb144fc
KB
54 while ((ch = getopt(argc, argv, "-if")) != EOF)
55 switch (ch) {
d53fe8dd 56 case 'i':
52b78a7d 57 iflg = 1;
50da9e1a
KB
58 break;
59 case 'f':
52b78a7d 60 fflg = 1;
d53fe8dd 61 break;
e96ee99d 62 case '-': /* Undocumented; for compatibility. */
50da9e1a 63 goto endarg;
8161a03a 64 case '?':
d53fe8dd 65 default:
8161a03a 66 usage();
d53fe8dd 67 }
50da9e1a
KB
68endarg: argc -= optind;
69 argv += optind;
8161a03a
KB
70
71 if (argc < 2)
72 usage();
d53fe8dd 73
50da9e1a 74 /*
52b78a7d
KB
75 * If the stat on the target fails or the target isn't a directory,
76 * try the move. More than 2 arguments is an error in this case.
50da9e1a 77 */
52b78a7d 78 if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) {
50da9e1a
KB
79 if (argc > 2)
80 usage();
81 exit(do_move(argv[0], argv[1]));
82 }
d53fe8dd 83
52b78a7d 84 /* It's a directory, move each file into it. */
50da9e1a
KB
85 (void)strcpy(path, argv[argc - 1]);
86 baselen = strlen(path);
87 endp = &path[baselen];
88 *endp++ = '/';
89 ++baselen;
dfb144fc 90 for (rval = 0; --argc; ++argv) {
2041d53d 91 if ((p = strrchr(*argv, '/')) == NULL)
50da9e1a
KB
92 p = *argv;
93 else
94 ++p;
e96ee99d 95 if ((baselen + (len = strlen(p))) >= MAXPATHLEN) {
2041d53d 96 warnx("%s: destination pathname too long", *argv);
dfb144fc 97 rval = 1;
e96ee99d 98 } else {
2041d53d 99 memmove(endp, p, len + 1);
dfb144fc
KB
100 if (do_move(*argv, path))
101 rval = 1;
50da9e1a 102 }
d53fe8dd 103 }
dfb144fc 104 exit(rval);
3880b4a9
BJ
105}
106
e96ee99d 107int
50da9e1a
KB
108do_move(from, to)
109 char *from, *to;
3880b4a9 110{
52b78a7d 111 struct stat sb;
50da9e1a 112 int ask, ch;
dfb144fc 113 char modep[15];
3880b4a9 114
d53fe8dd 115 /*
52b78a7d 116 * Check access. If interactive and file exists, ask user if it
50da9e1a
KB
117 * should be replaced. Otherwise if file exists but isn't writable
118 * make sure the user wants to clobber it.
d53fe8dd 119 */
50da9e1a
KB
120 if (!fflg && !access(to, F_OK)) {
121 ask = 0;
122 if (iflg) {
123 (void)fprintf(stderr, "overwrite %s? ", to);
124 ask = 1;
dfb144fc
KB
125 } else if (access(to, W_OK) && !stat(to, &sb)) {
126 strmode(sb.st_mode, modep);
127 (void)fprintf(stderr, "override %s%s%s/%s for %s? ",
128 modep + 1, modep[9] == ' ' ? "" : " ",
129 user_from_uid(sb.st_uid, 0),
130 group_from_gid(sb.st_gid, 0), to);
50da9e1a
KB
131 ask = 1;
132 }
133 if (ask) {
134 if ((ch = getchar()) != EOF && ch != '\n')
135 while (getchar() != '\n');
136 if (ch != 'y')
e96ee99d 137 return (0);
3880b4a9 138 }
b07dfbb8 139 }
50da9e1a 140 if (!rename(from, to))
e96ee99d 141 return (0);
52b78a7d 142
b07dfbb8 143 if (errno != EXDEV) {
2041d53d 144 warn("rename %s to %s", from, to);
e96ee99d 145 return (1);
3880b4a9 146 }
52b78a7d 147
d53fe8dd 148 /*
dfb144fc
KB
149 * If rename fails because we're trying to cross devices, and
150 * it's a regular file, do the copy internally; otherwise, use
151 * cp and rm.
d53fe8dd 152 */
52b78a7d 153 if (stat(from, &sb)) {
2041d53d 154 warn("%s", from);
e96ee99d 155 return (1);
d53fe8dd 156 }
e96ee99d 157 return (S_ISREG(sb.st_mode) ?
52b78a7d 158 fastcopy(from, to, &sb) : copy(from, to));
50da9e1a 159}
465786e2 160
e96ee99d 161int
50da9e1a
KB
162fastcopy(from, to, sbp)
163 char *from, *to;
164 struct stat *sbp;
165{
166 struct timeval tval[2];
167 static u_int blen;
168 static char *bp;
169 register int nread, from_fd, to_fd;
50da9e1a
KB
170
171 if ((from_fd = open(from, O_RDONLY, 0)) < 0) {
2041d53d 172 warn("%s", from);
e96ee99d 173 return (1);
d53fe8dd 174 }
dfb144fc
KB
175 if ((to_fd =
176 open(to, O_CREAT | O_TRUNC | O_WRONLY, sbp->st_mode)) < 0) {
2041d53d 177 warn("%s", to);
50da9e1a 178 (void)close(from_fd);
e96ee99d 179 return (1);
3880b4a9 180 }
50da9e1a 181 if (!blen && !(bp = malloc(blen = sbp->st_blksize))) {
2041d53d 182 warn(NULL);
e96ee99d 183 return (1);
3880b4a9 184 }
50da9e1a
KB
185 while ((nread = read(from_fd, bp, blen)) > 0)
186 if (write(to_fd, bp, nread) != nread) {
2041d53d 187 warn("%s", to);
50da9e1a
KB
188 goto err;
189 }
190 if (nread < 0) {
2041d53d
KB
191 warn("%s", from);
192err: if (unlink(to))
193 warn("%s: remove", to);
50da9e1a
KB
194 (void)close(from_fd);
195 (void)close(to_fd);
e96ee99d 196 return (1);
50da9e1a 197 }
50da9e1a 198 (void)close(from_fd);
2041d53d
KB
199
200 if (fchown(to_fd, sbp->st_uid, sbp->st_gid))
201 warn("%s: set owner/group", to);
202 if (fchmod(to_fd, sbp->st_mode))
203 warn("%s: set mode", to);
50da9e1a
KB
204
205 tval[0].tv_sec = sbp->st_atime;
206 tval[1].tv_sec = sbp->st_mtime;
207 tval[0].tv_usec = tval[1].tv_usec = 0;
2041d53d
KB
208 if (utimes(to, tval))
209 warn("%s: set times", to);
210
211 if (close(to_fd)) {
212 warn("%s", to);
213 return (1);
214 }
215
216 if (unlink(from)) {
217 warn("%s: remove", from);
218 return (1);
219 }
e96ee99d 220 return (0);
3880b4a9
BJ
221}
222
e96ee99d 223int
50da9e1a
KB
224copy(from, to)
225 char *from, *to;
3880b4a9 226{
50da9e1a 227 int pid, status;
d53fe8dd 228
dfb144fc
KB
229 if ((pid = vfork()) == 0) {
230 execl(_PATH_CP, "mv", "-PRp", from, to, NULL);
2041d53d 231 warn("%s", _PATH_CP);
50da9e1a
KB
232 _exit(1);
233 }
2041d53d
KB
234 if (waitpid(pid, &status, 0) == -1) {
235 warn("%s: waitpid", _PATH_CP);
236 return (1);
237 }
238 if (!WIFEXITED(status)) {
239 warn("%s: did not terminate normally", _PATH_CP);
e96ee99d 240 return (1);
2041d53d
KB
241 }
242 if (WEXITSTATUS(status)) {
243 warn("%s: terminated with %d (non-zero) status",
244 _PATH_CP, WEXITSTATUS(status));
245 return (1);
246 }
50da9e1a 247 if (!(pid = vfork())) {
79688ddf 248 execl(_PATH_RM, "mv", "-rf", from, NULL);
2041d53d 249 warn("%s", _PATH_RM);
50da9e1a
KB
250 _exit(1);
251 }
2041d53d
KB
252 if (waitpid(pid, &status, 0) == -1) {
253 warn("%s: waitpid", _PATH_RM);
254 return (1);
255 }
256 if (!WIFEXITED(status)) {
257 warn("%s: did not terminate normally", _PATH_RM);
258 return (1);
259 }
260 if (WEXITSTATUS(status)) {
261 warn("%s: terminated with %d (non-zero) status",
262 _PATH_RM, WEXITSTATUS(status));
263 return (1);
264 }
265 return (0);
52b78a7d
KB
266}
267
e96ee99d 268void
8161a03a
KB
269usage()
270{
50da9e1a
KB
271 (void)fprintf(stderr,
272"usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n");
8161a03a
KB
273 exit(1);
274}