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