2 Copyright © 2001-2011, The AROS Development Team. All rights reserved.
9 /*****************************************************************************
17 FROM/M, TO, ALL/S, QUIET/S, BUF=BUFFER/K/N, CLONE/S, DATES/S, NOPRO/S,
18 COM=COMMENT/S, NOREQ/S,
20 PAT=PATTERN/K, DIRECT/S,SILENT/S, ERRWARN/S, MAKEDIR/S, MOVE/S,
21 DELETE/S, HARD=HARDLINK/S, SOFT=SOFTLINK/S, FOLNK=FORCELINK/S,
22 FODEL=FORCEDELETE/S, FOOVR=FORCEOVERWRITE/S, DONTOVR=DONTOVERWRITE/S,
31 Creates identical copies of one or more files.
35 FROM -- multiple input files
36 TO -- destination file or directory
37 ALL -- deep scan into sub directories
38 QUIET -- suppress all output and requesters
39 BUFFER -- buffer size for copy buffer in 512 byte blocks
40 (default 1024 (= 512K))
41 CLONE -- copy comment, protection bits and date as well
43 NOPRO -- do not copy protection bits
44 COMMENT -- copy file comment
45 NOREQ -- suppress requesters
47 PATTERN -- a pattern the filenames must match
48 DIRECT -- copy mode only: copy file without any tests or options
49 VERBOSE -- gives more output
50 ERRWARN -- do not proceed, when one file failed
51 MAKEDIR -- produce directories
52 MOVE -- delete source files after copying successful
53 DELETE -- do not copy, but delete the source files
54 HARDLINK -- make a hardlink to source instead of copying
55 SOFTLINK -- make a softlink to source instead of copying
56 FOLNK -- also makes links to directories
57 FODEL -- delete protected files also
58 FOOVR -- also overwrite protected files
59 DONTOVR -- never overwrite destination
60 FORCE -- DO NOT USE. Call compatibility only.
61 NEWER -- compare version strings and only overwrites older files.
64 More detailed descriptions:
67 Source file(s). For directories, all contained files are source files. May
68 have standard patterns.
71 Destination file or for multiple sources destination directory. Destination
72 directories are created (including all needed parent directories).
75 Scan directories recursively
78 Copy is completely silent here. Really no output is given, also no requests
79 for missing disks or other problems!
82 Specify the number of 512 byte buffers for copying. Default are 200 buffers
83 [100KB memory]. One buffer is minimum size, but should never be used.
86 PATTERN allows to specify a standard dos pattern, all file have to match.
87 This is useful with ALL option.
90 When you want to delete all .info files in a directory tree, you need
91 this option: Copy DELETE #? ALL PAT #?.info
94 The filecomment, date and protection bits of the source files are copied to
95 destination file or directory.
98 The date information of source is copied to destination.
101 The protection bits of sources are NOT copied. So the destination gets
105 The filecomment is copied to destination.
108 No standard DOS requests are displayed, when an error occurs.
112 Certain devices do not allow some of the used DOS packet request types.
113 This option is a really easy copy command, which only opens source and
114 destination directly without any tests and checks.
115 Options ALL, PAT, CLONE, DATES, NOPRO, COM, MAKEDIR, MOVE, DELETE, HARD,
116 SOFT, FOLNK, FODEL, FOOVR, DONTOVR and multiple input files cannot be
117 specified together with DIRECT. This options needs one input and one output
119 When you want to delete a softlink, which does no longer point to a valid
120 file, you need this option as well.
121 Example use: 'Copy DIRECT text PRT:' to print a file called text.
122 - Copy manages a lot of such cases automatically, but maybe this option is
126 Copy gives additional output.
129 Copy knows and returns the 3 types of dos.library errors:
130 5 WARN The processing of one file failed, Copy skips this file
131 and proceeds the next.
132 10 ERROR The creation of a directory or any other bad error happend.
133 Copy quits after that.
134 20 FAIL A really hard error happend (No memory, Examine failed, ...)
135 Copy quits after that.
136 When option ERRWARN is used, the result 5 (WARN) gets result 10 (ERROR). So
137 Copy aborts everytime an error occured.
140 All names specified in FROM field are taken as directories, which must be
144 The files are not copied, but moved (or renamed). This means that after
145 move operation the source does no longer exist.
148 This does not copy anything, but deletes the source files!
151 Instead of copying the files, a hard link is created. This only works,
152 when destination is on same device as source.
153 When ALL option is specified, the directories are scanned recursively, else
154 Copy produces links to the directories.
157 Instead of copying directories, a soft link is created. These links are
158 useable between different devices also. Soft links are only created for
159 directories. Files are skipped here. Option FORCELINK is therefor always
161 NOTE: Softlinks are not official supported by OS and may be dangerous.
162 I suggest not to use this option! See description below.
165 When linking of directories should be possible, this option is needed. See
166 section "About links" for possible problems.
169 When this option is enabled, files are deleted also, when they are delete
172 FOOVR=FORCEOVERWRITE:
173 When this option is enabled, files are overwritten also, when they are
176 DONTOVR=DONTOVERWRITE:
177 This option prevents overwriting of destination files.
180 This option scans the version strings of the source and destination files and
181 only overwrites if the source file is newer than the destination file.
193 Delete, Rename, MakeDir, MakeLink
197 The separation of the different switches above is according to
198 what the AmigaDOS Copy command had, and the extensions respectively.
200 Some comments on how the program does its job:
202 The program has 6 working modes: COPY, MOVE, SOFTLINK, HARDLINK,
203 DELETE and MAKEDIR. Only one of these can be used at same time!
205 Move option renames the files, when on same device, else the file
206 is copied and the source deleted after that.
207 When a directory is processed and on the same device it is
208 renamed! This means MOVE option copies complete directories also
209 without ALL option (when on same device).
211 In Copy mode you may use f.e. "Copy C: RAM:K" instead of
212 "Copy C:#? RAM:K". For the other modes this does not work!
214 Destination files are always overwritten, except DONTOVERWRITE is
215 turned on, or they are protected.
217 When the destination directory does not exists, it is created.
218 Existing files with same name are overwritten. When some parent
219 directories do not exist, they are also created. This is also done,
220 when only one file is copied.
222 The program does a loop detection, so that copying/moving/linking
223 with a sub directory of source as destination and ALL option is not
226 Example: Copy RAM:S RAM:S/C ALL
229 Useful aliases you may add to S:User-StartUp:
231 Alias Delete Copy [] DELETE VERBOSE
232 Alias MakeDir Copy [] MAKEDIR
233 Alias MakeLink Copy TO [] HARDLINK
234 Alias Move Copy [] MOVE CLONE VERBOSE
235 Alias Rename Copy [] MOVE CLONE
237 Some programs do want the files to be in C: directory. For these
238 you may additionally use following lines:
240 Alias C:Delete Copy [] DELETE VERBOSE
241 Alias C:MakeDir Copy [] MAKEDIR
242 Alias C:MakeLink Copy TO [] HARDLINK
243 Alias C:Rename Copy [] MOVE CLONE
249 When copying one file to annother place on same disk, the file
250 afterwards uses double space. Links are a method to resolve that
251 problem. When using a link, the file is not copied, but only a new
252 entry to the same data as created. This saves space and allows to
253 have copies of files always up-to-date (as when on link is updated,
254 all the others are new as well).
257 This is a link method, which is NOT official supported by the OS.
258 Soft links do not need to be on the same partition. The may be used
259 for references between different partitions. NOTE: Using this links
260 may cause lots of problems. You may test for yourself if it works for
264 Links to directories may cause infinite directory loops!
266 Example: Having following directory tree:
272 Some loops are detected, for example when trying to do:
273 MakeLink DEV:A/C DEV:A FORCE
274 Here you get an error message, that loops are not allowed.
276 Some more complicated links cannot be detected:
277 MakeLink DEV:A/C DEV:B FORCE
279 MakeLink DEV:B/C DEV:A FORCE
280 Till now no error message is possible, so the result is an infinite
286 Copy was done by Dirk Stoecker (stoecker@amigaworld.com), donated
287 to AROS in March 2001
289 ******************************************************************************/
291 #define CTRL_C (SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
296 #define USE_SOFTLINKCHECK 1
297 #define USE_ALWAYSVERBOSE 1
298 #define USE_BOGUSEOFWORKAROUND 0
300 #include <aros/asmcall.h>
301 #include <exec/devices.h>
303 #include <exec/memory.h>
304 #include <exec/semaphores.h>
305 #include <exec/types.h>
306 #include <dos/exall.h>
311 #include <aros/debug.h>
314 #include <proto/dos.h>
315 #include <proto/exec.h>
319 const TEXT version
[] = "\0$VER: Copy 50.17 (30.12.2011)";
321 static const UBYTE
*PARAM
=
322 "FROM/M,TO,PAT=PATTERN/K,BUF=BUFFER/K/N,ALL/S,"
323 "DIRECT/S,CLONE/S,DATES/S,NOPRO/S,COM=COMMENT/S,"
325 #if !USE_ALWAYSVERBOSE
328 "NOREQ/S,ERRWARN/S,MAKEDIR/S,"
329 "MOVE/S,DELETE/S,HARD=HARDLINK/S,SOFT=SOFTLINK/S,"
330 "FOLNK=FORCELINK/S,FODEL=FORCEDELETE/S,"
331 "FOOVR=FORCEOVERWRITE/S,DONTOVR=DONTOVERWRITE/S,"
334 #define COPYFLAG_ALL (1<<0)
335 #define COPYFLAG_DATES (1<<1)
336 #define COPYFLAG_NOPRO (1<<2)
337 #define COPYFLAG_COMMENT (1<<3)
338 #define COPYFLAG_FORCELINK (1<<4)
339 #define COPYFLAG_FORCEDELETE (1<<5)
340 #define COPYFLAG_FORCEOVERWRITE (1<<6)
341 #define COPYFLAG_DONTOVERWRITE (1<<7)
342 #define COPYFLAG_QUIET (1<<8)
343 #define COPYFLAG_VERBOSE (1<<9)
344 #define COPYFLAG_ERRWARN (1<<10)
345 #define COPYFLAG_NEWER (1<<11)
347 #define COPYFLAG_SOFTLINK (1<<20) /* produce softlinks */
348 #define COPYFLAG_DEST_FILE (1<<21) /* one file mode */
349 #define COPYFLAG_DONE (1<<22) /* did something in DoWork */
350 #define COPYFLAG_ENTERSECOND (1<<23) /* entered directory second time */
352 #define COPYFLAG_SRCNOFILESYS (1<<24) /* source is no filesystem */
353 #define COPYFLAG_DESNOFILESYS (1<<25) /* destination is no filesystem */
355 #define COPYMODE_COPY 0
356 #define COPYMODE_MOVE 1
357 #define COPYMODE_DELETE 2
358 #define COPYMODE_MAKEDIR 3
359 #define COPYMODE_LINK 4
361 #define PRINTOUT_SIZE 50 /* maximum size of name printout */
362 #define PRINTOUT_SPACES 10 /* maximum number of spaces */
364 #define FILEPATH_SIZE 2048 /* maximum size of filepaths */
367 #define TESTDEST_DIR_OK 2 /* directory exists, go in */
368 #define TESTDEST_DELETED 1 /* file or empty directory deleted */
369 #define TESTDEST_NONE 0 /* nothing existed */
370 #define TESTDEST_ERROR -1 /* an error occured */
371 #define TESTDEST_CANTDELETE -2 /* deletion not allowed (DONTOV) */
386 #if !USE_ALWAYSVERBOSE
437 struct ExecBase
*SysBase
;
438 struct DosLibrary
*DOSBase
;
444 BPTR CurDest
; /* Current Destination */
446 struct FileInfoBlock Fib
;
448 UBYTE RetVal
; /* when set, error output is already done */
449 UBYTE RetVal2
; /* when set, error output must be done */
452 UBYTE FileName
[FILEPATH_SIZE
];
453 UBYTE DestName
[FILEPATH_SIZE
];
460 ** This data keeps the extracted data from a version string
463 #define VDNAMESIZE 96
466 UBYTE vd_Name
[VDNAMESIZE
];
476 #define MIN(a,b) ((a)<(b)?(a):(b))
479 #define CHECKVER_DESTOLDER -1
480 #define CHECKVER_DESTNEWER 1
481 #define CHECKVER_EQUAL 0
483 #define TEXT_READ texts[0]
484 #define TEXT_COPIED texts[1]
485 #define TEXT_MOVED texts[2]
486 #define TEXT_DELETED texts[3]
487 #define TEXT_LINKED texts[4]
488 #define TEXT_RENAMED texts[5]
489 #define TEXT_CREATED texts[6]
490 #define TEXT_ENTERED texts[7]
491 #define TEXT_OPENED_FOR_OUTPUT texts[8]
492 #define TEXTNUM_MODE 9
493 #define TEXT_DIRECTORY texts[15]
494 #define TEXT_NOT_DONE texts[16]
495 #define TEXT_NOTHING_DONE texts[17]
496 #define TEXT_ERR_FORCELINK texts[18]
497 #define TEXT_ERR_DELETE_DEVICE texts[19]
498 #define TEXT_ERR_DEST_DIR texts[20]
499 #define TEXT_ERR_INFINITE_LOOP texts[21]
500 #define TEXT_ERR_WILDCARD_DEST texts[22]
502 const CONST_STRPTR texts
[] =
519 "%s (Dir)", /* output of directories */
521 "No file was processed.\n",
522 "FORCELINK keyword required.\n",
523 "A device cannot be deleted.",
524 "Destination must be a directory.\n",
525 "Infinite loop not allowed.\n",
526 "Wildcard destination invalid.\n",
529 LONG
CopyFile(BPTR
, BPTR
, ULONG
, struct CopyData
*);
530 void DoWork(STRPTR
, struct CopyData
*);
531 LONG
IsMatchPattern(STRPTR name
, struct CopyData
*cd
);
532 LONG
IsPattern(STRPTR
, struct CopyData
*); /* return 0 -> NOPATTERN, return -1 --> ERROR */
533 LONG
KillFile(STRPTR
, ULONG
, struct CopyData
*);
534 LONG
KillFileKeepErr(STRPTR name
, ULONG doit
, struct CopyData
*);
535 LONG
LinkFile(BPTR
, STRPTR
, ULONG
, struct CopyData
*);
536 BPTR
OpenDestDir(STRPTR
, struct CopyData
*);
537 void PatCopy(STRPTR
, struct CopyData
*);
538 void PrintName(CONST_STRPTR
, ULONG
, ULONG
, ULONG
, struct CopyData
*);
539 void PrintNotDone(CONST_STRPTR
, CONST_STRPTR
, ULONG
, ULONG
, struct CopyData
*);
540 ULONG
TestFileSys(STRPTR
, struct CopyData
*); /* returns value, when is a filesystem */
541 void SetData(STRPTR
, struct CopyData
*);
542 LONG
TestDest(STRPTR
, ULONG
, struct CopyData
*);
543 ULONG
TestLoop(BPTR
, BPTR
, struct CopyData
*);
544 static LONG
CheckVersion( struct CopyData
*cd
);
545 static void makeversionfromstring( STRPTR buffer
, struct VersionData
*vd
, struct CopyData
*cd
);
546 static STRPTR
skipspaces( STRPTR buffer
);
547 static STRPTR
skipnonspaces( STRPTR buffer
);
548 static BOOL
VersionFind( CONST_STRPTR path
, struct VersionData
*vds
, struct CopyData
*cd
);
555 __startup
static AROS_ENTRY(int, Start
,
556 AROS_UFHA(char *, argstr
, A0
),
557 AROS_UFHA(ULONG
, argsize
, D0
),
558 struct ExecBase
*, SysBase
)
563 struct DosLibrary
*DOSBase
;
564 struct Process
*task
;
566 int retval
= RETURN_FAIL
;
568 /* test for WB and reply startup-message */
569 if (!(task
= (struct Process
*)FindTask(NULL
))->pr_CLI
)
571 WaitPort(&task
->pr_MsgPort
);
573 ReplyMsg(GetMsg(&task
->pr_MsgPort
));
578 DOSBase
= (struct DosLibrary
*)OpenLibrary("dos.library", 37);
579 cd
= AllocMem(sizeof(*cd
), MEMF_PUBLIC
| MEMF_CLEAR
);
583 STRPTR a
[2] = { "", 0 };
585 struct IptrArgs iArgs
;
586 struct Args args
= {};
588 cd
->SysBase
= SysBase
;
589 cd
->DOSBase
= DOSBase
;
590 #define SysBase cd->SysBase
591 #define DOSBase cd->DOSBase
593 cd
->BufferSize
= 512*1024;
594 cd
->Mode
= COPYMODE_COPY
;
595 cd
->RetVal2
= RETURN_FAIL
;
598 memset(&iArgs
, 0, sizeof(struct IptrArgs
));
600 rda
= (struct RDArgs
*)AllocDosObject(DOS_RDARGS
, NULL
);
604 "FROM multiple input files\n"
605 "TO destination file or directory\n"
606 "PATTERN a pattern the filenames must match\n"
607 "BUFFER buffersize for copy buffer (default 200 [100K])\n"
608 "ALL deep scan into sub directories\n"
609 "DIRECT copy/delete only: work without any tests or options\n"
610 "CLONE copy comment, protection bits and date as well\n"
612 "NOPRO do not copy protection bits\n"
613 "COMMENT copy filecomment\n"
614 "QUIET suppress all output and requesters\n"
615 #if !USE_ALWAYSVERBOSE
616 "VERBOSE give additional output\n"
618 "NOREQ suppress requesters\n"
619 "ERRWARN do not proceed, when one file failed\n"
620 "MAKEDIR produce directories\n"
621 "MOVE delete source files after copying successful\n"
622 "DELETE do not copy, but delete the source files\n"
623 "HARDLINK make a hardlink to source instead of copying\n"
624 "SOFTLINK make a softlink to source instead of copying\n"
625 "FOLNK also makes links to directories\n"
626 "FODEL delete protected files also\n"
627 "FOOVR also overwrite protected files\n"
628 "DONTOVR do never overwrite destination\n"
629 "FORCE DO NOT USE. Call compatibility only.\n"
630 "NEWER will compare version strings and only overwrites older files\n";
632 if (ReadArgs(PARAM
, (IPTR
*)&iArgs
, rda
))
634 ULONG patbufsize
= 0;
636 APTR win
= task
->pr_WindowPtr
;
638 args
.from
= (STRPTR
*)iArgs
.from
;
639 args
.to
= (STRPTR
)iArgs
.to
;
640 args
.pattern
= (STRPTR
)iArgs
.pattern
;
641 args
.buffer
= (LONG
*)iArgs
.buffer
;
642 args
.all
= (LONG
)iArgs
.all
;
643 args
.direct
= (LONG
)iArgs
.direct
;
644 args
.clone
= (LONG
)iArgs
.clone
;
645 args
.dates
= (LONG
)iArgs
.dates
;
646 args
.nopro
= (LONG
)iArgs
.nopro
;
647 args
.comment
= (LONG
)iArgs
.comment
;
648 args
.quiet
= (LONG
)iArgs
.quiet
;
649 #if USE_ALWAYSVERBOSE
650 args
.verbose
= FALSE
;
652 args
.verbose
= (LONG
)iArgs
.verbose
;
654 args
.noreq
= (LONG
)iArgs
.noreq
;
655 args
.errwarn
= (LONG
)iArgs
.errwarn
;
656 args
.makedir
= (LONG
)iArgs
.makedir
;
657 args
.move_mode
= (LONG
)iArgs
.move_mode
;
658 args
.delete_mode
= (LONG
)iArgs
.delete_mode
;
659 args
.hardlink
= (LONG
)iArgs
.hardlink
;
660 args
.softlink
= (LONG
)iArgs
.softlink
;
661 args
.forcelink
= (LONG
)iArgs
.forcelink
;
662 args
.forcedelete
= (LONG
)iArgs
.forcedelete
;
663 args
.forceoverwrite
= (LONG
)iArgs
.forceoverwrite
;
664 args
.dontoverwrite
= (LONG
)iArgs
.dontoverwrite
;
665 args
.force
= (LONG
)iArgs
.force
;
666 args
.newer
= (LONG
)iArgs
.newer
;
668 if (args
.quiet
) /* when QUIET, SILENT and NOREQ are also
671 /* Original doesn't hide requesters with QUIET */
673 args
.verbose
= FALSE
;
676 if (args
.buffer
&& *args
.buffer
> 0) /* minimum buffer size */
678 cd
->BufferSize
= *args
.buffer
* 512;
683 cd
->Flags
|= COPYFLAG_QUIET
;
686 #if !USE_ALWAYSVERBOSE
689 cd
->Flags
|= COPYFLAG_VERBOSE
;
694 cd
->Flags
|= COPYFLAG_ALL
;
699 cd
->Flags
|= COPYFLAG_DATES
| COPYFLAG_COMMENT
;
704 cd
->Flags
|= COPYFLAG_DATES
;
709 cd
->Flags
|= COPYFLAG_COMMENT
;
714 cd
->Flags
|= COPYFLAG_NOPRO
;
719 cd
->Flags
|= COPYFLAG_FORCELINK
;
722 if (args
.forcedelete
)
724 cd
->Flags
|= COPYFLAG_FORCEDELETE
;
727 if (args
.forceoverwrite
)
729 cd
->Flags
|= COPYFLAG_FORCEOVERWRITE
;
732 if (args
.dontoverwrite
)
734 cd
->Flags
|= COPYFLAG_DONTOVERWRITE
;
739 cd
->Flags
|= COPYFLAG_NEWER
|COPYFLAG_DONTOVERWRITE
;
744 cd
->Flags
|= COPYFLAG_ERRWARN
;
747 if (args
.force
) /* support OS Delete and MakeLink command
750 if (args
.delete_mode
)
752 cd
->Flags
|= COPYFLAG_FORCEDELETE
;
755 if (args
.hardlink
|| args
.softlink
)
757 cd
->Flags
|= COPYFLAG_FORCELINK
;
761 if (!args
.from
) /* no args.from means currentdir */
766 if (args
.noreq
) /* no dos.library requests allowed */
768 task
->pr_WindowPtr
= (APTR
)-1;
771 if (args
.delete_mode
)
774 cd
->Mode
= COPYMODE_DELETE
;
780 cd
->Mode
= COPYMODE_MOVE
;
786 cd
->Mode
= COPYMODE_MAKEDIR
;
792 cd
->Mode
= COPYMODE_LINK
;
798 cd
->Mode
= COPYMODE_LINK
;
799 cd
->Flags
|= COPYFLAG_SOFTLINK
| COPYFLAG_FORCELINK
;
802 if (cd
->Mode
!= COPYMODE_DELETE
&&
803 cd
->Mode
!= COPYMODE_MAKEDIR
&& !args
.to
)
805 if (*(args
.from
+ 1)) /* when no TO is specified, the arg
807 { /* one of from. Copy this argument into */
808 STRPTR
*a
; /* args.to */
819 #if USE_ALWAYSVERBOSE
821 /* Only do this if quiet isn't set - bigfoot */
824 /* If more than two args, be verbose... - Piru */
825 if (args
.from
[0] && args
.from
[1])
828 cd
->Flags
|= COPYFLAG_VERBOSE
;
831 /* If any of the sources is a pattern, be verbose... - Piru */
835 for (a
= args
.from
; *a
; a
++)
837 if (IsMatchPattern(*a
, cd
) != 0)
840 cd
->Flags
|= COPYFLAG_VERBOSE
;
847 /* test if more than one of the above four or any other wrong
851 (args
.from
== a
&& cd
->Mode
== COPYMODE_MAKEDIR
) ||
852 (args
.direct
&& (args
.from
== a
|| !*args
.from
||
854 (cd
->Flags
& ~(COPYFLAG_QUIET
| COPYFLAG_VERBOSE
| COPYFLAG_ERRWARN
)) ||
855 (cd
->Mode
!= COPYMODE_DELETE
&& (cd
->Mode
!= COPYMODE_COPY
||
856 !args
.to
|| args
.from
[1])))) ||
857 (args
.dontoverwrite
&& args
.forceoverwrite
) ||
858 /* (args.nopro && args.clone) ||*/ /* Ignore, like original - Piru */
859 (args
.softlink
&& args
.all
) ||
860 (!args
.to
&& cd
->Mode
!= COPYMODE_DELETE
&& cd
->Mode
!= COPYMODE_MAKEDIR
))
862 cd
->IoErr
= ERROR_TOO_MANY_ARGS
;
864 else if (cd
->Mode
== COPYMODE_MAKEDIR
)
868 cd
->RetVal2
= RETURN_OK
;
870 #if !USE_ALWAYSVERBOSE
873 PutStr(texts
[TEXTNUM_MODE
+ COPYMODE_MAKEDIR
]);
877 while (!cd
->RetVal
&& !cd
->RetVal2
&& *args
.from
)
879 if ((i
= IsPattern(*args
.from
, cd
)))
883 cd
->RetVal
= RETURN_ERROR
;
887 PutStr(TEXT_ERR_WILDCARD_DEST
);
892 cd
->RetVal2
= RETURN_FAIL
;
896 if ((dir
= OpenDestDir(*args
.from
, cd
)))
899 cd
->Flags
|= COPYFLAG_DONE
;
904 } /* cd->Mode == COPYMODE_MAKEDIR */
905 else if (args
.direct
)
907 if (cd
->Mode
== COPYMODE_COPY
)
911 if ((in
= Open(*args
.from
, MODE_OLDFILE
)))
913 if ((out
= Open(args
.to
, MODE_NEWFILE
)))
915 cd
->RetVal2
= CopyFile(in
, out
, cd
->BufferSize
, cd
);
916 if (cd
->RetVal2
!= 0)
928 else /* COPYMODE_DELETE */
932 KillFile(*(args
.from
++), cd
->Flags
& COPYFLAG_FORCEDELETE
, cd
);
935 cd
->RetVal2
= RETURN_OK
;
940 if (args
.pattern
&& *args
.pattern
)
942 patbufsize
= (strlen(args
.pattern
) << 1) + 3;
944 if ((cd
->Pattern
= (STRPTR
)AllocMem(patbufsize
,
947 if (ParsePatternNoCase(args
.pattern
, cd
->Pattern
,
950 FreeMem(cd
->Pattern
, patbufsize
);
956 if (1) // (cd->Fib = (struct FileInfoBlock *)AllocDosObject(DOS_FIB, NULL)))
958 #if !USE_ALWAYSVERBOSE
961 PutStr(texts
[TEXTNUM_MODE
+ cd
->Mode
+
962 (cd
->Flags
& COPYFLAG_SOFTLINK
? 1 : 0)]);
965 if (args
.pattern
&& !cd
->Pattern
)
969 cd
->IoErr
= ERROR_BAD_TEMPLATE
;
972 else if (cd
->Mode
== COPYMODE_DELETE
)
974 cd
->RetVal2
= RETURN_OK
;
976 while (cd
->RetVal
<= (args
.errwarn
? RETURN_OK
: RETURN_WARN
)
979 PatCopy(*(args
.from
++), cd
);
982 else if ((i
= IsPattern(args
.to
, cd
)))
986 cd
->RetVal
= RETURN_ERROR
;
990 PutStr(TEXT_ERR_WILDCARD_DEST
);
998 if (*(path
= PathPart(args
.to
)) == '/')
1000 ++path
; /* is destination a path description ? */
1003 if (*path
&& !*(args
.from
+1) &&
1004 !(i
= IsMatchPattern(*args
.from
, cd
)))
1008 /* is destination an existing directory */
1009 if ((lock
= Lock(args
.to
, SHARED_LOCK
)))
1011 if (Examine(lock
, &cd
->Fib
))
1013 if (cd
->Fib
.fib_DirEntryType
> 0)
1015 cd
->RetVal2
= RETURN_OK
;
1018 /* indicate dir-mode for next if */
1028 /* Some magic to handle tick quoted pattern object names. Quite crude way to
1029 * handle it, but I couldn't think of anything better. - Piru
1032 if (!i
&& cd
->RetVal2
&& !IsMatchPattern(*args
.from
, cd
))
1037 //Printf("pattern check <%s>\n", *args.from);
1039 len
= (strlen(*args
.from
) << 1) + 3;
1041 if ((pat
= (STRPTR
)AllocMem(len
,
1044 if (ParsePattern(*args
.from
, pat
, len
) > -1 &&
1045 strlen(pat
) <= strlen(*args
.from
))
1047 lock
= Lock(pat
, SHARED_LOCK
);
1052 strcpy(*args
.from
, pat
);
1061 /* is source a directory */
1062 if (!i
&& cd
->RetVal2
&&
1063 (lock
= Lock(*args
.from
, SHARED_LOCK
)))
1065 if (Examine(lock
, &cd
->Fib
))
1067 cd
->RetVal2
= RETURN_OK
;
1068 if (cd
->Mode
!= COPYMODE_COPY
||
1069 cd
->Fib
.fib_DirEntryType
< 0)
1073 cd
->Flags
|= COPYFLAG_DEST_FILE
;
1075 /* produce missing destination directories */
1079 if ((cd
->CurDest
= OpenDestDir(args
.to
, cd
)))
1086 CopyMem(*args
.from
, cd
->FileName
,
1087 1 + strlen(*args
.from
));
1088 DoWork(FilePart(args
.to
), cd
); /* on file call */
1089 UnLock(cd
->CurDest
);
1100 if (lock
== 0 && cd
->Mode
== COPYMODE_COPY
&& !TestFileSys(*args
.from
, cd
))
1103 cd
->Flags
|= COPYFLAG_DEST_FILE
| COPYFLAG_SRCNOFILESYS
;
1104 cd
->RetVal2
= RETURN_OK
;
1106 /* produce missing destination directories */
1110 if ((cd
->CurDest
= OpenDestDir(args
.to
, cd
)))
1115 CopyMem(*args
.from
, cd
->FileName
, 1 + strlen(*args
.from
));
1116 DoWork(FilePart(args
.to
), cd
); /* on file call */
1117 UnLock(cd
->CurDest
);
1123 cd
->RetVal2
= RETURN_OK
;
1126 if (!cd
->RetVal
&& !cd
->RetVal2
&& !(cd
->Flags
& COPYFLAG_DEST_FILE
) &&
1127 (cd
->Destination
= OpenDestDir(args
.to
, cd
)))
1129 while (cd
->RetVal
<= (args
.errwarn
? RETURN_OK
: RETURN_WARN
)
1130 && *args
.from
&& !CTRL_C
)
1132 PatCopy(*(args
.from
++), cd
);
1135 UnLock(cd
->Destination
);
1139 if (!(cd
->Flags
& COPYFLAG_DONE
) && args
.verbose
&&
1140 !cd
->RetVal
&& !cd
->RetVal2
)
1142 PutStr(TEXT_NOTHING_DONE
);
1149 FreeMem(cd
->Pattern
, patbufsize
);
1153 task
->pr_WindowPtr
= win
;
1158 FreeDosObject(DOS_RDARGS
, rda
);
1159 } /* AllocDosObject */
1161 if (!cd
->RetVal2
&& CTRL_C
)
1163 SetIoErr(ERROR_BREAK
);
1164 cd
->RetVal2
= RETURN_WARN
;
1169 cd
->RetVal2
= cd
->RetVal
;
1172 if (args
.errwarn
&& cd
->RetVal2
== RETURN_WARN
)
1174 cd
->RetVal2
= RETURN_ERROR
;
1179 FreeMem(cd
->CopyBuf
, cd
->CopyBufLen
);
1187 retval
= cd
->RetVal2
;
1188 SetIoErr(cd
->IoErr
);
1189 FreeMem(cd
, sizeof(*cd
));
1193 PrintFault(IoErr(), NULL
);
1197 CloseLibrary((struct Library
*)DOSBase
);
1204 /* This code is pure and has library bases in explicitly allocated data structure */
1205 #define SysBase cd->SysBase
1206 #define DOSBase cd->DOSBase
1208 void PatCopy(STRPTR name
, struct CopyData
*cd
)
1210 struct AnchorPath
*APath
;
1211 ULONG retval
, doit
= 0, deep
= 0, failval
= RETURN_WARN
, first
= 0;
1214 Printf("PatCopy(%s, .)\n", name
);
1217 if ((cd
->Mode
== COPYMODE_COPY
|| (cd
->Flags
& COPYFLAG_ALL
)) && !IsMatchPattern(name
, cd
))
1219 first
= 1; /* enter first directory (support of old copy style) */
1222 if (cd
->Flags
& COPYFLAG_ERRWARN
)
1224 failval
= RETURN_OK
;
1227 cd
->CurDest
= cd
->Destination
;
1228 cd
->DestPathSize
= 0;
1230 if (cd
->Mode
== COPYMODE_COPY
&& !TestFileSys(name
, cd
))
1232 cd
->Flags
|= COPYFLAG_SRCNOFILESYS
;
1233 CopyMem(name
, cd
->FileName
, 1 + strlen(name
));
1234 DoWork(FilePart(name
), cd
);
1235 cd
->Flags
&= ~COPYFLAG_SRCNOFILESYS
;
1240 if ((APath
= (struct AnchorPath
*)AllocMem(sizeof(struct AnchorPath
) + FILEPATH_SIZE
,
1241 MEMF_PUBLIC
| MEMF_CLEAR
)))
1243 int parentdirerr
= 0;
1245 APath
->ap_BreakBits
= SIGBREAKF_CTRL_C
;
1246 APath
->ap_Strlen
= FILEPATH_SIZE
;
1248 for (retval
= MatchFirst(name
, APath
);
1249 !retval
&& cd
->RetVal
<= failval
&& !cd
->RetVal2
;
1250 retval
= MatchNext(APath
)
1255 //Printf("ParentDir() fuxored last round! Would copy next files to SYS: !\n");
1258 cd
->IoErr
= retval
= ERROR_INVALID_LOCK
;
1264 DoWork(cd
->Fib
.fib_FileName
, cd
);
1268 if (deep
) /* used for Deep checking */
1274 cd
->Flags
&= ~COPYFLAG_ENTERSECOND
;
1276 CopyMem(APath
->ap_Buf
, cd
->FileName
, FILEPATH_SIZE
);
1277 CopyMem(&APath
->ap_Info
, &cd
->Fib
, sizeof(struct FileInfoBlock
));
1279 if (first
&& APath
->ap_Info
.fib_DirEntryType
> 0)
1281 #if USE_ALWAYSVERBOSE
1282 /* If the source is a directory, be verbose - Piru */
1283 cd
->Flags
|= COPYFLAG_VERBOSE
;
1285 APath
->ap_Flags
|= APF_DODIR
;
1287 else if (APath
->ap_Flags
& APF_DIDDIR
)
1291 cd
->Flags
|= COPYFLAG_ENTERSECOND
;
1292 APath
->ap_Flags
&= ~APF_DIDDIR
;
1295 if (cd
->Mode
== COPYMODE_DELETE
|| cd
->Mode
== COPYMODE_MOVE
)
1300 if ((i
= cd
->CurDest
))
1302 cd
->CurDest
= ParentDir(i
);
1303 cd
->DestPathSize
= 0;
1305 if (i
!= cd
->Destination
)
1317 else if (APath
->ap_Info
.fib_DirEntryType
> 0)
1321 if (cd
->Flags
& COPYFLAG_ALL
)
1325 #if USE_SOFTLINKCHECK
1327 #define BUFFERSIZE 512
1328 if (APath
->ap_Info
.fib_DirEntryType
== ST_SOFTLINK
)
1330 UBYTE
*buffer
= AllocMem(BUFFERSIZE
, MEMF_PUBLIC
);
1332 D(Printf("%s is a softlink\n", APath
->ap_Info
.fib_FileName
));
1338 struct DevProc
*dvp
= GetDeviceProc("", NULL
);
1340 if (ReadLink(dvp
->dvp_Port
, APath
->ap_Current
->an_Lock
, APath
->ap_Info
.fib_FileName
, buffer
, BUFFERSIZE
- 1) > 0)
1342 BOOL link_ok
= FALSE
;
1345 buffer
[BUFFERSIZE
- 1] = '\0';
1346 D(Printf("Softlink target: %s\n", buffer
));
1348 dir
= CurrentDir(APath
->ap_Current
->an_Lock
);
1349 lock
= Lock(buffer
, SHARED_LOCK
);
1352 struct FileInfoBlock
*fib
= AllocDosObject(DOS_FIB
, NULL
);
1356 if (Examine(lock
, fib
))
1359 D(Printf("Target type: %ld\n", fib
->fib_DirEntryType
));
1361 if (fib
->fib_DirEntryType
> 0)
1365 * FIXME: This currently just prevents treating symlinks to files as
1366 * directories during copying.
1367 * DoWork() should be extended to handle symlinks correctly. BTW, how exactly ?
1371 FreeDosObject(DOS_FIB
, fib
);
1380 Printf("Warning: Skipping dangling softlink %s -> %s\n",
1381 APath
->ap_Info
.fib_FileName
, buffer
);
1384 FreeDeviceProc(dvp
);
1385 FreeMem(buffer
, BUFFERSIZE
);
1389 #endif /* USE_SOFTLINKCHECK */
1394 APath
->ap_Flags
|= APF_DODIR
;
1399 else if (!cd
->Pattern
|| MatchPatternNoCase(cd
->Pattern
, APath
->ap_Info
.fib_FileName
))
1409 if (retval
&& retval
!= ERROR_NO_MORE_ENTRIES
)
1411 LONG ioerr
= IoErr();
1412 #if USE_ALWAYSVERBOSE
1413 cd
->Flags
|= COPYFLAG_VERBOSE
;
1415 Printf("%s - ", APath
->ap_Info
.fib_FileName
);
1416 PrintFault(ioerr
, NULL
);
1419 cd
->RetVal2
= RETURN_FAIL
;
1424 DoWork(cd
->Fib
.fib_FileName
, cd
);
1427 /* No need to clear the flags here, as they are cleared on next PatJoin
1428 call (DoWork is not called first round, as lock is zero!). */
1430 FreeMem(APath
, sizeof(struct AnchorPath
) + FILEPATH_SIZE
);
1434 cd
->RetVal
= RETURN_FAIL
;
1436 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1438 PrintFault(ERROR_NO_FREE_STORE
, NULL
);
1442 if (cd
->CurDest
&& cd
->CurDest
!= cd
->Destination
)
1444 UnLock(cd
->CurDest
);
1449 LONG
IsPattern(STRPTR name
, struct CopyData
*cd
)
1454 a
= (strlen(name
) << 1) + 3;
1456 if ((buffer
= (STRPTR
)AllocMem(a
, MEMF_ANY
)))
1458 ret
= ParsePattern(name
, buffer
, a
);
1464 cd
->IoErr
= ERROR_NO_FREE_STORE
;
1471 LONG
IsMatchPattern(STRPTR name
, struct CopyData
*cd
)
1473 struct AnchorPath ap
;
1476 ap
.ap_BreakBits
= 0;
1477 ap
.ap_Flags
= APF_DOWILD
;
1480 if (MatchFirst(name
, &ap
) == 0)
1482 ret
= (ap
.ap_Flags
& APF_ITSWILD
) ? TRUE
: FALSE
;
1491 LONG
KillFile(STRPTR name
, ULONG doit
, struct CopyData
*cd
)
1495 SetProtection(name
, 0);
1498 return DeleteFile(name
);
1502 BPTR
OpenDestDir(STRPTR name
, struct CopyData
*cd
)
1504 LONG a
, err
= 0, cr
= 0;
1509 if ((cd
->Mode
== COPYMODE_COPY
|| cd
->Mode
== COPYMODE_MOVE
) && !TestFileSys(name
, cd
))
1511 cd
->Flags
|= COPYFLAG_DESNOFILESYS
;
1512 CopyMem(name
, cd
->DestName
, 1 + strlen(name
));
1514 return Lock("", SHARED_LOCK
);
1517 while (!err
&& *ptr
!= 0)
1519 while (*ptr
&& *ptr
!= '/')
1527 if ((a
= TestDest(name
, 1, cd
)) == TESTDEST_CANTDELETE
)
1529 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1531 PutStr(TEXT_ERR_DEST_DIR
);
1540 else if (a
!= TESTDEST_DIR_OK
)
1542 if ((dir
= CreateDir(name
)))
1546 if ((cd
->Flags
& COPYFLAG_VERBOSE
))
1548 PrintName(name
, 1, 1, 1, cd
);
1549 Printf("%s\n", TEXT_CREATED
);
1556 cd
->IoErr
= IoErr();
1557 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1559 PrintNotDone(name
, TEXT_CREATED
, 1, 1, cd
);
1568 /* 26-Oct-2003 bugfix: Don't scan past end of the string.
1569 * as is the old char, if '\0' we've reached the end of the
1580 cd
->RetVal
= RETURN_ERROR
;
1582 if (!(cd
->Flags
& COPYFLAG_QUIET
) && err
== 1)
1584 PrintNotDone(name
, TEXT_OPENED_FOR_OUTPUT
, 1, 1, cd
);
1590 if (cd
->Mode
== COPYMODE_MAKEDIR
&& !cr
&& !(cd
->Flags
& COPYFLAG_QUIET
))
1592 cd
->IoErr
= ERROR_OBJECT_EXISTS
;
1593 PrintNotDone(name
, TEXT_CREATED
, 1, 1, cd
);
1596 return Lock(name
, SHARED_LOCK
);
1600 void PrintName(CONST_STRPTR name
, ULONG deep
, ULONG dir
, ULONG txt
, struct CopyData
*cd
)
1603 deep
%= PRINTOUT_SPACES
; /* reduce number of spaces */
1604 /* This produces an error with MaxonC++ */
1622 if ((deep
= strlen(name
)) > PRINTOUT_SIZE
) /* reduce name size */
1624 name
+= deep
-PRINTOUT_SIZE
;
1629 Printf((dir
? TEXT_DIRECTORY
: (STRPTR
) "%s"), name
);
1640 void PrintNotDone(CONST_STRPTR name
, CONST_STRPTR txt
, ULONG deep
, ULONG dir
, struct CopyData
*cd
)
1642 #if !USE_ALWAYSVERBOSE
1645 PrintName(name
, deep
, dir
, 1, cd
);
1649 Printf(TEXT_NOT_DONE
, txt
);
1653 PrintFault(cd
->IoErr
, NULL
);
1660 /* returns value, when it seems to be a filesystem */
1661 ULONG
TestFileSys(STRPTR name
, struct CopyData
*cd
)
1666 while (*n
&& *n
!= ':')
1677 ret
= IsFileSystem(name
);
1685 void DoWork(STRPTR name
, struct CopyData
*cd
)
1687 BPTR pdir
, lock
= 0;
1688 CONST_STRPTR printerr
= NULL
, printok
= "";
1691 Printf("DoWork(%s, .)\n", name
);
1694 if (cd
->RetVal
> (cd
->Flags
& COPYFLAG_ERRWARN
? RETURN_OK
: RETURN_WARN
) || cd
->RetVal2
)
1697 Printf("DoWork(RetVal %ld)\n", cd
->RetVal
);
1702 if (cd
->Mode
!= COPYMODE_DELETE
&& !(cd
->Flags
& COPYFLAG_DESNOFILESYS
))
1704 if (!cd
->DestPathSize
)
1706 if (!NameFromLock(cd
->CurDest
, cd
->DestName
, FILEPATH_SIZE
))
1708 cd
->RetVal2
= RETURN_FAIL
;
1711 Printf("DoWork(NameFromLock RetVal %ld)\n", cd
->RetVal
);
1717 cd
->DestPathSize
= strlen(cd
->DestName
);
1720 cd
->DestName
[cd
->DestPathSize
] = 0;
1721 AddPart(cd
->DestName
, name
, FILEPATH_SIZE
);
1724 if (cd
->Flags
& (COPYFLAG_SRCNOFILESYS
|COPYFLAG_DESNOFILESYS
))
1728 CONST_STRPTR txt
= TEXT_OPENED_FOR_OUTPUT
;
1731 Printf("Partly DIRECT mode active now (%s - %s)\n", cd
->FileName
,
1735 if ((in
= Open(cd
->FileName
, MODE_OLDFILE
)))
1737 txt
= cd
->Mode
== COPYMODE_MOVE
? TEXT_MOVED
: TEXT_COPIED
;
1739 if ((out
= Open(cd
->DestName
, MODE_NEWFILE
)))
1743 h
= CopyFile(in
, out
, cd
->BufferSize
, cd
);
1745 cd
->IoErr
= IoErr();
1752 if (cd
->Mode
== COPYMODE_MOVE
)
1754 if (KillFile(cd
->FileName
, cd
->Flags
& COPYFLAG_FORCEDELETE
, cd
))
1764 KillFile(cd
->DestName
, 0, cd
);
1768 cd
->IoErr
= IoErr();
1774 cd
->IoErr
= IoErr();
1776 if (!res
&& !(cd
->Flags
& COPYFLAG_QUIET
))
1778 PrintNotDone(name
, txt
, cd
->Deep
, cd
->Fib
.fib_DirEntryType
> 0, cd
);
1782 cd
->Flags
|= COPYFLAG_DONE
;
1784 if ((cd
->Flags
& COPYFLAG_VERBOSE
))
1786 Printf("%s\n", txt
);
1791 PutStr("DoWork(done)\n");
1796 if (!(lock
= Lock(cd
->FileName
, SHARED_LOCK
)))
1798 cd
->RetVal
= RETURN_WARN
;
1799 cd
->IoErr
= IoErr();
1801 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1803 PrintNotDone(cd
->Fib
.fib_FileName
, TEXT_READ
, cd
->Deep
,
1804 cd
->Fib
.fib_DirEntryType
> 0, cd
);
1808 Printf("DoWork(Lock RetVal %ld)\n", cd
->RetVal
);
1813 if (!(pdir
= ParentDir(lock
)))
1815 cd
->RetVal
= RETURN_ERROR
;
1817 if (cd
->Mode
== COPYMODE_DELETE
)
1819 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1821 Printf(" %s ", cd
->FileName
);
1822 Printf(TEXT_NOT_DONE
, TEXT_DELETED
);
1823 Printf("%s\n", TEXT_ERR_DELETE_DEVICE
);
1830 Printf("DoWork(ParentDir %ld)\n", cd
->RetVal
);
1837 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1839 if ((cd
->Flags
& COPYFLAG_VERBOSE
))
1841 PrintName(name
, cd
->Deep
, cd
->Fib
.fib_DirEntryType
> 0, cd
->Fib
.fib_DirEntryType
< 0 ||
1842 (cd
->Flags
& COPYFLAG_ALL
? cd
->Mode
!= COPYMODE_DELETE
: cd
->Mode
!= COPYMODE_COPY
) ||
1843 cd
->Flags
& COPYFLAG_ENTERSECOND
, cd
);
1847 if ((cd
->Flags
& COPYFLAG_ENTERSECOND
) || (cd
->Mode
== COPYMODE_DELETE
&&
1848 (!(cd
->Flags
& COPYFLAG_ALL
) || cd
->Fib
.fib_DirEntryType
< 0)))
1853 if (KillFile(cd
->FileName
, cd
->Flags
& COPYFLAG_FORCEDELETE
, cd
))
1855 printok
= TEXT_DELETED
;
1859 cd
->RetVal
= RETURN_WARN
;
1860 printerr
= TEXT_DELETED
;
1863 else if (cd
->Mode
== COPYMODE_DELETE
)
1867 else if (cd
->Fib
.fib_DirEntryType
> 0)
1871 if ((cd
->Flags
& COPYFLAG_ALL
|| cd
->Mode
== COPYMODE_LINK
||
1872 cd
->Mode
== COPYMODE_MOVE
) && TestLoop(lock
, cd
->CurDest
, cd
))
1875 cd
->RetVal
= RETURN_ERROR
;
1877 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1879 if (!(cd
->Flags
& COPYFLAG_VERBOSE
))
1881 PrintName(name
, cd
->Deep
, 1, 1, cd
);
1884 Printf(TEXT_NOT_DONE
, TEXT_ENTERED
);
1885 PutStr(TEXT_ERR_INFINITE_LOOP
);
1888 else if ((a
= TestDest(cd
->DestName
, 1, cd
)) < 0)
1890 printerr
= TEXT_CREATED
;
1891 cd
->RetVal
= RETURN_ERROR
;
1893 else if (cd
->Flags
& COPYFLAG_ALL
)
1898 cd
->DestPathSize
= 0;
1900 if (a
== TESTDEST_DIR_OK
)
1902 if (!(cd
->CurDest
= Lock(cd
->DestName
, SHARED_LOCK
)))
1904 cd
->IoErr
= IoErr();
1905 printerr
= TEXT_ENTERED
;
1906 cd
->RetVal
= RETURN_ERROR
;
1910 #if USE_ALWAYSVERBOSE
1913 printok
= TEXT_ENTERED
;
1917 else if ((cd
->CurDest
= CreateDir(cd
->DestName
)))
1919 UnLock(cd
->CurDest
);
1921 if ((cd
->CurDest
= Lock(cd
->DestName
, SHARED_LOCK
)))
1923 printok
= TEXT_CREATED
;
1927 cd
->IoErr
= IoErr();
1928 printerr
= TEXT_ENTERED
;
1929 cd
->RetVal
= RETURN_ERROR
;
1934 cd
->IoErr
= IoErr();
1935 printerr
= TEXT_CREATED
;
1936 cd
->RetVal
= RETURN_ERROR
;
1943 else if (i
!= cd
->Destination
)
1948 else if (cd
->Mode
== COPYMODE_MOVE
)
1950 if (Rename(cd
->FileName
, cd
->DestName
))
1952 printok
= TEXT_RENAMED
;
1956 cd
->IoErr
= IoErr();
1957 printerr
= TEXT_RENAMED
;
1958 cd
->RetVal
= RETURN_WARN
;
1961 else if (cd
->Mode
== COPYMODE_LINK
)
1963 if (!(cd
->Flags
& COPYFLAG_FORCELINK
))
1966 cd
->RetVal
= RETURN_WARN
;
1968 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1970 if (!(cd
->Flags
& COPYFLAG_VERBOSE
))
1972 PrintName(name
, cd
->Deep
, 1, 1, cd
);
1975 Printf(TEXT_NOT_DONE
, TEXT_LINKED
);
1976 PutStr(TEXT_ERR_FORCELINK
);
1979 else if (LinkFile(lock
, cd
->DestName
, cd
->Flags
& COPYFLAG_SOFTLINK
, cd
))
1981 printok
= TEXT_LINKED
;
1985 printerr
= TEXT_LINKED
;
1986 cd
->RetVal
= RETURN_WARN
;
1989 else /* COPY mode only displays directories, when not ALL */
1993 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1995 if (cd
->Flags
& COPYFLAG_VERBOSE
)
2004 /* test for existing destination file */
2005 if (TestDest(cd
->DestName
, 0, cd
) < 0)
2007 printerr
= TEXT_OPENED_FOR_OUTPUT
;
2009 else if (cd
->Mode
== COPYMODE_MOVE
&& Rename(cd
->FileName
, cd
->DestName
))
2011 printok
= TEXT_RENAMED
;
2013 else if (cd
->Mode
== COPYMODE_LINK
)
2015 if (!(cd
->Flags
& COPYFLAG_SOFTLINK
) && LinkFile(lock
, cd
->DestName
, 0, cd
))
2017 printok
= TEXT_LINKED
;
2021 printerr
= TEXT_LINKED
;
2022 cd
->RetVal
= RETURN_WARN
;
2024 if (cd
->Flags
& COPYFLAG_SOFTLINK
)
2026 cd
->IoErr
= ERROR_OBJECT_WRONG_TYPE
;
2034 CONST_STRPTR txt
= TEXT_OPENED_FOR_OUTPUT
;
2036 if ((out
= Open(cd
->DestName
, MODE_NEWFILE
)))
2040 txt
= cd
->Mode
== COPYMODE_MOVE
? TEXT_MOVED
: TEXT_COPIED
;
2044 if ((in
= Open(cd
->FileName
, MODE_OLDFILE
)))
2046 h
= CopyFile(in
, out
, cd
->BufferSize
, cd
);
2048 cd
->IoErr
= IoErr();
2057 if (cd
->Mode
== COPYMODE_MOVE
)
2059 if (KillFile(cd
->FileName
, cd
->Flags
& COPYFLAG_FORCEDELETE
, cd
))
2070 else cd
->IoErr
= IoErr();
2079 KillFile(cd
->DestName
, 0, cd
);
2082 else cd
->IoErr
= IoErr();
2087 cd
->RetVal
= RETURN_WARN
;
2096 if (printerr
&& !(cd
->Flags
& COPYFLAG_QUIET
))
2098 PrintNotDone(name
, printerr
, cd
->Deep
, cd
->Fib
.fib_DirEntryType
> 0, cd
);
2102 cd
->Flags
|= COPYFLAG_DONE
;
2104 if (!(cd
->Flags
& COPYFLAG_QUIET
))
2106 if ((cd
->Flags
& COPYFLAG_VERBOSE
))
2108 Printf("%s\n", printok
);
2112 SetData(cd
->DestName
, cd
);
2122 LONG
CopyFile(BPTR from
, BPTR to
, ULONG bufsize
, struct CopyData
*cd
)
2129 buffer
= cd
->CopyBuf
;
2130 bufsize
= cd
->CopyBufLen
;
2136 buffer
= (STRPTR
)AllocMem(bufsize
, MEMF_PUBLIC
);
2139 cd
->CopyBuf
= buffer
;
2140 cd
->CopyBufLen
= bufsize
;
2146 } while (bufsize
>= 512);
2151 #if USE_BOGUSEOFWORKAROUND
2152 struct FileInfoBlock
*fib
= (struct FileInfoBlock
*) buffer
; /* NOTE: bufsize is min 512 bytes */
2154 if (ExamineFH(from
, fib
))
2156 #warning "****** WARNING: No largefile support! ******"
2157 ULONG filesize
= fib
->fib_Size
, copied
= 0;
2159 /*Printf("filesize: %lu\n", filesize);*/
2164 if (brk
|| (s
= Read(from
, buffer
, bufsize
)) == -1 || Write(to
, buffer
, s
) != s
)
2168 cd
->IoErr
= ERROR_BREAK
;
2173 else if (s
== 0 && copied
< filesize
)
2175 /* premature EOF with buggy fs */
2182 } while (copied
< filesize
);
2184 /*{ LONG ioerr = IoErr();
2185 Printf("copied %lu/%lu\n", copied, filesize);
2189 #endif /* USE_BOGUSEOFWORKAROUND */
2191 /* Stream or so, copy until EOF or error */
2195 /* AROS: This flush appears to be required if reading from '*'
2196 * Maybe a bug in Read(), or AROS buffering?
2199 if (brk
|| (s
= Read(from
, buffer
, bufsize
)) == -1 || Write(to
, buffer
, s
) != s
)
2203 cd
->IoErr
= ERROR_BREAK
;
2211 /* Freed at exit to avoid fragmentation */
2212 /*FreeMem(buffer, bufsize);*/
2223 /* Softlink's path starts always with device name! f.e. "Ram Disk:T/..." */
2224 LONG
LinkFile(BPTR from
, STRPTR to
, ULONG soft
, struct CopyData
*cd
)
2231 name
= AllocMem(FILEPATH_SIZE
, MEMF_ANY
);
2234 if (NameFromLock(from
, name
, FILEPATH_SIZE
))
2236 ret
= MakeLink(to
, name
, LINK_SOFT
);
2239 FreeMem(name
, FILEPATH_SIZE
);
2246 return MakeLink(to
, (void *)from
, LINK_HARD
);
2251 /* return 0 means no loop, return != 0 means loop found */
2252 ULONG
TestLoop(BPTR srcdir
, BPTR destdir
, struct CopyData
*cd
)
2259 if (SameDevice(srcdir
, destdir
))
2263 if (!SameLock(srcdir
, lock
))
2269 par
= ParentDir(lock
);
2271 if (lock
!= destdir
)
2279 while(!loop
&& lock
);
2282 if (lock
!= destdir
)
2291 void SetData(STRPTR name
, struct CopyData
*cd
)
2293 if (cd
->Flags
& COPYFLAG_NOPRO
)
2295 /* Is already set! - Piru */
2296 //SetProtection(name, 0);
2300 SetProtection(name
, cd
->Fib
.fib_Protection
& (ULONG
) ~FIBF_ARCHIVE
);
2303 if (cd
->Flags
& COPYFLAG_DATES
)
2305 SetFileDate(name
, &cd
->Fib
.fib_Date
);
2308 if (cd
->Flags
& COPYFLAG_COMMENT
)
2310 SetComment(name
, cd
->Fib
.fib_Comment
);
2315 LONG
TestDest(STRPTR name
, ULONG type
, struct CopyData
*cd
)
2317 LONG ret
= TESTDEST_ERROR
;
2320 if ((lock
= Lock(name
, SHARED_LOCK
)))
2322 struct FileInfoBlock
*fib
;
2324 if ((fib
= (struct FileInfoBlock
*)AllocDosObject(DOS_FIB
, NULL
)))
2326 if (Examine(lock
, fib
))
2333 if (fib
->fib_DirEntryType
> 0)
2335 ret
= TESTDEST_DIR_OK
;
2337 else if (!(cd
->Flags
& COPYFLAG_DONTOVERWRITE
))
2339 if (KillFile(name
, cd
->Flags
& COPYFLAG_FORCEOVERWRITE
, cd
))
2341 ret
= TESTDEST_DELETED
;
2346 ret
= TESTDEST_CANTDELETE
;
2349 else if (cd
->Flags
& COPYFLAG_DONTOVERWRITE
)
2351 if (cd
->Flags
& COPYFLAG_NEWER
)
2353 if( CheckVersion( cd
) == CHECKVER_DESTOLDER
)
2355 if (KillFile(name
, cd
->Flags
& COPYFLAG_FORCEOVERWRITE
, cd
))
2357 ret
= TESTDEST_DELETED
;
2362 ret
= TESTDEST_CANTDELETE
;
2365 else /* normal "dont overwrite mode" */
2367 ret
= TESTDEST_CANTDELETE
;
2370 else if (KillFile(name
, cd
->Flags
& COPYFLAG_FORCEOVERWRITE
, cd
))
2372 ret
= TESTDEST_DELETED
;
2376 FreeDosObject(DOS_FIB
, fib
);
2386 ret
= TESTDEST_NONE
;
2389 if (ret
== TESTDEST_CANTDELETE
)
2391 cd
->IoErr
= ERROR_OBJECT_EXISTS
;
2393 else if (ret
== TESTDEST_ERROR
)
2395 cd
->IoErr
= IoErr();
2402 ** We compare current file versions and return the result
2403 ** see CHECKVER_#? values
2406 static LONG
CheckVersion( struct CopyData
*cd
)
2408 struct VersionData vds
;
2409 struct VersionData vdd
;
2410 LONG resversion
= CHECKVER_EQUAL
;
2411 LONG resdate
= CHECKVER_EQUAL
;
2413 if( VersionFind( cd
->FileName
, &vds
, cd
) )
2415 if( VersionFind( cd
->DestName
, &vdd
, cd
) )
2417 /* version and revision must be available to ensure a proper operation */
2418 if( ((vdd
.vd_Version
!= -1) && (vds
.vd_Version
!= -1) && (vdd
.vd_Revision
!= -1) && (vds
.vd_Revision
!= -1)) )
2420 /* first we make the stuff comparable. If one component is missing we reset both */
2421 if( vdd
.vd_Year
== -1 || vds
.vd_Year
== -1 )
2426 if( vdd
.vd_Month
== -1 || vds
.vd_Month
== -1 )
2431 if( vdd
.vd_Day
== -1 || vds
.vd_Day
== -1 )
2438 resversion
= CHECKVER_DESTOLDER
;
2439 if( ((vdd
.vd_Version
== vds
.vd_Version
) && vdd
.vd_Revision
== vds
.vd_Revision
) )
2441 resversion
= CHECKVER_EQUAL
;
2443 else if( (vdd
.vd_Version
> vds
.vd_Version
) ||
2444 ((vdd
.vd_Version
== vds
.vd_Version
) && vdd
.vd_Revision
> vds
.vd_Revision
) )
2446 resversion
= CHECKVER_DESTNEWER
;
2450 resdate
= CHECKVER_DESTOLDER
;
2451 if( ((vdd
.vd_Year
== vds
.vd_Year
) && (vdd
.vd_Month
== vds
.vd_Month
) && (vdd
.vd_Day
== vds
.vd_Day
) ) )
2453 resdate
= CHECKVER_EQUAL
;
2457 if( ( (vdd
.vd_Year
> vds
.vd_Year
) ||
2458 ( (vdd
.vd_Year
== vds
.vd_Year
) && (vdd
.vd_Month
> vds
.vd_Month
) ) ||
2459 ( (vdd
.vd_Year
== vds
.vd_Year
) && (vdd
.vd_Month
== vds
.vd_Month
) && (vdd
.vd_Day
> vds
.vd_Day
) ) ))
2461 resdate
= CHECKVER_DESTNEWER
;
2465 /* plausible check */
2466 if( ((resversion
== CHECKVER_DESTNEWER
) && (resdate
== CHECKVER_DESTOLDER
)) || /* newer version with older date */
2467 ((resversion
== CHECKVER_DESTOLDER
) && (resdate
== CHECKVER_DESTNEWER
)) ) /* older version with newer date */
2469 /* we maybe should inform the user about this */
2470 return( CHECKVER_EQUAL
);
2475 /* compose result */
2477 if( (resversion
== resdate
) || (resversion
== CHECKVER_EQUAL
) )
2483 return( resversion
);
2490 ** Searches the given file for a version string and fills version data struct with the result.
2491 ** Returns false if no version was found. Returns true if the version got parsed and version data
2495 #define VERSBUFFERSIZE 4096 /* must be as big as the biggest version string we want to handle. */
2497 static BOOL
VersionFind( CONST_STRPTR path
, struct VersionData
*vds
, struct CopyData
*cd
)
2505 if ( (buf
= AllocVec(VERSBUFFERSIZE
, MEMF_PUBLIC
| MEMF_CLEAR
)) )
2507 if ( (handle
= Open(path
, MODE_OLDFILE
)) )
2511 while( ( (index
+= Read(handle
, &buf
[index
], VERSBUFFERSIZE
-index
)) > 5) && !rc
)
2513 for (i
= 0; i
< index
-5; i
++) {
2514 if( buf
[i
] == '$' && buf
[i
+1] == 'V' && buf
[i
+2] == 'E' && buf
[i
+3] == 'R' && buf
[i
+4] == ':' ) {
2515 CopyMem( &buf
[i
], buf
, index
-i
);
2517 (index
+= Read(handle
, &buf
[index
], VERSBUFFERSIZE
-index
));
2518 /* now the version string is aligned and complete in buffer */
2519 makeversionfromstring( buf
, vds
, cd
);
2524 CopyMem( &buf
[index
-5], &buf
[0], 5 );
2536 ** This function extracts the version information from a given version string.
2537 ** the result will be store in the given version data structure.
2539 ** NOTE: There is no need to preset the version data struct. All fields will
2540 ** be reset to defaults, so in case of a faulty version string the result data
2541 ** will be checkable.
2545 void makeversionfromstring( STRPTR buffer
, struct VersionData
*vd
, struct CopyData
*cd
)
2551 /* reset data field */
2553 vd
->vd_Name
[0] = '\0';
2554 vd
->vd_Version
= -1;
2555 vd
->vd_Revision
= -1;
2560 buffer
= skipspaces( buffer
); /* skip before $VER: */
2561 buffer
= skipnonspaces( buffer
); /* skip $VER: */
2562 buffer
= skipspaces( buffer
); /* skip spaces before tool name */
2564 buffer
= skipnonspaces( buffer
); /* skip name of tool */
2566 if( (tmp
= ((long) buffer
- (long) name
) ) && *buffer
)
2568 CopyMem( name
, vd
->vd_Name
, MIN( tmp
, VDNAMESIZE
-1) );
2569 vd
->vd_Name
[MIN( tmp
, VDNAMESIZE
-1)] = '\0'; /* terminate name string inside target buffer */
2571 buffer
= skipspaces( buffer
); /* skip spaces before version */
2576 if( (pos
= StrToLong((STRPTR
) buffer
, &tmp
)) != -1 )
2578 vd
->vd_Version
= tmp
;
2583 buffer
= skipspaces(buffer
);
2584 if (*buffer
++ == '.')
2586 if( (pos
= StrToLong((STRPTR
) buffer
, &tmp
)) != -1 )
2588 vd
->vd_Revision
= tmp
;
2590 buffer
= skipspaces(buffer
);
2591 if (*buffer
++ == '(')
2593 if( (pos
= StrToLong((STRPTR
) buffer
, &tmp
)) != -1 )
2597 if (*buffer
++ == '.')
2599 if( (pos
= StrToLong((STRPTR
) buffer
, &tmp
)) != -1 )
2603 if (*buffer
++ == '.')
2605 if( (pos
= StrToLong((STRPTR
) buffer
, &tmp
)) != -1 )
2607 if( (tmp
>= 70) && (tmp
<= 99) )
2629 /* Return a pointer to a string, stripped by all leading space characters
2633 STRPTR
skipspaces( STRPTR buffer
)
2637 if (buffer
[0] == '\0' || buffer
[0] != ' ')
2644 /* Return a pointer to a string, stripped by all non space characters
2648 STRPTR
skipnonspaces( STRPTR buffer
)
2652 if (buffer
[0] == '\0' || buffer
[0] == ' ')