date and time created 88/10/18 22:07:11 by bostic
[unix-history] / usr / src / bin / mv / mv.c
index 274abdd..69801c4 100644 (file)
-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 <stdio.h>
-#include <sys/types.h>
+#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 <sys/param.h>
 #include <sys/stat.h>
 #include <sys/stat.h>
-#include <signal.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <stdio.h>
+#include <errno.h>
 
 
-#define        DOT     "."
-#define        DOTDOT  ".."
 #define        DELIM   '/'
 #define        DELIM   '/'
-#define SDELIM "/"
-#define        MAXN    100
 #define MODEBITS 07777
 #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();
 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)
 
 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)
 
        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<argc-1; i++)
-               r |= move(argv[i], argv[argc-1]);
-       return(r);
-usage:
-       fprintf(stderr, "usage: mv [-if] f1 f2; or mv [-if] d1 d2; or mv [-if] f1 ... fn d1\n");
-       return(1);
+       (void)sprintf(target, "%s/%s", dest, shortname);
+       return (move(src, target));
 }
 
 move(source, target)
 }
 
 move(source, target)
-char *source, *target;
+       char *source, *target;
 {
 {
-       register c, i;
-       int     status;
-       char    buf[MAXN];
+       int targetexists;
+       struct stat s1, s2;
 
 
-       if (stat(source, &s1) < 0) {
-               fprintf(stderr, "mv: cannot access %s\n", source);
-               return(1);
+       if (lstat(source, &s1) < 0) {
+               Perror2(source, "Cannot access");
+               return (1);
        }
        }
-       if ((s1.st_mode & S_IFMT) == S_IFDIR) {
-               fprintf(stderr, "mv: directory rename only\n");
-               return(1);
-       }
-       if (stat(target, &s2) >= 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) {
        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)
 }
 
 char *
 dname(name)
-register char *name;
+       register char *name;
 {
        register char *p;
 
 {
        register char *p;
 
@@ -298,42 +262,36 @@ register char *name;
        return 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);
 }
 }