- Overhauled error-code handling (IoErr() must be called immediately
[AROS.git] / workbench / c / Copy.c
blob884f409eab306f317e17a909b6060ccda1fc466a
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 __startup static AROS_ENTRY(int, Start,
551 AROS_UFHA(char *, argstr, A0),
552 AROS_UFHA(ULONG, argsize, D0),
553 struct ExecBase *, SysBase)
555 AROS_USERFUNC_INIT
557 struct DosLibrary *DOSBase;
558 struct Process *task;
559 struct CopyData *cd;
560 int retval = RETURN_FAIL;
562 /* test for WB and reply startup-message */
563 if (!(task = (struct Process *)FindTask(NULL))->pr_CLI)
565 WaitPort(&task->pr_MsgPort);
566 Forbid();
567 ReplyMsg(GetMsg(&task->pr_MsgPort));
569 return RETURN_FAIL;
572 DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 37);
573 cd = AllocMem(sizeof(*cd), MEMF_PUBLIC | MEMF_CLEAR);
575 if (DOSBase && cd)
577 STRPTR a[2] = { "", 0 };
578 struct RDArgs *rda;
579 struct IptrArgs iArgs;
580 struct Args args = {};
582 cd->SysBase = SysBase;
583 cd->DOSBase = DOSBase;
584 #define SysBase cd->SysBase
585 #define DOSBase cd->DOSBase
587 cd->BufferSize = 512*1024;
588 cd->Mode = COPYMODE_COPY;
589 cd->RetVal2 = RETURN_FAIL;
590 cd->Deep = 1;
592 memset(&iArgs, 0, sizeof(struct IptrArgs));
594 rda = (struct RDArgs *)AllocDosObject(DOS_RDARGS, NULL);
595 if (rda)
597 rda->RDA_ExtHelp =
598 "FROM multiple input files\n"
599 "TO destination file or directory\n"
600 "PATTERN a pattern the filenames must match\n"
601 "BUFFER buffersize for copy buffer (default 200 [100K])\n"
602 "ALL deep scan into sub directories\n"
603 "DIRECT copy/delete only: work without any tests or options\n"
604 "CLONE copy comment, protection bits and date as well\n"
605 "DATES copy dates\n"
606 "NOPRO do not copy protection bits\n"
607 "COMMENT copy filecomment\n"
608 "QUIET suppress all output and requesters\n"
609 #if !USE_ALWAYSVERBOSE
610 "VERBOSE give additional output\n"
611 #endif
612 "NOREQ suppress requesters\n"
613 "ERRWARN do not proceed, when one file failed\n"
614 "MAKEDIR produce directories\n"
615 "MOVE delete source files after copying successful\n"
616 "DELETE do not copy, but delete the source files\n"
617 "HARDLINK make a hardlink to source instead of copying\n"
618 "SOFTLINK make a softlink to source instead of copying\n"
619 "FOLNK also makes links to directories\n"
620 "FODEL delete protected files also\n"
621 "FOOVR also overwrite protected files\n"
622 "DONTOVR do never overwrite destination\n"
623 "FORCE DO NOT USE. Call compatibility only.\n"
624 "NEWER will compare version strings and only overwrites older files\n";
626 if (ReadArgs(PARAM, (IPTR *)&iArgs, rda))
628 ULONG patbufsize = 0;
629 LONG i = 0;
630 APTR win = task->pr_WindowPtr;
632 args.from = (STRPTR *)iArgs.from;
633 args.to = (STRPTR)iArgs.to;
634 args.pattern = (STRPTR)iArgs.pattern;
635 args.buffer = (LONG *)iArgs.buffer;
636 args.all = (LONG)iArgs.all;
637 args.direct = (LONG)iArgs.direct;
638 args.clone = (LONG)iArgs.clone;
639 args.dates = (LONG)iArgs.dates;
640 args.nopro = (LONG)iArgs.nopro;
641 args.comment = (LONG)iArgs.comment;
642 args.quiet = (LONG)iArgs.quiet;
643 #if USE_ALWAYSVERBOSE
644 args.verbose = FALSE;
645 #else
646 args.verbose = (LONG)iArgs.verbose;
647 #endif
648 args.noreq = (LONG)iArgs.noreq;
649 args.errwarn = (LONG)iArgs.errwarn;
650 args.makedir = (LONG)iArgs.makedir;
651 args.move_mode = (LONG)iArgs.move_mode;
652 args.delete_mode = (LONG)iArgs.delete_mode;
653 args.hardlink = (LONG)iArgs.hardlink;
654 args.softlink = (LONG)iArgs.softlink;
655 args.forcelink = (LONG)iArgs.forcelink;
656 args.forcedelete = (LONG)iArgs.forcedelete;
657 args.forceoverwrite = (LONG)iArgs.forceoverwrite;
658 args.dontoverwrite = (LONG)iArgs.dontoverwrite;
659 args.force = (LONG)iArgs.force;
660 args.newer = (LONG)iArgs.newer;
662 if (args.quiet) /* when QUIET, SILENT and NOREQ are also
663 true! */
665 /* Original doesn't hide requesters with QUIET */
666 /*args.noreq = 1;*/
667 args.verbose = FALSE;
670 if (args.buffer && *args.buffer > 0) /* minimum buffer size */
672 cd->BufferSize = *args.buffer * 512;
675 if (args.quiet)
677 cd->Flags |= COPYFLAG_QUIET;
680 #if !USE_ALWAYSVERBOSE
681 if (args.verbose)
683 cd->Flags |= COPYFLAG_VERBOSE;
685 #endif
686 if (args.all)
688 cd->Flags |= COPYFLAG_ALL;
691 if (args.clone)
693 cd->Flags |= COPYFLAG_DATES | COPYFLAG_COMMENT;
696 if (args.dates)
698 cd->Flags |= COPYFLAG_DATES;
701 if (args.comment)
703 cd->Flags |= COPYFLAG_COMMENT;
706 if (args.nopro)
708 cd->Flags |= COPYFLAG_NOPRO;
711 if (args.forcelink)
713 cd->Flags |= COPYFLAG_FORCELINK;
716 if (args.forcedelete)
718 cd->Flags |= COPYFLAG_FORCEDELETE;
721 if (args.forceoverwrite)
723 cd->Flags |= COPYFLAG_FORCEOVERWRITE;
726 if (args.dontoverwrite)
728 cd->Flags |= COPYFLAG_DONTOVERWRITE;
731 if (args.newer)
733 cd->Flags |= COPYFLAG_NEWER|COPYFLAG_DONTOVERWRITE;
736 if (args.errwarn)
738 cd->Flags |= COPYFLAG_ERRWARN;
741 if (args.force) /* support OS Delete and MakeLink command
742 options */
744 if (args.delete_mode)
746 cd->Flags |= COPYFLAG_FORCEDELETE;
749 if (args.hardlink || args.softlink)
751 cd->Flags |= COPYFLAG_FORCELINK;
755 if (!args.from) /* no args.from means currentdir */
757 args.from = a;
760 if (args.noreq) /* no dos.library requests allowed */
762 task->pr_WindowPtr = (APTR)-1;
765 if (args.delete_mode)
767 ++i;
768 cd->Mode = COPYMODE_DELETE;
771 if (args.move_mode)
773 ++i;
774 cd->Mode = COPYMODE_MOVE;
777 if (args.makedir)
779 ++i;
780 cd->Mode = COPYMODE_MAKEDIR;
783 if (args.hardlink)
785 ++i;
786 cd->Mode = COPYMODE_LINK;
789 if (args.softlink)
791 ++i;
792 cd->Mode = COPYMODE_LINK;
793 cd->Flags |= COPYFLAG_SOFTLINK | COPYFLAG_FORCELINK;
796 if (cd->Mode != COPYMODE_DELETE &&
797 cd->Mode != COPYMODE_MAKEDIR && !args.to)
799 if (*(args.from + 1)) /* when no TO is specified, the arg
800 is last */
801 { /* one of from. Copy this argument into */
802 STRPTR *a; /* args.to */
804 a = args.from;
806 while(*(++a))
809 args.to = *(--a);
810 *a = 0;
813 #if USE_ALWAYSVERBOSE
815 /* Only do this if quiet isn't set - bigfoot */
816 if (!args.quiet)
818 /* If more than two args, be verbose... - Piru */
819 if (args.from[0] && args.from[1])
821 args.verbose = TRUE;
822 cd->Flags |= COPYFLAG_VERBOSE;
825 /* If any of the sources is a pattern, be verbose... - Piru */
827 STRPTR *a;
829 for (a = args.from; *a; a++)
831 if (IsMatchPattern(*a, cd) != 0)
833 args.verbose = TRUE;
834 cd->Flags |= COPYFLAG_VERBOSE;
839 #endif
841 /* test if more than one of the above four or any other wrong
842 arguments */
844 if (i > 1 ||
845 (args.from == a && cd->Mode == COPYMODE_MAKEDIR) ||
846 (args.direct && (args.from == a || !*args.from ||
847 args.pattern ||
848 (cd->Flags & ~(COPYFLAG_QUIET | COPYFLAG_VERBOSE | COPYFLAG_ERRWARN)) ||
849 (cd->Mode != COPYMODE_DELETE && (cd->Mode != COPYMODE_COPY ||
850 !args.to || args.from[1])))) ||
851 (args.dontoverwrite && args.forceoverwrite) ||
852 /* (args.nopro && args.clone) ||*/ /* Ignore, like original - Piru */
853 (args.softlink && args.all) ||
854 (!args.to && cd->Mode != COPYMODE_DELETE && cd->Mode != COPYMODE_MAKEDIR))
856 cd->IoErr = ERROR_TOO_MANY_ARGS;
858 else if (cd->Mode == COPYMODE_MAKEDIR)
860 LONG i;
861 BPTR dir;
862 cd->RetVal2 = RETURN_OK;
864 #if !USE_ALWAYSVERBOSE
865 if (args.verbose)
867 PutStr(texts[TEXTNUM_MODE + COPYMODE_MAKEDIR]);
869 #endif
871 while (!cd->RetVal && !cd->RetVal2 && *args.from)
873 if ((i = IsPattern(*args.from, cd)))
875 if (i != -1)
877 cd->RetVal = RETURN_ERROR;
879 if (!args.quiet)
881 PutStr(TEXT_ERR_WILDCARD_DEST);
884 else
886 cd->RetVal2 = RETURN_FAIL;
890 if ((dir = OpenDestDir(*args.from, cd)))
892 UnLock(dir);
893 cd->Flags |= COPYFLAG_DONE;
896 ++args.from;
898 } /* cd->Mode == COPYMODE_MAKEDIR */
899 else if (args.direct)
901 if (cd->Mode == COPYMODE_COPY)
903 BPTR in, out;
905 if ((in = Open(*args.from, MODE_OLDFILE)))
907 if ((out = Open(args.to, MODE_NEWFILE)))
909 cd->RetVal2 = CopyFile(in, out, cd->BufferSize, cd);
910 if (cd->RetVal2 != 0)
911 cd->IoErr = IoErr();
912 Close(out);
914 else
915 cd->IoErr = IoErr();
917 Close(in);
919 else
920 cd->IoErr = IoErr();
922 else /* COPYMODE_DELETE */
924 while (*args.from)
926 KillFile(*(args.from++), cd->Flags & COPYFLAG_FORCEDELETE, cd);
929 cd->RetVal2 = RETURN_OK;
932 else
934 if (args.pattern && *args.pattern)
936 patbufsize = (strlen(args.pattern) << 1) + 3;
938 if ((cd->Pattern = (STRPTR)AllocMem(patbufsize,
939 MEMF_ANY)))
941 if (ParsePatternNoCase(args.pattern, cd->Pattern,
942 patbufsize) < 0)
944 FreeMem(cd->Pattern, patbufsize);
945 cd->Pattern = 0;
950 if (1) // (cd->Fib = (struct FileInfoBlock *)AllocDosObject(DOS_FIB, NULL)))
952 #if !USE_ALWAYSVERBOSE
953 if (args.verbose)
955 PutStr(texts[TEXTNUM_MODE + cd->Mode +
956 (cd->Flags & COPYFLAG_SOFTLINK ? 1 : 0)]);
958 #endif
959 if (args.pattern && !cd->Pattern)
961 if (!*args.pattern)
963 cd->IoErr = ERROR_BAD_TEMPLATE;
966 else if (cd->Mode == COPYMODE_DELETE)
968 cd->RetVal2 = RETURN_OK;
970 while (cd->RetVal <= (args.errwarn ? RETURN_OK : RETURN_WARN)
971 && *args.from)
973 PatCopy(*(args.from++), cd);
976 else if ((i = IsPattern(args.to, cd)))
978 if (i != -1)
980 cd->RetVal = RETURN_ERROR;
982 if (!args.quiet)
984 PutStr(TEXT_ERR_WILDCARD_DEST);
988 else
990 STRPTR path;
992 if (*(path = PathPart(args.to)) == '/')
994 ++path; /* is destination a path description ? */
997 if (*path && !*(args.from+1) &&
998 !(i = IsMatchPattern(*args.from, cd)))
1000 BPTR lock;
1002 /* is destination an existing directory */
1003 if ((lock = Lock(args.to, SHARED_LOCK)))
1005 if (Examine(lock, &cd->Fib))
1007 if (cd->Fib.fib_DirEntryType > 0)
1009 cd->RetVal2 = RETURN_OK;
1012 /* indicate dir-mode for next if */
1014 else
1016 i = 1;
1019 UnLock(lock);
1022 /* Some magic to handle tick quoted pattern object names. Quite crude way to
1023 * handle it, but I couldn't think of anything better. - Piru
1025 #if 1
1026 if (!i && cd->RetVal2 && !IsMatchPattern(*args.from, cd))
1028 ULONG len;
1029 STRPTR pat;
1031 //Printf("pattern check <%s>\n", *args.from);
1033 len = (strlen(*args.from) << 1) + 3;
1035 if ((pat = (STRPTR)AllocMem(len,
1036 MEMF_ANY)))
1038 if (ParsePattern(*args.from, pat, len) > -1 &&
1039 strlen(pat) <= strlen(*args.from))
1041 lock = Lock(pat, SHARED_LOCK);
1042 if (lock)
1044 UnLock(lock);
1046 strcpy(*args.from, pat);
1050 FreeMem(pat, len);
1053 #endif
1055 /* is source a directory */
1056 if (!i && cd->RetVal2 &&
1057 (lock = Lock(*args.from, SHARED_LOCK)))
1059 if (Examine(lock, &cd->Fib))
1061 cd->RetVal2 = RETURN_OK;
1062 if (cd->Mode != COPYMODE_COPY ||
1063 cd->Fib.fib_DirEntryType < 0)
1065 UBYTE sep;
1067 cd->Flags |= COPYFLAG_DEST_FILE;
1069 /* produce missing destination directories */
1070 sep = *path;
1071 *path = 0;
1073 if ((cd->CurDest = OpenDestDir(args.to, cd)))
1075 *path = sep;
1077 /* do the job */
1078 UnLock(lock);
1079 lock = 0;
1080 CopyMem(*args.from, cd->FileName,
1081 1 + strlen(*args.from));
1082 DoWork(FilePart(args.to), cd); /* on file call */
1083 UnLock(cd->CurDest);
1088 if (lock)
1090 UnLock(lock);
1094 if (lock == 0 && cd->Mode == COPYMODE_COPY && !TestFileSys(*args.from, cd))
1096 UBYTE sep;
1097 cd->Flags |= COPYFLAG_DEST_FILE | COPYFLAG_SRCNOFILESYS;
1098 cd->RetVal2 = RETURN_OK;
1100 /* produce missing destination directories */
1101 sep = *path;
1102 *path = 0;
1104 if ((cd->CurDest = OpenDestDir(args.to, cd)))
1106 *path = sep;
1108 /* do the job */
1109 CopyMem(*args.from, cd->FileName, 1 + strlen(*args.from));
1110 DoWork(FilePart(args.to), cd); /* on file call */
1111 UnLock(cd->CurDest);
1115 else if (i != -1)
1117 cd->RetVal2 = RETURN_OK;
1120 if (!cd->RetVal && !cd->RetVal2 && !(cd->Flags & COPYFLAG_DEST_FILE) &&
1121 (cd->Destination = OpenDestDir(args.to, cd)))
1123 while (cd->RetVal <= (args.errwarn ? RETURN_OK : RETURN_WARN)
1124 && *args.from && !CTRL_C)
1126 PatCopy(*(args.from++), cd);
1129 UnLock(cd->Destination);
1131 } /* else */
1133 if (!(cd->Flags & COPYFLAG_DONE) && args.verbose &&
1134 !cd->RetVal && !cd->RetVal2)
1136 PutStr(TEXT_NOTHING_DONE);
1139 } /* if (1) */
1141 if (cd->Pattern)
1143 FreeMem(cd->Pattern, patbufsize);
1145 } /* else */
1147 task->pr_WindowPtr = win;
1149 FreeArgs(rda);
1150 } /* ReadArgs */
1152 FreeDosObject(DOS_RDARGS, rda);
1153 } /* AllocDosObject */
1155 if (!cd->RetVal2 && CTRL_C)
1157 SetIoErr(ERROR_BREAK);
1158 cd->RetVal2 = RETURN_WARN;
1161 if (cd->RetVal)
1163 cd->RetVal2 = cd->RetVal;
1166 if (args.errwarn && cd->RetVal2 == RETURN_WARN)
1168 cd->RetVal2 = RETURN_ERROR;
1171 if (cd->CopyBuf)
1173 FreeMem(cd->CopyBuf, cd->CopyBufLen);
1176 #undef SysBase
1177 #undef DOSBase
1179 if (cd)
1181 retval = cd->RetVal2;
1182 SetIoErr(cd->IoErr);
1183 FreeMem(cd, sizeof(*cd));
1185 else if (DOSBase)
1187 PrintFault(IoErr(), NULL);
1189 if (DOSBase)
1191 CloseLibrary((struct Library *)DOSBase);
1193 return retval;
1195 AROS_USERFUNC_EXIT
1198 /* This code is pure and has library bases in explicitly allocated data structure */
1199 #define SysBase cd->SysBase
1200 #define DOSBase cd->DOSBase
1202 void PatCopy(STRPTR name, struct CopyData *cd)
1204 struct AnchorPath *APath;
1205 ULONG retval, doit = 0, deep = 0, failval = RETURN_WARN, first = 0;
1207 #if DEBUG
1208 Printf("PatCopy(%s, .)\n", name);
1209 #endif
1211 if ((cd->Mode == COPYMODE_COPY || (cd->Flags & COPYFLAG_ALL)) && !IsMatchPattern(name, cd))
1213 first = 1; /* enter first directory (support of old copy style) */
1216 if (cd->Flags & COPYFLAG_ERRWARN)
1218 failval = RETURN_OK;
1221 cd->CurDest = cd->Destination;
1222 cd->DestPathSize = 0;
1224 if (cd->Mode == COPYMODE_COPY && !TestFileSys(name, cd))
1226 cd->Flags |= COPYFLAG_SRCNOFILESYS;
1227 CopyMem(name, cd->FileName, 1 + strlen(name));
1228 DoWork(FilePart(name), cd);
1229 cd->Flags &= ~COPYFLAG_SRCNOFILESYS;
1231 return;
1234 if ((APath = (struct AnchorPath *)AllocMem(sizeof(struct AnchorPath) + FILEPATH_SIZE,
1235 MEMF_PUBLIC | MEMF_CLEAR)))
1237 int parentdirerr = 0;
1239 APath->ap_BreakBits = SIGBREAKF_CTRL_C;
1240 APath->ap_Strlen = FILEPATH_SIZE;
1242 for (retval = MatchFirst(name, APath);
1243 !retval && cd->RetVal <= failval && !cd->RetVal2;
1244 retval = MatchNext(APath)
1247 if (parentdirerr)
1249 //Printf("ParentDir() fuxored last round! Would copy next files to SYS: !\n");
1250 retval = IoErr();
1251 if (!retval)
1252 cd->IoErr = retval = ERROR_INVALID_LOCK;
1253 break;
1256 if (doit)
1258 DoWork(cd->Fib.fib_FileName, cd);
1259 doit = 0;
1262 if (deep) /* used for Deep checking */
1264 ++cd->Deep;
1265 deep = 0;
1268 cd->Flags &= ~COPYFLAG_ENTERSECOND;
1270 CopyMem(APath->ap_Buf, cd->FileName, FILEPATH_SIZE);
1271 CopyMem(&APath->ap_Info, &cd->Fib, sizeof(struct FileInfoBlock));
1273 if (first && APath->ap_Info.fib_DirEntryType > 0)
1275 #if USE_ALWAYSVERBOSE
1276 /* If the source is a directory, be verbose - Piru */
1277 cd->Flags |= COPYFLAG_VERBOSE;
1278 #endif
1279 APath->ap_Flags |= APF_DODIR;
1281 else if (APath->ap_Flags & APF_DIDDIR)
1283 BPTR i;
1285 cd->Flags |= COPYFLAG_ENTERSECOND;
1286 APath->ap_Flags &= ~APF_DIDDIR;
1287 --cd->Deep;
1289 if (cd->Mode == COPYMODE_DELETE || cd->Mode == COPYMODE_MOVE)
1291 doit = 1;
1294 if ((i = cd->CurDest))
1296 cd->CurDest = ParentDir(i);
1297 cd->DestPathSize = 0;
1299 if (i != cd->Destination)
1301 UnLock(i);
1304 if (!cd->CurDest)
1306 parentdirerr = 1;
1307 continue;
1311 else if (APath->ap_Info.fib_DirEntryType > 0)
1313 doit = 1;
1315 if (cd->Flags & COPYFLAG_ALL)
1317 BOOL enter;
1319 #if USE_SOFTLINKCHECK
1321 #define BUFFERSIZE 512
1322 if (APath->ap_Info.fib_DirEntryType == ST_SOFTLINK)
1324 UBYTE *buffer = AllocMem(BUFFERSIZE, MEMF_PUBLIC);
1326 D(Printf("%s is a softlink\n", APath->ap_Info.fib_FileName));
1327 enter = FALSE;
1328 doit = FALSE;
1330 if (buffer)
1332 struct DevProc *dvp = GetDeviceProc("", NULL);
1334 if (ReadLink(dvp->dvp_Port, APath->ap_Current->an_Lock, APath->ap_Info.fib_FileName, buffer, BUFFERSIZE - 1) > 0)
1336 BOOL link_ok = FALSE;
1337 BPTR dir, lock;
1339 buffer[BUFFERSIZE - 1] = '\0';
1340 D(Printf("Softlink target: %s\n", buffer));
1342 dir = CurrentDir(APath->ap_Current->an_Lock);
1343 lock = Lock(buffer, SHARED_LOCK);
1344 if (lock)
1346 struct FileInfoBlock *fib = AllocDosObject(DOS_FIB, NULL);
1348 if (fib)
1350 if (Examine(lock, fib))
1352 link_ok = TRUE;
1353 D(Printf("Target type: %ld\n", fib->fib_DirEntryType));
1355 if (fib->fib_DirEntryType > 0)
1356 enter = TRUE;
1357 else
1359 * FIXME: This currently just prevents treating symlinks to files as
1360 * directories during copying.
1361 * DoWork() should be extended to handle symlinks correctly. BTW, how exactly ?
1363 doit = FALSE;
1365 FreeDosObject(DOS_FIB, fib);
1367 UnLock(lock);
1370 CurrentDir(dir);
1372 if (!link_ok)
1374 Printf("Warning: Skipping dangling softlink %s -> %s\n",
1375 APath->ap_Info.fib_FileName, buffer);
1378 FreeDeviceProc(dvp);
1379 FreeMem(buffer, BUFFERSIZE);
1382 else
1383 #endif /* USE_SOFTLINKCHECK */
1384 enter = TRUE;
1386 if (enter)
1388 APath->ap_Flags |= APF_DODIR;
1389 deep = 1;
1393 else if (!cd->Pattern || MatchPatternNoCase(cd->Pattern, APath->ap_Info.fib_FileName))
1395 doit = 1;
1398 first = 0;
1401 MatchEnd(APath);
1403 if (retval && retval != ERROR_NO_MORE_ENTRIES)
1405 LONG ioerr = IoErr();
1406 #if USE_ALWAYSVERBOSE
1407 cd->Flags |= COPYFLAG_VERBOSE;
1408 #endif
1409 Printf("%s - ", APath->ap_Info.fib_FileName);
1410 PrintFault(ioerr, NULL);
1411 cd->IoErr = ioerr;
1413 cd->RetVal2 = RETURN_FAIL;
1416 if (doit)
1418 DoWork(cd->Fib.fib_FileName, cd);
1421 /* No need to clear the flags here, as they are cleared on next PatJoin
1422 call (DoWork is not called first round, as lock is zero!). */
1424 FreeMem(APath, sizeof(struct AnchorPath) + FILEPATH_SIZE);
1426 else
1428 cd->RetVal = RETURN_FAIL;
1430 if (!(cd->Flags & COPYFLAG_QUIET))
1432 PrintFault(ERROR_NO_FREE_STORE, NULL);
1436 if (cd->CurDest && cd->CurDest != cd->Destination)
1438 UnLock(cd->CurDest);
1443 LONG IsPattern(STRPTR name, struct CopyData *cd)
1445 LONG a, ret = -1;
1446 STRPTR buffer;
1448 a = (strlen(name) << 1) + 3;
1450 if ((buffer = (STRPTR)AllocMem(a, MEMF_ANY)))
1452 ret = ParsePattern(name, buffer, a);
1453 FreeMem(buffer, a);
1456 if (ret == -1)
1458 cd->IoErr = ERROR_NO_FREE_STORE;
1461 return ret;
1465 LONG IsMatchPattern(STRPTR name, struct CopyData *cd)
1467 struct AnchorPath ap;
1468 LONG ret = -1;
1470 ap.ap_BreakBits = 0;
1471 ap.ap_Flags = APF_DOWILD;
1472 ap.ap_Strlen = 0;
1474 if (MatchFirst(name, &ap) == 0)
1476 ret = (ap.ap_Flags & APF_ITSWILD) ? TRUE : FALSE;
1478 MatchEnd(&ap);
1481 return ret;
1485 LONG KillFile(STRPTR name, ULONG doit, struct CopyData *cd)
1487 if (doit)
1489 SetProtection(name, 0);
1492 return DeleteFile(name);
1496 BPTR OpenDestDir(STRPTR name, struct CopyData *cd)
1498 LONG a, err = 0, cr = 0;
1499 BPTR dir;
1500 STRPTR ptr = name;
1501 UBYTE as;
1503 if ((cd->Mode == COPYMODE_COPY || cd->Mode == COPYMODE_MOVE) && !TestFileSys(name, cd))
1505 cd->Flags |= COPYFLAG_DESNOFILESYS;
1506 CopyMem(name, cd->DestName, 1 + strlen(name));
1508 return Lock("", SHARED_LOCK);
1511 while (!err && *ptr != 0)
1513 while (*ptr && *ptr != '/')
1515 ++ptr;
1518 as = *ptr;
1519 *ptr = 0;
1521 if ((a = TestDest(name, 1, cd)) == TESTDEST_CANTDELETE)
1523 if (!(cd->Flags & COPYFLAG_QUIET))
1525 PutStr(TEXT_ERR_DEST_DIR);
1528 err = 2;
1530 else if (a < 0)
1532 err = 1;
1534 else if (a != TESTDEST_DIR_OK)
1536 if ((dir = CreateDir(name)))
1538 ++cr;
1540 if ((cd->Flags & COPYFLAG_VERBOSE))
1542 PrintName(name, 1, 1, 1, cd);
1543 Printf("%s\n", TEXT_CREATED);
1546 UnLock(dir);
1548 else
1550 cd->IoErr = IoErr();
1551 if (!(cd->Flags & COPYFLAG_QUIET))
1553 PrintNotDone(name, TEXT_CREATED, 1, 1, cd);
1556 err = 2;
1560 *ptr = as;
1562 /* 26-Oct-2003 bugfix: Don't scan past end of the string.
1563 * as is the old char, if '\0' we've reached the end of the
1564 * string. - Piru
1566 if (as)
1568 ptr++;
1572 if (err)
1574 cd->RetVal = RETURN_ERROR;
1576 if (!(cd->Flags & COPYFLAG_QUIET) && err == 1)
1578 PrintNotDone(name, TEXT_OPENED_FOR_OUTPUT, 1, 1, cd);
1581 return 0;
1584 if (cd->Mode == COPYMODE_MAKEDIR && !cr && !(cd->Flags & COPYFLAG_QUIET))
1586 cd->IoErr = ERROR_OBJECT_EXISTS;
1587 PrintNotDone(name, TEXT_CREATED, 1, 1, cd);
1590 return Lock(name, SHARED_LOCK);
1594 void PrintName(CONST_STRPTR name, ULONG deep, ULONG dir, ULONG txt, struct CopyData *cd)
1596 #if 0
1597 deep %= PRINTOUT_SPACES; /* reduce number of spaces */
1598 /* This produces an error with MaxonC++ */
1599 #endif
1601 PutStr(" ");
1602 if (deep)
1604 while (--deep)
1606 PutStr(" ");
1610 if (dir)
1612 PutStr(" ");
1615 #if 0
1616 if ((deep = strlen(name)) > PRINTOUT_SIZE) /* reduce name size */
1618 name += deep-PRINTOUT_SIZE;
1619 PutStr("...");
1621 #endif
1623 Printf((dir ? TEXT_DIRECTORY : (STRPTR) "%s"), name);
1625 if (!dir && txt)
1627 PutStr("..");
1630 Flush(Output());
1634 void PrintNotDone(CONST_STRPTR name, CONST_STRPTR txt, ULONG deep, ULONG dir, struct CopyData *cd)
1636 #if !USE_ALWAYSVERBOSE
1637 if (name)
1639 PrintName(name, deep, dir, 1, cd);
1641 #endif
1643 Printf(TEXT_NOT_DONE, txt);
1644 if (cd->IoErr != 0)
1646 PutStr(" - ");
1647 PrintFault(cd->IoErr, NULL);
1649 else
1650 PutStr("\n");
1654 /* returns value, when it seems to be a filesystem */
1655 ULONG TestFileSys(STRPTR name, struct CopyData *cd)
1657 STRPTR n = name;
1658 ULONG ret = 1;
1660 while (*n && *n != ':')
1662 ++n;
1665 if (*(n++) == ':')
1667 UBYTE a;
1669 a = *n;
1670 *n = 0;
1671 ret = IsFileSystem(name);
1672 *n = a;
1675 return ret;
1679 void DoWork(STRPTR name, struct CopyData *cd)
1681 BPTR pdir, lock = 0;
1682 CONST_STRPTR printerr = NULL, printok = "";
1684 #if DEBUG
1685 Printf("DoWork(%s, .)\n", name);
1686 #endif
1688 if (cd->RetVal > (cd->Flags & COPYFLAG_ERRWARN ? RETURN_OK : RETURN_WARN) || cd->RetVal2)
1690 #if DEBUG
1691 Printf("DoWork(RetVal %ld)\n", cd->RetVal);
1692 #endif
1693 return;
1696 if (cd->Mode != COPYMODE_DELETE && !(cd->Flags & COPYFLAG_DESNOFILESYS))
1698 if (!cd->DestPathSize)
1700 if (!NameFromLock(cd->CurDest, cd->DestName, FILEPATH_SIZE))
1702 cd->RetVal2 = RETURN_FAIL;
1704 #if DEBUG
1705 Printf("DoWork(NameFromLock RetVal %ld)\n", cd->RetVal);
1706 #endif
1708 return;
1711 cd->DestPathSize = strlen(cd->DestName);
1714 cd->DestName[cd->DestPathSize] = 0;
1715 AddPart(cd->DestName, name, FILEPATH_SIZE);
1718 if (cd->Flags & (COPYFLAG_SRCNOFILESYS|COPYFLAG_DESNOFILESYS))
1720 ULONG res = 0;
1721 BPTR in, out;
1722 CONST_STRPTR txt = TEXT_OPENED_FOR_OUTPUT;
1724 #if DEBUG
1725 Printf("Partly DIRECT mode active now (%s - %s)\n", cd->FileName,
1726 cd->DestName);
1727 #endif
1729 if ((in = Open(cd->FileName, MODE_OLDFILE)))
1731 txt = cd->Mode == COPYMODE_MOVE ? TEXT_MOVED : TEXT_COPIED;
1733 if ((out = Open(cd->DestName, MODE_NEWFILE)))
1735 ULONG h;
1737 h = CopyFile(in, out, cd->BufferSize, cd);
1738 if (h != 0)
1739 cd->IoErr = IoErr();
1740 Close(out);
1741 Close(in);
1742 in = BNULL;
1744 if (!h)
1746 if (cd->Mode == COPYMODE_MOVE)
1748 if (KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE, cd))
1750 res = 1;
1753 else
1755 res = 1;
1757 } else {
1758 KillFile(cd->DestName, 0, cd);
1761 else
1762 cd->IoErr = IoErr();
1764 if (in != BNULL)
1765 Close(in);
1767 else
1768 cd->IoErr = IoErr();
1770 if (!res && !(cd->Flags & COPYFLAG_QUIET))
1772 PrintNotDone(name, txt, cd->Deep, cd->Fib.fib_DirEntryType > 0, cd);
1774 else
1776 cd->Flags |= COPYFLAG_DONE;
1778 if ((cd->Flags & COPYFLAG_VERBOSE))
1780 Printf("%s\n", txt);
1784 #if DEBUG
1785 PutStr("DoWork(done)\n");
1786 #endif
1787 return;
1790 if (!(lock = Lock(cd->FileName, SHARED_LOCK)))
1792 cd->RetVal = RETURN_WARN;
1793 cd->IoErr = IoErr();
1795 if (!(cd->Flags & COPYFLAG_QUIET))
1797 PrintNotDone(cd->Fib.fib_FileName, TEXT_READ, cd->Deep,
1798 cd->Fib.fib_DirEntryType > 0, cd);
1801 #if DEBUG
1802 Printf("DoWork(Lock RetVal %ld)\n", cd->RetVal);
1803 #endif
1804 return;
1807 if (!(pdir = ParentDir(lock)))
1809 cd->RetVal = RETURN_ERROR;
1811 if (cd->Mode == COPYMODE_DELETE)
1813 if (!(cd->Flags & COPYFLAG_QUIET))
1815 Printf(" %s ", cd->FileName);
1816 Printf(TEXT_NOT_DONE, TEXT_DELETED);
1817 Printf("%s\n", TEXT_ERR_DELETE_DEVICE);
1821 UnLock(lock);
1823 #if DEBUG
1824 Printf("DoWork(ParentDir %ld)\n", cd->RetVal);
1825 #endif
1826 return;
1829 UnLock(pdir);
1831 if (!(cd->Flags & COPYFLAG_QUIET))
1833 if ((cd->Flags & COPYFLAG_VERBOSE))
1835 PrintName(name, cd->Deep, cd->Fib.fib_DirEntryType > 0, cd->Fib.fib_DirEntryType < 0 ||
1836 (cd->Flags & COPYFLAG_ALL ? cd->Mode != COPYMODE_DELETE : cd->Mode != COPYMODE_COPY) ||
1837 cd->Flags & COPYFLAG_ENTERSECOND, cd);
1841 if ((cd->Flags & COPYFLAG_ENTERSECOND) || (cd->Mode == COPYMODE_DELETE &&
1842 (!(cd->Flags & COPYFLAG_ALL) || cd->Fib.fib_DirEntryType < 0)))
1844 UnLock(lock);
1845 lock = 0;
1847 if (KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE, cd))
1849 printok = TEXT_DELETED;
1851 else
1853 cd->RetVal = RETURN_WARN;
1854 printerr = TEXT_DELETED;
1857 else if (cd->Mode == COPYMODE_DELETE)
1861 else if (cd->Fib.fib_DirEntryType > 0)
1863 LONG a;
1865 if ((cd->Flags & COPYFLAG_ALL || cd->Mode == COPYMODE_LINK ||
1866 cd->Mode == COPYMODE_MOVE) && TestLoop(lock, cd->CurDest, cd))
1868 printok = 0;
1869 cd->RetVal = RETURN_ERROR;
1871 if (!(cd->Flags & COPYFLAG_QUIET))
1873 if (!(cd->Flags & COPYFLAG_VERBOSE))
1875 PrintName(name, cd->Deep, 1, 1, cd);
1878 Printf(TEXT_NOT_DONE, TEXT_ENTERED);
1879 PutStr(TEXT_ERR_INFINITE_LOOP);
1882 else if ((a = TestDest(cd->DestName, 1, cd)) < 0)
1884 printerr = TEXT_CREATED;
1885 cd->RetVal = RETURN_ERROR;
1887 else if (cd->Flags & COPYFLAG_ALL)
1889 BPTR i;
1891 i = cd->CurDest;
1892 cd->DestPathSize = 0;
1894 if (a == TESTDEST_DIR_OK)
1896 if (!(cd->CurDest = Lock(cd->DestName, SHARED_LOCK)))
1898 cd->IoErr = IoErr();
1899 printerr = TEXT_ENTERED;
1900 cd->RetVal = RETURN_ERROR;
1902 else
1904 #if USE_ALWAYSVERBOSE
1905 printok = "";
1906 #else
1907 printok = TEXT_ENTERED;
1908 #endif
1911 else if ((cd->CurDest = CreateDir(cd->DestName)))
1913 UnLock(cd->CurDest);
1915 if ((cd->CurDest = Lock(cd->DestName, SHARED_LOCK)))
1917 printok = TEXT_CREATED;
1919 else
1921 cd->IoErr = IoErr();
1922 printerr = TEXT_ENTERED;
1923 cd->RetVal = RETURN_ERROR;
1926 else
1928 cd->IoErr = IoErr();
1929 printerr = TEXT_CREATED;
1930 cd->RetVal = RETURN_ERROR;
1933 if (!cd->CurDest)
1935 cd->CurDest = i;
1937 else if (i != cd->Destination)
1939 UnLock(i);
1942 else if (cd->Mode == COPYMODE_MOVE)
1944 if (Rename(cd->FileName, cd->DestName))
1946 printok = TEXT_RENAMED;
1948 else
1950 cd->IoErr = IoErr();
1951 printerr = TEXT_RENAMED;
1952 cd->RetVal = RETURN_WARN;
1955 else if (cd->Mode == COPYMODE_LINK)
1957 if (!(cd->Flags & COPYFLAG_FORCELINK))
1959 printok = 0;
1960 cd->RetVal = RETURN_WARN;
1962 if (!(cd->Flags & COPYFLAG_QUIET))
1964 if (!(cd->Flags & COPYFLAG_VERBOSE))
1966 PrintName(name, cd->Deep, 1, 1, cd);
1969 Printf(TEXT_NOT_DONE, TEXT_LINKED);
1970 PutStr(TEXT_ERR_FORCELINK);
1973 else if (LinkFile(lock, cd->DestName, cd->Flags & COPYFLAG_SOFTLINK, cd))
1975 printok = TEXT_LINKED;
1977 else
1979 printerr = TEXT_LINKED;
1980 cd->RetVal = RETURN_WARN;
1983 else /* COPY mode only displays directories, when not ALL */
1985 printok = 0;
1987 if (!(cd->Flags & COPYFLAG_QUIET))
1989 if (cd->Flags & COPYFLAG_VERBOSE)
1991 PutStr("\n");
1996 else
1998 /* test for existing destination file */
1999 if (TestDest(cd->DestName, 0, cd) < 0)
2001 printerr = TEXT_OPENED_FOR_OUTPUT;
2003 else if (cd->Mode == COPYMODE_MOVE && Rename(cd->FileName, cd->DestName))
2005 printok = TEXT_RENAMED;
2007 else if (cd->Mode == COPYMODE_LINK)
2009 if (!(cd->Flags & COPYFLAG_SOFTLINK) && LinkFile(lock, cd->DestName, 0, cd))
2011 printok = TEXT_LINKED;
2013 else
2015 printerr = TEXT_LINKED;
2016 cd->RetVal = RETURN_WARN;
2018 if (cd->Flags & COPYFLAG_SOFTLINK)
2020 cd->IoErr = ERROR_OBJECT_WRONG_TYPE;
2024 else
2026 ULONG res = 0, h;
2027 BPTR in, out;
2028 CONST_STRPTR txt = TEXT_OPENED_FOR_OUTPUT;
2030 if ((out = Open(cd->DestName, MODE_NEWFILE)))
2032 ULONG kill = 1;
2034 txt = cd->Mode == COPYMODE_MOVE ? TEXT_MOVED : TEXT_COPIED;
2035 UnLock(lock);
2036 lock = 0;
2038 if ((in = Open(cd->FileName, MODE_OLDFILE)))
2040 h = CopyFile(in, out, cd->BufferSize, cd);
2041 if (h != 0)
2042 cd->IoErr = IoErr();
2043 Close(out);
2044 out = BNULL;
2045 Close(in);
2047 if (!h)
2049 kill = 0;
2051 if (cd->Mode == COPYMODE_MOVE)
2053 if (KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE, cd))
2055 res = 1;
2058 else
2060 res = 1;
2064 else cd->IoErr = IoErr();
2066 if (out)
2068 Close(out);
2071 if (kill)
2073 KillFile(cd->DestName, 0, cd);
2076 else cd->IoErr = IoErr();
2078 if (!res)
2080 printerr = txt;
2081 cd->RetVal = RETURN_WARN;
2083 else
2085 printok = txt;
2090 if (printerr && !(cd->Flags & COPYFLAG_QUIET))
2092 PrintNotDone(name, printerr, cd->Deep, cd->Fib.fib_DirEntryType > 0, cd);
2094 else if (printok)
2096 cd->Flags |= COPYFLAG_DONE;
2098 if (!(cd->Flags & COPYFLAG_QUIET))
2100 if ((cd->Flags & COPYFLAG_VERBOSE))
2102 Printf("%s\n", printok);
2106 SetData(cd->DestName, cd);
2109 if (lock)
2111 UnLock(lock);
2116 LONG CopyFile(BPTR from, BPTR to, ULONG bufsize, struct CopyData *cd)
2118 STRPTR buffer;
2119 LONG s, err = 0;
2121 if (cd->CopyBuf)
2123 buffer = cd->CopyBuf;
2124 bufsize = cd->CopyBufLen;
2126 else
2130 buffer = (STRPTR)AllocMem(bufsize, MEMF_PUBLIC);
2131 if (buffer)
2133 cd->CopyBuf = buffer;
2134 cd->CopyBufLen = bufsize;
2135 break;
2138 bufsize >>= 1;
2140 } while (bufsize >= 512);
2143 if (buffer)
2145 #if USE_BOGUSEOFWORKAROUND
2146 struct FileInfoBlock *fib = (struct FileInfoBlock *) buffer; /* NOTE: bufsize is min 512 bytes */
2148 if (ExamineFH(from, fib))
2150 #warning "****** WARNING: No largefile support! ******"
2151 ULONG filesize = fib->fib_Size, copied = 0;
2153 /*Printf("filesize: %lu\n", filesize);*/
2157 ULONG brk = CTRL_C;
2158 if (brk || (s = Read(from, buffer, bufsize)) == -1 || Write(to, buffer, s) != s)
2160 if (brk)
2162 cd->IoErr = ERROR_BREAK;
2164 err = RETURN_FAIL;
2165 break;
2167 else if (s == 0 && copied < filesize)
2169 /* premature EOF with buggy fs */
2170 err = RETURN_FAIL;
2171 break;
2174 copied += s;
2176 } while (copied < filesize);
2178 /*{ LONG ioerr = IoErr();
2179 Printf("copied %lu/%lu\n", copied, filesize);
2180 SetIoErr(ioerr);}*/
2182 else
2183 #endif /* USE_BOGUSEOFWORKAROUND */
2185 /* Stream or so, copy until EOF or error */
2188 ULONG brk = CTRL_C;
2189 /* AROS: This flush appears to be required if reading from '*'
2190 * Maybe a bug in Read(), or AROS buffering?
2192 Flush(from);
2193 if (brk || (s = Read(from, buffer, bufsize)) == -1 || Write(to, buffer, s) != s)
2195 if (brk)
2197 cd->IoErr = ERROR_BREAK;
2199 err = RETURN_FAIL;
2200 break;
2202 } while (s > 0);
2205 /* Freed at exit to avoid fragmentation */
2206 /*FreeMem(buffer, bufsize);*/
2208 else
2210 err = RETURN_FAIL;
2213 return err;
2217 /* Softlink's path starts always with device name! f.e. "Ram Disk:T/..." */
2218 LONG LinkFile(BPTR from, STRPTR to, ULONG soft, struct CopyData *cd)
2220 if (soft)
2222 LONG ret = FALSE;
2223 UBYTE *name;
2225 name = AllocMem(FILEPATH_SIZE, MEMF_ANY);
2226 if (name)
2228 if (NameFromLock(from, name, FILEPATH_SIZE))
2230 ret = MakeLink(to, name, LINK_SOFT);
2233 FreeMem(name, FILEPATH_SIZE);
2236 return ret;
2238 else
2240 return MakeLink(to, (void *)from, LINK_HARD);
2245 /* return 0 means no loop, return != 0 means loop found */
2246 ULONG TestLoop(BPTR srcdir, BPTR destdir, struct CopyData *cd)
2248 ULONG loop = 0;
2249 BPTR par, lock;
2251 lock = destdir;
2253 if (SameDevice(srcdir, destdir))
2257 if (!SameLock(srcdir, lock))
2259 loop = 1;
2261 else
2263 par = ParentDir(lock);
2265 if (lock != destdir)
2267 UnLock(lock);
2270 lock = par;
2273 while(!loop && lock);
2276 if (lock != destdir)
2278 UnLock(lock);
2281 return loop;
2285 void SetData(STRPTR name, struct CopyData *cd)
2287 if (cd->Flags & COPYFLAG_NOPRO)
2289 /* Is already set! - Piru */
2290 //SetProtection(name, 0);
2292 else
2294 SetProtection(name, cd->Fib.fib_Protection & (ULONG) ~FIBF_ARCHIVE);
2297 if (cd->Flags & COPYFLAG_DATES)
2299 SetFileDate(name, &cd->Fib.fib_Date);
2302 if (cd->Flags & COPYFLAG_COMMENT)
2304 SetComment(name, cd->Fib.fib_Comment);
2309 LONG TestDest(STRPTR name, ULONG type, struct CopyData *cd)
2311 LONG ret = TESTDEST_ERROR;
2312 BPTR lock;
2314 if ((lock = Lock(name, SHARED_LOCK)))
2316 struct FileInfoBlock *fib;
2318 if ((fib = (struct FileInfoBlock *)AllocDosObject(DOS_FIB, NULL)))
2320 if (Examine(lock, fib))
2322 UnLock(lock);
2323 lock = 0;
2325 if (type)
2327 if (fib->fib_DirEntryType > 0)
2329 ret = TESTDEST_DIR_OK;
2331 else if (!(cd->Flags & COPYFLAG_DONTOVERWRITE))
2333 if (KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE, cd))
2335 ret = TESTDEST_DELETED;
2338 else
2340 ret = TESTDEST_CANTDELETE;
2343 else if (cd->Flags & COPYFLAG_DONTOVERWRITE)
2345 if (cd->Flags & COPYFLAG_NEWER)
2347 if( CheckVersion( cd ) == CHECKVER_DESTOLDER )
2349 if (KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE, cd))
2351 ret = TESTDEST_DELETED;
2354 else
2356 ret = TESTDEST_CANTDELETE;
2359 else /* normal "dont overwrite mode" */
2361 ret = TESTDEST_CANTDELETE;
2364 else if (KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE, cd))
2366 ret = TESTDEST_DELETED;
2370 FreeDosObject(DOS_FIB, fib);
2373 if (lock)
2375 UnLock(lock);
2378 else
2380 ret = TESTDEST_NONE;
2383 if (ret == TESTDEST_CANTDELETE)
2385 cd->IoErr = ERROR_OBJECT_EXISTS;
2387 else if (ret == TESTDEST_ERROR)
2389 cd->IoErr = IoErr();
2392 return ret;
2396 ** We compare current file versions and return the result
2397 ** see CHECKVER_#? values
2400 static LONG CheckVersion( struct CopyData *cd )
2402 struct VersionData vds;
2403 struct VersionData vdd;
2404 LONG resversion = CHECKVER_EQUAL;
2405 LONG resdate = CHECKVER_EQUAL;
2407 if( VersionFind( cd->FileName, &vds, cd ) )
2409 if( VersionFind( cd->DestName, &vdd, cd ) )
2411 /* version and revision must be available to ensure a proper operation */
2412 if( ((vdd.vd_Version != -1) && (vds.vd_Version != -1) && (vdd.vd_Revision != -1) && (vds.vd_Revision != -1)) )
2414 /* first we make the stuff comparable. If one component is missing we reset both */
2415 if( vdd.vd_Year == -1 || vds.vd_Year == -1 )
2417 vdd.vd_Year = 0;
2418 vds.vd_Year = 0;
2420 if( vdd.vd_Month == -1 || vds.vd_Month == -1 )
2422 vdd.vd_Month = 0;
2423 vds.vd_Month = 0;
2425 if( vdd.vd_Day == -1 || vds.vd_Day == -1 )
2427 vdd.vd_Day = 0;
2428 vds.vd_Day = 0;
2431 /* check version */
2432 resversion = CHECKVER_DESTOLDER;
2433 if( ((vdd.vd_Version == vds.vd_Version) && vdd.vd_Revision == vds.vd_Revision ) )
2435 resversion = CHECKVER_EQUAL;
2437 else if( (vdd.vd_Version > vds.vd_Version) ||
2438 ((vdd.vd_Version == vds.vd_Version) && vdd.vd_Revision > vds.vd_Revision ) )
2440 resversion = CHECKVER_DESTNEWER;
2442 /* check date */
2444 resdate = CHECKVER_DESTOLDER;
2445 if( ((vdd.vd_Year == vds.vd_Year) && (vdd.vd_Month == vds.vd_Month) && (vdd.vd_Day == vds.vd_Day) ) )
2447 resdate = CHECKVER_EQUAL;
2449 else
2451 if( ( (vdd.vd_Year > vds.vd_Year ) ||
2452 ( (vdd.vd_Year == vds.vd_Year) && (vdd.vd_Month > vds.vd_Month ) ) ||
2453 ( (vdd.vd_Year == vds.vd_Year) && (vdd.vd_Month == vds.vd_Month ) && (vdd.vd_Day > vds.vd_Day ) ) ))
2455 resdate = CHECKVER_DESTNEWER;
2459 /* plausible check */
2460 if( ((resversion == CHECKVER_DESTNEWER) && (resdate == CHECKVER_DESTOLDER)) || /* newer version with older date */
2461 ((resversion == CHECKVER_DESTOLDER) && (resdate == CHECKVER_DESTNEWER)) ) /* older version with newer date */
2463 /* we maybe should inform the user about this */
2464 return( CHECKVER_EQUAL );
2469 /* compose result */
2471 if( (resversion == resdate) || (resversion == CHECKVER_EQUAL) )
2473 return( resdate );
2475 else
2477 return( resversion );
2484 ** Searches the given file for a version string and fills version data struct with the result.
2485 ** Returns false if no version was found. Returns true if the version got parsed and version data
2486 ** is valid.
2489 #define VERSBUFFERSIZE 4096 /* must be as big as the biggest version string we want to handle. */
2491 static BOOL VersionFind( CONST_STRPTR path, struct VersionData *vds, struct CopyData *cd)
2493 BPTR handle;
2494 STRPTR buf;
2495 ULONG i, rc;
2497 rc = FALSE;
2499 if ( (buf = AllocVec(VERSBUFFERSIZE, MEMF_PUBLIC | MEMF_CLEAR)) )
2501 if ( (handle = Open(path, MODE_OLDFILE)) )
2503 long index = 0;
2505 while( ( (index += Read(handle, &buf[index], VERSBUFFERSIZE-index)) > 5) && !rc )
2507 for (i = 0; i < index-5; i++) {
2508 if( buf[i] == '$' && buf[i+1] == 'V' && buf[i+2] == 'E' && buf[i+3] == 'R' && buf[i+4] == ':' ) {
2509 CopyMem( &buf[i], buf, index-i );
2510 index -= i;
2511 (index += Read(handle, &buf[index], VERSBUFFERSIZE-index));
2512 /* now the version string is aligned and complete in buffer */
2513 makeversionfromstring( buf, vds, cd );
2514 rc = TRUE;
2515 break;
2518 CopyMem( &buf[index-5], &buf[0], 5 );
2519 index = 5;
2521 Close(handle);
2523 FreeVec(buf);
2525 return (rc);
2530 ** This function extracts the version information from a given version string.
2531 ** the result will be store in the given version data structure.
2533 ** NOTE: There is no need to preset the version data struct. All fields will
2534 ** be reset to defaults, so in case of a faulty version string the result data
2535 ** will be checkable.
2538 static
2539 void makeversionfromstring( STRPTR buffer, struct VersionData *vd, struct CopyData *cd)
2541 LONG pos;
2542 ULONG tmp;
2543 STRPTR name;
2545 /* reset data field */
2547 vd->vd_Name[0] = '\0';
2548 vd->vd_Version = -1;
2549 vd->vd_Revision = -1;
2550 vd->vd_Day = -1;
2551 vd->vd_Month = -1;
2552 vd->vd_Year = -1;
2554 buffer = skipspaces( buffer ); /* skip before $VER: */
2555 buffer = skipnonspaces( buffer ); /* skip $VER: */
2556 buffer = skipspaces( buffer ); /* skip spaces before tool name */
2557 name = buffer;
2558 buffer = skipnonspaces( buffer ); /* skip name of tool */
2560 if( (tmp = ((long) buffer - (long) name) ) && *buffer )
2562 CopyMem( name, vd->vd_Name, MIN( tmp, VDNAMESIZE-1) );
2563 vd->vd_Name[MIN( tmp, VDNAMESIZE-1)] = '\0'; /* terminate name string inside target buffer */
2565 buffer = skipspaces( buffer ); /* skip spaces before version */
2566 if( *buffer ) {
2568 /* Do version */
2570 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2572 vd->vd_Version = tmp;
2574 /* Do revision */
2576 buffer += pos;
2577 buffer = skipspaces(buffer);
2578 if (*buffer++ == '.')
2580 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2582 vd->vd_Revision = tmp;
2583 buffer += pos;
2584 buffer = skipspaces(buffer);
2585 if (*buffer++ == '(')
2587 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2589 vd->vd_Day = tmp;
2590 buffer += pos;
2591 if (*buffer++ == '.')
2593 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2595 vd->vd_Month = tmp;
2596 buffer += pos;
2597 if (*buffer++ == '.')
2599 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2601 if( (tmp >= 70) && (tmp <= 99) )
2603 tmp += 1900;
2605 if( (tmp < 70) )
2607 tmp += 2000;
2609 vd->vd_Year = tmp;
2623 /* Return a pointer to a string, stripped by all leading space characters
2624 * (SPACE).
2626 static
2627 STRPTR skipspaces( STRPTR buffer)
2629 for (;; buffer++)
2631 if (buffer[0] == '\0' || buffer[0] != ' ')
2633 return( buffer );
2638 /* Return a pointer to a string, stripped by all non space characters
2639 * (SPACE).
2641 static
2642 STRPTR skipnonspaces( STRPTR buffer)
2644 for (;; buffer++)
2646 if (buffer[0] == '\0' || buffer[0] == ' ')
2648 return( buffer );