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
copy(TCHAR source[MAX_PATH],TCHAR dest[MAX_PATH],INT append,DWORD lpdwFlags,BOOL bTouch)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 /* For MS-DOS backwards-compatibility, always remove the read-only attribute */
262 SetFileAttributes (dest, dwAttrib & ~FILE_ATTRIBUTE_READONLY);
263
264 /* Now finish off the copy if needed with CopyFileEx */
265 if (lpdwFlags & COPY_RESTART)
266 {
267 if (!CopyFileEx(dest, TrueDest, NULL, NULL, FALSE, COPY_FILE_RESTARTABLE))
268 {
269 nErrorLevel = 1;
270 DeleteFile(dest);
271 return 0;
272 }
273 /* Take care of file in the temp folder */
274 DeleteFile(dest);
275
276 }
277
278 if (lpdwFlags & COPY_DECRYPT)
279 DeleteFile(TempSrc);
280
281 return 1;
282 }
283
284
CopyOverwrite(LPTSTR fn)285 static INT CopyOverwrite (LPTSTR fn)
286 {
287 /*ask the user if they want to override*/
288 INT res;
289 ConOutResPrintf(STRING_COPY_HELP1, fn);
290 res = FilePromptYNA (0);
291 return res;
292 }
293
294 /* The following lines of copy were written by someone else
295 (most likely Eric Kohl) and it was taken from ren.c */
296 static void
BuildFileName(LPTSTR pszSource,LPTSTR pszTarget,LPTSTR pszOutput)297 BuildFileName(
298 LPTSTR pszSource,
299 LPTSTR pszTarget,
300 LPTSTR pszOutput)
301 {
302 /* build destination file name */
303 while (*pszTarget != 0)
304 {
305 if (*pszTarget == _T('*'))
306 {
307 pszTarget++;
308 while ((*pszSource != 0) && (*pszSource != *pszTarget))
309 {
310 *pszOutput++ = *pszSource++;
311 }
312 }
313 else if (*pszTarget == _T('?'))
314 {
315 pszTarget++;
316 if (*pszSource != 0)
317 {
318 *pszOutput++ = *pszSource++;
319 }
320 }
321 else
322 {
323 *pszOutput++ = *pszTarget++;
324 if (*pszSource != 0)
325 pszSource++;
326 }
327 }
328
329 *pszOutput = 0;
330 }
331
cmd_copy(LPTSTR param)332 INT cmd_copy(LPTSTR param)
333 {
334 LPTSTR *arg;
335 INT argc, i, nFiles, nOverwrite = 0, nSrc = -1, nDes = -1;
336 /* this is the path up to the folder of the src and dest ie C:\windows\ */
337 TCHAR szDestPath[MAX_PATH];
338 TCHAR szSrcPath[MAX_PATH];
339 DWORD dwFlags = 0;
340 /* If this is the type of copy where we are adding files */
341 BOOL bAppend = FALSE;
342 WIN32_FIND_DATA findBuffer;
343 HANDLE hFile = NULL;
344 BOOL bTouch = FALSE;
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 to know whether or not the destination name will be taken 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 /* Used for something like "copy c*.exe d*.exe" */
360 BOOL bMultipleSource = FALSE, bMultipleDest = FALSE;
361
362
363 /* Show help/usage info */
364 if (!_tcsncmp(param, _T("/?"), 2))
365 {
366 ConOutResPaging(TRUE, STRING_COPY_HELP2);
367 return 0;
368 }
369
370 nErrorLevel = 0;
371
372 /* Get the env variable value if it exists */
373 evar = cmd_alloc(512 * sizeof(TCHAR));
374 if (evar == NULL)
375 size = 0;
376 else
377 size = GetEnvironmentVariable (_T("COPYCMD"), evar, 512);
378
379 if (size > 512)
380 {
381 TCHAR *old_evar = evar;
382 evar = cmd_realloc(evar,size * sizeof(TCHAR) );
383 if (evar!=NULL)
384 size = GetEnvironmentVariable (_T("COPYCMD"), evar, size);
385 else
386 {
387 size=0;
388 evar = old_evar;
389 }
390 }
391
392 /* check see if we did get any env variable */
393 if (size != 0)
394 {
395 int t = 0;
396
397 /* scan and set the flags */
398 for (t = 0; t < size; t++)
399 {
400 if (_tcsncicmp(_T("/A"),&evar[t],2) == 0)
401 {
402 dwFlags |=COPY_ASCII;
403 t++;
404 }
405 else if (_tcsncicmp(_T("/B"),&evar[t],2) == 0)
406 {
407 dwFlags |= COPY_BINARY;
408 t++;
409 }
410 else if (_tcsncicmp(_T("/D"),&evar[t],2) == 0)
411 {
412 dwFlags |= COPY_DECRYPT;
413 t++;
414 }
415 else if (_tcsncicmp(_T("/V"),&evar[t],2) == 0)
416 {
417 dwFlags |= COPY_VERIFY;
418 t++;
419 }
420 else if (_tcsncicmp(_T("/N"),&evar[t],2) == 0)
421 {
422 dwFlags |= COPY_SHORTNAME;
423 t++;
424 }
425 else if (_tcsncicmp(_T("/Y"),&evar[t],2) == 0)
426 {
427 dwFlags |= COPY_NO_PROMPT;
428 t++;
429 }
430 else if (_tcsncicmp(_T("/-Y"),&evar[t],3) == 0)
431 {
432 dwFlags |= COPY_PROMPT;
433 t+=2;
434 }
435 else if (_tcsncicmp(_T("/Z"),&evar[t],2) == 0)
436 {
437 dwFlags |= COPY_PROMPT;
438 t++;
439 }
440 }
441 }
442 cmd_free(evar);
443
444
445 /* Split the user input into array */
446 arg = split(param, &argc, FALSE, TRUE);
447 nFiles = argc;
448
449 /* Read switches and count files */
450 for (i = 0; i < argc; i++)
451 {
452 if (*arg[i] == _T('/'))
453 {
454 if (_tcslen(arg[i]) >= 2)
455 {
456 switch (_totupper(arg[i][1]))
457 {
458 case _T('A'):
459 dwFlags |= COPY_ASCII;
460 break;
461
462 case _T('B'):
463 dwFlags |= COPY_BINARY;
464 break;
465
466 case _T('D'):
467 dwFlags |= COPY_DECRYPT;
468 break;
469
470 case _T('V'):
471 dwFlags |= COPY_VERIFY;
472 break;
473
474 case _T('N'):
475 dwFlags |= COPY_SHORTNAME;
476 break;
477
478 case _T('Y'):
479 dwFlags |= COPY_NO_PROMPT;
480 dwFlags &= ~COPY_PROMPT;
481 break;
482
483 case _T('-'):
484 if (_tcslen(arg[i]) >= 3)
485 {
486 if (_totupper(arg[i][2]) == _T('Y'))
487 {
488 dwFlags &= ~COPY_NO_PROMPT;
489 dwFlags |= COPY_PROMPT;
490 }
491 }
492
493 break;
494
495 case _T('Z'):
496 dwFlags |= COPY_RESTART;
497 break;
498
499 default:
500 /* Invalid switch */
501 ConOutResPrintf(STRING_ERROR_INVALID_SWITCH, _totupper(arg[i][1]));
502 nErrorLevel = 1;
503 freep (arg);
504 return 1;
505 break;
506 }
507 }
508 /* If it was a switch, subtract from total arguments */
509 nFiles--;
510 }
511 else
512 {
513 /* If it isn't a switch then it is the source or destination */
514 if (nSrc == -1)
515 {
516 nSrc = i;
517 }
518 else if (*arg[i] == _T('+'))
519 {
520 /* Next file should be appended */
521 bMoreFiles = TRUE;
522 nFiles -= 1;
523 }
524 else if (bMoreFiles)
525 {
526 /* Add this file to the source string
527 this way we can do all checks
528 directly on source string later on */
529 TCHAR * ptr;
530 int length = (_tcslen(arg[nSrc]) + _tcslen(arg[i]) + 2) * sizeof(TCHAR);
531 ptr = cmd_alloc(length);
532 if (ptr)
533 {
534 _tcscpy(ptr, arg[nSrc]);
535 _tcscat(ptr, _T("|"));
536 _tcscat(ptr, arg[i]);
537 cmd_free(arg[nSrc]);
538 arg[nSrc] = ptr;
539 nFiles -= 1;
540 }
541
542 bMoreFiles = FALSE;
543 }
544 else if (nDes == -1)
545 {
546 nDes = i;
547 }
548 }
549 }
550
551 /* keep quiet within batch files */
552 if (bc != NULL)
553 {
554 dwFlags |= COPY_NO_PROMPT;
555 dwFlags &= ~COPY_PROMPT;
556 }
557
558 if (nFiles < 1)
559 {
560 /* There are not enough files, there has to be at least 1 */
561 ConOutResPuts(STRING_ERROR_REQ_PARAM_MISSING);
562 freep(arg);
563 return 1;
564 }
565
566 if (nFiles > 2)
567 {
568 /* There are too many file names in command */
569 ConErrResPrintf(STRING_ERROR_TOO_MANY_PARAMETERS,_T(""));
570 nErrorLevel = 1;
571 freep(arg);
572 return 1;
573 }
574
575 if ((_tcschr(arg[nSrc], _T('|')) != NULL) ||
576 (_tcschr(arg[nSrc], _T('*')) != NULL) ||
577 (_tcschr(arg[nSrc], _T('?')) != NULL) ||
578 IsExistingDirectory(arg[nSrc]))
579 {
580 bMultipleSource = TRUE;
581 }
582
583 /* Reuse the number of files variable */
584 nFiles = 0;
585
586 /* Check if no destination argument is passed */
587 if (nDes == -1)
588 {
589 /* If no destination was entered then just use
590 the current directory as the destination */
591 GetCurrentDirectory(ARRAYSIZE(szDestPath), szDestPath);
592 }
593 else
594 {
595 /* Check if the destination is 'x:' */
596 if ((arg[nDes][1] == _T(':')) && (arg[nDes][2] == _T('\0')))
597 {
598 GetRootPath(arg[nDes], szDestPath, ARRAYSIZE(szDestPath));
599 }
600 else
601 {
602 /* If the user entered two file names then form the full string path */
603 GetFullPathName(arg[nDes], ARRAYSIZE(szDestPath), szDestPath, NULL);
604 }
605
606 /* Make sure there is an ending slash to the path if the dest is a folder */
607 if ((_tcschr(szDestPath, _T('*')) == NULL) &&
608 IsExistingDirectory(szDestPath))
609 {
610 bMultipleDest = TRUE;
611 if (szDestPath[_tcslen(szDestPath) - 1] != _T('\\'))
612 _tcscat(szDestPath, _T("\\"));
613 }
614
615 /* Check if the destination uses wildcards */
616 if ((_tcschr(arg[nDes], _T('*')) != NULL) ||
617 (_tcschr(arg[nDes], _T('?')) != NULL))
618 {
619 bMultipleDest = TRUE;
620 }
621 }
622
623 if (nDes != -1) /* Append files only when there is a destination */
624 {
625 if (bMultipleSource && !bMultipleDest)
626 {
627 /* We have multiple source files, but not multiple destination
628 files. This means we are appending the source files. */
629 bAppend = TRUE;
630 if (_tcschr(arg[nSrc], _T('|')) != NULL)
631 appendPointer = arg[nSrc];
632 }
633 }
634
635 /* Save the name the user entered */
636 UseThisName = _tcsrchr(szDestPath,_T('\\'));
637 if (UseThisName)
638 {
639 /* Split the name from the path */
640 *UseThisName++ = _T('\0');
641
642 /* Check if the dest path ends with '\*' or '\' */
643 if (((UseThisName[0] == _T('*')) && (UseThisName[1] == _T('\0'))) ||
644 (UseThisName[0] == _T('\0')))
645 {
646 /* In this case we will be using the same name as the source file
647 for the destination file because destination is a folder */
648 bSrcName = TRUE;
649 UseThisName = NULL;
650 }
651 }
652 else
653 {
654 /* Something's seriously wrong! */
655 UseThisName = szDestPath;
656 }
657
658 do
659 {
660 /* Get the full string of the path to the source file */
661 if (_tcschr(arg[nSrc], _T('|')) != NULL)
662 {
663 /* Reset the source path */
664 szSrcPath[0] = _T('\0');
665
666 /* Loop through the source file name and copy all
667 the chars one at a time until we reach the separator */
668 while(TRUE)
669 {
670 if (appendPointer[0] == _T('|'))
671 {
672 /* Skip the | and go to the next file name */
673 appendPointer++;
674 break;
675 }
676 else if (appendPointer[0] == _T('\0'))
677 {
678 bDone = TRUE;
679 break;
680 }
681
682 _tcsncat(szSrcPath, appendPointer, 1);
683 appendPointer++;
684 }
685
686 if (_tcschr(arg[nSrc], _T(',')) != NULL)
687 {
688 /* Only time there is a , in the source is when they are using touch
689 Cant have a destination and can only have on ,, at the end of the string
690 Cant have more than one file name */
691 szTouch = _tcsstr(arg[nSrc], _T("|"));
692 if (_tcsncmp(szTouch,_T("|,,\0"), 4) || (nDes != -1))
693 {
694 ConErrResPrintf(STRING_ERROR_INVALID_PARAM_FORMAT,arg[nSrc]);
695 nErrorLevel = 1;
696 freep (arg);
697 return 1;
698 }
699 bTouch = TRUE;
700 bDone = TRUE;
701 }
702 }
703 else
704 {
705 bDone = TRUE;
706 _tcscpy(szSrcPath, arg[nSrc]);
707 }
708
709 /* "x:" is not a valid source path format. */
710 if ((szSrcPath[1] == _T(':')) && (szSrcPath[2] == _T('\0')))
711 {
712 ConOutPrintf(_T("%s\n"), szSrcPath);
713 ConOutFormatMessage(ERROR_FILE_NOT_FOUND, szSrcPath);
714 nErrorLevel = 1;
715 break;
716 }
717
718
719 /* From this point on, we can assume that the shortest path is
720 3 letters long and that would be [DriveLetter]:\ */
721
722 /* Check if the path has a wildcard */
723 bHasWildcard = (_tcschr(szSrcPath, _T('*')) != NULL);
724
725 /* If there is no * in the path name and it is a folder then we will
726 need to add a wildcard to the pathname so FindFirstFile comes up
727 with all the files in that folder */
728 if (!bHasWildcard && IsExistingDirectory(szSrcPath))
729 {
730 /* If it doesnt have a \ at the end already then on needs to be added */
731 if (szSrcPath[_tcslen(szSrcPath) - 1] != _T('\\'))
732 _tcscat(szSrcPath, _T("\\"));
733 _tcscat(szSrcPath, _T("*"));
734 bHasWildcard = TRUE;
735 }
736
737 /* If the path ends with '\' add a wildcard at the end */
738 if (szSrcPath[_tcslen(szSrcPath) - 1] == _T('\\'))
739 {
740 _tcscat(szSrcPath, _T("*"));
741 bHasWildcard = TRUE;
742 }
743
744 /* Get a list of all the files */
745 hFile = FindFirstFile(szSrcPath, &findBuffer);
746
747 /* If we could not open the file handle, print out the error */
748 if (hFile == INVALID_HANDLE_VALUE)
749 {
750 /* only print source name when more than one file */
751 if (bMultipleSource)
752 ConOutPrintf(_T("%s\n"), szSrcPath);
753
754 ConOutFormatMessage(GetLastError(), szSrcPath);
755 freep(arg);
756 nErrorLevel = 1;
757 return 1;
758 }
759
760 /* Strip the paths back to the folder they are in */
761 for (i = (_tcslen(szSrcPath) - 1); i > -1; i--)
762 {
763 if (szSrcPath[i] != _T('\\'))
764 szSrcPath[i] = _T('\0');
765 else
766 break;
767 }
768
769 do
770 {
771 /* Check Breaker */
772 if (CheckCtrlBreak(BREAK_INPUT))
773 {
774 FindClose(hFile);
775 freep(arg);
776 return 1;
777 }
778
779 /* Set the override to yes each new file */
780 nOverwrite = 1;
781
782 /* Ignore the . and .. files */
783 if (!_tcscmp(findBuffer.cFileName, _T(".")) ||
784 !_tcscmp(findBuffer.cFileName, _T("..")) ||
785 findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
786 {
787 continue;
788 }
789
790 /* Copy the base folder over to a tmp string */
791 _tcscpy(tmpDestPath, szDestPath);
792 _tcscat(tmpDestPath, _T("\\"));
793
794 /* Can't put a file into a folder that isn't there */
795 if (_tcscmp(tmpDestPath, _T("\\\\.\\")) &&
796 !IsExistingDirectory(tmpDestPath))
797 {
798 FindClose(hFile);
799 ConOutFormatMessage(GetLastError(), szSrcPath);
800 freep(arg);
801 nErrorLevel = 1;
802 return 1;
803 }
804
805 /* Copy over the destination path name */
806 if (bSrcName)
807 _tcscat(tmpDestPath, findBuffer.cFileName);
808 else
809 {
810 /* If there is no wildcard, use the name the user entered */
811 if ((_tcschr(UseThisName, _T('*')) == NULL) &&
812 (_tcschr(UseThisName, _T('?')) == NULL))
813 {
814 _tcscat(tmpDestPath, UseThisName);
815 }
816 else
817 {
818 TCHAR DoneFile[MAX_PATH];
819
820 BuildFileName(findBuffer.cFileName,
821 UseThisName,
822 DoneFile);
823
824
825 /* Add the filename to the tmp string path */
826 _tcscat(tmpDestPath, DoneFile);
827 }
828 }
829
830 /* Build the string path to the source file */
831 _tcscpy(tmpSrcPath,szSrcPath);
832 _tcscat (tmpSrcPath, findBuffer.cFileName);
833
834 /* Check to see if the file is the same file */
835 if (!bTouch && !_tcscmp(tmpSrcPath, tmpDestPath))
836 {
837 ConOutResPrintf(STRING_COPY_ERROR2);
838
839 nErrorLevel = 1;
840 break;
841 }
842
843 /* only print source name when more than one file */
844 if (bMultipleSource)
845 ConOutPrintf(_T("%s\n"), tmpSrcPath);
846
847 /* Handle any overriding / prompting that needs to be done */
848 if (((!(dwFlags & COPY_NO_PROMPT) && IsExistingFile (tmpDestPath)) || dwFlags & COPY_PROMPT) && !bTouch)
849 nOverwrite = CopyOverwrite(tmpDestPath);
850 if (nOverwrite == PROMPT_NO || nOverwrite == PROMPT_BREAK)
851 continue;
852 if (nOverwrite == PROMPT_ALL || (nOverwrite == PROMPT_YES && bAppend))
853 dwFlags |= COPY_NO_PROMPT;
854
855 /* Tell whether the copy was successful or not */
856 if (copy(tmpSrcPath,tmpDestPath, bAppend, dwFlags, bTouch))
857 {
858 nFiles++;
859 }
860 else
861 {
862 /* print out the error message */
863 ConOutResPrintf(STRING_COPY_ERROR3);
864 ConOutFormatMessage (GetLastError(), szSrcPath);
865 nErrorLevel = 1;
866 }
867
868 /* Loop through all wildcard files */
869 } while (FindNextFile(hFile, &findBuffer));
870
871 FindClose(hFile);
872
873 /* Loop through all files in src string with a + */
874 } while (!bDone);
875
876 /* print out the number of files copied */
877 ConOutResPrintf(STRING_COPY_FILE, bAppend ? 1 : nFiles);
878
879 if (arg != NULL)
880 freep(arg);
881
882 return 0;
883 }
884
885 #endif /* INCLUDE_CMD_COPY */
886