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