/*
- * Copyright (c) 1980 Regents of the University of California.
- * All rights reserved. The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ken Smith of The State University of New York at Buffalo.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
*/
#ifndef lint
char copyright[] =
-"@(#) Copyright (c) 1980 Regents of the University of California.\n\
+"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
All rights reserved.\n";
-#endif not lint
+#endif /* not lint */
#ifndef lint
-static char sccsid[] = "@(#)mv.c 5.4 (Berkeley) %G%";
-#endif not lint
+static char sccsid[] = "@(#)mv.c 5.11 (Berkeley) 4/3/91";
+#endif /* not lint */
-/*
- * mv file1 file2
- */
#include <sys/param.h>
-#include <sys/stat.h>
#include <sys/time.h>
-
-#include <stdio.h>
-#include <sys/dir.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <errno.h>
-#include <signal.h>
-
-#define DELIM '/'
-#define MODEBITS 07777
-
-#define ISDIR(st) (((st).st_mode&S_IFMT) == S_IFDIR)
-#define ISLNK(st) (((st).st_mode&S_IFMT) == S_IFLNK)
-#define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG)
-#define ISDEV(st) \
- (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK)
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pathnames.h"
-char *sprintf();
-char *dname();
-struct stat s1, s2;
-int iflag = 0; /* interactive mode */
-int fflag = 0; /* force overwriting */
-extern unsigned errno;
+int fflg, iflg;
main(argc, argv)
- register char *argv[];
+ int argc;
+ char **argv;
{
- register i, r;
- register char *arg;
- char *dest;
-
- if (argc < 2)
- goto usage;
- while (argc > 1 && *argv[1] == '-') {
- argc--;
- arg = *++argv;
-
- /*
- * all files following a null option
- * are considered file names
- */
- if (*(arg+1) == '\0')
- break;
- while (*++arg != '\0') switch (*arg) {
-
+ extern char *optarg;
+ extern int optind;
+ register int baselen, exitval, len;
+ register char *p, *endp;
+ struct stat sb;
+ int ch;
+ char path[MAXPATHLEN + 1];
+
+ while (((ch = getopt(argc, argv, "-if")) != EOF))
+ switch((char)ch) {
case 'i':
- iflag++;
+ iflg = 1;
break;
-
case 'f':
- fflag++;
+ fflg = 1;
break;
-
+ case '-': /* undocumented; for compatibility */
+ goto endarg;
+ case '?':
default:
- goto usage;
+ usage();
}
- }
- if (argc < 3)
- goto usage;
- dest = argv[argc-1];
- if (stat(dest, &s2) >= 0 && ISDIR(s2)) {
- r = 0;
- for (i = 1; i < argc-1; i++)
- r |= movewithshortname(argv[i], dest);
- exit(r);
- }
- if (argc > 3)
- goto usage;
- r = move(argv[1], argv[2]);
- exit(r);
- /*NOTREACHED*/
-usage:
- fprintf(stderr,
-"usage: mv [-if] f1 f2 or mv [-if] f1 ... fn d1 (`fn' is a file or directory)\n");
- return (1);
-}
+endarg: argc -= optind;
+ argv += optind;
-movewithshortname(src, dest)
- char *src, *dest;
-{
- register char *shortname;
- char target[MAXPATHLEN + 1];
+ if (argc < 2)
+ usage();
- shortname = dname(src);
- if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) {
- error("%s/%s: pathname too long", dest,
- shortname);
- return (1);
+ /*
+ * If the stat on the target fails or the target isn't a directory,
+ * try the move. More than 2 arguments is an error in this case.
+ */
+ if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) {
+ if (argc > 2)
+ usage();
+ exit(do_move(argv[0], argv[1]));
}
- sprintf(target, "%s/%s", dest, shortname);
- return (move(src, target));
+
+ /* It's a directory, move each file into it. */
+ (void)strcpy(path, argv[argc - 1]);
+ baselen = strlen(path);
+ endp = &path[baselen];
+ *endp++ = '/';
+ ++baselen;
+ for (exitval = 0; --argc; ++argv) {
+ if ((p = rindex(*argv, '/')) == NULL)
+ p = *argv;
+ else
+ ++p;
+ if ((baselen + (len = strlen(p))) >= MAXPATHLEN)
+ (void)fprintf(stderr,
+ "mv: %s: destination pathname too long\n", *argv);
+ else {
+ bcopy(p, endp, len + 1);
+ exitval |= do_move(*argv, path);
+ }
+ }
+ exit(exitval);
}
-move(source, target)
- char *source, *target;
+do_move(from, to)
+ char *from, *to;
{
- int targetexists;
+ struct stat sb;
+ int ask, ch;
- if (lstat(source, &s1) < 0) {
- Perror2(source, "Cannot access");
- return (1);
- }
/*
- * First, try to rename source to destination.
- * The only reason we continue on failure is if
- * the move is on a nondirectory and not across
- * file systems.
+ * Check access. If interactive and file exists, ask user if it
+ * should be replaced. Otherwise if file exists but isn't writable
+ * make sure the user wants to clobber it.
*/
- targetexists = lstat(target, &s2) >= 0;
- if (targetexists) {
- if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) {
- error("%s and %s are identical", source, target);
- return (1);
+ if (!fflg && !access(to, F_OK)) {
+ ask = 0;
+ if (iflg) {
+ (void)fprintf(stderr, "overwrite %s? ", to);
+ ask = 1;
+ }
+ else if (access(to, W_OK) && !stat(to, &sb)) {
+ (void)fprintf(stderr, "override mode %o on %s? ",
+ sb.st_mode & 07777, to);
+ ask = 1;
}
- if (iflag && !fflag && isatty(fileno(stdin)) &&
- query("remove %s? ", target) == 0)
- return (1);
- if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) {
- if (query("override protection %o for %s? ",
- s2.st_mode & MODEBITS, target) == 0)
- return (1);
+ if (ask) {
+ if ((ch = getchar()) != EOF && ch != '\n')
+ while (getchar() != '\n');
+ if (ch != 'y')
+ return(0);
}
}
- if (rename(source, target) >= 0)
- return (0);
+ if (!rename(from, to))
+ return(0);
+
if (errno != EXDEV) {
- Perror2(errno == ENOENT && targetexists == 0 ? target : source,
- "rename");
- return (1);
- }
- if (ISDIR(s1)) {
- error("can't mv directories across file systems");
- return (1);
- }
- if (targetexists && unlink(target) < 0) {
- Perror2(target, "Cannot unlink");
- return (1);
+ (void)fprintf(stderr,
+ "mv: rename %s to %s: %s\n", from, to, strerror(errno));
+ return(1);
}
+
/*
- * File can't be renamed, try to recreate the symbolic
- * link or special device, or copy the file wholesale
- * between file systems.
+ * If rename fails, and it's a regular file, do the copy internally;
+ * otherwise, use cp and rm.
*/
- if (ISLNK(s1)) {
- register m;
- char symln[MAXPATHLEN + 1];
-
- m = readlink(source, symln, sizeof (symln) - 1);
- if (m < 0) {
- Perror(source);
- return (1);
- }
- symln[m] = '\0';
-
- (void) umask(~(s1.st_mode & MODEBITS));
- if (symlink(symln, target) < 0) {
- Perror(target);
- return (1);
- }
- goto cleanup;
+ if (stat(from, &sb)) {
+ (void)fprintf(stderr, "mv: %s: %s\n", from, strerror(errno));
+ return(1);
}
- (void) umask(0);
- if (ISDEV(s1)) {
- struct timeval tv[2];
-
- if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
- Perror(target);
- return (1);
- }
+ return(S_ISREG(sb.st_mode) ?
+ fastcopy(from, to, &sb) : copy(from, to));
+}
- tv[0].tv_sec = s1.st_atime;
- tv[0].tv_usec = 0;
- tv[1].tv_sec = s1.st_mtime;
- tv[1].tv_usec = 0;
- (void) utimes(target, tv);
- goto cleanup;
+fastcopy(from, to, sbp)
+ char *from, *to;
+ struct stat *sbp;
+{
+ struct timeval tval[2];
+ static u_int blen;
+ static char *bp;
+ register int nread, from_fd, to_fd;
+
+ if ((from_fd = open(from, O_RDONLY, 0)) < 0) {
+ error(from);
+ return(1);
}
- if (ISREG(s1)) {
- register int fi, fo, n;
- struct timeval tv[2];
- char buf[MAXBSIZE];
-
- fi = open(source, 0);
- if (fi < 0) {
- Perror(source);
- return (1);
- }
-
- fo = creat(target, s1.st_mode & MODEBITS);
- if (fo < 0) {
- Perror(target);
- close(fi);
- return (1);
- }
-
- for (;;) {
- n = read(fi, buf, sizeof buf);
- if (n == 0) {
- break;
- } else if (n < 0) {
- Perror2(source, "read");
- close(fi);
- close(fo);
- return (1);
- } else if (write(fo, buf, n) != n) {
- Perror2(target, "write");
- close(fi);
- close(fo);
- return (1);
- }
- }
-
- close(fi);
- close(fo);
-
- tv[0].tv_sec = s1.st_atime;
- tv[0].tv_usec = 0;
- tv[1].tv_sec = s1.st_mtime;
- tv[1].tv_usec = 0;
- (void) utimes(target, tv);
- goto cleanup;
+ if ((to_fd = open(to, O_CREAT|O_TRUNC|O_WRONLY, sbp->st_mode)) < 0) {
+ error(to);
+ (void)close(from_fd);
+ return(1);
}
- error("%s: unknown file type %o", source, s1.st_mode);
- return (1);
-
-cleanup:
- if (unlink(source) < 0) {
- Perror2(source, "Cannot unlink");
- return (1);
+ if (!blen && !(bp = malloc(blen = sbp->st_blksize))) {
+ error(NULL);
+ return(1);
}
- return (0);
-}
-
-/*VARARGS*/
-query(prompt, a1, a2)
- char *a1;
-{
- register int i, c;
-
- fprintf(stderr, prompt, a1, a2);
- i = c = getchar();
- while (c != '\n' && c != EOF)
- c = getchar();
- return (i == 'y');
-}
-
-char *
-dname(name)
- register char *name;
-{
- register char *p;
-
- p = name;
- while (*p)
- if (*p++ == DELIM && *p)
- name = p;
- return name;
+ while ((nread = read(from_fd, bp, blen)) > 0)
+ if (write(to_fd, bp, nread) != nread) {
+ error(to);
+ goto err;
+ }
+ if (nread < 0) {
+ error(from);
+err: (void)unlink(to);
+ (void)close(from_fd);
+ (void)close(to_fd);
+ return(1);
+ }
+ (void)fchown(to_fd, sbp->st_uid, sbp->st_gid);
+ (void)fchmod(to_fd, sbp->st_mode);
+
+ (void)close(from_fd);
+ (void)close(to_fd);
+
+ tval[0].tv_sec = sbp->st_atime;
+ tval[1].tv_sec = sbp->st_mtime;
+ tval[0].tv_usec = tval[1].tv_usec = 0;
+ (void)utimes(to, tval);
+ (void)unlink(from);
+ return(0);
}
-/*VARARGS*/
-error(fmt, a1, a2)
- char *fmt;
+copy(from, to)
+ char *from, *to;
{
+ int pid, status;
- fprintf(stderr, "mv: ");
- fprintf(stderr, fmt, a1, a2);
- fprintf(stderr, "\n");
+ if (!(pid = vfork())) {
+ execl(_PATH_CP, "mv", "-pr", from, to, NULL);
+ error(_PATH_CP);
+ _exit(1);
+ }
+ (void)waitpid(pid, &status, 0);
+ if (!WIFEXITED(status) || WEXITSTATUS(status))
+ return(1);
+ if (!(pid = vfork())) {
+ execl(_PATH_RM, "mv", "-rf", from, NULL);
+ error(_PATH_RM);
+ _exit(1);
+ }
+ (void)waitpid(pid, &status, 0);
+ return(!WIFEXITED(status) || WEXITSTATUS(status));
}
-Perror(s)
+error(s)
char *s;
{
- char buf[MAXPATHLEN + 10];
-
- sprintf(buf, "mv: %s", s);
- perror(buf);
+ if (s)
+ (void)fprintf(stderr, "mv: %s: %s\n", s, strerror(errno));
+ else
+ (void)fprintf(stderr, "mv: %s\n", strerror(errno));
}
-Perror2(s1, s2)
- char *s1, *s2;
+usage()
{
- char buf[MAXPATHLEN + 20];
-
- sprintf(buf, "mv: %s: %s", s1, s2);
- perror(buf);
+ (void)fprintf(stderr,
+"usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n");
+ exit(1);
}