a5f896d17dd19c5ff5605e6394ca0be24ac1ef09
[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 Impleneted */
43 COPY_SHORTNAME = 0x010, /* /N : Dummy, Never will be Impleneted */
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 nErrorLevel = 1;
140 return 0;
141 }
142 _tcscpy(source, TempSrc);
143 }
144
145
146 if (lpdwFlags & COPY_RESTART)
147 {
148 _tcscpy(TrueDest, dest);
149 GetEnvironmentVariable(_T("TEMP"),dest,MAX_PATH);
150 _tcscat(dest,_T("\\"));
151 FileName = _tcsrchr(TrueDest,_T('\\'));
152 FileName++;
153 _tcscat(dest,FileName);
154 }
155
156
157 if (!IsExistingFile (dest))
158 {
159 TRACE ("opening/creating\n");
160 hFileDest =
161 CreateFile (dest, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
162 }
163 else if (!append)
164 {
165 TRACE ("SetFileAttributes (%s, FILE_ATTRIBUTE_NORMAL);\n", debugstr_aw(dest));
166 SetFileAttributes (dest, FILE_ATTRIBUTE_NORMAL);
167
168 TRACE ("DeleteFile (%s);\n", debugstr_aw(dest));
169 DeleteFile (dest);
170
171 hFileDest = CreateFile (dest, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
172 }
173 else
174 {
175 LONG lFilePosHigh = 0;
176
177 if (!_tcscmp (dest, source))
178 {
179 CloseHandle (hFileSrc);
180 return 0;
181 }
182
183 TRACE ("opening/appending\n");
184 SetFileAttributes (dest, FILE_ATTRIBUTE_NORMAL);
185
186 hFileDest =
187 CreateFile (dest, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
188
189 /* Move to end of file to start writing */
190 SetFilePointer (hFileDest, 0, &lFilePosHigh,FILE_END);
191 }
192
193
194 if (hFileDest == INVALID_HANDLE_VALUE)
195 {
196 CloseHandle (hFileSrc);
197 ConOutResPuts(STRING_ERROR_PATH_NOT_FOUND);
198 nErrorLevel = 1;
199 return 0;
200 }
201
202 /* A page-aligned buffer usually give more speed */
203 buffer = (LPBYTE)VirtualAlloc(NULL, BUFF_SIZE, MEM_COMMIT, PAGE_READWRITE);
204 if (buffer == NULL)
205 {
206 CloseHandle (hFileDest);
207 CloseHandle (hFileSrc);
208 ConOutResPuts(STRING_ERROR_OUT_OF_MEMORY);
209 nErrorLevel = 1;
210 return 0;
211 }
212
213 do
214 {
215 ReadFile (hFileSrc, buffer, BUFF_SIZE, &dwRead, NULL);
216 if (lpdwFlags & COPY_ASCII)
217 {
218 LPBYTE pEof = memchr(buffer, 0x1A, dwRead);
219 if (pEof != NULL)
220 {
221 bEof = TRUE;
222 dwRead = pEof-buffer+1;
223 break;
224 }
225 }
226
227 if (dwRead == 0)
228 break;
229
230 WriteFile (hFileDest, buffer, dwRead, &dwWritten, NULL);
231 if (dwWritten != dwRead || CheckCtrlBreak(BREAK_INPUT))
232 {
233 ConOutResPuts(STRING_COPY_ERROR3);
234
235 VirtualFree (buffer, 0, MEM_RELEASE);
236 CloseHandle (hFileDest);
237 CloseHandle (hFileSrc);
238 nErrorLevel = 1;
239 return 0;
240 }
241 }
242 while (!bEof);
243
244 TRACE ("setting time\n");
245 SetFileTime (hFileDest, &srctime, NULL, NULL);
246
247 if ((lpdwFlags & COPY_ASCII) && !bEof)
248 {
249 /* we're dealing with ASCII files! */
250 buffer[0] = 0x1A;
251 TRACE ("appending ^Z\n");
252 WriteFile (hFileDest, buffer, sizeof(CHAR), &dwWritten, NULL);
253 }
254
255 VirtualFree (buffer, 0, MEM_RELEASE);
256 CloseHandle (hFileDest);
257 CloseHandle (hFileSrc);
258
259 TRACE ("setting mode\n");
260 SetFileAttributes (dest, dwAttrib);
261
262 /* Now finish off the copy if needed with CopyFileEx */
263 if (lpdwFlags & COPY_RESTART)
264 {
265 if (!CopyFileEx(dest, TrueDest, NULL, NULL, FALSE, COPY_FILE_RESTARTABLE))
266 {
267 nErrorLevel = 1;
268 DeleteFile(dest);
269 return 0;
270 }
271 /* Take care of file in the temp folder */
272 DeleteFile(dest);
273
274 }
275
276 if (lpdwFlags & COPY_DECRYPT)
277 DeleteFile(TempSrc);
278
279 return 1;
280 }
281
282
283 static INT CopyOverwrite (LPTSTR fn)
284 {
285 /*ask the user if they want to override*/
286 INT res;
287 ConOutResPrintf(STRING_COPY_HELP1, fn);
288 res = FilePromptYNA (0);
289 return res;
290 }
291
292 /* The following lines of copy were written by someone else
293 (most likely Eric Kohl) and it was taken from ren.c */
294 static void
295 BuildFileName(
296 LPTSTR pszSource,
297 LPTSTR pszTarget,
298 LPTSTR pszOutput)
299 {
300 /* build destination file name */
301 while (*pszTarget != 0)
302 {
303 if (*pszTarget == _T('*'))
304 {
305 pszTarget++;
306 while ((*pszSource != 0) && (*pszSource != *pszTarget))
307 {
308 *pszOutput++ = *pszSource++;
309 }
310 }
311 else if (*pszTarget == _T('?'))
312 {
313 pszTarget++;
314 if (*pszSource != 0)
315 {
316 *pszOutput++ = *pszSource++;
317 }
318 }
319 else
320 {
321 *pszOutput++ = *pszTarget++;
322 if (*pszSource != 0)
323 pszSource++;
324 }
325 }
326
327 *pszOutput = 0;
328 }
329
330 INT cmd_copy(LPTSTR param)
331 {
332 LPTSTR *arg;
333 INT argc, i, nFiles, nOverwrite = 0, nSrc = -1, nDes = -1;
334 /* this is the path up to the folder of the src and dest ie C:\windows\ */
335 TCHAR szDestPath[MAX_PATH];
336 TCHAR szSrcPath[MAX_PATH];
337 DWORD dwFlags = 0;
338 /* If this is the type of copy where we are adding files */
339 BOOL bAppend = FALSE;
340 WIN32_FIND_DATA findBuffer;
341 HANDLE hFile = NULL;
342 BOOL bTouch = FALSE;
343 /* Used when something like "copy c*.exe d*.exe" during the process of
344 figuring out the new name */
345 /* Pointer to keep track of how far through the append input(file1+file2+file3) we are */
346 TCHAR * appendPointer = _T("\0");
347 /* The full path to src and dest. This has drive letter, folders, and filename */
348 TCHAR tmpDestPath[MAX_PATH];
349 TCHAR tmpSrcPath[MAX_PATH];
350 /* A bool on weather or not the destination name will be taking from the input */
351 BOOL bSrcName = FALSE;
352 /* Seems like a waste but it is a pointer used to copy from input to PreserveName */
353 TCHAR * UseThisName;
354 /* for CMDCOPY env */
355 TCHAR *evar;
356 int size;
357 TCHAR * szTouch;
358 BOOL bHasWildcard, bDone = FALSE, bMoreFiles = FALSE;
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 envor 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 if (_totupper(arg[i][2]) == _T('Y'))
485 {
486 dwFlags &= ~COPY_NO_PROMPT;
487 dwFlags |= COPY_PROMPT;
488 }
489
490 break;
491
492 case _T('Z'):
493 dwFlags |= COPY_RESTART;
494 break;
495
496 default:
497 /* Invalid switch */
498 ConOutResPrintf(STRING_ERROR_INVALID_SWITCH, _totupper(arg[i][1]));
499 nErrorLevel = 1;
500 freep (arg);
501 return 1;
502 break;
503 }
504 }
505 /* If it was a switch, subtract from total arguments */
506 nFiles--;
507 }
508 else
509 {
510 /* If it isn't a switch then it is the source or destination */
511 if (nSrc == -1)
512 {
513 nSrc = i;
514 }
515 else if (*arg[i] == _T('+'))
516 {
517 /* Next file should be appended */
518 bMoreFiles = TRUE;
519 nFiles -= 1;
520 }
521 else if (bMoreFiles)
522 {
523 /* Add this file to the source string
524 this way we can do all checks
525 directly on source string later on */
526 TCHAR * ptr;
527 int length = (_tcslen(arg[nSrc]) + _tcslen(arg[i]) + 2) * sizeof(TCHAR);
528 ptr = cmd_alloc(length);
529 if (ptr)
530 {
531 _tcscpy(ptr, arg[nSrc]);
532 _tcscat(ptr, _T("|"));
533 _tcscat(ptr, arg[i]);
534 cmd_free(arg[nSrc]);
535 arg[nSrc] = ptr;
536 nFiles -= 1;
537 }
538
539 bMoreFiles = FALSE;
540 }
541 else if (nDes == -1)
542 {
543 nDes = i;
544 }
545 }
546 }
547
548 /* keep quiet within batch files */
549 if (bc != NULL)
550 {
551 dwFlags |= COPY_NO_PROMPT;
552 dwFlags &= ~COPY_PROMPT;
553 }
554
555 if (nFiles < 1)
556 {
557 /* There are not enough files, there has to be at least 1 */
558 ConOutResPuts(STRING_ERROR_REQ_PARAM_MISSING);
559 freep(arg);
560 return 1;
561 }
562
563 if (nFiles > 2)
564 {
565 /* There are too many file names in command */
566 ConErrResPrintf(STRING_ERROR_TOO_MANY_PARAMETERS,_T(""));
567 nErrorLevel = 1;
568 freep(arg);
569 return 1;
570 }
571
572 if ((_tcschr(arg[nSrc], _T('|')) != NULL) ||
573 (_tcschr(arg[nSrc], _T('*')) != NULL) ||
574 (_tcschr(arg[nSrc], _T('?')) != NULL) ||
575 IsExistingDirectory(arg[nSrc]))
576 {
577 bMultipleSource = TRUE;
578 }
579
580 /* Reusing the number of files variable */
581 nFiles = 0;
582
583 /* Check if no destination argument is passed */
584 if (nDes == -1)
585 {
586 /* If no destination was entered then just use
587 the current directory as the destination */
588 GetCurrentDirectory(MAX_PATH, szDestPath);
589 }
590 else
591 {
592 /* Check if the destination is 'x:' */
593 if ((arg[nDes][1] == _T(':')) && (arg[nDes][2] == _T('\0')))
594 {
595 GetRootPath(arg[nDes], szDestPath, MAX_PATH);
596 }
597 else
598 {
599 /* If the user entered two file names then form the full string path */
600 GetFullPathName(arg[nDes], MAX_PATH, szDestPath, NULL);
601 }
602
603 /* Make sure there is an ending slash to the path if the dest is a folder */
604 if ((_tcschr(szDestPath, _T('*')) == NULL) &&
605 IsExistingDirectory(szDestPath))
606 {
607 bMultipleDest = TRUE;
608 if (szDestPath[_tcslen(szDestPath) - 1] != _T('\\'))
609 _tcscat(szDestPath, _T("\\"));
610 }
611
612 /* Check if the destination uses wildcards */
613 if ((_tcschr(arg[nDes], _T('*')) != NULL) ||
614 (_tcschr(arg[nDes], _T('?')) != NULL))
615 {
616 bMultipleDest = TRUE;
617 }
618 }
619
620 if (nDes != -1) /* you can only append files when there is a destination */
621 {
622 if (bMultipleSource && !bMultipleDest)
623 {
624 /* We have multiple source files, but not multiple destination
625 files. This means we are appending the soruce files. */
626 bAppend = TRUE;
627 if (_tcschr(arg[nSrc], _T('|')) != NULL)
628 appendPointer = arg[nSrc];
629 }
630 }
631
632 /* Save the name the user entered */
633 UseThisName = _tcsrchr(szDestPath,_T('\\'));
634 if (UseThisName)
635 {
636 /* Split the name from the path */
637 *UseThisName++ = _T('\0');
638
639 /* Check if the dest path ends with '\*' or '\' */
640 if (((UseThisName[0] == _T('*')) && (UseThisName[1] == _T('\0'))) ||
641 (UseThisName[0] == _T('\0')))
642 {
643 /* In this case we will be using the same name as the source file
644 for the destination file because destination is a folder */
645 bSrcName = TRUE;
646 UseThisName = NULL;
647 }
648 }
649 else
650 {
651 /* Something's seriously wrong! */
652 UseThisName = szDestPath;
653 }
654
655 do
656 {
657 /* Get the full string of the path to the source file */
658 if (_tcschr(arg[nSrc], _T('|')) != NULL)
659 {
660 /* Reset the source path */
661 szSrcPath[0] = _T('\0');
662
663 /* Loop through the source file name and copy all
664 the chars one at a time until it gets too + */
665 while(TRUE)
666 {
667 if (appendPointer[0] == _T('|'))
668 {
669 /* Skip the | and go to the next file name */
670 appendPointer++;
671 break;
672 }
673 else if (appendPointer[0] == _T('\0'))
674 {
675 bDone = TRUE;
676 break;
677 }
678
679 _tcsncat(szSrcPath, appendPointer, 1);
680 appendPointer++;
681 }
682
683 if (_tcschr(arg[nSrc], _T(',')) != NULL)
684 {
685 /* Only time there is a , in the source is when they are using touch
686 Cant have a destination and can only have on ,, at the end of the string
687 Cant have more then one file name */
688 szTouch = _tcsstr(arg[nSrc], _T("|"));
689 if (_tcsncmp(szTouch,_T("|,,\0"), 4) || (nDes != -1))
690 {
691 ConErrResPrintf(STRING_ERROR_INVALID_PARAM_FORMAT,arg[nSrc]);
692 nErrorLevel = 1;
693 freep (arg);
694 return 1;
695 }
696 bTouch = TRUE;
697 bDone = TRUE;
698 }
699 }
700 else
701 {
702 bDone = TRUE;
703 _tcscpy(szSrcPath, arg[nSrc]);
704 }
705
706 /* "x:" is not a valid source path format. */
707 if ((szSrcPath[1] == _T(':')) && (szSrcPath[2] == _T('\0')))
708 {
709 ConOutPrintf(_T("%s\n"), szSrcPath);
710 ConOutFormatMessage(ERROR_FILE_NOT_FOUND, szSrcPath);
711 nErrorLevel = 1;
712 break;
713 }
714
715
716 /* From this point on, we can assume that the shortest path is 3 letters long
717 and that would be [DriveLetter]:\ */
718
719 /* Check if the path has a wildcard */
720 bHasWildcard = (_tcschr(szSrcPath, _T('*')) != NULL);
721
722 /* If there is no * in the path name and it is a folder then we will
723 need to add a wildcard to the pathname so FindFirstFile comes up
724 with all the files in that folder */
725 if (!bHasWildcard && IsExistingDirectory(szSrcPath))
726 {
727 /* If it doesnt have a \ at the end already then on needs to be added */
728 if (szSrcPath[_tcslen(szSrcPath) - 1] != _T('\\'))
729 _tcscat(szSrcPath, _T("\\"));
730 _tcscat(szSrcPath, _T("*"));
731 bHasWildcard = TRUE;
732 }
733
734 /* If the path ends with '\' add a wildcard at the end */
735 if (szSrcPath[_tcslen(szSrcPath) - 1] == _T('\\'))
736 {
737 _tcscat(szSrcPath, _T("*"));
738 bHasWildcard = TRUE;
739 }
740
741 /* Get a list of all the files */
742 hFile = FindFirstFile(szSrcPath, &findBuffer);
743
744 /* If it couldnt open the file handle, print out the error */
745 if (hFile == INVALID_HANDLE_VALUE)
746 {
747 /* only print source name when more then one file */
748 if (bMultipleSource)
749 ConOutPrintf(_T("%s\n"), szSrcPath);
750
751 ConOutFormatMessage(GetLastError(), szSrcPath);
752 freep(arg);
753 nErrorLevel = 1;
754 return 1;
755 }
756
757 /* Strip the paths back to the folder they are in */
758 for (i = (_tcslen(szSrcPath) - 1); i > -1; i--)
759 if (szSrcPath[i] != _T('\\'))
760 szSrcPath[i] = _T('\0');
761 else
762 break;
763
764 do
765 {
766 /* Check Breaker */
767 if (CheckCtrlBreak(BREAK_INPUT))
768 {
769 freep(arg);
770 return 1;
771 }
772
773 /* Set the override to yes each new file */
774 nOverwrite = 1;
775
776 /* Ignore the . and .. files */
777 if (!_tcscmp(findBuffer.cFileName, _T(".")) ||
778 !_tcscmp(findBuffer.cFileName, _T("..")) ||
779 findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
780 {
781 continue;
782 }
783
784 /* Copy the base folder over to a tmp string */
785 _tcscpy(tmpDestPath, szDestPath);
786 _tcscat(tmpDestPath, _T("\\"));
787
788 /* Can't put a file into a folder that isn't there */
789 if (_tcscmp(tmpDestPath, _T("\\\\.\\")) &&
790 !IsExistingDirectory(tmpDestPath))
791 {
792 ConOutFormatMessage(GetLastError(), szSrcPath);
793 freep(arg);
794 nErrorLevel = 1;
795 return 1;
796 }
797
798 /* Copy over the destination path name */
799 if (bSrcName)
800 _tcscat(tmpDestPath, findBuffer.cFileName);
801 else
802 {
803 /* If there is no wildcard you can use the name the user entered */
804 if ((_tcschr(UseThisName, _T('*')) == NULL) &&
805 (_tcschr(UseThisName, _T('?')) == NULL))
806 {
807 _tcscat(tmpDestPath, UseThisName);
808 }
809 else
810 {
811 TCHAR DoneFile[MAX_PATH];
812
813 BuildFileName(findBuffer.cFileName,
814 UseThisName,
815 DoneFile);
816
817
818 /* Add the filename to the tmp string path */
819 _tcscat(tmpDestPath, DoneFile);
820 }
821 }
822
823 /* Build the string path to the source file */
824 _tcscpy(tmpSrcPath,szSrcPath);
825 _tcscat (tmpSrcPath, findBuffer.cFileName);
826
827 /* Check to see if the file is the same file */
828 if (!bTouch && !_tcscmp(tmpSrcPath, tmpDestPath))
829 {
830 ConOutResPrintf(STRING_COPY_ERROR2);
831
832 nErrorLevel = 1;
833 break;
834 }
835
836 /* only print source name when more then one file */
837 if (bMultipleSource)
838 ConOutPrintf(_T("%s\n"), tmpSrcPath);
839
840 /* Handle any overriding / prompting that needs to be done */
841 if (((!(dwFlags & COPY_NO_PROMPT) && IsExistingFile (tmpDestPath)) || dwFlags & COPY_PROMPT) && !bTouch)
842 nOverwrite = CopyOverwrite(tmpDestPath);
843 if (nOverwrite == PROMPT_NO || nOverwrite == PROMPT_BREAK)
844 continue;
845 if (nOverwrite == PROMPT_ALL || (nOverwrite == PROMPT_YES && bAppend))
846 dwFlags |= COPY_NO_PROMPT;
847
848 /* Tell weather the copy was successful or not */
849 if (copy(tmpSrcPath,tmpDestPath, bAppend, dwFlags, bTouch))
850 {
851 nFiles++;
852 //LoadString(CMD_ModuleHandle, STRING_MOVE_ERROR1, szMsg, RC_STRING_MAX_SIZE);
853 }
854 else
855 {
856 /* print out the error message */
857 ConOutResPrintf(STRING_COPY_ERROR3);
858 ConOutFormatMessage (GetLastError(), szSrcPath);
859 nErrorLevel = 1;
860 }
861
862 /* Loop through all wildcard files */
863 } while (FindNextFile(hFile, &findBuffer));
864
865 /* Loop through all files in src string with a + */
866 } while(!bDone);
867
868 /* print out the number of files copied */
869 ConOutResPrintf(STRING_COPY_FILE, bAppend ? 1 : nFiles);
870
871 if (hFile) FindClose(hFile);
872
873 if (arg != NULL)
874 freep(arg);
875
876 return 0;
877 }
878
879 #endif /* INCLUDE_CMD_COPY */