BSD 4 release
[unix-history] / usr / src / cmd / mv.c
static char *sccsid = "@(#)mv.c 4.1 (Berkeley) 10/6/80";
/*
* mv file1 file2
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <signal.h>
#define DOT "."
#define DOTDOT ".."
#define DELIM '/'
#define SDELIM "/"
#define MAXN 100
#define MODEBITS 07777
#define ROOTINO 2
char *pname();
char *sprintf();
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 */
main(argc, argv)
register char *argv[];
{
register i, r;
/* get the flag(s) */
if (argc < 2)
goto usage;
if (*argv[1] == '-') {
argc--;
while (*++argv[1] != '\0')
switch (*argv[1]) {
/* interactive mode */
case 'i':
iflag++;
break;
/* force moves */
case 'f':
fflag++;
break;
/* don't live with bad options */
default:
goto usage;
}
argv++;
}
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 f1 f2; or mv d1 d2; or mv f1 ... fn d1\n");
return(1);
}
move(source, target)
char *source, *target;
{
register c, i;
int status;
char buf[MAXN];
if (stat(source, &s1) < 0) {
fprintf(stderr, "mv: cannot access %s\n", source);
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;
}
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 (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);
}
}
}
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 (unlink(source) < 0) {
fprintf(stderr, "mv: cannot unlink %s\n", source);
return(1);
}
return(0);
}
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);
} else if (iflag && !fflag) {
fprintf(stderr, "remove %s? ", target);
cc = c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
if (cc != 'y')
return(1);
}
if (strlen(target) > MAXN-DIRSIZ-2) {
fprintf(stderr, "mv :target name too long\n");
return(1);
}
strcpy(buf, target);
target = buf;
strcat(buf, SDELIM);
strcat(buf, dname(source));
if (stat(target, &s2) >= 0) {
fprintf(stderr, "mv: %s exists\n", buf);
return(1);
}
}
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 (access(source, 2) < 0) {
fprintf(stderr, "mv: no write access to %s\n", 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];
if (chkdot(source) || chkdot(target)) {
fprintf(stderr, "mv: Sorry, path names including %s aren't allowed\n", DOTDOT);
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);
}
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);
}
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);
}
return(0);
}
if (link(source, target) < 0) {
fprintf(stderr, "mv: cannot link %s and %s\n",
source, target);
return(1);
}
if (unlink(source) < 0) {
fprintf(stderr, "mv: ?? cannot unlink %s\n", source);
return(1);
}
return(0);
}
char *
pname(name)
register char *name;
{
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;
}
char *
dname(name)
register char *name;
{
register char *p;
p = name;
while (*p)
if (*p++ == DELIM && *p)
name = p;
return name;
}
check(spth, dinode)
char *spth;
ino_t dinode;
{
char nspth[MAXN];
struct stat sbuf;
sbuf.st_ino = 0;
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);
}
chkdot(s)
register char *s;
{
do {
if (strcmp(dname(s), DOTDOT) == 0)
return(1);
s = pname(s);
} while (strcmp(s, DOT) != 0 && strcmp(s, SDELIM) != 0);
return(0);
}