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 /* 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 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 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 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