backport
[AROS.git] / workbench / c / Copy.c
blob66b6543f8dc4e070bf97c95ff8a9827789138b91
1 /*
2 Copyright © 2001-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Copy CLI command
6 Lang: English
7 */
9 /*****************************************************************************
11 NAME
13 Copy
15 SYNOPSIS
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,
23 FORCE/S,NEWER/S
25 LOCATION
29 FUNCTION
31 Creates identical copies of one or more files.
33 INPUTS
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
42 DATES -- copy dates
43 NOPRO -- do not copy protection bits
44 COMMENT -- copy file comment
45 NOREQ -- suppress requesters
47 PATTERN -- a pattern the filenames must match
48 DIRECT -- copy mode only: copy file without any tests or options
49 VERBOSE -- gives more output
50 ERRWARN -- do not proceed, when one file failed
51 MAKEDIR -- produce directories
52 MOVE -- delete source files after copying successful
53 DELETE -- do not copy, but delete the source files
54 HARDLINK -- make a hardlink to source instead of copying
55 SOFTLINK -- make a softlink to source instead of copying
56 FOLNK -- also makes links to directories
57 FODEL -- delete protected files also
58 FOOVR -- also overwrite protected files
59 DONTOVR -- never overwrite destination
60 FORCE -- DO NOT USE. Call compatibility only.
61 NEWER -- compare version strings and only overwrites older files.
64 More detailed descriptions:
66 FROM:
67 Source file(s). For directories, all contained files are source files. May
68 have standard patterns.
70 TO:
71 Destination file or for multiple sources destination directory. Destination
72 directories are created (including all needed parent directories).
74 ALL:
75 Scan directories recursively
77 QUIET:
78 Copy is completely silent here. Really no output is given, also no requests
79 for missing disks or other problems!
81 BUF=BUFFER:
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.
85 PAT=PATTERN:
86 PATTERN allows to specify a standard dos pattern, all file have to match.
87 This is useful with ALL option.
89 Example:
90 When you want to delete all .info files in a directory tree, you need
91 this option: Copy DELETE #? ALL PAT #?.info
93 CLONE:
94 The filecomment, date and protection bits of the source files are copied to
95 destination file or directory.
97 DATES:
98 The date information of source is copied to destination.
100 NOPRO:
101 The protection bits of sources are NOT copied. So the destination gets
102 default bits [rwed].
104 COM=COMMENT:
105 The filecomment is copied to destination.
107 NOREQ:
108 No standard DOS requests are displayed, when an error occurs.
111 DIRECT:
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
118 file.
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
123 needed sometimes.
125 VERBOSE:
126 Copy gives additional output.
128 ERRWARN:
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.
139 MAKEDIR:
140 All names specified in FROM field are taken as directories, which must be
141 created.
143 MOVE:
144 The files are not copied, but moved (or renamed). This means that after
145 move operation the source does no longer exist.
147 DELETE:
148 This does not copy anything, but deletes the source files!
150 HARD=HARDLINK:
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.
156 SOFT=SOFTLINK:
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
160 set to true.
161 NOTE: Softlinks are not official supported by OS and may be dangerous.
162 I suggest not to use this option! See description below.
164 FOLNK=FORCELINK:
165 When linking of directories should be possible, this option is needed. See
166 section "About links" for possible problems.
168 FODEL=FORCEDELETE:
169 When this option is enabled, files are deleted also, when they are delete
170 protected.
172 FOOVR=FORCEOVERWRITE:
173 When this option is enabled, files are overwritten also, when they are
174 protected.
176 DONTOVR=DONTOVERWRITE:
177 This option prevents overwriting of destination files.
179 NEWER:
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.
183 RESULT
185 NOTES
187 EXAMPLE
189 BUGS
191 SEE ALSO
193 Delete, Rename, MakeDir, MakeLink
195 INTERNALS
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
224 possible.
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
246 About links:
248 HARDLINKS:
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).
256 SOFTLINKS:
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
261 you!
263 PROBLEMS:
264 Links to directories may cause infinite directory loops!
266 Example: Having following directory tree:
268 DEV:
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
281 loop.
284 HISTORY
286 Copy was done by Dirk Stoecker (stoecker@amigaworld.com), donated
287 to AROS in March 2001
289 ******************************************************************************/
291 #define CTRL_C (SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
293 #define DEBUG 0
294 #define D(x)
296 #define USE_SOFTLINKCHECK 1
297 #define USE_ALWAYSVERBOSE 1
298 #define USE_BOGUSEOFWORKAROUND 0
300 #include <aros/asmcall.h>
301 #include <exec/devices.h>
302 #include <exec/io.h>
303 #include <exec/memory.h>
304 #include <exec/semaphores.h>
305 #include <exec/types.h>
306 #include <dos/exall.h>
308 #ifdef __SASC
309 typedef ULONG IPTR;
310 #else /* __SASC */
311 #include <aros/debug.h>
312 #endif /* __SASC */
314 #include <proto/dos.h>
315 #include <proto/exec.h>
317 #include <string.h>
319 const TEXT version[] = "\0$VER: Copy 50.17 (30.12.2011)";
321 static const UBYTE *PARAM =
322 "FROM/M,TO,PAT=PATTERN/K,BUF=BUFFER/K/N,ALL/S,"
323 "DIRECT/S,CLONE/S,DATES/S,NOPRO/S,COM=COMMENT/S,"
324 "QUIET/S,"
325 #if !USE_ALWAYSVERBOSE
326 "VERBOSE/S,"
327 #endif
328 "NOREQ/S,ERRWARN/S,MAKEDIR/S,"
329 "MOVE/S,DELETE/S,HARD=HARDLINK/S,SOFT=SOFTLINK/S,"
330 "FOLNK=FORCELINK/S,FODEL=FORCEDELETE/S,"
331 "FOOVR=FORCEOVERWRITE/S,DONTOVR=DONTOVERWRITE/S,"
332 "FORCE/S,NEWER/S";
334 #define COPYFLAG_ALL (1<<0)
335 #define COPYFLAG_DATES (1<<1)
336 #define COPYFLAG_NOPRO (1<<2)
337 #define COPYFLAG_COMMENT (1<<3)
338 #define COPYFLAG_FORCELINK (1<<4)
339 #define COPYFLAG_FORCEDELETE (1<<5)
340 #define COPYFLAG_FORCEOVERWRITE (1<<6)
341 #define COPYFLAG_DONTOVERWRITE (1<<7)
342 #define COPYFLAG_QUIET (1<<8)
343 #define COPYFLAG_VERBOSE (1<<9)
344 #define COPYFLAG_ERRWARN (1<<10)
345 #define COPYFLAG_NEWER (1<<11)
347 #define COPYFLAG_SOFTLINK (1<<20) /* produce softlinks */
348 #define COPYFLAG_DEST_FILE (1<<21) /* one file mode */
349 #define COPYFLAG_DONE (1<<22) /* did something in DoWork */
350 #define COPYFLAG_ENTERSECOND (1<<23) /* entered directory second time */
352 #define COPYFLAG_SRCNOFILESYS (1<<24) /* source is no filesystem */
353 #define COPYFLAG_DESNOFILESYS (1<<25) /* destination is no filesystem */
355 #define COPYMODE_COPY 0
356 #define COPYMODE_MOVE 1
357 #define COPYMODE_DELETE 2
358 #define COPYMODE_MAKEDIR 3
359 #define COPYMODE_LINK 4
361 #define PRINTOUT_SIZE 50 /* maximum size of name printout */
362 #define PRINTOUT_SPACES 10 /* maximum number of spaces */
364 #define FILEPATH_SIZE 2048 /* maximum size of filepaths */
366 /* return values */
367 #define TESTDEST_DIR_OK 2 /* directory exists, go in */
368 #define TESTDEST_DELETED 1 /* file or empty directory deleted */
369 #define TESTDEST_NONE 0 /* nothing existed */
370 #define TESTDEST_ERROR -1 /* an error occured */
371 #define TESTDEST_CANTDELETE -2 /* deletion not allowed (DONTOV) */
373 struct IptrArgs
375 IPTR from;
376 IPTR to;
377 IPTR pattern;
378 IPTR buffer;
379 IPTR all;
380 IPTR direct;
381 IPTR clone;
382 IPTR dates;
383 IPTR nopro;
384 IPTR comment;
385 IPTR quiet;
386 #if !USE_ALWAYSVERBOSE
387 IPTR verbose;
388 #endif
389 IPTR noreq;
390 IPTR errwarn;
391 IPTR makedir;
392 IPTR move_mode;
393 IPTR delete_mode;
394 IPTR hardlink;
395 IPTR softlink;
396 IPTR forcelink;
397 IPTR forcedelete;
398 IPTR forceoverwrite;
399 IPTR dontoverwrite;
400 IPTR force;
401 IPTR newer;
405 struct Args
407 STRPTR *from;
408 STRPTR to;
409 STRPTR pattern;
410 LONG *buffer;
411 LONG all;
412 LONG direct;
413 LONG clone;
414 LONG dates;
415 LONG nopro;
416 LONG comment;
417 LONG quiet;
418 LONG verbose;
419 LONG noreq;
420 LONG errwarn;
421 LONG makedir;
422 LONG move_mode;
423 LONG delete_mode;
424 LONG hardlink;
425 LONG softlink;
426 LONG forcelink;
427 LONG forcedelete;
428 LONG forceoverwrite;
429 LONG dontoverwrite;
430 LONG force;
431 LONG newer;
435 struct CopyData
437 struct ExecBase *SysBase;
438 struct DosLibrary *DOSBase;
440 ULONG Flags;
441 ULONG BufferSize;
442 STRPTR Pattern;
443 BPTR Destination;
444 BPTR CurDest; /* Current Destination */
445 ULONG DestPathSize;
446 struct FileInfoBlock Fib;
447 UBYTE Mode;
448 UBYTE RetVal; /* when set, error output is already done */
449 UBYTE RetVal2; /* when set, error output must be done */
450 LONG IoErr;
451 UBYTE Deep;
452 UBYTE FileName[FILEPATH_SIZE];
453 UBYTE DestName[FILEPATH_SIZE];
455 STRPTR CopyBuf;
456 ULONG CopyBufLen;
460 ** This data keeps the extracted data from a version string
463 #define VDNAMESIZE 96
465 struct VersionData {
466 UBYTE vd_Name[VDNAMESIZE];
467 LONG vd_Version;
468 LONG vd_Revision;
470 LONG vd_Day;
471 LONG vd_Month;
472 LONG vd_Year;
475 #ifndef MIN
476 #define MIN(a,b) ((a)<(b)?(a):(b))
477 #endif
479 #define CHECKVER_DESTOLDER -1
480 #define CHECKVER_DESTNEWER 1
481 #define CHECKVER_EQUAL 0
483 #define TEXT_READ texts[0]
484 #define TEXT_COPIED texts[1]
485 #define TEXT_MOVED texts[2]
486 #define TEXT_DELETED texts[3]
487 #define TEXT_LINKED texts[4]
488 #define TEXT_RENAMED texts[5]
489 #define TEXT_CREATED texts[6]
490 #define TEXT_ENTERED texts[7]
491 #define TEXT_OPENED_FOR_OUTPUT texts[8]
492 #define TEXTNUM_MODE 9
493 #define TEXT_DIRECTORY texts[15]
494 #define TEXT_NOT_DONE texts[16]
495 #define TEXT_NOTHING_DONE texts[17]
496 #define TEXT_ERR_FORCELINK texts[18]
497 #define TEXT_ERR_DELETE_DEVICE texts[19]
498 #define TEXT_ERR_DEST_DIR texts[20]
499 #define TEXT_ERR_INFINITE_LOOP texts[21]
500 #define TEXT_ERR_WILDCARD_DEST texts[22]
502 const CONST_STRPTR texts[] =
504 "read.",
505 "copied",
506 "moved",
507 "deleted",
508 "linked",
509 "renamed",
510 " [created]",
511 "entered",
512 "opened for output",
513 "COPY mode\n",
514 "MOVE mode\n",
515 "DELETE mode\n",
516 "MAKEDIR mode\n",
517 "HARDLINK mode\n",
518 "SOFTLINK mode\n",
519 "%s (Dir)", /* output of directories */
520 " not %s",
521 "No file was processed.\n",
522 "FORCELINK keyword required.\n",
523 "A device cannot be deleted.",
524 "Destination must be a directory.\n",
525 "Infinite loop not allowed.\n",
526 "Wildcard destination invalid.\n",
529 LONG CopyFile(BPTR, BPTR, ULONG, struct CopyData *);
530 void DoWork(STRPTR, struct CopyData *);
531 LONG IsMatchPattern(STRPTR name, struct CopyData *cd);
532 LONG IsPattern(STRPTR, struct CopyData *); /* return 0 -> NOPATTERN, return -1 --> ERROR */
533 LONG KillFile(STRPTR, ULONG, struct CopyData *);
534 LONG KillFileKeepErr(STRPTR name, ULONG doit, struct CopyData *);
535 LONG LinkFile(BPTR, STRPTR, ULONG, struct CopyData *);
536 BPTR OpenDestDir(STRPTR, struct CopyData *);
537 void PatCopy(STRPTR, struct CopyData *);
538 void PrintName(CONST_STRPTR, ULONG, ULONG, ULONG, struct CopyData *);
539 void PrintNotDone(CONST_STRPTR, CONST_STRPTR, ULONG, ULONG, struct CopyData *);
540 ULONG TestFileSys(STRPTR, struct CopyData *); /* returns value, when is a filesystem */
541 void SetData(STRPTR, struct CopyData *);
542 LONG TestDest(STRPTR, ULONG, struct CopyData *);
543 ULONG TestLoop(BPTR, BPTR, struct CopyData *);
544 static LONG CheckVersion( struct CopyData *cd );
545 static void makeversionfromstring( STRPTR buffer, struct VersionData *vd, struct CopyData *cd);
546 static STRPTR skipspaces( STRPTR buffer);
547 static STRPTR skipnonspaces( STRPTR buffer);
548 static BOOL VersionFind( CONST_STRPTR path, struct VersionData *vds, struct CopyData *cd);
550 #ifdef __MORPHOS__
551 #define BNULL NULL
552 int main()
554 #else
555 __startup static AROS_ENTRY(int, Start,
556 AROS_UFHA(char *, argstr, A0),
557 AROS_UFHA(ULONG, argsize, D0),
558 struct ExecBase *, SysBase)
559 #endif
561 AROS_USERFUNC_INIT
563 struct DosLibrary *DOSBase;
564 struct Process *task;
565 struct CopyData *cd;
566 int retval = RETURN_FAIL;
568 /* test for WB and reply startup-message */
569 if (!(task = (struct Process *)FindTask(NULL))->pr_CLI)
571 WaitPort(&task->pr_MsgPort);
572 Forbid();
573 ReplyMsg(GetMsg(&task->pr_MsgPort));
575 return RETURN_FAIL;
578 DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 37);
579 cd = AllocMem(sizeof(*cd), MEMF_PUBLIC | MEMF_CLEAR);
581 if (DOSBase && cd)
583 STRPTR a[2] = { "", 0 };
584 struct RDArgs *rda;
585 struct IptrArgs iArgs;
586 struct Args args = {};
588 cd->SysBase = SysBase;
589 cd->DOSBase = DOSBase;
590 #define SysBase cd->SysBase
591 #define DOSBase cd->DOSBase
593 cd->BufferSize = 512*1024;
594 cd->Mode = COPYMODE_COPY;
595 cd->RetVal2 = RETURN_FAIL;
596 cd->Deep = 1;
598 memset(&iArgs, 0, sizeof(struct IptrArgs));
600 rda = (struct RDArgs *)AllocDosObject(DOS_RDARGS, NULL);
601 if (rda)
603 rda->RDA_ExtHelp =
604 "FROM multiple input files\n"
605 "TO destination file or directory\n"
606 "PATTERN a pattern the filenames must match\n"
607 "BUFFER buffersize for copy buffer (default 200 [100K])\n"
608 "ALL deep scan into sub directories\n"
609 "DIRECT copy/delete only: work without any tests or options\n"
610 "CLONE copy comment, protection bits and date as well\n"
611 "DATES copy dates\n"
612 "NOPRO do not copy protection bits\n"
613 "COMMENT copy filecomment\n"
614 "QUIET suppress all output and requesters\n"
615 #if !USE_ALWAYSVERBOSE
616 "VERBOSE give additional output\n"
617 #endif
618 "NOREQ suppress requesters\n"
619 "ERRWARN do not proceed, when one file failed\n"
620 "MAKEDIR produce directories\n"
621 "MOVE delete source files after copying successful\n"
622 "DELETE do not copy, but delete the source files\n"
623 "HARDLINK make a hardlink to source instead of copying\n"
624 "SOFTLINK make a softlink to source instead of copying\n"
625 "FOLNK also makes links to directories\n"
626 "FODEL delete protected files also\n"
627 "FOOVR also overwrite protected files\n"
628 "DONTOVR do never overwrite destination\n"
629 "FORCE DO NOT USE. Call compatibility only.\n"
630 "NEWER will compare version strings and only overwrites older files\n";
632 if (ReadArgs(PARAM, (IPTR *)&iArgs, rda))
634 ULONG patbufsize = 0;
635 LONG i = 0;
636 APTR win = task->pr_WindowPtr;
638 args.from = (STRPTR *)iArgs.from;
639 args.to = (STRPTR)iArgs.to;
640 args.pattern = (STRPTR)iArgs.pattern;
641 args.buffer = (LONG *)iArgs.buffer;
642 args.all = (LONG)iArgs.all;
643 args.direct = (LONG)iArgs.direct;
644 args.clone = (LONG)iArgs.clone;
645 args.dates = (LONG)iArgs.dates;
646 args.nopro = (LONG)iArgs.nopro;
647 args.comment = (LONG)iArgs.comment;
648 args.quiet = (LONG)iArgs.quiet;
649 #if USE_ALWAYSVERBOSE
650 args.verbose = FALSE;
651 #else
652 args.verbose = (LONG)iArgs.verbose;
653 #endif
654 args.noreq = (LONG)iArgs.noreq;
655 args.errwarn = (LONG)iArgs.errwarn;
656 args.makedir = (LONG)iArgs.makedir;
657 args.move_mode = (LONG)iArgs.move_mode;
658 args.delete_mode = (LONG)iArgs.delete_mode;
659 args.hardlink = (LONG)iArgs.hardlink;
660 args.softlink = (LONG)iArgs.softlink;
661 args.forcelink = (LONG)iArgs.forcelink;
662 args.forcedelete = (LONG)iArgs.forcedelete;
663 args.forceoverwrite = (LONG)iArgs.forceoverwrite;
664 args.dontoverwrite = (LONG)iArgs.dontoverwrite;
665 args.force = (LONG)iArgs.force;
666 args.newer = (LONG)iArgs.newer;
668 if (args.quiet) /* when QUIET, SILENT and NOREQ are also
669 true! */
671 /* Original doesn't hide requesters with QUIET */
672 /*args.noreq = 1;*/
673 args.verbose = FALSE;
676 if (args.buffer && *args.buffer > 0) /* minimum buffer size */
678 cd->BufferSize = *args.buffer * 512;
681 if (args.quiet)
683 cd->Flags |= COPYFLAG_QUIET;
686 #if !USE_ALWAYSVERBOSE
687 if (args.verbose)
689 cd->Flags |= COPYFLAG_VERBOSE;
691 #endif
692 if (args.all)
694 cd->Flags |= COPYFLAG_ALL;
697 if (args.clone)
699 cd->Flags |= COPYFLAG_DATES | COPYFLAG_COMMENT;
702 if (args.dates)
704 cd->Flags |= COPYFLAG_DATES;
707 if (args.comment)
709 cd->Flags |= COPYFLAG_COMMENT;
712 if (args.nopro)
714 cd->Flags |= COPYFLAG_NOPRO;
717 if (args.forcelink)
719 cd->Flags |= COPYFLAG_FORCELINK;
722 if (args.forcedelete)
724 cd->Flags |= COPYFLAG_FORCEDELETE;
727 if (args.forceoverwrite)
729 cd->Flags |= COPYFLAG_FORCEOVERWRITE;
732 if (args.dontoverwrite)
734 cd->Flags |= COPYFLAG_DONTOVERWRITE;
737 if (args.newer)
739 cd->Flags |= COPYFLAG_NEWER|COPYFLAG_DONTOVERWRITE;
742 if (args.errwarn)
744 cd->Flags |= COPYFLAG_ERRWARN;
747 if (args.force) /* support OS Delete and MakeLink command
748 options */
750 if (args.delete_mode)
752 cd->Flags |= COPYFLAG_FORCEDELETE;
755 if (args.hardlink || args.softlink)
757 cd->Flags |= COPYFLAG_FORCELINK;
761 if (!args.from) /* no args.from means currentdir */
763 args.from = a;
766 if (args.noreq) /* no dos.library requests allowed */
768 task->pr_WindowPtr = (APTR)-1;
771 if (args.delete_mode)
773 ++i;
774 cd->Mode = COPYMODE_DELETE;
777 if (args.move_mode)
779 ++i;
780 cd->Mode = COPYMODE_MOVE;
783 if (args.makedir)
785 ++i;
786 cd->Mode = COPYMODE_MAKEDIR;
789 if (args.hardlink)
791 ++i;
792 cd->Mode = COPYMODE_LINK;
795 if (args.softlink)
797 ++i;
798 cd->Mode = COPYMODE_LINK;
799 cd->Flags |= COPYFLAG_SOFTLINK | COPYFLAG_FORCELINK;
802 if (cd->Mode != COPYMODE_DELETE &&
803 cd->Mode != COPYMODE_MAKEDIR && !args.to)
805 if (*(args.from + 1)) /* when no TO is specified, the arg
806 is last */
807 { /* one of from. Copy this argument into */
808 STRPTR *a; /* args.to */
810 a = args.from;
812 while(*(++a))
815 args.to = *(--a);
816 *a = 0;
819 #if USE_ALWAYSVERBOSE
821 /* Only do this if quiet isn't set - bigfoot */
822 if (!args.quiet)
824 /* If more than two args, be verbose... - Piru */
825 if (args.from[0] && args.from[1])
827 args.verbose = TRUE;
828 cd->Flags |= COPYFLAG_VERBOSE;
831 /* If any of the sources is a pattern, be verbose... - Piru */
833 STRPTR *a;
835 for (a = args.from; *a; a++)
837 if (IsMatchPattern(*a, cd) != 0)
839 args.verbose = TRUE;
840 cd->Flags |= COPYFLAG_VERBOSE;
845 #endif
847 /* test if more than one of the above four or any other wrong
848 arguments */
850 if (i > 1 ||
851 (args.from == a && cd->Mode == COPYMODE_MAKEDIR) ||
852 (args.direct && (args.from == a || !*args.from ||
853 args.pattern ||
854 (cd->Flags & ~(COPYFLAG_QUIET | COPYFLAG_VERBOSE | COPYFLAG_ERRWARN)) ||
855 (cd->Mode != COPYMODE_DELETE && (cd->Mode != COPYMODE_COPY ||
856 !args.to || args.from[1])))) ||
857 (args.dontoverwrite && args.forceoverwrite) ||
858 /* (args.nopro && args.clone) ||*/ /* Ignore, like original - Piru */
859 (args.softlink && args.all) ||
860 (!args.to && cd->Mode != COPYMODE_DELETE && cd->Mode != COPYMODE_MAKEDIR))
862 cd->IoErr = ERROR_TOO_MANY_ARGS;
864 else if (cd->Mode == COPYMODE_MAKEDIR)
866 LONG i;
867 BPTR dir;
868 cd->RetVal2 = RETURN_OK;
870 #if !USE_ALWAYSVERBOSE
871 if (args.verbose)
873 PutStr(texts[TEXTNUM_MODE + COPYMODE_MAKEDIR]);
875 #endif
877 while (!cd->RetVal && !cd->RetVal2 && *args.from)
879 if ((i = IsPattern(*args.from, cd)))
881 if (i != -1)
883 cd->RetVal = RETURN_ERROR;
885 if (!args.quiet)
887 PutStr(TEXT_ERR_WILDCARD_DEST);
890 else
892 cd->RetVal2 = RETURN_FAIL;
896 if ((dir = OpenDestDir(*args.from, cd)))
898 UnLock(dir);
899 cd->Flags |= COPYFLAG_DONE;
902 ++args.from;
904 } /* cd->Mode == COPYMODE_MAKEDIR */
905 else if (args.direct)
907 if (cd->Mode == COPYMODE_COPY)
909 BPTR in, out;
911 if ((in = Open(*args.from, MODE_OLDFILE)))
913 if ((out = Open(args.to, MODE_NEWFILE)))
915 cd->RetVal2 = CopyFile(in, out, cd->BufferSize, cd);
916 if (cd->RetVal2 != 0)
917 cd->IoErr = IoErr();
918 Close(out);
920 else
921 cd->IoErr = IoErr();
923 Close(in);
925 else
926 cd->IoErr = IoErr();
928 else /* COPYMODE_DELETE */
930 while (*args.from)
932 KillFile(*(args.from++), cd->Flags & COPYFLAG_FORCEDELETE, cd);
935 cd->RetVal2 = RETURN_OK;
938 else
940 if (args.pattern && *args.pattern)
942 patbufsize = (strlen(args.pattern) << 1) + 3;
944 if ((cd->Pattern = (STRPTR)AllocMem(patbufsize,
945 MEMF_ANY)))
947 if (ParsePatternNoCase(args.pattern, cd->Pattern,
948 patbufsize) < 0)
950 FreeMem(cd->Pattern, patbufsize);
951 cd->Pattern = 0;
956 if (1) // (cd->Fib = (struct FileInfoBlock *)AllocDosObject(DOS_FIB, NULL)))
958 #if !USE_ALWAYSVERBOSE
959 if (args.verbose)
961 PutStr(texts[TEXTNUM_MODE + cd->Mode +
962 (cd->Flags & COPYFLAG_SOFTLINK ? 1 : 0)]);
964 #endif
965 if (args.pattern && !cd->Pattern)
967 if (!*args.pattern)
969 cd->IoErr = ERROR_BAD_TEMPLATE;
972 else if (cd->Mode == COPYMODE_DELETE)
974 cd->RetVal2 = RETURN_OK;
976 while (cd->RetVal <= (args.errwarn ? RETURN_OK : RETURN_WARN)
977 && *args.from)
979 PatCopy(*(args.from++), cd);
982 else if ((i = IsPattern(args.to, cd)))
984 if (i != -1)
986 cd->RetVal = RETURN_ERROR;
988 if (!args.quiet)
990 PutStr(TEXT_ERR_WILDCARD_DEST);
994 else
996 STRPTR path;
998 if (*(path = PathPart(args.to)) == '/')
1000 ++path; /* is destination a path description ? */
1003 if (*path && !*(args.from+1) &&
1004 !(i = IsMatchPattern(*args.from, cd)))
1006 BPTR lock;
1008 /* is destination an existing directory */
1009 if ((lock = Lock(args.to, SHARED_LOCK)))
1011 if (Examine(lock, &cd->Fib))
1013 if (cd->Fib.fib_DirEntryType > 0)
1015 cd->RetVal2 = RETURN_OK;
1018 /* indicate dir-mode for next if */
1020 else
1022 i = 1;
1025 UnLock(lock);
1028 /* Some magic to handle tick quoted pattern object names. Quite crude way to
1029 * handle it, but I couldn't think of anything better. - Piru
1031 #if 1
1032 if (!i && cd->RetVal2 && !IsMatchPattern(*args.from, cd))
1034 ULONG len;
1035 STRPTR pat;
1037 //Printf("pattern check <%s>\n", *args.from);
1039 len = (strlen(*args.from) << 1) + 3;
1041 if ((pat = (STRPTR)AllocMem(len,
1042 MEMF_ANY)))
1044 if (ParsePattern(*args.from, pat, len) > -1 &&
1045 strlen(pat) <= strlen(*args.from))
1047 lock = Lock(pat, SHARED_LOCK);
1048 if (lock)
1050 UnLock(lock);
1052 strcpy(*args.from, pat);
1056 FreeMem(pat, len);
1059 #endif
1061 /* is source a directory */
1062 if (!i && cd->RetVal2 &&
1063 (lock = Lock(*args.from, SHARED_LOCK)))
1065 if (Examine(lock, &cd->Fib))
1067 cd->RetVal2 = RETURN_OK;
1068 if (cd->Mode != COPYMODE_COPY ||
1069 cd->Fib.fib_DirEntryType < 0)
1071 UBYTE sep;
1073 cd->Flags |= COPYFLAG_DEST_FILE;
1075 /* produce missing destination directories */
1076 sep = *path;
1077 *path = 0;
1079 if ((cd->CurDest = OpenDestDir(args.to, cd)))
1081 *path = sep;
1083 /* do the job */
1084 UnLock(lock);
1085 lock = 0;
1086 CopyMem(*args.from, cd->FileName,
1087 1 + strlen(*args.from));
1088 DoWork(FilePart(args.to), cd); /* on file call */
1089 UnLock(cd->CurDest);
1094 if (lock)
1096 UnLock(lock);
1100 if (lock == 0 && cd->Mode == COPYMODE_COPY && !TestFileSys(*args.from, cd))
1102 UBYTE sep;
1103 cd->Flags |= COPYFLAG_DEST_FILE | COPYFLAG_SRCNOFILESYS;
1104 cd->RetVal2 = RETURN_OK;
1106 /* produce missing destination directories */
1107 sep = *path;
1108 *path = 0;
1110 if ((cd->CurDest = OpenDestDir(args.to, cd)))
1112 *path = sep;
1114 /* do the job */
1115 CopyMem(*args.from, cd->FileName, 1 + strlen(*args.from));
1116 DoWork(FilePart(args.to), cd); /* on file call */
1117 UnLock(cd->CurDest);
1121 else if (i != -1)
1123 cd->RetVal2 = RETURN_OK;
1126 if (!cd->RetVal && !cd->RetVal2 && !(cd->Flags & COPYFLAG_DEST_FILE) &&
1127 (cd->Destination = OpenDestDir(args.to, cd)))
1129 while (cd->RetVal <= (args.errwarn ? RETURN_OK : RETURN_WARN)
1130 && *args.from && !CTRL_C)
1132 PatCopy(*(args.from++), cd);
1135 UnLock(cd->Destination);
1137 } /* else */
1139 if (!(cd->Flags & COPYFLAG_DONE) && args.verbose &&
1140 !cd->RetVal && !cd->RetVal2)
1142 PutStr(TEXT_NOTHING_DONE);
1145 } /* if (1) */
1147 if (cd->Pattern)
1149 FreeMem(cd->Pattern, patbufsize);
1151 } /* else */
1153 task->pr_WindowPtr = win;
1155 FreeArgs(rda);
1156 } /* ReadArgs */
1158 FreeDosObject(DOS_RDARGS, rda);
1159 } /* AllocDosObject */
1161 if (!cd->RetVal2 && CTRL_C)
1163 SetIoErr(ERROR_BREAK);
1164 cd->RetVal2 = RETURN_WARN;
1167 if (cd->RetVal)
1169 cd->RetVal2 = cd->RetVal;
1172 if (args.errwarn && cd->RetVal2 == RETURN_WARN)
1174 cd->RetVal2 = RETURN_ERROR;
1177 if (cd->CopyBuf)
1179 FreeMem(cd->CopyBuf, cd->CopyBufLen);
1182 #undef SysBase
1183 #undef DOSBase
1185 if (cd)
1187 retval = cd->RetVal2;
1188 SetIoErr(cd->IoErr);
1189 FreeMem(cd, sizeof(*cd));
1191 else if (DOSBase)
1193 PrintFault(IoErr(), NULL);
1195 if (DOSBase)
1197 CloseLibrary((struct Library *)DOSBase);
1199 return retval;
1201 AROS_USERFUNC_EXIT
1204 /* This code is pure and has library bases in explicitly allocated data structure */
1205 #define SysBase cd->SysBase
1206 #define DOSBase cd->DOSBase
1208 void PatCopy(STRPTR name, struct CopyData *cd)
1210 struct AnchorPath *APath;
1211 ULONG retval, doit = 0, deep = 0, failval = RETURN_WARN, first = 0;
1213 #if DEBUG
1214 Printf("PatCopy(%s, .)\n", name);
1215 #endif
1217 if ((cd->Mode == COPYMODE_COPY || (cd->Flags & COPYFLAG_ALL)) && !IsMatchPattern(name, cd))
1219 first = 1; /* enter first directory (support of old copy style) */
1222 if (cd->Flags & COPYFLAG_ERRWARN)
1224 failval = RETURN_OK;
1227 cd->CurDest = cd->Destination;
1228 cd->DestPathSize = 0;
1230 if (cd->Mode == COPYMODE_COPY && !TestFileSys(name, cd))
1232 cd->Flags |= COPYFLAG_SRCNOFILESYS;
1233 CopyMem(name, cd->FileName, 1 + strlen(name));
1234 DoWork(FilePart(name), cd);
1235 cd->Flags &= ~COPYFLAG_SRCNOFILESYS;
1237 return;
1240 if ((APath = (struct AnchorPath *)AllocMem(sizeof(struct AnchorPath) + FILEPATH_SIZE,
1241 MEMF_PUBLIC | MEMF_CLEAR)))
1243 int parentdirerr = 0;
1245 APath->ap_BreakBits = SIGBREAKF_CTRL_C;
1246 APath->ap_Strlen = FILEPATH_SIZE;
1248 for (retval = MatchFirst(name, APath);
1249 !retval && cd->RetVal <= failval && !cd->RetVal2;
1250 retval = MatchNext(APath)
1253 if (parentdirerr)
1255 //Printf("ParentDir() fuxored last round! Would copy next files to SYS: !\n");
1256 retval = IoErr();
1257 if (!retval)
1258 cd->IoErr = retval = ERROR_INVALID_LOCK;
1259 break;
1262 if (doit)
1264 DoWork(cd->Fib.fib_FileName, cd);
1265 doit = 0;
1268 if (deep) /* used for Deep checking */
1270 ++cd->Deep;
1271 deep = 0;
1274 cd->Flags &= ~COPYFLAG_ENTERSECOND;
1276 CopyMem(APath->ap_Buf, cd->FileName, FILEPATH_SIZE);
1277 CopyMem(&APath->ap_Info, &cd->Fib, sizeof(struct FileInfoBlock));
1279 if (first && APath->ap_Info.fib_DirEntryType > 0)
1281 #if USE_ALWAYSVERBOSE
1282 /* If the source is a directory, be verbose - Piru */
1283 cd->Flags |= COPYFLAG_VERBOSE;
1284 #endif
1285 APath->ap_Flags |= APF_DODIR;
1287 else if (APath->ap_Flags & APF_DIDDIR)
1289 BPTR i;
1291 cd->Flags |= COPYFLAG_ENTERSECOND;
1292 APath->ap_Flags &= ~APF_DIDDIR;
1293 --cd->Deep;
1295 if (cd->Mode == COPYMODE_DELETE || cd->Mode == COPYMODE_MOVE)
1297 doit = 1;
1300 if ((i = cd->CurDest))
1302 cd->CurDest = ParentDir(i);
1303 cd->DestPathSize = 0;
1305 if (i != cd->Destination)
1307 UnLock(i);
1310 if (!cd->CurDest)
1312 parentdirerr = 1;
1313 continue;
1317 else if (APath->ap_Info.fib_DirEntryType > 0)
1319 doit = 1;
1321 if (cd->Flags & COPYFLAG_ALL)
1323 BOOL enter;
1325 #if USE_SOFTLINKCHECK
1327 #define BUFFERSIZE 512
1328 if (APath->ap_Info.fib_DirEntryType == ST_SOFTLINK)
1330 UBYTE *buffer = AllocMem(BUFFERSIZE, MEMF_PUBLIC);
1332 D(Printf("%s is a softlink\n", APath->ap_Info.fib_FileName));
1333 enter = FALSE;
1334 doit = FALSE;
1336 if (buffer)
1338 struct DevProc *dvp = GetDeviceProc("", NULL);
1340 if (ReadLink(dvp->dvp_Port, APath->ap_Current->an_Lock, APath->ap_Info.fib_FileName, buffer, BUFFERSIZE - 1) > 0)
1342 BOOL link_ok = FALSE;
1343 BPTR dir, lock;
1345 buffer[BUFFERSIZE - 1] = '\0';
1346 D(Printf("Softlink target: %s\n", buffer));
1348 dir = CurrentDir(APath->ap_Current->an_Lock);
1349 lock = Lock(buffer, SHARED_LOCK);
1350 if (lock)
1352 struct FileInfoBlock *fib = AllocDosObject(DOS_FIB, NULL);
1354 if (fib)
1356 if (Examine(lock, fib))
1358 link_ok = TRUE;
1359 D(Printf("Target type: %ld\n", fib->fib_DirEntryType));
1361 if (fib->fib_DirEntryType > 0)
1362 enter = TRUE;
1363 else
1365 * FIXME: This currently just prevents treating symlinks to files as
1366 * directories during copying.
1367 * DoWork() should be extended to handle symlinks correctly. BTW, how exactly ?
1369 doit = FALSE;
1371 FreeDosObject(DOS_FIB, fib);
1373 UnLock(lock);
1376 CurrentDir(dir);
1378 if (!link_ok)
1380 Printf("Warning: Skipping dangling softlink %s -> %s\n",
1381 APath->ap_Info.fib_FileName, buffer);
1384 FreeDeviceProc(dvp);
1385 FreeMem(buffer, BUFFERSIZE);
1388 else
1389 #endif /* USE_SOFTLINKCHECK */
1390 enter = TRUE;
1392 if (enter)
1394 APath->ap_Flags |= APF_DODIR;
1395 deep = 1;
1399 else if (!cd->Pattern || MatchPatternNoCase(cd->Pattern, APath->ap_Info.fib_FileName))
1401 doit = 1;
1404 first = 0;
1407 MatchEnd(APath);
1409 if (retval && retval != ERROR_NO_MORE_ENTRIES)
1411 LONG ioerr = IoErr();
1412 #if USE_ALWAYSVERBOSE
1413 cd->Flags |= COPYFLAG_VERBOSE;
1414 #endif
1415 Printf("%s - ", APath->ap_Info.fib_FileName);
1416 PrintFault(ioerr, NULL);
1417 cd->IoErr = ioerr;
1419 cd->RetVal2 = RETURN_FAIL;
1422 if (doit)
1424 DoWork(cd->Fib.fib_FileName, cd);
1427 /* No need to clear the flags here, as they are cleared on next PatJoin
1428 call (DoWork is not called first round, as lock is zero!). */
1430 FreeMem(APath, sizeof(struct AnchorPath) + FILEPATH_SIZE);
1432 else
1434 cd->RetVal = RETURN_FAIL;
1436 if (!(cd->Flags & COPYFLAG_QUIET))
1438 PrintFault(ERROR_NO_FREE_STORE, NULL);
1442 if (cd->CurDest && cd->CurDest != cd->Destination)
1444 UnLock(cd->CurDest);
1449 LONG IsPattern(STRPTR name, struct CopyData *cd)
1451 LONG a, ret = -1;
1452 STRPTR buffer;
1454 a = (strlen(name) << 1) + 3;
1456 if ((buffer = (STRPTR)AllocMem(a, MEMF_ANY)))
1458 ret = ParsePattern(name, buffer, a);
1459 FreeMem(buffer, a);
1462 if (ret == -1)
1464 cd->IoErr = ERROR_NO_FREE_STORE;
1467 return ret;
1471 LONG IsMatchPattern(STRPTR name, struct CopyData *cd)
1473 struct AnchorPath ap;
1474 LONG ret = -1;
1476 ap.ap_BreakBits = 0;
1477 ap.ap_Flags = APF_DOWILD;
1478 ap.ap_Strlen = 0;
1480 if (MatchFirst(name, &ap) == 0)
1482 ret = (ap.ap_Flags & APF_ITSWILD) ? TRUE : FALSE;
1484 MatchEnd(&ap);
1487 return ret;
1491 LONG KillFile(STRPTR name, ULONG doit, struct CopyData *cd)
1493 if (doit)
1495 SetProtection(name, 0);
1498 return DeleteFile(name);
1502 BPTR OpenDestDir(STRPTR name, struct CopyData *cd)
1504 LONG a, err = 0, cr = 0;
1505 BPTR dir;
1506 STRPTR ptr = name;
1507 UBYTE as;
1509 if ((cd->Mode == COPYMODE_COPY || cd->Mode == COPYMODE_MOVE) && !TestFileSys(name, cd))
1511 cd->Flags |= COPYFLAG_DESNOFILESYS;
1512 CopyMem(name, cd->DestName, 1 + strlen(name));
1514 return Lock("", SHARED_LOCK);
1517 while (!err && *ptr != 0)
1519 while (*ptr && *ptr != '/')
1521 ++ptr;
1524 as = *ptr;
1525 *ptr = 0;
1527 if ((a = TestDest(name, 1, cd)) == TESTDEST_CANTDELETE)
1529 if (!(cd->Flags & COPYFLAG_QUIET))
1531 PutStr(TEXT_ERR_DEST_DIR);
1534 err = 2;
1536 else if (a < 0)
1538 err = 1;
1540 else if (a != TESTDEST_DIR_OK)
1542 if ((dir = CreateDir(name)))
1544 ++cr;
1546 if ((cd->Flags & COPYFLAG_VERBOSE))
1548 PrintName(name, 1, 1, 1, cd);
1549 Printf("%s\n", TEXT_CREATED);
1552 UnLock(dir);
1554 else
1556 cd->IoErr = IoErr();
1557 if (!(cd->Flags & COPYFLAG_QUIET))
1559 PrintNotDone(name, TEXT_CREATED, 1, 1, cd);
1562 err = 2;
1566 *ptr = as;
1568 /* 26-Oct-2003 bugfix: Don't scan past end of the string.
1569 * as is the old char, if '\0' we've reached the end of the
1570 * string. - Piru
1572 if (as)
1574 ptr++;
1578 if (err)
1580 cd->RetVal = RETURN_ERROR;
1582 if (!(cd->Flags & COPYFLAG_QUIET) && err == 1)
1584 PrintNotDone(name, TEXT_OPENED_FOR_OUTPUT, 1, 1, cd);
1587 return 0;
1590 if (cd->Mode == COPYMODE_MAKEDIR && !cr && !(cd->Flags & COPYFLAG_QUIET))
1592 cd->IoErr = ERROR_OBJECT_EXISTS;
1593 PrintNotDone(name, TEXT_CREATED, 1, 1, cd);
1596 return Lock(name, SHARED_LOCK);
1600 void PrintName(CONST_STRPTR name, ULONG deep, ULONG dir, ULONG txt, struct CopyData *cd)
1602 #if 0
1603 deep %= PRINTOUT_SPACES; /* reduce number of spaces */
1604 /* This produces an error with MaxonC++ */
1605 #endif
1607 PutStr(" ");
1608 if (deep)
1610 while (--deep)
1612 PutStr(" ");
1616 if (dir)
1618 PutStr(" ");
1621 #if 0
1622 if ((deep = strlen(name)) > PRINTOUT_SIZE) /* reduce name size */
1624 name += deep-PRINTOUT_SIZE;
1625 PutStr("...");
1627 #endif
1629 Printf((dir ? TEXT_DIRECTORY : (STRPTR) "%s"), name);
1631 if (!dir && txt)
1633 PutStr("..");
1636 Flush(Output());
1640 void PrintNotDone(CONST_STRPTR name, CONST_STRPTR txt, ULONG deep, ULONG dir, struct CopyData *cd)
1642 #if !USE_ALWAYSVERBOSE
1643 if (name)
1645 PrintName(name, deep, dir, 1, cd);
1647 #endif
1649 Printf(TEXT_NOT_DONE, txt);
1650 if (cd->IoErr != 0)
1652 PutStr(" - ");
1653 PrintFault(cd->IoErr, NULL);
1655 else
1656 PutStr("\n");
1660 /* returns value, when it seems to be a filesystem */
1661 ULONG TestFileSys(STRPTR name, struct CopyData *cd)
1663 STRPTR n = name;
1664 ULONG ret = 1;
1666 while (*n && *n != ':')
1668 ++n;
1671 if (*(n++) == ':')
1673 UBYTE a;
1675 a = *n;
1676 *n = 0;
1677 ret = IsFileSystem(name);
1678 *n = a;
1681 return ret;
1685 void DoWork(STRPTR name, struct CopyData *cd)
1687 BPTR pdir, lock = 0;
1688 CONST_STRPTR printerr = NULL, printok = "";
1690 #if DEBUG
1691 Printf("DoWork(%s, .)\n", name);
1692 #endif
1694 if (cd->RetVal > (cd->Flags & COPYFLAG_ERRWARN ? RETURN_OK : RETURN_WARN) || cd->RetVal2)
1696 #if DEBUG
1697 Printf("DoWork(RetVal %ld)\n", cd->RetVal);
1698 #endif
1699 return;
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;
1710 #if DEBUG
1711 Printf("DoWork(NameFromLock RetVal %ld)\n", cd->RetVal);
1712 #endif
1714 return;
1717 cd->DestPathSize = strlen(cd->DestName);
1720 cd->DestName[cd->DestPathSize] = 0;
1721 AddPart(cd->DestName, name, FILEPATH_SIZE);
1724 if (cd->Flags & (COPYFLAG_SRCNOFILESYS|COPYFLAG_DESNOFILESYS))
1726 ULONG res = 0;
1727 BPTR in, out;
1728 CONST_STRPTR txt = TEXT_OPENED_FOR_OUTPUT;
1730 #if DEBUG
1731 Printf("Partly DIRECT mode active now (%s - %s)\n", cd->FileName,
1732 cd->DestName);
1733 #endif
1735 if ((in = Open(cd->FileName, MODE_OLDFILE)))
1737 txt = cd->Mode == COPYMODE_MOVE ? TEXT_MOVED : TEXT_COPIED;
1739 if ((out = Open(cd->DestName, MODE_NEWFILE)))
1741 ULONG h;
1743 h = CopyFile(in, out, cd->BufferSize, cd);
1744 if (h != 0)
1745 cd->IoErr = IoErr();
1746 Close(out);
1747 Close(in);
1748 in = BNULL;
1750 if (!h)
1752 if (cd->Mode == COPYMODE_MOVE)
1754 if (KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE, cd))
1756 res = 1;
1759 else
1761 res = 1;
1763 } else {
1764 KillFile(cd->DestName, 0, cd);
1767 else
1768 cd->IoErr = IoErr();
1770 if (in != BNULL)
1771 Close(in);
1773 else
1774 cd->IoErr = IoErr();
1776 if (!res && !(cd->Flags & COPYFLAG_QUIET))
1778 PrintNotDone(name, txt, cd->Deep, cd->Fib.fib_DirEntryType > 0, cd);
1780 else
1782 cd->Flags |= COPYFLAG_DONE;
1784 if ((cd->Flags & COPYFLAG_VERBOSE))
1786 Printf("%s\n", txt);
1790 #if DEBUG
1791 PutStr("DoWork(done)\n");
1792 #endif
1793 return;
1796 if (!(lock = Lock(cd->FileName, SHARED_LOCK)))
1798 cd->RetVal = RETURN_WARN;
1799 cd->IoErr = IoErr();
1801 if (!(cd->Flags & COPYFLAG_QUIET))
1803 PrintNotDone(cd->Fib.fib_FileName, TEXT_READ, cd->Deep,
1804 cd->Fib.fib_DirEntryType > 0, cd);
1807 #if DEBUG
1808 Printf("DoWork(Lock RetVal %ld)\n", cd->RetVal);
1809 #endif
1810 return;
1813 if (!(pdir = ParentDir(lock)))
1815 cd->RetVal = RETURN_ERROR;
1817 if (cd->Mode == COPYMODE_DELETE)
1819 if (!(cd->Flags & COPYFLAG_QUIET))
1821 Printf(" %s ", cd->FileName);
1822 Printf(TEXT_NOT_DONE, TEXT_DELETED);
1823 Printf("%s\n", TEXT_ERR_DELETE_DEVICE);
1827 UnLock(lock);
1829 #if DEBUG
1830 Printf("DoWork(ParentDir %ld)\n", cd->RetVal);
1831 #endif
1832 return;
1835 UnLock(pdir);
1837 if (!(cd->Flags & COPYFLAG_QUIET))
1839 if ((cd->Flags & COPYFLAG_VERBOSE))
1841 PrintName(name, cd->Deep, cd->Fib.fib_DirEntryType > 0, cd->Fib.fib_DirEntryType < 0 ||
1842 (cd->Flags & COPYFLAG_ALL ? cd->Mode != COPYMODE_DELETE : cd->Mode != COPYMODE_COPY) ||
1843 cd->Flags & COPYFLAG_ENTERSECOND, cd);
1847 if ((cd->Flags & COPYFLAG_ENTERSECOND) || (cd->Mode == COPYMODE_DELETE &&
1848 (!(cd->Flags & COPYFLAG_ALL) || cd->Fib.fib_DirEntryType < 0)))
1850 UnLock(lock);
1851 lock = 0;
1853 if (KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE, cd))
1855 printok = TEXT_DELETED;
1857 else
1859 cd->RetVal = RETURN_WARN;
1860 printerr = TEXT_DELETED;
1863 else if (cd->Mode == COPYMODE_DELETE)
1867 else if (cd->Fib.fib_DirEntryType > 0)
1869 LONG a;
1871 if ((cd->Flags & COPYFLAG_ALL || cd->Mode == COPYMODE_LINK ||
1872 cd->Mode == COPYMODE_MOVE) && TestLoop(lock, cd->CurDest, cd))
1874 printok = 0;
1875 cd->RetVal = RETURN_ERROR;
1877 if (!(cd->Flags & COPYFLAG_QUIET))
1879 if (!(cd->Flags & COPYFLAG_VERBOSE))
1881 PrintName(name, cd->Deep, 1, 1, cd);
1884 Printf(TEXT_NOT_DONE, TEXT_ENTERED);
1885 PutStr(TEXT_ERR_INFINITE_LOOP);
1888 else if ((a = TestDest(cd->DestName, 1, cd)) < 0)
1890 printerr = TEXT_CREATED;
1891 cd->RetVal = RETURN_ERROR;
1893 else if (cd->Flags & COPYFLAG_ALL)
1895 BPTR i;
1897 i = cd->CurDest;
1898 cd->DestPathSize = 0;
1900 if (a == TESTDEST_DIR_OK)
1902 if (!(cd->CurDest = Lock(cd->DestName, SHARED_LOCK)))
1904 cd->IoErr = IoErr();
1905 printerr = TEXT_ENTERED;
1906 cd->RetVal = RETURN_ERROR;
1908 else
1910 #if USE_ALWAYSVERBOSE
1911 printok = "";
1912 #else
1913 printok = TEXT_ENTERED;
1914 #endif
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;
1925 else
1927 cd->IoErr = IoErr();
1928 printerr = TEXT_ENTERED;
1929 cd->RetVal = RETURN_ERROR;
1932 else
1934 cd->IoErr = IoErr();
1935 printerr = TEXT_CREATED;
1936 cd->RetVal = RETURN_ERROR;
1939 if (!cd->CurDest)
1941 cd->CurDest = i;
1943 else if (i != cd->Destination)
1945 UnLock(i);
1948 else if (cd->Mode == COPYMODE_MOVE)
1950 if (Rename(cd->FileName, cd->DestName))
1952 printok = TEXT_RENAMED;
1954 else
1956 cd->IoErr = IoErr();
1957 printerr = TEXT_RENAMED;
1958 cd->RetVal = RETURN_WARN;
1961 else if (cd->Mode == COPYMODE_LINK)
1963 if (!(cd->Flags & COPYFLAG_FORCELINK))
1965 printok = 0;
1966 cd->RetVal = RETURN_WARN;
1968 if (!(cd->Flags & COPYFLAG_QUIET))
1970 if (!(cd->Flags & COPYFLAG_VERBOSE))
1972 PrintName(name, cd->Deep, 1, 1, cd);
1975 Printf(TEXT_NOT_DONE, TEXT_LINKED);
1976 PutStr(TEXT_ERR_FORCELINK);
1979 else if (LinkFile(lock, cd->DestName, cd->Flags & COPYFLAG_SOFTLINK, cd))
1981 printok = TEXT_LINKED;
1983 else
1985 printerr = TEXT_LINKED;
1986 cd->RetVal = RETURN_WARN;
1989 else /* COPY mode only displays directories, when not ALL */
1991 printok = 0;
1993 if (!(cd->Flags & COPYFLAG_QUIET))
1995 if (cd->Flags & COPYFLAG_VERBOSE)
1997 PutStr("\n");
2002 else
2004 /* test for existing destination file */
2005 if (TestDest(cd->DestName, 0, cd) < 0)
2007 printerr = TEXT_OPENED_FOR_OUTPUT;
2009 else if (cd->Mode == COPYMODE_MOVE && Rename(cd->FileName, cd->DestName))
2011 printok = TEXT_RENAMED;
2013 else if (cd->Mode == COPYMODE_LINK)
2015 if (!(cd->Flags & COPYFLAG_SOFTLINK) && LinkFile(lock, cd->DestName, 0, cd))
2017 printok = TEXT_LINKED;
2019 else
2021 printerr = TEXT_LINKED;
2022 cd->RetVal = RETURN_WARN;
2024 if (cd->Flags & COPYFLAG_SOFTLINK)
2026 cd->IoErr = ERROR_OBJECT_WRONG_TYPE;
2030 else
2032 ULONG res = 0, h;
2033 BPTR in, out;
2034 CONST_STRPTR txt = TEXT_OPENED_FOR_OUTPUT;
2036 if ((out = Open(cd->DestName, MODE_NEWFILE)))
2038 ULONG kill = 1;
2040 txt = cd->Mode == COPYMODE_MOVE ? TEXT_MOVED : TEXT_COPIED;
2041 UnLock(lock);
2042 lock = 0;
2044 if ((in = Open(cd->FileName, MODE_OLDFILE)))
2046 h = CopyFile(in, out, cd->BufferSize, cd);
2047 if (h != 0)
2048 cd->IoErr = IoErr();
2049 Close(out);
2050 out = BNULL;
2051 Close(in);
2053 if (!h)
2055 kill = 0;
2057 if (cd->Mode == COPYMODE_MOVE)
2059 if (KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE, cd))
2061 res = 1;
2064 else
2066 res = 1;
2070 else cd->IoErr = IoErr();
2072 if (out)
2074 Close(out);
2077 if (kill)
2079 KillFile(cd->DestName, 0, cd);
2082 else cd->IoErr = IoErr();
2084 if (!res)
2086 printerr = txt;
2087 cd->RetVal = RETURN_WARN;
2089 else
2091 printok = txt;
2096 if (printerr && !(cd->Flags & COPYFLAG_QUIET))
2098 PrintNotDone(name, printerr, cd->Deep, cd->Fib.fib_DirEntryType > 0, cd);
2100 else if (printok)
2102 cd->Flags |= COPYFLAG_DONE;
2104 if (!(cd->Flags & COPYFLAG_QUIET))
2106 if ((cd->Flags & COPYFLAG_VERBOSE))
2108 Printf("%s\n", printok);
2112 SetData(cd->DestName, cd);
2115 if (lock)
2117 UnLock(lock);
2122 LONG CopyFile(BPTR from, BPTR to, ULONG bufsize, struct CopyData *cd)
2124 STRPTR buffer;
2125 LONG s, err = 0;
2127 if (cd->CopyBuf)
2129 buffer = cd->CopyBuf;
2130 bufsize = cd->CopyBufLen;
2132 else
2136 buffer = (STRPTR)AllocMem(bufsize, MEMF_PUBLIC);
2137 if (buffer)
2139 cd->CopyBuf = buffer;
2140 cd->CopyBufLen = bufsize;
2141 break;
2144 bufsize >>= 1;
2146 } while (bufsize >= 512);
2149 if (buffer)
2151 #if USE_BOGUSEOFWORKAROUND
2152 struct FileInfoBlock *fib = (struct FileInfoBlock *) buffer; /* NOTE: bufsize is min 512 bytes */
2154 if (ExamineFH(from, fib))
2156 #warning "****** WARNING: No largefile support! ******"
2157 ULONG filesize = fib->fib_Size, copied = 0;
2159 /*Printf("filesize: %lu\n", filesize);*/
2163 ULONG brk = CTRL_C;
2164 if (brk || (s = Read(from, buffer, bufsize)) == -1 || Write(to, buffer, s) != s)
2166 if (brk)
2168 cd->IoErr = ERROR_BREAK;
2170 err = RETURN_FAIL;
2171 break;
2173 else if (s == 0 && copied < filesize)
2175 /* premature EOF with buggy fs */
2176 err = RETURN_FAIL;
2177 break;
2180 copied += s;
2182 } while (copied < filesize);
2184 /*{ LONG ioerr = IoErr();
2185 Printf("copied %lu/%lu\n", copied, filesize);
2186 SetIoErr(ioerr);}*/
2188 else
2189 #endif /* USE_BOGUSEOFWORKAROUND */
2191 /* Stream or so, copy until EOF or error */
2194 ULONG brk = CTRL_C;
2195 /* AROS: This flush appears to be required if reading from '*'
2196 * Maybe a bug in Read(), or AROS buffering?
2198 Flush(from);
2199 if (brk || (s = Read(from, buffer, bufsize)) == -1 || Write(to, buffer, s) != s)
2201 if (brk)
2203 cd->IoErr = ERROR_BREAK;
2205 err = RETURN_FAIL;
2206 break;
2208 } while (s > 0);
2211 /* Freed at exit to avoid fragmentation */
2212 /*FreeMem(buffer, bufsize);*/
2214 else
2216 err = RETURN_FAIL;
2219 return err;
2223 /* Softlink's path starts always with device name! f.e. "Ram Disk:T/..." */
2224 LONG LinkFile(BPTR from, STRPTR to, ULONG soft, struct CopyData *cd)
2226 if (soft)
2228 LONG ret = FALSE;
2229 UBYTE *name;
2231 name = AllocMem(FILEPATH_SIZE, MEMF_ANY);
2232 if (name)
2234 if (NameFromLock(from, name, FILEPATH_SIZE))
2236 ret = MakeLink(to, name, LINK_SOFT);
2239 FreeMem(name, FILEPATH_SIZE);
2242 return ret;
2244 else
2246 return MakeLink(to, (void *)from, LINK_HARD);
2251 /* return 0 means no loop, return != 0 means loop found */
2252 ULONG TestLoop(BPTR srcdir, BPTR destdir, struct CopyData *cd)
2254 ULONG loop = 0;
2255 BPTR par, lock;
2257 lock = destdir;
2259 if (SameDevice(srcdir, destdir))
2263 if (!SameLock(srcdir, lock))
2265 loop = 1;
2267 else
2269 par = ParentDir(lock);
2271 if (lock != destdir)
2273 UnLock(lock);
2276 lock = par;
2279 while(!loop && lock);
2282 if (lock != destdir)
2284 UnLock(lock);
2287 return loop;
2291 void SetData(STRPTR name, struct CopyData *cd)
2293 if (cd->Flags & COPYFLAG_NOPRO)
2295 /* Is already set! - Piru */
2296 //SetProtection(name, 0);
2298 else
2300 SetProtection(name, cd->Fib.fib_Protection & (ULONG) ~FIBF_ARCHIVE);
2303 if (cd->Flags & COPYFLAG_DATES)
2305 SetFileDate(name, &cd->Fib.fib_Date);
2308 if (cd->Flags & COPYFLAG_COMMENT)
2310 SetComment(name, cd->Fib.fib_Comment);
2315 LONG TestDest(STRPTR name, ULONG type, struct CopyData *cd)
2317 LONG ret = TESTDEST_ERROR;
2318 BPTR lock;
2320 if ((lock = Lock(name, SHARED_LOCK)))
2322 struct FileInfoBlock *fib;
2324 if ((fib = (struct FileInfoBlock *)AllocDosObject(DOS_FIB, NULL)))
2326 if (Examine(lock, fib))
2328 UnLock(lock);
2329 lock = 0;
2331 if (type)
2333 if (fib->fib_DirEntryType > 0)
2335 ret = TESTDEST_DIR_OK;
2337 else if (!(cd->Flags & COPYFLAG_DONTOVERWRITE))
2339 if (KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE, cd))
2341 ret = TESTDEST_DELETED;
2344 else
2346 ret = TESTDEST_CANTDELETE;
2349 else if (cd->Flags & COPYFLAG_DONTOVERWRITE)
2351 if (cd->Flags & COPYFLAG_NEWER)
2353 if( CheckVersion( cd ) == CHECKVER_DESTOLDER )
2355 if (KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE, cd))
2357 ret = TESTDEST_DELETED;
2360 else
2362 ret = TESTDEST_CANTDELETE;
2365 else /* normal "dont overwrite mode" */
2367 ret = TESTDEST_CANTDELETE;
2370 else if (KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE, cd))
2372 ret = TESTDEST_DELETED;
2376 FreeDosObject(DOS_FIB, fib);
2379 if (lock)
2381 UnLock(lock);
2384 else
2386 ret = TESTDEST_NONE;
2389 if (ret == TESTDEST_CANTDELETE)
2391 cd->IoErr = ERROR_OBJECT_EXISTS;
2393 else if (ret == TESTDEST_ERROR)
2395 cd->IoErr = IoErr();
2398 return ret;
2402 ** We compare current file versions and return the result
2403 ** see CHECKVER_#? values
2406 static LONG CheckVersion( struct CopyData *cd )
2408 struct VersionData vds;
2409 struct VersionData vdd;
2410 LONG resversion = CHECKVER_EQUAL;
2411 LONG resdate = CHECKVER_EQUAL;
2413 if( VersionFind( cd->FileName, &vds, cd ) )
2415 if( VersionFind( cd->DestName, &vdd, cd ) )
2417 /* version and revision must be available to ensure a proper operation */
2418 if( ((vdd.vd_Version != -1) && (vds.vd_Version != -1) && (vdd.vd_Revision != -1) && (vds.vd_Revision != -1)) )
2420 /* first we make the stuff comparable. If one component is missing we reset both */
2421 if( vdd.vd_Year == -1 || vds.vd_Year == -1 )
2423 vdd.vd_Year = 0;
2424 vds.vd_Year = 0;
2426 if( vdd.vd_Month == -1 || vds.vd_Month == -1 )
2428 vdd.vd_Month = 0;
2429 vds.vd_Month = 0;
2431 if( vdd.vd_Day == -1 || vds.vd_Day == -1 )
2433 vdd.vd_Day = 0;
2434 vds.vd_Day = 0;
2437 /* check version */
2438 resversion = CHECKVER_DESTOLDER;
2439 if( ((vdd.vd_Version == vds.vd_Version) && vdd.vd_Revision == vds.vd_Revision ) )
2441 resversion = CHECKVER_EQUAL;
2443 else if( (vdd.vd_Version > vds.vd_Version) ||
2444 ((vdd.vd_Version == vds.vd_Version) && vdd.vd_Revision > vds.vd_Revision ) )
2446 resversion = CHECKVER_DESTNEWER;
2448 /* check date */
2450 resdate = CHECKVER_DESTOLDER;
2451 if( ((vdd.vd_Year == vds.vd_Year) && (vdd.vd_Month == vds.vd_Month) && (vdd.vd_Day == vds.vd_Day) ) )
2453 resdate = CHECKVER_EQUAL;
2455 else
2457 if( ( (vdd.vd_Year > vds.vd_Year ) ||
2458 ( (vdd.vd_Year == vds.vd_Year) && (vdd.vd_Month > vds.vd_Month ) ) ||
2459 ( (vdd.vd_Year == vds.vd_Year) && (vdd.vd_Month == vds.vd_Month ) && (vdd.vd_Day > vds.vd_Day ) ) ))
2461 resdate = CHECKVER_DESTNEWER;
2465 /* plausible check */
2466 if( ((resversion == CHECKVER_DESTNEWER) && (resdate == CHECKVER_DESTOLDER)) || /* newer version with older date */
2467 ((resversion == CHECKVER_DESTOLDER) && (resdate == CHECKVER_DESTNEWER)) ) /* older version with newer date */
2469 /* we maybe should inform the user about this */
2470 return( CHECKVER_EQUAL );
2475 /* compose result */
2477 if( (resversion == resdate) || (resversion == CHECKVER_EQUAL) )
2479 return( resdate );
2481 else
2483 return( resversion );
2490 ** Searches the given file for a version string and fills version data struct with the result.
2491 ** Returns false if no version was found. Returns true if the version got parsed and version data
2492 ** is valid.
2495 #define VERSBUFFERSIZE 4096 /* must be as big as the biggest version string we want to handle. */
2497 static BOOL VersionFind( CONST_STRPTR path, struct VersionData *vds, struct CopyData *cd)
2499 BPTR handle;
2500 STRPTR buf;
2501 ULONG i, rc;
2503 rc = FALSE;
2505 if ( (buf = AllocVec(VERSBUFFERSIZE, MEMF_PUBLIC | MEMF_CLEAR)) )
2507 if ( (handle = Open(path, MODE_OLDFILE)) )
2509 long index = 0;
2511 while( ( (index += Read(handle, &buf[index], VERSBUFFERSIZE-index)) > 5) && !rc )
2513 for (i = 0; i < index-5; i++) {
2514 if( buf[i] == '$' && buf[i+1] == 'V' && buf[i+2] == 'E' && buf[i+3] == 'R' && buf[i+4] == ':' ) {
2515 CopyMem( &buf[i], buf, index-i );
2516 index -= i;
2517 (index += Read(handle, &buf[index], VERSBUFFERSIZE-index));
2518 /* now the version string is aligned and complete in buffer */
2519 makeversionfromstring( buf, vds, cd );
2520 rc = TRUE;
2521 break;
2524 CopyMem( &buf[index-5], &buf[0], 5 );
2525 index = 5;
2527 Close(handle);
2529 FreeVec(buf);
2531 return (rc);
2536 ** This function extracts the version information from a given version string.
2537 ** the result will be store in the given version data structure.
2539 ** NOTE: There is no need to preset the version data struct. All fields will
2540 ** be reset to defaults, so in case of a faulty version string the result data
2541 ** will be checkable.
2544 static
2545 void makeversionfromstring( STRPTR buffer, struct VersionData *vd, struct CopyData *cd)
2547 LONG pos;
2548 ULONG tmp;
2549 STRPTR name;
2551 /* reset data field */
2553 vd->vd_Name[0] = '\0';
2554 vd->vd_Version = -1;
2555 vd->vd_Revision = -1;
2556 vd->vd_Day = -1;
2557 vd->vd_Month = -1;
2558 vd->vd_Year = -1;
2560 buffer = skipspaces( buffer ); /* skip before $VER: */
2561 buffer = skipnonspaces( buffer ); /* skip $VER: */
2562 buffer = skipspaces( buffer ); /* skip spaces before tool name */
2563 name = buffer;
2564 buffer = skipnonspaces( buffer ); /* skip name of tool */
2566 if( (tmp = ((long) buffer - (long) name) ) && *buffer )
2568 CopyMem( name, vd->vd_Name, MIN( tmp, VDNAMESIZE-1) );
2569 vd->vd_Name[MIN( tmp, VDNAMESIZE-1)] = '\0'; /* terminate name string inside target buffer */
2571 buffer = skipspaces( buffer ); /* skip spaces before version */
2572 if( *buffer ) {
2574 /* Do version */
2576 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2578 vd->vd_Version = tmp;
2580 /* Do revision */
2582 buffer += pos;
2583 buffer = skipspaces(buffer);
2584 if (*buffer++ == '.')
2586 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2588 vd->vd_Revision = tmp;
2589 buffer += pos;
2590 buffer = skipspaces(buffer);
2591 if (*buffer++ == '(')
2593 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2595 vd->vd_Day = tmp;
2596 buffer += pos;
2597 if (*buffer++ == '.')
2599 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2601 vd->vd_Month = tmp;
2602 buffer += pos;
2603 if (*buffer++ == '.')
2605 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2607 if( (tmp >= 70) && (tmp <= 99) )
2609 tmp += 1900;
2611 if( (tmp < 70) )
2613 tmp += 2000;
2615 vd->vd_Year = tmp;
2629 /* Return a pointer to a string, stripped by all leading space characters
2630 * (SPACE).
2632 static
2633 STRPTR skipspaces( STRPTR buffer)
2635 for (;; buffer++)
2637 if (buffer[0] == '\0' || buffer[0] != ' ')
2639 return( buffer );
2644 /* Return a pointer to a string, stripped by all non space characters
2645 * (SPACE).
2647 static
2648 STRPTR skipnonspaces( STRPTR buffer)
2650 for (;; buffer++)
2652 if (buffer[0] == '\0' || buffer[0] == ' ')
2654 return( buffer );