- Implement ProtocolResetComplete
[reactos.git] / 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 cmd_free (buffer);
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
293 INT cmd_copy (LPTSTR cmd, LPTSTR param)
294 {
295 LPTSTR *arg;
296 INT argc, i, nFiles, nOverwrite = 0, nSrc = -1, nDes = -1;
297 /* this is the path up to the folder of the src and dest ie C:\windows\ */
298 TCHAR szDestPath[MAX_PATH];
299 TCHAR szSrcPath[MAX_PATH];
300 DWORD dwFlags = 0;
301 /* If this is the type of copy where we are adding files */
302 BOOL bAppend = FALSE;
303 WIN32_FIND_DATA findBuffer;
304 HANDLE hFile;
305 BOOL bTouch = FALSE;
306 /* Used when something like "copy c*.exe d*.exe" during the process of
307 figuring out the new name */
308 TCHAR tmpName[MAX_PATH] = _T("");
309 /* Pointer to keep track of how far through the append input(file1+file2+file3) we are */
310 TCHAR * appendPointer = _T("\0");
311 /* The full path to src and dest. This has drive letter, folders, and filename */
312 TCHAR tmpDestPath[MAX_PATH];
313 TCHAR tmpSrcPath[MAX_PATH];
314 /* A bool on weather or not the destination name will be taking from the input */
315 BOOL bSrcName = FALSE;
316 /* Seems like a waste but it is a pointer used to copy from input to PreserveName */
317 TCHAR * UseThisName;
318 /* Stores the name( i.e. blah.txt or blah*.txt) which later we might need */
319 TCHAR PreserveName[MAX_PATH];
320 /* for CMDCOPY env */
321 TCHAR *evar;
322 int size;
323 TCHAR * szTouch;
324 BOOL bDone = FALSE;
325
326
327 /* Show help/usage info */
328 if (!_tcsncmp (param, _T("/?"), 2))
329 {
330 ConOutResPaging(TRUE, STRING_COPY_HELP2);
331 return 0;
332 }
333
334 nErrorLevel = 0;
335
336 /* Get the envor value if it exists */
337 evar = cmd_alloc(512 * sizeof(TCHAR));
338 if (evar == NULL)
339 size = 0;
340 else
341 size = GetEnvironmentVariable (_T("COPYCMD"), evar, 512);
342
343 if (size > 512)
344 {
345 evar = cmd_realloc(evar,size * sizeof(TCHAR) );
346 if (evar!=NULL)
347 size = GetEnvironmentVariable (_T("COPYCMD"), evar, size);
348 else
349 size=0;
350 }
351
352 /* check see if we did get any env variable */
353 if (size !=0)
354 {
355 int t=0;
356 /* scan and set the flags */
357 for (t=0;t<size;t++)
358 {
359 if (_tcsncicmp(_T("/A"),&evar[t],2) == 0)
360 {
361 dwFlags |=COPY_ASCII;
362 t++;
363 }
364 else if (_tcsncicmp(_T("/B"),&evar[t],2) == 0)
365 {
366 dwFlags |= COPY_BINARY;
367 t++;
368 }
369 else if (_tcsncicmp(_T("/D"),&evar[t],2) == 0)
370 {
371 dwFlags |= COPY_DECRYPT;
372 t++;
373 }
374 else if (_tcsncicmp(_T("/V"),&evar[t],2) == 0)
375 {
376 dwFlags |= COPY_VERIFY;
377 t++;
378 }
379 else if (_tcsncicmp(_T("/N"),&evar[t],2) == 0)
380 {
381 dwFlags |= COPY_SHORTNAME;
382 t++;
383 }
384 else if (_tcsncicmp(_T("/Y"),&evar[t],2) == 0)
385 {
386 dwFlags |= COPY_NO_PROMPT;
387 t++;
388 }
389 else if (_tcsncicmp(_T("/-Y"),&evar[t],3) == 0)
390 {
391 dwFlags |= COPY_PROMPT;
392 t+=2;
393 }
394 else if (_tcsncicmp(_T("/Z"),&evar[t],2) == 0)
395 {
396 dwFlags |= COPY_PROMPT;
397 t++;
398 }
399 }
400 }
401 cmd_free(evar);
402
403
404 /* Split the user input into array */
405 arg = split (param, &argc, FALSE);
406 nFiles = argc;
407
408
409 /* Read switches and count files */
410 for (i = 0; i < argc; i++)
411 {
412 if (*arg[i] == _T('/'))
413 {
414 if (_tcslen(arg[i]) >= 2)
415 {
416 switch (_totupper(arg[i][1]))
417 {
418 case _T('A'):
419 dwFlags |= COPY_ASCII;
420 break;
421
422 case _T('B'):
423 dwFlags |= COPY_BINARY;
424 break;
425
426 case _T('D'):
427 dwFlags |= COPY_DECRYPT;
428 break;
429
430 case _T('V'):
431 dwFlags |= COPY_VERIFY;
432 break;
433
434 case _T('N'):
435 dwFlags |= COPY_SHORTNAME;
436 break;
437
438 case _T('Y'):
439 dwFlags |= COPY_NO_PROMPT;
440 dwFlags &= ~COPY_PROMPT;
441 break;
442
443 case _T('-'):
444 if(_tcslen(arg[i]) >= 3)
445 if(_totupper(arg[i][2]) == _T('Y'))
446 {
447 dwFlags &= ~COPY_NO_PROMPT;
448 dwFlags |= COPY_PROMPT;
449 }
450
451 break;
452
453 case _T('Z'):
454 dwFlags |= COPY_RESTART;
455 break;
456
457 default:
458 /* Invalid switch */
459 ConOutResPrintf(STRING_ERROR_INVALID_SWITCH, _totupper(arg[i][1]));
460 nErrorLevel = 1;
461 freep (arg);
462 return 1;
463 break;
464 }
465 }
466 /* If it was a switch, subtract from total arguments */
467 nFiles--;
468 }
469 else
470 {
471 /* If it isn't a switch then it is the source or destination */
472 if(nSrc == -1)
473 {
474 nSrc = i;
475 }
476 else if(*arg[i] == _T('+') || *arg[i] == _T(','))
477 {
478 /* Add these onto the source string
479 this way we can do all checks
480 directly on source string later on */
481 TCHAR * ptr;
482 int length = (_tcslen(arg[nSrc]) +_tcslen(arg[i]) + _tcslen(arg[i+1]) + 1) * sizeof(TCHAR);
483 ptr = cmd_alloc(length);
484 if (ptr)
485 {
486 _tcscpy(ptr, arg[nSrc]);
487 _tcscat(ptr, arg[i]);
488 _tcscat(ptr, arg[i+1]);
489 cmd_free(arg[nSrc]);
490 arg[nSrc] = ptr;
491 i++;
492 nFiles -= 2;
493 }
494 }
495 else if(nDes == -1)
496 {
497 nDes = i;
498 }
499 }
500 }
501
502 /* keep quiet within batch files */
503 if (bc != NULL)
504 {
505 dwFlags |= COPY_NO_PROMPT;
506 dwFlags &= ~COPY_PROMPT;
507 }
508
509 if(nFiles < 1)
510 {
511 /* There are not enough files, there has to be at least 1 */
512 ConOutResPuts(STRING_ERROR_REQ_PARAM_MISSING);
513 freep (arg);
514 return 1;
515 }
516
517 if(nFiles > 2)
518 {
519 /* There are too many file names in command */
520 ConErrResPrintf(STRING_ERROR_TOO_MANY_PARAMETERS,_T(""));
521 nErrorLevel = 1;
522 freep (arg);
523 return 1;
524 }
525
526 if (nDes != -1) /* you can only append files when there is a destination */
527 {
528 if(((_tcschr (arg[nSrc], _T('+')) != NULL) ||
529 (_tcschr (arg[nSrc], _T('*')) != NULL && _tcschr (arg[nDes], _T('*')) == NULL) ||
530 (IsExistingDirectory (arg[nSrc]) && (_tcschr (arg[nDes], _T('*')) == NULL && !IsExistingDirectory (arg[nDes])))
531 ))
532 {
533 /* There is a + in the source filename, this means
534 that there is more then one file being put into
535 one file. */
536 bAppend = TRUE;
537 if(_tcschr (arg[nSrc], _T('+')) != NULL)
538 appendPointer = arg[nSrc];
539 }
540 }
541
542 /* Reusing the number of files variable */
543 nFiles = 0;
544
545 do
546 {
547 /* Set up the string that is the path to the destination */
548 if(nDes != -1)
549 {
550 if(_tcslen(arg[nDes]) == 2 && arg[nDes][1] == _T(':'))
551 {
552 GetRootPath(arg[nDes],szDestPath,MAX_PATH);
553 }
554 else
555 /* If the user entered two file names then form the full string path */
556 GetFullPathName (arg[nDes], MAX_PATH, szDestPath, NULL);
557 }
558 else
559 {
560 /* If no destination was entered then just use
561 the current directory as the destination */
562 GetCurrentDirectory (MAX_PATH, szDestPath);
563 }
564
565 /* Get the full string of the path to the source file */
566 if(_tcschr (arg[nSrc], _T('+')) != NULL)
567 {
568 _tcscpy(tmpName,_T("\0"));
569 /* Loop through the source file name and copy all
570 the chars one at a time until it gets too + */
571 while(TRUE)
572 {
573 if(!_tcsncmp (appendPointer,_T("+"),1) || !_tcsncmp (appendPointer,_T("\0"),1))
574 {
575 /* Now that the pointer is on the + we
576 need to go to the start of the next filename */
577 if(!_tcsncmp (appendPointer,_T("+"),1))
578 appendPointer++;
579 else
580 bDone = TRUE;
581 break;
582 }
583
584 _tcsncat(tmpName,appendPointer,1);
585 appendPointer++;
586 }
587
588 /* Finish the string off with a null char */
589 _tcsncat(tmpName,_T("\0"),1);
590
591 if(_tcschr (arg[nSrc], _T(',')) != NULL)
592 {
593 /* Only time there is a , in the source is when they are using touch
594 Cant have a destination and can only have on ,, at the end of the string
595 Cant have more then one file name */
596 szTouch = _tcsstr (arg[nSrc], _T("+"));
597 if(_tcsncmp (szTouch,_T("+,,\0"),4) || nDes != -1)
598 {
599 ConErrResPrintf(STRING_ERROR_INVALID_PARAM_FORMAT,arg[nSrc]);
600 nErrorLevel = 1;
601 freep (arg);
602 return 1;
603 }
604 bTouch = TRUE;
605 bDone = TRUE;
606 }
607
608 if(_tcslen(tmpName) == 2)
609 {
610 if(tmpName[1] == _T(':'))
611 {
612 GetRootPath(tmpName,szSrcPath,MAX_PATH);
613 }
614 }
615 else
616 /* Get the full path to first file in the string of file names */
617 GetFullPathName (tmpName, MAX_PATH, szSrcPath, NULL);
618 }
619 else
620 {
621 bDone = TRUE;
622 if(_tcslen(arg[nSrc]) == 2 && arg[nSrc][1] == _T(':'))
623 {
624 GetRootPath(arg[nSrc],szSrcPath,MAX_PATH);
625 }
626 else
627 /* Get the full path of the source file */
628 GetFullPathName (arg[nSrc], MAX_PATH, szSrcPath, NULL);
629 }
630
631 /* From this point on, we can assume that the shortest path is 3 letters long
632 and that would be [DriveLetter]:\ */
633
634 /* If there is no * in the path name and it is a folder
635 then we will need to add a wildcard to the pathname
636 so FindFirstFile comes up with all the files in that
637 folder */
638 if(_tcschr (szSrcPath, _T('*')) == NULL &&
639 IsExistingDirectory (szSrcPath))
640 {
641 /* If it doesnt have a \ at the end already then on needs to be added */
642 if(szSrcPath[_tcslen(szSrcPath) - 1] != _T('\\'))
643 _tcscat (szSrcPath, _T("\\"));
644 /* Add a wildcard after the \ */
645 _tcscat (szSrcPath, _T("*"));
646 }
647 /* Make sure there is an ending slash to the path if the dest is a folder */
648 if(_tcschr (szDestPath, _T('*')) == NULL &&
649 IsExistingDirectory(szDestPath))
650 {
651 if(szDestPath[_tcslen(szDestPath) - 1] != _T('\\'))
652 _tcscat (szDestPath, _T("\\"));
653 }
654
655 /* Get a list of all the files */
656 hFile = FindFirstFile (szSrcPath, &findBuffer);
657
658 /* We need to figure out what the name of the file in the is going to be */
659 if((szDestPath[_tcslen(szDestPath) - 1] == _T('*') && szDestPath[_tcslen(szDestPath) - 2] == _T('\\')) ||
660 szDestPath[_tcslen(szDestPath) - 1] == _T('\\'))
661 {
662 /* In this case we will be using the same name as the source file
663 for the destination file because destination is a folder */
664 bSrcName = TRUE;
665 }
666 else
667 {
668 /* Save the name the user entered */
669 UseThisName = _tcsrchr(szDestPath,_T('\\'));
670 UseThisName++;
671 _tcscpy(PreserveName,UseThisName);
672 }
673
674 /* Strip the paths back to the folder they are in */
675 for(i = (_tcslen(szSrcPath) - 1); i > -1; i--)
676 if(szSrcPath[i] != _T('\\'))
677 szSrcPath[i] = _T('\0');
678 else
679 break;
680
681 for(i = (_tcslen(szDestPath) - 1); i > -1; i--)
682 if(szDestPath[i] != _T('\\'))
683 szDestPath[i] = _T('\0');
684 else
685 break;
686
687 do
688 {
689 /* Check Breaker */
690 if(CheckCtrlBreak(BREAK_INPUT))
691 {
692 freep(arg);
693 return 1;
694 }
695 /* Set the override to yes each new file */
696 nOverwrite = 1;
697
698 /* If it couldnt open the file handle, print out the error */
699 if(hFile == INVALID_HANDLE_VALUE)
700 {
701 ConOutFormatMessage (GetLastError(), szSrcPath);
702 freep (arg);
703 nErrorLevel = 1;
704 return 1;
705 }
706
707 /* Ignore the . and .. files */
708 if(!_tcscmp (findBuffer.cFileName, _T(".")) ||
709 !_tcscmp (findBuffer.cFileName, _T(".."))||
710 findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
711 continue;
712
713 /* Copy the base folder over to a tmp string */
714 _tcscpy(tmpDestPath,szDestPath);
715
716 /* Can't put a file into a folder that isnt there */
717 if(_tcscmp (szDestPath, _T("\\\\.\\")) && !IsExistingDirectory(szDestPath))
718 {
719 ConOutFormatMessage (GetLastError (), szSrcPath);
720 freep (arg);
721 nErrorLevel = 1;
722 return 1;
723 }
724 /* Copy over the destination path name */
725 if(bSrcName)
726 _tcscat (tmpDestPath, findBuffer.cFileName);
727 else
728 {
729 /* If there is no wildcard you can use the name the user entered */
730 if(_tcschr (PreserveName, _T('*')) == NULL)
731 {
732 _tcscat (tmpDestPath, PreserveName);
733 }
734 else
735 {
736 /* The following lines of copy were written by someone else
737 (most likely Eric Khoul) and it was taken from ren.c */
738 LPTSTR p,q,r;
739 TCHAR DoneFile[MAX_PATH];
740 /* build destination file name */
741 p = findBuffer.cFileName;
742 q = PreserveName;
743 r = DoneFile;
744 while(*q != 0)
745 {
746 if (*q == '*')
747 {
748 q++;
749 while (*p != 0 && *p != *q)
750 {
751 *r = *p;
752 p++;
753 r++;
754 }
755 }
756 else if (*q == '?')
757 {
758 q++;
759 if (*p != 0)
760 {
761 *r = *p;
762 p++;
763 r++;
764 }
765 }
766 else
767 {
768 *r = *q;
769 if (*p != 0)
770 p++;
771 q++;
772 r++;
773 }
774 }
775 *r = 0;
776 /* Add the filename to the tmp string path */
777 _tcscat (tmpDestPath, DoneFile);
778 }
779 }
780
781 /* Build the string path to the source file */
782 _tcscpy(tmpSrcPath,szSrcPath);
783 _tcscat (tmpSrcPath, findBuffer.cFileName);
784
785 /* Check to see if the file is the same file */
786 if(!bTouch && !_tcscmp (tmpSrcPath, tmpDestPath))
787 {
788 ConOutResPrintf(STRING_COPY_ERROR2);
789
790 nErrorLevel = 1;
791 break;
792 }
793
794 /* Handle any overriding / prompting that needs to be done */
795 if(((!(dwFlags & COPY_NO_PROMPT) && IsExistingFile (tmpDestPath)) || dwFlags & COPY_PROMPT) && !bTouch)
796 nOverwrite = CopyOverwrite(tmpDestPath);
797 if(nOverwrite == PROMPT_NO || nOverwrite == PROMPT_BREAK)
798 continue;
799 if(nOverwrite == PROMPT_ALL || (nOverwrite == PROMPT_YES && bAppend))
800 dwFlags |= COPY_NO_PROMPT;
801
802 /* Tell weather the copy was successful or not */
803 if(copy(tmpSrcPath,tmpDestPath, bAppend, dwFlags, bTouch))
804 {
805 nFiles++;
806 /* only print source name when more then one file */
807 if(_tcschr (arg[nSrc], _T('+')) != NULL || _tcschr (arg[nSrc], _T('*')) != NULL)
808 ConOutPrintf(_T("%s\n"),findBuffer.cFileName);
809 //LoadString(CMD_ModuleHandle, STRING_MOVE_ERROR1, szMsg, RC_STRING_MAX_SIZE);
810 }
811 else
812 {
813 /* print out the error message */
814 ConOutResPrintf(STRING_COPY_ERROR3);
815 ConOutFormatMessage (GetLastError(), szSrcPath);
816 nErrorLevel = 1;
817 }
818
819 /* Loop through all wildcard files */
820 } while(FindNextFile (hFile, &findBuffer));
821
822 /* Loop through all files in src string with a + */
823 } while(!bDone);
824
825 /* print out the number of files copied */
826 ConOutResPrintf(STRING_COPY_FILE, nFiles);
827
828 FindClose(hFile);
829
830 if (arg != NULL)
831 freep(arg);
832
833 return 0;
834 }
835
836 #endif /* INCLUDE_CMD_COPY */