BSD 4_3_Net_2 development
[unix-history] / .ref-8c8a5b54e79564c14fc7a2823a21a8f048449bcf / usr / src / bin / mv / mv.c
CommitLineData
bcf1365c 1/*
50da9e1a
KB
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
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
12char copyright[] =
50da9e1a 13"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
bcf1365c 14 All rights reserved.\n";
8161a03a 15#endif /* not lint */
bcf1365c 16
d53fe8dd 17#ifndef lint
79688ddf 18static char sccsid[] = "@(#)mv.c 5.11 (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>
52b78a7d
KB
25#include <fcntl.h>
26#include <errno.h>
27#include <unistd.h>
3880b4a9 28#include <stdio.h>
52b78a7d 29#include <stdlib.h>
50da9e1a
KB
30#include <string.h>
31#include "pathnames.h"
3880b4a9 32
50da9e1a 33int fflg, iflg;
3880b4a9
BJ
34
35main(argc, argv)
50da9e1a
KB
36 int argc;
37 char **argv;
3880b4a9 38{
50da9e1a 39 extern char *optarg;
8161a03a 40 extern int optind;
50da9e1a
KB
41 register int baselen, exitval, len;
42 register char *p, *endp;
52b78a7d 43 struct stat sb;
50da9e1a
KB
44 int ch;
45 char path[MAXPATHLEN + 1];
3880b4a9 46
50da9e1a 47 while (((ch = getopt(argc, argv, "-if")) != EOF))
8161a03a 48 switch((char)ch) {
d53fe8dd 49 case 'i':
52b78a7d 50 iflg = 1;
50da9e1a
KB
51 break;
52 case 'f':
52b78a7d 53 fflg = 1;
d53fe8dd 54 break;
50da9e1a
KB
55 case '-': /* undocumented; for compatibility */
56 goto endarg;
8161a03a 57 case '?':
d53fe8dd 58 default:
8161a03a 59 usage();
d53fe8dd 60 }
50da9e1a
KB
61endarg: argc -= optind;
62 argv += optind;
8161a03a
KB
63
64 if (argc < 2)
65 usage();
d53fe8dd 66
50da9e1a 67 /*
52b78a7d
KB
68 * If the stat on the target fails or the target isn't a directory,
69 * try the move. More than 2 arguments is an error in this case.
50da9e1a 70 */
52b78a7d 71 if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) {
50da9e1a
KB
72 if (argc > 2)
73 usage();
74 exit(do_move(argv[0], argv[1]));
75 }
d53fe8dd 76
52b78a7d 77 /* It's a directory, move each file into it. */
50da9e1a
KB
78 (void)strcpy(path, argv[argc - 1]);
79 baselen = strlen(path);
80 endp = &path[baselen];
81 *endp++ = '/';
82 ++baselen;
83 for (exitval = 0; --argc; ++argv) {
84 if ((p = rindex(*argv, '/')) == NULL)
85 p = *argv;
86 else
87 ++p;
88 if ((baselen + (len = strlen(p))) >= MAXPATHLEN)
89 (void)fprintf(stderr,
90 "mv: %s: destination pathname too long\n", *argv);
91 else {
92 bcopy(p, endp, len + 1);
93 exitval |= do_move(*argv, path);
94 }
d53fe8dd 95 }
50da9e1a 96 exit(exitval);
3880b4a9
BJ
97}
98
50da9e1a
KB
99do_move(from, to)
100 char *from, *to;
3880b4a9 101{
52b78a7d 102 struct stat sb;
50da9e1a 103 int ask, ch;
3880b4a9 104
d53fe8dd 105 /*
52b78a7d 106 * Check access. If interactive and file exists, ask user if it
50da9e1a
KB
107 * should be replaced. Otherwise if file exists but isn't writable
108 * make sure the user wants to clobber it.
d53fe8dd 109 */
50da9e1a
KB
110 if (!fflg && !access(to, F_OK)) {
111 ask = 0;
112 if (iflg) {
113 (void)fprintf(stderr, "overwrite %s? ", to);
114 ask = 1;
115 }
52b78a7d 116 else if (access(to, W_OK) && !stat(to, &sb)) {
50da9e1a 117 (void)fprintf(stderr, "override mode %o on %s? ",
52b78a7d 118 sb.st_mode & 07777, to);
50da9e1a
KB
119 ask = 1;
120 }
121 if (ask) {
122 if ((ch = getchar()) != EOF && ch != '\n')
123 while (getchar() != '\n');
124 if (ch != 'y')
125 return(0);
3880b4a9 126 }
b07dfbb8 127 }
50da9e1a
KB
128 if (!rename(from, to))
129 return(0);
52b78a7d 130
b07dfbb8 131 if (errno != EXDEV) {
50da9e1a
KB
132 (void)fprintf(stderr,
133 "mv: rename %s to %s: %s\n", from, to, strerror(errno));
134 return(1);
3880b4a9 135 }
52b78a7d 136
d53fe8dd 137 /*
52b78a7d
KB
138 * If rename fails, and it's a regular file, do the copy internally;
139 * otherwise, use cp and rm.
d53fe8dd 140 */
52b78a7d
KB
141 if (stat(from, &sb)) {
142 (void)fprintf(stderr, "mv: %s: %s\n", from, strerror(errno));
50da9e1a 143 return(1);
d53fe8dd 144 }
52b78a7d
KB
145 return(S_ISREG(sb.st_mode) ?
146 fastcopy(from, to, &sb) : copy(from, to));
50da9e1a 147}
465786e2 148
50da9e1a
KB
149fastcopy(from, to, sbp)
150 char *from, *to;
151 struct stat *sbp;
152{
153 struct timeval tval[2];
154 static u_int blen;
155 static char *bp;
156 register int nread, from_fd, to_fd;
50da9e1a
KB
157
158 if ((from_fd = open(from, O_RDONLY, 0)) < 0) {
52b78a7d 159 error(from);
50da9e1a 160 return(1);
d53fe8dd 161 }
52b78a7d
KB
162 if ((to_fd = open(to, O_CREAT|O_TRUNC|O_WRONLY, sbp->st_mode)) < 0) {
163 error(to);
50da9e1a
KB
164 (void)close(from_fd);
165 return(1);
3880b4a9 166 }
50da9e1a 167 if (!blen && !(bp = malloc(blen = sbp->st_blksize))) {
52b78a7d 168 error(NULL);
50da9e1a 169 return(1);
3880b4a9 170 }
50da9e1a
KB
171 while ((nread = read(from_fd, bp, blen)) > 0)
172 if (write(to_fd, bp, nread) != nread) {
52b78a7d 173 error(to);
50da9e1a
KB
174 goto err;
175 }
176 if (nread < 0) {
52b78a7d 177 error(from);
50da9e1a
KB
178err: (void)unlink(to);
179 (void)close(from_fd);
180 (void)close(to_fd);
181 return(1);
182 }
183 (void)fchown(to_fd, sbp->st_uid, sbp->st_gid);
184 (void)fchmod(to_fd, sbp->st_mode);
185
186 (void)close(from_fd);
187 (void)close(to_fd);
188
189 tval[0].tv_sec = sbp->st_atime;
190 tval[1].tv_sec = sbp->st_mtime;
191 tval[0].tv_usec = tval[1].tv_usec = 0;
192 (void)utimes(to, tval);
193 (void)unlink(from);
194 return(0);
3880b4a9
BJ
195}
196
50da9e1a
KB
197copy(from, to)
198 char *from, *to;
3880b4a9 199{
50da9e1a 200 int pid, status;
d53fe8dd 201
50da9e1a 202 if (!(pid = vfork())) {
79688ddf 203 execl(_PATH_CP, "mv", "-pr", from, to, NULL);
52b78a7d 204 error(_PATH_CP);
50da9e1a
KB
205 _exit(1);
206 }
207 (void)waitpid(pid, &status, 0);
208 if (!WIFEXITED(status) || WEXITSTATUS(status))
209 return(1);
210 if (!(pid = vfork())) {
79688ddf 211 execl(_PATH_RM, "mv", "-rf", from, NULL);
52b78a7d 212 error(_PATH_RM);
50da9e1a
KB
213 _exit(1);
214 }
215 (void)waitpid(pid, &status, 0);
216 return(!WIFEXITED(status) || WEXITSTATUS(status));
3880b4a9 217}
8161a03a 218
52b78a7d
KB
219error(s)
220 char *s;
221{
222 if (s)
223 (void)fprintf(stderr, "mv: %s: %s\n", s, strerror(errno));
224 else
225 (void)fprintf(stderr, "mv: %s\n", strerror(errno));
226}
227
8161a03a
KB
228usage()
229{
50da9e1a
KB
230 (void)fprintf(stderr,
231"usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n");
8161a03a
KB
232 exit(1);
233}