muimaster.library: migrate event handling from Listview to List
[AROS.git] / workbench / c / Copy.c
blob0d59a14d74d430f3f8a50621621e1e21f04096b0
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_PROCH(Start, argstr, argsize, SysBase)
556 #endif
558 AROS_PROCFUNC_INIT
560 struct DosLibrary *DOSBase;
561 struct Process *task;
562 struct CopyData *cd;
563 int retval = RETURN_FAIL;
565 /* test for WB and reply startup-message */
566 if (!(task = (struct Process *)FindTask(NULL))->pr_CLI)
568 WaitPort(&task->pr_MsgPort);
569 Forbid();
570 ReplyMsg(GetMsg(&task->pr_MsgPort));
572 return RETURN_FAIL;
575 DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 37);
576 cd = AllocMem(sizeof(*cd), MEMF_PUBLIC | MEMF_CLEAR);
578 if (DOSBase && cd)
580 STRPTR a[2] = { "", 0 };
581 struct RDArgs *rda;
582 struct IptrArgs iArgs;
583 struct Args args = {};
585 cd->SysBase = SysBase;
586 cd->DOSBase = DOSBase;
587 #define SysBase cd->SysBase
588 #define DOSBase cd->DOSBase
590 cd->BufferSize = 512*1024;
591 cd->Mode = COPYMODE_COPY;
592 cd->RetVal2 = RETURN_FAIL;
593 cd->Deep = 1;
595 memset(&iArgs, 0, sizeof(struct IptrArgs));
597 rda = (struct RDArgs *)AllocDosObject(DOS_RDARGS, NULL);
598 if (rda)
600 rda->RDA_ExtHelp =
601 "FROM multiple input files\n"
602 "TO destination file or directory\n"
603 "PATTERN a pattern the filenames must match\n"
604 "BUFFER buffersize for copy buffer (default 200 [100K])\n"
605 "ALL deep scan into sub directories\n"
606 "DIRECT copy/delete only: work without any tests or options\n"
607 "CLONE copy comment, protection bits and date as well\n"
608 "DATES copy dates\n"
609 "NOPRO do not copy protection bits\n"
610 "COMMENT copy filecomment\n"
611 "QUIET suppress all output and requesters\n"
612 #if !USE_ALWAYSVERBOSE
613 "VERBOSE give additional output\n"
614 #endif
615 "NOREQ suppress requesters\n"
616 "ERRWARN do not proceed, when one file failed\n"
617 "MAKEDIR produce directories\n"
618 "MOVE delete source files after copying successful\n"
619 "DELETE do not copy, but delete the source files\n"
620 "HARDLINK make a hardlink to source instead of copying\n"
621 "SOFTLINK make a softlink to source instead of copying\n"
622 "FOLNK also makes links to directories\n"
623 "FODEL delete protected files also\n"
624 "FOOVR also overwrite protected files\n"
625 "DONTOVR do never overwrite destination\n"
626 "FORCE DO NOT USE. Call compatibility only.\n"
627 "NEWER will compare version strings and only overwrites older files\n";
629 if (ReadArgs(PARAM, (IPTR *)&iArgs, rda))
631 ULONG patbufsize = 0;
632 LONG i = 0;
633 APTR win = task->pr_WindowPtr;
635 args.from = (STRPTR *)iArgs.from;
636 args.to = (STRPTR)iArgs.to;
637 args.pattern = (STRPTR)iArgs.pattern;
638 args.buffer = (LONG *)iArgs.buffer;
639 args.all = (LONG)iArgs.all;
640 args.direct = (LONG)iArgs.direct;
641 args.clone = (LONG)iArgs.clone;
642 args.dates = (LONG)iArgs.dates;
643 args.nopro = (LONG)iArgs.nopro;
644 args.comment = (LONG)iArgs.comment;
645 args.quiet = (LONG)iArgs.quiet;
646 #if USE_ALWAYSVERBOSE
647 args.verbose = FALSE;
648 #else
649 args.verbose = (LONG)iArgs.verbose;
650 #endif
651 args.noreq = (LONG)iArgs.noreq;
652 args.errwarn = (LONG)iArgs.errwarn;
653 args.makedir = (LONG)iArgs.makedir;
654 args.move_mode = (LONG)iArgs.move_mode;
655 args.delete_mode = (LONG)iArgs.delete_mode;
656 args.hardlink = (LONG)iArgs.hardlink;
657 args.softlink = (LONG)iArgs.softlink;
658 args.forcelink = (LONG)iArgs.forcelink;
659 args.forcedelete = (LONG)iArgs.forcedelete;
660 args.forceoverwrite = (LONG)iArgs.forceoverwrite;
661 args.dontoverwrite = (LONG)iArgs.dontoverwrite;
662 args.force = (LONG)iArgs.force;
663 args.newer = (LONG)iArgs.newer;
665 if (args.quiet) /* when QUIET, SILENT and NOREQ are also
666 true! */
668 /* Original doesn't hide requesters with QUIET */
669 /*args.noreq = 1;*/
670 args.verbose = FALSE;
673 if (args.buffer && *args.buffer > 0) /* minimum buffer size */
675 cd->BufferSize = *args.buffer * 512;
678 if (args.quiet)
680 cd->Flags |= COPYFLAG_QUIET;
683 #if !USE_ALWAYSVERBOSE
684 if (args.verbose)
686 cd->Flags |= COPYFLAG_VERBOSE;
688 #endif
689 if (args.all)
691 cd->Flags |= COPYFLAG_ALL;
694 if (args.clone)
696 cd->Flags |= COPYFLAG_DATES | COPYFLAG_COMMENT;
699 if (args.dates)
701 cd->Flags |= COPYFLAG_DATES;
704 if (args.comment)
706 cd->Flags |= COPYFLAG_COMMENT;
709 if (args.nopro)
711 cd->Flags |= COPYFLAG_NOPRO;
714 if (args.forcelink)
716 cd->Flags |= COPYFLAG_FORCELINK;
719 if (args.forcedelete)
721 cd->Flags |= COPYFLAG_FORCEDELETE;
724 if (args.forceoverwrite)
726 cd->Flags |= COPYFLAG_FORCEOVERWRITE;
729 if (args.dontoverwrite)
731 cd->Flags |= COPYFLAG_DONTOVERWRITE;
734 if (args.newer)
736 cd->Flags |= COPYFLAG_NEWER|COPYFLAG_DONTOVERWRITE;
739 if (args.errwarn)
741 cd->Flags |= COPYFLAG_ERRWARN;
744 if (args.force) /* support OS Delete and MakeLink command
745 options */
747 if (args.delete_mode)
749 cd->Flags |= COPYFLAG_FORCEDELETE;
752 if (args.hardlink || args.softlink)
754 cd->Flags |= COPYFLAG_FORCELINK;
758 if (!args.from) /* no args.from means currentdir */
760 args.from = a;
763 if (args.noreq) /* no dos.library requests allowed */
765 task->pr_WindowPtr = (APTR)-1;
768 if (args.delete_mode)
770 ++i;
771 cd->Mode = COPYMODE_DELETE;
774 if (args.move_mode)
776 ++i;
777 cd->Mode = COPYMODE_MOVE;
780 if (args.makedir)
782 ++i;
783 cd->Mode = COPYMODE_MAKEDIR;
786 if (args.hardlink)
788 ++i;
789 cd->Mode = COPYMODE_LINK;
792 if (args.softlink)
794 ++i;
795 cd->Mode = COPYMODE_LINK;
796 cd->Flags |= COPYFLAG_SOFTLINK | COPYFLAG_FORCELINK;
799 if (cd->Mode != COPYMODE_DELETE &&
800 cd->Mode != COPYMODE_MAKEDIR && !args.to)
802 if (*(args.from + 1)) /* when no TO is specified, the arg
803 is last */
804 { /* one of from. Copy this argument into */
805 STRPTR *a; /* args.to */
807 a = args.from;
809 while(*(++a))
812 args.to = *(--a);
813 *a = 0;
816 #if USE_ALWAYSVERBOSE
818 /* Only do this if quiet isn't set - bigfoot */
819 if (!args.quiet)
821 /* If more than two args, be verbose... - Piru */
822 if (args.from[0] && args.from[1])
824 args.verbose = TRUE;
825 cd->Flags |= COPYFLAG_VERBOSE;
828 /* If any of the sources is a pattern, be verbose... - Piru */
830 STRPTR *a;
832 for (a = args.from; *a; a++)
834 if (IsMatchPattern(*a, cd) != 0)
836 args.verbose = TRUE;
837 cd->Flags |= COPYFLAG_VERBOSE;
842 #endif
844 /* test if more than one of the above four or any other wrong
845 arguments */
847 if (i > 1 ||
848 (args.from == a && cd->Mode == COPYMODE_MAKEDIR) ||
849 (args.direct && (args.from == a || !*args.from ||
850 args.pattern ||
851 (cd->Flags & ~(COPYFLAG_QUIET | COPYFLAG_VERBOSE | COPYFLAG_ERRWARN)) ||
852 (cd->Mode != COPYMODE_DELETE && (cd->Mode != COPYMODE_COPY ||
853 !args.to || args.from[1])))) ||
854 (args.dontoverwrite && args.forceoverwrite) ||
855 /* (args.nopro && args.clone) ||*/ /* Ignore, like original - Piru */
856 (args.softlink && args.all) ||
857 (!args.to && cd->Mode != COPYMODE_DELETE && cd->Mode != COPYMODE_MAKEDIR))
859 cd->IoErr = ERROR_TOO_MANY_ARGS;
861 else if (cd->Mode == COPYMODE_MAKEDIR)
863 LONG i;
864 BPTR dir;
865 cd->RetVal2 = RETURN_OK;
867 #if !USE_ALWAYSVERBOSE
868 if (args.verbose)
870 PutStr(texts[TEXTNUM_MODE + COPYMODE_MAKEDIR]);
872 #endif
874 while (!cd->RetVal && !cd->RetVal2 && *args.from)
876 if ((i = IsPattern(*args.from, cd)))
878 if (i != -1)
880 cd->RetVal = RETURN_ERROR;
882 if (!args.quiet)
884 PutStr(TEXT_ERR_WILDCARD_DEST);
887 else
889 cd->RetVal2 = RETURN_FAIL;
893 if ((dir = OpenDestDir(*args.from, cd)))
895 UnLock(dir);
896 cd->Flags |= COPYFLAG_DONE;
899 ++args.from;
901 } /* cd->Mode == COPYMODE_MAKEDIR */
902 else if (args.direct)
904 if (cd->Mode == COPYMODE_COPY)
906 BPTR in, out;
908 if ((in = Open(*args.from, MODE_OLDFILE)))
910 if ((out = Open(args.to, MODE_NEWFILE)))
912 cd->RetVal2 = CopyFile(in, out, cd->BufferSize, cd);
913 if (cd->RetVal2 != 0)
914 cd->IoErr = IoErr();
915 Close(out);
917 else
918 cd->IoErr = IoErr();
920 Close(in);
922 else
923 cd->IoErr = IoErr();
925 else /* COPYMODE_DELETE */
927 while (*args.from)
929 KillFile(*(args.from++), cd->Flags & COPYFLAG_FORCEDELETE, cd);
932 cd->RetVal2 = RETURN_OK;
935 else
937 if (args.pattern && *args.pattern)
939 patbufsize = (strlen(args.pattern) << 1) + 3;
941 if ((cd->Pattern = (STRPTR)AllocMem(patbufsize,
942 MEMF_ANY)))
944 if (ParsePatternNoCase(args.pattern, cd->Pattern,
945 patbufsize) < 0)
947 FreeMem(cd->Pattern, patbufsize);
948 cd->Pattern = 0;
953 if (1) // (cd->Fib = (struct FileInfoBlock *)AllocDosObject(DOS_FIB, NULL)))
955 #if !USE_ALWAYSVERBOSE
956 if (args.verbose)
958 PutStr(texts[TEXTNUM_MODE + cd->Mode +
959 (cd->Flags & COPYFLAG_SOFTLINK ? 1 : 0)]);
961 #endif
962 if (args.pattern && !cd->Pattern)
964 if (!*args.pattern)
966 cd->IoErr = ERROR_BAD_TEMPLATE;
969 else if (cd->Mode == COPYMODE_DELETE)
971 cd->RetVal2 = RETURN_OK;
973 while (cd->RetVal <= (args.errwarn ? RETURN_OK : RETURN_WARN)
974 && *args.from)
976 PatCopy(*(args.from++), cd);
979 else if ((i = IsPattern(args.to, cd)))
981 if (i != -1)
983 cd->RetVal = RETURN_ERROR;
985 if (!args.quiet)
987 PutStr(TEXT_ERR_WILDCARD_DEST);
991 else
993 STRPTR path;
995 if (*(path = PathPart(args.to)) == '/')
997 ++path; /* is destination a path description ? */
1000 if (*path && !*(args.from+1) &&
1001 !(i = IsMatchPattern(*args.from, cd)))
1003 BPTR lock;
1005 /* is destination an existing directory */
1006 if ((lock = Lock(args.to, SHARED_LOCK)))
1008 if (Examine(lock, &cd->Fib))
1010 if (cd->Fib.fib_DirEntryType > 0)
1012 cd->RetVal2 = RETURN_OK;
1015 /* indicate dir-mode for next if */
1017 else
1019 i = 1;
1022 UnLock(lock);
1025 /* Some magic to handle tick quoted pattern object names. Quite crude way to
1026 * handle it, but I couldn't think of anything better. - Piru
1028 #if 1
1029 if (!i && cd->RetVal2 && !IsMatchPattern(*args.from, cd))
1031 ULONG len;
1032 STRPTR pat;
1034 //Printf("pattern check <%s>\n", *args.from);
1036 len = (strlen(*args.from) << 1) + 3;
1038 if ((pat = (STRPTR)AllocMem(len,
1039 MEMF_ANY)))
1041 if (ParsePattern(*args.from, pat, len) > -1 &&
1042 strlen(pat) <= strlen(*args.from))
1044 lock = Lock(pat, SHARED_LOCK);
1045 if (lock)
1047 UnLock(lock);
1049 strcpy(*args.from, pat);
1053 FreeMem(pat, len);
1056 #endif
1058 /* is source a directory */
1059 if (!i && cd->RetVal2 &&
1060 (lock = Lock(*args.from, SHARED_LOCK)))
1062 if (Examine(lock, &cd->Fib))
1064 cd->RetVal2 = RETURN_OK;
1065 if (cd->Mode != COPYMODE_COPY ||
1066 cd->Fib.fib_DirEntryType < 0)
1068 UBYTE sep;
1070 cd->Flags |= COPYFLAG_DEST_FILE;
1072 /* produce missing destination directories */
1073 sep = *path;
1074 *path = 0;
1076 if ((cd->CurDest = OpenDestDir(args.to, cd)))
1078 *path = sep;
1080 /* do the job */
1081 UnLock(lock);
1082 lock = 0;
1083 CopyMem(*args.from, cd->FileName,
1084 1 + strlen(*args.from));
1085 DoWork(FilePart(args.to), cd); /* on file call */
1086 UnLock(cd->CurDest);
1091 if (lock)
1093 UnLock(lock);
1097 if (lock == 0 && cd->Mode == COPYMODE_COPY && !TestFileSys(*args.from, cd))
1099 UBYTE sep;
1100 cd->Flags |= COPYFLAG_DEST_FILE | COPYFLAG_SRCNOFILESYS;
1101 cd->RetVal2 = RETURN_OK;
1103 /* produce missing destination directories */
1104 sep = *path;
1105 *path = 0;
1107 if ((cd->CurDest = OpenDestDir(args.to, cd)))
1109 *path = sep;
1111 /* do the job */
1112 CopyMem(*args.from, cd->FileName, 1 + strlen(*args.from));
1113 DoWork(FilePart(args.to), cd); /* on file call */
1114 UnLock(cd->CurDest);
1118 else if (i != -1)
1120 cd->RetVal2 = RETURN_OK;
1123 if (!cd->RetVal && !cd->RetVal2 && !(cd->Flags & COPYFLAG_DEST_FILE) &&
1124 (cd->Destination = OpenDestDir(args.to, cd)))
1126 while (cd->RetVal <= (args.errwarn ? RETURN_OK : RETURN_WARN)
1127 && *args.from && !CTRL_C)
1129 PatCopy(*(args.from++), cd);
1132 UnLock(cd->Destination);
1134 } /* else */
1136 if (!(cd->Flags & COPYFLAG_DONE) && args.verbose &&
1137 !cd->RetVal && !cd->RetVal2)
1139 PutStr(TEXT_NOTHING_DONE);
1142 } /* if (1) */
1144 if (cd->Pattern)
1146 FreeMem(cd->Pattern, patbufsize);
1148 } /* else */
1150 task->pr_WindowPtr = win;
1152 FreeArgs(rda);
1153 } /* ReadArgs */
1155 FreeDosObject(DOS_RDARGS, rda);
1156 } /* AllocDosObject */
1158 if (!cd->RetVal2 && CTRL_C)
1160 SetIoErr(ERROR_BREAK);
1161 cd->RetVal2 = RETURN_WARN;
1164 if (cd->RetVal)
1166 cd->RetVal2 = cd->RetVal;
1169 if (args.errwarn && cd->RetVal2 == RETURN_WARN)
1171 cd->RetVal2 = RETURN_ERROR;
1174 if (cd->CopyBuf)
1176 FreeMem(cd->CopyBuf, cd->CopyBufLen);
1179 #undef SysBase
1180 #undef DOSBase
1182 if (cd)
1184 retval = cd->RetVal2;
1185 SetIoErr(cd->IoErr);
1186 FreeMem(cd, sizeof(*cd));
1188 else if (DOSBase)
1190 PrintFault(IoErr(), NULL);
1192 if (DOSBase)
1194 CloseLibrary((struct Library *)DOSBase);
1196 return retval;
1198 AROS_PROCFUNC_EXIT
1201 /* This code is pure and has library bases in explicitly allocated data structure */
1202 #define SysBase cd->SysBase
1203 #define DOSBase cd->DOSBase
1205 void PatCopy(STRPTR name, struct CopyData *cd)
1207 struct AnchorPath *APath;
1208 ULONG retval, doit = 0, deep = 0, failval = RETURN_WARN, first = 0;
1210 #if DEBUG
1211 Printf("PatCopy(%s, .)\n", name);
1212 #endif
1214 if ((cd->Mode == COPYMODE_COPY || (cd->Flags & COPYFLAG_ALL)) && !IsMatchPattern(name, cd))
1216 first = 1; /* enter first directory (support of old copy style) */
1219 if (cd->Flags & COPYFLAG_ERRWARN)
1221 failval = RETURN_OK;
1224 cd->CurDest = cd->Destination;
1225 cd->DestPathSize = 0;
1227 if (cd->Mode == COPYMODE_COPY && !TestFileSys(name, cd))
1229 cd->Flags |= COPYFLAG_SRCNOFILESYS;
1230 CopyMem(name, cd->FileName, 1 + strlen(name));
1231 DoWork(FilePart(name), cd);
1232 cd->Flags &= ~COPYFLAG_SRCNOFILESYS;
1234 return;
1237 if ((APath = (struct AnchorPath *)AllocMem(sizeof(struct AnchorPath) + FILEPATH_SIZE,
1238 MEMF_PUBLIC | MEMF_CLEAR)))
1240 int parentdirerr = 0;
1242 APath->ap_BreakBits = SIGBREAKF_CTRL_C;
1243 APath->ap_Strlen = FILEPATH_SIZE;
1245 for (retval = MatchFirst(name, APath);
1246 !retval && cd->RetVal <= failval && !cd->RetVal2;
1247 retval = MatchNext(APath)
1250 if (parentdirerr)
1252 //Printf("ParentDir() fuxored last round! Would copy next files to SYS: !\n");
1253 retval = IoErr();
1254 if (!retval)
1255 cd->IoErr = retval = ERROR_INVALID_LOCK;
1256 break;
1259 if (doit)
1261 DoWork(cd->Fib.fib_FileName, cd);
1262 doit = 0;
1265 if (deep) /* used for Deep checking */
1267 ++cd->Deep;
1268 deep = 0;
1271 cd->Flags &= ~COPYFLAG_ENTERSECOND;
1273 CopyMem(APath->ap_Buf, cd->FileName, FILEPATH_SIZE);
1274 CopyMem(&APath->ap_Info, &cd->Fib, sizeof(struct FileInfoBlock));
1276 if (first && APath->ap_Info.fib_DirEntryType > 0)
1278 #if USE_ALWAYSVERBOSE
1279 /* If the source is a directory, be verbose - Piru */
1280 cd->Flags |= COPYFLAG_VERBOSE;
1281 #endif
1282 APath->ap_Flags |= APF_DODIR;
1284 else if (APath->ap_Flags & APF_DIDDIR)
1286 BPTR i;
1288 cd->Flags |= COPYFLAG_ENTERSECOND;
1289 APath->ap_Flags &= ~APF_DIDDIR;
1290 --cd->Deep;
1292 if (cd->Mode == COPYMODE_DELETE || cd->Mode == COPYMODE_MOVE)
1294 doit = 1;
1297 if ((i = cd->CurDest))
1299 cd->CurDest = ParentDir(i);
1300 cd->DestPathSize = 0;
1302 if (i != cd->Destination)
1304 UnLock(i);
1307 if (!cd->CurDest)
1309 parentdirerr = 1;
1310 continue;
1314 else if (APath->ap_Info.fib_DirEntryType > 0)
1316 doit = 1;
1318 if (cd->Flags & COPYFLAG_ALL)
1320 BOOL enter;
1322 #if USE_SOFTLINKCHECK
1324 #define BUFFERSIZE 512
1325 if (APath->ap_Info.fib_DirEntryType == ST_SOFTLINK)
1327 UBYTE *buffer = AllocMem(BUFFERSIZE, MEMF_PUBLIC);
1329 D(Printf("%s is a softlink\n", APath->ap_Info.fib_FileName));
1330 enter = FALSE;
1331 doit = FALSE;
1333 if (buffer)
1335 struct DevProc *dvp = GetDeviceProc("", NULL);
1337 if (ReadLink(dvp->dvp_Port, APath->ap_Current->an_Lock, APath->ap_Info.fib_FileName, buffer, BUFFERSIZE - 1) > 0)
1339 BOOL link_ok = FALSE;
1340 BPTR dir, lock;
1342 buffer[BUFFERSIZE - 1] = '\0';
1343 D(Printf("Softlink target: %s\n", buffer));
1345 dir = CurrentDir(APath->ap_Current->an_Lock);
1346 lock = Lock(buffer, SHARED_LOCK);
1347 if (lock)
1349 struct FileInfoBlock *fib = AllocDosObject(DOS_FIB, NULL);
1351 if (fib)
1353 if (Examine(lock, fib))
1355 link_ok = TRUE;
1356 D(Printf("Target type: %ld\n", fib->fib_DirEntryType));
1358 if (fib->fib_DirEntryType > 0)
1359 enter = TRUE;
1360 else
1362 * FIXME: This currently just prevents treating symlinks to files as
1363 * directories during copying.
1364 * DoWork() should be extended to handle symlinks correctly. BTW, how exactly ?
1366 doit = FALSE;
1368 FreeDosObject(DOS_FIB, fib);
1370 UnLock(lock);
1373 CurrentDir(dir);
1375 if (!link_ok)
1377 Printf("Warning: Skipping dangling softlink %s -> %s\n",
1378 APath->ap_Info.fib_FileName, buffer);
1381 FreeDeviceProc(dvp);
1382 FreeMem(buffer, BUFFERSIZE);
1385 else
1386 #endif /* USE_SOFTLINKCHECK */
1387 enter = TRUE;
1389 if (enter)
1391 APath->ap_Flags |= APF_DODIR;
1392 deep = 1;
1396 else if (!cd->Pattern || MatchPatternNoCase(cd->Pattern, APath->ap_Info.fib_FileName))
1398 doit = 1;
1401 first = 0;
1404 MatchEnd(APath);
1406 if (retval && retval != ERROR_NO_MORE_ENTRIES)
1408 LONG ioerr = IoErr();
1409 #if USE_ALWAYSVERBOSE
1410 cd->Flags |= COPYFLAG_VERBOSE;
1411 #endif
1412 Printf("%s - ", APath->ap_Info.fib_FileName);
1413 PrintFault(ioerr, NULL);
1414 cd->IoErr = ioerr;
1416 cd->RetVal2 = RETURN_FAIL;
1419 if (doit)
1421 DoWork(cd->Fib.fib_FileName, cd);
1424 /* No need to clear the flags here, as they are cleared on next PatJoin
1425 call (DoWork is not called first round, as lock is zero!). */
1427 FreeMem(APath, sizeof(struct AnchorPath) + FILEPATH_SIZE);
1429 else
1431 cd->RetVal = RETURN_FAIL;
1433 if (!(cd->Flags & COPYFLAG_QUIET))
1435 PrintFault(ERROR_NO_FREE_STORE, NULL);
1439 if (cd->CurDest && cd->CurDest != cd->Destination)
1441 UnLock(cd->CurDest);
1446 LONG IsPattern(STRPTR name, struct CopyData *cd)
1448 LONG a, ret = -1;
1449 STRPTR buffer;
1451 a = (strlen(name) << 1) + 3;
1453 if ((buffer = (STRPTR)AllocMem(a, MEMF_ANY)))
1455 ret = ParsePattern(name, buffer, a);
1456 FreeMem(buffer, a);
1459 if (ret == -1)
1461 cd->IoErr = ERROR_NO_FREE_STORE;
1464 return ret;
1468 LONG IsMatchPattern(STRPTR name, struct CopyData *cd)
1470 struct AnchorPath ap;
1471 LONG ret = -1;
1473 ap.ap_BreakBits = 0;
1474 ap.ap_Flags = APF_DOWILD;
1475 ap.ap_Strlen = 0;
1477 if (MatchFirst(name, &ap) == 0)
1479 ret = (ap.ap_Flags & APF_ITSWILD) ? TRUE : FALSE;
1481 MatchEnd(&ap);
1484 return ret;
1488 LONG KillFile(STRPTR name, ULONG doit, struct CopyData *cd)
1490 if (doit)
1492 SetProtection(name, 0);
1495 return DeleteFile(name);
1499 BPTR OpenDestDir(STRPTR name, struct CopyData *cd)
1501 LONG a, err = 0, cr = 0;
1502 BPTR dir;
1503 STRPTR ptr = name;
1504 UBYTE as;
1506 if ((cd->Mode == COPYMODE_COPY || cd->Mode == COPYMODE_MOVE) && !TestFileSys(name, cd))
1508 cd->Flags |= COPYFLAG_DESNOFILESYS;
1509 CopyMem(name, cd->DestName, 1 + strlen(name));
1511 return Lock("", SHARED_LOCK);
1514 while (!err && *ptr != 0)
1516 while (*ptr && *ptr != '/')
1518 ++ptr;
1521 as = *ptr;
1522 *ptr = 0;
1524 if ((a = TestDest(name, 1, cd)) == TESTDEST_CANTDELETE)
1526 if (!(cd->Flags & COPYFLAG_QUIET))
1528 PutStr(TEXT_ERR_DEST_DIR);
1531 err = 2;
1533 else if (a < 0)
1535 err = 1;
1537 else if (a != TESTDEST_DIR_OK)
1539 if ((dir = CreateDir(name)))
1541 ++cr;
1543 if ((cd->Flags & COPYFLAG_VERBOSE))
1545 PrintName(name, 1, 1, 1, cd);
1546 Printf("%s\n", TEXT_CREATED);
1549 UnLock(dir);
1551 else
1553 cd->IoErr = IoErr();
1554 if (!(cd->Flags & COPYFLAG_QUIET))
1556 PrintNotDone(name, TEXT_CREATED, 1, 1, cd);
1559 err = 2;
1563 *ptr = as;
1565 /* 26-Oct-2003 bugfix: Don't scan past end of the string.
1566 * as is the old char, if '\0' we've reached the end of the
1567 * string. - Piru
1569 if (as)
1571 ptr++;
1575 if (err)
1577 cd->RetVal = RETURN_ERROR;
1579 if (!(cd->Flags & COPYFLAG_QUIET) && err == 1)
1581 PrintNotDone(name, TEXT_OPENED_FOR_OUTPUT, 1, 1, cd);
1584 return 0;
1587 if (cd->Mode == COPYMODE_MAKEDIR && !cr && !(cd->Flags & COPYFLAG_QUIET))
1589 cd->IoErr = ERROR_OBJECT_EXISTS;
1590 PrintNotDone(name, TEXT_CREATED, 1, 1, cd);
1593 return Lock(name, SHARED_LOCK);
1597 void PrintName(CONST_STRPTR name, ULONG deep, ULONG dir, ULONG txt, struct CopyData *cd)
1599 #if 0
1600 deep %= PRINTOUT_SPACES; /* reduce number of spaces */
1601 /* This produces an error with MaxonC++ */
1602 #endif
1604 PutStr(" ");
1605 if (deep)
1607 while (--deep)
1609 PutStr(" ");
1613 if (dir)
1615 PutStr(" ");
1618 #if 0
1619 if ((deep = strlen(name)) > PRINTOUT_SIZE) /* reduce name size */
1621 name += deep-PRINTOUT_SIZE;
1622 PutStr("...");
1624 #endif
1626 Printf((dir ? TEXT_DIRECTORY : (STRPTR) "%s"), name);
1628 if (!dir && txt)
1630 PutStr("..");
1633 Flush(Output());
1637 void PrintNotDone(CONST_STRPTR name, CONST_STRPTR txt, ULONG deep, ULONG dir, struct CopyData *cd)
1639 #if !USE_ALWAYSVERBOSE
1640 if (name)
1642 PrintName(name, deep, dir, 1, cd);
1644 #endif
1646 Printf(TEXT_NOT_DONE, txt);
1647 if (cd->IoErr != 0)
1649 PutStr(" - ");
1650 PrintFault(cd->IoErr, NULL);
1652 else
1653 PutStr("\n");
1657 /* returns value, when it seems to be a filesystem */
1658 ULONG TestFileSys(STRPTR name, struct CopyData *cd)
1660 STRPTR n = name;
1661 ULONG ret = 1;
1663 while (*n && *n != ':')
1665 ++n;
1668 if (*(n++) == ':')
1670 UBYTE a;
1672 a = *n;
1673 *n = 0;
1674 ret = IsFileSystem(name);
1675 *n = a;
1678 return ret;
1682 void DoWork(STRPTR name, struct CopyData *cd)
1684 BPTR pdir, lock = 0;
1685 CONST_STRPTR printerr = NULL, printok = "";
1687 #if DEBUG
1688 Printf("DoWork(%s, .)\n", name);
1689 #endif
1691 if (cd->RetVal > (cd->Flags & COPYFLAG_ERRWARN ? RETURN_OK : RETURN_WARN) || cd->RetVal2)
1693 #if DEBUG
1694 Printf("DoWork(RetVal %ld)\n", cd->RetVal);
1695 #endif
1696 return;
1699 if (cd->Mode != COPYMODE_DELETE && !(cd->Flags & COPYFLAG_DESNOFILESYS))
1701 if (!cd->DestPathSize)
1703 if (!NameFromLock(cd->CurDest, cd->DestName, FILEPATH_SIZE))
1705 cd->RetVal2 = RETURN_FAIL;
1707 #if DEBUG
1708 Printf("DoWork(NameFromLock RetVal %ld)\n", cd->RetVal);
1709 #endif
1711 return;
1714 cd->DestPathSize = strlen(cd->DestName);
1717 cd->DestName[cd->DestPathSize] = 0;
1718 AddPart(cd->DestName, name, FILEPATH_SIZE);
1721 if (cd->Flags & (COPYFLAG_SRCNOFILESYS|COPYFLAG_DESNOFILESYS))
1723 ULONG res = 0;
1724 BPTR in, out;
1725 CONST_STRPTR txt = TEXT_OPENED_FOR_OUTPUT;
1727 #if DEBUG
1728 Printf("Partly DIRECT mode active now (%s - %s)\n", cd->FileName,
1729 cd->DestName);
1730 #endif
1732 if ((in = Open(cd->FileName, MODE_OLDFILE)))
1734 txt = cd->Mode == COPYMODE_MOVE ? TEXT_MOVED : TEXT_COPIED;
1736 if ((out = Open(cd->DestName, MODE_NEWFILE)))
1738 ULONG h;
1740 h = CopyFile(in, out, cd->BufferSize, cd);
1741 if (h != 0)
1742 cd->IoErr = IoErr();
1743 Close(out);
1744 Close(in);
1745 in = BNULL;
1747 if (!h)
1749 if (cd->Mode == COPYMODE_MOVE)
1751 if (KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE, cd))
1753 res = 1;
1756 else
1758 res = 1;
1760 } else {
1761 KillFile(cd->DestName, 0, cd);
1764 else
1765 cd->IoErr = IoErr();
1767 if (in != BNULL)
1768 Close(in);
1770 else
1771 cd->IoErr = IoErr();
1773 if (!res && !(cd->Flags & COPYFLAG_QUIET))
1775 PrintNotDone(name, txt, cd->Deep, cd->Fib.fib_DirEntryType > 0, cd);
1777 else
1779 cd->Flags |= COPYFLAG_DONE;
1781 if ((cd->Flags & COPYFLAG_VERBOSE))
1783 Printf("%s\n", txt);
1787 #if DEBUG
1788 PutStr("DoWork(done)\n");
1789 #endif
1790 return;
1793 if (!(lock = Lock(cd->FileName, SHARED_LOCK)))
1795 cd->RetVal = RETURN_WARN;
1796 cd->IoErr = IoErr();
1798 if (!(cd->Flags & COPYFLAG_QUIET))
1800 PrintNotDone(cd->Fib.fib_FileName, TEXT_READ, cd->Deep,
1801 cd->Fib.fib_DirEntryType > 0, cd);
1804 #if DEBUG
1805 Printf("DoWork(Lock RetVal %ld)\n", cd->RetVal);
1806 #endif
1807 return;
1810 if (!(pdir = ParentDir(lock)))
1812 cd->RetVal = RETURN_ERROR;
1814 if (cd->Mode == COPYMODE_DELETE)
1816 if (!(cd->Flags & COPYFLAG_QUIET))
1818 Printf(" %s ", cd->FileName);
1819 Printf(TEXT_NOT_DONE, TEXT_DELETED);
1820 Printf("%s\n", TEXT_ERR_DELETE_DEVICE);
1824 UnLock(lock);
1826 #if DEBUG
1827 Printf("DoWork(ParentDir %ld)\n", cd->RetVal);
1828 #endif
1829 return;
1832 UnLock(pdir);
1834 if (!(cd->Flags & COPYFLAG_QUIET))
1836 if ((cd->Flags & COPYFLAG_VERBOSE))
1838 PrintName(name, cd->Deep, cd->Fib.fib_DirEntryType > 0, cd->Fib.fib_DirEntryType < 0 ||
1839 (cd->Flags & COPYFLAG_ALL ? cd->Mode != COPYMODE_DELETE : cd->Mode != COPYMODE_COPY) ||
1840 cd->Flags & COPYFLAG_ENTERSECOND, cd);
1844 if ((cd->Flags & COPYFLAG_ENTERSECOND) || (cd->Mode == COPYMODE_DELETE &&
1845 (!(cd->Flags & COPYFLAG_ALL) || cd->Fib.fib_DirEntryType < 0)))
1847 UnLock(lock);
1848 lock = 0;
1850 if (KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE, cd))
1852 printok = TEXT_DELETED;
1854 else
1856 cd->RetVal = RETURN_WARN;
1857 printerr = TEXT_DELETED;
1860 else if (cd->Mode == COPYMODE_DELETE)
1864 else if (cd->Fib.fib_DirEntryType > 0)
1866 LONG a;
1868 if ((cd->Flags & COPYFLAG_ALL || cd->Mode == COPYMODE_LINK ||
1869 cd->Mode == COPYMODE_MOVE) && TestLoop(lock, cd->CurDest, cd))
1871 printok = 0;
1872 cd->RetVal = RETURN_ERROR;
1874 if (!(cd->Flags & COPYFLAG_QUIET))
1876 if (!(cd->Flags & COPYFLAG_VERBOSE))
1878 PrintName(name, cd->Deep, 1, 1, cd);
1881 Printf(TEXT_NOT_DONE, TEXT_ENTERED);
1882 PutStr(TEXT_ERR_INFINITE_LOOP);
1885 else if ((a = TestDest(cd->DestName, 1, cd)) < 0)
1887 printerr = TEXT_CREATED;
1888 cd->RetVal = RETURN_ERROR;
1890 else if (cd->Flags & COPYFLAG_ALL)
1892 BPTR i;
1894 i = cd->CurDest;
1895 cd->DestPathSize = 0;
1897 if (a == TESTDEST_DIR_OK)
1899 if (!(cd->CurDest = Lock(cd->DestName, SHARED_LOCK)))
1901 cd->IoErr = IoErr();
1902 printerr = TEXT_ENTERED;
1903 cd->RetVal = RETURN_ERROR;
1905 else
1907 #if USE_ALWAYSVERBOSE
1908 printok = "";
1909 #else
1910 printok = TEXT_ENTERED;
1911 #endif
1914 else if ((cd->CurDest = CreateDir(cd->DestName)))
1916 UnLock(cd->CurDest);
1918 if ((cd->CurDest = Lock(cd->DestName, SHARED_LOCK)))
1920 printok = TEXT_CREATED;
1922 else
1924 cd->IoErr = IoErr();
1925 printerr = TEXT_ENTERED;
1926 cd->RetVal = RETURN_ERROR;
1929 else
1931 cd->IoErr = IoErr();
1932 printerr = TEXT_CREATED;
1933 cd->RetVal = RETURN_ERROR;
1936 if (!cd->CurDest)
1938 cd->CurDest = i;
1940 else if (i != cd->Destination)
1942 UnLock(i);
1945 else if (cd->Mode == COPYMODE_MOVE)
1947 if (Rename(cd->FileName, cd->DestName))
1949 printok = TEXT_RENAMED;
1951 else
1953 cd->IoErr = IoErr();
1954 printerr = TEXT_RENAMED;
1955 cd->RetVal = RETURN_WARN;
1958 else if (cd->Mode == COPYMODE_LINK)
1960 if (!(cd->Flags & COPYFLAG_FORCELINK))
1962 printok = 0;
1963 cd->RetVal = RETURN_WARN;
1965 if (!(cd->Flags & COPYFLAG_QUIET))
1967 if (!(cd->Flags & COPYFLAG_VERBOSE))
1969 PrintName(name, cd->Deep, 1, 1, cd);
1972 Printf(TEXT_NOT_DONE, TEXT_LINKED);
1973 PutStr(TEXT_ERR_FORCELINK);
1976 else if (LinkFile(lock, cd->DestName, cd->Flags & COPYFLAG_SOFTLINK, cd))
1978 printok = TEXT_LINKED;
1980 else
1982 printerr = TEXT_LINKED;
1983 cd->RetVal = RETURN_WARN;
1986 else /* COPY mode only displays directories, when not ALL */
1988 printok = 0;
1990 if (!(cd->Flags & COPYFLAG_QUIET))
1992 if (cd->Flags & COPYFLAG_VERBOSE)
1994 PutStr("\n");
1999 else
2001 /* test for existing destination file */
2002 if (TestDest(cd->DestName, 0, cd) < 0)
2004 printerr = TEXT_OPENED_FOR_OUTPUT;
2006 else if (cd->Mode == COPYMODE_MOVE && Rename(cd->FileName, cd->DestName))
2008 printok = TEXT_RENAMED;
2010 else if (cd->Mode == COPYMODE_LINK)
2012 if (!(cd->Flags & COPYFLAG_SOFTLINK) && LinkFile(lock, cd->DestName, 0, cd))
2014 printok = TEXT_LINKED;
2016 else
2018 printerr = TEXT_LINKED;
2019 cd->RetVal = RETURN_WARN;
2021 if (cd->Flags & COPYFLAG_SOFTLINK)
2023 cd->IoErr = ERROR_OBJECT_WRONG_TYPE;
2027 else
2029 ULONG res = 0, h;
2030 BPTR in, out;
2031 CONST_STRPTR txt = TEXT_OPENED_FOR_OUTPUT;
2033 if ((out = Open(cd->DestName, MODE_NEWFILE)))
2035 ULONG kill = 1;
2037 txt = cd->Mode == COPYMODE_MOVE ? TEXT_MOVED : TEXT_COPIED;
2038 UnLock(lock);
2039 lock = 0;
2041 if ((in = Open(cd->FileName, MODE_OLDFILE)))
2043 h = CopyFile(in, out, cd->BufferSize, cd);
2044 if (h != 0)
2045 cd->IoErr = IoErr();
2046 Close(out);
2047 out = BNULL;
2048 Close(in);
2050 if (!h)
2052 kill = 0;
2054 if (cd->Mode == COPYMODE_MOVE)
2056 if (KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE, cd))
2058 res = 1;
2061 else
2063 res = 1;
2067 else cd->IoErr = IoErr();
2069 if (out)
2071 Close(out);
2074 if (kill)
2076 KillFile(cd->DestName, 0, cd);
2079 else cd->IoErr = IoErr();
2081 if (!res)
2083 printerr = txt;
2084 cd->RetVal = RETURN_WARN;
2086 else
2088 printok = txt;
2093 if (printerr && !(cd->Flags & COPYFLAG_QUIET))
2095 PrintNotDone(name, printerr, cd->Deep, cd->Fib.fib_DirEntryType > 0, cd);
2097 else if (printok)
2099 cd->Flags |= COPYFLAG_DONE;
2101 if (!(cd->Flags & COPYFLAG_QUIET))
2103 if ((cd->Flags & COPYFLAG_VERBOSE))
2105 Printf("%s\n", printok);
2109 SetData(cd->DestName, cd);
2112 if (lock)
2114 UnLock(lock);
2119 LONG CopyFile(BPTR from, BPTR to, ULONG bufsize, struct CopyData *cd)
2121 STRPTR buffer;
2122 LONG s, err = 0;
2124 if (cd->CopyBuf)
2126 buffer = cd->CopyBuf;
2127 bufsize = cd->CopyBufLen;
2129 else
2133 buffer = (STRPTR)AllocMem(bufsize, MEMF_PUBLIC);
2134 if (buffer)
2136 cd->CopyBuf = buffer;
2137 cd->CopyBufLen = bufsize;
2138 break;
2141 bufsize >>= 1;
2143 } while (bufsize >= 512);
2146 if (buffer)
2148 #if USE_BOGUSEOFWORKAROUND
2149 struct FileInfoBlock *fib = (struct FileInfoBlock *) buffer; /* NOTE: bufsize is min 512 bytes */
2151 if (ExamineFH(from, fib))
2153 #warning "****** WARNING: No largefile support! ******"
2154 ULONG filesize = fib->fib_Size, copied = 0;
2156 /*Printf("filesize: %lu\n", filesize);*/
2160 ULONG brk = CTRL_C;
2161 if (brk || (s = Read(from, buffer, bufsize)) == -1 || Write(to, buffer, s) != s)
2163 if (brk)
2165 cd->IoErr = ERROR_BREAK;
2167 err = RETURN_FAIL;
2168 break;
2170 else if (s == 0 && copied < filesize)
2172 /* premature EOF with buggy fs */
2173 err = RETURN_FAIL;
2174 break;
2177 copied += s;
2179 } while (copied < filesize);
2181 /*{ LONG ioerr = IoErr();
2182 Printf("copied %lu/%lu\n", copied, filesize);
2183 SetIoErr(ioerr);}*/
2185 else
2186 #endif /* USE_BOGUSEOFWORKAROUND */
2188 /* Stream or so, copy until EOF or error */
2191 ULONG brk = CTRL_C;
2192 /* AROS: This flush appears to be required if reading from '*'
2193 * Maybe a bug in Read(), or AROS buffering?
2195 Flush(from);
2196 if (brk || (s = Read(from, buffer, bufsize)) == -1 || Write(to, buffer, s) != s)
2198 if (brk)
2200 cd->IoErr = ERROR_BREAK;
2202 err = RETURN_FAIL;
2203 break;
2205 } while (s > 0);
2208 /* Freed at exit to avoid fragmentation */
2209 /*FreeMem(buffer, bufsize);*/
2211 else
2213 err = RETURN_FAIL;
2216 return err;
2220 /* Softlink's path starts always with device name! f.e. "Ram Disk:T/..." */
2221 LONG LinkFile(BPTR from, STRPTR to, ULONG soft, struct CopyData *cd)
2223 if (soft)
2225 LONG ret = FALSE;
2226 UBYTE *name;
2228 name = AllocMem(FILEPATH_SIZE, MEMF_ANY);
2229 if (name)
2231 if (NameFromLock(from, name, FILEPATH_SIZE))
2233 ret = MakeLink(to, (SIPTR)name, LINK_SOFT);
2236 FreeMem(name, FILEPATH_SIZE);
2239 return ret;
2241 else
2243 return MakeLink(to, (SIPTR)from, LINK_HARD);
2248 /* return 0 means no loop, return != 0 means loop found */
2249 ULONG TestLoop(BPTR srcdir, BPTR destdir, struct CopyData *cd)
2251 ULONG loop = 0;
2252 BPTR par, lock;
2254 lock = destdir;
2256 if (SameDevice(srcdir, destdir))
2260 if (!SameLock(srcdir, lock))
2262 loop = 1;
2264 else
2266 par = ParentDir(lock);
2268 if (lock != destdir)
2270 UnLock(lock);
2273 lock = par;
2276 while(!loop && lock);
2279 if (lock != destdir)
2281 UnLock(lock);
2284 return loop;
2288 void SetData(STRPTR name, struct CopyData *cd)
2290 if (cd->Flags & COPYFLAG_NOPRO)
2292 /* Is already set! - Piru */
2293 //SetProtection(name, 0);
2295 else
2297 SetProtection(name, cd->Fib.fib_Protection & (ULONG) ~FIBF_ARCHIVE);
2300 if (cd->Flags & COPYFLAG_DATES)
2302 SetFileDate(name, &cd->Fib.fib_Date);
2305 if (cd->Flags & COPYFLAG_COMMENT)
2307 SetComment(name, cd->Fib.fib_Comment);
2312 LONG TestDest(STRPTR name, ULONG type, struct CopyData *cd)
2314 LONG ret = TESTDEST_ERROR;
2315 BPTR lock;
2317 if ((lock = Lock(name, SHARED_LOCK)))
2319 struct FileInfoBlock *fib;
2321 if ((fib = (struct FileInfoBlock *)AllocDosObject(DOS_FIB, NULL)))
2323 if (Examine(lock, fib))
2325 UnLock(lock);
2326 lock = 0;
2328 if (type)
2330 if (fib->fib_DirEntryType > 0)
2332 ret = TESTDEST_DIR_OK;
2334 else if (!(cd->Flags & COPYFLAG_DONTOVERWRITE))
2336 if (KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE, cd))
2338 ret = TESTDEST_DELETED;
2341 else
2343 ret = TESTDEST_CANTDELETE;
2346 else if (cd->Flags & COPYFLAG_DONTOVERWRITE)
2348 if (cd->Flags & COPYFLAG_NEWER)
2350 if (CheckVersion( cd ) == CHECKVER_DESTOLDER )
2352 if (KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE, cd))
2354 ret = TESTDEST_DELETED;
2357 else
2359 ret = TESTDEST_CANTDELETE;
2362 else /* normal "dont overwrite mode" */
2364 ret = TESTDEST_CANTDELETE;
2367 else if (KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE, cd))
2369 ret = TESTDEST_DELETED;
2373 FreeDosObject(DOS_FIB, fib);
2376 if (lock)
2378 UnLock(lock);
2381 else
2383 ret = TESTDEST_NONE;
2386 if (ret == TESTDEST_CANTDELETE)
2388 cd->IoErr = ERROR_OBJECT_EXISTS;
2390 else if (ret == TESTDEST_ERROR)
2392 cd->IoErr = IoErr();
2395 return ret;
2399 ** We compare current file versions and return the result
2400 ** see CHECKVER_#? values
2403 static LONG CheckVersion( struct CopyData *cd )
2405 struct VersionData vds;
2406 struct VersionData vdd;
2407 LONG resversion = CHECKVER_EQUAL;
2408 LONG resdate = CHECKVER_EQUAL;
2410 if( VersionFind( cd->FileName, &vds, cd ) )
2412 if( VersionFind( cd->DestName, &vdd, cd ) )
2414 /* version and revision must be available to ensure a proper operation */
2415 if( ((vdd.vd_Version != -1) && (vds.vd_Version != -1) && (vdd.vd_Revision != -1) && (vds.vd_Revision != -1)) )
2417 /* first we make the stuff comparable. If one component is missing we reset both */
2418 if( vdd.vd_Year == -1 || vds.vd_Year == -1 )
2420 vdd.vd_Year = 0;
2421 vds.vd_Year = 0;
2423 if( vdd.vd_Month == -1 || vds.vd_Month == -1 )
2425 vdd.vd_Month = 0;
2426 vds.vd_Month = 0;
2428 if( vdd.vd_Day == -1 || vds.vd_Day == -1 )
2430 vdd.vd_Day = 0;
2431 vds.vd_Day = 0;
2434 /* check version */
2435 resversion = CHECKVER_DESTOLDER;
2436 if( ((vdd.vd_Version == vds.vd_Version) && vdd.vd_Revision == vds.vd_Revision ) )
2438 resversion = CHECKVER_EQUAL;
2440 else if( (vdd.vd_Version > vds.vd_Version) ||
2441 ((vdd.vd_Version == vds.vd_Version) && vdd.vd_Revision > vds.vd_Revision ) )
2443 resversion = CHECKVER_DESTNEWER;
2445 /* check date */
2447 resdate = CHECKVER_DESTOLDER;
2448 if( ((vdd.vd_Year == vds.vd_Year) && (vdd.vd_Month == vds.vd_Month) && (vdd.vd_Day == vds.vd_Day) ) )
2450 resdate = CHECKVER_EQUAL;
2452 else
2454 if( ( (vdd.vd_Year > vds.vd_Year ) ||
2455 ( (vdd.vd_Year == vds.vd_Year) && (vdd.vd_Month > vds.vd_Month ) ) ||
2456 ( (vdd.vd_Year == vds.vd_Year) && (vdd.vd_Month == vds.vd_Month ) && (vdd.vd_Day > vds.vd_Day ) ) ))
2458 resdate = CHECKVER_DESTNEWER;
2462 /* plausible check */
2463 if( ((resversion == CHECKVER_DESTNEWER) && (resdate == CHECKVER_DESTOLDER)) || /* newer version with older date */
2464 ((resversion == CHECKVER_DESTOLDER) && (resdate == CHECKVER_DESTNEWER)) ) /* older version with newer date */
2466 /* we maybe should inform the user about this */
2467 return( CHECKVER_EQUAL );
2472 /* compose result */
2474 if( (resversion == resdate) || (resversion == CHECKVER_EQUAL) )
2476 return( resdate );
2478 else
2480 return( resversion );
2487 ** Searches the given file for a version string and fills version data struct with the result.
2488 ** Returns false if no version was found. Returns true if the version got parsed and version data
2489 ** is valid.
2492 #define VERSBUFFERSIZE 4096 /* must be as big as the biggest version string we want to handle. */
2494 static BOOL VersionFind( CONST_STRPTR path, struct VersionData *vds, struct CopyData *cd)
2496 BPTR handle;
2497 STRPTR buf;
2498 ULONG i, rc;
2500 rc = FALSE;
2502 if ( (buf = AllocVec(VERSBUFFERSIZE, MEMF_PUBLIC | MEMF_CLEAR)) )
2504 if ( (handle = Open(path, MODE_OLDFILE)) )
2506 long index = 0;
2508 while( ( (index += Read(handle, &buf[index], VERSBUFFERSIZE-index)) > 5) && !rc )
2510 for (i = 0; i < index-5; i++) {
2511 if( buf[i] == '$' && buf[i+1] == 'V' && buf[i+2] == 'E' && buf[i+3] == 'R' && buf[i+4] == ':' ) {
2512 CopyMem( &buf[i], buf, index-i );
2513 index -= i;
2514 (index += Read(handle, &buf[index], VERSBUFFERSIZE-index));
2515 /* now the version string is aligned and complete in buffer */
2516 makeversionfromstring( buf, vds, cd );
2517 rc = TRUE;
2518 break;
2521 CopyMem( &buf[index-5], &buf[0], 5 );
2522 index = 5;
2524 Close(handle);
2526 FreeVec(buf);
2528 return (rc);
2533 ** This function extracts the version information from a given version string.
2534 ** the result will be store in the given version data structure.
2536 ** NOTE: There is no need to preset the version data struct. All fields will
2537 ** be reset to defaults, so in case of a faulty version string the result data
2538 ** will be checkable.
2541 static
2542 void makeversionfromstring( STRPTR buffer, struct VersionData *vd, struct CopyData *cd)
2544 LONG pos;
2545 ULONG tmp;
2546 STRPTR name;
2548 /* reset data field */
2550 vd->vd_Name[0] = '\0';
2551 vd->vd_Version = -1;
2552 vd->vd_Revision = -1;
2553 vd->vd_Day = -1;
2554 vd->vd_Month = -1;
2555 vd->vd_Year = -1;
2557 buffer = skipspaces( buffer ); /* skip before $VER: */
2558 buffer = skipnonspaces( buffer ); /* skip $VER: */
2559 buffer = skipspaces( buffer ); /* skip spaces before tool name */
2560 name = buffer;
2561 buffer = skipnonspaces( buffer ); /* skip name of tool */
2563 if( (tmp = ((long) buffer - (long) name) ) && *buffer )
2565 CopyMem( name, vd->vd_Name, MIN( tmp, VDNAMESIZE-1) );
2566 vd->vd_Name[MIN( tmp, VDNAMESIZE-1)] = '\0'; /* terminate name string inside target buffer */
2568 buffer = skipspaces( buffer ); /* skip spaces before version */
2569 if( *buffer ) {
2571 /* Do version */
2573 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2575 vd->vd_Version = tmp;
2577 /* Do revision */
2579 buffer += pos;
2580 buffer = skipspaces(buffer);
2581 if (*buffer++ == '.')
2583 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2585 vd->vd_Revision = tmp;
2586 buffer += pos;
2587 buffer = skipspaces(buffer);
2588 if (*buffer++ == '(')
2590 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2592 vd->vd_Day = tmp;
2593 buffer += pos;
2594 if (*buffer++ == '.')
2596 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2598 vd->vd_Month = tmp;
2599 buffer += pos;
2600 if (*buffer++ == '.')
2602 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2604 if( (tmp >= 70) && (tmp <= 99) )
2606 tmp += 1900;
2608 if( (tmp < 70) )
2610 tmp += 2000;
2612 vd->vd_Year = tmp;
2626 /* Return a pointer to a string, stripped by all leading space characters
2627 * (SPACE).
2629 static
2630 STRPTR skipspaces( STRPTR buffer)
2632 for (;; buffer++)
2634 if (buffer[0] == '\0' || buffer[0] != ' ')
2636 return( buffer );
2641 /* Return a pointer to a string, stripped by all non space characters
2642 * (SPACE).
2644 static
2645 STRPTR skipnonspaces( STRPTR buffer)
2647 for (;; buffer++)
2649 if (buffer[0] == '\0' || buffer[0] == ' ')
2651 return( buffer );