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 delete 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 it's 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)
306 #warning "FIXME: Not trusting softlinks in AROS DOS!"
307 #define USE_SOFTLINKCHECK 0
309 #define USE_SOFTLINKCHECK 1
312 #define USE_ALWAYSVERBOSE 1
313 #define USE_BOGUSEOFWORKAROUND 0
317 #include <exec/devices.h>
319 #include <exec/memory.h>
320 #include <exec/semaphores.h>
321 #include <exec/types.h>
322 #include <dos/exall.h>
327 #include <aros/debug.h>
330 #include <proto/dos.h>
331 #include <proto/exec.h>
336 const TEXT version
[] = "\0$VER: Copy 50.16 (7.3.2007)";
338 static const UBYTE
*PARAM
=
339 "FROM/M,TO,PAT=PATTERN/K,BUF=BUFFER/K/N,ALL/S,"
340 "DIRECT/S,CLONE/S,DATES/S,NOPRO/S,COM=COMMENT/S,"
342 #if !USE_ALWAYSVERBOSE
345 "NOREQ/S,ERRWARN/S,MAKEDIR/S,"
346 "MOVE/S,DELETE/S,HARD=HARDLINK/S,SOFT=SOFTLINK/S,"
347 "FOLNK=FORCELINK/S,FODEL=FORCEDELETE/S,"
348 "FOOVR=FORCEOVERWRITE/S,DONTOVR=DONTOVERWRITE/S,"
351 #define COPYFLAG_ALL (1<<0)
352 #define COPYFLAG_DATES (1<<1)
353 #define COPYFLAG_NOPRO (1<<2)
354 #define COPYFLAG_COMMENT (1<<3)
355 #define COPYFLAG_FORCELINK (1<<4)
356 #define COPYFLAG_FORCEDELETE (1<<5)
357 #define COPYFLAG_FORCEOVERWRITE (1<<6)
358 #define COPYFLAG_DONTOVERWRITE (1<<7)
359 #define COPYFLAG_QUIET (1<<8)
360 #define COPYFLAG_VERBOSE (1<<9)
361 #define COPYFLAG_ERRWARN (1<<10)
362 #define COPYFLAG_PROTECTION (1<<11)
363 #define COPYFLAG_NEWER (1<<12)
365 #define COPYFLAG_SOFTLINK (1<<20) /* produce softlinks */
366 #define COPYFLAG_DEST_FILE (1<<21) /* one file mode */
367 #define COPYFLAG_DONE (1<<22) /* did something in DoWork */
368 #define COPYFLAG_ENTERSECOND (1<<23) /* entered directory second time */
370 #define COPYFLAG_SRCNOFILESYS (1<<24) /* source is no filesystem */
371 #define COPYFLAG_DESNOFILESYS (1<<25) /* destination is no filesystem */
373 #define COPYMODE_COPY 0
374 #define COPYMODE_MOVE 1
375 #define COPYMODE_DELETE 2
376 #define COPYMODE_MAKEDIR 3
377 #define COPYMODE_LINK 4
379 #define PRINTOUT_SIZE 50 /* maximum size of name printout */
380 #define PRINTOUT_SPACES 10 /* maximum number of spaces */
382 #define FILEPATH_SIZE 2048 /* maximum size of filepaths */
385 #define TESTDEST_DIR_OK 2 /* directory exists, go in */
386 #define TESTDEST_DELETED 1 /* file or empty directory deleted */
387 #define TESTDEST_NONE 0 /* nothing existed */
388 #define TESTDEST_ERROR -1 /* an error occured */
389 #define TESTDEST_CANTDELETE -2 /* deletion not allowed (DONTOV) */
404 #if !USE_ALWAYSVERBOSE
455 struct ExecBase
*SysBase
;
456 struct DosLibrary
*DOSBase
;
462 BPTR CurDest
; /* Current Destination */
464 struct FileInfoBlock Fib
;
466 UBYTE RetVal
; /* when set, error output is already done */
467 UBYTE RetVal2
; /* when set, error output must be done */
469 UBYTE FileName
[FILEPATH_SIZE
];
470 UBYTE DestName
[FILEPATH_SIZE
];
477 ** This data keeps the extracted data from a version string
480 #define VDNAMESIZE 96
483 UBYTE vd_Name
[VDNAMESIZE
];
493 #define MIN(a,b) ((a)<(b)?(a):(b))
496 #define CHECKVER_DESTOLDER -1
497 #define CHECKVER_DESTNEWER 1
498 #define CHECKVER_EQUAL 0
500 #define TEXT_READ texts[0]
501 #define TEXT_COPIED texts[1]
502 #define TEXT_MOVED texts[2]
503 #define TEXT_DELETED texts[3]
504 #define TEXT_LINKED texts[4]
505 #define TEXT_RENAMED texts[5]
506 #define TEXT_CREATED texts[6]
507 #define TEXT_ENTERED texts[7]
508 #define TEXT_OPENED_FOR_OUTPUT texts[8]
509 #define TEXTNUM_MODE 9
510 #define TEXT_DIRECTORY texts[15]
511 #define TEXT_NOT_DONE texts[16]
512 #define TEXT_NOTHING_DONE texts[17]
513 #define TEXT_ERR_FORCELINK texts[18]
514 #define TEXT_ERR_DELETE_DEVICE texts[19]
515 #define TEXT_ERR_DEST_DIR texts[20]
516 #define TEXT_ERR_INFINITE_LOOP texts[21]
517 #define TEXT_ERR_WILDCARD_DEST texts[22]
519 const CONST_STRPTR texts
[] =
536 "%s (Dir)", /* output of directories */
538 "No file was processed.\n",
539 "FORCELINK keyword required.\n",
540 "A device cannot be deleted.",
541 "Destination must be a directory.\n",
542 "Infinite loop not allowed.\n",
543 "Wildcard destination invalid.\n",
546 LONG
CopyFile(BPTR
, BPTR
, ULONG
, struct CopyData
*);
547 void DoWork(STRPTR
, struct CopyData
*);
548 LONG
IsMatchPattern(STRPTR name
, struct CopyData
*cd
);
549 LONG
IsPattern(STRPTR
, struct CopyData
*); /* return 0 -> NOPATTERN, return -1 --> ERROR */
550 LONG
KillFile(STRPTR
, ULONG
, struct CopyData
*);
551 LONG
KillFileKeepErr(STRPTR name
, ULONG doit
, struct CopyData
*);
552 LONG
LinkFile(BPTR
, STRPTR
, ULONG
, struct CopyData
*);
553 BPTR
OpenDestDir(STRPTR
, struct CopyData
*);
554 void PatCopy(STRPTR
, struct CopyData
*);
555 void PrintName(CONST_STRPTR
, ULONG
, ULONG
, ULONG
, struct CopyData
*);
556 void PrintNotDone(CONST_STRPTR
, CONST_STRPTR
, ULONG
, ULONG
, struct CopyData
*);
557 ULONG
TestFileSys(STRPTR
, struct CopyData
*); /* returns value, when is a filesystem */
558 void SetData(STRPTR
, struct CopyData
*);
559 LONG
TestDest(STRPTR
, ULONG
, struct CopyData
*);
560 ULONG
TestLoop(BPTR
, BPTR
, struct CopyData
*);
561 static LONG
CheckVersion( struct CopyData
*cd
);
562 static void makeversionfromstring( STRPTR buffer
, struct VersionData
*vd
, struct CopyData
*cd
);
563 static STRPTR
skipspaces( STRPTR buffer
);
564 static STRPTR
skipnonspaces( STRPTR buffer
);
565 static BOOL
VersionFind( CONST_STRPTR path
, struct VersionData
*vds
, struct CopyData
*cd
);
570 struct ExecBase
*SysBase
;
571 struct DosLibrary
*DOSBase
;
573 struct Process
*task
;
575 int retval
= RETURN_FAIL
;
578 SysBase
=*((struct ExecBase
**) 4);
581 /* test for WB and reply startup-message */
582 if (!(task
= (struct Process
*)FindTask(NULL
))->pr_CLI
)
584 WaitPort(&task
->pr_MsgPort
);
586 ReplyMsg(GetMsg(&task
->pr_MsgPort
));
592 DOSBase
= (struct DosLibrary
*)OpenLibrary("dos.library", 37);
594 cd
= AllocMem(sizeof(*cd
), MEMF_PUBLIC
| MEMF_CLEAR
);
598 STRPTR a
[2] = { "", 0 };
600 struct IptrArgs iArgs
;
603 cd
->SysBase
= SysBase
;
604 cd
->DOSBase
= DOSBase
;
605 #define SysBase cd->SysBase
606 #define DOSBase cd->DOSBase
608 cd
->BufferSize
= 512*1024;
609 cd
->Mode
= COPYMODE_COPY
;
610 cd
->RetVal2
= RETURN_FAIL
;
613 memset(&iArgs
, 0, sizeof(struct IptrArgs
));
615 rda
= (struct RDArgs
*)AllocDosObject(DOS_RDARGS
, NULL
);
619 "FROM multiple input files\n"
620 "TO destination file or directory\n"
621 "PATTERN a pattern the filenames must match\n"
622 "BUFFER buffersize for copy buffer (default 200 [100K])\n"
623 "ALL deep scan into sub directories\n"
624 "DIRECT copy/delete only: work without any tests or options\n"
625 "CLONE copy comment, protection bits and date as well\n"
627 "NOPRO do not copy protection bits\n"
628 "COMMENT copy filecomment\n"
629 "QUIET suppress all output and requesters\n"
630 #if !USE_ALWAYSVERBOSE
631 "VERBOSE give additional output\n"
633 "NOREQ suppress requesters\n"
634 "ERRWARN do not proceed, when one file failed\n"
635 "MAKEDIR produce directories\n"
636 "MOVE delete source files after copying successful\n"
637 "DELETE do not copy, but delete the source files\n"
638 "HARDLINK make a hardlink to source instead of copying\n"
639 "SOFTLINK make a softlink to source instead of copying\n"
640 "FOLNK also makes links to directories\n"
641 "FODEL delete protected files also\n"
642 "FOOVR also overwrite protected files\n"
643 "DONTOVR do never overwrite destination\n"
644 "FORCE DO NOT USE. Call compatibility only.\n"
645 "NEWER will compare version strings and only overwrites older files\n";
647 if (ReadArgs(PARAM
, (IPTR
*)&iArgs
, rda
))
649 ULONG patbufsize
= 0;
651 APTR win
= task
->pr_WindowPtr
;
653 args
.from
= (STRPTR
*)iArgs
.from
;
654 args
.to
= (STRPTR
)iArgs
.to
;
655 args
.pattern
= (STRPTR
)iArgs
.pattern
;
656 args
.buffer
= (LONG
*)iArgs
.buffer
;
657 args
.all
= (LONG
)iArgs
.all
;
658 args
.direct
= (LONG
)iArgs
.direct
;
659 args
.clone
= (LONG
)iArgs
.clone
;
660 args
.dates
= (LONG
)iArgs
.dates
;
661 args
.nopro
= (LONG
)iArgs
.nopro
;
662 args
.comment
= (LONG
)iArgs
.comment
;
663 args
.quiet
= (LONG
)iArgs
.quiet
;
664 #if USE_ALWAYSVERBOSE
665 args
.verbose
= FALSE
;
667 args
.verbose
= (LONG
)iArgs
.verbose
;
669 args
.noreq
= (LONG
)iArgs
.noreq
;
670 args
.errwarn
= (LONG
)iArgs
.errwarn
;
671 args
.makedir
= (LONG
)iArgs
.makedir
;
672 args
.move_mode
= (LONG
)iArgs
.move_mode
;
673 args
.delete_mode
= (LONG
)iArgs
.delete_mode
;
674 args
.hardlink
= (LONG
)iArgs
.hardlink
;
675 args
.softlink
= (LONG
)iArgs
.softlink
;
676 args
.forcelink
= (LONG
)iArgs
.forcelink
;
677 args
.forcedelete
= (LONG
)iArgs
.forcedelete
;
678 args
.forceoverwrite
= (LONG
)iArgs
.forceoverwrite
;
679 args
.dontoverwrite
= (LONG
)iArgs
.dontoverwrite
;
680 args
.force
= (LONG
)iArgs
.force
;
681 args
.newer
= (LONG
)iArgs
.newer
;
683 if (args
.quiet
) /* when QUIET, SILENT and NOREQ are also
686 /* Original doesn't hide requesters with QUIET */
688 args
.verbose
= FALSE
;
691 if (args
.buffer
&& *args
.buffer
> 0) /* minimum buffer size */
693 cd
->BufferSize
= *args
.buffer
* 512;
698 cd
->Flags
|= COPYFLAG_QUIET
;
701 #if !USE_ALWAYSVERBOSE
704 cd
->Flags
|= COPYFLAG_VERBOSE
;
709 cd
->Flags
|= COPYFLAG_ALL
;
712 /* 12-jul-03 bugfix: always copy protection flags! -Piru */
713 cd
->Flags
|= COPYFLAG_PROTECTION
;
716 cd
->Flags
|= COPYFLAG_DATES
| COPYFLAG_COMMENT
| COPYFLAG_PROTECTION
;
721 cd
->Flags
|= COPYFLAG_DATES
;
726 cd
->Flags
|= COPYFLAG_COMMENT
;
731 cd
->Flags
|= COPYFLAG_NOPRO
;
736 cd
->Flags
|= COPYFLAG_FORCELINK
;
739 if (args
.forcedelete
)
741 cd
->Flags
|= COPYFLAG_FORCEDELETE
;
744 if (args
.forceoverwrite
)
746 cd
->Flags
|= COPYFLAG_FORCEOVERWRITE
;
749 if (args
.dontoverwrite
)
751 cd
->Flags
|= COPYFLAG_DONTOVERWRITE
;
756 cd
->Flags
|= COPYFLAG_NEWER
|COPYFLAG_DONTOVERWRITE
;
761 cd
->Flags
|= COPYFLAG_ERRWARN
;
764 if (args
.force
) /* support OS Delete and MakeLink command
767 if (args
.delete_mode
)
769 cd
->Flags
|= COPYFLAG_FORCEDELETE
;
772 if (args
.hardlink
|| args
.softlink
)
774 cd
->Flags
|= COPYFLAG_FORCELINK
;
778 if (!args
.from
) /* no args.from means currentdir */
783 if (args
.noreq
) /* no dos.library requests allowed */
785 task
->pr_WindowPtr
= (APTR
)-1;
788 if (args
.delete_mode
)
791 cd
->Mode
= COPYMODE_DELETE
;
797 cd
->Mode
= COPYMODE_MOVE
;
803 cd
->Mode
= COPYMODE_MAKEDIR
;
809 cd
->Mode
= COPYMODE_LINK
;
815 cd
->Mode
= COPYMODE_LINK
;
816 cd
->Flags
|= COPYFLAG_SOFTLINK
| COPYFLAG_FORCELINK
;
820 #warning "CHECKME: Is this still needed?"
821 if (*args
.from
== NULL
)
823 PutStr("No arguments specified\n");
825 FreeDosObject(DOS_RDARGS
, rda
);
830 if (cd
->Mode
!= COPYMODE_DELETE
&&
831 cd
->Mode
!= COPYMODE_MAKEDIR
&& !args
.to
)
833 if (*(args
.from
+ 1)) /* when no TO is specified, the arg
835 { /* one of from. Copy this argument into */
836 STRPTR
*a
; /* args.to */
847 #if USE_ALWAYSVERBOSE
849 /* Only do this if quiet isn't set - bigfoot */
852 /* If more than two args, be verbose... - Piru */
853 if (args
.from
[0] && args
.from
[1])
856 cd
->Flags
|= COPYFLAG_VERBOSE
;
859 /* If any of the sources is a pattern, be verbose... - Piru */
863 for (a
= args
.from
; *a
; a
++)
865 if (IsMatchPattern(*a
, cd
) != 0)
868 cd
->Flags
|= COPYFLAG_VERBOSE
;
875 /* test if more than one of the above four or any other wrong
879 (args
.from
== a
&& cd
->Mode
== COPYMODE_MAKEDIR
) ||
880 (args
.direct
&& (args
.from
== a
|| !*args
.from
||
882 (cd
->Flags
& ~(COPYFLAG_QUIET
| COPYFLAG_VERBOSE
| COPYFLAG_ERRWARN
)) ||
883 (cd
->Mode
!= COPYMODE_DELETE
&& (cd
->Mode
!= COPYMODE_COPY
||
884 !args
.to
|| args
.from
[1])))) ||
885 (args
.dontoverwrite
&& args
.forceoverwrite
) ||
886 /* (args.nopro && args.clone) ||*/ /* Ignore, like original - Piru */
887 (args
.softlink
&& args
.all
) ||
888 (!args
.to
&& cd
->Mode
!= COPYMODE_DELETE
&& cd
->Mode
!= COPYMODE_MAKEDIR
))
890 SetIoErr(ERROR_TOO_MANY_ARGS
);
892 else if (cd
->Mode
== COPYMODE_MAKEDIR
)
896 cd
->RetVal2
= RETURN_OK
;
898 #if !USE_ALWAYSVERBOSE
901 PutStr(texts
[TEXTNUM_MODE
+ COPYMODE_MAKEDIR
]);
905 while (!cd
->RetVal
&& !cd
->RetVal2
&& *args
.from
)
907 if ((i
= IsPattern(*args
.from
, cd
)))
911 cd
->RetVal
= RETURN_ERROR
;
915 PutStr(TEXT_ERR_WILDCARD_DEST
);
920 cd
->RetVal2
= RETURN_FAIL
;
924 if ((dir
= OpenDestDir(*args
.from
, cd
)))
927 cd
->Flags
|= COPYFLAG_DONE
;
932 } /* cd->Mode == COPYMODE_MAKEDIR */
933 else if (args
.direct
)
935 if (cd
->Mode
== COPYMODE_COPY
)
939 if ((in
= Open(*args
.from
, MODE_OLDFILE
)))
941 if ((out
= Open(args
.to
, MODE_NEWFILE
)))
943 cd
->RetVal2
= CopyFile(in
, out
, cd
->BufferSize
, cd
);
950 else /* COPYMODE_DELETE */
954 KillFile(*(args
.from
++), cd
->Flags
& COPYFLAG_FORCEDELETE
, cd
);
957 cd
->RetVal2
= RETURN_OK
;
962 if (args
.pattern
&& *args
.pattern
)
964 patbufsize
= (strlen(args
.pattern
) << 1) + 3;
966 if ((cd
->Pattern
= (STRPTR
)AllocMem(patbufsize
,
969 if (ParsePatternNoCase(args
.pattern
, cd
->Pattern
,
972 FreeMem(cd
->Pattern
, patbufsize
);
978 if (1) // (cd->Fib = (struct FileInfoBlock *)AllocDosObject(DOS_FIB, NULL)))
980 #if !USE_ALWAYSVERBOSE
983 PutStr(texts
[TEXTNUM_MODE
+ cd
->Mode
+
984 (cd
->Flags
& COPYFLAG_SOFTLINK
? 1 : 0)]);
987 if (args
.pattern
&& !cd
->Pattern
)
991 SetIoErr(ERROR_BAD_TEMPLATE
);
994 else if (cd
->Mode
== COPYMODE_DELETE
)
996 cd
->RetVal2
= RETURN_OK
;
998 while (cd
->RetVal
<= (args
.errwarn
? RETURN_OK
: RETURN_WARN
)
1001 PatCopy(*(args
.from
++), cd
);
1004 else if ((i
= IsPattern(args
.to
, cd
)))
1008 cd
->RetVal
= RETURN_ERROR
;
1012 PutStr(TEXT_ERR_WILDCARD_DEST
);
1020 if (*(path
= PathPart(args
.to
)) == '/')
1022 ++path
; /* is destination a path description ? */
1025 if (*path
&& !*(args
.from
+1) &&
1026 !(i
= IsMatchPattern(*args
.from
, cd
)))
1031 /* is destination an existing directory */
1032 if ((lock
= Lock(args
.to
, SHARED_LOCK
)))
1034 if (Examine(lock
, &cd
->Fib
))
1036 if (cd
->Fib
.fib_DirEntryType
> 0)
1038 cd
->RetVal2
= RETURN_OK
;
1041 /* indicate dir-mode for next if */
1051 /* Some magic to handle tick quoted pattern object names. Quite crude way to
1052 * handle it, but I couldn't think of anything better. - Piru
1055 if (!i
&& cd
->RetVal2
&& !IsMatchPattern(*args
.from
, cd
))
1060 //Printf("pattern check <%s>\n", *args.from);
1062 len
= (strlen(*args
.from
) << 1) + 3;
1064 if ((pat
= (STRPTR
)AllocMem(len
,
1067 if (ParsePattern(*args
.from
, pat
, len
) > -1 &&
1068 strlen(pat
) <= strlen(*args
.from
))
1070 lock
= Lock(pat
, SHARED_LOCK
);
1075 strcpy(*args
.from
, pat
);
1084 /* is source a directory */
1085 if (!i
&& cd
->RetVal2
&&
1086 (lock
= Lock(*args
.from
, SHARED_LOCK
)))
1088 if (Examine(lock
, &cd
->Fib
))
1090 cd
->RetVal2
= RETURN_OK
;
1091 if (cd
->Mode
!= COPYMODE_COPY
||
1092 cd
->Fib
.fib_DirEntryType
< 0)
1096 cd
->Flags
|= COPYFLAG_DEST_FILE
;
1098 /* produce missing destination directories */
1102 if ((cd
->CurDest
= OpenDestDir(args
.to
, cd
)))
1109 CopyMem(*args
.from
, cd
->FileName
,
1110 1 + strlen(*args
.from
));
1111 DoWork(FilePart(args
.to
), cd
); /* on file call */
1112 UnLock(cd
->CurDest
);
1123 lockioerr
= IoErr(); /* We save ioerr here, because TestFileSys changes it */
1125 if (lock
== 0 && cd
->Mode
== COPYMODE_COPY
&& !TestFileSys(*args
.from
, cd
))
1128 cd
->Flags
|= COPYFLAG_DEST_FILE
| COPYFLAG_SRCNOFILESYS
;
1129 cd
->RetVal2
= RETURN_OK
;
1131 /* produce missing destination directories */
1135 if ((cd
->CurDest
= OpenDestDir(args
.to
, cd
)))
1140 CopyMem(*args
.from
, cd
->FileName
, 1 + strlen(*args
.from
));
1141 DoWork(FilePart(args
.to
), cd
); /* on file call */
1142 UnLock(cd
->CurDest
);
1146 SetIoErr(lockioerr
);
1150 cd
->RetVal2
= RETURN_OK
;
1153 if (!cd
->RetVal
&& !cd
->RetVal2
&& !(cd
->Flags
& COPYFLAG_DEST_FILE
) &&
1154 (cd
->Destination
= OpenDestDir(args
.to
, cd
)))
1156 while (cd
->RetVal
<= (args
.errwarn
? RETURN_OK
: RETURN_WARN
)
1157 && *args
.from
&& !CTRL_C
)
1159 PatCopy(*(args
.from
++), cd
);
1162 UnLock(cd
->Destination
);
1166 if (!(cd
->Flags
& COPYFLAG_DONE
) && args
.verbose
&&
1167 !cd
->RetVal
&& !cd
->RetVal2
)
1169 PutStr(TEXT_NOTHING_DONE
);
1176 FreeMem(cd
->Pattern
, patbufsize
);
1180 task
->pr_WindowPtr
= win
;
1185 FreeDosObject(DOS_RDARGS
, rda
);
1186 } /* AllocDosObject */
1188 if (!cd
->RetVal2
&& CTRL_C
)
1190 SetIoErr(ERROR_BREAK
);
1191 cd
->RetVal2
= RETURN_WARN
;
1194 if (cd
->RetVal2
&& !args
.quiet
&& !cd
->RetVal
)
1196 PrintFault(IoErr(), NULL
);
1201 cd
->RetVal2
= cd
->RetVal
;
1204 if (args
.errwarn
&& cd
->RetVal2
== RETURN_WARN
)
1206 cd
->RetVal2
= RETURN_ERROR
;
1211 FreeMem(cd
->CopyBuf
, cd
->CopyBufLen
);
1219 retval
= cd
->RetVal2
;
1220 FreeMem(cd
, sizeof(*cd
));
1224 PrintFault(IoErr(), NULL
);
1229 CloseLibrary((struct Library
*)DOSBase
);
1235 #define SysBase cd->SysBase
1236 #define DOSBase cd->DOSBase
1238 void PatCopy(STRPTR name
, struct CopyData
*cd
)
1240 struct AnchorPath
*APath
;
1241 ULONG retval
, doit
= 0, deep
= 0, failval
= RETURN_WARN
, first
= 0;
1244 Printf("PatCopy(%s, .)\n", name
);
1247 if ((cd
->Mode
== COPYMODE_COPY
|| (cd
->Flags
& COPYFLAG_ALL
)) && !IsMatchPattern(name
, cd
))
1249 first
= 1; /* enter first directory (support of old copy style) */
1252 if (cd
->Flags
& COPYFLAG_ERRWARN
)
1254 failval
= RETURN_OK
;
1257 cd
->CurDest
= cd
->Destination
;
1258 cd
->DestPathSize
= 0;
1260 if (cd
->Mode
== COPYMODE_COPY
&& !TestFileSys(name
, cd
))
1262 cd
->Flags
|= COPYFLAG_SRCNOFILESYS
;
1263 CopyMem(name
, cd
->FileName
, 1 + strlen(name
));
1264 DoWork(FilePart(name
), cd
);
1265 cd
->Flags
&= ~COPYFLAG_SRCNOFILESYS
;
1270 if ((APath
= (struct AnchorPath
*)AllocMem(sizeof(struct AnchorPath
) + FILEPATH_SIZE
,
1271 MEMF_PUBLIC
| MEMF_CLEAR
)))
1273 int parentdirerr
= 0;
1275 APath
->ap_BreakBits
= SIGBREAKF_CTRL_C
;
1276 APath
->ap_Strlen
= FILEPATH_SIZE
;
1278 for (retval
= MatchFirst(name
, APath
);
1279 !retval
&& cd
->RetVal
<= failval
&& !cd
->RetVal2
;
1280 retval
= MatchNext(APath
)
1285 //Printf("ParentDir() fuxored last round! Would copy next files to SYS: !\n");
1288 SetIoErr(retval
= ERROR_INVALID_LOCK
);
1294 DoWork(cd
->Fib
.fib_FileName
, cd
);
1298 if (deep
) /* used for Deep checking */
1304 cd
->Flags
&= ~COPYFLAG_ENTERSECOND
;
1306 CopyMem(APath
->ap_Buf
, cd
->FileName
, FILEPATH_SIZE
);
1307 CopyMem(&APath
->ap_Info
, &cd
->Fib
, sizeof(struct FileInfoBlock
));
1309 if (first
&& APath
->ap_Info
.fib_DirEntryType
> 0)
1311 #if USE_ALWAYSVERBOSE
1312 /* If the source is a directory, be verbose - Piru */
1313 cd
->Flags
|= COPYFLAG_VERBOSE
;
1315 APath
->ap_Flags
|= APF_DODIR
;
1317 else if (APath
->ap_Flags
& APF_DIDDIR
)
1321 cd
->Flags
|= COPYFLAG_ENTERSECOND
;
1322 APath
->ap_Flags
&= ~APF_DIDDIR
;
1325 if (cd
->Mode
== COPYMODE_DELETE
|| cd
->Mode
== COPYMODE_MOVE
)
1330 if ((i
= cd
->CurDest
))
1332 cd
->CurDest
= ParentDir(i
);
1333 cd
->DestPathSize
= 0;
1335 if (i
!= cd
->Destination
)
1347 else if (APath
->ap_Info
.fib_DirEntryType
> 0)
1351 if (cd
->Flags
& COPYFLAG_ALL
)
1353 #if USE_SOFTLINKCHECK
1358 dirlock
= CurrentDir(APath
->ap_Current
->an_Lock
);
1359 lock
= Lock(APath
->ap_Info
.fib_FileName
, ACCESS_READ
);
1364 struct DevProc
*dvp
;
1365 LONG ioerr
= IoErr();
1367 if (ioerr
== ERROR_OBJECT_NOT_FOUND
&&
1368 (dvp
= GetDeviceProc("", NULL
)))
1370 #define BUFFERSIZE 512
1371 UBYTE
*buffer
= AllocMem(BUFFERSIZE
, MEMF_PUBLIC
);
1375 if (ReadLink(dvp
->dvp_Port
, dvp
->dvp_Lock
, APath
->ap_Info
.fib_FileName
, buffer
, BUFFERSIZE
- 1) > 0)
1377 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1379 buffer
[BUFFERSIZE
- 1] = '\0';
1381 Printf("Warning: Skipping dangling softlink %s -> %s\n",
1382 APath
->ap_Info
.fib_FileName
, buffer
);
1388 FreeMem(buffer
, BUFFERSIZE
);
1391 FreeDeviceProc(dvp
);
1396 CurrentDir(dirlock
);
1400 APath
->ap_Flags
|= APF_DODIR
;
1404 #else /* USE_SOFTLINKCHECK */
1406 APath
->ap_Flags
|= APF_DODIR
;
1409 #endif /* USE_SOFTLINKCHECK */
1412 else if (!cd
->Pattern
|| MatchPatternNoCase(cd
->Pattern
, APath
->ap_Info
.fib_FileName
))
1422 if (retval
&& retval
!= ERROR_NO_MORE_ENTRIES
)
1424 LONG ioerr
= IoErr();
1425 #if USE_ALWAYSVERBOSE
1426 cd
->Flags
|= COPYFLAG_VERBOSE
;
1428 Printf("%s - ", name
);
1429 PrintFault(ioerr
, NULL
);
1432 cd
->RetVal2
= RETURN_FAIL
;
1437 DoWork(cd
->Fib
.fib_FileName
, cd
);
1440 /* No need to clear the flags here, as they are cleared on next PatJoin
1441 call (DoWork is not called first round, as lock is zero!). */
1443 FreeMem(APath
, sizeof(struct AnchorPath
) + FILEPATH_SIZE
);
1447 cd
->RetVal
= RETURN_FAIL
;
1449 if (!cd
->Flags
& COPYFLAG_QUIET
)
1451 PrintFault(ERROR_NO_FREE_STORE
, NULL
);
1455 if (cd
->CurDest
&& cd
->CurDest
!= cd
->Destination
)
1457 UnLock(cd
->CurDest
);
1462 LONG
IsPattern(STRPTR name
, struct CopyData
*cd
)
1467 a
= (strlen(name
) << 1) + 3;
1469 if ((buffer
= (STRPTR
)AllocMem(a
, MEMF_ANY
)))
1471 ret
= ParsePattern(name
, buffer
, a
);
1477 SetIoErr(ERROR_NO_FREE_STORE
);
1484 LONG
IsMatchPattern(STRPTR name
, struct CopyData
*cd
)
1486 struct AnchorPath ap
;
1489 ap
.ap_BreakBits
= 0;
1490 ap
.ap_Flags
= APF_DOWILD
;
1493 if (MatchFirst(name
, &ap
) == 0)
1495 ret
= (ap
.ap_Flags
& APF_ITSWILD
) ? TRUE
: FALSE
;
1504 LONG
KillFile(STRPTR name
, ULONG doit
, struct CopyData
*cd
)
1508 SetProtection(name
, 0);
1511 return DeleteFile(name
);
1515 LONG
KillFileKeepErr(STRPTR name
, ULONG doit
, struct CopyData
*cd
)
1520 ret
= KillFile(name
, doit
, cd
);
1527 BPTR
OpenDestDir(STRPTR name
, struct CopyData
*cd
)
1529 LONG a
, err
= 0, cr
= 0;
1534 if ((cd
->Mode
== COPYMODE_COPY
|| cd
->Mode
== COPYMODE_MOVE
) && !TestFileSys(name
, cd
))
1536 cd
->Flags
|= COPYFLAG_DESNOFILESYS
;
1537 CopyMem(name
, cd
->DestName
, 1 + strlen(name
));
1539 return Lock("", SHARED_LOCK
);
1542 while (!err
&& *ptr
!= 0)
1544 while (*ptr
&& *ptr
!= '/')
1552 if ((a
= TestDest(name
, 1, cd
)) == TESTDEST_CANTDELETE
)
1554 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1556 PutStr(TEXT_ERR_DEST_DIR
);
1565 else if (a
!= TESTDEST_DIR_OK
)
1567 if ((dir
= CreateDir(name
)))
1571 if ((cd
->Flags
& COPYFLAG_VERBOSE
))
1573 PrintName(name
, 1, 1, 1, cd
);
1574 Printf("%s\n", TEXT_CREATED
);
1581 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1583 PrintNotDone(name
, TEXT_CREATED
, 1, 1, cd
);
1592 /* 26-Oct-2003 bugfix: Don't scan past end of the string.
1593 * as is the old char, if '\0' we've reached the end of the
1604 cd
->RetVal
= RETURN_ERROR
;
1606 if (!(cd
->Flags
& COPYFLAG_QUIET
) && err
== 1)
1608 PrintNotDone(name
, TEXT_OPENED_FOR_OUTPUT
, 1, 1, cd
);
1614 if (cd
->Mode
== COPYMODE_MAKEDIR
&& !cr
&& !(cd
->Flags
& COPYFLAG_QUIET
))
1616 SetIoErr(ERROR_OBJECT_EXISTS
);
1617 PrintNotDone(name
, TEXT_CREATED
, 1, 1, cd
);
1620 return Lock(name
, SHARED_LOCK
);
1624 void PrintName(CONST_STRPTR name
, ULONG deep
, ULONG dir
, ULONG txt
, struct CopyData
*cd
)
1627 deep
%= PRINTOUT_SPACES
; /* reduce number of spaces */
1628 /* This produces an error with MaxonC++ */
1646 if ((deep
= strlen(name
)) > PRINTOUT_SIZE
) /* reduce name size */
1648 name
+= deep
-PRINTOUT_SIZE
;
1653 Printf((dir
? TEXT_DIRECTORY
: (STRPTR
) "%s"), name
);
1664 void PrintNotDone(CONST_STRPTR name
, CONST_STRPTR txt
, ULONG deep
, ULONG dir
, struct CopyData
*cd
)
1666 #if !USE_ALWAYSVERBOSE
1669 PrintName(name
, deep
, dir
, 1, cd
);
1673 Printf(TEXT_NOT_DONE
, txt
);
1674 PrintFault(IoErr(), NULL
);
1678 /* returns value, when it seems to be a filesystem */
1679 ULONG
TestFileSys(STRPTR name
, struct CopyData
*cd
)
1684 while (*n
&& *n
!= ':')
1695 ret
= IsFileSystem(name
);
1703 void DoWork(STRPTR name
, struct CopyData
*cd
)
1705 BPTR pdir
, lock
= 0;
1706 CONST_STRPTR printerr
= NULL
, printok
= "";
1709 Printf("DoWork(%s, .)\n", name
);
1712 if (cd
->RetVal
> (cd
->Flags
& COPYFLAG_ERRWARN
? RETURN_OK
: RETURN_WARN
) || cd
->RetVal2
)
1715 Printf("DoWork(RetVal %ld)\n", cd
->RetVal
);
1720 if (cd
->Mode
!= COPYMODE_DELETE
&& !(cd
->Flags
& COPYFLAG_DESNOFILESYS
))
1722 if (!cd
->DestPathSize
)
1724 if (!NameFromLock(cd
->CurDest
, cd
->DestName
, FILEPATH_SIZE
))
1726 cd
->RetVal2
= RETURN_FAIL
;
1730 Printf("DoWork(NameFromLock RetVal %ld)\n", cd
->RetVal
);
1736 cd
->DestPathSize
= strlen(cd
->DestName
);
1739 cd
->DestName
[cd
->DestPathSize
] = 0;
1740 AddPart(cd
->DestName
, name
, FILEPATH_SIZE
);
1743 if (cd
->Flags
& (COPYFLAG_SRCNOFILESYS
|COPYFLAG_DESNOFILESYS
))
1745 ULONG res
= 0, kill
= 1;
1747 CONST_STRPTR txt
= TEXT_OPENED_FOR_OUTPUT
;
1750 Printf("Partly DIRECT mode active now (%s - %s)\n", cd
->FileName
,
1754 if ((out
= Open(cd
->DestName
, MODE_NEWFILE
)))
1756 txt
= cd
->Mode
== COPYMODE_MOVE
? TEXT_MOVED
: TEXT_COPIED
;
1758 if ((in
= Open(cd
->FileName
, MODE_OLDFILE
)))
1762 h
= CopyFile(in
, out
, cd
->BufferSize
, cd
);
1763 Close(out
); out
= NULL
;
1770 if (cd
->Mode
== COPYMODE_MOVE
)
1772 if (KillFile(cd
->FileName
, cd
->Flags
& COPYFLAG_FORCEDELETE
, cd
))
1791 KillFileKeepErr(cd
->DestName
, 0, cd
);
1795 if (!res
&& !(cd
->Flags
& COPYFLAG_QUIET
))
1797 PrintNotDone(cd
->Flags
& COPYFLAG_VERBOSE
? name
: 0,
1798 txt
, cd
->Deep
, cd
->Fib
.fib_DirEntryType
> 0, cd
);
1802 cd
->Flags
|= COPYFLAG_DONE
;
1804 if ((cd
->Flags
& COPYFLAG_VERBOSE
))
1806 Printf("%s\n", txt
);
1811 PutStr("DoWork(done)\n");
1816 if (!(lock
= Lock(cd
->FileName
, SHARED_LOCK
)))
1818 cd
->RetVal
= RETURN_WARN
;
1820 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1822 PrintNotDone(cd
->Fib
.fib_FileName
, TEXT_READ
, cd
->Deep
,
1823 cd
->Fib
.fib_DirEntryType
> 0, cd
);
1827 Printf("DoWork(Lock RetVal %ld)\n", cd
->RetVal
);
1832 if (!(pdir
= ParentDir(lock
)))
1834 cd
->RetVal
= RETURN_ERROR
;
1836 if (cd
->Mode
== COPYMODE_DELETE
)
1838 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1840 Printf(" %s ", cd
->FileName
);
1841 Printf(TEXT_NOT_DONE
, TEXT_DELETED
);
1842 Printf("%s\n", TEXT_ERR_DELETE_DEVICE
);
1849 Printf("DoWork(ParentDir %ld)\n", cd
->RetVal
);
1856 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1858 if ((cd
->Flags
& COPYFLAG_VERBOSE
))
1860 PrintName(name
, cd
->Deep
, cd
->Fib
.fib_DirEntryType
> 0, cd
->Fib
.fib_DirEntryType
< 0 ||
1861 (cd
->Flags
& COPYFLAG_ALL
? cd
->Mode
!= COPYMODE_DELETE
: cd
->Mode
!= COPYMODE_COPY
) ||
1862 cd
->Flags
& COPYFLAG_ENTERSECOND
, cd
);
1866 if ((cd
->Flags
& COPYFLAG_ENTERSECOND
) || (cd
->Mode
== COPYMODE_DELETE
&&
1867 (!(cd
->Flags
& COPYFLAG_ALL
) || cd
->Fib
.fib_DirEntryType
< 0)))
1872 if (KillFile(cd
->FileName
, cd
->Flags
& COPYFLAG_FORCEDELETE
, cd
))
1874 printok
= TEXT_DELETED
;
1878 cd
->RetVal
= RETURN_WARN
;
1879 printerr
= TEXT_DELETED
;
1882 else if (cd
->Mode
== COPYMODE_DELETE
)
1886 else if (cd
->Fib
.fib_DirEntryType
> 0)
1890 if ((cd
->Flags
& COPYFLAG_ALL
|| cd
->Mode
== COPYMODE_LINK
||
1891 cd
->Mode
== COPYMODE_MOVE
) && TestLoop(lock
, cd
->CurDest
, cd
))
1894 cd
->RetVal
= RETURN_ERROR
;
1896 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1898 if (!(cd
->Flags
& COPYFLAG_VERBOSE
))
1900 PrintName(name
, cd
->Deep
, 1, 1, cd
);
1903 Printf(TEXT_NOT_DONE
, TEXT_ENTERED
);
1904 PutStr(TEXT_ERR_INFINITE_LOOP
);
1907 else if ((a
= TestDest(cd
->DestName
, 1, cd
)) < 0)
1909 printerr
= TEXT_CREATED
;
1910 cd
->RetVal
= RETURN_ERROR
;
1912 else if (cd
->Flags
& COPYFLAG_ALL
)
1917 cd
->DestPathSize
= 0;
1919 if (a
== TESTDEST_DIR_OK
)
1921 if (!(cd
->CurDest
= Lock(cd
->DestName
, SHARED_LOCK
)))
1923 printerr
= TEXT_ENTERED
;
1924 cd
->RetVal
= RETURN_ERROR
;
1928 #if USE_ALWAYSVERBOSE
1931 printok
= TEXT_ENTERED
;
1935 else if ((cd
->CurDest
= CreateDir(cd
->DestName
)))
1937 UnLock(cd
->CurDest
);
1939 if ((cd
->CurDest
= Lock(cd
->DestName
, SHARED_LOCK
)))
1941 printok
= TEXT_CREATED
;
1945 printerr
= TEXT_ENTERED
;
1946 cd
->RetVal
= RETURN_ERROR
;
1951 printerr
= TEXT_CREATED
;
1952 cd
->RetVal
= RETURN_ERROR
;
1959 else if (i
!= cd
->Destination
)
1964 else if (cd
->Mode
== COPYMODE_MOVE
)
1966 if (Rename(cd
->FileName
, cd
->DestName
))
1968 printok
= TEXT_RENAMED
;
1972 printerr
= TEXT_RENAMED
;
1973 cd
->RetVal
= RETURN_WARN
;
1976 else if (cd
->Mode
== COPYMODE_LINK
)
1978 if (!(cd
->Flags
& COPYFLAG_FORCELINK
))
1981 cd
->RetVal
= RETURN_WARN
;
1983 if (!(cd
->Flags
& COPYFLAG_QUIET
))
1985 if (!(cd
->Flags
& COPYFLAG_VERBOSE
))
1987 PrintName(name
, cd
->Deep
, 1, 1, cd
);
1990 Printf(TEXT_NOT_DONE
, TEXT_LINKED
);
1991 PutStr(TEXT_ERR_FORCELINK
);
1994 else if (LinkFile(lock
, cd
->DestName
, cd
->Flags
& COPYFLAG_SOFTLINK
, cd
))
1996 printok
= TEXT_LINKED
;
2000 printerr
= TEXT_LINKED
;
2001 cd
->RetVal
= RETURN_WARN
;
2004 else /* COPY mode only displays directories, when not ALL */
2008 if (!(cd
->Flags
& COPYFLAG_QUIET
))
2010 if (cd
->Flags
& COPYFLAG_VERBOSE
)
2019 /* test for existing destination file */
2020 if (TestDest(cd
->DestName
, 0, cd
) < 0)
2022 printerr
= TEXT_OPENED_FOR_OUTPUT
;
2024 else if (cd
->Mode
== COPYMODE_MOVE
&& Rename(cd
->FileName
, cd
->DestName
))
2026 printok
= TEXT_RENAMED
;
2028 else if (cd
->Mode
== COPYMODE_LINK
)
2030 if (!(cd
->Flags
& COPYFLAG_SOFTLINK
) && LinkFile(lock
, cd
->DestName
, 0, cd
))
2032 printok
= TEXT_LINKED
;
2036 printerr
= TEXT_LINKED
;
2037 cd
->RetVal
= RETURN_WARN
;
2039 if (cd
->Flags
& COPYFLAG_SOFTLINK
)
2041 SetIoErr(ERROR_OBJECT_WRONG_TYPE
);
2049 CONST_STRPTR txt
= TEXT_OPENED_FOR_OUTPUT
;
2051 if ((out
= Open(cd
->DestName
, MODE_NEWFILE
)))
2055 txt
= cd
->Mode
== COPYMODE_MOVE
? TEXT_MOVED
: TEXT_COPIED
;
2059 if ((in
= Open(cd
->FileName
, MODE_OLDFILE
)))
2061 h
= CopyFile(in
, out
, cd
->BufferSize
, cd
);
2062 Close(out
); out
= NULL
;
2069 if (cd
->Mode
== COPYMODE_MOVE
)
2071 if (KillFile(cd
->FileName
, cd
->Flags
& COPYFLAG_FORCEDELETE
, cd
))
2090 KillFileKeepErr(cd
->DestName
, 0, cd
);
2097 cd
->RetVal
= RETURN_WARN
;
2106 if (printerr
&& !(cd
->Flags
& COPYFLAG_QUIET
))
2108 PrintNotDone(cd
->Flags
& COPYFLAG_VERBOSE
? name
: 0,
2109 printerr
, cd
->Deep
, cd
->Fib
.fib_DirEntryType
> 0, cd
);
2113 cd
->Flags
|= COPYFLAG_DONE
;
2115 if (!(cd
->Flags
& COPYFLAG_QUIET
))
2117 if ((cd
->Flags
& COPYFLAG_VERBOSE
))
2119 Printf("%s\n", printok
);
2123 SetData(cd
->DestName
, cd
);
2133 LONG
CopyFile(BPTR from
, BPTR to
, ULONG bufsize
, struct CopyData
*cd
)
2140 buffer
= cd
->CopyBuf
;
2141 bufsize
= cd
->CopyBufLen
;
2147 buffer
= (STRPTR
)AllocMem(bufsize
, MEMF_PUBLIC
);
2150 cd
->CopyBuf
= buffer
;
2151 cd
->CopyBufLen
= bufsize
;
2157 } while (bufsize
>= 512);
2162 #if USE_BOGUSEOFWORKAROUND
2163 struct FileInfoBlock
*fib
= (struct FileInfoBlock
*) buffer
; /* NOTE: bufsize is min 512 bytes */
2165 if (ExamineFH(from
, fib
))
2167 #warning "****** WARNING: No largefile support! ******"
2168 ULONG filesize
= fib
->fib_Size
, copied
= 0;
2170 /*Printf("filesize: %lu\n", filesize);*/
2175 if (brk
|| (s
= Read(from
, buffer
, bufsize
)) == -1 || Write(to
, buffer
, s
) != s
)
2179 SetIoErr(ERROR_BREAK
);
2184 else if (s
== 0 && copied
< filesize
)
2186 /* premature EOF with buggy fs */
2193 } while (copied
< filesize
);
2195 /*{ LONG ioerr = IoErr();
2196 Printf("copied %lu/%lu\n", copied, filesize);
2200 #endif /* USE_BOGUSEOFWORKAROUND */
2202 /* Stream or so, copy until EOF or error */
2206 if (brk
|| (s
= Read(from
, buffer
, bufsize
)) == -1 || Write(to
, buffer
, s
) != s
)
2210 SetIoErr(ERROR_BREAK
);
2215 } while (s
== bufsize
);
2218 /* Freed at exit to avoid fragmentation */
2219 /*FreeMem(buffer, bufsize);*/
2230 /* Softlink's path starts always with device name! f.e. "Ram Disk:T/..." */
2231 LONG
LinkFile(BPTR from
, STRPTR to
, ULONG soft
, struct CopyData
*cd
)
2238 name
= AllocMem(FILEPATH_SIZE
, MEMF_ANY
);
2241 if (NameFromLock(from
, name
, FILEPATH_SIZE
))
2243 ret
= MakeLink(to
, name
, LINK_SOFT
);
2246 FreeMem(name
, FILEPATH_SIZE
);
2253 return MakeLink(to
, from
, LINK_HARD
);
2258 /* return 0 means no loop, return != 0 means loop found */
2259 ULONG
TestLoop(BPTR srcdir
, BPTR destdir
, struct CopyData
*cd
)
2266 if (SameDevice(srcdir
, destdir
))
2270 if (!SameLock(srcdir
, lock
))
2276 par
= ParentDir(lock
);
2278 if (lock
!= destdir
)
2286 while(!loop
&& lock
);
2289 if (lock
!= destdir
)
2298 void SetData(STRPTR name
, struct CopyData
*cd
)
2300 if (cd
->Flags
& COPYFLAG_NOPRO
)
2302 /* Is already set! - Piru */
2303 //SetProtection(name, 0);
2305 else if (cd
->Flags
& COPYFLAG_PROTECTION
)
2307 SetProtection(name
, cd
->Fib
.fib_Protection
& (ULONG
) ~FIBF_ARCHIVE
);
2310 if (cd
->Flags
& COPYFLAG_DATES
)
2312 SetFileDate(name
, &cd
->Fib
.fib_Date
);
2315 if (cd
->Flags
& COPYFLAG_COMMENT
)
2317 SetComment(name
, cd
->Fib
.fib_Comment
);
2322 LONG
TestDest(STRPTR name
, ULONG type
, struct CopyData
*cd
)
2324 LONG ret
= TESTDEST_ERROR
;
2327 if ((lock
= Lock(name
, SHARED_LOCK
)))
2329 struct FileInfoBlock
*fib
;
2331 if ((fib
= (struct FileInfoBlock
*)AllocDosObject(DOS_FIB
, NULL
)))
2333 if (Examine(lock
, fib
))
2340 if (fib
->fib_DirEntryType
> 0)
2342 ret
= TESTDEST_DIR_OK
;
2344 else if (!(cd
->Flags
& COPYFLAG_DONTOVERWRITE
))
2346 if (KillFile(name
, cd
->Flags
& COPYFLAG_FORCEOVERWRITE
, cd
))
2348 ret
= TESTDEST_DELETED
;
2353 ret
= TESTDEST_CANTDELETE
;
2356 else if (cd
->Flags
& COPYFLAG_DONTOVERWRITE
)
2358 if (cd
->Flags
& COPYFLAG_NEWER
)
2360 if( CheckVersion( cd
) == CHECKVER_DESTOLDER
)
2362 if (KillFile(name
, cd
->Flags
& COPYFLAG_FORCEOVERWRITE
, cd
))
2364 ret
= TESTDEST_DELETED
;
2369 ret
= TESTDEST_CANTDELETE
;
2372 else /* normal "dont overwrite mode" */
2374 ret
= TESTDEST_CANTDELETE
;
2377 else if (KillFile(name
, cd
->Flags
& COPYFLAG_FORCEOVERWRITE
, cd
))
2379 ret
= TESTDEST_DELETED
;
2383 FreeDosObject(DOS_FIB
, fib
);
2393 ret
= TESTDEST_NONE
;
2396 if (ret
== TESTDEST_CANTDELETE
)
2398 SetIoErr(ERROR_OBJECT_EXISTS
);
2405 ** We compare current file versions and return the result
2406 ** see CHECKVER_#? values
2409 static LONG
CheckVersion( struct CopyData
*cd
)
2411 struct VersionData vds
;
2412 struct VersionData vdd
;
2413 LONG resversion
= CHECKVER_EQUAL
;
2414 LONG resdate
= CHECKVER_EQUAL
;
2416 if( VersionFind( cd
->FileName
, &vds
, cd
) )
2418 if( VersionFind( cd
->DestName
, &vdd
, cd
) )
2420 /* version and revision must be available to ensure a proper operation */
2421 if( ((vdd
.vd_Version
!= -1) && (vds
.vd_Version
!= -1) && (vdd
.vd_Revision
!= -1) && (vds
.vd_Revision
!= -1)) )
2423 /* first we make the stuff comparable. If one component is missing we reset both */
2424 if( vdd
.vd_Year
== -1 || vds
.vd_Year
== -1 )
2429 if( vdd
.vd_Month
== -1 || vds
.vd_Month
== -1 )
2434 if( vdd
.vd_Day
== -1 || vds
.vd_Day
== -1 )
2441 resversion
= CHECKVER_DESTOLDER
;
2442 if( ((vdd
.vd_Version
== vds
.vd_Version
) && vdd
.vd_Revision
== vds
.vd_Revision
) )
2444 resversion
= CHECKVER_EQUAL
;
2446 else if( (vdd
.vd_Version
> vds
.vd_Version
) ||
2447 ((vdd
.vd_Version
== vds
.vd_Version
) && vdd
.vd_Revision
> vds
.vd_Revision
) )
2449 resversion
= CHECKVER_DESTNEWER
;
2453 resdate
= CHECKVER_DESTOLDER
;
2454 if( ((vdd
.vd_Year
== vds
.vd_Year
) && (vdd
.vd_Month
== vds
.vd_Month
) && (vdd
.vd_Day
== vds
.vd_Day
) ) )
2456 resdate
= CHECKVER_EQUAL
;
2460 if( ( (vdd
.vd_Year
> vds
.vd_Year
) ||
2461 ( (vdd
.vd_Year
== vds
.vd_Year
) && (vdd
.vd_Month
> vds
.vd_Month
) ) ||
2462 ( (vdd
.vd_Year
== vds
.vd_Year
) && (vdd
.vd_Month
== vds
.vd_Month
) && (vdd
.vd_Day
> vds
.vd_Day
) ) ))
2464 resdate
= CHECKVER_DESTNEWER
;
2468 /* plausible check */
2469 if( ((resversion
== CHECKVER_DESTNEWER
) && (resdate
== CHECKVER_DESTOLDER
)) || /* newer version with older date */
2470 ((resversion
== CHECKVER_DESTOLDER
) && (resdate
== CHECKVER_DESTNEWER
)) ) /* older version with newer date */
2472 /* we maybe should inform the user about this */
2473 return( CHECKVER_EQUAL
);
2478 /* compose result */
2480 if( (resversion
== resdate
) || (resversion
== CHECKVER_EQUAL
) )
2486 return( resversion
);
2493 ** Searches the given file for a version string and fills version data struct with the result.
2494 ** Returns false if no version was found. Returns true if the version got parsed and version data
2498 #define VERSBUFFERSIZE 4096 /* must be as big as the biggest version string we want to handle. */
2500 static BOOL
VersionFind( CONST_STRPTR path
, struct VersionData
*vds
, struct CopyData
*cd
)
2508 if ( (buf
= AllocVec(VERSBUFFERSIZE
, MEMF_PUBLIC
| MEMF_CLEAR
)) )
2510 if ( (handle
= Open(path
, MODE_OLDFILE
)) )
2514 while( ( (index
+= Read(handle
, &buf
[index
], VERSBUFFERSIZE
-index
)) > 5) && !rc
)
2516 for (i
= 0; i
< index
-5; i
++) {
2517 if( buf
[i
] == '$' && buf
[i
+1] == 'V' && buf
[i
+2] == 'E' && buf
[i
+3] == 'R' && buf
[i
+4] == ':' ) {
2518 CopyMem( &buf
[i
], buf
, index
-i
);
2520 (index
+= Read(handle
, &buf
[index
], VERSBUFFERSIZE
-index
));
2521 /* now the version string is aligned and complete in buffer */
2522 makeversionfromstring( buf
, vds
, cd
);
2527 CopyMem( &buf
[index
-5], &buf
[0], 5 );
2539 ** This function extracts the version information from a given version string.
2540 ** the result will be store in the given version data structure.
2542 ** NOTE: There is no need to preset the version data struct. All fields will
2543 ** be reset to defaults, so in case of a faulty version string the result data
2544 ** will be checkable.
2548 void makeversionfromstring( STRPTR buffer
, struct VersionData
*vd
, struct CopyData
*cd
)
2554 /* reset data field */
2556 vd
->vd_Name
[0] = '\0';
2557 vd
->vd_Version
= -1;
2558 vd
->vd_Revision
= -1;
2563 buffer
= skipspaces( buffer
); /* skip before $VER: */
2564 buffer
= skipnonspaces( buffer
); /* skip $VER: */
2565 buffer
= skipspaces( buffer
); /* skip spaces before tool name */
2567 buffer
= skipnonspaces( buffer
); /* skip name of tool */
2569 if( (tmp
= ((int) buffer
- (int) name
) ) && *buffer
)
2571 CopyMem( name
, vd
->vd_Name
, MIN( tmp
, VDNAMESIZE
-1) );
2572 vd
->vd_Name
[MIN( tmp
, VDNAMESIZE
-1)] = '\0'; /* terminate name string inside target buffer */
2574 buffer
= skipspaces( buffer
); /* skip spaces before version */
2579 if( (pos
= StrToLong((STRPTR
) buffer
, &tmp
)) != -1 )
2581 vd
->vd_Version
= tmp
;
2586 buffer
= skipspaces(buffer
);
2587 if (*buffer
++ == '.')
2589 if( (pos
= StrToLong((STRPTR
) buffer
, &tmp
)) != -1 )
2591 vd
->vd_Revision
= tmp
;
2593 buffer
= skipspaces(buffer
);
2594 if (*buffer
++ == '(')
2596 if( (pos
= StrToLong((STRPTR
) buffer
, &tmp
)) != -1 )
2600 if (*buffer
++ == '.')
2602 if( (pos
= StrToLong((STRPTR
) buffer
, &tmp
)) != -1 )
2606 if (*buffer
++ == '.')
2608 if( (pos
= StrToLong((STRPTR
) buffer
, &tmp
)) != -1 )
2610 if( (tmp
>= 70) && (tmp
<= 99) )
2632 /* Return a pointer to a string, stripped by all leading space characters
2636 STRPTR
skipspaces( STRPTR buffer
)
2640 if (buffer
[0] == '\0' || buffer
[0] != ' ')
2647 /* Return a pointer to a string, stripped by all non space characters
2651 STRPTR
skipnonspaces( STRPTR buffer
)
2655 if (buffer
[0] == '\0' || buffer
[0] == ' ')