summaryrefslogtreecommitdiff
path: root/mcopy.c
diff options
context:
space:
mode:
Diffstat (limited to 'mcopy.c')
-rw-r--r--mcopy.c631
1 files changed, 631 insertions, 0 deletions
diff --git a/mcopy.c b/mcopy.c
new file mode 100644
index 0000000..3b4d4cf
--- /dev/null
+++ b/mcopy.c
@@ -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));
+}