16b561727d0de8ec932c2644e89b8eb1052f5ac8
[reactos.git] / rosapps / cmd / copy.c
1 /*
2 * COPY.C -- copy internal command.
3 *
4 *
5 * History:
6 *
7 * 01-Aug-98 (Rob Lake z63rrl@morgan.ucs.mun.ca)
8 * started
9 *
10 * 13-Aug-1998 (John P. Price)
11 * fixed memory leak problem in copy function.
12 * fixed copy function so it would work with wildcards in the source
13 *
14 * 13-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
15 * Added COPY command to CMD.
16 *
17 * 26-Jan-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
18 * Replaced CRT io functions by Win32 io functions.
19 */
20
21 #define WIN32_LEAN_AND_MEAN
22
23 #include "config.h"
24
25 #ifdef INCLUDE_CMD_COPY
26
27 #include <windows.h>
28 #include <tchar.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <ctype.h>
32
33 #include "cmd.h"
34
35
36 #define VERIFY 1 /* VERIFY Switch */
37 #define BINARY 2 /* File is to be copied as BINARY */
38 #define ASCII 4 /* File is to be copied as ASCII */
39 #define PROMPT 8 /* Prompt before overwriting files */
40 #define NPROMPT 16 /* Do not prompt before overwriting files */
41 #define HELP 32 /* Help was asked for */
42 #define SOURCE 128 /* File is a source */
43
44
45 typedef struct tagFILES
46 {
47 struct tagFILES *next;
48 TCHAR szFile[MAX_PATH];
49 DWORD dwFlag; /* BINARY -xor- ASCII */
50 } FILES, *LPFILES;
51
52
53 static BOOL DoSwitches (LPTSTR, LPDWORD);
54 static BOOL AddFile (LPFILES, char *, int *, int *, unsigned *);
55 static BOOL AddFiles (LPFILES, char *, int *, int *, int *, unsigned *);
56 static BOOL GetDestination (LPFILES, LPFILES);
57 static INT ParseCommand (LPFILES, int, char **, LPDWORD);
58 static VOID DeleteFileList (LPFILES);
59 static INT Overwrite (LPTSTR);
60
61
62
63 static BOOL IsDirectory (LPTSTR fn)
64 {
65 if (!IsValidFileName (fn))
66 return FALSE;
67 return (GetFileAttributes (fn) & FILE_ATTRIBUTE_DIRECTORY);
68 }
69
70
71 static BOOL DoSwitches (LPTSTR arg, LPDWORD lpdwFlags)
72 {
73 if (!_tcsicmp (arg, _T("/-Y")))
74 {
75 *lpdwFlags |= PROMPT;
76 *lpdwFlags &= ~NPROMPT;
77 return TRUE;
78 }
79 else if (_tcslen (arg) > 2)
80 {
81 error_too_many_parameters ("");
82 return FALSE;
83 }
84
85 switch (_totupper (arg[1]))
86 {
87 case _T('V'):
88 *lpdwFlags |= VERIFY;
89 break;
90
91 case _T('A'):
92 *lpdwFlags |= ASCII;
93 *lpdwFlags &= ~BINARY;
94 break;
95
96 case _T('B'):
97 *lpdwFlags |= BINARY;
98 *lpdwFlags &= ~ASCII;
99 break;
100
101 case _T('Y'):
102 *lpdwFlags &= ~PROMPT;
103 *lpdwFlags |= NPROMPT;
104 break;
105
106 default:
107 error_invalid_switch (arg[1]);
108 return FALSE;
109 }
110 return TRUE;
111 }
112
113
114 static BOOL
115 AddFile (LPFILES f, char *arg, int *source, int *dest, unsigned *flags)
116 {
117 if (*dest)
118 {
119 error_too_many_parameters ("");
120 return FALSE;
121 }
122 if (*source)
123 {
124 *dest = 1;
125 f->dwFlag = 0;
126 }
127 else
128 {
129 *source = 1;
130 f->dwFlag = SOURCE;
131 }
132 _tcscpy(f->szFile, arg);
133 f->dwFlag |= *flags & ASCII ? ASCII : BINARY;
134 if ((f->next = (LPFILES)malloc (sizeof (FILES))) == NULL)
135 {
136 error_out_of_memory ();
137 return FALSE;
138 }
139 f = f->next;
140 f->dwFlag = 0;
141 f->next = NULL;
142 return TRUE;
143 }
144
145
146 static BOOL
147 AddFiles (LPFILES f, char *arg, int *source, int *dest,
148 int *count, unsigned *flags)
149 {
150 char t[128];
151 int j;
152 int k;
153
154 if (*dest)
155 {
156 error_too_many_parameters ("");
157 return FALSE;
158 }
159
160 j = 0;
161 k = 0;
162
163 while (arg[j] == _T('+'))
164 j++;
165
166 while (arg[j] != _T('\0'))
167 {
168 t[k] = arg[j++];
169 if (t[k] == '+' || arg[j] == _T('\0'))
170 {
171 if (!k)
172 continue;
173 if (arg[j] == _T('\0') && t[k] != _T('+'))
174 k++;
175 t[k] = _T('\0');
176 *count += 1;
177 _tcscpy (f->szFile, t);
178 *source = 1;
179 if (*flags & ASCII)
180 f->dwFlag |= *flags | SOURCE | ASCII;
181 else
182 f->dwFlag |= *flags | BINARY | SOURCE;
183
184 if ((f->next = (LPFILES)malloc (sizeof (FILES))) == NULL)
185 {
186 error_out_of_memory ();
187 return FALSE;
188 }
189 f = f->next;
190 f->next = NULL;
191 k = 0;
192 f->dwFlag = 0;
193 continue;
194 }
195 k++;
196 }
197
198 if (arg[--j] == _T('+'))
199 *source = 0;
200
201 return 1;
202 }
203
204
205 static BOOL GetDestination (LPFILES f, LPFILES dest)
206 {
207 LPFILES p;
208 LPFILES start = f;
209
210 while (f->next != NULL)
211 {
212 p = f;
213 f = f->next;
214 }
215
216 f = p;
217
218 if ((f->dwFlag & SOURCE) == 0)
219 {
220 free (p->next);
221 p->next = NULL;
222 _tcscpy (dest->szFile, f->szFile);
223 dest->dwFlag = f->dwFlag;
224 dest->next = NULL;
225 f = start;
226 return TRUE;
227 }
228
229 return FALSE;
230 }
231
232
233 static INT
234 ParseCommand (LPFILES f, int argc, char **arg, LPDWORD lpdwFlags)
235 {
236 INT i;
237 INT dest;
238 INT source;
239 INT count;
240
241 dest = 0;
242 source = 0;
243 count = 0;
244
245 for (i = 0; i < argc; i++)
246 {
247 if (arg[i][0] == _T('/'))
248 {
249 if (!DoSwitches (arg[i], lpdwFlags))
250 return -1;
251 }
252 else
253 {
254 if (!_tcscmp(arg[i], _T("+")))
255 source = 0;
256 else if (!_tcschr(arg[i], _T('+')) && source)
257 {
258 if (!AddFile(f, arg[i], &source, &dest, lpdwFlags))
259 return -1;
260 f = f->next;
261 count++;
262 }
263 else
264 {
265 if (!AddFiles(f, arg[i], &source, &dest, &count, lpdwFlags))
266 return -1;
267 while (f->next != NULL)
268 f = f->next;
269 }
270 }
271 }
272 #ifdef _DEBUG
273 DebugPrintf ("ParseCommand: flags has %s\n",
274 *lpdwFlags & ASCII ? "ASCII" : "BINARY");
275 #endif
276 return count;
277 }
278
279
280 static VOID DeleteFileList (LPFILES f)
281 {
282 LPFILES temp;
283
284 while (f != NULL)
285 {
286 temp = f;
287 f = f->next;
288 free (temp);
289 }
290 }
291
292
293 static INT
294 Overwrite (LPTSTR fn)
295 {
296 TCHAR inp[10];
297 LPTSTR p;
298
299 ConOutPrintf (_T("Overwrite %s (Yes/No/All)? "), fn);
300 ConInString (inp, 10);
301
302 _tcsupr (inp);
303 for (p = inp; _istspace (*p); p++)
304 ;
305
306 if (*p != _T('Y') && *p != _T('A'))
307 return 0;
308 if (*p == _T('A'))
309 return 2;
310
311 return 1;
312 }
313
314
315 #define BUFF_SIZE 16384 /* 16k = max buffer size */
316
317
318 int copy (LPTSTR source, LPTSTR dest, int append, LPDWORD lpdwFlags)
319 {
320 FILETIME srctime;
321 HANDLE hFileSrc;
322 HANDLE hFileDest;
323 LPBYTE buffer;
324 DWORD dwAttrib;
325 DWORD dwRead;
326 DWORD dwWritten;
327 DWORD i;
328 BOOL bEof = FALSE;
329
330 #ifdef _DEBUG
331 DebugPrintf ("checking mode\n");
332 #endif
333
334 dwAttrib = GetFileAttributes (source);
335
336 hFileSrc = CreateFile (source, GENERIC_READ, FILE_SHARE_READ,
337 NULL, OPEN_EXISTING, 0, NULL);
338 if (hFileSrc == INVALID_HANDLE_VALUE)
339 {
340 ConErrPrintf (_T("Error: Cannot open source - %s!\n"), source);
341 return 0;
342 }
343
344 #ifdef _DEBUG
345 DebugPrintf (_T("getting time\n"));
346 #endif
347
348 GetFileTime (hFileSrc, &srctime, NULL, NULL);
349
350 #ifdef _DEBUG
351 DebugPrintf (_T("copy: flags has %s\n"),
352 *lpdwFlags & ASCII ? "ASCII" : "BINARY");
353 #endif
354
355 if (!IsValidFileName (dest))
356 {
357 #ifdef _DEBUG
358 DebugPrintf (_T("opening/creating\n"));
359 #endif
360 hFileDest =
361 CreateFile (dest, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
362 }
363 else if (!append)
364 {
365 if (!_tcscmp (dest, source))
366 {
367 ConErrPrintf (_T("Error: Can't copy onto itself!\n"));
368 CloseHandle (hFileSrc);
369 return 0;
370 }
371
372 #ifdef _DEBUG
373 DebugPrintf (_T("SetFileAttributes (%s, FILE_ATTRIBUTE_NORMAL);\n"), dest);
374 #endif
375 SetFileAttributes (dest, FILE_ATTRIBUTE_NORMAL);
376
377 #ifdef _DEBUG
378 DebugPrintf (_T("DeleteFile (%s);\n"), dest);
379 #endif
380 DeleteFile (dest);
381
382 hFileDest =
383 CreateFile (dest, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
384 }
385 else
386 {
387 LONG lFilePosHigh = 0;
388
389 if (!_tcscmp (dest, source))
390 {
391 CloseHandle (hFileSrc);
392 return 0;
393 }
394
395 #ifdef _DEBUG
396 DebugPrintf (_T("opening/appending\n"));
397 #endif
398 hFileDest =
399 CreateFile (dest, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
400 SetFilePointer (hFileDest, 0, &lFilePosHigh,FILE_END);
401 }
402
403 if (hFileDest == INVALID_HANDLE_VALUE)
404 {
405 CloseHandle (hFileSrc);
406 error_path_not_found ();
407 return 0;
408 }
409
410 buffer = (LPBYTE)malloc (BUFF_SIZE);
411 if (buffer == NULL)
412 {
413 CloseHandle (hFileDest);
414 CloseHandle (hFileSrc);
415 error_out_of_memory ();
416 return 0;
417 }
418
419 ConOutPuts (source);
420
421 do
422 {
423 ReadFile (hFileSrc, buffer, BUFF_SIZE, &dwRead, NULL);
424 if (*lpdwFlags & ASCII)
425 {
426 for (i = 0; i < dwRead; i++)
427 {
428 if (((LPTSTR)buffer)[i] == 0x1A)
429 {
430 bEof = TRUE;
431 break;
432 }
433 }
434 dwRead = i;
435 }
436
437 if (dwRead == 0)
438 break;
439
440 WriteFile (hFileDest, buffer, dwRead, &dwWritten, NULL);
441 if (dwWritten != dwRead)
442 {
443 ConErrPrintf (_T("Error writing destination!\n"));
444 free (buffer);
445 CloseHandle (hFileDest);
446 CloseHandle (hFileSrc);
447 return 0;
448 }
449 }
450 while (dwRead && !bEof);
451
452 #ifdef _DEBUG
453 DebugPrintf (_T("setting time\n"));
454 #endif
455 SetFileTime (hFileDest, &srctime, NULL, NULL);
456
457 if (*lpdwFlags & ASCII)
458 {
459 ((LPTSTR)buffer)[0] = 0x1A;
460 ((LPTSTR)buffer)[1] = _T('\0');
461 #ifdef _DEBUG
462 DebugPrintf (_T("appending ^Z\n"));
463 #endif
464 WriteFile (hFileDest, buffer, sizeof(TCHAR), &dwWritten, NULL);
465 }
466
467 free (buffer);
468 CloseHandle (hFileDest);
469 CloseHandle (hFileSrc);
470
471 #ifdef _DEBUG
472 DebugPrintf (_T("setting mode\n"));
473 #endif
474 SetFileAttributes (dest, dwAttrib);
475
476 return 1;
477 }
478
479
480 int setup_copy (LPFILES sources, char **p, BOOL bMultiple,
481 char *drive_d, char *dir_d, char *file_d,
482 char *ext_d, int *append, LPDWORD lpdwFlags)
483 {
484 WIN32_FIND_DATA find;
485
486 char drive_s[_MAX_DRIVE],
487 dir_s[_MAX_DIR],
488 file_s[_MAX_FNAME],
489 ext_s[_MAX_EXT];
490 char from_merge[_MAX_PATH];
491
492 LPTSTR real_source;
493 LPTSTR real_dest;
494
495 INT nCopied = 0;
496 BOOL bAll = FALSE;
497 BOOL bDone;
498 HANDLE hFind;
499
500
501 real_source = (LPTSTR)malloc (MAX_PATH);
502 real_dest = (LPTSTR)malloc (MAX_PATH);
503
504 if (!real_source || !real_dest)
505 {
506 error_out_of_memory ();
507 DeleteFileList (sources);
508 free (real_source);
509 free (real_dest);
510 freep (p);
511 return 0;
512 }
513
514 while (sources->next != NULL)
515 {
516 _splitpath (sources->szFile, drive_s, dir_s, file_s, ext_s);
517 hFind = FindFirstFile (sources->szFile, &find);
518 if (hFind == INVALID_HANDLE_VALUE)
519 {
520 error_file_not_found();
521 DeleteFileList (sources);
522 freep(p);
523 free(real_source);
524 free(real_dest);
525 return 0;
526 }
527
528 do
529 {
530 if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
531 goto next;
532
533 _makepath(from_merge, drive_d, dir_d, file_d, ext_d);
534 if (from_merge[_tcslen(from_merge) - 1] == _T('\\'))
535 from_merge[_tcslen(from_merge) - 1] = 0;
536
537 if (IsDirectory (from_merge))
538 {
539 bMultiple = FALSE;
540 _tcscat (from_merge, _T("\\"));
541 _tcscat (from_merge, find.cFileName);
542 }
543 else
544 bMultiple = TRUE;
545
546 _tcscpy (real_dest, from_merge);
547 _makepath (real_source, drive_s, dir_s, find.cFileName, NULL);
548
549 #ifdef _DEBUG
550 DebugPrintf (_T("copying %s -> %s (%sappending%s)\n"),
551 real_source, real_dest,
552 *append ? "" : "not ",
553 sources->dwFlag & ASCII ? ", ASCII" : ", BINARY");
554 #endif
555
556 if (IsValidFileName (real_dest) && !bAll)
557 {
558 int over = Overwrite (real_dest);
559 if (over == 2)
560 bAll = TRUE;
561 else if (over == 0)
562 goto next;
563 else if (bMultiple)
564 bAll = TRUE;
565 }
566 if (copy (real_source, real_dest, *append, lpdwFlags))
567 nCopied++;
568 next:
569 bDone = FindNextFile (hFind, &find);
570
571 if (bMultiple)
572 *append = 1;
573 }
574 while (bDone);
575
576 FindClose (hFind);
577 sources = sources->next;
578
579 }
580 free (real_source);
581 free (real_dest);
582
583 return nCopied;
584 }
585
586
587 INT cmd_copy (LPTSTR first, LPTSTR rest)
588 {
589 char **p;
590 char drive_d[_MAX_DRIVE],
591 dir_d[_MAX_DIR],
592 file_d[_MAX_FNAME],
593 ext_d[_MAX_EXT];
594
595 int argc,
596 append,
597 files,
598 copied;
599
600 LPFILES sources;
601 FILES dest;
602 LPFILES start;
603 BOOL bMultiple;
604 BOOL bWildcards;
605 BOOL bDestFound;
606 DWORD dwFlags = 0;
607
608 if (!_tcsncmp (rest, _T("/?"), 2))
609 {
610 ConOutPuts (_T("Copies one or more files to another location.\n"
611 "\n"
612 "COPY [/V][/Y|/-Y][/A|/B] source [/A|/B]\n"
613 " [+ source [/A|/B] [+ ...]] [destination [/A|/B]]\n"
614 "\n"
615 " source Specifies the file or files to be copied.\n"
616 " /A Indicates an ASCII text file.\n"
617 " /B Indicates a binary file.\n"
618 " destination Specifies the directory and/or filename for the new file(s).\n"
619 " /V Verifies that new files are written correctly.\n"
620 " /Y Suppresses prompting to confirm you want to overwrite an\n"
621 " existing destination file.\n"
622 " /-Y Causes prompting to confirm you want to overwrite an\n"
623 " existing destination file.\n"
624 "\n"
625 "The switch /Y may be present in the COPYCMD environment variable.\n"
626 "..."));
627 return 1;
628 }
629
630 p = split (rest, &argc);
631
632 if (argc == 0)
633 {
634 error_req_param_missing ();
635 return 0;
636 }
637
638 sources = (LPFILES)malloc (sizeof (FILES));
639 if (!sources)
640 {
641 error_out_of_memory ();
642 return 0;
643 }
644 sources->next = NULL;
645 sources->dwFlag = 0;
646
647 if ((files = ParseCommand (sources, argc, p, &dwFlags)) == -1)
648 {
649 DeleteFileList (sources);
650 freep (p);
651 return 0;
652 }
653 else if (files == 0)
654 {
655 error_req_param_missing();
656 DeleteFileList (sources);
657 freep (p);
658 return 0;
659 }
660 start = sources;
661
662 bDestFound = GetDestination (sources, &dest);
663 if (bDestFound)
664 {
665 _splitpath (dest.szFile, drive_d, dir_d, file_d, ext_d);
666 if (IsDirectory (dest.szFile))
667 {
668 _tcscat (dir_d, file_d);
669 _tcscat (dir_d, ext_d);
670 file_d[0] = _T('\0');
671 ext_d[0] = _T('\0');
672 }
673 }
674
675 if (_tcschr (dest.szFile, _T('*')) || _tcschr (dest.szFile, _T('?')))
676 bWildcards = TRUE;
677 else
678 bWildcards = FALSE;
679
680 if (strchr(rest, '+'))
681 bMultiple = TRUE;
682 else
683 bMultiple = FALSE;
684
685 append = 0;
686 copied = 0;
687
688 if (bDestFound && !bWildcards)
689 {
690 copied = setup_copy (sources, p, bMultiple, drive_d, dir_d, file_d, ext_d, &append, &dwFlags);
691 }
692 else if (bDestFound && bWildcards)
693 {
694 ConErrPrintf (_T("Error: Not implemented yet!\n"));
695 DeleteFileList (sources);
696 freep (p);
697 return 0;
698 }
699 else if (!bDestFound && !bMultiple)
700 {
701 _splitpath (sources->szFile, drive_d, dir_d, file_d, ext_d);
702 if (IsDirectory (sources->szFile))
703 {
704 _tcscat (dir_d, file_d);
705 _tcscat (dir_d, ext_d);
706 file_d[0] = _T('\0');
707 ext_d[0] = _T('\0');
708 }
709 copied = setup_copy (sources, p, FALSE, "", "", file_d, ext_d, &append, &dwFlags);
710 }
711 else
712 {
713 _splitpath(sources->szFile, drive_d, dir_d, file_d, ext_d);
714 if (IsDirectory (sources->szFile))
715 {
716 _tcscat (dir_d, file_d);
717 _tcscat (dir_d, ext_d);
718 file_d[0] = _T('\0');
719 ext_d[0] = _T('\0');
720 }
721
722 ConOutPuts (sources->szFile);
723 append = 1;
724 copied = setup_copy (sources->next, p, bMultiple, drive_d, dir_d, file_d, ext_d, &append, &dwFlags) + 1;
725 }
726
727 DeleteFileList (start);
728 freep (p);
729 ConOutPrintf (_T(" %d file(s) copied\n"), copied);
730
731 return 1;
732 }
733 #endif /* INCLUDE_CMD_COPY */