[CMD]: Continue refactoring to lay out the way to using the CONUTILS library in CMD...
[reactos.git] / reactos / base / shell / 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)
15 * Added COPY command to CMD.
16 *
17 * 26-Jan-1998 (Eric Kohl)
18 * Replaced CRT io functions by Win32 io functions.
19 *
20 * 27-Oct-1998 (Eric Kohl)
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 * 13-Jul-2005 (Brandon Turner <turnerb7@msu.edu>)
27 * Rewrite to clean up copy and support wildcard.
28 *
29 * 20-Jul-2005 (Brandon Turner <turnerb7@msu.edu>)
30 * Add touch syntax. "copy arp.exe+,,"
31 * Copy command is now completed.
32 */
33
34 #include "precomp.h"
35
36 #ifdef INCLUDE_CMD_COPY
37
38 enum
39 {
40 COPY_ASCII = 0x001, /* /A */
41 COPY_DECRYPT = 0x004, /* /D */
42 COPY_VERIFY = 0x008, /* /V : Dummy, Never will be Implemented */
43 COPY_SHORTNAME = 0x010, /* /N : Dummy, Never will be Implemented */
44 COPY_NO_PROMPT = 0x020, /* /Y */
45 COPY_PROMPT = 0x040, /* /-Y */
46 COPY_RESTART = 0x080, /* /Z */
47 COPY_BINARY = 0x100, /* /B */
48 };
49
50 INT
51 copy(TCHAR source[MAX_PATH],
52 TCHAR dest[MAX_PATH],
53 INT append,
54 DWORD lpdwFlags,
55 BOOL bTouch)
56 {
57 FILETIME srctime,NewFileTime;
58 HANDLE hFileSrc;
59 HANDLE hFileDest;
60 LPBYTE buffer;
61 DWORD dwAttrib;
62 DWORD dwRead;
63 DWORD dwWritten;
64 BOOL bEof = FALSE;
65 TCHAR TrueDest[MAX_PATH];
66 TCHAR TempSrc[MAX_PATH];
67 TCHAR * FileName;
68 SYSTEMTIME CurrentTime;
69
70 /* Check Breaker */
71 if (CheckCtrlBreak(BREAK_INPUT))
72 return 0;
73
74 TRACE ("checking mode\n");
75
76 if (bTouch)
77 {
78 hFileSrc = CreateFile (source, GENERIC_WRITE, FILE_SHARE_READ,
79 NULL, OPEN_EXISTING, 0, NULL);
80 if (hFileSrc == INVALID_HANDLE_VALUE)
81 {
82 ConOutResPrintf(STRING_COPY_ERROR1, source);
83 nErrorLevel = 1;
84 return 0;
85 }
86
87 GetSystemTime(&CurrentTime);
88 SystemTimeToFileTime(&CurrentTime, &NewFileTime);
89 if (SetFileTime(hFileSrc,(LPFILETIME) NULL, (LPFILETIME) NULL, &NewFileTime))
90 {
91 CloseHandle(hFileSrc);
92 nErrorLevel = 1;
93 return 1;
94
95 }
96 else
97 {
98 CloseHandle(hFileSrc);
99 return 0;
100 }
101 }
102
103 dwAttrib = GetFileAttributes (source);
104
105 hFileSrc = CreateFile (source, GENERIC_READ, FILE_SHARE_READ,
106 NULL, OPEN_EXISTING, 0, NULL);
107 if (hFileSrc == INVALID_HANDLE_VALUE)
108 {
109 ConOutResPrintf(STRING_COPY_ERROR1, source);
110 nErrorLevel = 1;
111 return 0;
112 }
113
114 TRACE ("getting time\n");
115
116 GetFileTime (hFileSrc, &srctime, NULL, NULL);
117
118 TRACE ("copy: flags has %s\n",
119 lpdwFlags & COPY_ASCII ? "ASCII" : "BINARY");
120
121 /* Check to see if /D or /Z are true, if so we need a middle
122 man to copy the file too to allow us to use CopyFileEx later */
123 if (lpdwFlags & COPY_DECRYPT)
124 {
125 GetEnvironmentVariable(_T("TEMP"),TempSrc,MAX_PATH);
126 _tcscat(TempSrc,_T("\\"));
127 FileName = _tcsrchr(source,_T('\\'));
128 FileName++;
129 _tcscat(TempSrc,FileName);
130 /* This is needed to be on the end to prevent an error
131 if the user did "copy /D /Z foo bar then it would be copied
132 too %TEMP%\foo here and when %TEMP%\foo when it sets it up
133 for COPY_RESTART, this would mean it is copying to itself
134 which would error when it tried to open the handles for ReadFile
135 and WriteFile */
136 _tcscat(TempSrc,_T(".decrypt"));
137 if (!CopyFileEx(source, TempSrc, NULL, NULL, FALSE, COPY_FILE_ALLOW_DECRYPTED_DESTINATION))
138 {
139 CloseHandle (hFileSrc);
140 nErrorLevel = 1;
141 return 0;
142 }
143 _tcscpy(source, TempSrc);
144 }
145
146
147 if (lpdwFlags & COPY_RESTART)
148 {
149 _tcscpy(TrueDest, dest);
150 GetEnvironmentVariable(_T("TEMP"),dest,MAX_PATH);
151 _tcscat(dest,_T("\\"));
152 FileName = _tcsrchr(TrueDest,_T('\\'));
153 FileName++;
154 _tcscat(dest,FileName);
155 }
156
157
158 if (!IsExistingFile (dest))
159 {
160 TRACE ("opening/creating\n");
161 hFileDest =
162 CreateFile (dest, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
163 }
164 else if (!append)
165 {
166 TRACE ("SetFileAttributes (%s, FILE_ATTRIBUTE_NORMAL);\n", debugstr_aw(dest));
167 SetFileAttributes (dest, FILE_ATTRIBUTE_NORMAL);
168
169 TRACE ("DeleteFile (%s);\n", debugstr_aw(dest));
170 DeleteFile (dest);
171
172 hFileDest = CreateFile (dest, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
173 }
174 else
175 {
176 LONG lFilePosHigh = 0;
177
178 if (!_tcscmp (dest, source))
179 {
180 CloseHandle (hFileSrc);
181 return 0;
182 }
183
184 TRACE ("opening/appending\n");
185 SetFileAttributes (dest, FILE_ATTRIBUTE_NORMAL);
186
187 hFileDest =
188 CreateFile (dest, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
189
190 /* Move to end of file to start writing */
191 SetFilePointer (hFileDest, 0, &lFilePosHigh,FILE_END);
192 }
193
194
195 if (hFileDest == INVALID_HANDLE_VALUE)
196 {
197 CloseHandle (hFileSrc);
198 ConOutResPuts(STRING_ERROR_PATH_NOT_FOUND);
199 nErrorLevel = 1;
200 return 0;
201 }
202
203 /* A page-aligned buffer usually give more speed */
204 buffer = VirtualAlloc(NULL, BUFF_SIZE, MEM_COMMIT, PAGE_READWRITE);
205 if (buffer == NULL)
206 {
207 CloseHandle (hFileDest);
208 CloseHandle (hFileSrc);
209 ConOutResPuts(STRING_ERROR_OUT_OF_MEMORY);
210 nErrorLevel = 1;
211 return 0;
212 }
213
214 do
215 {
216 ReadFile (hFileSrc, buffer, BUFF_SIZE, &dwRead, NULL);
217 if (lpdwFlags & COPY_ASCII)
218 {
219 LPBYTE pEof = memchr(buffer, 0x1A, dwRead);
220 if (pEof != NULL)
221 {
222 bEof = TRUE;
223 dwRead = pEof-buffer+1;
224 break;
225 }
226 }
227
228 if (dwRead == 0)
229 break;
230
231 WriteFile (hFileDest, buffer, dwRead, &dwWritten, NULL);
232 if (dwWritten != dwRead || CheckCtrlBreak(BREAK_INPUT))
233 {
234 ConOutResPuts(STRING_COPY_ERROR3);
235
236 VirtualFree (buffer, 0, MEM_RELEASE);
237 CloseHandle (hFileDest);
238 CloseHandle (hFileSrc);
239 nErrorLevel = 1;
240 return 0;
241 }
242 }
243 while (!bEof);
244
245 TRACE ("setting time\n");
246 SetFileTime (hFileDest, &srctime, NULL, NULL);
247
248 if ((lpdwFlags & COPY_ASCII) && !bEof)
249 {
250 /* we're dealing with ASCII files! */
251 buffer[0] = 0x1A;
252 TRACE ("appending ^Z\n");
253 WriteFile (hFileDest, buffer, sizeof(CHAR), &dwWritten, NULL);
254 }
255
256 VirtualFree (buffer, 0, MEM_RELEASE);
257 CloseHandle (hFileDest);
258 CloseHandle (hFileSrc);
259
260 TRACE ("setting mode\n");
261 SetFileAttributes (dest, dwAttrib);
262
263 /* Now finish off the copy if needed with CopyFileEx */
264 if (lpdwFlags & COPY_RESTART)
265 {
266 if (!CopyFileEx(dest, TrueDest, NULL, NULL, FALSE, COPY_FILE_RESTARTABLE))
267 {
268 nErrorLevel = 1;
269 DeleteFile(dest);
270 return 0;
271 }
272 /* Take care of file in the temp folder */
273 DeleteFile(dest);
274
275 }
276
277 if (lpdwFlags & COPY_DECRYPT)
278 DeleteFile(TempSrc);
279
280 return 1;
281 }
282
283
284 static INT CopyOverwrite (LPTSTR fn)
285 {
286 /*ask the user if they want to override*/
287 INT res;
288 ConOutResPrintf(STRING_COPY_HELP1, fn);
289 res = FilePromptYNA (0);
290 return res;
291 }
292
293 /* The following lines of copy were written by someone else
294 (most likely Eric Kohl) and it was taken from ren.c */
295 static void
296 BuildFileName(
297 LPTSTR pszSource,
298 LPTSTR pszTarget,
299 LPTSTR pszOutput)
300 {
301 /* build destination file name */
302 while (*pszTarget != 0)
303 {
304 if (*pszTarget == _T('*'))
305 {
306 pszTarget++;
307 while ((*pszSource != 0) && (*pszSource != *pszTarget))
308 {
309 *pszOutput++ = *pszSource++;
310 }
311 }
312 else if (*pszTarget == _T('?'))
313 {
314 pszTarget++;
315 if (*pszSource != 0)
316 {
317 *pszOutput++ = *pszSource++;
318 }
319 }
320 else
321 {
322 *pszOutput++ = *pszTarget++;
323 if (*pszSource != 0)
324 pszSource++;
325 }
326 }
327
328 *pszOutput = 0;
329 }
330
331 INT cmd_copy(LPTSTR param)
332 {
333 LPTSTR *arg;
334 INT argc, i, nFiles, nOverwrite = 0, nSrc = -1, nDes = -1;
335 /* this is the path up to the folder of the src and dest ie C:\windows\ */
336 TCHAR szDestPath[MAX_PATH];
337 TCHAR szSrcPath[MAX_PATH];
338 DWORD dwFlags = 0;
339 /* If this is the type of copy where we are adding files */
340 BOOL bAppend = FALSE;
341 WIN32_FIND_DATA findBuffer;
342 HANDLE hFile = NULL;
343 BOOL bTouch = FALSE;
344 /* Pointer to keep track of how far through the append input(file1+file2+file3) we are */
345 TCHAR * appendPointer = _T("\0");
346 /* The full path to src and dest. This has drive letter, folders, and filename */
347 TCHAR tmpDestPath[MAX_PATH];
348 TCHAR tmpSrcPath[MAX_PATH];
349 /* A bool to know whether or not the destination name will be taken from the input */
350 BOOL bSrcName = FALSE;
351 /* Seems like a waste but it is a pointer used to copy from input to PreserveName */
352 TCHAR * UseThisName;
353 /* for CMDCOPY env */
354 TCHAR *evar;
355 int size;
356 TCHAR * szTouch;
357 BOOL bHasWildcard, bDone = FALSE, bMoreFiles = FALSE;
358 /* Used for something like "copy c*.exe d*.exe" */
359 BOOL bMultipleSource = FALSE, bMultipleDest = FALSE;
360
361
362 /* Show help/usage info */
363 if (!_tcsncmp(param, _T("/?"), 2))
364 {
365 ConOutResPaging(TRUE, STRING_COPY_HELP2);
366 return 0;
367 }
368
369 nErrorLevel = 0;
370
371 /* Get the env variable value if it exists */
372 evar = cmd_alloc(512 * sizeof(TCHAR));
373 if (evar == NULL)
374 size = 0;
375 else
376 size = GetEnvironmentVariable (_T("COPYCMD"), evar, 512);
377
378 if (size > 512)
379 {
380 TCHAR *old_evar = evar;
381 evar = cmd_realloc(evar,size * sizeof(TCHAR) );
382 if (evar!=NULL)
383 size = GetEnvironmentVariable (_T("COPYCMD"), evar, size);
384 else
385 {
386 size=0;
387 evar = old_evar;
388 }
389 }
390
391 /* check see if we did get any env variable */
392 if (size != 0)
393 {
394 int t = 0;
395
396 /* scan and set the flags */
397 for (t = 0; t < size; t++)
398 {
399 if (_tcsncicmp(_T("/A"),&evar[t],2) == 0)
400 {
401 dwFlags |=COPY_ASCII;
402 t++;
403 }
404 else if (_tcsncicmp(_T("/B"),&evar[t],2) == 0)
405 {
406 dwFlags |= COPY_BINARY;
407 t++;
408 }
409 else if (_tcsncicmp(_T("/D"),&evar[t],2) == 0)
410 {
411 dwFlags |= COPY_DECRYPT;
412 t++;
413 }
414 else if (_tcsncicmp(_T("/V"),&evar[t],2) == 0)
415 {
416 dwFlags |= COPY_VERIFY;
417 t++;
418 }
419 else if (_tcsncicmp(_T("/N"),&evar[t],2) == 0)
420 {
421 dwFlags |= COPY_SHORTNAME;
422 t++;
423 }
424 else if (_tcsncicmp(_T("/Y"),&evar[t],2) == 0)
425 {
426 dwFlags |= COPY_NO_PROMPT;
427 t++;
428 }
429 else if (_tcsncicmp(_T("/-Y"),&evar[t],3) == 0)
430 {
431 dwFlags |= COPY_PROMPT;
432 t+=2;
433 }
434 else if (_tcsncicmp(_T("/Z"),&evar[t],2) == 0)
435 {
436 dwFlags |= COPY_PROMPT;
437 t++;
438 }
439 }
440 }
441 cmd_free(evar);
442
443
444 /* Split the user input into array */
445 arg = split(param, &argc, FALSE, TRUE);
446 nFiles = argc;
447
448 /* Read switches and count files */
449 for (i = 0; i < argc; i++)
450 {
451 if (*arg[i] == _T('/'))
452 {
453 if (_tcslen(arg[i]) >= 2)
454 {
455 switch (_totupper(arg[i][1]))
456 {
457 case _T('A'):
458 dwFlags |= COPY_ASCII;
459 break;
460
461 case _T('B'):
462 dwFlags |= COPY_BINARY;
463 break;
464
465 case _T('D'):
466 dwFlags |= COPY_DECRYPT;
467 break;
468
469 case _T('V'):
470 dwFlags |= COPY_VERIFY;
471 break;
472
473 case _T('N'):
474 dwFlags |= COPY_SHORTNAME;
475 break;
476
477 case _T('Y'):
478 dwFlags |= COPY_NO_PROMPT;
479 dwFlags &= ~COPY_PROMPT;
480 break;
481
482 case _T('-'):
483 if (_tcslen(arg[i]) >= 3)
484 {
485 if (_totupper(arg[i][2]) == _T('Y'))
486 {
487 dwFlags &= ~COPY_NO_PROMPT;
488 dwFlags |= COPY_PROMPT;
489 }
490 }
491
492 break;
493
494 case _T('Z'):
495 dwFlags |= COPY_RESTART;
496 break;
497
498 default:
499 /* Invalid switch */
500 ConOutResPrintf(STRING_ERROR_INVALID_SWITCH, _totupper(arg[i][1]));
501 nErrorLevel = 1;
502 freep (arg);
503 return 1;
504 break;
505 }
506 }
507 /* If it was a switch, subtract from total arguments */
508 nFiles--;
509 }
510 else
511 {
512 /* If it isn't a switch then it is the source or destination */
513 if (nSrc == -1)
514 {
515 nSrc = i;
516 }
517 else if (*arg[i] == _T('+'))
518 {
519 /* Next file should be appended */
520 bMoreFiles = TRUE;
521 nFiles -= 1;
522 }
523 else if (bMoreFiles)
524 {
525 /* Add this file to the source string
526 this way we can do all checks
527 directly on source string later on */
528 TCHAR * ptr;
529 int length = (_tcslen(arg[nSrc]) + _tcslen(arg[i]) + 2) * sizeof(TCHAR);
530 ptr = cmd_alloc(length);
531 if (ptr)
532 {
533 _tcscpy(ptr, arg[nSrc]);
534 _tcscat(ptr, _T("|"));
535 _tcscat(ptr, arg[i]);
536 cmd_free(arg[nSrc]);
537 arg[nSrc] = ptr;
538 nFiles -= 1;
539 }
540
541 bMoreFiles = FALSE;
542 }
543 else if (nDes == -1)
544 {
545 nDes = i;
546 }
547 }
548 }
549
550 /* keep quiet within batch files */
551 if (bc != NULL)
552 {
553 dwFlags |= COPY_NO_PROMPT;
554 dwFlags &= ~COPY_PROMPT;
555 }
556
557 if (nFiles < 1)
558 {
559 /* There are not enough files, there has to be at least 1 */
560 ConOutResPuts(STRING_ERROR_REQ_PARAM_MISSING);
561 freep(arg);
562 return 1;
563 }
564
565 if (nFiles > 2)
566 {
567 /* There are too many file names in command */
568 ConErrResPrintf(STRING_ERROR_TOO_MANY_PARAMETERS,_T(""));
569 nErrorLevel = 1;
570 freep(arg);
571 return 1;
572 }
573
574 if ((_tcschr(arg[nSrc], _T('|')) != NULL) ||
575 (_tcschr(arg[nSrc], _T('*')) != NULL) ||
576 (_tcschr(arg[nSrc], _T('?')) != NULL) ||
577 IsExistingDirectory(arg[nSrc]))
578 {
579 bMultipleSource = TRUE;
580 }
581
582 /* Reuse the number of files variable */
583 nFiles = 0;
584
585 /* Check if no destination argument is passed */
586 if (nDes == -1)
587 {
588 /* If no destination was entered then just use
589 the current directory as the destination */
590 GetCurrentDirectory(ARRAYSIZE(szDestPath), szDestPath);
591 }
592 else
593 {
594 /* Check if the destination is 'x:' */
595 if ((arg[nDes][1] == _T(':')) && (arg[nDes][2] == _T('\0')))
596 {
597 GetRootPath(arg[nDes], szDestPath, ARRAYSIZE(szDestPath));
598 }
599 else
600 {
601 /* If the user entered two file names then form the full string path */
602 GetFullPathName(arg[nDes], ARRAYSIZE(szDestPath), szDestPath, NULL);
603 }
604
605 /* Make sure there is an ending slash to the path if the dest is a folder */
606 if ((_tcschr(szDestPath, _T('*')) == NULL) &&
607 IsExistingDirectory(szDestPath))
608 {
609 bMultipleDest = TRUE;
610 if (szDestPath[_tcslen(szDestPath) - 1] != _T('\\'))
611 _tcscat(szDestPath, _T("\\"));
612 }
613
614 /* Check if the destination uses wildcards */
615 if ((_tcschr(arg[nDes], _T('*')) != NULL) ||
616 (_tcschr(arg[nDes], _T('?')) != NULL))
617 {
618 bMultipleDest = TRUE;
619 }
620 }
621
622 if (nDes != -1) /* Append files only when there is a destination */
623 {
624 if (bMultipleSource && !bMultipleDest)
625 {
626 /* We have multiple source files, but not multiple destination
627 files. This means we are appending the source files. */
628 bAppend = TRUE;
629 if (_tcschr(arg[nSrc], _T('|')) != NULL)
630 appendPointer = arg[nSrc];
631 }
632 }
633
634 /* Save the name the user entered */
635 UseThisName = _tcsrchr(szDestPath,_T('\\'));
636 if (UseThisName)
637 {
638 /* Split the name from the path */
639 *UseThisName++ = _T('\0');
640
641 /* Check if the dest path ends with '\*' or '\' */
642 if (((UseThisName[0] == _T('*')) && (UseThisName[1] == _T('\0'))) ||
643 (UseThisName[0] == _T('\0')))
644 {
645 /* In this case we will be using the same name as the source file
646 for the destination file because destination is a folder */
647 bSrcName = TRUE;
648 UseThisName = NULL;
649 }
650 }
651 else
652 {
653 /* Something's seriously wrong! */
654 UseThisName = szDestPath;
655 }
656
657 do
658 {
659 /* Get the full string of the path to the source file */
660 if (_tcschr(arg[nSrc], _T('|')) != NULL)
661 {
662 /* Reset the source path */
663 szSrcPath[0] = _T('\0');
664
665 /* Loop through the source file name and copy all
666 the chars one at a time until we reach the separator */
667 while(TRUE)
668 {
669 if (appendPointer[0] == _T('|'))
670 {
671 /* Skip the | and go to the next file name */
672 appendPointer++;
673 break;
674 }
675 else if (appendPointer[0] == _T('\0'))
676 {
677 bDone = TRUE;
678 break;
679 }
680
681 _tcsncat(szSrcPath, appendPointer, 1);
682 appendPointer++;
683 }
684
685 if (_tcschr(arg[nSrc], _T(',')) != NULL)
686 {
687 /* Only time there is a , in the source is when they are using touch
688 Cant have a destination and can only have on ,, at the end of the string
689 Cant have more than one file name */
690 szTouch = _tcsstr(arg[nSrc], _T("|"));
691 if (_tcsncmp(szTouch,_T("|,,\0"), 4) || (nDes != -1))
692 {
693 ConErrResPrintf(STRING_ERROR_INVALID_PARAM_FORMAT,arg[nSrc]);
694 nErrorLevel = 1;
695 freep (arg);
696 return 1;
697 }
698 bTouch = TRUE;
699 bDone = TRUE;
700 }
701 }
702 else
703 {
704 bDone = TRUE;
705 _tcscpy(szSrcPath, arg[nSrc]);
706 }
707
708 /* "x:" is not a valid source path format. */
709 if ((szSrcPath[1] == _T(':')) && (szSrcPath[2] == _T('\0')))
710 {
711 ConOutPrintf(_T("%s\n"), szSrcPath);
712 ConOutFormatMessage(ERROR_FILE_NOT_FOUND, szSrcPath);
713 nErrorLevel = 1;
714 break;
715 }
716
717
718 /* From this point on, we can assume that the shortest path is
719 3 letters long and that would be [DriveLetter]:\ */
720
721 /* Check if the path has a wildcard */
722 bHasWildcard = (_tcschr(szSrcPath, _T('*')) != NULL);
723
724 /* If there is no * in the path name and it is a folder then we will
725 need to add a wildcard to the pathname so FindFirstFile comes up
726 with all the files in that folder */
727 if (!bHasWildcard && IsExistingDirectory(szSrcPath))
728 {
729 /* If it doesnt have a \ at the end already then on needs to be added */
730 if (szSrcPath[_tcslen(szSrcPath) - 1] != _T('\\'))
731 _tcscat(szSrcPath, _T("\\"));
732 _tcscat(szSrcPath, _T("*"));
733 bHasWildcard = TRUE;
734 }
735
736 /* If the path ends with '\' add a wildcard at the end */
737 if (szSrcPath[_tcslen(szSrcPath) - 1] == _T('\\'))
738 {
739 _tcscat(szSrcPath, _T("*"));
740 bHasWildcard = TRUE;
741 }
742
743 /* Get a list of all the files */
744 hFile = FindFirstFile(szSrcPath, &findBuffer);
745
746 /* If we could not open the file handle, print out the error */
747 if (hFile == INVALID_HANDLE_VALUE)
748 {
749 /* only print source name when more than one file */
750 if (bMultipleSource)
751 ConOutPrintf(_T("%s\n"), szSrcPath);
752
753 ConOutFormatMessage(GetLastError(), szSrcPath);
754 freep(arg);
755 nErrorLevel = 1;
756 return 1;
757 }
758
759 /* Strip the paths back to the folder they are in */
760 for (i = (_tcslen(szSrcPath) - 1); i > -1; i--)
761 {
762 if (szSrcPath[i] != _T('\\'))
763 szSrcPath[i] = _T('\0');
764 else
765 break;
766 }
767
768 do
769 {
770 /* Check Breaker */
771 if (CheckCtrlBreak(BREAK_INPUT))
772 {
773 FindClose(hFile);
774 freep(arg);
775 return 1;
776 }
777
778 /* Set the override to yes each new file */
779 nOverwrite = 1;
780
781 /* Ignore the . and .. files */
782 if (!_tcscmp(findBuffer.cFileName, _T(".")) ||
783 !_tcscmp(findBuffer.cFileName, _T("..")) ||
784 findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
785 {
786 continue;
787 }
788
789 /* Copy the base folder over to a tmp string */
790 _tcscpy(tmpDestPath, szDestPath);
791 _tcscat(tmpDestPath, _T("\\"));
792
793 /* Can't put a file into a folder that isn't there */
794 if (_tcscmp(tmpDestPath, _T("\\\\.\\")) &&
795 !IsExistingDirectory(tmpDestPath))
796 {
797 FindClose(hFile);
798 ConOutFormatMessage(GetLastError(), szSrcPath);
799 freep(arg);
800 nErrorLevel = 1;
801 return 1;
802 }
803
804 /* Copy over the destination path name */
805 if (bSrcName)
806 _tcscat(tmpDestPath, findBuffer.cFileName);
807 else
808 {
809 /* If there is no wildcard, use the name the user entered */
810 if ((_tcschr(UseThisName, _T('*')) == NULL) &&
811 (_tcschr(UseThisName, _T('?')) == NULL))
812 {
813 _tcscat(tmpDestPath, UseThisName);
814 }
815 else
816 {
817 TCHAR DoneFile[MAX_PATH];
818
819 BuildFileName(findBuffer.cFileName,
820 UseThisName,
821 DoneFile);
822
823
824 /* Add the filename to the tmp string path */
825 _tcscat(tmpDestPath, DoneFile);
826 }
827 }
828
829 /* Build the string path to the source file */
830 _tcscpy(tmpSrcPath,szSrcPath);
831 _tcscat (tmpSrcPath, findBuffer.cFileName);
832
833 /* Check to see if the file is the same file */
834 if (!bTouch && !_tcscmp(tmpSrcPath, tmpDestPath))
835 {
836 ConOutResPrintf(STRING_COPY_ERROR2);
837
838 nErrorLevel = 1;
839 break;
840 }
841
842 /* only print source name when more than one file */
843 if (bMultipleSource)
844 ConOutPrintf(_T("%s\n"), tmpSrcPath);
845
846 /* Handle any overriding / prompting that needs to be done */
847 if (((!(dwFlags & COPY_NO_PROMPT) && IsExistingFile (tmpDestPath)) || dwFlags & COPY_PROMPT) && !bTouch)
848 nOverwrite = CopyOverwrite(tmpDestPath);
849 if (nOverwrite == PROMPT_NO || nOverwrite == PROMPT_BREAK)
850 continue;
851 if (nOverwrite == PROMPT_ALL || (nOverwrite == PROMPT_YES && bAppend))
852 dwFlags |= COPY_NO_PROMPT;
853
854 /* Tell whether the copy was successful or not */
855 if (copy(tmpSrcPath,tmpDestPath, bAppend, dwFlags, bTouch))
856 {
857 nFiles++;
858 }
859 else
860 {
861 /* print out the error message */
862 ConOutResPrintf(STRING_COPY_ERROR3);
863 ConOutFormatMessage (GetLastError(), szSrcPath);
864 nErrorLevel = 1;
865 }
866
867 /* Loop through all wildcard files */
868 } while (FindNextFile(hFile, &findBuffer));
869
870 FindClose(hFile);
871
872 /* Loop through all files in src string with a + */
873 } while (!bDone);
874
875 /* print out the number of files copied */
876 ConOutResPrintf(STRING_COPY_FILE, bAppend ? 1 : nFiles);
877
878 if (arg != NULL)
879 freep(arg);
880
881 return 0;
882 }
883
884 #endif /* INCLUDE_CMD_COPY */