gcc specsfile: *lib: add -lllibinit -lautoinit
[AROS.git] / workbench / c / Copy.c
blobc891167072166b2e95669d8fc9b73978680156b9
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 -- do never overwrite destination
60 FORCE -- DO NOT USE. Call compatibility only.
61 NEWER -- compare version strings and only overwrites older files.
64 More detailed descriptions:
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 3.3.2001 -- AROSified by Johan 'S.Duvan' Alfredsson
290 29.7.2002 -- Fixed silly IoErr trashing bug, bumped to 50.1 - Piru
291 7.11.2002 -- Fixed even silier bug where it would put out a bogus
292 errormessage when the copy destination was a volume
293 root. bumped to 50.2 - Piru
294 6.3.2007 -- Fixed small signed bug. bumped to 50.15 - Geit
295 7.3.2006 -- Implemented new copy mode named "NEWER" which scans for
296 version information and only copies over a file if it is
297 newer than an existing target. bumped to 50.16 - Geit
299 ******************************************************************************/
301 #define CTRL_C (SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
303 #define DEBUG 0
304 #define D(x)
306 /* Enabled softlinks check for testing. Define this to 0 in case of problems.
307 Pavel Fedin <sonic_amiga@rambler.ru> */
308 #define USE_SOFTLINKCHECK 1
310 #define USE_ALWAYSVERBOSE 1
311 #define USE_BOGUSEOFWORKAROUND 0
314 #include <aros/asmcall.h>
315 #include <exec/devices.h>
316 #include <exec/io.h>
317 #include <exec/memory.h>
318 #include <exec/semaphores.h>
319 #include <exec/types.h>
320 #include <dos/exall.h>
322 #ifdef __SASC
323 typedef ULONG IPTR;
324 #else /* __SASC */
325 #include <aros/debug.h>
326 #endif /* __SASC */
328 #include <proto/dos.h>
329 #include <proto/exec.h>
331 #include <string.h>
333 const TEXT version[] = "\0$VER: Copy 50.16 (7.3.2007)";
335 static const UBYTE *PARAM =
336 "FROM/M,TO,PAT=PATTERN/K,BUF=BUFFER/K/N,ALL/S,"
337 "DIRECT/S,CLONE/S,DATES/S,NOPRO/S,COM=COMMENT/S,"
338 "QUIET/S,"
339 #if !USE_ALWAYSVERBOSE
340 "VERBOSE/S,"
341 #endif
342 "NOREQ/S,ERRWARN/S,MAKEDIR/S,"
343 "MOVE/S,DELETE/S,HARD=HARDLINK/S,SOFT=SOFTLINK/S,"
344 "FOLNK=FORCELINK/S,FODEL=FORCEDELETE/S,"
345 "FOOVR=FORCEOVERWRITE/S,DONTOVR=DONTOVERWRITE/S,"
346 "FORCE/S,NEWER/S";
348 #define COPYFLAG_ALL (1<<0)
349 #define COPYFLAG_DATES (1<<1)
350 #define COPYFLAG_NOPRO (1<<2)
351 #define COPYFLAG_COMMENT (1<<3)
352 #define COPYFLAG_FORCELINK (1<<4)
353 #define COPYFLAG_FORCEDELETE (1<<5)
354 #define COPYFLAG_FORCEOVERWRITE (1<<6)
355 #define COPYFLAG_DONTOVERWRITE (1<<7)
356 #define COPYFLAG_QUIET (1<<8)
357 #define COPYFLAG_VERBOSE (1<<9)
358 #define COPYFLAG_ERRWARN (1<<10)
359 #define COPYFLAG_PROTECTION (1<<11)
360 #define COPYFLAG_NEWER (1<<12)
362 #define COPYFLAG_SOFTLINK (1<<20) /* produce softlinks */
363 #define COPYFLAG_DEST_FILE (1<<21) /* one file mode */
364 #define COPYFLAG_DONE (1<<22) /* did something in DoWork */
365 #define COPYFLAG_ENTERSECOND (1<<23) /* entered directory second time */
367 #define COPYFLAG_SRCNOFILESYS (1<<24) /* source is no filesystem */
368 #define COPYFLAG_DESNOFILESYS (1<<25) /* destination is no filesystem */
370 #define COPYMODE_COPY 0
371 #define COPYMODE_MOVE 1
372 #define COPYMODE_DELETE 2
373 #define COPYMODE_MAKEDIR 3
374 #define COPYMODE_LINK 4
376 #define PRINTOUT_SIZE 50 /* maximum size of name printout */
377 #define PRINTOUT_SPACES 10 /* maximum number of spaces */
379 #define FILEPATH_SIZE 2048 /* maximum size of filepaths */
381 /* return values */
382 #define TESTDEST_DIR_OK 2 /* directory exists, go in */
383 #define TESTDEST_DELETED 1 /* file or empty directory deleted */
384 #define TESTDEST_NONE 0 /* nothing existed */
385 #define TESTDEST_ERROR -1 /* an error occured */
386 #define TESTDEST_CANTDELETE -2 /* deletion not allowed (DONTOV) */
388 struct IptrArgs
390 IPTR from;
391 IPTR to;
392 IPTR pattern;
393 IPTR buffer;
394 IPTR all;
395 IPTR direct;
396 IPTR clone;
397 IPTR dates;
398 IPTR nopro;
399 IPTR comment;
400 IPTR quiet;
401 #if !USE_ALWAYSVERBOSE
402 IPTR verbose;
403 #endif
404 IPTR noreq;
405 IPTR errwarn;
406 IPTR makedir;
407 IPTR move_mode;
408 IPTR delete_mode;
409 IPTR hardlink;
410 IPTR softlink;
411 IPTR forcelink;
412 IPTR forcedelete;
413 IPTR forceoverwrite;
414 IPTR dontoverwrite;
415 IPTR force;
416 IPTR newer;
420 struct Args
422 STRPTR *from;
423 STRPTR to;
424 STRPTR pattern;
425 LONG *buffer;
426 LONG all;
427 LONG direct;
428 LONG clone;
429 LONG dates;
430 LONG nopro;
431 LONG comment;
432 LONG quiet;
433 LONG verbose;
434 LONG noreq;
435 LONG errwarn;
436 LONG makedir;
437 LONG move_mode;
438 LONG delete_mode;
439 LONG hardlink;
440 LONG softlink;
441 LONG forcelink;
442 LONG forcedelete;
443 LONG forceoverwrite;
444 LONG dontoverwrite;
445 LONG force;
446 LONG newer;
450 struct CopyData
452 struct ExecBase *SysBase;
453 struct DosLibrary *DOSBase;
455 ULONG Flags;
456 ULONG BufferSize;
457 STRPTR Pattern;
458 BPTR Destination;
459 BPTR CurDest; /* Current Destination */
460 ULONG DestPathSize;
461 struct FileInfoBlock Fib;
462 UBYTE Mode;
463 UBYTE RetVal; /* when set, error output is already done */
464 UBYTE RetVal2; /* when set, error output must be done */
465 UBYTE Deep;
466 UBYTE FileName[FILEPATH_SIZE];
467 UBYTE DestName[FILEPATH_SIZE];
469 STRPTR CopyBuf;
470 ULONG CopyBufLen;
474 ** This data keeps the extracted data from a version string
477 #define VDNAMESIZE 96
479 struct VersionData {
480 UBYTE vd_Name[VDNAMESIZE];
481 LONG vd_Version;
482 LONG vd_Revision;
484 LONG vd_Day;
485 LONG vd_Month;
486 LONG vd_Year;
489 #ifndef MIN
490 #define MIN(a,b) ((a)<(b)?(a):(b))
491 #endif
493 #define CHECKVER_DESTOLDER -1
494 #define CHECKVER_DESTNEWER 1
495 #define CHECKVER_EQUAL 0
497 #define TEXT_READ texts[0]
498 #define TEXT_COPIED texts[1]
499 #define TEXT_MOVED texts[2]
500 #define TEXT_DELETED texts[3]
501 #define TEXT_LINKED texts[4]
502 #define TEXT_RENAMED texts[5]
503 #define TEXT_CREATED texts[6]
504 #define TEXT_ENTERED texts[7]
505 #define TEXT_OPENED_FOR_OUTPUT texts[8]
506 #define TEXTNUM_MODE 9
507 #define TEXT_DIRECTORY texts[15]
508 #define TEXT_NOT_DONE texts[16]
509 #define TEXT_NOTHING_DONE texts[17]
510 #define TEXT_ERR_FORCELINK texts[18]
511 #define TEXT_ERR_DELETE_DEVICE texts[19]
512 #define TEXT_ERR_DEST_DIR texts[20]
513 #define TEXT_ERR_INFINITE_LOOP texts[21]
514 #define TEXT_ERR_WILDCARD_DEST texts[22]
516 const CONST_STRPTR texts[] =
518 "read.",
519 "copied.",
520 "moved.",
521 "deleted.",
522 "linked.",
523 "renamed.",
524 " [created]",
525 "entered",
526 "opened for output",
527 "COPY mode\n",
528 "MOVE mode\n",
529 "DELETE mode\n",
530 "MAKEDIR mode\n",
531 "HARDLINK mode\n",
532 "SOFTLINK mode\n",
533 "%s (Dir)", /* output of directories */
534 " not %s: ",
535 "No file was processed.\n",
536 "FORCELINK keyword required.\n",
537 "A device cannot be deleted.",
538 "Destination must be a directory.\n",
539 "Infinite loop not allowed.\n",
540 "Wildcard destination invalid.\n",
543 LONG CopyFile(BPTR, BPTR, ULONG, struct CopyData *);
544 void DoWork(STRPTR, struct CopyData *);
545 LONG IsMatchPattern(STRPTR name, struct CopyData *cd);
546 LONG IsPattern(STRPTR, struct CopyData *); /* return 0 -> NOPATTERN, return -1 --> ERROR */
547 LONG KillFile(STRPTR, ULONG, struct CopyData *);
548 LONG KillFileKeepErr(STRPTR name, ULONG doit, struct CopyData *);
549 LONG LinkFile(BPTR, STRPTR, ULONG, struct CopyData *);
550 BPTR OpenDestDir(STRPTR, struct CopyData *);
551 void PatCopy(STRPTR, struct CopyData *);
552 void PrintName(CONST_STRPTR, ULONG, ULONG, ULONG, struct CopyData *);
553 void PrintNotDone(CONST_STRPTR, CONST_STRPTR, ULONG, ULONG, struct CopyData *);
554 ULONG TestFileSys(STRPTR, struct CopyData *); /* returns value, when is a filesystem */
555 void SetData(STRPTR, struct CopyData *);
556 LONG TestDest(STRPTR, ULONG, struct CopyData *);
557 ULONG TestLoop(BPTR, BPTR, struct CopyData *);
558 static LONG CheckVersion( struct CopyData *cd );
559 static void makeversionfromstring( STRPTR buffer, struct VersionData *vd, struct CopyData *cd);
560 static STRPTR skipspaces( STRPTR buffer);
561 static STRPTR skipnonspaces( STRPTR buffer);
562 static BOOL VersionFind( CONST_STRPTR path, struct VersionData *vds, struct CopyData *cd);
564 __startup static AROS_ENTRY(int, Start,
565 AROS_UFHA(char *, argstr, A0),
566 AROS_UFHA(ULONG, argsize, D0),
567 struct ExecBase *, SysBase)
569 AROS_USERFUNC_INIT
571 struct DosLibrary *DOSBase;
572 struct Process *task;
573 struct CopyData *cd;
574 int retval = RETURN_FAIL;
576 /* test for WB and reply startup-message */
577 if (!(task = (struct Process *)FindTask(NULL))->pr_CLI)
579 WaitPort(&task->pr_MsgPort);
580 Forbid();
581 ReplyMsg(GetMsg(&task->pr_MsgPort));
583 return RETURN_FAIL;
586 DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 37);
587 cd = AllocMem(sizeof(*cd), MEMF_PUBLIC | MEMF_CLEAR);
589 if (DOSBase && cd)
591 STRPTR a[2] = { "", 0 };
592 struct RDArgs *rda;
593 struct IptrArgs iArgs;
594 struct Args args = {};
596 cd->SysBase = SysBase;
597 cd->DOSBase = DOSBase;
598 /* FIXME: Remove these #define xxxBase hacks
599 Do not use this in new code !
601 #define SysBase cd->SysBase
602 #define DOSBase cd->DOSBase
604 cd->BufferSize = 512*1024;
605 cd->Mode = COPYMODE_COPY;
606 cd->RetVal2 = RETURN_FAIL;
607 cd->Deep = 1;
609 memset(&iArgs, 0, sizeof(struct IptrArgs));
611 rda = (struct RDArgs *)AllocDosObject(DOS_RDARGS, NULL);
612 if (rda)
614 rda->RDA_ExtHelp =
615 "FROM multiple input files\n"
616 "TO destination file or directory\n"
617 "PATTERN a pattern the filenames must match\n"
618 "BUFFER buffersize for copy buffer (default 200 [100K])\n"
619 "ALL deep scan into sub directories\n"
620 "DIRECT copy/delete only: work without any tests or options\n"
621 "CLONE copy comment, protection bits and date as well\n"
622 "DATES copy dates\n"
623 "NOPRO do not copy protection bits\n"
624 "COMMENT copy filecomment\n"
625 "QUIET suppress all output and requesters\n"
626 #if !USE_ALWAYSVERBOSE
627 "VERBOSE give additional output\n"
628 #endif
629 "NOREQ suppress requesters\n"
630 "ERRWARN do not proceed, when one file failed\n"
631 "MAKEDIR produce directories\n"
632 "MOVE delete source files after copying successful\n"
633 "DELETE do not copy, but delete the source files\n"
634 "HARDLINK make a hardlink to source instead of copying\n"
635 "SOFTLINK make a softlink to source instead of copying\n"
636 "FOLNK also makes links to directories\n"
637 "FODEL delete protected files also\n"
638 "FOOVR also overwrite protected files\n"
639 "DONTOVR do never overwrite destination\n"
640 "FORCE DO NOT USE. Call compatibility only.\n"
641 "NEWER will compare version strings and only overwrites older files\n";
643 if (ReadArgs(PARAM, (IPTR *)&iArgs, rda))
645 ULONG patbufsize = 0;
646 LONG i = 0;
647 APTR win = task->pr_WindowPtr;
649 args.from = (STRPTR *)iArgs.from;
650 args.to = (STRPTR)iArgs.to;
651 args.pattern = (STRPTR)iArgs.pattern;
652 args.buffer = (LONG *)iArgs.buffer;
653 args.all = (LONG)iArgs.all;
654 args.direct = (LONG)iArgs.direct;
655 args.clone = (LONG)iArgs.clone;
656 args.dates = (LONG)iArgs.dates;
657 args.nopro = (LONG)iArgs.nopro;
658 args.comment = (LONG)iArgs.comment;
659 args.quiet = (LONG)iArgs.quiet;
660 #if USE_ALWAYSVERBOSE
661 args.verbose = FALSE;
662 #else
663 args.verbose = (LONG)iArgs.verbose;
664 #endif
665 args.noreq = (LONG)iArgs.noreq;
666 args.errwarn = (LONG)iArgs.errwarn;
667 args.makedir = (LONG)iArgs.makedir;
668 args.move_mode = (LONG)iArgs.move_mode;
669 args.delete_mode = (LONG)iArgs.delete_mode;
670 args.hardlink = (LONG)iArgs.hardlink;
671 args.softlink = (LONG)iArgs.softlink;
672 args.forcelink = (LONG)iArgs.forcelink;
673 args.forcedelete = (LONG)iArgs.forcedelete;
674 args.forceoverwrite = (LONG)iArgs.forceoverwrite;
675 args.dontoverwrite = (LONG)iArgs.dontoverwrite;
676 args.force = (LONG)iArgs.force;
677 args.newer = (LONG)iArgs.newer;
679 if (args.quiet) /* when QUIET, SILENT and NOREQ are also
680 true! */
682 /* Original doesn't hide requesters with QUIET */
683 /*args.noreq = 1;*/
684 args.verbose = FALSE;
687 if (args.buffer && *args.buffer > 0) /* minimum buffer size */
689 cd->BufferSize = *args.buffer * 512;
692 if (args.quiet)
694 cd->Flags |= COPYFLAG_QUIET;
697 #if !USE_ALWAYSVERBOSE
698 if (args.verbose)
700 cd->Flags |= COPYFLAG_VERBOSE;
702 #endif
703 if (args.all)
705 cd->Flags |= COPYFLAG_ALL;
708 /* 12-jul-03 bugfix: always copy protection flags! -Piru */
709 cd->Flags |= COPYFLAG_PROTECTION;
710 if (args.clone)
712 cd->Flags |= COPYFLAG_DATES | COPYFLAG_COMMENT | COPYFLAG_PROTECTION;
715 if (args.dates)
717 cd->Flags |= COPYFLAG_DATES;
720 if (args.comment)
722 cd->Flags |= COPYFLAG_COMMENT;
725 if (args.nopro)
727 cd->Flags |= COPYFLAG_NOPRO;
730 if (args.forcelink)
732 cd->Flags |= COPYFLAG_FORCELINK;
735 if (args.forcedelete)
737 cd->Flags |= COPYFLAG_FORCEDELETE;
740 if (args.forceoverwrite)
742 cd->Flags |= COPYFLAG_FORCEOVERWRITE;
745 if (args.dontoverwrite)
747 cd->Flags |= COPYFLAG_DONTOVERWRITE;
750 if (args.newer)
752 cd->Flags |= COPYFLAG_NEWER|COPYFLAG_DONTOVERWRITE;
755 if (args.errwarn)
757 cd->Flags |= COPYFLAG_ERRWARN;
760 if (args.force) /* support OS Delete and MakeLink command
761 options */
763 if (args.delete_mode)
765 cd->Flags |= COPYFLAG_FORCEDELETE;
768 if (args.hardlink || args.softlink)
770 cd->Flags |= COPYFLAG_FORCELINK;
774 if (!args.from) /* no args.from means currentdir */
776 args.from = a;
779 if (args.noreq) /* no dos.library requests allowed */
781 task->pr_WindowPtr = (APTR)-1;
784 if (args.delete_mode)
786 ++i;
787 cd->Mode = COPYMODE_DELETE;
790 if (args.move_mode)
792 ++i;
793 cd->Mode = COPYMODE_MOVE;
796 if (args.makedir)
798 ++i;
799 cd->Mode = COPYMODE_MAKEDIR;
802 if (args.hardlink)
804 ++i;
805 cd->Mode = COPYMODE_LINK;
808 if (args.softlink)
810 ++i;
811 cd->Mode = COPYMODE_LINK;
812 cd->Flags |= COPYFLAG_SOFTLINK | COPYFLAG_FORCELINK;
815 if (cd->Mode != COPYMODE_DELETE &&
816 cd->Mode != COPYMODE_MAKEDIR && !args.to)
818 if (*(args.from + 1)) /* when no TO is specified, the arg
819 is last */
820 { /* one of from. Copy this argument into */
821 STRPTR *a; /* args.to */
823 a = args.from;
825 while(*(++a))
828 args.to = *(--a);
829 *a = 0;
832 #if USE_ALWAYSVERBOSE
834 /* Only do this if quiet isn't set - bigfoot */
835 if (!args.quiet)
837 /* If more than two args, be verbose... - Piru */
838 if (args.from[0] && args.from[1])
840 args.verbose = TRUE;
841 cd->Flags |= COPYFLAG_VERBOSE;
844 /* If any of the sources is a pattern, be verbose... - Piru */
846 STRPTR *a;
848 for (a = args.from; *a; a++)
850 if (IsMatchPattern(*a, cd) != 0)
852 args.verbose = TRUE;
853 cd->Flags |= COPYFLAG_VERBOSE;
858 #endif
860 /* test if more than one of the above four or any other wrong
861 arguments */
863 if (i > 1 ||
864 (args.from == a && cd->Mode == COPYMODE_MAKEDIR) ||
865 (args.direct && (args.from == a || !*args.from ||
866 args.pattern ||
867 (cd->Flags & ~(COPYFLAG_QUIET | COPYFLAG_VERBOSE | COPYFLAG_ERRWARN)) ||
868 (cd->Mode != COPYMODE_DELETE && (cd->Mode != COPYMODE_COPY ||
869 !args.to || args.from[1])))) ||
870 (args.dontoverwrite && args.forceoverwrite) ||
871 /* (args.nopro && args.clone) ||*/ /* Ignore, like original - Piru */
872 (args.softlink && args.all) ||
873 (!args.to && cd->Mode != COPYMODE_DELETE && cd->Mode != COPYMODE_MAKEDIR))
875 SetIoErr(ERROR_TOO_MANY_ARGS);
877 else if (cd->Mode == COPYMODE_MAKEDIR)
879 LONG i;
880 BPTR dir;
881 cd->RetVal2 = RETURN_OK;
883 #if !USE_ALWAYSVERBOSE
884 if (args.verbose)
886 PutStr(texts[TEXTNUM_MODE + COPYMODE_MAKEDIR]);
888 #endif
890 while (!cd->RetVal && !cd->RetVal2 && *args.from)
892 if ((i = IsPattern(*args.from, cd)))
894 if (i != -1)
896 cd->RetVal = RETURN_ERROR;
898 if (!args.quiet)
900 PutStr(TEXT_ERR_WILDCARD_DEST);
903 else
905 cd->RetVal2 = RETURN_FAIL;
909 if ((dir = OpenDestDir(*args.from, cd)))
911 UnLock(dir);
912 cd->Flags |= COPYFLAG_DONE;
915 ++args.from;
917 } /* cd->Mode == COPYMODE_MAKEDIR */
918 else if (args.direct)
920 if (cd->Mode == COPYMODE_COPY)
922 BPTR in, out;
924 if ((in = Open(*args.from, MODE_OLDFILE)))
926 if ((out = Open(args.to, MODE_NEWFILE)))
928 cd->RetVal2 = CopyFile(in, out, cd->BufferSize, cd);
929 Close(out);
932 Close(in);
935 else /* COPYMODE_DELETE */
937 while (*args.from)
939 KillFile(*(args.from++), cd->Flags & COPYFLAG_FORCEDELETE, cd);
942 cd->RetVal2 = RETURN_OK;
945 else
947 if (args.pattern && *args.pattern)
949 patbufsize = (strlen(args.pattern) << 1) + 3;
951 if ((cd->Pattern = (STRPTR)AllocMem(patbufsize,
952 MEMF_ANY)))
954 if (ParsePatternNoCase(args.pattern, cd->Pattern,
955 patbufsize) < 0)
957 FreeMem(cd->Pattern, patbufsize);
958 cd->Pattern = 0;
963 if (1) // (cd->Fib = (struct FileInfoBlock *)AllocDosObject(DOS_FIB, NULL)))
965 #if !USE_ALWAYSVERBOSE
966 if (args.verbose)
968 PutStr(texts[TEXTNUM_MODE + cd->Mode +
969 (cd->Flags & COPYFLAG_SOFTLINK ? 1 : 0)]);
971 #endif
972 if (args.pattern && !cd->Pattern)
974 if (!*args.pattern)
976 SetIoErr(ERROR_BAD_TEMPLATE);
979 else if (cd->Mode == COPYMODE_DELETE)
981 cd->RetVal2 = RETURN_OK;
983 while (cd->RetVal <= (args.errwarn ? RETURN_OK : RETURN_WARN)
984 && *args.from)
986 PatCopy(*(args.from++), cd);
989 else if ((i = IsPattern(args.to, cd)))
991 if (i != -1)
993 cd->RetVal = RETURN_ERROR;
995 if (!args.quiet)
997 PutStr(TEXT_ERR_WILDCARD_DEST);
1001 else
1003 STRPTR path;
1005 if (*(path = PathPart(args.to)) == '/')
1007 ++path; /* is destination a path description ? */
1010 if (*path && !*(args.from+1) &&
1011 !(i = IsMatchPattern(*args.from, cd)))
1013 BPTR lock;
1014 LONG lockioerr;
1016 /* is destination an existing directory */
1017 if ((lock = Lock(args.to, SHARED_LOCK)))
1019 if (Examine(lock, &cd->Fib))
1021 if (cd->Fib.fib_DirEntryType > 0)
1023 cd->RetVal2 = RETURN_OK;
1026 /* indicate dir-mode for next if */
1028 else
1030 i = 1;
1033 UnLock(lock);
1036 /* Some magic to handle tick quoted pattern object names. Quite crude way to
1037 * handle it, but I couldn't think of anything better. - Piru
1039 #if 1
1040 if (!i && cd->RetVal2 && !IsMatchPattern(*args.from, cd))
1042 ULONG len;
1043 STRPTR pat;
1045 //Printf("pattern check <%s>\n", *args.from);
1047 len = (strlen(*args.from) << 1) + 3;
1049 if ((pat = (STRPTR)AllocMem(len,
1050 MEMF_ANY)))
1052 if (ParsePattern(*args.from, pat, len) > -1 &&
1053 strlen(pat) <= strlen(*args.from))
1055 lock = Lock(pat, SHARED_LOCK);
1056 if (lock)
1058 UnLock(lock);
1060 strcpy(*args.from, pat);
1064 FreeMem(pat, len);
1067 #endif
1069 /* is source a directory */
1070 if (!i && cd->RetVal2 &&
1071 (lock = Lock(*args.from, SHARED_LOCK)))
1073 if (Examine(lock, &cd->Fib))
1075 cd->RetVal2 = RETURN_OK;
1076 if (cd->Mode != COPYMODE_COPY ||
1077 cd->Fib.fib_DirEntryType < 0)
1079 UBYTE sep;
1081 cd->Flags |= COPYFLAG_DEST_FILE;
1083 /* produce missing destination directories */
1084 sep = *path;
1085 *path = 0;
1087 if ((cd->CurDest = OpenDestDir(args.to, cd)))
1089 *path = sep;
1091 /* do the job */
1092 UnLock(lock);
1093 lock = 0;
1094 CopyMem(*args.from, cd->FileName,
1095 1 + strlen(*args.from));
1096 DoWork(FilePart(args.to), cd); /* on file call */
1097 UnLock(cd->CurDest);
1102 if (lock)
1104 UnLock(lock);
1108 lockioerr = IoErr(); /* We save ioerr here, because TestFileSys changes it */
1110 if (lock == 0 && cd->Mode == COPYMODE_COPY && !TestFileSys(*args.from, cd))
1112 UBYTE sep;
1113 cd->Flags |= COPYFLAG_DEST_FILE | COPYFLAG_SRCNOFILESYS;
1114 cd->RetVal2 = RETURN_OK;
1116 /* produce missing destination directories */
1117 sep = *path;
1118 *path = 0;
1120 if ((cd->CurDest = OpenDestDir(args.to, cd)))
1122 *path = sep;
1124 /* do the job */
1125 CopyMem(*args.from, cd->FileName, 1 + strlen(*args.from));
1126 DoWork(FilePart(args.to), cd); /* on file call */
1127 UnLock(cd->CurDest);
1130 else
1131 SetIoErr(lockioerr);
1133 else if (i != -1)
1135 cd->RetVal2 = RETURN_OK;
1138 if (!cd->RetVal && !cd->RetVal2 && !(cd->Flags & COPYFLAG_DEST_FILE) &&
1139 (cd->Destination = OpenDestDir(args.to, cd)))
1141 while (cd->RetVal <= (args.errwarn ? RETURN_OK : RETURN_WARN)
1142 && *args.from && !CTRL_C)
1144 PatCopy(*(args.from++), cd);
1147 UnLock(cd->Destination);
1149 } /* else */
1151 if (!(cd->Flags & COPYFLAG_DONE) && args.verbose &&
1152 !cd->RetVal && !cd->RetVal2)
1154 PutStr(TEXT_NOTHING_DONE);
1157 } /* if (1) */
1159 if (cd->Pattern)
1161 FreeMem(cd->Pattern, patbufsize);
1163 } /* else */
1165 task->pr_WindowPtr = win;
1167 FreeArgs(rda);
1168 } /* ReadArgs */
1170 FreeDosObject(DOS_RDARGS, rda);
1171 } /* AllocDosObject */
1173 if (!cd->RetVal2 && CTRL_C)
1175 SetIoErr(ERROR_BREAK);
1176 cd->RetVal2 = RETURN_WARN;
1179 if (cd->RetVal2 && !args.quiet && !cd->RetVal)
1181 PrintFault(IoErr(), NULL);
1184 if (cd->RetVal)
1186 cd->RetVal2 = cd->RetVal;
1189 if (args.errwarn && cd->RetVal2 == RETURN_WARN)
1191 cd->RetVal2 = RETURN_ERROR;
1194 if (cd->CopyBuf)
1196 FreeMem(cd->CopyBuf, cd->CopyBufLen);
1199 #undef SysBase
1200 #undef DOSBase
1202 if (cd)
1204 retval = cd->RetVal2;
1205 FreeMem(cd, sizeof(*cd));
1207 else if (DOSBase)
1209 PrintFault(IoErr(), NULL);
1211 if (DOSBase)
1213 CloseLibrary((struct Library *)DOSBase);
1215 return retval;
1217 AROS_USERFUNC_EXIT
1220 /* This code is pure and has library bases in explicitly allocated data structure */
1221 #define SysBase cd->SysBase
1222 #define DOSBase cd->DOSBase
1224 void PatCopy(STRPTR name, struct CopyData *cd)
1226 struct AnchorPath *APath;
1227 ULONG retval, doit = 0, deep = 0, failval = RETURN_WARN, first = 0;
1229 #if DEBUG
1230 Printf("PatCopy(%s, .)\n", name);
1231 #endif
1233 if ((cd->Mode == COPYMODE_COPY || (cd->Flags & COPYFLAG_ALL)) && !IsMatchPattern(name, cd))
1235 first = 1; /* enter first directory (support of old copy style) */
1238 if (cd->Flags & COPYFLAG_ERRWARN)
1240 failval = RETURN_OK;
1243 cd->CurDest = cd->Destination;
1244 cd->DestPathSize = 0;
1246 if (cd->Mode == COPYMODE_COPY && !TestFileSys(name, cd))
1248 cd->Flags |= COPYFLAG_SRCNOFILESYS;
1249 CopyMem(name, cd->FileName, 1 + strlen(name));
1250 DoWork(FilePart(name), cd);
1251 cd->Flags &= ~COPYFLAG_SRCNOFILESYS;
1253 return;
1256 if ((APath = (struct AnchorPath *)AllocMem(sizeof(struct AnchorPath) + FILEPATH_SIZE,
1257 MEMF_PUBLIC | MEMF_CLEAR)))
1259 int parentdirerr = 0;
1261 APath->ap_BreakBits = SIGBREAKF_CTRL_C;
1262 APath->ap_Strlen = FILEPATH_SIZE;
1264 for (retval = MatchFirst(name, APath);
1265 !retval && cd->RetVal <= failval && !cd->RetVal2;
1266 retval = MatchNext(APath)
1269 if (parentdirerr)
1271 //Printf("ParentDir() fuxored last round! Would copy next files to SYS: !\n");
1272 retval = IoErr();
1273 if (!retval)
1274 SetIoErr(retval = ERROR_INVALID_LOCK);
1275 break;
1278 if (doit)
1280 DoWork(cd->Fib.fib_FileName, cd);
1281 doit = 0;
1284 if (deep) /* used for Deep checking */
1286 ++cd->Deep;
1287 deep = 0;
1290 cd->Flags &= ~COPYFLAG_ENTERSECOND;
1292 CopyMem(APath->ap_Buf, cd->FileName, FILEPATH_SIZE);
1293 CopyMem(&APath->ap_Info, &cd->Fib, sizeof(struct FileInfoBlock));
1295 if (first && APath->ap_Info.fib_DirEntryType > 0)
1297 #if USE_ALWAYSVERBOSE
1298 /* If the source is a directory, be verbose - Piru */
1299 cd->Flags |= COPYFLAG_VERBOSE;
1300 #endif
1301 APath->ap_Flags |= APF_DODIR;
1303 else if (APath->ap_Flags & APF_DIDDIR)
1305 BPTR i;
1307 cd->Flags |= COPYFLAG_ENTERSECOND;
1308 APath->ap_Flags &= ~APF_DIDDIR;
1309 --cd->Deep;
1311 if (cd->Mode == COPYMODE_DELETE || cd->Mode == COPYMODE_MOVE)
1313 doit = 1;
1316 if ((i = cd->CurDest))
1318 cd->CurDest = ParentDir(i);
1319 cd->DestPathSize = 0;
1321 if (i != cd->Destination)
1323 UnLock(i);
1326 if (!cd->CurDest)
1328 parentdirerr = 1;
1329 continue;
1333 else if (APath->ap_Info.fib_DirEntryType > 0)
1335 doit = 1;
1337 if (cd->Flags & COPYFLAG_ALL)
1339 BOOL enter;
1341 #if USE_SOFTLINKCHECK
1343 #define BUFFERSIZE 512
1344 if (APath->ap_Info.fib_DirEntryType == ST_SOFTLINK)
1346 UBYTE *buffer = AllocMem(BUFFERSIZE, MEMF_PUBLIC);
1348 D(Printf("%s is a softlink\n", APath->ap_Info.fib_FileName));
1349 enter = FALSE;
1350 doit = FALSE;
1352 if (buffer)
1354 BPTR lock;
1355 struct DevProc *dvp = GetDeviceProc("", NULL);
1357 CurrentDir(APath->ap_Current->an_Lock);
1358 if (ReadLink(dvp->dvp_Port, APath->ap_Current->an_Lock, APath->ap_Info.fib_FileName, buffer, BUFFERSIZE - 1) > 0)
1360 BOOL link_ok = FALSE;
1362 buffer[BUFFERSIZE - 1] = '\0';
1363 D(Printf("Softlink target: %s\n", buffer));
1365 lock = Lock(buffer, SHARED_LOCK);
1366 if (lock)
1368 struct FileInfoBlock *fib = AllocDosObject(DOS_FIB, NULL);
1370 if (fib)
1372 if (Examine(lock, fib))
1374 link_ok = TRUE;
1375 D(Printf("Target type: %ld\n", fib->fib_DirEntryType));
1377 if (fib->fib_DirEntryType > 0)
1378 enter = TRUE;
1379 else
1381 * FIXME: This currently just prevents treating symlinks to files as
1382 * directories during copying.
1383 * DoWork() should be extended to handle symlinks correctly. BTW, how exactly ?
1385 doit = FALSE;
1387 FreeDosObject(DOS_FIB, fib);
1389 UnLock(lock);
1392 if (!link_ok)
1394 Printf("Warning: Skipping dangling softlink %s -> %s\n",
1395 APath->ap_Info.fib_FileName, buffer);
1397 FreeDeviceProc(dvp);
1399 FreeMem(buffer, BUFFERSIZE);
1402 else
1403 #endif /* USE_SOFTLINKCHECK */
1404 enter = TRUE;
1406 if (enter)
1408 APath->ap_Flags |= APF_DODIR;
1409 deep = 1;
1413 else if (!cd->Pattern || MatchPatternNoCase(cd->Pattern, APath->ap_Info.fib_FileName))
1415 doit = 1;
1418 first = 0;
1421 MatchEnd(APath);
1423 if (retval && retval != ERROR_NO_MORE_ENTRIES)
1425 LONG ioerr = IoErr();
1426 #if USE_ALWAYSVERBOSE
1427 cd->Flags |= COPYFLAG_VERBOSE;
1428 #endif
1429 Printf("%s - ", APath->ap_Info.fib_FileName);
1430 PrintFault(ioerr, NULL);
1431 SetIoErr(ioerr);
1433 cd->RetVal2 = RETURN_FAIL;
1436 if (doit)
1438 DoWork(cd->Fib.fib_FileName, cd);
1441 /* No need to clear the flags here, as they are cleared on next PatJoin
1442 call (DoWork is not called first round, as lock is zero!). */
1444 FreeMem(APath, sizeof(struct AnchorPath) + FILEPATH_SIZE);
1446 else
1448 cd->RetVal = RETURN_FAIL;
1450 if (!(cd->Flags & COPYFLAG_QUIET))
1452 PrintFault(ERROR_NO_FREE_STORE, NULL);
1456 if (cd->CurDest && cd->CurDest != cd->Destination)
1458 UnLock(cd->CurDest);
1463 LONG IsPattern(STRPTR name, struct CopyData *cd)
1465 LONG a, ret = -1;
1466 STRPTR buffer;
1468 a = (strlen(name) << 1) + 3;
1470 if ((buffer = (STRPTR)AllocMem(a, MEMF_ANY)))
1472 ret = ParsePattern(name, buffer, a);
1473 FreeMem(buffer, a);
1476 if (ret == -1)
1478 SetIoErr(ERROR_NO_FREE_STORE);
1481 return ret;
1485 LONG IsMatchPattern(STRPTR name, struct CopyData *cd)
1487 struct AnchorPath ap;
1488 LONG ret = -1;
1490 ap.ap_BreakBits = 0;
1491 ap.ap_Flags = APF_DOWILD;
1492 ap.ap_Strlen = 0;
1494 if (MatchFirst(name, &ap) == 0)
1496 ret = (ap.ap_Flags & APF_ITSWILD) ? TRUE : FALSE;
1498 MatchEnd(&ap);
1501 return ret;
1505 LONG KillFile(STRPTR name, ULONG doit, struct CopyData *cd)
1507 if (doit)
1509 SetProtection(name, 0);
1512 return DeleteFile(name);
1516 LONG KillFileKeepErr(STRPTR name, ULONG doit, struct CopyData *cd)
1518 LONG ret, ioerr;
1520 ioerr = IoErr();
1521 ret = KillFile(name, doit, cd);
1522 SetIoErr(ioerr);
1524 return ret;
1528 BPTR OpenDestDir(STRPTR name, struct CopyData *cd)
1530 LONG a, err = 0, cr = 0;
1531 BPTR dir;
1532 STRPTR ptr = name;
1533 UBYTE as;
1535 if ((cd->Mode == COPYMODE_COPY || cd->Mode == COPYMODE_MOVE) && !TestFileSys(name, cd))
1537 cd->Flags |= COPYFLAG_DESNOFILESYS;
1538 CopyMem(name, cd->DestName, 1 + strlen(name));
1540 return Lock("", SHARED_LOCK);
1543 while (!err && *ptr != 0)
1545 while (*ptr && *ptr != '/')
1547 ++ptr;
1550 as = *ptr;
1551 *ptr = 0;
1553 if ((a = TestDest(name, 1, cd)) == TESTDEST_CANTDELETE)
1555 if (!(cd->Flags & COPYFLAG_QUIET))
1557 PutStr(TEXT_ERR_DEST_DIR);
1560 err = 2;
1562 else if (a < 0)
1564 err = 1;
1566 else if (a != TESTDEST_DIR_OK)
1568 if ((dir = CreateDir(name)))
1570 ++cr;
1572 if ((cd->Flags & COPYFLAG_VERBOSE))
1574 PrintName(name, 1, 1, 1, cd);
1575 Printf("%s\n", TEXT_CREATED);
1578 UnLock(dir);
1580 else
1582 if (!(cd->Flags & COPYFLAG_QUIET))
1584 PrintNotDone(name, TEXT_CREATED, 1, 1, cd);
1587 err = 2;
1591 *ptr = as;
1593 /* 26-Oct-2003 bugfix: Don't scan past end of the string.
1594 * as is the old char, if '\0' we've reached the end of the
1595 * string. - Piru
1597 if (as)
1599 ptr++;
1603 if (err)
1605 cd->RetVal = RETURN_ERROR;
1607 if (!(cd->Flags & COPYFLAG_QUIET) && err == 1)
1609 PrintNotDone(name, TEXT_OPENED_FOR_OUTPUT, 1, 1, cd);
1612 return 0;
1615 if (cd->Mode == COPYMODE_MAKEDIR && !cr && !(cd->Flags & COPYFLAG_QUIET))
1617 SetIoErr(ERROR_OBJECT_EXISTS);
1618 PrintNotDone(name, TEXT_CREATED, 1, 1, cd);
1621 return Lock(name, SHARED_LOCK);
1625 void PrintName(CONST_STRPTR name, ULONG deep, ULONG dir, ULONG txt, struct CopyData *cd)
1627 #if 0
1628 deep %= PRINTOUT_SPACES; /* reduce number of spaces */
1629 /* This produces an error with MaxonC++ */
1630 #endif
1632 PutStr(" ");
1633 if (deep)
1635 while (--deep)
1637 PutStr(" ");
1641 if (dir)
1643 PutStr(" ");
1646 #if 0
1647 if ((deep = strlen(name)) > PRINTOUT_SIZE) /* reduce name size */
1649 name += deep-PRINTOUT_SIZE;
1650 PutStr("...");
1652 #endif
1654 Printf((dir ? TEXT_DIRECTORY : (STRPTR) "%s"), name);
1656 if (!dir && txt)
1658 PutStr("..");
1661 Flush(Output());
1665 void PrintNotDone(CONST_STRPTR name, CONST_STRPTR txt, ULONG deep, ULONG dir, struct CopyData *cd)
1667 #if !USE_ALWAYSVERBOSE
1668 if (name)
1670 PrintName(name, deep, dir, 1, cd);
1672 #endif
1674 Printf(TEXT_NOT_DONE, txt);
1675 PrintFault(IoErr(), NULL);
1679 /* returns value, when it seems to be a filesystem */
1680 ULONG TestFileSys(STRPTR name, struct CopyData *cd)
1682 STRPTR n = name;
1683 ULONG ret = 1;
1685 while (*n && *n != ':')
1687 ++n;
1690 if (*(n++) == ':')
1692 UBYTE a;
1694 a = *n;
1695 *n = 0;
1696 ret = IsFileSystem(name);
1697 *n = a;
1700 return ret;
1704 void DoWork(STRPTR name, struct CopyData *cd)
1706 BPTR pdir, lock = 0;
1707 CONST_STRPTR printerr = NULL, printok = "";
1709 #if DEBUG
1710 Printf("DoWork(%s, .)\n", name);
1711 #endif
1713 if (cd->RetVal > (cd->Flags & COPYFLAG_ERRWARN ? RETURN_OK : RETURN_WARN) || cd->RetVal2)
1715 #if DEBUG
1716 Printf("DoWork(RetVal %ld)\n", cd->RetVal);
1717 #endif
1718 return;
1721 if (cd->Mode != COPYMODE_DELETE && !(cd->Flags & COPYFLAG_DESNOFILESYS))
1723 if (!cd->DestPathSize)
1725 if (!NameFromLock(cd->CurDest, cd->DestName, FILEPATH_SIZE))
1727 cd->RetVal2 = RETURN_FAIL;
1729 #if DEBUG
1730 Printf("DoWork(NameFromLock RetVal %ld)\n", cd->RetVal);
1731 #endif
1733 return;
1736 cd->DestPathSize = strlen(cd->DestName);
1739 cd->DestName[cd->DestPathSize] = 0;
1740 AddPart(cd->DestName, name, FILEPATH_SIZE);
1743 if (cd->Flags & (COPYFLAG_SRCNOFILESYS|COPYFLAG_DESNOFILESYS))
1745 ULONG res = 0, kill = 1;
1746 BPTR in, out;
1747 CONST_STRPTR txt = TEXT_OPENED_FOR_OUTPUT;
1749 #if DEBUG
1750 Printf("Partly DIRECT mode active now (%s - %s)\n", cd->FileName,
1751 cd->DestName);
1752 #endif
1754 if ((out = Open(cd->DestName, MODE_NEWFILE)))
1756 txt = cd->Mode == COPYMODE_MOVE ? TEXT_MOVED : TEXT_COPIED;
1758 if ((in = Open(cd->FileName, MODE_OLDFILE)))
1760 ULONG h;
1762 h = CopyFile(in, out, cd->BufferSize, cd);
1763 Close(out); out = BNULL;
1764 Close(in);
1766 if (!h)
1768 kill = 0;
1770 if (cd->Mode == COPYMODE_MOVE)
1772 if (KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE, cd))
1774 res = 1;
1777 else
1779 res = 1;
1784 if (out)
1786 Close(out);
1789 if (kill)
1791 KillFileKeepErr(cd->DestName, 0, cd);
1795 if (!res && !(cd->Flags & COPYFLAG_QUIET))
1797 PrintNotDone(cd->Flags & COPYFLAG_VERBOSE ? name : 0,
1798 txt, cd->Deep, cd->Fib.fib_DirEntryType > 0, cd);
1800 else
1802 cd->Flags |= COPYFLAG_DONE;
1804 if ((cd->Flags & COPYFLAG_VERBOSE))
1806 Printf("%s\n", txt);
1810 #if DEBUG
1811 PutStr("DoWork(done)\n");
1812 #endif
1813 return;
1816 if (!(lock = Lock(cd->FileName, SHARED_LOCK)))
1818 cd->RetVal = RETURN_WARN;
1820 if (!(cd->Flags & COPYFLAG_QUIET))
1822 PrintNotDone(cd->Fib.fib_FileName, TEXT_READ, cd->Deep,
1823 cd->Fib.fib_DirEntryType > 0, cd);
1826 #if DEBUG
1827 Printf("DoWork(Lock RetVal %ld)\n", cd->RetVal);
1828 #endif
1829 return;
1832 if (!(pdir = ParentDir(lock)))
1834 cd->RetVal = RETURN_ERROR;
1836 if (cd->Mode == COPYMODE_DELETE)
1838 if (!(cd->Flags & COPYFLAG_QUIET))
1840 Printf(" %s ", cd->FileName);
1841 Printf(TEXT_NOT_DONE, TEXT_DELETED);
1842 Printf("%s\n", TEXT_ERR_DELETE_DEVICE);
1846 UnLock(lock);
1848 #if DEBUG
1849 Printf("DoWork(ParentDir %ld)\n", cd->RetVal);
1850 #endif
1851 return;
1854 UnLock(pdir);
1856 if (!(cd->Flags & COPYFLAG_QUIET))
1858 if ((cd->Flags & COPYFLAG_VERBOSE))
1860 PrintName(name, cd->Deep, cd->Fib.fib_DirEntryType > 0, cd->Fib.fib_DirEntryType < 0 ||
1861 (cd->Flags & COPYFLAG_ALL ? cd->Mode != COPYMODE_DELETE : cd->Mode != COPYMODE_COPY) ||
1862 cd->Flags & COPYFLAG_ENTERSECOND, cd);
1866 if ((cd->Flags & COPYFLAG_ENTERSECOND) || (cd->Mode == COPYMODE_DELETE &&
1867 (!(cd->Flags & COPYFLAG_ALL) || cd->Fib.fib_DirEntryType < 0)))
1869 UnLock(lock);
1870 lock = 0;
1872 if (KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE, cd))
1874 printok = TEXT_DELETED;
1876 else
1878 cd->RetVal = RETURN_WARN;
1879 printerr = TEXT_DELETED;
1882 else if (cd->Mode == COPYMODE_DELETE)
1886 else if (cd->Fib.fib_DirEntryType > 0)
1888 LONG a;
1890 if ((cd->Flags & COPYFLAG_ALL || cd->Mode == COPYMODE_LINK ||
1891 cd->Mode == COPYMODE_MOVE) && TestLoop(lock, cd->CurDest, cd))
1893 printok = 0;
1894 cd->RetVal = RETURN_ERROR;
1896 if (!(cd->Flags & COPYFLAG_QUIET))
1898 if (!(cd->Flags & COPYFLAG_VERBOSE))
1900 PrintName(name, cd->Deep, 1, 1, cd);
1903 Printf(TEXT_NOT_DONE, TEXT_ENTERED);
1904 PutStr(TEXT_ERR_INFINITE_LOOP);
1907 else if ((a = TestDest(cd->DestName, 1, cd)) < 0)
1909 printerr = TEXT_CREATED;
1910 cd->RetVal = RETURN_ERROR;
1912 else if (cd->Flags & COPYFLAG_ALL)
1914 BPTR i;
1916 i = cd->CurDest;
1917 cd->DestPathSize = 0;
1919 if (a == TESTDEST_DIR_OK)
1921 if (!(cd->CurDest = Lock(cd->DestName, SHARED_LOCK)))
1923 printerr = TEXT_ENTERED;
1924 cd->RetVal = RETURN_ERROR;
1926 else
1928 #if USE_ALWAYSVERBOSE
1929 printok = "";
1930 #else
1931 printok = TEXT_ENTERED;
1932 #endif
1935 else if ((cd->CurDest = CreateDir(cd->DestName)))
1937 UnLock(cd->CurDest);
1939 if ((cd->CurDest = Lock(cd->DestName, SHARED_LOCK)))
1941 printok = TEXT_CREATED;
1943 else
1945 printerr = TEXT_ENTERED;
1946 cd->RetVal = RETURN_ERROR;
1949 else
1951 printerr = TEXT_CREATED;
1952 cd->RetVal = RETURN_ERROR;
1955 if (!cd->CurDest)
1957 cd->CurDest = i;
1959 else if (i != cd->Destination)
1961 UnLock(i);
1964 else if (cd->Mode == COPYMODE_MOVE)
1966 if (Rename(cd->FileName, cd->DestName))
1968 printok = TEXT_RENAMED;
1970 else
1972 printerr = TEXT_RENAMED;
1973 cd->RetVal = RETURN_WARN;
1976 else if (cd->Mode == COPYMODE_LINK)
1978 if (!(cd->Flags & COPYFLAG_FORCELINK))
1980 printok = 0;
1981 cd->RetVal = RETURN_WARN;
1983 if (!(cd->Flags & COPYFLAG_QUIET))
1985 if (!(cd->Flags & COPYFLAG_VERBOSE))
1987 PrintName(name, cd->Deep, 1, 1, cd);
1990 Printf(TEXT_NOT_DONE, TEXT_LINKED);
1991 PutStr(TEXT_ERR_FORCELINK);
1994 else if (LinkFile(lock, cd->DestName, cd->Flags & COPYFLAG_SOFTLINK, cd))
1996 printok = TEXT_LINKED;
1998 else
2000 printerr = TEXT_LINKED;
2001 cd->RetVal = RETURN_WARN;
2004 else /* COPY mode only displays directories, when not ALL */
2006 printok = 0;
2008 if (!(cd->Flags & COPYFLAG_QUIET))
2010 if (cd->Flags & COPYFLAG_VERBOSE)
2012 PutStr("\n");
2017 else
2019 /* test for existing destination file */
2020 if (TestDest(cd->DestName, 0, cd) < 0)
2022 printerr = TEXT_OPENED_FOR_OUTPUT;
2024 else if (cd->Mode == COPYMODE_MOVE && Rename(cd->FileName, cd->DestName))
2026 printok = TEXT_RENAMED;
2028 else if (cd->Mode == COPYMODE_LINK)
2030 if (!(cd->Flags & COPYFLAG_SOFTLINK) && LinkFile(lock, cd->DestName, 0, cd))
2032 printok = TEXT_LINKED;
2034 else
2036 printerr = TEXT_LINKED;
2037 cd->RetVal = RETURN_WARN;
2039 if (cd->Flags & COPYFLAG_SOFTLINK)
2041 SetIoErr(ERROR_OBJECT_WRONG_TYPE);
2045 else
2047 ULONG res = 0, h;
2048 BPTR in, out;
2049 CONST_STRPTR txt = TEXT_OPENED_FOR_OUTPUT;
2051 if ((out = Open(cd->DestName, MODE_NEWFILE)))
2053 ULONG kill = 1;
2055 txt = cd->Mode == COPYMODE_MOVE ? TEXT_MOVED : TEXT_COPIED;
2056 UnLock(lock);
2057 lock = 0;
2059 if ((in = Open(cd->FileName, MODE_OLDFILE)))
2061 h = CopyFile(in, out, cd->BufferSize, cd);
2062 Close(out); out = BNULL;
2063 Close(in);
2065 if (!h)
2067 kill = 0;
2069 if (cd->Mode == COPYMODE_MOVE)
2071 if (KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE, cd))
2073 res = 1;
2076 else
2078 res = 1;
2083 if (out)
2085 Close(out);
2088 if (kill)
2090 KillFileKeepErr(cd->DestName, 0, cd);
2094 if (!res)
2096 printerr = txt;
2097 cd->RetVal = RETURN_WARN;
2099 else
2101 printok = txt;
2106 if (printerr && !(cd->Flags & COPYFLAG_QUIET))
2108 PrintNotDone(cd->Flags & COPYFLAG_VERBOSE ? name : 0,
2109 printerr, cd->Deep, cd->Fib.fib_DirEntryType > 0, cd);
2111 else if (printok)
2113 cd->Flags |= COPYFLAG_DONE;
2115 if (!(cd->Flags & COPYFLAG_QUIET))
2117 if ((cd->Flags & COPYFLAG_VERBOSE))
2119 Printf("%s\n", printok);
2123 SetData(cd->DestName, cd);
2126 if (lock)
2128 UnLock(lock);
2133 LONG CopyFile(BPTR from, BPTR to, ULONG bufsize, struct CopyData *cd)
2135 STRPTR buffer;
2136 LONG s, err = 0;
2138 if (cd->CopyBuf)
2140 buffer = cd->CopyBuf;
2141 bufsize = cd->CopyBufLen;
2143 else
2147 buffer = (STRPTR)AllocMem(bufsize, MEMF_PUBLIC);
2148 if (buffer)
2150 cd->CopyBuf = buffer;
2151 cd->CopyBufLen = bufsize;
2152 break;
2155 bufsize >>= 1;
2157 } while (bufsize >= 512);
2160 if (buffer)
2162 #if USE_BOGUSEOFWORKAROUND
2163 struct FileInfoBlock *fib = (struct FileInfoBlock *) buffer; /* NOTE: bufsize is min 512 bytes */
2165 if (ExamineFH(from, fib))
2167 #warning "****** WARNING: No largefile support! ******"
2168 ULONG filesize = fib->fib_Size, copied = 0;
2170 /*Printf("filesize: %lu\n", filesize);*/
2174 ULONG brk = CTRL_C;
2175 if (brk || (s = Read(from, buffer, bufsize)) == -1 || Write(to, buffer, s) != s)
2177 if (brk)
2179 SetIoErr(ERROR_BREAK);
2181 err = RETURN_FAIL;
2182 break;
2184 else if (s == 0 && copied < filesize)
2186 /* premature EOF with buggy fs */
2187 err = RETURN_FAIL;
2188 break;
2191 copied += s;
2193 } while (copied < filesize);
2195 /*{ LONG ioerr = IoErr();
2196 Printf("copied %lu/%lu\n", copied, filesize);
2197 SetIoErr(ioerr);}*/
2199 else
2200 #endif /* USE_BOGUSEOFWORKAROUND */
2202 /* Stream or so, copy until EOF or error */
2205 ULONG brk = CTRL_C;
2206 if (brk || (s = Read(from, buffer, bufsize)) == -1 || Write(to, buffer, s) != s)
2208 if (brk)
2210 SetIoErr(ERROR_BREAK);
2212 err = RETURN_FAIL;
2213 break;
2215 } while (s == bufsize);
2218 /* Freed at exit to avoid fragmentation */
2219 /*FreeMem(buffer, bufsize);*/
2221 else
2223 err = RETURN_FAIL;
2226 return err;
2230 /* Softlink's path starts always with device name! f.e. "Ram Disk:T/..." */
2231 LONG LinkFile(BPTR from, STRPTR to, ULONG soft, struct CopyData *cd)
2233 if (soft)
2235 LONG ret = FALSE;
2236 UBYTE *name;
2238 name = AllocMem(FILEPATH_SIZE, MEMF_ANY);
2239 if (name)
2241 if (NameFromLock(from, name, FILEPATH_SIZE))
2243 ret = MakeLink(to, name, LINK_SOFT);
2246 FreeMem(name, FILEPATH_SIZE);
2249 return ret;
2251 else
2253 return MakeLink(to, (void *)from, LINK_HARD);
2258 /* return 0 means no loop, return != 0 means loop found */
2259 ULONG TestLoop(BPTR srcdir, BPTR destdir, struct CopyData *cd)
2261 ULONG loop = 0;
2262 BPTR par, lock;
2264 lock = destdir;
2266 if (SameDevice(srcdir, destdir))
2270 if (!SameLock(srcdir, lock))
2272 loop = 1;
2274 else
2276 par = ParentDir(lock);
2278 if (lock != destdir)
2280 UnLock(lock);
2283 lock = par;
2286 while(!loop && lock);
2289 if (lock != destdir)
2291 UnLock(lock);
2294 return loop;
2298 void SetData(STRPTR name, struct CopyData *cd)
2300 if (cd->Flags & COPYFLAG_NOPRO)
2302 /* Is already set! - Piru */
2303 //SetProtection(name, 0);
2305 else if (cd->Flags & COPYFLAG_PROTECTION)
2307 SetProtection(name, cd->Fib.fib_Protection & (ULONG) ~FIBF_ARCHIVE);
2310 if (cd->Flags & COPYFLAG_DATES)
2312 SetFileDate(name, &cd->Fib.fib_Date);
2315 if (cd->Flags & COPYFLAG_COMMENT)
2317 SetComment(name, cd->Fib.fib_Comment);
2322 LONG TestDest(STRPTR name, ULONG type, struct CopyData *cd)
2324 LONG ret = TESTDEST_ERROR;
2325 BPTR lock;
2327 if ((lock = Lock(name, SHARED_LOCK)))
2329 struct FileInfoBlock *fib;
2331 if ((fib = (struct FileInfoBlock *)AllocDosObject(DOS_FIB, NULL)))
2333 if (Examine(lock, fib))
2335 UnLock(lock);
2336 lock = 0;
2338 if (type)
2340 if (fib->fib_DirEntryType > 0)
2342 ret = TESTDEST_DIR_OK;
2344 else if (!(cd->Flags & COPYFLAG_DONTOVERWRITE))
2346 if (KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE, cd))
2348 ret = TESTDEST_DELETED;
2351 else
2353 ret = TESTDEST_CANTDELETE;
2356 else if (cd->Flags & COPYFLAG_DONTOVERWRITE)
2358 if (cd->Flags & COPYFLAG_NEWER)
2360 if( CheckVersion( cd ) == CHECKVER_DESTOLDER )
2362 if (KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE, cd))
2364 ret = TESTDEST_DELETED;
2367 else
2369 ret = TESTDEST_CANTDELETE;
2372 else /* normal "dont overwrite mode" */
2374 ret = TESTDEST_CANTDELETE;
2377 else if (KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE, cd))
2379 ret = TESTDEST_DELETED;
2383 FreeDosObject(DOS_FIB, fib);
2386 if (lock)
2388 UnLock(lock);
2391 else
2393 ret = TESTDEST_NONE;
2396 if (ret == TESTDEST_CANTDELETE)
2398 SetIoErr(ERROR_OBJECT_EXISTS);
2401 return ret;
2405 ** We compare current file versions and return the result
2406 ** see CHECKVER_#? values
2409 static LONG CheckVersion( struct CopyData *cd )
2411 struct VersionData vds;
2412 struct VersionData vdd;
2413 LONG resversion = CHECKVER_EQUAL;
2414 LONG resdate = CHECKVER_EQUAL;
2416 if( VersionFind( cd->FileName, &vds, cd ) )
2418 if( VersionFind( cd->DestName, &vdd, cd ) )
2420 /* version and revision must be available to ensure a proper operation */
2421 if( ((vdd.vd_Version != -1) && (vds.vd_Version != -1) && (vdd.vd_Revision != -1) && (vds.vd_Revision != -1)) )
2423 /* first we make the stuff comparable. If one component is missing we reset both */
2424 if( vdd.vd_Year == -1 || vds.vd_Year == -1 )
2426 vdd.vd_Year = 0;
2427 vds.vd_Year = 0;
2429 if( vdd.vd_Month == -1 || vds.vd_Month == -1 )
2431 vdd.vd_Month = 0;
2432 vds.vd_Month = 0;
2434 if( vdd.vd_Day == -1 || vds.vd_Day == -1 )
2436 vdd.vd_Day = 0;
2437 vds.vd_Day = 0;
2440 /* check version */
2441 resversion = CHECKVER_DESTOLDER;
2442 if( ((vdd.vd_Version == vds.vd_Version) && vdd.vd_Revision == vds.vd_Revision ) )
2444 resversion = CHECKVER_EQUAL;
2446 else if( (vdd.vd_Version > vds.vd_Version) ||
2447 ((vdd.vd_Version == vds.vd_Version) && vdd.vd_Revision > vds.vd_Revision ) )
2449 resversion = CHECKVER_DESTNEWER;
2451 /* check date */
2453 resdate = CHECKVER_DESTOLDER;
2454 if( ((vdd.vd_Year == vds.vd_Year) && (vdd.vd_Month == vds.vd_Month) && (vdd.vd_Day == vds.vd_Day) ) )
2456 resdate = CHECKVER_EQUAL;
2458 else
2460 if( ( (vdd.vd_Year > vds.vd_Year ) ||
2461 ( (vdd.vd_Year == vds.vd_Year) && (vdd.vd_Month > vds.vd_Month ) ) ||
2462 ( (vdd.vd_Year == vds.vd_Year) && (vdd.vd_Month == vds.vd_Month ) && (vdd.vd_Day > vds.vd_Day ) ) ))
2464 resdate = CHECKVER_DESTNEWER;
2468 /* plausible check */
2469 if( ((resversion == CHECKVER_DESTNEWER) && (resdate == CHECKVER_DESTOLDER)) || /* newer version with older date */
2470 ((resversion == CHECKVER_DESTOLDER) && (resdate == CHECKVER_DESTNEWER)) ) /* older version with newer date */
2472 /* we maybe should inform the user about this */
2473 return( CHECKVER_EQUAL );
2478 /* compose result */
2480 if( (resversion == resdate) || (resversion == CHECKVER_EQUAL) )
2482 return( resdate );
2484 else
2486 return( resversion );
2493 ** Searches the given file for a version string and fills version data struct with the result.
2494 ** Returns false if no version was found. Returns true if the version got parsed and version data
2495 ** is valid.
2498 #define VERSBUFFERSIZE 4096 /* must be as big as the biggest version string we want to handle. */
2500 static BOOL VersionFind( CONST_STRPTR path, struct VersionData *vds, struct CopyData *cd)
2502 BPTR handle;
2503 STRPTR buf;
2504 ULONG i, rc;
2506 rc = FALSE;
2508 if ( (buf = AllocVec(VERSBUFFERSIZE, MEMF_PUBLIC | MEMF_CLEAR)) )
2510 if ( (handle = Open(path, MODE_OLDFILE)) )
2512 long index = 0;
2514 while( ( (index += Read(handle, &buf[index], VERSBUFFERSIZE-index)) > 5) && !rc )
2516 for (i = 0; i < index-5; i++) {
2517 if( buf[i] == '$' && buf[i+1] == 'V' && buf[i+2] == 'E' && buf[i+3] == 'R' && buf[i+4] == ':' ) {
2518 CopyMem( &buf[i], buf, index-i );
2519 index -= i;
2520 (index += Read(handle, &buf[index], VERSBUFFERSIZE-index));
2521 /* now the version string is aligned and complete in buffer */
2522 makeversionfromstring( buf, vds, cd );
2523 rc = TRUE;
2524 break;
2527 CopyMem( &buf[index-5], &buf[0], 5 );
2528 index = 5;
2530 Close(handle);
2532 FreeVec(buf);
2534 return (rc);
2539 ** This function extracts the version information from a given version string.
2540 ** the result will be store in the given version data structure.
2542 ** NOTE: There is no need to preset the version data struct. All fields will
2543 ** be reset to defaults, so in case of a faulty version string the result data
2544 ** will be checkable.
2547 static
2548 void makeversionfromstring( STRPTR buffer, struct VersionData *vd, struct CopyData *cd)
2550 LONG pos;
2551 ULONG tmp;
2552 STRPTR name;
2554 /* reset data field */
2556 vd->vd_Name[0] = '\0';
2557 vd->vd_Version = -1;
2558 vd->vd_Revision = -1;
2559 vd->vd_Day = -1;
2560 vd->vd_Month = -1;
2561 vd->vd_Year = -1;
2563 buffer = skipspaces( buffer ); /* skip before $VER: */
2564 buffer = skipnonspaces( buffer ); /* skip $VER: */
2565 buffer = skipspaces( buffer ); /* skip spaces before tool name */
2566 name = buffer;
2567 buffer = skipnonspaces( buffer ); /* skip name of tool */
2569 if( (tmp = ((long) buffer - (long) name) ) && *buffer )
2571 CopyMem( name, vd->vd_Name, MIN( tmp, VDNAMESIZE-1) );
2572 vd->vd_Name[MIN( tmp, VDNAMESIZE-1)] = '\0'; /* terminate name string inside target buffer */
2574 buffer = skipspaces( buffer ); /* skip spaces before version */
2575 if( *buffer ) {
2577 /* Do version */
2579 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2581 vd->vd_Version = tmp;
2583 /* Do revision */
2585 buffer += pos;
2586 buffer = skipspaces(buffer);
2587 if (*buffer++ == '.')
2589 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2591 vd->vd_Revision = tmp;
2592 buffer += pos;
2593 buffer = skipspaces(buffer);
2594 if (*buffer++ == '(')
2596 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2598 vd->vd_Day = tmp;
2599 buffer += pos;
2600 if (*buffer++ == '.')
2602 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2604 vd->vd_Month = tmp;
2605 buffer += pos;
2606 if (*buffer++ == '.')
2608 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2610 if( (tmp >= 70) && (tmp <= 99) )
2612 tmp += 1900;
2614 if( (tmp < 70) )
2616 tmp += 2000;
2618 vd->vd_Year = tmp;
2632 /* Return a pointer to a string, stripped by all leading space characters
2633 * (SPACE).
2635 static
2636 STRPTR skipspaces( STRPTR buffer)
2638 for (;; buffer++)
2640 if (buffer[0] == '\0' || buffer[0] != ' ')
2642 return( buffer );
2647 /* Return a pointer to a string, stripped by all non space characters
2648 * (SPACE).
2650 static
2651 STRPTR skipnonspaces( STRPTR buffer)
2653 for (;; buffer++)
2655 if (buffer[0] == '\0' || buffer[0] == ' ')
2657 return( buffer );