Dear Rob.

Attached file is "mv" command.
This file implements -f, -i and -n options.
Just put it into "toys" directory and make.

Please let me know your opinion.

Thanks.
Munsoo.
/* vi: set sw=4 ts=4:
 *
 * mv.c - Move file / Directory.
 *
 * Copyright 2012 Ranjan Kumar <[email protected]>
 *
 * Reviewed by Munsoo Kim <[email protected]>
 *
 * See http://www.opengroup.org/onlinepubs/009695399/utilities/mv.html
 *
USE_MV(NEWTOY(mv, "<2fin", TOYFLAG_BIN))
config MV
    bool "mv"
    default y
    help
      usage: mv [-fin] SOURCE DEST
             mv [-fin] SOURCE... DIRECTORY

      Rename SOURCE to DEST or Move SOURCES(s) to DIRECTORY.

        -f  Don't Prompt before overwrite
        -i  interactive, prompt before overwrite
        -n  Don't overwrite an existing file
*/

#include "toys.h"
#include <sys/time.h>

#define FLAG_n 1
#define FLAG_i 2
#define FLAG_f 4


DEFINE_GLOBALS(
	char *destname;
	int destisdir;
	int destisnew;
	int keep_symlinks;
)

#define TT this.mv
//Function Declaration
int   do_copy(char *, char *, struct stat *);
int	do_move(char *, char *);
int	do_fastcopy(char *, char *, struct stat *);
static int remove_file(const char *Path);


void mv_main(void) {
	//Local Variable Declaration
	char *srcname, *destname;
	struct stat sb;
	int rval;
	size_t pathlen, len;
	char destpath[PATH_MAX + 1];
	char *p, *endp;

	int Argc;
    char **Argv;

    /*Make sure stdin is a terminal.*/
    if (!isatty (STDIN_FILENO)) {
		toys.exitval = EXIT_FAILURE;
		perror_exit("Not a terminal.");
    }

    //Grab target argument. (Guaranteed to be there due to "<2" above.)
    destname = toys.optargs[toys.optc - 1];
	TT.destname = toys.optargs[toys.optc - 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(destname, &sb)) || (!S_ISDIR(sb.st_mode)) ) {
		srcname  = toys.optargs[toys.optc - 2];
		exit(do_move(srcname, destname));
	}

	/* It's a directory, move each file into it.*/
	pathlen = strlen(destname);
	if(pathlen >= sizeof(destpath)) {
		toys.exitval = 1;
		perror_exit("'%s': destination pathname too long", destname);
	}
	strcpy(destpath, destname);
	if(destpath == NULL) {
		toys.exitval = 1;
		perror_exit("Destination Path is NULL");
	}

	//Add '/' at the end of destination path (if not present)
	endp = &destpath[pathlen];
	if (!pathlen || *(endp - 1) != '/') {
		*endp++ = '/';
		++pathlen;
	}

	//Get number of arguments.
	Argc = toys.optc;
    Argv = toys.optargs;

	for (rval = 0; --Argc; ++Argv) {
		p = *Argv + strlen(*Argv) - 1;
        while (*p == '/' && p != *Argv)
            *p-- = '\0';
        if ((p = strrchr(*Argv, '/')) == NULL)
            p = *Argv;
        else
            ++p;

        if ((pathlen + (len = strlen(p))) >= PATH_MAX) {
            perror_msg("'%s': destination pathname too long", *Argv);
			rval = 1;
        } else {
            memmove(endp, p, len + 1);
			if (do_move(*Argv, destpath))
                rval = 1;
        }
    }
	exit(rval);
	/* NOTREACHED */
	return;
}//End of main function...

int do_move(char *source, char *dest) {
	//Local Variable Declaration.
	struct stat sb;

	/* Conditions:
 	 * (1)	If the destination path exists and '-f' option is not set
	 *		The following conditions are valid:
 	 *
 	 *		(a) The permissions of the destination path do not permit writing
 	 *			and the standard input is a terminal.
 	 *
 	 *		(b) The -i option is set.
	 *
  	 *	The mv utility shall write a prompt to standard error and
  	 *	read a line from standard input. If the response is not
  	 *	affirmative, mv shall do nothing more with the current source file...
  	 *
 	 */
	if( !(toys.optflags & FLAG_f)  && !access(dest, F_OK) ){
		int ask = 1;
		int ch;

		if(toys.optflags & FLAG_n) {
			return 0;
		}
		if(toys.optflags & FLAG_i) {
			if(access(source, F_OK)) {
				perror_msg("rename '%s'", source);
				toys.exitval = 1;
				return toys.exitval;
			}
			(void)fprintf(stderr, "overwrite %s? ", dest);
		}
		else if(access(dest, W_OK) && (!stat(dest, &sb)) ) {
			if(access(source, F_OK)) {
					perror_msg("rename '%s'", source);
	                toys.exitval = 1;
    	            return toys.exitval;
				}
			(void)fprintf(stderr, "override %ud/%ud for %s? ", sb.st_uid, sb.st_gid, dest);
		} //end of elseif
		else
			ask = 0;
		if(ask) {
			if(((ch = getchar()) != EOF) && (ch != '\n')) {
				int ch1;
				while(((ch1 = getchar()) != EOF) && (ch1 != '\n'))
					continue;
			}
			if((ch != 'y') && (ch != 'Y'))
				return 0;
		}
	}//End of condition 1....

	/*Conditions:
 	 * (2)	If rename() succeeds, mv shall do nothing more with the
 	 *		current source file.  If it fails for any other reason than
 	 *		EXDEV, mv shall write a diagnostic message to the standard
 	 *		error and do nothing more with the current source file.
 	 *
 	 * (3)	If the destination path exists, and it is a file of type
 	 *		directory and source_file is not a file of type directory,
 	 *		or it is a file not of type directory, and source file is
 	 *		a file of type directory, mv shall write a diagnostic
 	 *		message to standard error, and do nothing more with the
 	 *		current source file...
 	 */
	if (!rename(source, dest)) {
		return (0);
	}
	if (errno != EXDEV) {
		 perror_msg("rename '%s' to '%s'", source, dest);
    	toys.exitval = 1;
        return toys.exitval;
	}//End of Condition 2 & 3......

	/*Conditions:
 	 * (4)	If the destination path exists, mv shall attempt to remove it.
 	 * 		If this fails for any reason, mv shall write a diagnostic
 	 * 		message to the standard error and do nothing more with the
 	 * 		current source file...
 	 */
	if (!lstat(dest, &sb)) {
		if ((S_ISDIR(sb.st_mode)) ? rmdir(dest) : unlink(dest)) {
			perror_msg("cannot remove '%s'", dest);
         	toys.exitval = 1;
         	return toys.exitval;
		}
	}//End of Condition 4.....

	TT.destisdir++;
	/*Conditions:
 	 * (5)	The file hierarchy rooted in source_file shall be duplicated
 	 * 		as a file hierarchy rooted in the destination path...
 	 */
	if (lstat(source, &sb)) {
		perror_msg("'%s'", source);
        toys.exitval = 1;
        return toys.exitval;
	}//End of Condition 5.....

	//For the regular file..
	if(S_ISREG(sb.st_mode)) {
		return (do_fastcopy(source, dest, &sb));
	}
	else {
		return (do_copy(source, dest, &sb));
	}
}//End of do_move function.....


int do_fastcopy(char *source, char *dest, struct stat *sbp) {
	struct timeval tval[2];
	static u_int blen;
	static char *bp;
	int nread, source_fd, dest_fd;

	if ((source_fd = open(source, O_RDONLY, 0)) < 0) {
		perror_msg("'%s'", source);
		return (1);
	}

	if ((dest_fd = open(dest, O_CREAT | O_TRUNC | O_WRONLY, sbp->st_mode)) < 0) {
		perror_msg("'%s'", dest);
		(void)close(source_fd);
		return (1);
	}
	if (!blen && !(bp = malloc(blen = sbp->st_blksize))) {
		perror_msg(NULL);
		return (1);
	}
	while ((nread = read(source_fd, bp, blen)) > 0)
		if (write(dest_fd, bp, nread) != nread) {
			perror_msg("'%s'", dest);
			goto err;
		}
	if (nread < 0) {
		perror_msg("'%s'", source);
err:
		if (unlink(dest))
			perror_msg("'%s': remove", dest);
		(void)close(source_fd);
		(void)close(dest_fd);
		return (1);
	}
	(void)close(source_fd);
#ifdef BSD4_4
	TIMESPEC_TO_TIMEVAL(&tval[0], &sbp->st_atimespec);
	TIMESPEC_TO_TIMEVAL(&tval[1], &sbp->st_mtimespec);
#else
	tval[0].tv_sec = sbp->st_atime;
	tval[1].tv_sec = sbp->st_mtime;
	tval[0].tv_usec = 0;
	tval[1].tv_usec = 0;
#endif
#ifdef __SVR4
	if (utimes(dest, tval))
#else
	if (futimes(dest_fd, tval))
#endif
		perror_msg("'%s': set times", dest);
	if (fchown(dest_fd, sbp->st_uid, sbp->st_gid)) {
		if (errno != EPERM)
			perror_msg("'%s': set owner/group", dest);
		sbp->st_mode &= ~(S_ISUID | S_ISGID);
	}
	if (fchmod(dest_fd, sbp->st_mode))
		perror_msg("'%s': set mode", dest);

	if (close(dest_fd)) {
		perror_msg("'%s'", dest);
		return (1);
	}

	if (unlink(source)) {
		perror_msg("'%s': remove", source);
		return (1);
	}
	return (0);
}

static void cp_file(char *src, char *dst, struct stat *srcst) {

	int fdout = -1;

	// -i flag is specified and dst file exists.
	if ((toys.optflags&FLAG_i) && !access(dst, R_OK) && !yesno("cp: overwrite", 1))
		return;

	if (S_ISDIR(srcst->st_mode)) {
		struct stat st2;

		if ((mkdir(dst, srcst->st_mode | 0200) && errno != EEXIST)
			|| 0>(fdout=open(dst, 0)) || fstat(fdout, &st2)
			|| !S_ISDIR(st2.st_mode))
		{
			perror_exit("can't create directory '%s'", dst);
		}
	}
	else if (S_ISLNK(srcst->st_mode)) {
		char *link = xreadlink(src);
		if (!link || symlink(link, dst)) perror_msg("link '%s'", dst);
		free(link);
		return;
	}
	else {
		int fdin, i;
		fdin = xopen(src, O_RDONLY);
		for (i=2 ; i; i--) {
			fdout = open(dst, O_RDWR|O_CREAT|O_TRUNC, srcst->st_mode);
			if (fdout>=0 || !(toys.optflags & FLAG_f)) break;
			unlink(dst);
		}
		if (fdout<0) perror_exit("%s", dst);
		xsendfile(fdin, fdout);
		close(fdin);
		unlink(src);
	}
	xclose(fdout);
	return;
}

static int cp_node(struct dirtree *node)
{    
    char *source = dirtree_path(node, 0);
	char *s = source+strlen(source);
	struct dirtree *n;

	// Find appropriate chunk of source for destination.
	n = node;
	if (!TT.destisdir) n = n->parent;
	for (;;n = n->parent) {
		while (s!=source) {
			if (*(--s)=='/') break;
		}
		if (!n) break;
	}
	if (s != source) s++;

	s = xmsprintf("%s/%s", TT.destname, s);
	cp_file(source, s, &(node->st));
	free(s);
	return 0;
}

int do_copy(char *source, char *dest, struct stat *st) {
	int status = 0;

	if (S_ISDIR(st->st_mode)) {
		cp_file(source, dest, st);
		strncpy(toybuf, source, sizeof(toybuf)-1);
		toybuf[sizeof(toybuf)-1]=0;
		dirtree_read(toybuf, cp_node);
	}
	else
		cp_file(source, dest, st);
	status = remove_file(source);
	if(status != 0) {
		perror_msg("Can't remove directory '%s'", source);
		return status;
	}
	return status;
}

static int remove_file(const char *Path) {
	struct stat Path_stat;

	/* is it a file or directory? */
	if (lstat(Path, &Path_stat) < 0) {
		if(errno != ENOENT) {
			perror_msg("can't stat '%s'", Path);
        	return -1;
		}
		if( !(toys.optflags & FLAG_f)) {
			perror_msg("Can't Remove '%s'", Path);
			return -1;
		}
		return 0;
	}

	/* a directory, so open handle */
	if(S_ISDIR(Path_stat.st_mode)) {
		DIR *dir;
    	struct dirent *de;
    	int Status = 0;

		dir = opendir(Path);
    	if (dir == NULL) {
        	return -1;
		}
		/* recurse over components */
		errno = 0;
		while((de = readdir(dir)) != NULL) {
    	    char dn[PATH_MAX];
        	if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
				continue;
			}
	        sprintf(dn, "%s/%s", Path, de->d_name);
    	    if (remove_file(dn) < 0) {
        	    Status = 1;
				break;
	        }
			errno = 0;
    	}
		/* in case readdir or unlink_recursively failed */
		if (Status || errno < 0) {
        	int save = errno;
        	closedir(dir);
        	errno = save;
        	return -1;
    	}

		/* close directory handle */
		if (closedir(dir) < 0) {
			perror_msg("Can't close '%s'", Path);
    	    return -1;
		}

		/* delete target directory */
		if(rmdir(Path) < 0) {
			perror_msg("Can't remove directory '%s'",Path);
		}
		return Status;
	}

	/* a file, so unlink it... */
	if (!S_ISDIR(Path_stat.st_mode)){
		if(unlink(Path) < 0) {
			perror_msg("Can't remove file '%s'", Path);
			return -1;
		}
	}
	return 0;
}

_______________________________________________
Toybox mailing list
[email protected]
http://lists.landley.net/listinfo.cgi/toybox-landley.net

Reply via email to