X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/24bb4e1964e27f02c0ecd59b48c366a48ca15862..21b0ad8192097b8bf5da34cb61d50004875d372c:/usr/src/bin/mv/mv.c diff --git a/usr/src/bin/mv/mv.c b/usr/src/bin/mv/mv.c index 274abdd1ec..69801c439e 100644 --- a/usr/src/bin/mv/mv.c +++ b/usr/src/bin/mv/mv.c @@ -1,293 +1,257 @@ -static char *sccsid = "@(#)mv.c 4.5 (Berkeley) 82/02/11"; /* - * mv file1 file2 + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. */ -#include -#include +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1980 Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mv.c 5.7 (Berkeley) %G%"; +#endif /* not lint */ + +/* + * mv file1 file2 + */ +#include #include -#include +#include +#include +#include +#include -#define DOT "." -#define DOTDOT ".." #define DELIM '/' -#define SDELIM "/" -#define MAXN 100 #define MODEBITS 07777 -#define ROOTINO 2 -char *pname(); -char *sprintf(); +#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) + char *dname(); -struct stat s1, s2; -int iflag = 0; /* interactive flag. If this flag is set, - * the user is queried before files are - * destroyed by cp. - */ -int fflag = 0; /* force flag. supercedes all restrictions */ +int iflag = 0; /* interactive mode */ +int fflag = 0; /* force overwriting */ +extern unsigned errno; main(argc, argv) -register char *argv[]; + register int argc; + register char **argv; { - register i, r; - register char *arg; + extern int optind; + struct stat st; + int ch, r; + char *dest; - /* get the flag(s) */ + while ((ch = getopt(argc, argv, "-fi")) != EOF) + switch((char)ch) { + case '-': + goto endarg; + case 'f': + fflag++; + break; + case 'i': + iflag++; + break; + case '?': + default: + usage(); + } +endarg: argv += optind; + argc -= optind; 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) { - - /* interactive mode */ - case 'i': - iflag++; - break; + usage(); + dest = argv[argc - 1]; + if (stat(dest, &st) >= 0 && ISDIR(st)) { + for (r = 0; --argc; ++argv) + r |= movewithshortname(*argv, dest); + exit(r); + } + if (argc != 2) + usage(); + r = move(argv[0], argv[1]); + exit(r); +} - /* force moves */ - case 'f': - fflag++; - break; +movewithshortname(src, dest) + char *src, *dest; +{ + register char *shortname; + char target[MAXPATHLEN + 1]; - /* don't live with bad options */ - default: - goto usage; - } + shortname = dname(src); + if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) { + error("%s/%s: pathname too long", dest, + shortname); + return (1); } - if (argc < 3) - goto usage; - if (stat(argv[1], &s1) < 0) { - fprintf(stderr, "mv: cannot access %s\n", argv[1]); - return(1); - } - if ((s1.st_mode & S_IFMT) == S_IFDIR) { - if (argc != 3) - goto usage; - return mvdir(argv[1], argv[2]); - } - setuid(getuid()); - if (argc > 3) - if (stat(argv[argc-1], &s2) < 0 || (s2.st_mode & S_IFMT) != S_IFDIR) - goto usage; - r = 0; - for (i=1; i= 0) { - if ((s2.st_mode & S_IFMT) == S_IFDIR) { - sprintf(buf, "%s/%s", target, dname(source)); - target = buf; + /* + * 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. + */ + 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 (stat(target, &s2) >= 0) { - if ((s2.st_mode & S_IFMT) == S_IFDIR) { - fprintf(stderr, "mv: %s is a directory\n", target); - return(1); - } else if (iflag && !fflag) { - fprintf(stderr, "remove %s? ", target); - i = c = getchar(); - while (c != '\n' && c != EOF) - c = getchar(); - if (i != 'y') - return(1); - } - if (s1.st_dev==s2.st_dev && s1.st_ino==s2.st_ino) { - fprintf(stderr, "mv: %s and %s are identical\n", - source, target); - return(1); + if (!fflag && isatty(fileno(stdin))) + if (iflag) { + if (!query("remove %s? ", target)) + return (1); } - if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) { - fprintf(stderr, "override protection %o for %s? ", - s2.st_mode & MODEBITS, target); - i = c = getchar(); - while (c != '\n' && c != EOF) - c = getchar(); - if (i != 'y') - return(1); - } - if (unlink(target) < 0) { - fprintf(stderr, "mv: cannot unlink %s\n", target); - return(1); - } - } + else if (access(target, W_OK) < 0 && + !query("override protection %o for %s? ", + s2.st_mode & MODEBITS, target)) + return (1); } - if (link(source, target) < 0) { - i = fork(); - if (i == -1) { - fprintf(stderr, "mv: try again\n"); - return(1); - } - if (i == 0) { - execl("/bin/cp", "cp", source, target, 0); - fprintf(stderr, "mv: cannot exec cp\n"); - exit(1); - } - while ((c = wait(&status)) != i && c != -1) - ; - if (status != 0) - return(1); - utime(target, &s1.st_atime); + if (rename(source, target) >= 0) + return (0); + if (errno != EXDEV) { + Perror2(errno == ENOENT && targetexists == 0 ? target : source, + "rename"); + return (1); } - if (unlink(source) < 0) { - fprintf(stderr, "mv: cannot unlink %s\n", source); - return(1); + if (ISDIR(s1)) { + error("can't mv directories across file systems"); + return (1); } - return(0); -} + if (targetexists && unlink(target) < 0) { + Perror2(target, "Cannot unlink"); + 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 (ISLNK(s1)) { + register m; + char symln[MAXPATHLEN + 1]; -mvdir(source, target) -char *source, *target; -{ - register char *p; - register i; - char buf[MAXN]; - char c,cc; - - if (stat(target, &s2) >= 0) { - if ((s2.st_mode&S_IFMT) != S_IFDIR) { - fprintf(stderr, "mv: %s exists\n", target); - return(1); + m = readlink(source, symln, sizeof (symln) - 1); + if (m < 0) { + Perror(source); + return (1); } - p = dname(source); - if (strlen(target) > MAXN-strlen(p)-2) { - fprintf(stderr, "mv :target name too long\n"); - return(1); - } - strcpy(buf, target); - target = buf; - strcat(buf, SDELIM); - strcat(buf, p); - if (stat(target, &s2) >= 0) { - fprintf(stderr, "mv: %s exists\n", buf); - return(1); + symln[m] = '\0'; + + (void) umask(~(s1.st_mode & MODEBITS)); + if (symlink(symln, target) < 0) { + Perror(target); + return (1); } + goto cleanup; } - if (strcmp(source, target) == 0) { - fprintf(stderr, "mv: ?? source == target, source exists and target doesnt\n"); - return(1); - } - p = dname(source); - if (!strcmp(p, DOT) || !strcmp(p, DOTDOT) || !strcmp(p, "") || p[strlen(p)-1]=='/') { - fprintf(stderr, "mv: cannot rename %s\n", p); - return(1); - } - if (stat(pname(source), &s1) < 0 || stat(pname(target), &s2) < 0) { - fprintf(stderr, "mv: cannot locate parent\n"); - return(1); - } - if (access(pname(target), 2) < 0) { - fprintf(stderr, "mv: no write access to %s\n", pname(target)); - return(1); - } - if (access(pname(source), 2) < 0) { - fprintf(stderr, "mv: no write access to %s\n", pname(source)); - return(1); - } - if (s1.st_dev != s2.st_dev) { - fprintf(stderr, "mv: cannot move directories across devices\n"); - return(1); - } - if (s1.st_ino != s2.st_ino) { - char dst[MAXN+5]; + (void) umask(0); + if (ISDEV(s1)) { + struct timeval tv[2]; - if (chkdot(source) || chkdot(target)) { - fprintf(stderr, "mv: Sorry, path names including %s aren't allowed\n", DOTDOT); - return(1); + if (mknod(target, s1.st_mode, s1.st_rdev) < 0) { + Perror(target); + return (1); } - stat(source, &s1); - if (check(pname(target), s1.st_ino)) - return(1); - for (i = 1; i <= NSIG; i++) - signal(i, SIG_IGN); - if (link(source, target) < 0) { - fprintf(stderr, "mv: cannot link %s to %s\n", target, source); - return(1); - } - if (unlink(source) < 0) { - fprintf(stderr, "mv: %s: cannot unlink\n", source); - unlink(target); - return(1); + + 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 (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); } - strcat(dst, target); - strcat(dst, "/"); - strcat(dst, DOTDOT); - if (unlink(dst) < 0) { - fprintf(stderr, "mv: %s: cannot unlink\n", dst); - if (link(target, source) >= 0) - unlink(target); - return(1); + + fo = creat(target, s1.st_mode & MODEBITS); + if (fo < 0) { + Perror(target); + close(fi); + return (1); } - if (link(pname(target), dst) < 0) { - fprintf(stderr, "mv: cannot link %s to %s\n", - dst, pname(target)); - if (link(pname(source), dst) >= 0) - if (link(target, source) >= 0) - unlink(target); - 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); + } } - return(0); - } - if (link(source, target) < 0) { - fprintf(stderr, "mv: cannot link %s and %s\n", - source, target); - 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; } + error("%s: unknown file type %o", source, s1.st_mode); + return (1); + +cleanup: if (unlink(source) < 0) { - fprintf(stderr, "mv: ?? cannot unlink %s\n", source); - return(1); + Perror2(source, "Cannot unlink"); + return (1); } - return(0); + return (0); } -char * -pname(name) -register char *name; +/*VARARGS*/ +query(prompt, a1, a2) + char *a1; { - register c; - register char *p, *q; - static char buf[MAXN]; - - p = q = buf; - while (c = *p++ = *name++) - if (c == DELIM) - q = p-1; - if (q == buf && *q == DELIM) - q++; - *q = 0; - return buf[0]? buf : DOT; + 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 *name; { register char *p; @@ -298,42 +262,36 @@ register char *name; return name; } -check(spth, dinode) -char *spth; -ino_t dinode; +/*VARARGS*/ +error(fmt, a1, a2) + char *fmt; { - char nspth[MAXN]; - struct stat sbuf; - sbuf.st_ino = 0; + fprintf(stderr, "mv: "); + fprintf(stderr, fmt, a1, a2); + fprintf(stderr, "\n"); +} - strcpy(nspth, spth); - while (sbuf.st_ino != ROOTINO) { - if (stat(nspth, &sbuf) < 0) { - fprintf(stderr, "mv: cannot access %s\n", nspth); - return(1); - } - if (sbuf.st_ino == dinode) { - fprintf(stderr, "mv: cannot move a directory into itself\n"); - return(1); - } - if (strlen(nspth) > MAXN-2-sizeof(DOTDOT)) { - fprintf(stderr, "mv: name too long\n"); - return(1); - } - strcat(nspth, SDELIM); - strcat(nspth, DOTDOT); - } - return(0); +Perror(s) + char *s; +{ + char buf[MAXPATHLEN + 10]; + + (void)sprintf(buf, "mv: %s", s); + perror(buf); +} + +Perror2(s1, s2) + char *s1, *s2; +{ + char buf[MAXPATHLEN + 20]; + + (void)sprintf(buf, "mv: %s: %s", s1, s2); + perror(buf); } -chkdot(s) -register char *s; +usage() { - do { - if (strcmp(dname(s), DOTDOT) == 0) - return(1); - s = pname(s); - } while (strcmp(s, DOT) != 0 && strcmp(s, SDELIM) != 0); - return(0); + fputs("usage: mv [-if] file1 file2 or mv [-if] file/directory ... directory\n", stderr); + exit(1); }