Don't unlock null lock.
[cake.git] / workbench / c / Copy.c
blob17c1d3a1d324f43acae24e6cc73299aa43b3d362
1 /*
2 Copyright © 2001-2009, 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
305 /* Enabled softlinks check for testing. Define this to 0 in case of problems.
306 Pavel Fedin <sonic_amiga@rambler.ru> */
307 #define USE_SOFTLINKCHECK 1
309 #define USE_ALWAYSVERBOSE 1
310 #define USE_BOGUSEOFWORKAROUND 0
313 #include <aros/asmcall.h>
314 #include <exec/devices.h>
315 #include <exec/io.h>
316 #include <exec/memory.h>
317 #include <exec/semaphores.h>
318 #include <exec/types.h>
319 #include <dos/exall.h>
321 #ifdef __SASC
322 typedef ULONG IPTR;
323 #else /* __SASC */
324 #include <aros/debug.h>
325 #endif /* __SASC */
327 #include <proto/dos.h>
328 #include <proto/exec.h>
330 #include <string.h>
331 #include <stdio.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 AROS_UFH3(__startup static int, Start,
565 AROS_UFHA(char *, argstr, A0),
566 AROS_UFHA(ULONG, argsize, D0),
567 AROS_UFHA(struct ExecBase *, SysBase, A6))
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 #define SysBase cd->SysBase
599 #define DOSBase cd->DOSBase
601 cd->BufferSize = 512*1024;
602 cd->Mode = COPYMODE_COPY;
603 cd->RetVal2 = RETURN_FAIL;
604 cd->Deep = 1;
606 memset(&iArgs, 0, sizeof(struct IptrArgs));
608 rda = (struct RDArgs *)AllocDosObject(DOS_RDARGS, NULL);
609 if (rda)
611 rda->RDA_ExtHelp =
612 "FROM multiple input files\n"
613 "TO destination file or directory\n"
614 "PATTERN a pattern the filenames must match\n"
615 "BUFFER buffersize for copy buffer (default 200 [100K])\n"
616 "ALL deep scan into sub directories\n"
617 "DIRECT copy/delete only: work without any tests or options\n"
618 "CLONE copy comment, protection bits and date as well\n"
619 "DATES copy dates\n"
620 "NOPRO do not copy protection bits\n"
621 "COMMENT copy filecomment\n"
622 "QUIET suppress all output and requesters\n"
623 #if !USE_ALWAYSVERBOSE
624 "VERBOSE give additional output\n"
625 #endif
626 "NOREQ suppress requesters\n"
627 "ERRWARN do not proceed, when one file failed\n"
628 "MAKEDIR produce directories\n"
629 "MOVE delete source files after copying successful\n"
630 "DELETE do not copy, but delete the source files\n"
631 "HARDLINK make a hardlink to source instead of copying\n"
632 "SOFTLINK make a softlink to source instead of copying\n"
633 "FOLNK also makes links to directories\n"
634 "FODEL delete protected files also\n"
635 "FOOVR also overwrite protected files\n"
636 "DONTOVR do never overwrite destination\n"
637 "FORCE DO NOT USE. Call compatibility only.\n"
638 "NEWER will compare version strings and only overwrites older files\n";
640 if (ReadArgs(PARAM, (IPTR *)&iArgs, rda))
642 ULONG patbufsize = 0;
643 LONG i = 0;
644 APTR win = task->pr_WindowPtr;
646 args.from = (STRPTR *)iArgs.from;
647 args.to = (STRPTR)iArgs.to;
648 args.pattern = (STRPTR)iArgs.pattern;
649 args.buffer = (LONG *)iArgs.buffer;
650 args.all = (LONG)iArgs.all;
651 args.direct = (LONG)iArgs.direct;
652 args.clone = (LONG)iArgs.clone;
653 args.dates = (LONG)iArgs.dates;
654 args.nopro = (LONG)iArgs.nopro;
655 args.comment = (LONG)iArgs.comment;
656 args.quiet = (LONG)iArgs.quiet;
657 #if USE_ALWAYSVERBOSE
658 args.verbose = FALSE;
659 #else
660 args.verbose = (LONG)iArgs.verbose;
661 #endif
662 args.noreq = (LONG)iArgs.noreq;
663 args.errwarn = (LONG)iArgs.errwarn;
664 args.makedir = (LONG)iArgs.makedir;
665 args.move_mode = (LONG)iArgs.move_mode;
666 args.delete_mode = (LONG)iArgs.delete_mode;
667 args.hardlink = (LONG)iArgs.hardlink;
668 args.softlink = (LONG)iArgs.softlink;
669 args.forcelink = (LONG)iArgs.forcelink;
670 args.forcedelete = (LONG)iArgs.forcedelete;
671 args.forceoverwrite = (LONG)iArgs.forceoverwrite;
672 args.dontoverwrite = (LONG)iArgs.dontoverwrite;
673 args.force = (LONG)iArgs.force;
674 args.newer = (LONG)iArgs.newer;
676 if (args.quiet) /* when QUIET, SILENT and NOREQ are also
677 true! */
679 /* Original doesn't hide requesters with QUIET */
680 /*args.noreq = 1;*/
681 args.verbose = FALSE;
684 if (args.buffer && *args.buffer > 0) /* minimum buffer size */
686 cd->BufferSize = *args.buffer * 512;
689 if (args.quiet)
691 cd->Flags |= COPYFLAG_QUIET;
694 #if !USE_ALWAYSVERBOSE
695 if (args.verbose)
697 cd->Flags |= COPYFLAG_VERBOSE;
699 #endif
700 if (args.all)
702 cd->Flags |= COPYFLAG_ALL;
705 /* 12-jul-03 bugfix: always copy protection flags! -Piru */
706 cd->Flags |= COPYFLAG_PROTECTION;
707 if (args.clone)
709 cd->Flags |= COPYFLAG_DATES | COPYFLAG_COMMENT | COPYFLAG_PROTECTION;
712 if (args.dates)
714 cd->Flags |= COPYFLAG_DATES;
717 if (args.comment)
719 cd->Flags |= COPYFLAG_COMMENT;
722 if (args.nopro)
724 cd->Flags |= COPYFLAG_NOPRO;
727 if (args.forcelink)
729 cd->Flags |= COPYFLAG_FORCELINK;
732 if (args.forcedelete)
734 cd->Flags |= COPYFLAG_FORCEDELETE;
737 if (args.forceoverwrite)
739 cd->Flags |= COPYFLAG_FORCEOVERWRITE;
742 if (args.dontoverwrite)
744 cd->Flags |= COPYFLAG_DONTOVERWRITE;
747 if (args.newer)
749 cd->Flags |= COPYFLAG_NEWER|COPYFLAG_DONTOVERWRITE;
752 if (args.errwarn)
754 cd->Flags |= COPYFLAG_ERRWARN;
757 if (args.force) /* support OS Delete and MakeLink command
758 options */
760 if (args.delete_mode)
762 cd->Flags |= COPYFLAG_FORCEDELETE;
765 if (args.hardlink || args.softlink)
767 cd->Flags |= COPYFLAG_FORCELINK;
771 if (!args.from) /* no args.from means currentdir */
773 args.from = a;
776 if (args.noreq) /* no dos.library requests allowed */
778 task->pr_WindowPtr = (APTR)-1;
781 if (args.delete_mode)
783 ++i;
784 cd->Mode = COPYMODE_DELETE;
787 if (args.move_mode)
789 ++i;
790 cd->Mode = COPYMODE_MOVE;
793 if (args.makedir)
795 ++i;
796 cd->Mode = COPYMODE_MAKEDIR;
799 if (args.hardlink)
801 ++i;
802 cd->Mode = COPYMODE_LINK;
805 if (args.softlink)
807 ++i;
808 cd->Mode = COPYMODE_LINK;
809 cd->Flags |= COPYFLAG_SOFTLINK | COPYFLAG_FORCELINK;
812 if (cd->Mode != COPYMODE_DELETE &&
813 cd->Mode != COPYMODE_MAKEDIR && !args.to)
815 if (*(args.from + 1)) /* when no TO is specified, the arg
816 is last */
817 { /* one of from. Copy this argument into */
818 STRPTR *a; /* args.to */
820 a = args.from;
822 while(*(++a))
825 args.to = *(--a);
826 *a = 0;
829 #if USE_ALWAYSVERBOSE
831 /* Only do this if quiet isn't set - bigfoot */
832 if (!args.quiet)
834 /* If more than two args, be verbose... - Piru */
835 if (args.from[0] && args.from[1])
837 args.verbose = TRUE;
838 cd->Flags |= COPYFLAG_VERBOSE;
841 /* If any of the sources is a pattern, be verbose... - Piru */
843 STRPTR *a;
845 for (a = args.from; *a; a++)
847 if (IsMatchPattern(*a, cd) != 0)
849 args.verbose = TRUE;
850 cd->Flags |= COPYFLAG_VERBOSE;
855 #endif
857 /* test if more than one of the above four or any other wrong
858 arguments */
860 if (i > 1 ||
861 (args.from == a && cd->Mode == COPYMODE_MAKEDIR) ||
862 (args.direct && (args.from == a || !*args.from ||
863 args.pattern ||
864 (cd->Flags & ~(COPYFLAG_QUIET | COPYFLAG_VERBOSE | COPYFLAG_ERRWARN)) ||
865 (cd->Mode != COPYMODE_DELETE && (cd->Mode != COPYMODE_COPY ||
866 !args.to || args.from[1])))) ||
867 (args.dontoverwrite && args.forceoverwrite) ||
868 /* (args.nopro && args.clone) ||*/ /* Ignore, like original - Piru */
869 (args.softlink && args.all) ||
870 (!args.to && cd->Mode != COPYMODE_DELETE && cd->Mode != COPYMODE_MAKEDIR))
872 SetIoErr(ERROR_TOO_MANY_ARGS);
874 else if (cd->Mode == COPYMODE_MAKEDIR)
876 LONG i;
877 BPTR dir;
878 cd->RetVal2 = RETURN_OK;
880 #if !USE_ALWAYSVERBOSE
881 if (args.verbose)
883 PutStr(texts[TEXTNUM_MODE + COPYMODE_MAKEDIR]);
885 #endif
887 while (!cd->RetVal && !cd->RetVal2 && *args.from)
889 if ((i = IsPattern(*args.from, cd)))
891 if (i != -1)
893 cd->RetVal = RETURN_ERROR;
895 if (!args.quiet)
897 PutStr(TEXT_ERR_WILDCARD_DEST);
900 else
902 cd->RetVal2 = RETURN_FAIL;
906 if ((dir = OpenDestDir(*args.from, cd)))
908 UnLock(dir);
909 cd->Flags |= COPYFLAG_DONE;
912 ++args.from;
914 } /* cd->Mode == COPYMODE_MAKEDIR */
915 else if (args.direct)
917 if (cd->Mode == COPYMODE_COPY)
919 BPTR in, out;
921 if ((in = Open(*args.from, MODE_OLDFILE)))
923 if ((out = Open(args.to, MODE_NEWFILE)))
925 cd->RetVal2 = CopyFile(in, out, cd->BufferSize, cd);
926 Close(out);
929 Close(in);
932 else /* COPYMODE_DELETE */
934 while (*args.from)
936 KillFile(*(args.from++), cd->Flags & COPYFLAG_FORCEDELETE, cd);
939 cd->RetVal2 = RETURN_OK;
942 else
944 if (args.pattern && *args.pattern)
946 patbufsize = (strlen(args.pattern) << 1) + 3;
948 if ((cd->Pattern = (STRPTR)AllocMem(patbufsize,
949 MEMF_ANY)))
951 if (ParsePatternNoCase(args.pattern, cd->Pattern,
952 patbufsize) < 0)
954 FreeMem(cd->Pattern, patbufsize);
955 cd->Pattern = 0;
960 if (1) // (cd->Fib = (struct FileInfoBlock *)AllocDosObject(DOS_FIB, NULL)))
962 #if !USE_ALWAYSVERBOSE
963 if (args.verbose)
965 PutStr(texts[TEXTNUM_MODE + cd->Mode +
966 (cd->Flags & COPYFLAG_SOFTLINK ? 1 : 0)]);
968 #endif
969 if (args.pattern && !cd->Pattern)
971 if (!*args.pattern)
973 SetIoErr(ERROR_BAD_TEMPLATE);
976 else if (cd->Mode == COPYMODE_DELETE)
978 cd->RetVal2 = RETURN_OK;
980 while (cd->RetVal <= (args.errwarn ? RETURN_OK : RETURN_WARN)
981 && *args.from)
983 PatCopy(*(args.from++), cd);
986 else if ((i = IsPattern(args.to, cd)))
988 if (i != -1)
990 cd->RetVal = RETURN_ERROR;
992 if (!args.quiet)
994 PutStr(TEXT_ERR_WILDCARD_DEST);
998 else
1000 STRPTR path;
1002 if (*(path = PathPart(args.to)) == '/')
1004 ++path; /* is destination a path description ? */
1007 if (*path && !*(args.from+1) &&
1008 !(i = IsMatchPattern(*args.from, cd)))
1010 BPTR lock;
1011 LONG lockioerr;
1013 /* is destination an existing directory */
1014 if ((lock = Lock(args.to, SHARED_LOCK)))
1016 if (Examine(lock, &cd->Fib))
1018 if (cd->Fib.fib_DirEntryType > 0)
1020 cd->RetVal2 = RETURN_OK;
1023 /* indicate dir-mode for next if */
1025 else
1027 i = 1;
1030 UnLock(lock);
1033 /* Some magic to handle tick quoted pattern object names. Quite crude way to
1034 * handle it, but I couldn't think of anything better. - Piru
1036 #if 1
1037 if (!i && cd->RetVal2 && !IsMatchPattern(*args.from, cd))
1039 ULONG len;
1040 STRPTR pat;
1042 //Printf("pattern check <%s>\n", *args.from);
1044 len = (strlen(*args.from) << 1) + 3;
1046 if ((pat = (STRPTR)AllocMem(len,
1047 MEMF_ANY)))
1049 if (ParsePattern(*args.from, pat, len) > -1 &&
1050 strlen(pat) <= strlen(*args.from))
1052 lock = Lock(pat, SHARED_LOCK);
1053 if (lock)
1055 UnLock(lock);
1057 strcpy(*args.from, pat);
1061 FreeMem(pat, len);
1064 #endif
1066 /* is source a directory */
1067 if (!i && cd->RetVal2 &&
1068 (lock = Lock(*args.from, SHARED_LOCK)))
1070 if (Examine(lock, &cd->Fib))
1072 cd->RetVal2 = RETURN_OK;
1073 if (cd->Mode != COPYMODE_COPY ||
1074 cd->Fib.fib_DirEntryType < 0)
1076 UBYTE sep;
1078 cd->Flags |= COPYFLAG_DEST_FILE;
1080 /* produce missing destination directories */
1081 sep = *path;
1082 *path = 0;
1084 if ((cd->CurDest = OpenDestDir(args.to, cd)))
1086 *path = sep;
1088 /* do the job */
1089 UnLock(lock);
1090 lock = 0;
1091 CopyMem(*args.from, cd->FileName,
1092 1 + strlen(*args.from));
1093 DoWork(FilePart(args.to), cd); /* on file call */
1094 UnLock(cd->CurDest);
1099 if (lock)
1101 UnLock(lock);
1105 lockioerr = IoErr(); /* We save ioerr here, because TestFileSys changes it */
1107 if (lock == 0 && cd->Mode == COPYMODE_COPY && !TestFileSys(*args.from, cd))
1109 UBYTE sep;
1110 cd->Flags |= COPYFLAG_DEST_FILE | COPYFLAG_SRCNOFILESYS;
1111 cd->RetVal2 = RETURN_OK;
1113 /* produce missing destination directories */
1114 sep = *path;
1115 *path = 0;
1117 if ((cd->CurDest = OpenDestDir(args.to, cd)))
1119 *path = sep;
1121 /* do the job */
1122 CopyMem(*args.from, cd->FileName, 1 + strlen(*args.from));
1123 DoWork(FilePart(args.to), cd); /* on file call */
1124 UnLock(cd->CurDest);
1127 else
1128 SetIoErr(lockioerr);
1130 else if (i != -1)
1132 cd->RetVal2 = RETURN_OK;
1135 if (!cd->RetVal && !cd->RetVal2 && !(cd->Flags & COPYFLAG_DEST_FILE) &&
1136 (cd->Destination = OpenDestDir(args.to, cd)))
1138 while (cd->RetVal <= (args.errwarn ? RETURN_OK : RETURN_WARN)
1139 && *args.from && !CTRL_C)
1141 PatCopy(*(args.from++), cd);
1144 UnLock(cd->Destination);
1146 } /* else */
1148 if (!(cd->Flags & COPYFLAG_DONE) && args.verbose &&
1149 !cd->RetVal && !cd->RetVal2)
1151 PutStr(TEXT_NOTHING_DONE);
1154 } /* if (1) */
1156 if (cd->Pattern)
1158 FreeMem(cd->Pattern, patbufsize);
1160 } /* else */
1162 task->pr_WindowPtr = win;
1164 FreeArgs(rda);
1165 } /* ReadArgs */
1167 FreeDosObject(DOS_RDARGS, rda);
1168 } /* AllocDosObject */
1170 if (!cd->RetVal2 && CTRL_C)
1172 SetIoErr(ERROR_BREAK);
1173 cd->RetVal2 = RETURN_WARN;
1176 if (cd->RetVal2 && !args.quiet && !cd->RetVal)
1178 PrintFault(IoErr(), NULL);
1181 if (cd->RetVal)
1183 cd->RetVal2 = cd->RetVal;
1186 if (args.errwarn && cd->RetVal2 == RETURN_WARN)
1188 cd->RetVal2 = RETURN_ERROR;
1191 if (cd->CopyBuf)
1193 FreeMem(cd->CopyBuf, cd->CopyBufLen);
1196 #undef SysBase
1197 #undef DOSBase
1199 if (cd)
1201 retval = cd->RetVal2;
1202 FreeMem(cd, sizeof(*cd));
1204 else if (DOSBase)
1206 PrintFault(IoErr(), NULL);
1208 if (DOSBase)
1210 CloseLibrary((struct Library *)DOSBase);
1212 return retval;
1214 AROS_USERFUNC_EXIT
1217 #define SysBase cd->SysBase
1218 #define DOSBase cd->DOSBase
1220 void PatCopy(STRPTR name, struct CopyData *cd)
1222 struct AnchorPath *APath;
1223 ULONG retval, doit = 0, deep = 0, failval = RETURN_WARN, first = 0;
1225 #if DEBUG
1226 Printf("PatCopy(%s, .)\n", name);
1227 #endif
1229 if ((cd->Mode == COPYMODE_COPY || (cd->Flags & COPYFLAG_ALL)) && !IsMatchPattern(name, cd))
1231 first = 1; /* enter first directory (support of old copy style) */
1234 if (cd->Flags & COPYFLAG_ERRWARN)
1236 failval = RETURN_OK;
1239 cd->CurDest = cd->Destination;
1240 cd->DestPathSize = 0;
1242 if (cd->Mode == COPYMODE_COPY && !TestFileSys(name, cd))
1244 cd->Flags |= COPYFLAG_SRCNOFILESYS;
1245 CopyMem(name, cd->FileName, 1 + strlen(name));
1246 DoWork(FilePart(name), cd);
1247 cd->Flags &= ~COPYFLAG_SRCNOFILESYS;
1249 return;
1252 if ((APath = (struct AnchorPath *)AllocMem(sizeof(struct AnchorPath) + FILEPATH_SIZE,
1253 MEMF_PUBLIC | MEMF_CLEAR)))
1255 int parentdirerr = 0;
1257 APath->ap_BreakBits = SIGBREAKF_CTRL_C;
1258 APath->ap_Strlen = FILEPATH_SIZE;
1260 for (retval = MatchFirst(name, APath);
1261 !retval && cd->RetVal <= failval && !cd->RetVal2;
1262 retval = MatchNext(APath)
1265 if (parentdirerr)
1267 //Printf("ParentDir() fuxored last round! Would copy next files to SYS: !\n");
1268 retval = IoErr();
1269 if (!retval)
1270 SetIoErr(retval = ERROR_INVALID_LOCK);
1271 break;
1274 if (doit)
1276 DoWork(cd->Fib.fib_FileName, cd);
1277 doit = 0;
1280 if (deep) /* used for Deep checking */
1282 ++cd->Deep;
1283 deep = 0;
1286 cd->Flags &= ~COPYFLAG_ENTERSECOND;
1288 CopyMem(APath->ap_Buf, cd->FileName, FILEPATH_SIZE);
1289 CopyMem(&APath->ap_Info, &cd->Fib, sizeof(struct FileInfoBlock));
1291 if (first && APath->ap_Info.fib_DirEntryType > 0)
1293 #if USE_ALWAYSVERBOSE
1294 /* If the source is a directory, be verbose - Piru */
1295 cd->Flags |= COPYFLAG_VERBOSE;
1296 #endif
1297 APath->ap_Flags |= APF_DODIR;
1299 else if (APath->ap_Flags & APF_DIDDIR)
1301 BPTR i;
1303 cd->Flags |= COPYFLAG_ENTERSECOND;
1304 APath->ap_Flags &= ~APF_DIDDIR;
1305 --cd->Deep;
1307 if (cd->Mode == COPYMODE_DELETE || cd->Mode == COPYMODE_MOVE)
1309 doit = 1;
1312 if ((i = cd->CurDest))
1314 cd->CurDest = ParentDir(i);
1315 cd->DestPathSize = 0;
1317 if (i != cd->Destination)
1319 UnLock(i);
1322 if (!cd->CurDest)
1324 parentdirerr = 1;
1325 continue;
1329 else if (APath->ap_Info.fib_DirEntryType > 0)
1331 doit = 1;
1333 if (cd->Flags & COPYFLAG_ALL)
1335 #if USE_SOFTLINKCHECK
1337 BOOL enter = TRUE;
1338 BPTR dirlock, lock;
1340 dirlock = CurrentDir(APath->ap_Current->an_Lock);
1341 lock = Lock(APath->ap_Info.fib_FileName, ACCESS_READ);
1342 if (lock)
1343 UnLock(lock);
1344 else
1346 struct DevProc *dvp;
1347 LONG ioerr = IoErr();
1349 if (ioerr == ERROR_OBJECT_NOT_FOUND &&
1350 (dvp = GetDeviceProc("", NULL)))
1352 #define BUFFERSIZE 512
1353 UBYTE *buffer = AllocMem(BUFFERSIZE, MEMF_PUBLIC);
1355 if (buffer)
1357 if (ReadLink(dvp->dvp_Port, dvp->dvp_Lock, APath->ap_Info.fib_FileName, buffer, BUFFERSIZE - 1) > 0)
1359 if (!(cd->Flags & COPYFLAG_QUIET))
1361 buffer[BUFFERSIZE - 1] = '\0';
1363 Printf("Warning: Skipping dangling softlink %s -> %s\n",
1364 APath->ap_Info.fib_FileName, buffer);
1367 enter = FALSE;
1370 FreeMem(buffer, BUFFERSIZE);
1373 FreeDeviceProc(dvp);
1376 SetIoErr(ioerr);
1378 CurrentDir(dirlock);
1380 if (enter)
1382 APath->ap_Flags |= APF_DODIR;
1383 deep = 1;
1386 #else /* USE_SOFTLINKCHECK */
1388 APath->ap_Flags |= APF_DODIR;
1389 deep = 1;
1391 #endif /* USE_SOFTLINKCHECK */
1394 else if (!cd->Pattern || MatchPatternNoCase(cd->Pattern, APath->ap_Info.fib_FileName))
1396 doit = 1;
1399 first = 0;
1402 MatchEnd(APath);
1404 if (retval && retval != ERROR_NO_MORE_ENTRIES)
1406 LONG ioerr = IoErr();
1407 #if USE_ALWAYSVERBOSE
1408 cd->Flags |= COPYFLAG_VERBOSE;
1409 #endif
1410 Printf("%s - ", name);
1411 PrintFault(ioerr, NULL);
1412 SetIoErr(ioerr);
1414 cd->RetVal2 = RETURN_FAIL;
1417 if (doit)
1419 DoWork(cd->Fib.fib_FileName, cd);
1422 /* No need to clear the flags here, as they are cleared on next PatJoin
1423 call (DoWork is not called first round, as lock is zero!). */
1425 FreeMem(APath, sizeof(struct AnchorPath) + FILEPATH_SIZE);
1427 else
1429 cd->RetVal = RETURN_FAIL;
1431 if (!cd->Flags & COPYFLAG_QUIET)
1433 PrintFault(ERROR_NO_FREE_STORE, NULL);
1437 if (cd->CurDest && cd->CurDest != cd->Destination)
1439 UnLock(cd->CurDest);
1444 LONG IsPattern(STRPTR name, struct CopyData *cd)
1446 LONG a, ret = -1;
1447 STRPTR buffer;
1449 a = (strlen(name) << 1) + 3;
1451 if ((buffer = (STRPTR)AllocMem(a, MEMF_ANY)))
1453 ret = ParsePattern(name, buffer, a);
1454 FreeMem(buffer, a);
1457 if (ret == -1)
1459 SetIoErr(ERROR_NO_FREE_STORE);
1462 return ret;
1466 LONG IsMatchPattern(STRPTR name, struct CopyData *cd)
1468 struct AnchorPath ap;
1469 LONG ret = -1;
1471 ap.ap_BreakBits = 0;
1472 ap.ap_Flags = APF_DOWILD;
1473 ap.ap_Strlen = 0;
1475 if (MatchFirst(name, &ap) == 0)
1477 ret = (ap.ap_Flags & APF_ITSWILD) ? TRUE : FALSE;
1479 MatchEnd(&ap);
1482 return ret;
1486 LONG KillFile(STRPTR name, ULONG doit, struct CopyData *cd)
1488 if (doit)
1490 SetProtection(name, 0);
1493 return DeleteFile(name);
1497 LONG KillFileKeepErr(STRPTR name, ULONG doit, struct CopyData *cd)
1499 LONG ret, ioerr;
1501 ioerr = IoErr();
1502 ret = KillFile(name, doit, cd);
1503 SetIoErr(ioerr);
1505 return ret;
1509 BPTR OpenDestDir(STRPTR name, struct CopyData *cd)
1511 LONG a, err = 0, cr = 0;
1512 BPTR dir;
1513 STRPTR ptr = name;
1514 UBYTE as;
1516 if ((cd->Mode == COPYMODE_COPY || cd->Mode == COPYMODE_MOVE) && !TestFileSys(name, cd))
1518 cd->Flags |= COPYFLAG_DESNOFILESYS;
1519 CopyMem(name, cd->DestName, 1 + strlen(name));
1521 return Lock("", SHARED_LOCK);
1524 while (!err && *ptr != 0)
1526 while (*ptr && *ptr != '/')
1528 ++ptr;
1531 as = *ptr;
1532 *ptr = 0;
1534 if ((a = TestDest(name, 1, cd)) == TESTDEST_CANTDELETE)
1536 if (!(cd->Flags & COPYFLAG_QUIET))
1538 PutStr(TEXT_ERR_DEST_DIR);
1541 err = 2;
1543 else if (a < 0)
1545 err = 1;
1547 else if (a != TESTDEST_DIR_OK)
1549 if ((dir = CreateDir(name)))
1551 ++cr;
1553 if ((cd->Flags & COPYFLAG_VERBOSE))
1555 PrintName(name, 1, 1, 1, cd);
1556 Printf("%s\n", TEXT_CREATED);
1559 UnLock(dir);
1561 else
1563 if (!(cd->Flags & COPYFLAG_QUIET))
1565 PrintNotDone(name, TEXT_CREATED, 1, 1, cd);
1568 err = 2;
1572 *ptr = as;
1574 /* 26-Oct-2003 bugfix: Don't scan past end of the string.
1575 * as is the old char, if '\0' we've reached the end of the
1576 * string. - Piru
1578 if (as)
1580 ptr++;
1584 if (err)
1586 cd->RetVal = RETURN_ERROR;
1588 if (!(cd->Flags & COPYFLAG_QUIET) && err == 1)
1590 PrintNotDone(name, TEXT_OPENED_FOR_OUTPUT, 1, 1, cd);
1593 return 0;
1596 if (cd->Mode == COPYMODE_MAKEDIR && !cr && !(cd->Flags & COPYFLAG_QUIET))
1598 SetIoErr(ERROR_OBJECT_EXISTS);
1599 PrintNotDone(name, TEXT_CREATED, 1, 1, cd);
1602 return Lock(name, SHARED_LOCK);
1606 void PrintName(CONST_STRPTR name, ULONG deep, ULONG dir, ULONG txt, struct CopyData *cd)
1608 #if 0
1609 deep %= PRINTOUT_SPACES; /* reduce number of spaces */
1610 /* This produces an error with MaxonC++ */
1611 #endif
1613 PutStr(" ");
1614 if (deep)
1616 while (--deep)
1618 PutStr(" ");
1622 if (dir)
1624 PutStr(" ");
1627 #if 0
1628 if ((deep = strlen(name)) > PRINTOUT_SIZE) /* reduce name size */
1630 name += deep-PRINTOUT_SIZE;
1631 PutStr("...");
1633 #endif
1635 Printf((dir ? TEXT_DIRECTORY : (STRPTR) "%s"), name);
1637 if (!dir && txt)
1639 PutStr("..");
1642 Flush(Output());
1646 void PrintNotDone(CONST_STRPTR name, CONST_STRPTR txt, ULONG deep, ULONG dir, struct CopyData *cd)
1648 #if !USE_ALWAYSVERBOSE
1649 if (name)
1651 PrintName(name, deep, dir, 1, cd);
1653 #endif
1655 Printf(TEXT_NOT_DONE, txt);
1656 PrintFault(IoErr(), NULL);
1660 /* returns value, when it seems to be a filesystem */
1661 ULONG TestFileSys(STRPTR name, struct CopyData *cd)
1663 STRPTR n = name;
1664 ULONG ret = 1;
1666 while (*n && *n != ':')
1668 ++n;
1671 if (*(n++) == ':')
1673 UBYTE a;
1675 a = *n;
1676 *n = 0;
1677 ret = IsFileSystem(name);
1678 *n = a;
1681 return ret;
1685 void DoWork(STRPTR name, struct CopyData *cd)
1687 BPTR pdir, lock = 0;
1688 CONST_STRPTR printerr = NULL, printok = "";
1690 #if DEBUG
1691 Printf("DoWork(%s, .)\n", name);
1692 #endif
1694 if (cd->RetVal > (cd->Flags & COPYFLAG_ERRWARN ? RETURN_OK : RETURN_WARN) || cd->RetVal2)
1696 #if DEBUG
1697 Printf("DoWork(RetVal %ld)\n", cd->RetVal);
1698 #endif
1699 return;
1702 if (cd->Mode != COPYMODE_DELETE && !(cd->Flags & COPYFLAG_DESNOFILESYS))
1704 if (!cd->DestPathSize)
1706 if (!NameFromLock(cd->CurDest, cd->DestName, FILEPATH_SIZE))
1708 cd->RetVal2 = RETURN_FAIL;
1710 #if DEBUG
1711 Printf("DoWork(NameFromLock RetVal %ld)\n", cd->RetVal);
1712 #endif
1714 return;
1717 cd->DestPathSize = strlen(cd->DestName);
1720 cd->DestName[cd->DestPathSize] = 0;
1721 AddPart(cd->DestName, name, FILEPATH_SIZE);
1724 if (cd->Flags & (COPYFLAG_SRCNOFILESYS|COPYFLAG_DESNOFILESYS))
1726 ULONG res = 0, kill = 1;
1727 BPTR in, out;
1728 CONST_STRPTR txt = TEXT_OPENED_FOR_OUTPUT;
1730 #if DEBUG
1731 Printf("Partly DIRECT mode active now (%s - %s)\n", cd->FileName,
1732 cd->DestName);
1733 #endif
1735 if ((out = Open(cd->DestName, MODE_NEWFILE)))
1737 txt = cd->Mode == COPYMODE_MOVE ? TEXT_MOVED : TEXT_COPIED;
1739 if ((in = Open(cd->FileName, MODE_OLDFILE)))
1741 ULONG h;
1743 h = CopyFile(in, out, cd->BufferSize, cd);
1744 Close(out); out = NULL;
1745 Close(in);
1747 if (!h)
1749 kill = 0;
1751 if (cd->Mode == COPYMODE_MOVE)
1753 if (KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE, cd))
1755 res = 1;
1758 else
1760 res = 1;
1765 if (out)
1767 Close(out);
1770 if (kill)
1772 KillFileKeepErr(cd->DestName, 0, cd);
1776 if (!res && !(cd->Flags & COPYFLAG_QUIET))
1778 PrintNotDone(cd->Flags & COPYFLAG_VERBOSE ? name : 0,
1779 txt, cd->Deep, cd->Fib.fib_DirEntryType > 0, cd);
1781 else
1783 cd->Flags |= COPYFLAG_DONE;
1785 if ((cd->Flags & COPYFLAG_VERBOSE))
1787 Printf("%s\n", txt);
1791 #if DEBUG
1792 PutStr("DoWork(done)\n");
1793 #endif
1794 return;
1797 if (!(lock = Lock(cd->FileName, SHARED_LOCK)))
1799 cd->RetVal = RETURN_WARN;
1801 if (!(cd->Flags & COPYFLAG_QUIET))
1803 PrintNotDone(cd->Fib.fib_FileName, TEXT_READ, cd->Deep,
1804 cd->Fib.fib_DirEntryType > 0, cd);
1807 #if DEBUG
1808 Printf("DoWork(Lock RetVal %ld)\n", cd->RetVal);
1809 #endif
1810 return;
1813 if (!(pdir = ParentDir(lock)))
1815 cd->RetVal = RETURN_ERROR;
1817 if (cd->Mode == COPYMODE_DELETE)
1819 if (!(cd->Flags & COPYFLAG_QUIET))
1821 Printf(" %s ", cd->FileName);
1822 Printf(TEXT_NOT_DONE, TEXT_DELETED);
1823 Printf("%s\n", TEXT_ERR_DELETE_DEVICE);
1827 UnLock(lock);
1829 #if DEBUG
1830 Printf("DoWork(ParentDir %ld)\n", cd->RetVal);
1831 #endif
1832 return;
1835 UnLock(pdir);
1837 if (!(cd->Flags & COPYFLAG_QUIET))
1839 if ((cd->Flags & COPYFLAG_VERBOSE))
1841 PrintName(name, cd->Deep, cd->Fib.fib_DirEntryType > 0, cd->Fib.fib_DirEntryType < 0 ||
1842 (cd->Flags & COPYFLAG_ALL ? cd->Mode != COPYMODE_DELETE : cd->Mode != COPYMODE_COPY) ||
1843 cd->Flags & COPYFLAG_ENTERSECOND, cd);
1847 if ((cd->Flags & COPYFLAG_ENTERSECOND) || (cd->Mode == COPYMODE_DELETE &&
1848 (!(cd->Flags & COPYFLAG_ALL) || cd->Fib.fib_DirEntryType < 0)))
1850 UnLock(lock);
1851 lock = 0;
1853 if (KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE, cd))
1855 printok = TEXT_DELETED;
1857 else
1859 cd->RetVal = RETURN_WARN;
1860 printerr = TEXT_DELETED;
1863 else if (cd->Mode == COPYMODE_DELETE)
1867 else if (cd->Fib.fib_DirEntryType > 0)
1869 LONG a;
1871 if ((cd->Flags & COPYFLAG_ALL || cd->Mode == COPYMODE_LINK ||
1872 cd->Mode == COPYMODE_MOVE) && TestLoop(lock, cd->CurDest, cd))
1874 printok = 0;
1875 cd->RetVal = RETURN_ERROR;
1877 if (!(cd->Flags & COPYFLAG_QUIET))
1879 if (!(cd->Flags & COPYFLAG_VERBOSE))
1881 PrintName(name, cd->Deep, 1, 1, cd);
1884 Printf(TEXT_NOT_DONE, TEXT_ENTERED);
1885 PutStr(TEXT_ERR_INFINITE_LOOP);
1888 else if ((a = TestDest(cd->DestName, 1, cd)) < 0)
1890 printerr = TEXT_CREATED;
1891 cd->RetVal = RETURN_ERROR;
1893 else if (cd->Flags & COPYFLAG_ALL)
1895 BPTR i;
1897 i = cd->CurDest;
1898 cd->DestPathSize = 0;
1900 if (a == TESTDEST_DIR_OK)
1902 if (!(cd->CurDest = Lock(cd->DestName, SHARED_LOCK)))
1904 printerr = TEXT_ENTERED;
1905 cd->RetVal = RETURN_ERROR;
1907 else
1909 #if USE_ALWAYSVERBOSE
1910 printok = "";
1911 #else
1912 printok = TEXT_ENTERED;
1913 #endif
1916 else if ((cd->CurDest = CreateDir(cd->DestName)))
1918 UnLock(cd->CurDest);
1920 if ((cd->CurDest = Lock(cd->DestName, SHARED_LOCK)))
1922 printok = TEXT_CREATED;
1924 else
1926 printerr = TEXT_ENTERED;
1927 cd->RetVal = RETURN_ERROR;
1930 else
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 printerr = TEXT_RENAMED;
1954 cd->RetVal = RETURN_WARN;
1957 else if (cd->Mode == COPYMODE_LINK)
1959 if (!(cd->Flags & COPYFLAG_FORCELINK))
1961 printok = 0;
1962 cd->RetVal = RETURN_WARN;
1964 if (!(cd->Flags & COPYFLAG_QUIET))
1966 if (!(cd->Flags & COPYFLAG_VERBOSE))
1968 PrintName(name, cd->Deep, 1, 1, cd);
1971 Printf(TEXT_NOT_DONE, TEXT_LINKED);
1972 PutStr(TEXT_ERR_FORCELINK);
1975 else if (LinkFile(lock, cd->DestName, cd->Flags & COPYFLAG_SOFTLINK, cd))
1977 printok = TEXT_LINKED;
1979 else
1981 printerr = TEXT_LINKED;
1982 cd->RetVal = RETURN_WARN;
1985 else /* COPY mode only displays directories, when not ALL */
1987 printok = 0;
1989 if (!(cd->Flags & COPYFLAG_QUIET))
1991 if (cd->Flags & COPYFLAG_VERBOSE)
1993 PutStr("\n");
1998 else
2000 /* test for existing destination file */
2001 if (TestDest(cd->DestName, 0, cd) < 0)
2003 printerr = TEXT_OPENED_FOR_OUTPUT;
2005 else if (cd->Mode == COPYMODE_MOVE && Rename(cd->FileName, cd->DestName))
2007 printok = TEXT_RENAMED;
2009 else if (cd->Mode == COPYMODE_LINK)
2011 if (!(cd->Flags & COPYFLAG_SOFTLINK) && LinkFile(lock, cd->DestName, 0, cd))
2013 printok = TEXT_LINKED;
2015 else
2017 printerr = TEXT_LINKED;
2018 cd->RetVal = RETURN_WARN;
2020 if (cd->Flags & COPYFLAG_SOFTLINK)
2022 SetIoErr(ERROR_OBJECT_WRONG_TYPE);
2026 else
2028 ULONG res = 0, h;
2029 BPTR in, out;
2030 CONST_STRPTR txt = TEXT_OPENED_FOR_OUTPUT;
2032 if ((out = Open(cd->DestName, MODE_NEWFILE)))
2034 ULONG kill = 1;
2036 txt = cd->Mode == COPYMODE_MOVE ? TEXT_MOVED : TEXT_COPIED;
2037 UnLock(lock);
2038 lock = 0;
2040 if ((in = Open(cd->FileName, MODE_OLDFILE)))
2042 h = CopyFile(in, out, cd->BufferSize, cd);
2043 Close(out); out = NULL;
2044 Close(in);
2046 if (!h)
2048 kill = 0;
2050 if (cd->Mode == COPYMODE_MOVE)
2052 if (KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE, cd))
2054 res = 1;
2057 else
2059 res = 1;
2064 if (out)
2066 Close(out);
2069 if (kill)
2071 KillFileKeepErr(cd->DestName, 0, cd);
2075 if (!res)
2077 printerr = txt;
2078 cd->RetVal = RETURN_WARN;
2080 else
2082 printok = txt;
2087 if (printerr && !(cd->Flags & COPYFLAG_QUIET))
2089 PrintNotDone(cd->Flags & COPYFLAG_VERBOSE ? name : 0,
2090 printerr, cd->Deep, cd->Fib.fib_DirEntryType > 0, cd);
2092 else if (printok)
2094 cd->Flags |= COPYFLAG_DONE;
2096 if (!(cd->Flags & COPYFLAG_QUIET))
2098 if ((cd->Flags & COPYFLAG_VERBOSE))
2100 Printf("%s\n", printok);
2104 SetData(cd->DestName, cd);
2107 if (lock)
2109 UnLock(lock);
2114 LONG CopyFile(BPTR from, BPTR to, ULONG bufsize, struct CopyData *cd)
2116 STRPTR buffer;
2117 LONG s, err = 0;
2119 if (cd->CopyBuf)
2121 buffer = cd->CopyBuf;
2122 bufsize = cd->CopyBufLen;
2124 else
2128 buffer = (STRPTR)AllocMem(bufsize, MEMF_PUBLIC);
2129 if (buffer)
2131 cd->CopyBuf = buffer;
2132 cd->CopyBufLen = bufsize;
2133 break;
2136 bufsize >>= 1;
2138 } while (bufsize >= 512);
2141 if (buffer)
2143 #if USE_BOGUSEOFWORKAROUND
2144 struct FileInfoBlock *fib = (struct FileInfoBlock *) buffer; /* NOTE: bufsize is min 512 bytes */
2146 if (ExamineFH(from, fib))
2148 #warning "****** WARNING: No largefile support! ******"
2149 ULONG filesize = fib->fib_Size, copied = 0;
2151 /*Printf("filesize: %lu\n", filesize);*/
2155 ULONG brk = CTRL_C;
2156 if (brk || (s = Read(from, buffer, bufsize)) == -1 || Write(to, buffer, s) != s)
2158 if (brk)
2160 SetIoErr(ERROR_BREAK);
2162 err = RETURN_FAIL;
2163 break;
2165 else if (s == 0 && copied < filesize)
2167 /* premature EOF with buggy fs */
2168 err = RETURN_FAIL;
2169 break;
2172 copied += s;
2174 } while (copied < filesize);
2176 /*{ LONG ioerr = IoErr();
2177 Printf("copied %lu/%lu\n", copied, filesize);
2178 SetIoErr(ioerr);}*/
2180 else
2181 #endif /* USE_BOGUSEOFWORKAROUND */
2183 /* Stream or so, copy until EOF or error */
2186 ULONG brk = CTRL_C;
2187 if (brk || (s = Read(from, buffer, bufsize)) == -1 || Write(to, buffer, s) != s)
2189 if (brk)
2191 SetIoErr(ERROR_BREAK);
2193 err = RETURN_FAIL;
2194 break;
2196 } while (s == bufsize);
2199 /* Freed at exit to avoid fragmentation */
2200 /*FreeMem(buffer, bufsize);*/
2202 else
2204 err = RETURN_FAIL;
2207 return err;
2211 /* Softlink's path starts always with device name! f.e. "Ram Disk:T/..." */
2212 LONG LinkFile(BPTR from, STRPTR to, ULONG soft, struct CopyData *cd)
2214 if (soft)
2216 LONG ret = FALSE;
2217 UBYTE *name;
2219 name = AllocMem(FILEPATH_SIZE, MEMF_ANY);
2220 if (name)
2222 if (NameFromLock(from, name, FILEPATH_SIZE))
2224 ret = MakeLink(to, name, LINK_SOFT);
2227 FreeMem(name, FILEPATH_SIZE);
2230 return ret;
2232 else
2234 return MakeLink(to, from, LINK_HARD);
2239 /* return 0 means no loop, return != 0 means loop found */
2240 ULONG TestLoop(BPTR srcdir, BPTR destdir, struct CopyData *cd)
2242 ULONG loop = 0;
2243 BPTR par, lock;
2245 lock = destdir;
2247 if (SameDevice(srcdir, destdir))
2251 if (!SameLock(srcdir, lock))
2253 loop = 1;
2255 else
2257 par = ParentDir(lock);
2259 if (lock != destdir)
2261 UnLock(lock);
2264 lock = par;
2267 while(!loop && lock);
2270 if (lock != destdir)
2272 UnLock(lock);
2275 return loop;
2279 void SetData(STRPTR name, struct CopyData *cd)
2281 if (cd->Flags & COPYFLAG_NOPRO)
2283 /* Is already set! - Piru */
2284 //SetProtection(name, 0);
2286 else if (cd->Flags & COPYFLAG_PROTECTION)
2288 SetProtection(name, cd->Fib.fib_Protection & (ULONG) ~FIBF_ARCHIVE);
2291 if (cd->Flags & COPYFLAG_DATES)
2293 SetFileDate(name, &cd->Fib.fib_Date);
2296 if (cd->Flags & COPYFLAG_COMMENT)
2298 SetComment(name, cd->Fib.fib_Comment);
2303 LONG TestDest(STRPTR name, ULONG type, struct CopyData *cd)
2305 LONG ret = TESTDEST_ERROR;
2306 BPTR lock;
2308 if ((lock = Lock(name, SHARED_LOCK)))
2310 struct FileInfoBlock *fib;
2312 if ((fib = (struct FileInfoBlock *)AllocDosObject(DOS_FIB, NULL)))
2314 if (Examine(lock, fib))
2316 UnLock(lock);
2317 lock = 0;
2319 if (type)
2321 if (fib->fib_DirEntryType > 0)
2323 ret = TESTDEST_DIR_OK;
2325 else if (!(cd->Flags & COPYFLAG_DONTOVERWRITE))
2327 if (KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE, cd))
2329 ret = TESTDEST_DELETED;
2332 else
2334 ret = TESTDEST_CANTDELETE;
2337 else if (cd->Flags & COPYFLAG_DONTOVERWRITE)
2339 if (cd->Flags & COPYFLAG_NEWER)
2341 if( CheckVersion( cd ) == CHECKVER_DESTOLDER )
2343 if (KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE, cd))
2345 ret = TESTDEST_DELETED;
2348 else
2350 ret = TESTDEST_CANTDELETE;
2353 else /* normal "dont overwrite mode" */
2355 ret = TESTDEST_CANTDELETE;
2358 else if (KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE, cd))
2360 ret = TESTDEST_DELETED;
2364 FreeDosObject(DOS_FIB, fib);
2367 if (lock)
2369 UnLock(lock);
2372 else
2374 ret = TESTDEST_NONE;
2377 if (ret == TESTDEST_CANTDELETE)
2379 SetIoErr(ERROR_OBJECT_EXISTS);
2382 return ret;
2386 ** We compare current file versions and return the result
2387 ** see CHECKVER_#? values
2390 static LONG CheckVersion( struct CopyData *cd )
2392 struct VersionData vds;
2393 struct VersionData vdd;
2394 LONG resversion = CHECKVER_EQUAL;
2395 LONG resdate = CHECKVER_EQUAL;
2397 if( VersionFind( cd->FileName, &vds, cd ) )
2399 if( VersionFind( cd->DestName, &vdd, cd ) )
2401 /* version and revision must be available to ensure a proper operation */
2402 if( ((vdd.vd_Version != -1) && (vds.vd_Version != -1) && (vdd.vd_Revision != -1) && (vds.vd_Revision != -1)) )
2404 /* first we make the stuff comparable. If one component is missing we reset both */
2405 if( vdd.vd_Year == -1 || vds.vd_Year == -1 )
2407 vdd.vd_Year = 0;
2408 vds.vd_Year = 0;
2410 if( vdd.vd_Month == -1 || vds.vd_Month == -1 )
2412 vdd.vd_Month = 0;
2413 vds.vd_Month = 0;
2415 if( vdd.vd_Day == -1 || vds.vd_Day == -1 )
2417 vdd.vd_Day = 0;
2418 vds.vd_Day = 0;
2421 /* check version */
2422 resversion = CHECKVER_DESTOLDER;
2423 if( ((vdd.vd_Version == vds.vd_Version) && vdd.vd_Revision == vds.vd_Revision ) )
2425 resversion = CHECKVER_EQUAL;
2427 else if( (vdd.vd_Version > vds.vd_Version) ||
2428 ((vdd.vd_Version == vds.vd_Version) && vdd.vd_Revision > vds.vd_Revision ) )
2430 resversion = CHECKVER_DESTNEWER;
2432 /* check date */
2434 resdate = CHECKVER_DESTOLDER;
2435 if( ((vdd.vd_Year == vds.vd_Year) && (vdd.vd_Month == vds.vd_Month) && (vdd.vd_Day == vds.vd_Day) ) )
2437 resdate = CHECKVER_EQUAL;
2439 else
2441 if( ( (vdd.vd_Year > vds.vd_Year ) ||
2442 ( (vdd.vd_Year == vds.vd_Year) && (vdd.vd_Month > vds.vd_Month ) ) ||
2443 ( (vdd.vd_Year == vds.vd_Year) && (vdd.vd_Month == vds.vd_Month ) && (vdd.vd_Day > vds.vd_Day ) ) ))
2445 resdate = CHECKVER_DESTNEWER;
2449 /* plausible check */
2450 if( ((resversion == CHECKVER_DESTNEWER) && (resdate == CHECKVER_DESTOLDER)) || /* newer version with older date */
2451 ((resversion == CHECKVER_DESTOLDER) && (resdate == CHECKVER_DESTNEWER)) ) /* older version with newer date */
2453 /* we maybe should inform the user about this */
2454 return( CHECKVER_EQUAL );
2459 /* compose result */
2461 if( (resversion == resdate) || (resversion == CHECKVER_EQUAL) )
2463 return( resdate );
2465 else
2467 return( resversion );
2474 ** Searches the given file for a version string and fills version data struct with the result.
2475 ** Returns false if no version was found. Returns true if the version got parsed and version data
2476 ** is valid.
2479 #define VERSBUFFERSIZE 4096 /* must be as big as the biggest version string we want to handle. */
2481 static BOOL VersionFind( CONST_STRPTR path, struct VersionData *vds, struct CopyData *cd)
2483 BPTR handle;
2484 STRPTR buf;
2485 ULONG i, rc;
2487 rc = FALSE;
2489 if ( (buf = AllocVec(VERSBUFFERSIZE, MEMF_PUBLIC | MEMF_CLEAR)) )
2491 if ( (handle = Open(path, MODE_OLDFILE)) )
2493 long index = 0;
2495 while( ( (index += Read(handle, &buf[index], VERSBUFFERSIZE-index)) > 5) && !rc )
2497 for (i = 0; i < index-5; i++) {
2498 if( buf[i] == '$' && buf[i+1] == 'V' && buf[i+2] == 'E' && buf[i+3] == 'R' && buf[i+4] == ':' ) {
2499 CopyMem( &buf[i], buf, index-i );
2500 index -= i;
2501 (index += Read(handle, &buf[index], VERSBUFFERSIZE-index));
2502 /* now the version string is aligned and complete in buffer */
2503 makeversionfromstring( buf, vds, cd );
2504 rc = TRUE;
2505 break;
2508 CopyMem( &buf[index-5], &buf[0], 5 );
2509 index = 5;
2511 Close(handle);
2513 FreeVec(buf);
2515 return (rc);
2520 ** This function extracts the version information from a given version string.
2521 ** the result will be store in the given version data structure.
2523 ** NOTE: There is no need to preset the version data struct. All fields will
2524 ** be reset to defaults, so in case of a faulty version string the result data
2525 ** will be checkable.
2528 static
2529 void makeversionfromstring( STRPTR buffer, struct VersionData *vd, struct CopyData *cd)
2531 LONG pos;
2532 ULONG tmp;
2533 STRPTR name;
2535 /* reset data field */
2537 vd->vd_Name[0] = '\0';
2538 vd->vd_Version = -1;
2539 vd->vd_Revision = -1;
2540 vd->vd_Day = -1;
2541 vd->vd_Month = -1;
2542 vd->vd_Year = -1;
2544 buffer = skipspaces( buffer ); /* skip before $VER: */
2545 buffer = skipnonspaces( buffer ); /* skip $VER: */
2546 buffer = skipspaces( buffer ); /* skip spaces before tool name */
2547 name = buffer;
2548 buffer = skipnonspaces( buffer ); /* skip name of tool */
2550 if( (tmp = ((int) buffer - (int) name) ) && *buffer )
2552 CopyMem( name, vd->vd_Name, MIN( tmp, VDNAMESIZE-1) );
2553 vd->vd_Name[MIN( tmp, VDNAMESIZE-1)] = '\0'; /* terminate name string inside target buffer */
2555 buffer = skipspaces( buffer ); /* skip spaces before version */
2556 if( *buffer ) {
2558 /* Do version */
2560 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2562 vd->vd_Version = tmp;
2564 /* Do revision */
2566 buffer += pos;
2567 buffer = skipspaces(buffer);
2568 if (*buffer++ == '.')
2570 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2572 vd->vd_Revision = tmp;
2573 buffer += pos;
2574 buffer = skipspaces(buffer);
2575 if (*buffer++ == '(')
2577 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2579 vd->vd_Day = tmp;
2580 buffer += pos;
2581 if (*buffer++ == '.')
2583 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2585 vd->vd_Month = tmp;
2586 buffer += pos;
2587 if (*buffer++ == '.')
2589 if( (pos = StrToLong((STRPTR) buffer, &tmp)) != -1 )
2591 if( (tmp >= 70) && (tmp <= 99) )
2593 tmp += 1900;
2595 if( (tmp < 70) )
2597 tmp += 2000;
2599 vd->vd_Year = tmp;
2613 /* Return a pointer to a string, stripped by all leading space characters
2614 * (SPACE).
2616 static
2617 STRPTR skipspaces( STRPTR buffer)
2619 for (;; buffer++)
2621 if (buffer[0] == '\0' || buffer[0] != ' ')
2623 return( buffer );
2628 /* Return a pointer to a string, stripped by all non space characters
2629 * (SPACE).
2631 static
2632 STRPTR skipnonspaces( STRPTR buffer)
2634 for (;; buffer++)
2636 if (buffer[0] == '\0' || buffer[0] == ' ')
2638 return( buffer );