diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2013-01-09 19:04:18 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2013-01-09 19:04:18 +0000 |
commit | af0c3edb9706e470b45a9c8dd6debcc9e2d543c2 (patch) | |
tree | 340ee9c0f1b504061d4206d05d9fcc265c1302eb /mcopy.c | |
download | mtools-master.tar.gz |
mtools-4.0.18HEADmtools-4.0.18master
Diffstat (limited to 'mcopy.c')
-rw-r--r-- | mcopy.c | 631 |
1 files changed, 631 insertions, 0 deletions
@@ -0,0 +1,631 @@ +/* Copyright 1986-1992 Emmet P. Gray. + * Copyright 1994,1996-2002,2007-2009 Alain Knaff. + * This file is part of mtools. + * + * Mtools is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Mtools is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mtools. If not, see <http://www.gnu.org/licenses/>. + * + * mcopy.c + * Copy an MSDOS files to and from Unix + * + */ + + +#define LOWERCASE + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "vfat.h" +#include "mainloop.h" +#include "plain_io.h" +#include "nameclash.h" +#include "file.h" +#include "fs.h" + + +/* + * Preserve the file modification times after the fclose() + */ + +static void set_mtime(const char *target, time_t mtime) +{ + if (target && strcmp(target, "-") && mtime != 0L) { +#ifdef HAVE_UTIMES + struct timeval tv[2]; + tv[0].tv_sec = mtime; + tv[0].tv_usec = 0; + tv[1].tv_sec = mtime; + tv[1].tv_usec = 0; + utimes((char *)target, tv); +#else +#ifdef HAVE_UTIME + struct utimbuf utbuf; + + utbuf.actime = mtime; + utbuf.modtime = mtime; + utime(target, &utbuf); +#endif +#endif + } + return; +} + +typedef struct Arg_t { + int recursive; + int preserveAttributes; + int preserveTime; + unsigned char attr; + char *path; + int textmode; + int needfilter; + int nowarn; + int verbose; + int type; + int convertCharset; + MainParam_t mp; + ClashHandling_t ch; + int noClobber; +} Arg_t; + +static int _unix_write(MainParam_t *mp, int needfilter, const char *unixFile); + +/* Write the Unix file */ +static int unix_write(MainParam_t *mp, int needfilter) +{ + Arg_t *arg=(Arg_t *) mp->arg; + + if(arg->type) + return _unix_write(mp, needfilter, "-"); + else { + char *unixFile = mpBuildUnixFilename(mp); + int ret; + if(!unixFile) { + printOom(); + return ERROR_ONE; + } + ret = _unix_write(mp, needfilter, unixFile); + free(unixFile); + return ret; + } +} + + +/* Write the Unix file */ +static int _unix_write(MainParam_t *mp, int needfilter, const char *unixFile) +{ + Arg_t *arg=(Arg_t *) mp->arg; + time_t mtime; + Stream_t *File=mp->File; + Stream_t *Target, *Source; + struct MT_STAT stbuf; + int ret; + char errmsg[80]; + + File->Class->get_data(File, &mtime, 0, 0, 0); + + if (!arg->preserveTime) + mtime = 0L; + + /* if we are creating a file, check whether it already exists */ + if(!arg->type) { + if (!arg->nowarn && &arg->type && !access(unixFile, 0)){ + if(arg->noClobber) { + fprintf(stderr, "File \"%s\" exists. To overwrite, try again, and explicitly specify target directory\n",unixFile); + return ERROR_ONE; + } + + /* sanity checking */ + if (!MT_STAT(unixFile, &stbuf)) { + struct MT_STAT srcStbuf; + int sFd; /* Source file descriptor */ + if(!S_ISREG(stbuf.st_mode)) { + fprintf(stderr,"\"%s\" is not a regular file\n", + unixFile); + + return ERROR_ONE; + } + sFd = get_fd(File); + if(sFd == -1) { + fprintf(stderr, "Not ok Unix file ==> good\n"); + } + if((!MT_FSTAT(sFd, &srcStbuf)) && + stbuf.st_dev == srcStbuf.st_dev && + stbuf.st_ino == srcStbuf.st_ino) { + fprintf(stderr, "Attempt to copy file on itself\n"); + return ERROR_ONE; + } + } + + if( ask_confirmation("File \"%s\" exists, overwrite (y/n) ? ", + unixFile)) { + return ERROR_ONE; + } + + } + } + + if(!arg->type && arg->verbose) { + fprintf(stderr,"Copying "); + mpPrintFilename(stderr,mp); + fprintf(stderr,"\n"); + } + + if(got_signal) { + return ERROR_ONE; + } + + if ((Target = SimpleFileOpen(0, 0, unixFile, + O_WRONLY | O_CREAT | O_TRUNC, + errmsg, 0, 0, 0))) { + ret = 0; + if(needfilter && arg->textmode){ + Source = open_filter(COPY(File),arg->convertCharset); + if (!Source) + ret = -1; + } else + Source = COPY(File); + + if (ret == 0 ) + ret = copyfile(Source, Target); + FREE(&Source); + FREE(&Target); + if(ret <= -1){ + if(!arg->type) + unlink(unixFile); + return ERROR_ONE; + } + if(!arg->type) + set_mtime(unixFile, mtime); + return GOT_ONE; + } else { + fprintf(stderr,"%s\n", errmsg); + return ERROR_ONE; + } +} + +static int makeUnixDir(char *filename) +{ + if(!mkdir(filename +#ifndef OS_mingw32msvc + , 0777 +#endif + )) + return 0; + if(errno == EEXIST) { + struct MT_STAT buf; + if(MT_STAT(filename, &buf) < 0) + return -1; + if(S_ISDIR(buf.st_mode)) + return 0; + errno = ENOTDIR; + } + return -1; +} + +/* Copy a directory to Unix */ +static int unix_copydir(direntry_t *entry, MainParam_t *mp) +{ + Arg_t *arg=(Arg_t *) mp->arg; + time_t mtime; + Stream_t *File=mp->File; + int ret; + char *unixFile; + + if (!arg->recursive && mp->basenameHasWildcard) + return 0; + + File->Class->get_data(File, &mtime, 0, 0, 0); + if (!arg->preserveTime) + mtime = 0L; + if(!arg->type && arg->verbose) { + fprintf(stderr,"Copying "); + fprintPwd(stderr, entry,0); + fprintf(stderr, "\n"); + } + if(got_signal) + return ERROR_ONE; + unixFile = mpBuildUnixFilename(mp); + if(!unixFile) { + printOom(); + return ERROR_ONE; + } + if(arg->type || !*mpPickTargetName(mp) || !makeUnixDir(unixFile)) { + Arg_t newArg; + + newArg = *arg; + newArg.mp.arg = (void *) &newArg; + newArg.mp.unixTarget = unixFile; + newArg.mp.targetName = 0; + newArg.mp.basenameHasWildcard = 1; + + ret = mp->loop(File, &newArg.mp, "*"); + set_mtime(unixFile, mtime); + free(unixFile); + return ret | GOT_ONE; + } else { + perror("mkdir"); + fprintf(stderr, + "Failure to make directory %s\n", + unixFile); + free(unixFile); + return ERROR_ONE; + } +} + +static int dos_to_unix(direntry_t *entry UNUSEDP, MainParam_t *mp) +{ + return unix_write(mp, 1); +} + + +static int unix_to_unix(MainParam_t *mp) +{ + return unix_write(mp, 0); +} + + +static int directory_dos_to_unix(direntry_t *entry, MainParam_t *mp) +{ + return unix_copydir(entry, mp); +} + +/* + * Open the named file for read, create the cluster chain, return the + * directory structure or NULL on error. + */ +static int writeit(struct dos_name_t *dosname, + char *longname, + void *arg0, + direntry_t *entry) +{ + Stream_t *Target; + time_t now; + int type, fat, ret; + time_t date; + mt_size_t filesize, newsize; + Arg_t *arg = (Arg_t *) arg0; + + + + if (arg->mp.File->Class->get_data(arg->mp.File, + & date, &filesize, &type, 0) < 0 ){ + fprintf(stderr, "Can't stat source file\n"); + return -1; + } + + if(fileTooBig(filesize)) { + fprintf(stderr, "File \"%s\" too big\n", longname); + return 1; + } + + if (type){ + if (arg->verbose) + fprintf(stderr, "\"%s\" is a directory\n", longname); + return -1; + } + + /*if (!arg->single || arg->recursive)*/ + if(arg->verbose) + fprintf(stderr,"Copying %s\n", longname); + if(got_signal) + return -1; + + /* will it fit? */ + if (!getfreeMinBytes(arg->mp.targetDir, filesize)) + return -1; + + /* preserve mod time? */ + if (arg->preserveTime) + now = date; + else + getTimeNow(&now); + + mk_entry(dosname, arg->attr, 1, 0, now, &entry->dir); + + Target = OpenFileByDirentry(entry); + if(!Target){ + fprintf(stderr,"Could not open Target\n"); + exit(1); + } + if (arg->needfilter & arg->textmode) + Target = open_filter(Target,arg->convertCharset); + + + + ret = copyfile(arg->mp.File, Target); + GET_DATA(Target, 0, &newsize, 0, &fat); + FREE(&Target); + if (arg->needfilter & arg->textmode) + newsize++; /* ugly hack: we gathered the size before the Ctrl-Z + * was written. Increment it manually */ + if(ret < 0 ){ + fat_free(arg->mp.targetDir, fat); + return -1; + } else { + mk_entry(dosname, arg->attr, fat, truncBytes32(newsize), + now, &entry->dir); + return 0; + } +} + + + +static int dos_write(direntry_t *entry, MainParam_t *mp, int needfilter) +/* write a messy dos file to another messy dos file */ +{ + int result; + Arg_t * arg = (Arg_t *) (mp->arg); + const char *targetName = mpPickTargetName(mp); + + if(entry && arg->preserveAttributes) + arg->attr = entry->dir.attr; + else + arg->attr = ATTR_ARCHIVE; + + arg->needfilter = needfilter; + if (entry && mp->targetDir == entry->Dir){ + arg->ch.ignore_entry = -1; + arg->ch.source = entry->entry; + } else { + arg->ch.ignore_entry = -1; + arg->ch.source = -2; + } + result = mwrite_one(mp->targetDir, targetName, 0, + writeit, (void *)arg, &arg->ch); + if(result == 1) + return GOT_ONE; + else + return ERROR_ONE; +} + +static Stream_t *subDir(Stream_t *parent, const char *filename) +{ + direntry_t entry; + initializeDirentry(&entry, parent); + + switch(vfat_lookup(&entry, filename, -1, ACCEPT_DIR, 0, 0)) { + case 0: + return OpenFileByDirentry(&entry); + case -1: + return NULL; + default: /* IO Error */ + return NULL; + } +} + +static int dos_copydir(direntry_t *entry, MainParam_t *mp) +/* copyes a directory to Dos */ +{ + Arg_t * arg = (Arg_t *) (mp->arg); + Arg_t newArg; + time_t now; + time_t date; + int ret; + const char *targetName = mpPickTargetName(mp); + + if (!arg->recursive && mp->basenameHasWildcard) + return 0; + + if(entry && isSubdirOf(mp->targetDir, mp->File)) { + fprintf(stderr, "Cannot recursively copy directory "); + fprintPwd(stderr, entry,0); + fprintf(stderr, " into one of its own subdirectories "); + fprintPwd(stderr, getDirentry(mp->targetDir),0); + fprintf(stderr, "\n"); + return ERROR_ONE; + } + + if (arg->mp.File->Class->get_data(arg->mp.File, + & date, 0, 0, 0) < 0 ){ + fprintf(stderr, "Can't stat source file\n"); + return ERROR_ONE; + } + + if(!arg->type && arg->verbose) + fprintf(stderr,"Copying %s\n", mpGetBasename(mp)); + + if(entry && arg->preserveAttributes) + arg->attr = entry->dir.attr; + else + arg->attr = 0; + + if (entry && (mp->targetDir == entry->Dir)){ + arg->ch.ignore_entry = -1; + arg->ch.source = entry->entry; + } else { + arg->ch.ignore_entry = -1; + arg->ch.source = -2; + } + + /* preserve mod time? */ + if (arg->preserveTime) + now = date; + else + getTimeNow(&now); + + newArg = *arg; + newArg.mp.arg = &newArg; + newArg.mp.targetName = 0; + newArg.mp.basenameHasWildcard = 1; + if(*targetName) { + /* maybe the directory already exist. Use it */ + newArg.mp.targetDir = subDir(mp->targetDir, targetName); + if(!newArg.mp.targetDir) + newArg.mp.targetDir = createDir(mp->targetDir, + targetName, + &arg->ch, arg->attr, + now); + } else + newArg.mp.targetDir = mp->targetDir; + + if(!newArg.mp.targetDir) + return ERROR_ONE; + + ret = mp->loop(mp->File, &newArg.mp, "*"); + if(*targetName) + FREE(&newArg.mp.targetDir); + return ret | GOT_ONE; +} + + +static int dos_to_dos(direntry_t *entry, MainParam_t *mp) +{ + return dos_write(entry, mp, 0); +} + +static int unix_to_dos(MainParam_t *mp) +{ + return dos_write(0, mp, 1); +} + +static void usage(int ret) NORETURN; +static void usage(int ret) +{ + fprintf(stderr, + "Mtools version %s, dated %s\n", mversion, mdate); + fprintf(stderr, + "Usage: %s [-spatnmQVBT] [-D clash_option] sourcefile targetfile\n", progname); + fprintf(stderr, + " %s [-spatnmQVBT] [-D clash_option] sourcefile [sourcefiles...] targetdirectory\n", + progname); + exit(ret); +} + +void mcopy(int argc, char **argv, int mtype) +{ + Arg_t arg; + int c, fastquit; + + + /* get command line options */ + + init_clash_handling(& arg.ch); + + /* get command line options */ + arg.recursive = 0; + arg.preserveTime = 0; + arg.preserveAttributes = 0; + arg.nowarn = 0; + arg.textmode = 0; + arg.verbose = 0; + arg.convertCharset = 0; + arg.type = mtype; + fastquit = 0; + if(helpFlag(argc, argv)) + usage(0); + while ((c = getopt(argc, argv, "i:abB/sptTnmvQD:oh")) != EOF) { + switch (c) { + case 'i': + set_cmd_line_image(optarg); + break; + case 's': + case '/': + arg.recursive = 1; + break; + case 'p': + arg.preserveAttributes = 1; + break; + case 'T': + arg.convertCharset = 1; + case 'a': + case 't': + arg.textmode = 1; + break; + case 'n': + arg.nowarn = 1; + break; + case 'm': + arg.preserveTime = 1; + break; + case 'v': + arg.verbose = 1; + break; + case 'Q': + fastquit = 1; + break; + case 'B': + case 'b': + batchmode = 1; + break; + case 'o': + handle_clash_options(&arg.ch, c); + break; + case 'D': + if(handle_clash_options(&arg.ch, *optarg)) + usage(1); + break; + case 'h': + usage(0); + case '?': + usage(1); + default: + break; + } + } + + if (argc - optind < 1) + usage(1); + + init_mp(&arg.mp); + arg.mp.lookupflags = ACCEPT_PLAIN | ACCEPT_DIR | DO_OPEN | NO_DOTS; + arg.mp.fast_quit = fastquit; + arg.mp.arg = (void *) &arg; + arg.mp.openflags = O_RDONLY; + arg.noClobber = 0; + + /* last parameter is "-", use mtype mode */ + if(!mtype && !strcmp(argv[argc-1], "-")) { + arg.type = mtype = 1; + argc--; + } + + if(mtype){ + /* Mtype = copying to stdout */ + arg.mp.targetName = strdup("-"); + arg.mp.unixTarget = strdup(""); + arg.mp.callback = dos_to_unix; + arg.mp.dirCallback = unix_copydir; + arg.mp.unixcallback = unix_to_unix; + } else { + const char *target; + if (argc - optind == 1) { + /* copying to the current directory */ + target = "."; + arg.noClobber = 1; + } else { + /* target is the last item mentioned */ + argc--; + target = argv[argc]; + } + + target_lookup(&arg.mp, target); + if(!arg.mp.targetDir && !arg.mp.unixTarget) { + fprintf(stderr,"Bad target %s\n", target); + exit(1); + } + + /* callback functions */ + if(arg.mp.unixTarget) { + arg.mp.callback = dos_to_unix; + arg.mp.dirCallback = directory_dos_to_unix; + arg.mp.unixcallback = unix_to_unix; + } else { + arg.mp.dirCallback = dos_copydir; + arg.mp.callback = dos_to_dos; + arg.mp.unixcallback = unix_to_dos; + } + } + + exit(main_loop(&arg.mp, argv + optind, argc - optind)); +} |