2 Copyright © 2001-2007, 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 -- do 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 3.3.2001 -- AROSified by Johan 'S.Duvan' Alfredsson
290 29.7.2002 -- Fixed silly IoErr trashing bug, bumped to 50.1 - Piru
291 7.11.2002 -- Fixed even silier bug where it would put out a bogus
292 errormessage when the copy destination was a volume
293 root. bumped to 50.2 - Piru
294 6.3.2007 -- Fixed small signed bug. bumped to 50.15 - Geit
295 7.3.2006 -- Implemented new copy mode named "NEWER" which scans for
296 version information and only copies over a file if it is
297 newer than an existing target. bumped to 50.16 - Geit
299 ******************************************************************************/
301 #define CTRL_C (SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
305 /* Enabled softlinks check for testing. Define this to 0 in case of problems.
306 Pavel Fedin <sonic_amiga@rambler.ru> */
307 #define USE_SOFTLINKCHECK 1
309 #define USE_ALWAYSVERBOSE 1
310 #define USE_BOGUSEOFWORKAROUND 0
313 #include <aros/asmcall.h>
314 #include <exec/devices.h>
316 #include <exec/memory.h>
317 #include <exec/semaphores.h>
318 #include <exec/types.h>
319 #include <dos/exall.h>
324 #include <aros/debug.h>
327 #include <proto/dos.h>
328 #include <proto/exec.h>
333 const TEXT version
[] = "\0$VER: Copy 50.16 (7.3.2007)";
335 static const UBYTE
*PARAM
=
336 "FROM/M,TO,PAT=PATTERN/K,BUF=BUFFER/K/N,ALL/S,"
337 "DIRECT/S,CLONE/S,DATES/S,NOPRO/S,COM=COMMENT/S,"
339 #if !USE_ALWAYSVERBOSE
342 "NOREQ/S,ERRWARN/S,MAKEDIR/S,"
343 "MOVE/S,DELETE/S,HARD=HARDLINK/S,SOFT=SOFTLINK/S,"
344 "FOLNK=FORCELINK/S,FODEL=FORCEDELETE/S,"
345 "FOOVR=FORCEOVERWRITE/S,DONTOVR=DONTOVERWRITE/S,"
348 #define COPYFLAG_ALL (1<<0)
349 #define COPYFLAG_DATES (1<<1)
350 #define COPYFLAG_NOPRO (1<<2)
351 #define COPYFLAG_COMMENT (1<<3)
352 #define COPYFLAG_FORCELINK (1<<4)
353 #define COPYFLAG_FORCEDELETE (1<<5)
354 #define COPYFLAG_FORCEOVERWRITE (1<<6)
355 #define COPYFLAG_DONTOVERWRITE (1<<7)
356 #define COPYFLAG_QUIET (1<<8)
357 #define COPYFLAG_VERBOSE (1<<9)
358 #define COPYFLAG_ERRWARN (1<<10)
359 #define COPYFLAG_PROTECTION (1<<11)
360 #define COPYFLAG_NEWER (1<<12)
362 #define COPYFLAG_SOFTLINK (1<<20) /* produce softlinks */
363 #define COPYFLAG_DEST_FILE (1<<21) /* one file mode */
364 #define COPYFLAG_DONE (1<<22) /* did something in DoWork */
365 #define COPYFLAG_ENTERSECOND (1<<23) /* entered directory second time */
367 #define COPYFLAG_SRCNOFILESYS (1<<24) /* source is no filesystem */
368 #define COPYFLAG_DESNOFILESYS (1<<25) /* destination is no filesystem */
370 #define COPYMODE_COPY 0
371 #define COPYMODE_MOVE 1
372 #define COPYMODE_DELETE 2
373 #define COPYMODE_MAKEDIR 3
374 #define COPYMODE_LINK 4
376 #define PRINTOUT_SIZE 50 /* maximum size of name printout */
377 #define PRINTOUT_SPACES 10 /* maximum number of spaces */
379 #define FILEPATH_SIZE 2048 /* maximum size of filepaths */
382 #define TESTDEST_DIR_OK 2 /* directory exists, go in */
383 #define TESTDEST_DELETED 1 /* file or empty directory deleted */
384 #define TESTDEST_NONE 0 /* nothing existed */
385 #define TESTDEST_ERROR -1 /* an error occured */
386 #define TESTDEST_CANTDELETE -2 /* deletion not allowed (DONTOV) */
401 #if !USE_ALWAYSVERBOSE
452 struct ExecBase
*SysBase
;
453 struct DosLibrary
*DOSBase
;
459 BPTR CurDest
; /* Current Destination */
461 struct FileInfoBlock Fib
;
463 UBYTE RetVal
; /* when set, error output is already done */
464 UBYTE RetVal2
; /* when set, error output must be done */
466 UBYTE FileName
[FILEPATH_SIZE
];
467 UBYTE DestName
[FILEPATH_SIZE
];
474 ** This data keeps the extracted data from a version string
477 #define VDNAMESIZE 96
480 UBYTE vd_Name
[VDNAMESIZE
];
490 #define MIN(a,b) ((a)<(b)?(a):(b))
493 #define CHECKVER_DESTOLDER -1
494 #define CHECKVER_DESTNEWER 1
495 #define CHECKVER_EQUAL 0
497 #define TEXT_READ texts[0]
498 #define TEXT_COPIED texts[1]
499 #define TEXT_MOVED texts[2]
500 #define TEXT_DELETED texts[3]
501 #define TEXT_LINKED texts[4]
502 #define TEXT_RENAMED texts[5]
503 #define TEXT_CREATED texts[6]
504 #define TEXT_ENTERED texts[7]
505 #define TEXT_OPENED_FOR_OUTPUT texts[8]
506 #define TEXTNUM_MODE 9
507 #define TEXT_DIRECTORY texts[15]
508 #define TEXT_NOT_DONE texts[16]
509 #define TEXT_NOTHING_DONE texts[17]
510 #define TEXT_ERR_FORCELINK texts[18]
511 #define TEXT_ERR_DELETE_DEVICE texts[19]
512 #define TEXT_ERR_DEST_DIR texts[20]
513 #define TEXT_ERR_INFINITE_LOOP texts[21]
514 #define TEXT_ERR_WILDCARD_DEST texts[22]
516 const CONST_STRPTR texts
[] =
533 "%s (Dir)", /* output of directories */
535 "No file was processed.\n",
536 "FORCELINK keyword required.\n",
537 "A device cannot be deleted.",
538 "Destination must be a directory.\n",
539 "Infinite loop not allowed.\n",
540 "Wildcard destination invalid.\n",
543 LONG
CopyFile(BPTR
, BPTR
, ULONG
, struct CopyData
*);
544 void DoWork(STRPTR
, struct CopyData
*);
545 LONG
IsMatchPattern(STRPTR name
, struct CopyData
*cd
);
546 LONG
IsPattern(STRPTR
, struct CopyData
*); /* return 0 -> NOPATTERN, return -1 --> ERROR */
547 LONG
KillFile(STRPTR
, ULONG
, struct CopyData
*);
548 LONG
KillFileKeepErr(STRPTR name
, ULONG doit
, struct CopyData
*);
549 LONG
LinkFile(BPTR
, STRPTR
, ULONG
, struct CopyData
*);
550 BPTR
OpenDestDir(STRPTR
, struct CopyData
*);
551 void PatCopy(STRPTR
, struct CopyData
*);
552 void PrintName(CONST_STRPTR
, ULONG
, ULONG
, ULONG
, struct CopyData
*);
553 void PrintNotDone(CONST_STRPTR
, CONST_STRPTR
, ULONG
, ULONG
, struct CopyData
*);
554 ULONG
TestFileSys(STRPTR
, struct CopyData
*); /* returns value, when is a filesystem */
555 void SetData(STRPTR
, struct CopyData
*);
556 LONG
TestDest(STRPTR
, ULONG
, struct CopyData
*);
557 ULONG
TestLoop(BPTR
, BPTR
, struct CopyData
*);
558 static LONG
CheckVersion( struct CopyData
*cd
);
559 static void makeversionfromstring( STRPTR buffer
, struct VersionData
*vd
, struct CopyData
*cd
);
560 static STRPTR
skipspaces( STRPTR buffer
);
561 static STRPTR
skipnonspaces( STRPTR buffer
);
562 static BOOL
VersionFind( CONST_STRPTR path
, struct VersionData
*vds
, struct CopyData
*cd
);
564 AROS_UFH3(__startup
static int, Start
,
565 AROS_UFHA(char *, argstr
, A0
),
566 AROS_UFHA(ULONG
, argsize
, D0
),
567 AROS_UFHA(struct ExecBase
*, SysBase
, A6
))
571 struct DosLibrary
*DOSBase
;
572 struct Process
*task
;
574 int retval
= RETURN_FAIL
;
576 /* test for WB and reply startup-message */
577 if (!(task
= (struct Process
*)FindTask(NULL
))->pr_CLI
)
579 WaitPort(&task
->pr_MsgPort
);
581 ReplyMsg(GetMsg(&task
->pr_MsgPort
));
586 DOSBase
= (struct DosLibrary
*)OpenLibrary("dos.library", 37);
587 cd
= AllocMem(sizeof(*cd
), MEMF_PUBLIC
| MEMF_CLEAR
);
591 STRPTR a
[2] = { "", 0 };
593 struct IptrArgs iArgs
;
596 cd
->SysBase
= SysBase
;
597 cd
->DOSBase
= DOSBase
;
598 #define SysBase cd->SysBase
599 #define DOSBase cd->DOSBase
601 cd
->BufferSize
= 512*1024;
602 cd
->Mode
= COPYMODE_COPY
;
603 cd
->RetVal2
= RETURN_FAIL
;
606 memset(&iArgs
, 0, sizeof(struct IptrArgs
));
608 rda
= (struct RDArgs
*)AllocDosObject(DOS_RDARGS
, NULL
);
612 "FROM multiple input files\n"
613 "TO destination file or directory\n"
614 "PATTERN a pattern the filenames must match\n"
615 "BUFFER buffersize for copy buffer (default 200 [100K])\n"
616 "ALL deep scan into sub directories\n"
617 "DIRECT copy/delete only: work without any tests or options\n"
618 "CLONE copy comment, protection bits and date as well\n"
620 "NOPRO do not copy protection bits\n"
621 "COMMENT copy filecomment\n"
622 "QUIET suppress all output and requesters\n"
623 #if !USE_ALWAYSVERBOSE
624 "VERBOSE give additional output\n"
626 "NOREQ suppress requesters\n"
627 "ERRWARN do not proceed, when one file failed\n"
628 "MAKEDIR produce directories\n"
629 "MOVE delete source files after copying successful\n"
630 "DELETE do not copy, but delete the source files\n"
631 "HARDLINK make a hardlink to source instead of copying\n"
632 "SOFTLINK make a softlink to source instead of copying\n"
633 "FOLNK also makes links to directories\n"
634 "FODEL delete protected files also\n"
635 "FOOVR also overwrite protected files\n"
636 "DONTOVR do never overwrite destination\n"
637 "FORCE DO NOT USE. Call compatibility only.\n"
638 "NEWER will compare version strings and only overwrites older files\n";
640 if (ReadArgs(PARAM
, (IPTR
*)&iArgs
, rda
))
642 ULONG patbufsize
= 0;
644 APTR win
= task
->pr_WindowPtr
;
646 args
.from
= (STRPTR
*)iArgs
.from
;
647 args
.to
= (STRPTR
)iArgs
.to
;
648 args
.pattern
= (STRPTR
)iArgs
.pattern
;
649 args
.buffer
= (LONG
*)iArgs
.buffer
;
650 args
.all
= (LONG
)iArgs
.all
;
651 args
.direct
= (LONG
)iArgs
.direct
;
652 args
.clone
= (LONG
)iArgs
.clone
;
653 args
.dates
= (LONG
)iArgs
.dates
;
654 args
.nopro
= (LONG
)iArgs
.nopro
;
655 args
.comment
= (LONG
)iArgs
.comment
;
656 args
.quiet
= (LONG
)iArgs
.quiet
;
657 #if USE_ALWAYSVERBOSE
658 args
.verbose
= FALSE
;
660 args
.verbose
= (LONG
)iArgs
.verbose
;
662 args
.noreq
= (LONG
)iArgs
.noreq
;
663 args
.errwarn
= (LONG
)iArgs
.errwarn
;
664 args
.makedir
= (LONG
)iArgs
.makedir
;
665 args
.move_mode
= (LONG
)iArgs
.move_mode
;
666 args
.delete_mode
= (LONG
)iArgs
.delete_mode
;
667 args
.hardlink
= (LONG
)iArgs
.hardlink
;
668 args
.softlink
= (LONG
)iArgs
.softlink
;
669 args
.forcelink
= (LONG
)iArgs
.forcelink
;
670 args
.forcedelete
= (LONG
)iArgs
.forcedelete
;
671 args
.forceoverwrite
= (LONG
)iArgs
.forceoverwrite
;
672 args
.dontoverwrite
= (LONG
)iArgs
.dontoverwrite
;
673 args
.force
= (LONG
)iArgs
.force
;
674 args
.newer
= (LONG
)iArgs
.newer
;
676 if (args
.quiet
) /* when QUIET, SILENT and NOREQ are also
679 /* Original doesn't hide requesters with QUIET */
681 args
.verbose
= FALSE
;
684 if (args
.buffer
&& *args
.buffer
> 0) /* minimum buffer size */
686 cd
->BufferSize
= *args
.buffer
* 512;
691 cd
->Flags
|= COPYFLAG_QUIET
;
694 #if !USE_ALWAYSVERBOSE
697 cd
->Flags
|= COPYFLAG_VERBOSE
;
702 cd
->Flags
|= COPYFLAG_ALL
;
705 /* 12-jul-03 bugfix: always copy protection flags! -Piru */
706 cd
->Flags
|= COPYFLAG_PROTECTION
;
709 cd
->Flags
|= COPYFLAG_DATES
| COPYFLAG_COMMENT
| COPYFLAG_PROTECTION
;
714 cd
->Flags
|= COPYFLAG_DATES
;
719 cd
->Flags
|= COPYFLAG_COMMENT
;
724 cd
->Flags
|= COPYFLAG_NOPRO
;
729 cd
->Flags
|= COPYFLAG_FORCELINK
;
732 if (args
.forcedelete
)
734 cd
->Flags
|= COPYFLAG_FORCEDELETE
;
737 if (args
.forceoverwrite
)
739 cd
->Flags
|= COPYFLAG_FORCEOVERWRITE
;
742 if (args
.dontoverwrite
)
744 cd
->Flags
|= COPYFLAG_DONTOVERWRITE
;
749 cd
->Flags
|= COPYFLAG_NEWER
|COPYFLAG_DONTOVERWRITE
;
754 cd
->Flags
|= COPYFLAG_ERRWARN
;
757 if (args
.force
) /* support OS Delete and MakeLink command
760 if (args
.delete_mode
)
762 cd
->Flags
|= COPYFLAG_FORCEDELETE
;
765 if (args
.hardlink
|| args
.softlink
)
767 cd
->Flags
|= COPYFLAG_FORCELINK
;
771 if (!args
.from
) /* no args.from means currentdir */
776 if (args
.noreq
) /* no dos.library requests allowed */
778 task
->pr_WindowPtr
= (APTR
)-1;
781 if (args
.delete_mode
)
784 cd
->Mode
= COPYMODE_DELETE
;
790 cd
->Mode
= COPYMODE_MOVE
;
796 cd
->Mode
= COPYMODE_MAKEDIR
;
802 cd
->Mode
= COPYMODE_LINK
;
808 cd
->Mode
= COPYMODE_LINK
;
809 cd
->Flags
|= COPYFLAG_SOFTLINK
| COPYFLAG_FORCELINK
;
812 if (cd
->Mode
!= COPYMODE_DELETE
&&
813 cd
->Mode
!= COPYMODE_MAKEDIR
&& !args
.to
)
815 if (*(args
.from
+ 1)) /* when no TO is specified, the arg
817 { /* one of from. Copy this argument into */
818 STRPTR
*a
; /* args.to */
829 #if USE_ALWAYSVERBOSE
831 /* Only do this if quiet isn't set - bigfoot */
834 /* If more than two args, be verbose... - Piru */
835 if (args
.from
[0] && args
.from
[1])
838 cd
->Flags
|= COPYFLAG_VERBOSE
;
841 /* If any of the sources is a pattern, be verbose... - Piru */
845 for (a
= args
.from
; *a
; a
++)
847 if (IsMatchPattern(*a
, cd
) != 0)
850 cd
->Flags
|= COPYFLAG_VERBOSE
;
857 /* test if more than one of the above four or any other wrong
861 (args
.from
== a
&& cd
->Mode
== COPYMODE_MAKEDIR
) ||
862 (args
.direct
&& (args
.from
== a
|| !*args
.from
||
864 (cd
->Flags
& ~(COPYFLAG_QUIET
| COPYFLAG_VERBOSE
| COPYFLAG_ERRWARN
)) ||
865 (cd
->Mode
!= COPYMODE_DELETE
&& (cd
->Mode
!= COPYMODE_COPY
||
866 !args
.to
|| args
.from
[1])))) ||
867 (args
.dontoverwrite
&& args
.forceoverwrite
) ||
868 /* (args.nopro && args.clone) ||*/ /* Ignore, like original - Piru */
869 (args
.softlink
&& args
.all
) ||
870 (!args
.to
&& cd
->Mode
!= COPYMODE_DELETE
&& cd
->Mode
!= COPYMODE_MAKEDIR
))
872 SetIoErr(ERROR_TOO_MANY_ARGS
);
874 else if (cd
->Mode
== COPYMODE_MAKEDIR
)
878 cd
->RetVal2
= RETURN_OK
;
880 #if !USE_ALWAYSVERBOSE
883 PutStr(texts
[TEXTNUM_MODE
+ COPYMODE_MAKEDIR
]);
887 while (!cd
->RetVal
&& !cd
->RetVal2
&& *args
.from
)
889 if ((i
= IsPattern(*args
.from
, cd
)))
893 cd
->RetVal
= RETURN_ERROR
;
897 PutStr(TEXT_ERR_WILDCARD_DEST
);
902 cd
->RetVal2
= RETURN_FAIL
;
906 if ((dir
= OpenDestDir(*args
.from
, cd
)))
909 cd
->Flags
|= COPYFLAG_DONE
;
914 } /* cd->Mode == COPYMODE_MAKEDIR */
915 else if (args
.direct
)
917 if (cd
->Mode
== COPYMODE_COPY
)
921 if ((in
= Open(*args
.from
, MODE_OLDFILE
)))
923 if ((out
= Open(args
.to
, MODE_NEWFILE
)))
925 cd
->RetVal2
= CopyFile(in
, out
, cd
->BufferSize
, cd
);
932 else /* COPYMODE_DELETE */
936 KillFile(*(args
.from
++), cd
->Flags
& COPYFLAG_FORCEDELETE
, cd
);
939 cd
->RetVal2
= RETURN_OK
;
944 if (args
.pattern
&& *args
.pattern
)
946 patbufsize
= (strlen(args
.pattern
) << 1) + 3;
948 if ((cd
->Pattern
= (STRPTR
)AllocMem(patbufsize
,
951 if (ParsePatternNoCase(args
.pattern
, cd
->Pattern
,
954 FreeMem(cd
->Pattern
, patbufsize
);
960 if (1) // (cd->Fib = (struct FileInfoBlock *)AllocDosObject(DOS_FIB, NULL)))
962 #if !USE_ALWAYSVERBOSE
965 PutStr(texts
[TEXTNUM_MODE
+ cd
->Mode
+
966 (cd
->Flags
& COPYFLAG_SOFTLINK
? 1 : 0)]);
969 if (args
.pattern
&& !cd
->Pattern
)
973 SetIoErr(ERROR_BAD_TEMPLATE
);
976 else if (cd
->Mode
== COPYMODE_DELETE
)
978 cd
->RetVal2
= RETURN_OK
;
980 while (cd
->RetVal
<= (args
.errwarn
? RETURN_OK
: RETURN_WARN
)
983 PatCopy(*(args
.from
++), cd
);
986 else if ((i
= IsPattern(args
.to
, cd
)))
990 cd
->RetVal
= RETURN_ERROR
;
994 PutStr(TEXT_ERR_WILDCARD_DEST
);
1002 if (*(path
= PathPart(args
.to
)) == '/')
1004 ++path
; /* is destination a path description ? */
1007 if (*path
&& !*(args
.from
+1) &&
1008 !(i
= IsMatchPattern(*args
.from
, cd
)))
1013 /* is destination an existing directory */
1014 if ((lock
= Lock(args
.to
, SHARED_LOCK
)))
1016 if (Examine(lock
, &cd
->Fib
))
1018 if (cd
->Fib
.fib_DirEntryType
> 0)
1020 cd
->RetVal2
= RETURN_OK
;
1023 /* indicate dir-mode for next if */
1033 /* Some magic to handle tick quoted pattern object names. Quite crude way to
1034 * handle it, but I couldn't think of anything better. - Piru
1037 if (!i
&& cd
->RetVal2
&& !IsMatchPattern(*args
.from
, cd
))
1042 //Printf("pattern check <%s>\n", *args.from);
1044 len
= (strlen(*args
.from
) << 1) + 3;
1046 if ((pat
= (STRPTR
)AllocMem(len
,
1049 if (ParsePattern(*args
.from
, pat
, len
) > -1 &&
1050 strlen(pat
) <= strlen(*args
.from
))
1052 lock
= Lock(pat
, SHARED_LOCK
);
1057 strcpy(*args
.from
, pat
);
1066 /* is source a directory */
1067 if (!i
&& cd
->RetVal2
&&
1068 (lock
= Lock(*args
.from
, SHARED_LOCK
)))
1070 if (Examine(lock
, &cd
->Fib
))
1072 cd
->RetVal2
= RETURN_OK
;
1073 if (cd
->Mode
!= COPYMODE_COPY
||
1074 cd
->Fib
.fib_DirEntryType
< 0)
1078 cd
->Flags
|= COPYFLAG_DEST_FILE
;
1080 /* produce missing destination directories */
1084 if ((cd
->CurDest
= OpenDestDir(args
.to
, cd
)))
1091 CopyMem(*args
.from
, cd
->FileName
,
1092 1 + strlen(*args
.from
));
1093 DoWork(FilePart(args
.to
), cd
); /* on file call */
1094 UnLock(cd
->CurDest
);
1105 lockioerr
= IoErr(); /* We save ioerr here, because TestFileSys changes it */
1107 if (lock
== 0 && cd
->Mode
== COPYMODE_COPY
&& !TestFileSys(*args
.from
, cd
))
1110 cd
->Flags
|= COPYFLAG_DEST_FILE
| COPYFLAG_SRCNOFILESYS
;
1111 cd
->RetVal2
= RETURN_OK
;
1113 /* produce missing destination directories */
1117 if ((cd
->CurDest
= OpenDestDir(args
.to
, cd
)))
1122 CopyMem(*args
.from
, cd
->FileName
, 1 + strlen(*args
.from
));
1123 DoWork(FilePart(args
.to
), cd
); /* on file call */
1124 UnLock(cd
->CurDest
);
1128 SetIoErr(lockioerr
);
1132 cd
->RetVal2
= RETURN_OK
;
1135 if (!cd
->RetVal
&& !cd
->RetVal2
&& !(cd
->Flags
& COPYFLAG_DEST_FILE
) &&
1136 (cd
->Destination
= OpenDestDir(args
.to
, cd
)))
1138 while (cd
->RetVal
<= (args
.errwarn
? RETURN_OK
: RETURN_WARN
)
1139 && *args
.from
&& !CTRL_C
)
1141 PatCopy(*(args
.from
++), cd
);
1144 UnLock(cd
->Destination
);
1148 if (!(cd
->Flags
& COPYFLAG_DONE
) && args
.verbose
&&
1149 !cd
->RetVal
&& !cd
->RetVal2
)
1151 PutStr(TEXT_NOTHING_DONE
);
1158 FreeMem(cd
->Pattern
, patbufsize
);
1162 task
->pr_WindowPtr
= win
;
1167 FreeDosObject(DOS_RDARGS
, rda
);
1168 } /* AllocDosObject */
1170 if (!cd
->RetVal2
&& CTRL_C
)
1172 SetIoErr(ERROR_BREAK
);
1173 cd
->RetVal2
= RETURN_WARN
;
1176 if (cd
->RetVal2
&& !args
.quiet
&& !cd
->RetVal
)
1178 PrintFault(IoErr(), NULL
);
1183 cd
->RetVal2
= cd
->RetVal
;
1186 if (args
.errwarn
&& cd
->RetVal2
== RETURN_WARN
)
1188 cd
->RetVal2
= RETURN_ERROR
;
1193 FreeMem(cd
->CopyBuf
, cd
->CopyBufLen
);
1201 retval
= cd
->RetVal2
;
1202 FreeMem(cd
, sizeof(*cd
));
1206 PrintFault(IoErr(), NULL
);
1210 CloseLibrary((struct Library
*)DOSBase
);
1217 #define SysBase cd->SysBase
1218 #define DOSBase cd->DOSBase
1220 void PatCopy(STRPTR name
, struct CopyData
*cd
)
1222 struct AnchorPath
*APath
;
1223 ULONG retval
, doit
= 0, deep
= 0, failval
= RETURN_WARN
, first
= 0;
1226 Printf("PatCopy(%s, .)\n", name
);
1229 if ((cd
->Mode
== COPYMODE_COPY
|| (cd
->Flags
& COPYFLAG_ALL
)) && !IsMatchPattern(name
, cd
))
1231 first
= 1; /* enter first directory (support of old copy style) */
1234 if (cd
->Flags
& COPYFLAG_ERRWARN
)
1236 failval
= RETURN_OK
;
1239 cd
->CurDest
= cd
->Destination
;
1240 cd
->DestPathSize
= 0;
1242 if (cd
->Mode
== COPYMODE_COPY
&& !TestFileSys(name
, cd
))
1244 cd
->Flags
|= COPYFLAG_SRCNOFILESYS
;
1245 CopyMem(name
, cd
->FileName
, 1 + strlen(name
));
1246 DoWork(FilePart(name
), cd
);
1247 cd
->Flags
&= ~COPYFLAG_SRCNOFILESYS
;
1252 if ((APath
= (struct AnchorPath
*)AllocMem(sizeof(struct AnchorPath
) + FILEPATH_SIZE
,
1253 MEMF_PUBLIC
| MEMF_CLEAR
)))
1255 int parentdirerr
= 0;
1257 APath
->ap_BreakBits
= SIGBREAKF_CTRL_C
;
1258 APath
->ap_Strlen
= FILEPATH_SIZE
;
1260 for (retval
= MatchFirst(name
, APath
);
1261 !retval
&& cd
->RetVal
<= failval
&& !cd
->RetVal2
;
1262 retval
= MatchNext(APath
)
1267 //Printf("ParentDir() fuxored last round! Would copy next files to SYS: !\n");
1270 SetIoErr(retval
= ERROR_INVALID_LOCK
);
1276 DoWork(cd
->Fib
.fib_FileName
, cd
);
1280 if (deep
) /* used for Deep checking */
1286 cd
->Flags
&= ~COPYFLAG_ENTERSECOND
;
1288 CopyMem(APath
->ap_Buf
, cd
->FileName
, FILEPATH_SIZE
);
1289 CopyMem(&APath
->ap_Info
, &cd
->Fib
, sizeof(struct FileInfoBlock
));
1291 if (first
&& APath
->ap_Info
.fib_DirEntryType
> 0)
1293 #if USE_ALWAYSVERBOSE
1294 /* If the source is a directory, be verbose - Piru */
1295 cd
->Flags
|= COPYFLAG_VERBOSE
;
1297 APath
->ap_Flags
|= APF_DODIR
;
1299 else if (APath
->ap_Flags
& APF_DIDDIR
)
1303 cd
->Flags
|= COPYFLAG_ENTERSECOND
;
1304 APath
->ap_Flags
&= ~APF_DIDDIR
;
1307 if (cd
->Mode
== COPYMODE_DELETE
|| cd
->Mode
== COPYMODE_MOVE
)
1312 if ((i
= cd
->CurDest
))
1314 cd
->CurDest
= ParentDir(i
);
1315 cd
->DestPathSize
= 0;
1317 if (i
!= cd
->Destination
)
1329 else if (APath
->ap_Info
.fib_DirEntryType
> 0)
1333 if (cd
->Flags
& COPYFLAG_ALL
)
1335 #if USE_SOFTLINKCHECK
1340 dirlock
= CurrentDir(APath
->ap_Current
->an_Lock
);
1341 lock
= Lock(APath
->ap_Info
.fib_FileName
, ACCESS_READ
);
1346 struct DevProc
*dvp
;
1347 LONG ioerr
= IoErr();
1349 if (ioerr
== ERROR_OBJECT_NOT_FOUND
&&
1350 (dvp
= GetDeviceProc("", NULL
)))
1352 #define BUFFERSIZE 512
1353 UBYTE
*buffer
= AllocMem(BUFFERSIZE
, MEMF_PUBLIC
);
1357 if (ReadLink(dvp
->dvp_Port
, dvp
->dvp_Lock
, APath
->ap_Info
.fib_FileName
, buffer
, BUFFERSIZE
- 1) > 0)
1359 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1361 buffer
[BUFFERSIZE
- 1] = '\0';
1363 Printf("Warning: Skipping dangling softlink %s -> %s\n",
1364 APath
->ap_Info
.fib_FileName
, buffer
);
1370 FreeMem(buffer
, BUFFERSIZE
);
1373 FreeDeviceProc(dvp
);
1378 CurrentDir(dirlock
);
1382 APath
->ap_Flags
|= APF_DODIR
;
1386 #else /* USE_SOFTLINKCHECK */
1388 APath
->ap_Flags
|= APF_DODIR
;
1391 #endif /* USE_SOFTLINKCHECK */
1394 else if (!cd
->Pattern
|| MatchPatternNoCase(cd
->Pattern
, APath
->ap_Info
.fib_FileName
))
1404 if (retval
&& retval
!= ERROR_NO_MORE_ENTRIES
)
1406 LONG ioerr
= IoErr();
1407 #if USE_ALWAYSVERBOSE
1408 cd
->Flags
|= COPYFLAG_VERBOSE
;
1410 Printf("%s - ", name
);
1411 PrintFault(ioerr
, NULL
);
1414 cd
->RetVal2
= RETURN_FAIL
;
1419 DoWork(cd
->Fib
.fib_FileName
, cd
);
1422 /* No need to clear the flags here, as they are cleared on next PatJoin
1423 call (DoWork is not called first round, as lock is zero!). */
1425 FreeMem(APath
, sizeof(struct AnchorPath
) + FILEPATH_SIZE
);
1429 cd
->RetVal
= RETURN_FAIL
;
1431 if (!cd
->Flags
& COPYFLAG_QUIET
)
1433 PrintFault(ERROR_NO_FREE_STORE
, NULL
);
1437 if (cd
->CurDest
&& cd
->CurDest
!= cd
->Destination
)
1439 UnLock(cd
->CurDest
);
1444 LONG
IsPattern(STRPTR name
, struct CopyData
*cd
)
1449 a
= (strlen(name
) << 1) + 3;
1451 if ((buffer
= (STRPTR
)AllocMem(a
, MEMF_ANY
)))
1453 ret
= ParsePattern(name
, buffer
, a
);
1459 SetIoErr(ERROR_NO_FREE_STORE
);
1466 LONG
IsMatchPattern(STRPTR name
, struct CopyData
*cd
)
1468 struct AnchorPath ap
;
1471 ap
.ap_BreakBits
= 0;
1472 ap
.ap_Flags
= APF_DOWILD
;
1475 if (MatchFirst(name
, &ap
) == 0)
1477 ret
= (ap
.ap_Flags
& APF_ITSWILD
) ? TRUE
: FALSE
;
1486 LONG
KillFile(STRPTR name
, ULONG doit
, struct CopyData
*cd
)
1490 SetProtection(name
, 0);
1493 return DeleteFile(name
);
1497 LONG
KillFileKeepErr(STRPTR name
, ULONG doit
, struct CopyData
*cd
)
1502 ret
= KillFile(name
, doit
, cd
);
1509 BPTR
OpenDestDir(STRPTR name
, struct CopyData
*cd
)
1511 LONG a
, err
= 0, cr
= 0;
1516 if ((cd
->Mode
== COPYMODE_COPY
|| cd
->Mode
== COPYMODE_MOVE
) && !TestFileSys(name
, cd
))
1518 cd
->Flags
|= COPYFLAG_DESNOFILESYS
;
1519 CopyMem(name
, cd
->DestName
, 1 + strlen(name
));
1521 return Lock("", SHARED_LOCK
);
1524 while (!err
&& *ptr
!= 0)
1526 while (*ptr
&& *ptr
!= '/')
1534 if ((a
= TestDest(name
, 1, cd
)) == TESTDEST_CANTDELETE
)
1536 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1538 PutStr(TEXT_ERR_DEST_DIR
);
1547 else if (a
!= TESTDEST_DIR_OK
)
1549 if ((dir
= CreateDir(name
)))
1553 if ((cd
->Flags
& COPYFLAG_VERBOSE
))
1555 PrintName(name
, 1, 1, 1, cd
);
1556 Printf("%s\n", TEXT_CREATED
);
1563 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1565 PrintNotDone(name
, TEXT_CREATED
, 1, 1, cd
);
1574 /* 26-Oct-2003 bugfix: Don't scan past end of the string.
1575 * as is the old char, if '\0' we've reached the end of the
1586 cd
->RetVal
= RETURN_ERROR
;
1588 if (!(cd
->Flags
& COPYFLAG_QUIET
) && err
== 1)
1590 PrintNotDone(name
, TEXT_OPENED_FOR_OUTPUT
, 1, 1, cd
);
1596 if (cd
->Mode
== COPYMODE_MAKEDIR
&& !cr
&& !(cd
->Flags
& COPYFLAG_QUIET
))
1598 SetIoErr(ERROR_OBJECT_EXISTS
);
1599 PrintNotDone(name
, TEXT_CREATED
, 1, 1, cd
);
1602 return Lock(name
, SHARED_LOCK
);
1606 void PrintName(CONST_STRPTR name
, ULONG deep
, ULONG dir
, ULONG txt
, struct CopyData
*cd
)
1609 deep
%= PRINTOUT_SPACES
; /* reduce number of spaces */
1610 /* This produces an error with MaxonC++ */
1628 if ((deep
= strlen(name
)) > PRINTOUT_SIZE
) /* reduce name size */
1630 name
+= deep
-PRINTOUT_SIZE
;
1635 Printf((dir
? TEXT_DIRECTORY
: (STRPTR
) "%s"), name
);
1646 void PrintNotDone(CONST_STRPTR name
, CONST_STRPTR txt
, ULONG deep
, ULONG dir
, struct CopyData
*cd
)
1648 #if !USE_ALWAYSVERBOSE
1651 PrintName(name
, deep
, dir
, 1, cd
);
1655 Printf(TEXT_NOT_DONE
, txt
);
1656 PrintFault(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
;
1712 Printf("DoWork(NameFromLock RetVal %ld)\n", cd
->RetVal
);
1718 cd
->DestPathSize
= strlen(cd
->DestName
);
1721 cd
->DestName
[cd
->DestPathSize
] = 0;
1722 AddPart(cd
->DestName
, name
, FILEPATH_SIZE
);
1725 if (cd
->Flags
& (COPYFLAG_SRCNOFILESYS
|COPYFLAG_DESNOFILESYS
))
1727 ULONG res
= 0, kill
= 1;
1729 CONST_STRPTR txt
= TEXT_OPENED_FOR_OUTPUT
;
1732 Printf("Partly DIRECT mode active now (%s - %s)\n", cd
->FileName
,
1736 if ((out
= Open(cd
->DestName
, MODE_NEWFILE
)))
1738 txt
= cd
->Mode
== COPYMODE_MOVE
? TEXT_MOVED
: TEXT_COPIED
;
1740 if ((in
= Open(cd
->FileName
, MODE_OLDFILE
)))
1744 h
= CopyFile(in
, out
, cd
->BufferSize
, cd
);
1745 Close(out
); out
= NULL
;
1752 if (cd
->Mode
== COPYMODE_MOVE
)
1754 if (KillFile(cd
->FileName
, cd
->Flags
& COPYFLAG_FORCEDELETE
, cd
))
1773 KillFileKeepErr(cd
->DestName
, 0, cd
);
1777 if (!res
&& !(cd
->Flags
& COPYFLAG_QUIET
))
1779 PrintNotDone(cd
->Flags
& COPYFLAG_VERBOSE
? name
: 0,
1780 txt
, cd
->Deep
, cd
->Fib
.fib_DirEntryType
> 0, cd
);
1784 cd
->Flags
|= COPYFLAG_DONE
;
1786 if ((cd
->Flags
& COPYFLAG_VERBOSE
))
1788 Printf("%s\n", txt
);
1793 PutStr("DoWork(done)\n");
1798 if (!(lock
= Lock(cd
->FileName
, SHARED_LOCK
)))
1800 cd
->RetVal
= RETURN_WARN
;
1802 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1804 PrintNotDone(cd
->Fib
.fib_FileName
, TEXT_READ
, cd
->Deep
,
1805 cd
->Fib
.fib_DirEntryType
> 0, cd
);
1809 Printf("DoWork(Lock RetVal %ld)\n", cd
->RetVal
);
1814 if (!(pdir
= ParentDir(lock
)))
1816 cd
->RetVal
= RETURN_ERROR
;
1818 if (cd
->Mode
== COPYMODE_DELETE
)
1820 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1822 Printf(" %s ", cd
->FileName
);
1823 Printf(TEXT_NOT_DONE
, TEXT_DELETED
);
1824 Printf("%s\n", TEXT_ERR_DELETE_DEVICE
);
1831 Printf("DoWork(ParentDir %ld)\n", cd
->RetVal
);
1838 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1840 if ((cd
->Flags
& COPYFLAG_VERBOSE
))
1842 PrintName(name
, cd
->Deep
, cd
->Fib
.fib_DirEntryType
> 0, cd
->Fib
.fib_DirEntryType
< 0 ||
1843 (cd
->Flags
& COPYFLAG_ALL
? cd
->Mode
!= COPYMODE_DELETE
: cd
->Mode
!= COPYMODE_COPY
) ||
1844 cd
->Flags
& COPYFLAG_ENTERSECOND
, cd
);
1848 if ((cd
->Flags
& COPYFLAG_ENTERSECOND
) || (cd
->Mode
== COPYMODE_DELETE
&&
1849 (!(cd
->Flags
& COPYFLAG_ALL
) || cd
->Fib
.fib_DirEntryType
< 0)))
1854 if (KillFile(cd
->FileName
, cd
->Flags
& COPYFLAG_FORCEDELETE
, cd
))
1856 printok
= TEXT_DELETED
;
1860 cd
->RetVal
= RETURN_WARN
;
1861 printerr
= TEXT_DELETED
;
1864 else if (cd
->Mode
== COPYMODE_DELETE
)
1868 else if (cd
->Fib
.fib_DirEntryType
> 0)
1872 if ((cd
->Flags
& COPYFLAG_ALL
|| cd
->Mode
== COPYMODE_LINK
||
1873 cd
->Mode
== COPYMODE_MOVE
) && TestLoop(lock
, cd
->CurDest
, cd
))
1876 cd
->RetVal
= RETURN_ERROR
;
1878 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1880 if (!(cd
->Flags
& COPYFLAG_VERBOSE
))
1882 PrintName(name
, cd
->Deep
, 1, 1, cd
);
1885 Printf(TEXT_NOT_DONE
, TEXT_ENTERED
);
1886 PutStr(TEXT_ERR_INFINITE_LOOP
);
1889 else if ((a
= TestDest(cd
->DestName
, 1, cd
)) < 0)
1891 printerr
= TEXT_CREATED
;
1892 cd
->RetVal
= RETURN_ERROR
;
1894 else if (cd
->Flags
& COPYFLAG_ALL
)
1899 cd
->DestPathSize
= 0;
1901 if (a
== TESTDEST_DIR_OK
)
1903 if (!(cd
->CurDest
= Lock(cd
->DestName
, SHARED_LOCK
)))
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 printerr
= TEXT_ENTERED
;
1928 cd
->RetVal
= RETURN_ERROR
;
1933 printerr
= TEXT_CREATED
;
1934 cd
->RetVal
= RETURN_ERROR
;
1941 else if (i
!= cd
->Destination
)
1946 else if (cd
->Mode
== COPYMODE_MOVE
)
1948 if (Rename(cd
->FileName
, cd
->DestName
))
1950 printok
= TEXT_RENAMED
;
1954 printerr
= TEXT_RENAMED
;
1955 cd
->RetVal
= RETURN_WARN
;
1958 else if (cd
->Mode
== COPYMODE_LINK
)
1960 if (!(cd
->Flags
& COPYFLAG_FORCELINK
))
1963 cd
->RetVal
= RETURN_WARN
;
1965 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1967 if (!(cd
->Flags
& COPYFLAG_VERBOSE
))
1969 PrintName(name
, cd
->Deep
, 1, 1, cd
);
1972 Printf(TEXT_NOT_DONE
, TEXT_LINKED
);
1973 PutStr(TEXT_ERR_FORCELINK
);
1976 else if (LinkFile(lock
, cd
->DestName
, cd
->Flags
& COPYFLAG_SOFTLINK
, cd
))
1978 printok
= TEXT_LINKED
;
1982 printerr
= TEXT_LINKED
;
1983 cd
->RetVal
= RETURN_WARN
;
1986 else /* COPY mode only displays directories, when not ALL */
1990 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1992 if (cd
->Flags
& COPYFLAG_VERBOSE
)
2001 /* test for existing destination file */
2002 if (TestDest(cd
->DestName
, 0, cd
) < 0)
2004 printerr
= TEXT_OPENED_FOR_OUTPUT
;
2006 else if (cd
->Mode
== COPYMODE_MOVE
&& Rename(cd
->FileName
, cd
->DestName
))
2008 printok
= TEXT_RENAMED
;
2010 else if (cd
->Mode
== COPYMODE_LINK
)
2012 if (!(cd
->Flags
& COPYFLAG_SOFTLINK
) && LinkFile(lock
, cd
->DestName
, 0, cd
))
2014 printok
= TEXT_LINKED
;
2018 printerr
= TEXT_LINKED
;
2019 cd
->RetVal
= RETURN_WARN
;
2021 if (cd
->Flags
& COPYFLAG_SOFTLINK
)
2023 SetIoErr(ERROR_OBJECT_WRONG_TYPE
);
2031 CONST_STRPTR txt
= TEXT_OPENED_FOR_OUTPUT
;
2033 if ((out
= Open(cd
->DestName
, MODE_NEWFILE
)))
2037 txt
= cd
->Mode
== COPYMODE_MOVE
? TEXT_MOVED
: TEXT_COPIED
;
2041 if ((in
= Open(cd
->FileName
, MODE_OLDFILE
)))
2043 h
= CopyFile(in
, out
, cd
->BufferSize
, cd
);
2044 Close(out
); out
= NULL
;
2051 if (cd
->Mode
== COPYMODE_MOVE
)
2053 if (KillFile(cd
->FileName
, cd
->Flags
& COPYFLAG_FORCEDELETE
, cd
))
2072 KillFileKeepErr(cd
->DestName
, 0, cd
);
2079 cd
->RetVal
= RETURN_WARN
;
2088 if (printerr
&& !(cd
->Flags
& COPYFLAG_QUIET
))
2090 PrintNotDone(cd
->Flags
& COPYFLAG_VERBOSE
? name
: 0,
2091 printerr
, cd
->Deep
, cd
->Fib
.fib_DirEntryType
> 0, cd
);
2095 cd
->Flags
|= COPYFLAG_DONE
;
2097 if (!(cd
->Flags
& COPYFLAG_QUIET
))
2099 if ((cd
->Flags
& COPYFLAG_VERBOSE
))
2101 Printf("%s\n", printok
);
2105 SetData(cd
->DestName
, cd
);
2115 LONG
CopyFile(BPTR from
, BPTR to
, ULONG bufsize
, struct CopyData
*cd
)
2122 buffer
= cd
->CopyBuf
;
2123 bufsize
= cd
->CopyBufLen
;
2129 buffer
= (STRPTR
)AllocMem(bufsize
, MEMF_PUBLIC
);
2132 cd
->CopyBuf
= buffer
;
2133 cd
->CopyBufLen
= bufsize
;
2139 } while (bufsize
>= 512);
2144 #if USE_BOGUSEOFWORKAROUND
2145 struct FileInfoBlock
*fib
= (struct FileInfoBlock
*) buffer
; /* NOTE: bufsize is min 512 bytes */
2147 if (ExamineFH(from
, fib
))
2149 #warning "****** WARNING: No largefile support! ******"
2150 ULONG filesize
= fib
->fib_Size
, copied
= 0;
2152 /*Printf("filesize: %lu\n", filesize);*/
2157 if (brk
|| (s
= Read(from
, buffer
, bufsize
)) == -1 || Write(to
, buffer
, s
) != s
)
2161 SetIoErr(ERROR_BREAK
);
2166 else if (s
== 0 && copied
< filesize
)
2168 /* premature EOF with buggy fs */
2175 } while (copied
< filesize
);
2177 /*{ LONG ioerr = IoErr();
2178 Printf("copied %lu/%lu\n", copied, filesize);
2182 #endif /* USE_BOGUSEOFWORKAROUND */
2184 /* Stream or so, copy until EOF or error */
2188 if (brk
|| (s
= Read(from
, buffer
, bufsize
)) == -1 || Write(to
, buffer
, s
) != s
)
2192 SetIoErr(ERROR_BREAK
);
2197 } while (s
== bufsize
);
2200 /* Freed at exit to avoid fragmentation */
2201 /*FreeMem(buffer, bufsize);*/
2212 /* Softlink's path starts always with device name! f.e. "Ram Disk:T/..." */
2213 LONG
LinkFile(BPTR from
, STRPTR to
, ULONG soft
, struct CopyData
*cd
)
2220 name
= AllocMem(FILEPATH_SIZE
, MEMF_ANY
);
2223 if (NameFromLock(from
, name
, FILEPATH_SIZE
))
2225 ret
= MakeLink(to
, name
, LINK_SOFT
);
2228 FreeMem(name
, FILEPATH_SIZE
);
2235 return MakeLink(to
, from
, LINK_HARD
);
2240 /* return 0 means no loop, return != 0 means loop found */
2241 ULONG
TestLoop(BPTR srcdir
, BPTR destdir
, struct CopyData
*cd
)
2248 if (SameDevice(srcdir
, destdir
))
2252 if (!SameLock(srcdir
, lock
))
2258 par
= ParentDir(lock
);
2260 if (lock
!= destdir
)
2268 while(!loop
&& lock
);
2271 if (lock
!= destdir
)
2280 void SetData(STRPTR name
, struct CopyData
*cd
)
2282 if (cd
->Flags
& COPYFLAG_NOPRO
)
2284 /* Is already set! - Piru */
2285 //SetProtection(name, 0);
2287 else if (cd
->Flags
& COPYFLAG_PROTECTION
)
2289 SetProtection(name
, cd
->Fib
.fib_Protection
& (ULONG
) ~FIBF_ARCHIVE
);
2292 if (cd
->Flags
& COPYFLAG_DATES
)
2294 SetFileDate(name
, &cd
->Fib
.fib_Date
);
2297 if (cd
->Flags
& COPYFLAG_COMMENT
)
2299 SetComment(name
, cd
->Fib
.fib_Comment
);
2304 LONG
TestDest(STRPTR name
, ULONG type
, struct CopyData
*cd
)
2306 LONG ret
= TESTDEST_ERROR
;
2309 if ((lock
= Lock(name
, SHARED_LOCK
)))
2311 struct FileInfoBlock
*fib
;
2313 if ((fib
= (struct FileInfoBlock
*)AllocDosObject(DOS_FIB
, NULL
)))
2315 if (Examine(lock
, fib
))
2322 if (fib
->fib_DirEntryType
> 0)
2324 ret
= TESTDEST_DIR_OK
;
2326 else if (!(cd
->Flags
& COPYFLAG_DONTOVERWRITE
))
2328 if (KillFile(name
, cd
->Flags
& COPYFLAG_FORCEOVERWRITE
, cd
))
2330 ret
= TESTDEST_DELETED
;
2335 ret
= TESTDEST_CANTDELETE
;
2338 else if (cd
->Flags
& COPYFLAG_DONTOVERWRITE
)
2340 if (cd
->Flags
& COPYFLAG_NEWER
)
2342 if( CheckVersion( cd
) == CHECKVER_DESTOLDER
)
2344 if (KillFile(name
, cd
->Flags
& COPYFLAG_FORCEOVERWRITE
, cd
))
2346 ret
= TESTDEST_DELETED
;
2351 ret
= TESTDEST_CANTDELETE
;
2354 else /* normal "dont overwrite mode" */
2356 ret
= TESTDEST_CANTDELETE
;
2359 else if (KillFile(name
, cd
->Flags
& COPYFLAG_FORCEOVERWRITE
, cd
))
2361 ret
= TESTDEST_DELETED
;
2365 FreeDosObject(DOS_FIB
, fib
);
2375 ret
= TESTDEST_NONE
;
2378 if (ret
== TESTDEST_CANTDELETE
)
2380 SetIoErr(ERROR_OBJECT_EXISTS
);
2387 ** We compare current file versions and return the result
2388 ** see CHECKVER_#? values
2391 static LONG
CheckVersion( struct CopyData
*cd
)
2393 struct VersionData vds
;
2394 struct VersionData vdd
;
2395 LONG resversion
= CHECKVER_EQUAL
;
2396 LONG resdate
= CHECKVER_EQUAL
;
2398 if( VersionFind( cd
->FileName
, &vds
, cd
) )
2400 if( VersionFind( cd
->DestName
, &vdd
, cd
) )
2402 /* version and revision must be available to ensure a proper operation */
2403 if( ((vdd
.vd_Version
!= -1) && (vds
.vd_Version
!= -1) && (vdd
.vd_Revision
!= -1) && (vds
.vd_Revision
!= -1)) )
2405 /* first we make the stuff comparable. If one component is missing we reset both */
2406 if( vdd
.vd_Year
== -1 || vds
.vd_Year
== -1 )
2411 if( vdd
.vd_Month
== -1 || vds
.vd_Month
== -1 )
2416 if( vdd
.vd_Day
== -1 || vds
.vd_Day
== -1 )
2423 resversion
= CHECKVER_DESTOLDER
;
2424 if( ((vdd
.vd_Version
== vds
.vd_Version
) && vdd
.vd_Revision
== vds
.vd_Revision
) )
2426 resversion
= CHECKVER_EQUAL
;
2428 else if( (vdd
.vd_Version
> vds
.vd_Version
) ||
2429 ((vdd
.vd_Version
== vds
.vd_Version
) && vdd
.vd_Revision
> vds
.vd_Revision
) )
2431 resversion
= CHECKVER_DESTNEWER
;
2435 resdate
= CHECKVER_DESTOLDER
;
2436 if( ((vdd
.vd_Year
== vds
.vd_Year
) && (vdd
.vd_Month
== vds
.vd_Month
) && (vdd
.vd_Day
== vds
.vd_Day
) ) )
2438 resdate
= CHECKVER_EQUAL
;
2442 if( ( (vdd
.vd_Year
> vds
.vd_Year
) ||
2443 ( (vdd
.vd_Year
== vds
.vd_Year
) && (vdd
.vd_Month
> vds
.vd_Month
) ) ||
2444 ( (vdd
.vd_Year
== vds
.vd_Year
) && (vdd
.vd_Month
== vds
.vd_Month
) && (vdd
.vd_Day
> vds
.vd_Day
) ) ))
2446 resdate
= CHECKVER_DESTNEWER
;
2450 /* plausible check */
2451 if( ((resversion
== CHECKVER_DESTNEWER
) && (resdate
== CHECKVER_DESTOLDER
)) || /* newer version with older date */
2452 ((resversion
== CHECKVER_DESTOLDER
) && (resdate
== CHECKVER_DESTNEWER
)) ) /* older version with newer date */
2454 /* we maybe should inform the user about this */
2455 return( CHECKVER_EQUAL
);
2460 /* compose result */
2462 if( (resversion
== resdate
) || (resversion
== CHECKVER_EQUAL
) )
2468 return( resversion
);
2475 ** Searches the given file for a version string and fills version data struct with the result.
2476 ** Returns false if no version was found. Returns true if the version got parsed and version data
2480 #define VERSBUFFERSIZE 4096 /* must be as big as the biggest version string we want to handle. */
2482 static BOOL
VersionFind( CONST_STRPTR path
, struct VersionData
*vds
, struct CopyData
*cd
)
2490 if ( (buf
= AllocVec(VERSBUFFERSIZE
, MEMF_PUBLIC
| MEMF_CLEAR
)) )
2492 if ( (handle
= Open(path
, MODE_OLDFILE
)) )
2496 while( ( (index
+= Read(handle
, &buf
[index
], VERSBUFFERSIZE
-index
)) > 5) && !rc
)
2498 for (i
= 0; i
< index
-5; i
++) {
2499 if( buf
[i
] == '$' && buf
[i
+1] == 'V' && buf
[i
+2] == 'E' && buf
[i
+3] == 'R' && buf
[i
+4] == ':' ) {
2500 CopyMem( &buf
[i
], buf
, index
-i
);
2502 (index
+= Read(handle
, &buf
[index
], VERSBUFFERSIZE
-index
));
2503 /* now the version string is aligned and complete in buffer */
2504 makeversionfromstring( buf
, vds
, cd
);
2509 CopyMem( &buf
[index
-5], &buf
[0], 5 );
2521 ** This function extracts the version information from a given version string.
2522 ** the result will be store in the given version data structure.
2524 ** NOTE: There is no need to preset the version data struct. All fields will
2525 ** be reset to defaults, so in case of a faulty version string the result data
2526 ** will be checkable.
2530 void makeversionfromstring( STRPTR buffer
, struct VersionData
*vd
, struct CopyData
*cd
)
2536 /* reset data field */
2538 vd
->vd_Name
[0] = '\0';
2539 vd
->vd_Version
= -1;
2540 vd
->vd_Revision
= -1;
2545 buffer
= skipspaces( buffer
); /* skip before $VER: */
2546 buffer
= skipnonspaces( buffer
); /* skip $VER: */
2547 buffer
= skipspaces( buffer
); /* skip spaces before tool name */
2549 buffer
= skipnonspaces( buffer
); /* skip name of tool */
2551 if( (tmp
= ((int) buffer
- (int) name
) ) && *buffer
)
2553 CopyMem( name
, vd
->vd_Name
, MIN( tmp
, VDNAMESIZE
-1) );
2554 vd
->vd_Name
[MIN( tmp
, VDNAMESIZE
-1)] = '\0'; /* terminate name string inside target buffer */
2556 buffer
= skipspaces( buffer
); /* skip spaces before version */
2561 if( (pos
= StrToLong((STRPTR
) buffer
, &tmp
)) != -1 )
2563 vd
->vd_Version
= tmp
;
2568 buffer
= skipspaces(buffer
);
2569 if (*buffer
++ == '.')
2571 if( (pos
= StrToLong((STRPTR
) buffer
, &tmp
)) != -1 )
2573 vd
->vd_Revision
= tmp
;
2575 buffer
= skipspaces(buffer
);
2576 if (*buffer
++ == '(')
2578 if( (pos
= StrToLong((STRPTR
) buffer
, &tmp
)) != -1 )
2582 if (*buffer
++ == '.')
2584 if( (pos
= StrToLong((STRPTR
) buffer
, &tmp
)) != -1 )
2588 if (*buffer
++ == '.')
2590 if( (pos
= StrToLong((STRPTR
) buffer
, &tmp
)) != -1 )
2592 if( (tmp
>= 70) && (tmp
<= 99) )
2614 /* Return a pointer to a string, stripped by all leading space characters
2618 STRPTR
skipspaces( STRPTR buffer
)
2622 if (buffer
[0] == '\0' || buffer
[0] != ' ')
2629 /* Return a pointer to a string, stripped by all non space characters
2633 STRPTR
skipnonspaces( STRPTR buffer
)
2637 if (buffer
[0] == '\0' || buffer
[0] == ' ')