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