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(direntry_t *entry, MainParam_t *mp, int needfilter,
82 const char *unixFile);
84 /* Write the Unix file */
85 static int unix_write(direntry_t *entry, MainParam_t *mp, int needfilter)
87 Arg_t *arg=(Arg_t *) mp->arg;
90 return _unix_write(entry, mp, needfilter, "-");
92 char *unixFile = mpBuildUnixFilename(mp);
98 ret = _unix_write(entry, mp, needfilter, unixFile);
105 /* Write the Unix file */
106 static int _unix_write(direntry_t *entry, MainParam_t *mp, int needfilter,
107 const char *unixFile)
109 Arg_t *arg=(Arg_t *) mp->arg;
111 Stream_t *File=mp->File;
112 Stream_t *Target, *Source;
113 struct MT_STAT stbuf;
117 File->Class->get_data(File, &mtime, 0, 0, 0);
119 if (!arg->preserveTime)
122 /* if we are creating a file, check whether it already exists */
124 if (!arg->nowarn && &arg->type && !access(unixFile, 0)){
126 fprintf(stderr, "File \"%s\" exists. To overwrite, try again, and explicitly specify target directory\n",unixFile);
130 /* sanity checking */
131 if (!MT_STAT(unixFile, &stbuf)) {
132 struct MT_STAT srcStbuf;
133 int sFd; /* Source file descriptor */
134 if(!S_ISREG(stbuf.st_mode)) {
135 fprintf(stderr,"\"%s\" is not a regular file\n",
142 fprintf(stderr, "Not ok Unix file ==> good\n");
144 if((!MT_FSTAT(sFd, &srcStbuf)) &&
145 stbuf.st_dev == srcStbuf.st_dev &&
146 stbuf.st_ino == srcStbuf.st_ino) {
147 fprintf(stderr, "Attempt to copy file on itself\n");
152 if( ask_confirmation("File \"%s\" exists, overwrite (y/n) ? ",
160 if(!arg->type && arg->verbose) {
161 fprintf(stderr,"Copying ");
162 mpPrintFilename(stderr,mp);
163 fprintf(stderr,"\n");
170 if ((Target = SimpleFileOpen(0, 0, unixFile,
171 O_WRONLY | O_CREAT | O_TRUNC,
174 if(needfilter && arg->textmode){
175 Source = open_filter(COPY(File),arg->convertCharset);
182 ret = copyfile(Source, Target);
191 set_mtime(unixFile, mtime);
194 fprintf(stderr,"%s\n", errmsg);
199 static int makeUnixDir(char *filename)
202 #ifndef OS_mingw32msvc
207 if(errno == EEXIST) {
209 if(MT_STAT(filename, &buf) < 0)
211 if(S_ISDIR(buf.st_mode))
218 /* Copy a directory to Unix */
219 static int unix_copydir(direntry_t *entry, MainParam_t *mp)
221 Arg_t *arg=(Arg_t *) mp->arg;
223 Stream_t *File=mp->File;
227 if (!arg->recursive && mp->basenameHasWildcard)
230 File->Class->get_data(File, &mtime, 0, 0, 0);
231 if (!arg->preserveTime)
233 if(!arg->type && arg->verbose) {
234 fprintf(stderr,"Copying ");
235 fprintPwd(stderr, entry,0);
236 fprintf(stderr, "\n");
240 unixFile = mpBuildUnixFilename(mp);
245 if(arg->type || !*mpPickTargetName(mp) || !makeUnixDir(unixFile)) {
249 newArg.mp.arg = (void *) &newArg;
250 newArg.mp.unixTarget = unixFile;
251 newArg.mp.targetName = 0;
252 newArg.mp.basenameHasWildcard = 1;
254 ret = mp->loop(File, &newArg.mp, "*");
255 set_mtime(unixFile, mtime);
257 return ret | GOT_ONE;
261 "Failure to make directory %s\n",
268 static int dos_to_unix(direntry_t *entry, MainParam_t *mp)
270 return unix_write(entry, mp, 1);
274 static int unix_to_unix(MainParam_t *mp)
276 return unix_write(0, mp, 0);
280 static int directory_dos_to_unix(direntry_t *entry, MainParam_t *mp)
282 return unix_copydir(entry, mp);
286 * Open the named file for read, create the cluster chain, return the
287 * directory structure or NULL on error.
289 static int writeit(struct dos_name_t *dosname,
298 mt_size_t filesize, newsize;
299 Arg_t *arg = (Arg_t *) arg0;
303 if (arg->mp.File->Class->get_data(arg->mp.File,
304 & date, &filesize, &type, 0) < 0 ){
305 fprintf(stderr, "Can't stat source file\n");
309 if(fileTooBig(filesize)) {
310 fprintf(stderr, "File \"%s\" too big\n", longname);
316 fprintf(stderr, "\"%s\" is a directory\n", longname);
320 /*if (!arg->single || arg->recursive)*/
322 fprintf(stderr,"Copying %s\n", longname);
327 if (!getfreeMinBytes(arg->mp.targetDir, filesize))
330 /* preserve mod time? */
331 if (arg->preserveTime)
336 mk_entry(dosname, arg->attr, 1, 0, now, &entry->dir);
338 Target = OpenFileByDirentry(entry);
340 fprintf(stderr,"Could not open Target\n");
343 if (arg->needfilter & arg->textmode)
344 Target = open_filter(Target,arg->convertCharset);
348 ret = copyfile(arg->mp.File, Target);
349 GET_DATA(Target, 0, &newsize, 0, &fat);
351 if (arg->needfilter & arg->textmode)
352 newsize++; /* ugly hack: we gathered the size before the Ctrl-Z
353 * was written. Increment it manually */
355 fat_free(arg->mp.targetDir, fat);
358 mk_entry(dosname, arg->attr, fat, truncBytes32(newsize),
366 static int dos_write(direntry_t *entry, MainParam_t *mp, int needfilter)
367 /* write a messy dos file to another messy dos file */
370 Arg_t * arg = (Arg_t *) (mp->arg);
371 const char *targetName = mpPickTargetName(mp);
373 if(entry && arg->preserveAttributes)
374 arg->attr = entry->dir.attr;
376 arg->attr = ATTR_ARCHIVE;
378 arg->needfilter = needfilter;
379 if (entry && mp->targetDir == entry->Dir){
380 arg->ch.ignore_entry = -1;
381 arg->ch.source = entry->entry;
383 arg->ch.ignore_entry = -1;
386 result = mwrite_one(mp->targetDir, targetName, 0,
387 writeit, (void *)arg, &arg->ch);
394 static Stream_t *subDir(Stream_t *parent, const char *filename)
397 initializeDirentry(&entry, parent);
399 switch(vfat_lookup(&entry, filename, -1, ACCEPT_DIR, 0, 0)) {
401 return OpenFileByDirentry(&entry);
404 default: /* IO Error */
409 static int dos_copydir(direntry_t *entry, MainParam_t *mp)
410 /* copyes a directory to Dos */
412 Arg_t * arg = (Arg_t *) (mp->arg);
417 const char *targetName = mpPickTargetName(mp);
419 if (!arg->recursive && mp->basenameHasWildcard)
422 if(entry && isSubdirOf(mp->targetDir, mp->File)) {
423 fprintf(stderr, "Cannot recursively copy directory ");
424 fprintPwd(stderr, entry,0);
425 fprintf(stderr, " into one of its own subdirectories ");
426 fprintPwd(stderr, getDirentry(mp->targetDir),0);
427 fprintf(stderr, "\n");
431 if (arg->mp.File->Class->get_data(arg->mp.File,
432 & date, 0, 0, 0) < 0 ){
433 fprintf(stderr, "Can't stat source file\n");
437 if(!arg->type && arg->verbose)
438 fprintf(stderr,"Copying %s\n", mpGetBasename(mp));
440 if(entry && arg->preserveAttributes)
441 arg->attr = entry->dir.attr;
445 if (entry && (mp->targetDir == entry->Dir)){
446 arg->ch.ignore_entry = -1;
447 arg->ch.source = entry->entry;
449 arg->ch.ignore_entry = -1;
453 /* preserve mod time? */
454 if (arg->preserveTime)
460 newArg.mp.arg = &newArg;
461 newArg.mp.targetName = 0;
462 newArg.mp.basenameHasWildcard = 1;
464 /* maybe the directory already exist. Use it */
465 newArg.mp.targetDir = subDir(mp->targetDir, targetName);
466 if(!newArg.mp.targetDir)
467 newArg.mp.targetDir = createDir(mp->targetDir,
472 newArg.mp.targetDir = mp->targetDir;
474 if(!newArg.mp.targetDir)
477 ret = mp->loop(mp->File, &newArg.mp, "*");
479 FREE(&newArg.mp.targetDir);
480 return ret | GOT_ONE;
484 static int dos_to_dos(direntry_t *entry, MainParam_t *mp)
486 return dos_write(entry, mp, 0);
489 static int unix_to_dos(MainParam_t *mp)
491 return dos_write(0, mp, 1);
494 static void usage(int ret) NORETURN;
495 static void usage(int ret)
498 "Mtools version %s, dated %s\n", mversion, mdate);
500 "Usage: %s [-spatnmQVBT] [-D clash_option] sourcefile targetfile\n", progname);
502 " %s [-spatnmQVBT] [-D clash_option] sourcefile [sourcefiles...] targetdirectory\n",
507 void mcopy(int argc, char **argv, int mtype)
510 int c, ret, fastquit;
514 /* get command line options */
516 init_clash_handling(& arg.ch);
518 /* get command line options */
521 arg.preserveTime = 0;
522 arg.preserveAttributes = 0;
526 arg.convertCharset = 0;
529 if(helpFlag(argc, argv))
531 while ((c = getopt(argc, argv, "i:abB/sptTnmvQD:oh")) != EOF) {
534 set_cmd_line_image(optarg, 0);
541 arg.preserveAttributes = 1;
544 arg.convertCharset = 1;
553 arg.preserveTime = 1;
566 handle_clash_options(&arg.ch, c);
569 if(handle_clash_options(&arg.ch, *optarg))
581 if (argc - optind < 1)
585 arg.mp.lookupflags = ACCEPT_PLAIN | ACCEPT_DIR | DO_OPEN | NO_DOTS;
586 arg.mp.fast_quit = fastquit;
587 arg.mp.arg = (void *) &arg;
588 arg.mp.openflags = O_RDONLY;
591 /* last parameter is "-", use mtype mode */
592 if(!mtype && !strcmp(argv[argc-1], "-")) {
593 arg.type = mtype = 1;
598 /* Mtype = copying to stdout */
599 arg.mp.targetName = strdup("-");
600 arg.mp.unixTarget = strdup("");
601 arg.mp.callback = dos_to_unix;
602 arg.mp.dirCallback = unix_copydir;
603 arg.mp.unixcallback = unix_to_unix;
606 if (argc - optind == 1) {
607 /* copying to the current directory */
611 /* target is the last item mentioned */
616 ret = target_lookup(&arg.mp, target);
617 if(!arg.mp.targetDir && !arg.mp.unixTarget) {
618 fprintf(stderr,"Bad target %s\n", target);
622 /* callback functions */
623 if(arg.mp.unixTarget) {
624 arg.mp.callback = dos_to_unix;
625 arg.mp.dirCallback = directory_dos_to_unix;
626 arg.mp.unixcallback = unix_to_unix;
628 arg.mp.dirCallback = dos_copydir;
629 arg.mp.callback = dos_to_dos;
630 arg.mp.unixcallback = unix_to_dos;
634 exit(main_loop(&arg.mp, argv + optind, argc - optind));