xref: /csrg-svn/bin/mv/mv.c (revision 39246)
119845Sdist /*
2*39246Sbostic  * Copyright (c) 1989 The Regents of the University of California.
3*39246Sbostic  * All rights reserved.
4*39246Sbostic  *
5*39246Sbostic  * This code is derived from software contributed to Berkeley by
6*39246Sbostic  * Ken Smith of The State University of New York at Buffalo.
7*39246Sbostic  *
8*39246Sbostic  * Redistribution and use in source and binary forms are permitted
9*39246Sbostic  * provided that the above copyright notice and this paragraph are
10*39246Sbostic  * duplicated in all such forms and that any documentation,
11*39246Sbostic  * advertising materials, and other materials related to such
12*39246Sbostic  * distribution and use acknowledge that the software was developed
13*39246Sbostic  * by the University of California, Berkeley.  The name of the
14*39246Sbostic  * University may not be used to endorse or promote products derived
15*39246Sbostic  * from this software without specific prior written permission.
16*39246Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17*39246Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18*39246Sbostic  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1919845Sdist  */
2019845Sdist 
2110047Ssam #ifndef lint
2219845Sdist char copyright[] =
23*39246Sbostic "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
2419845Sdist  All rights reserved.\n";
2534045Sbostic #endif /* not lint */
268839Smckusick 
2719845Sdist #ifndef lint
28*39246Sbostic static char sccsid[] = "@(#)mv.c	5.8 (Berkeley) 10/01/89";
2934045Sbostic #endif /* not lint */
3019845Sdist 
3110047Ssam #include <sys/param.h>
32*39246Sbostic #include <sys/time.h>
33*39246Sbostic #include <sys/wait.h>
3410047Ssam #include <sys/stat.h>
3533382Sbostic #include <sys/file.h>
36*39246Sbostic #include <sys/errno.h>
371216Sbill #include <stdio.h>
38*39246Sbostic #include <string.h>
39*39246Sbostic #include "pathnames.h"
401216Sbill 
41*39246Sbostic extern int errno;
42*39246Sbostic int fflg, iflg;
431216Sbill 
441216Sbill main(argc, argv)
45*39246Sbostic 	int argc;
46*39246Sbostic 	char **argv;
471216Sbill {
48*39246Sbostic 	extern char *optarg;
4934045Sbostic 	extern int optind;
50*39246Sbostic 	register int baselen, exitval, len;
51*39246Sbostic 	register char *p, *endp;
52*39246Sbostic 	struct stat sbuf;
53*39246Sbostic 	int ch;
54*39246Sbostic 	char path[MAXPATHLEN + 1];
551216Sbill 
56*39246Sbostic 	while (((ch = getopt(argc, argv, "-if")) != EOF))
5734045Sbostic 		switch((char)ch) {
58*39246Sbostic 		case 'i':
59*39246Sbostic 			++iflg;
60*39246Sbostic 			break;
6134045Sbostic 		case 'f':
62*39246Sbostic 			++fflg;
6310047Ssam 			break;
64*39246Sbostic 		case '-':		/* undocumented; for compatibility */
65*39246Sbostic 			goto endarg;
6634045Sbostic 		case '?':
6710047Ssam 		default:
6834045Sbostic 			usage();
6910047Ssam 		}
70*39246Sbostic endarg:	argc -= optind;
71*39246Sbostic 	argv += optind;
7234045Sbostic 
7334045Sbostic 	if (argc < 2)
7434045Sbostic 		usage();
75*39246Sbostic 
76*39246Sbostic 	/*
77*39246Sbostic 	 * if stat fails on target, it doesn't exist (or can't be accessed
78*39246Sbostic 	 * by the user, doesn't matter which) try the move.  If target exists,
79*39246Sbostic 	 * and isn't a directory, try the move.  More than 2 arguments is an
80*39246Sbostic 	 * error.
81*39246Sbostic 	 */
82*39246Sbostic 	if (stat(argv[argc - 1], &sbuf) || !S_ISDIR(sbuf.st_mode)) {
83*39246Sbostic 		if (argc > 2)
84*39246Sbostic 			usage();
85*39246Sbostic 		exit(do_move(argv[0], argv[1]));
861216Sbill 	}
871216Sbill 
88*39246Sbostic 	/* got a directory, move each file into it */
89*39246Sbostic 	(void)strcpy(path, argv[argc - 1]);
90*39246Sbostic 	baselen = strlen(path);
91*39246Sbostic 	endp = &path[baselen];
92*39246Sbostic 	*endp++ = '/';
93*39246Sbostic 	++baselen;
94*39246Sbostic 	for (exitval = 0; --argc; ++argv) {
95*39246Sbostic 		if ((p = rindex(*argv, '/')) == NULL)
96*39246Sbostic 			p = *argv;
97*39246Sbostic 		else
98*39246Sbostic 			++p;
99*39246Sbostic 		if ((baselen + (len = strlen(p))) >= MAXPATHLEN)
100*39246Sbostic 			(void)fprintf(stderr,
101*39246Sbostic 			    "mv: %s: destination pathname too long\n", *argv);
102*39246Sbostic 		else {
103*39246Sbostic 			bcopy(p, endp, len + 1);
104*39246Sbostic 			exitval |= do_move(*argv, path);
105*39246Sbostic 		}
10610047Ssam 	}
107*39246Sbostic 	exit(exitval);
10810047Ssam }
10910047Ssam 
110*39246Sbostic do_move(from, to)
111*39246Sbostic 	char *from, *to;
1121216Sbill {
113*39246Sbostic 	struct stat sbuf;
114*39246Sbostic 	int ask, ch;
1151216Sbill 
11610047Ssam 	/*
117*39246Sbostic 	 * Check access.  If interactive and file exists ask user if it
118*39246Sbostic 	 * should be replaced.  Otherwise if file exists but isn't writable
119*39246Sbostic 	 * make sure the user wants to clobber it.
12010047Ssam 	 */
121*39246Sbostic 	if (!fflg && !access(to, F_OK)) {
122*39246Sbostic 		ask = 0;
123*39246Sbostic 		if (iflg) {
124*39246Sbostic 			(void)fprintf(stderr, "overwrite %s? ", to);
125*39246Sbostic 			ask = 1;
1261216Sbill 		}
127*39246Sbostic 		else if (access(to, W_OK) && !stat(to, &sbuf)) {
128*39246Sbostic 			(void)fprintf(stderr, "override mode %o on %s? ",
129*39246Sbostic 			    sbuf.st_mode & 07777, to);
130*39246Sbostic 			ask = 1;
131*39246Sbostic 		}
132*39246Sbostic 		if (ask) {
133*39246Sbostic 			if ((ch = getchar()) != EOF && ch != '\n')
134*39246Sbostic 				while (getchar() != '\n');
135*39246Sbostic 			if (ch != 'y')
136*39246Sbostic 				return(0);
137*39246Sbostic 		}
1381216Sbill 	}
139*39246Sbostic 	if (!rename(from, to))
140*39246Sbostic 		return(0);
14111642Ssam 	if (errno != EXDEV) {
142*39246Sbostic 		(void)fprintf(stderr,
143*39246Sbostic 		    "mv: rename %s to %s: %s\n", from, to, strerror(errno));
144*39246Sbostic 		return(1);
14511642Ssam 	}
14610047Ssam 	/*
147*39246Sbostic 	 * if rename fails, and it's a regular file, do the copy
148*39246Sbostic 	 * internally; otherwise, use cp and rm.
14910047Ssam 	 */
150*39246Sbostic 	if (stat(from, &sbuf)) {
151*39246Sbostic 		(void)fprintf(stderr,
152*39246Sbostic 		    "mv: %s: %s\n", from, strerror(errno));
153*39246Sbostic 		return(1);
15410047Ssam 	}
155*39246Sbostic 	return(S_ISREG(sbuf.st_mode) ?
156*39246Sbostic 	    fastcopy(from, to, &sbuf) : copy(from, to));
157*39246Sbostic }
15810166Ssam 
159*39246Sbostic fastcopy(from, to, sbp)
160*39246Sbostic 	char *from, *to;
161*39246Sbostic 	struct stat *sbp;
162*39246Sbostic {
163*39246Sbostic 	struct timeval tval[2];
164*39246Sbostic 	static u_int blen;
165*39246Sbostic 	static char *bp;
166*39246Sbostic 	register int nread, from_fd, to_fd;
167*39246Sbostic 	char *malloc();
16822601Sserge 
169*39246Sbostic 	if ((from_fd = open(from, O_RDONLY, 0)) < 0) {
170*39246Sbostic 		(void)fprintf(stderr,
171*39246Sbostic 		    "mv: %s: %s\n", from, strerror(errno));
172*39246Sbostic 		return(1);
17310047Ssam 	}
174*39246Sbostic 	if ((to_fd = open(to, O_WRONLY|O_CREAT|O_TRUNC, sbp->st_mode)) < 0) {
175*39246Sbostic 		(void)fprintf(stderr,
176*39246Sbostic 		    "mv: %s: %s\n", to, strerror(errno));
177*39246Sbostic 		(void)close(from_fd);
178*39246Sbostic 		return(1);
179*39246Sbostic 	}
180*39246Sbostic 	if (!blen && !(bp = malloc(blen = sbp->st_blksize))) {
181*39246Sbostic 		(void)fprintf(stderr, "mv: %s: out of memory.\n", from);
182*39246Sbostic 		return(1);
183*39246Sbostic 	}
184*39246Sbostic 	while ((nread = read(from_fd, bp, blen)) > 0)
185*39246Sbostic 		if (write(to_fd, bp, nread) != nread) {
186*39246Sbostic 			(void)fprintf(stderr, "mv: %s: %s\n",
187*39246Sbostic 			    to, strerror(errno));
188*39246Sbostic 			goto err;
1891216Sbill 		}
190*39246Sbostic 	if (nread < 0) {
191*39246Sbostic 		(void)fprintf(stderr, "mv: %s: %s\n", from, strerror(errno));
192*39246Sbostic err:		(void)unlink(to);
193*39246Sbostic 		(void)close(from_fd);
194*39246Sbostic 		(void)close(to_fd);
195*39246Sbostic 		return(1);
1961216Sbill 	}
197*39246Sbostic 	(void)fchown(to_fd, sbp->st_uid, sbp->st_gid);
198*39246Sbostic 	(void)fchmod(to_fd, sbp->st_mode);
1991216Sbill 
200*39246Sbostic 	(void)close(from_fd);
201*39246Sbostic 	(void)close(to_fd);
2021216Sbill 
203*39246Sbostic 	tval[0].tv_sec = sbp->st_atime;
204*39246Sbostic 	tval[1].tv_sec = sbp->st_mtime;
205*39246Sbostic 	tval[0].tv_usec = tval[1].tv_usec = 0;
206*39246Sbostic 	(void)utimes(to, tval);
207*39246Sbostic 	(void)unlink(from);
208*39246Sbostic 	return(0);
2091216Sbill }
2101216Sbill 
211*39246Sbostic copy(from, to)
212*39246Sbostic 	char *from, *to;
2131216Sbill {
214*39246Sbostic 	int pid, status;
2151216Sbill 
216*39246Sbostic 	if (!(pid = vfork())) {
217*39246Sbostic 		execlp(_PATH_CP, "mv", "-pr", from, to);
218*39246Sbostic 		(void)fprintf(stderr, "mv: can't exec %s.\n", _PATH_CP);
219*39246Sbostic 		_exit(1);
220*39246Sbostic 	}
221*39246Sbostic 	(void)waitpid(pid, &status, 0);
222*39246Sbostic 	if (!WIFEXITED(status) || WEXITSTATUS(status))
223*39246Sbostic 		return(1);
224*39246Sbostic 	if (!(pid = vfork())) {
225*39246Sbostic 		execlp(_PATH_RM, "mv", "-rf", from);
226*39246Sbostic 		(void)fprintf(stderr, "mv: can't exec %s.\n", _PATH_RM);
227*39246Sbostic 		_exit(1);
228*39246Sbostic 	}
229*39246Sbostic 	(void)waitpid(pid, &status, 0);
230*39246Sbostic 	return(!WIFEXITED(status) || WEXITSTATUS(status));
2311216Sbill }
2321216Sbill 
23334045Sbostic usage()
23434045Sbostic {
235*39246Sbostic 	(void)fprintf(stderr,
236*39246Sbostic "usage: mv [-if] src target;\n   or: mv [-if] src1 ... srcN directory\n");
23734045Sbostic 	exit(1);
23834045Sbostic }
239