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
