[comp.os.minix] New version of mv.c

ast@cs.vu.nl (Andy Tanenbaum) (07/26/87)

All previous versions of mv.c barfed on the command:  mkdir foo; mv foo bar
This one is thought to be better.  It should be owned by root with setuid on.

Andy Tanenbaum
----------------------------------mv.c-------------------------------------
/* mv - move files		Author: Adri Koppes 
 *
 * 4/25/87 - J. Paradis		Bug fixes for directory handling
 */

#include "signal.h"
#include "stat.h"

int     error = 0;
struct stat st, st1;

main (argc, argv)
int     argc;
char  **argv;
{
    char   *destdir;

    if (argc < 3) {
	std_err ("Usage: mv file1 file2 or mv dir1 dir2 or mv file1 file2 ... dir\n");
	exit (1);
    }
    if (argc == 3) {
	if (stat (argv[1], &st)) {
	    std_err ("mv: ");
	    std_err (argv[1]);
	    std_err (" doesn't exist\n");
	    exit (1);
	}

	move (argv[1], argv[2]);
    }
    else {
	destdir = argv[--argc];
	if (stat (destdir, &st)) {
	    std_err ("mv: target directory ");
	    std_err (destdir);
	    std_err (" doesn't exist\n");
	    exit(1);
	}
	if ((st.st_mode & S_IFMT) != S_IFDIR) {
	    std_err ("mv: target ");
	    std_err (destdir);
	    std_err (" not a directory\n");
	    exit (1);
	}
	while (--argc)
	    move (*++argv, destdir);
    }
    if (error) exit (1);
    exit(0);
}

move (old, new)
char   *old,
       *new;
{
    int     retval;
    char    name[64];

    /* It's too dangerous to fool with "." or ".." ! */
    if((strcmp(old, ".") == 0) || (strcmp(old, "..") == 0)) {
	cant(old);
    }

    /* Don't move a file to itself. */
    if (stat(old, &st)==0 && stat(new, &st1)==0 && st.st_dev == st1.st_dev &&
        st.st_ino == st1.st_ino)
	cant(old);

    if (!stat (new, &st))
	if((st.st_mode & S_IFMT) != S_IFDIR)
	    unlink (new);
    else {
	char *p, *rindex();

	if ((strlen(old) + strlen(new) + 2) > 64) {
		cant(old);
		error++;
		return;
	}
	strcpy(name, new);
	strcat(name, "/");
	p = rindex(old, '/');
	strcat(name, p ? p : old);
	new = name;
    }
    stat (old, &st);
    if (link (old, new))
	if ((st.st_mode & S_IFMT) != S_IFDIR) {
	    switch (fork ()) {
		case 0: 
		    setgid (getgid ());
		    setuid (getuid ());
		    execl ("/bin/cp", "cp", old, new, (char *) 0);
		    cant(old);
		case -1: 
		    std_err ("mv: can't fork\n");
		    exit (1);
		default:
		    wait (&retval);
		    if (retval)
			cant(old);
	    }
	} else
	    cant(old);

    /* If this was a directory that we moved, then we have
    ** to update its ".." entry (in case it was moved some-
    ** where else in the tree...)
    */
    if ((st.st_mode & S_IFMT) == S_IFDIR) {
	int	i;
	char	parent[64], dotdot[64];

	strcpy(parent, new);

	/* Determine the name for the parent of
	** the new name by counting back until we
	** hit a '/' or the begining of the string
	*/

	for(i = (strlen(parent) - 1); i > 0; i--) {
	    if(parent[i] == '/') break;
	}

	/* If there are no slashes, then the name is
	** in the current directory, so its parent
	** is ".".  Otherwise, the parent is the name
	** up to the last slash.
	*/
	if(i == 0) {
		strcpy(parent, ".");
	}
	else {
		/* null-terminate name at last slash */
		parent[i] = '\0';
	}

	/* Unlink the ".." entry */
	strcpy(dotdot, new);
	strcat(dotdot, "/..");
	unlink(dotdot);

	/* Now link it to its parent */
	link(parent, dotdot);
    }

    utime (new, &st.st_atime);
    unlink(old);
}

cant(name)
char *name;
{
	std_err("mv: can't move ");
	std_err (name);
	std_err ("\n");
	exit (1);
}