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