1 /* Copyright 1986-1992 Emmet P. Gray.
2 * Copyright 1994,1996-2002,2007-2009 Alain Knaff.
3 * This file is part of mtools.
5 * Mtools is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * Mtools is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
19 * Copy an MSDOS files to and from Unix
26 #include "sysincludes.h"
32 #include "nameclash.h"
38 * Preserve the file modification times after the fclose()
41 static void set_mtime(const char *target, time_t mtime)
43 if (target && strcmp(target, "-") && mtime != 0L) {
50 utimes((char *)target, tv);
56 utbuf.modtime = mtime;
57 utime(target, &utbuf);
64 typedef struct Arg_t {
66 int preserveAttributes;
81 static int _unix_write(MainParam_t *mp, int needfilter, const char *unixFile);
83 /* Write the Unix file */
84 static int unix_write(MainParam_t *mp, int needfilter)
86 Arg_t *arg=(Arg_t *) mp->arg;
89 return _unix_write(mp, needfilter, "-");
91 char *unixFile = mpBuildUnixFilename(mp);
97 ret = _unix_write(mp, needfilter, unixFile);
104 /* Write the Unix file */
105 static int _unix_write(MainParam_t *mp, int needfilter, const char *unixFile)
107 Arg_t *arg=(Arg_t *) mp->arg;
109 Stream_t *File=mp->File;
110 Stream_t *Target, *Source;
111 struct MT_STAT stbuf;
115 File->Class->get_data(File, &mtime, 0, 0, 0);
117 if (!arg->preserveTime)
120 /* if we are creating a file, check whether it already exists */
122 if (!arg->nowarn && &arg->type && !access(unixFile, 0)){
124 fprintf(stderr, "File \"%s\" exists. To overwrite, try again, and explicitly specify target directory\n",unixFile);
128 /* sanity checking */
129 if (!MT_STAT(unixFile, &stbuf)) {
130 struct MT_STAT srcStbuf;
131 int sFd; /* Source file descriptor */
132 if(!S_ISREG(stbuf.st_mode)) {
133 fprintf(stderr,"\"%s\" is not a regular file\n",
140 fprintf(stderr, "Not ok Unix file ==> good\n");
142 if((!MT_FSTAT(sFd, &srcStbuf)) &&
143 stbuf.st_dev == srcStbuf.st_dev &&
144 stbuf.st_ino == srcStbuf.st_ino) {
145 fprintf(stderr, "Attempt to copy file on itself\n");
150 if( ask_confirmation("File \"%s\" exists, overwrite (y/n) ? ",
158 if(!arg->type && arg->verbose) {
159 fprintf(stderr,"Copying ");
160 mpPrintFilename(stderr,mp);
161 fprintf(stderr,"\n");
168 if ((Target = SimpleFileOpen(0, 0, unixFile,
169 O_WRONLY | O_CREAT | O_TRUNC,
172 if(needfilter && arg->textmode){
173 Source = open_filter(COPY(File),arg->convertCharset);
180 ret = copyfile(Source, Target);
189 set_mtime(unixFile, mtime);
192 fprintf(stderr,"%s\n", errmsg);
197 static int makeUnixDir(char *filename)
200 #ifndef OS_mingw32msvc
205 if(errno == EEXIST) {
207 if(MT_STAT(filename, &buf) < 0)
209 if(S_ISDIR(buf.st_mode))
216 /* Copy a directory to Unix */
217 static int unix_copydir(direntry_t *entry, MainParam_t *mp)
219 Arg_t *arg=(Arg_t *) mp->arg;
221 Stream_t *File=mp->File;
225 if (!arg->recursive && mp->basenameHasWildcard)
228 File->Class->get_data(File, &mtime, 0, 0, 0);
229 if (!arg->preserveTime)
231 if(!arg->type && arg->verbose) {
232 fprintf(stderr,"Copying ");
233 fprintPwd(stderr, entry,0);
234 fprintf(stderr, "\n");
238 unixFile = mpBuildUnixFilename(mp);
243 if(arg->type || !*mpPickTargetName(mp) || !makeUnixDir(unixFile)) {
247 newArg.mp.arg = (void *) &newArg;
248 newArg.mp.unixTarget = unixFile;
249 newArg.mp.targetName = 0;
250 newArg.mp.basenameHasWildcard = 1;
252 ret = mp->loop(File, &newArg.mp, "*");
253 set_mtime(unixFile, mtime);
255 return ret | GOT_ONE;
259 "Failure to make directory %s\n",
266 static int dos_to_unix(direntry_t *entry UNUSEDP, MainParam_t *mp)
268 return unix_write(mp, 1);
272 static int unix_to_unix(MainParam_t *mp)
274 return unix_write(mp, 0);
278 static int directory_dos_to_unix(direntry_t *entry, MainParam_t *mp)
280 return unix_copydir(entry, mp);
284 * Open the named file for read, create the cluster chain, return the
285 * directory structure or NULL on error.
287 static int writeit(struct dos_name_t *dosname,
296 mt_size_t filesize, newsize;
297 Arg_t *arg = (Arg_t *) arg0;
301 if (arg->mp.File->Class->get_data(arg->mp.File,
302 & date, &filesize, &type, 0) < 0 ){
303 fprintf(stderr, "Can't stat source file\n");
307 if(fileTooBig(filesize)) {
308 fprintf(stderr, "File \"%s\" too big\n", longname);
314 fprintf(stderr, "\"%s\" is a directory\n", longname);
318 /*if (!arg->single || arg->recursive)*/
320 fprintf(stderr,"Copying %s\n", longname);
325 if (!getfreeMinBytes(arg->mp.targetDir, filesize))
328 /* preserve mod time? */
329 if (arg->preserveTime)
334 mk_entry(dosname, arg->attr, 1, 0, now, &entry->dir);
336 Target = OpenFileByDirentry(entry);
338 fprintf(stderr,"Could not open Target\n");
341 if (arg->needfilter & arg->textmode)
342 Target = open_filter(Target,arg->convertCharset);
346 ret = copyfile(arg->mp.File, Target);
347 GET_DATA(Target, 0, &newsize, 0, &fat);
349 if (arg->needfilter & arg->textmode)
350 newsize++; /* ugly hack: we gathered the size before the Ctrl-Z
351 * was written. Increment it manually */
353 fat_free(arg->mp.targetDir, fat);
356 mk_entry(dosname, arg->attr, fat, truncBytes32(newsize),
364 static int dos_write(direntry_t *entry, MainParam_t *mp, int needfilter)
365 /* write a messy dos file to another messy dos file */
368 Arg_t * arg = (Arg_t *) (mp->arg);
369 const char *targetName = mpPickTargetName(mp);
371 if(entry && arg->preserveAttributes)
372 arg->attr = entry->dir.attr;
374 arg->attr = ATTR_ARCHIVE;
376 arg->needfilter = needfilter;
377 if (entry && mp->targetDir == entry->Dir){
378 arg->ch.ignore_entry = -1;
379 arg->ch.source = entry->entry;
381 arg->ch.ignore_entry = -1;
384 result = mwrite_one(mp->targetDir, targetName, 0,
385 writeit, (void *)arg, &arg->ch);
392 static Stream_t *subDir(Stream_t *parent, const char *filename)
395 initializeDirentry(&entry, parent);
397 switch(vfat_lookup(&entry, filename, -1, ACCEPT_DIR, 0, 0)) {
399 return OpenFileByDirentry(&entry);
402 default: /* IO Error */
407 static int dos_copydir(direntry_t *entry, MainParam_t *mp)
408 /* copyes a directory to Dos */
410 Arg_t * arg = (Arg_t *) (mp->arg);
415 const char *targetName = mpPickTargetName(mp);
417 if (!arg->recursive && mp->basenameHasWildcard)
420 if(entry && isSubdirOf(mp->targetDir, mp->File)) {
421 fprintf(stderr, "Cannot recursively copy directory ");
422 fprintPwd(stderr, entry,0);
423 fprintf(stderr, " into one of its own subdirectories ");
424 fprintPwd(stderr, getDirentry(mp->targetDir),0);
425 fprintf(stderr, "\n");
429 if (arg->mp.File->Class->get_data(arg->mp.File,
430 & date, 0, 0, 0) < 0 ){
431 fprintf(stderr, "Can't stat source file\n");
435 if(!arg->type && arg->verbose)
436 fprintf(stderr,"Copying %s\n", mpGetBasename(mp));
438 if(entry && arg->preserveAttributes)
439 arg->attr = entry->dir.attr;
443 if (entry && (mp->targetDir == entry->Dir)){
444 arg->ch.ignore_entry = -1;
445 arg->ch.source = entry->entry;
447 arg->ch.ignore_entry = -1;
451 /* preserve mod time? */
452 if (arg->preserveTime)
458 newArg.mp.arg = &newArg;
459 newArg.mp.targetName = 0;
460 newArg.mp.basenameHasWildcard = 1;
462 /* maybe the directory already exist. Use it */
463 newArg.mp.targetDir = subDir(mp->targetDir, targetName);
464 if(!newArg.mp.targetDir)
465 newArg.mp.targetDir = createDir(mp->targetDir,
470 newArg.mp.targetDir = mp->targetDir;
472 if(!newArg.mp.targetDir)
475 ret = mp->loop(mp->File, &newArg.mp, "*");
477 FREE(&newArg.mp.targetDir);
478 return ret | GOT_ONE;
482 static int dos_to_dos(direntry_t *entry, MainParam_t *mp)
484 return dos_write(entry, mp, 0);
487 static int unix_to_dos(MainParam_t *mp)
489 return dos_write(0, mp, 1);
492 static void usage(int ret) NORETURN;
493 static void usage(int ret)
496 "Mtools version %s, dated %s\n", mversion, mdate);
498 "Usage: %s [-spatnmQVBT] [-D clash_option] sourcefile targetfile\n", progname);
500 " %s [-spatnmQVBT] [-D clash_option] sourcefile [sourcefiles...] targetdirectory\n",
505 void mcopy(int argc, char **argv, int mtype)
511 /* get command line options */
513 init_clash_handling(& arg.ch);
515 /* get command line options */
517 arg.preserveTime = 0;
518 arg.preserveAttributes = 0;
522 arg.convertCharset = 0;
525 if(helpFlag(argc, argv))
527 while ((c = getopt(argc, argv, "i:abB/sptTnmvQD:oh")) != EOF) {
530 set_cmd_line_image(optarg);
537 arg.preserveAttributes = 1;
540 arg.convertCharset = 1;
549 arg.preserveTime = 1;
562 handle_clash_options(&arg.ch, c);
565 if(handle_clash_options(&arg.ch, *optarg))
577 if (argc - optind < 1)
581 arg.mp.lookupflags = ACCEPT_PLAIN | ACCEPT_DIR | DO_OPEN | NO_DOTS;
582 arg.mp.fast_quit = fastquit;
583 arg.mp.arg = (void *) &arg;
584 arg.mp.openflags = O_RDONLY;
587 /* last parameter is "-", use mtype mode */
588 if(!mtype && !strcmp(argv[argc-1], "-")) {
589 arg.type = mtype = 1;
594 /* Mtype = copying to stdout */
595 arg.mp.targetName = strdup("-");
596 arg.mp.unixTarget = strdup("");
597 arg.mp.callback = dos_to_unix;
598 arg.mp.dirCallback = unix_copydir;
599 arg.mp.unixcallback = unix_to_unix;
602 if (argc - optind == 1) {
603 /* copying to the current directory */
607 /* target is the last item mentioned */
612 target_lookup(&arg.mp, target);
613 if(!arg.mp.targetDir && !arg.mp.unixTarget) {
614 fprintf(stderr,"Bad target %s\n", target);
618 /* callback functions */
619 if(arg.mp.unixTarget) {
620 arg.mp.callback = dos_to_unix;
621 arg.mp.dirCallback = directory_dos_to_unix;
622 arg.mp.unixcallback = unix_to_unix;
624 arg.mp.dirCallback = dos_copydir;
625 arg.mp.callback = dos_to_dos;
626 arg.mp.unixcallback = unix_to_dos;
630 exit(main_loop(&arg.mp, argv + optind, argc - optind));