2 * COPY.C -- copy internal command.
7 * 01-Aug-98 (Rob Lake z63rrl@morgan.ucs.mun.ca)
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
14 * 13-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
15 * Added COPY command to CMD.
17 * 26-Jan-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
18 * Replaced CRT io functions by Win32 io functions.
20 * 27-Oct-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
21 * Disabled prompting when used in batch mode.
23 * 03-Apr-2005 (Magnus Olsen) <magnus@greatlord.com>)
24 * Remove all hardcode string to En.rc
26 * 13-Jul-2005 (Brandon Turner) <turnerb7@msu.edu>)
27 * Rewrite to clean up copy and support wildcard.
29 * 20-Jul-2005 (Brandon Turner) <turnerb7@msu.edu>)
30 * Add touch syntax. "copy arp.exe+,,"
31 * Copy command is now completed.
37 #ifdef INCLUDE_CMD_COPY
41 COPY_ASCII
= 0x001, /* /A */
42 COPY_DECRYPT
= 0x004, /* /D */
43 COPY_VERIFY
= 0x008, /* /V : Dummy, Never will be Impleneted */
44 COPY_SHORTNAME
= 0x010, /* /N : Dummy, Never will be Impleneted */
45 COPY_NO_PROMPT
= 0x020, /* /Y */
46 COPY_PROMPT
= 0x040, /* /-Y */
47 COPY_RESTART
= 0x080, /* /Z */
48 COPY_BINARY
= 0x100, /* /B */
51 #define BUFF_SIZE 16384 /* 16k = max buffer size */
55 copy (TCHAR source
[MAX_PATH
],
61 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
62 FILETIME srctime
,NewFileTime
;
71 TCHAR TrueDest
[MAX_PATH
];
72 TCHAR TempSrc
[MAX_PATH
];
74 SYSTEMTIME CurrentTime
;
77 if(CheckCtrlBreak(BREAK_INPUT
))
81 DebugPrintf (_T("checking mode\n"));
86 hFileSrc
= CreateFile (source
, GENERIC_WRITE
, FILE_SHARE_READ
,
87 NULL
, OPEN_EXISTING
, 0, NULL
);
88 if (hFileSrc
== INVALID_HANDLE_VALUE
)
90 LoadString(CMD_ModuleHandle
, STRING_COPY_ERROR1
, szMsg
, RC_STRING_MAX_SIZE
);
91 ConOutPrintf(szMsg
, source
);
96 GetSystemTime(&CurrentTime
);
97 SystemTimeToFileTime(&CurrentTime
, &NewFileTime
);
98 if(SetFileTime(hFileSrc
,(LPFILETIME
) NULL
, (LPFILETIME
) NULL
, &NewFileTime
))
100 CloseHandle(hFileSrc
);
107 CloseHandle(hFileSrc
);
112 dwAttrib
= GetFileAttributes (source
);
114 hFileSrc
= CreateFile (source
, GENERIC_READ
, FILE_SHARE_READ
,
115 NULL
, OPEN_EXISTING
, 0, NULL
);
116 if (hFileSrc
== INVALID_HANDLE_VALUE
)
118 LoadString(CMD_ModuleHandle
, STRING_COPY_ERROR1
, szMsg
, RC_STRING_MAX_SIZE
);
119 ConOutPrintf(szMsg
, source
);
125 DebugPrintf (_T("getting time\n"));
128 GetFileTime (hFileSrc
, &srctime
, NULL
, NULL
);
131 DebugPrintf (_T("copy: flags has %s\n"),
132 lpdwFlags
& COPY_ASCII
? "ASCII" : "BINARY");
135 /* Check to see if /D or /Z are true, if so we need a middle
136 man to copy the file too to allow us to use CopyFileEx later */
137 if(lpdwFlags
& COPY_DECRYPT
)
139 GetEnvironmentVariable(_T("TEMP"),TempSrc
,MAX_PATH
);
140 _tcscat(TempSrc
,_T("\\"));
141 FileName
= _tcsrchr(source
,_T('\\'));
143 _tcscat(TempSrc
,FileName
);
144 /* This is needed to be on the end to prevent an error
145 if the user did "copy /D /Z foo bar then it would be copied
146 too %TEMP%\foo here and when %TEMP%\foo when it sets it up
147 for COPY_RESTART, this would mean it is copying to itself
148 which would error when it tried to open the handles for ReadFile
150 _tcscat(TempSrc
,_T(".decrypt"));
151 if(!CopyFileEx(source
, TempSrc
, NULL
, NULL
, FALSE
, COPY_FILE_ALLOW_DECRYPTED_DESTINATION
))
156 _tcscpy(source
, TempSrc
);
160 if(lpdwFlags
& COPY_RESTART
)
162 _tcscpy(TrueDest
, dest
);
163 GetEnvironmentVariable(_T("TEMP"),dest
,MAX_PATH
);
164 _tcscat(dest
,_T("\\"));
165 FileName
= _tcsrchr(TrueDest
,_T('\\'));
167 _tcscat(dest
,FileName
);
172 if (!IsExistingFile (dest
))
175 DebugPrintf (_T("opening/creating\n"));
178 CreateFile (dest
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
182 if (!_tcscmp (dest
, source
))
184 LoadString(CMD_ModuleHandle
, STRING_COPY_ERROR2
, szMsg
, RC_STRING_MAX_SIZE
);
185 ConOutPrintf(szMsg
, source
);
187 CloseHandle (hFileSrc
);
193 DebugPrintf (_T("SetFileAttributes (%s, FILE_ATTRIBUTE_NORMAL);\n"), dest
);
195 SetFileAttributes (dest
, FILE_ATTRIBUTE_NORMAL
);
198 DebugPrintf (_T("DeleteFile (%s);\n"), dest
);
202 hFileDest
= CreateFile (dest
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
206 LONG lFilePosHigh
= 0;
208 if (!_tcscmp (dest
, source
))
210 CloseHandle (hFileSrc
);
215 DebugPrintf (_T("opening/appending\n"));
217 SetFileAttributes (dest
, FILE_ATTRIBUTE_NORMAL
);
220 CreateFile (dest
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
222 /* Move to end of file to start writing */
223 SetFilePointer (hFileDest
, 0, &lFilePosHigh
,FILE_END
);
227 if (hFileDest
== INVALID_HANDLE_VALUE
)
229 CloseHandle (hFileSrc
);
230 ConOutResPuts(STRING_ERROR_PATH_NOT_FOUND
);
234 buffer
= (LPBYTE
)malloc (BUFF_SIZE
);
237 CloseHandle (hFileDest
);
238 CloseHandle (hFileSrc
);
239 ConOutResPuts(STRING_ERROR_OUT_OF_MEMORY
);
247 ReadFile (hFileSrc
, buffer
, BUFF_SIZE
, &dwRead
, NULL
);
248 if (lpdwFlags
& COPY_ASCII
)
250 for (i
= 0; i
< dwRead
; i
++)
252 /* we're dealing with ASCII files! */
253 if (((LPSTR
)buffer
)[i
] == 0x1A)
265 WriteFile (hFileDest
, buffer
, dwRead
, &dwWritten
, NULL
);
266 if (dwWritten
!= dwRead
|| CheckCtrlBreak(BREAK_INPUT
))
268 ConOutResPuts(STRING_COPY_ERROR3
);
271 CloseHandle (hFileDest
);
272 CloseHandle (hFileSrc
);
277 while (dwRead
&& !bEof
);
280 DebugPrintf (_T("setting time\n"));
282 SetFileTime (hFileDest
, &srctime
, NULL
, NULL
);
284 if (lpdwFlags
& COPY_ASCII
)
286 /* we're dealing with ASCII files! */
287 ((LPSTR
)buffer
)[0] = 0x1A;
288 ((LPSTR
)buffer
)[1] = '\0';
290 DebugPrintf (_T("appending ^Z\n"));
292 WriteFile (hFileDest
, buffer
, sizeof(CHAR
), &dwWritten
, NULL
);
296 CloseHandle (hFileDest
);
297 CloseHandle (hFileSrc
);
300 DebugPrintf (_T("setting mode\n"));
302 SetFileAttributes (dest
, dwAttrib
);
304 /* Now finish off the copy if needed with CopyFileEx */
305 if(lpdwFlags
& COPY_RESTART
)
307 if(!CopyFileEx(dest
, TrueDest
, NULL
, NULL
, FALSE
, COPY_FILE_RESTARTABLE
))
313 /* Take care of file in the temp folder */
318 if(lpdwFlags
& COPY_DECRYPT
)
327 static INT
CopyOverwrite (LPTSTR fn
)
329 /*ask the user if they want to override*/
330 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
332 LoadString(CMD_ModuleHandle
, STRING_COPY_HELP1
, szMsg
, RC_STRING_MAX_SIZE
);
333 ConOutPrintf(szMsg
,fn
);
334 res
= FilePromptYNA (_T(""));
339 INT
cmd_copy (LPTSTR cmd
, LPTSTR param
)
341 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
343 INT argc
, i
, nFiles
, nOverwrite
= 0, nSrc
= -1, nDes
= -1;
344 /* this is the path up to the folder of the src and dest ie C:\windows\ */
345 TCHAR szDestPath
[MAX_PATH
];
346 TCHAR szSrcPath
[MAX_PATH
];
348 /* If this is the type of copy where we are adding files */
349 BOOL bAppend
= FALSE
;
350 WIN32_FIND_DATA findBuffer
;
353 /* Used when something like "copy c*.exe d*.exe" during the process of
354 figuring out the new name */
355 TCHAR tmpName
[MAX_PATH
] = _T("");
356 /* Pointer to keep track of how far through the append input(file1+file2+file3) we are */
357 TCHAR
* appendPointer
= _T("\0");
358 /* The full path to src and dest. This has drive letter, folders, and filename */
359 TCHAR tmpDestPath
[MAX_PATH
];
360 TCHAR tmpSrcPath
[MAX_PATH
];
361 /* A bool on weather or not the destination name will be taking from the input */
362 BOOL bSrcName
= FALSE
;
363 /* Seems like a waste but it is a pointer used to copy from input to PreserveName */
365 /* Stores the name( i.e. blah.txt or blah*.txt) which later we might need */
366 TCHAR PreserveName
[MAX_PATH
];
367 /* for CMDCOPY env */
374 /*Show help/usage info*/
375 if (!_tcsncmp (param
, _T("/?"), 2))
377 ConOutResPaging(TRUE
, STRING_COPY_HELP2
);
383 /* Get the envor value if it exists */
384 evar
= malloc(512 * sizeof(TCHAR
));
385 if (evar
==NULL
) size
= 0;
388 size
= GetEnvironmentVariable (_T("COPYCMD"), evar
, 512);
392 evar
= realloc(evar
,size
* sizeof(TCHAR
) );
395 size
= GetEnvironmentVariable (_T("COPYCMD"), evar
, size
);
403 /* check see if we did get any env variable */
407 /* scan and set the flags */
410 if (_tcsncicmp(_T("/A"),&evar
[t
],2)==0)
412 dwFlags
|=COPY_ASCII
;
416 else if (_tcsncicmp(_T("/B"),&evar
[t
],2)==0)
418 dwFlags
|= COPY_BINARY
;
421 else if (_tcsncicmp(_T("/D"),&evar
[t
],2)==0)
423 dwFlags
|= COPY_DECRYPT
;
427 else if (_tcsncicmp(_T("/V"),&evar
[t
],2)==0)
429 dwFlags
|= COPY_VERIFY
;
433 else if (_tcsncicmp(_T("/N"),&evar
[t
],2)==0)
435 dwFlags
|= COPY_SHORTNAME
;
439 else if (_tcsncicmp(_T("/Y"),&evar
[t
],2)==0)
441 dwFlags
|= COPY_NO_PROMPT
;
445 else if (_tcsncicmp(_T("/-Y"),&evar
[t
],3)==0)
447 dwFlags
|= COPY_PROMPT
;
451 else if (_tcsncicmp(_T("/Z"),&evar
[t
],2)==0)
453 dwFlags
|= COPY_PROMPT
;
461 /*Split the user input into array*/
462 arg
= split (param
, &argc
, FALSE
);
466 /*Read switches and count files*/
467 for (i
= 0; i
< argc
; i
++)
469 if (*arg
[i
] == _T('/'))
471 if (_tcslen(arg
[i
]) >= 2)
473 switch (_totupper(arg
[i
][1]))
477 dwFlags
|= COPY_ASCII
;
481 dwFlags
|= COPY_BINARY
;
485 dwFlags
|= COPY_DECRYPT
;
489 dwFlags
|= COPY_VERIFY
;
493 dwFlags
|= COPY_SHORTNAME
;
497 dwFlags
|= COPY_NO_PROMPT
;
498 dwFlags
&= ~COPY_PROMPT
;
502 if(_tcslen(arg
[i
]) >= 3)
503 if(_totupper(arg
[i
][2]) == _T('Y'))
505 dwFlags
&= ~COPY_NO_PROMPT
;
506 dwFlags
|= COPY_PROMPT
;
512 dwFlags
|= COPY_RESTART
;
517 LoadString(CMD_ModuleHandle
, STRING_ERROR_INVALID_SWITCH
, szMsg
, RC_STRING_MAX_SIZE
);
518 ConOutPrintf(szMsg
, _totupper(arg
[i
][1]));
524 /*If it was a switch, subtract from total arguments*/
529 /*if it isnt a switch then it is the source or destination*/
534 else if(*arg
[i
] == _T('+') || *arg
[i
] == _T(','))
536 /* Add these onto the source string
537 this way we can do all checks
538 directly on source string later on */
539 _tcscat(arg
[nSrc
],arg
[i
]);
550 /* keep quiet within batch files */
553 dwFlags
|= COPY_NO_PROMPT
;
554 dwFlags
&= ~COPY_PROMPT
;
559 /* There is not enough files, there has to be at least 1 */
560 ConOutResPuts(STRING_ERROR_REQ_PARAM_MISSING
);
567 /* there is too many file names in command */
568 LoadString(CMD_ModuleHandle
, STRING_ERROR_TOO_MANY_PARAMETERS
, szMsg
, RC_STRING_MAX_SIZE
);
569 ConErrPrintf(szMsg
,_T(""));
575 if(((_tcschr (arg
[nSrc
], _T('+')) != NULL
) ||
576 (_tcschr (arg
[nSrc
], _T('*')) != NULL
&& _tcschr (arg
[nDes
], _T('*')) == NULL
) ||
577 (IsExistingDirectory (arg
[nSrc
]) && (_tcschr (arg
[nDes
], _T('*')) == NULL
&& !IsExistingDirectory (arg
[nDes
])))
580 /* There is a + in the source filename, this means
581 that there is more then one file being put into
584 if(_tcschr (arg
[nSrc
], _T('+')) != NULL
)
585 appendPointer
= arg
[nSrc
];
588 /* Reusing the number of files variable */
593 /* Set up the string that is the path to the destination */
596 if(_tcslen(arg
[nDes
]) == 2 && arg
[nDes
][1] == _T(':'))
598 GetRootPath(arg
[nDes
],szDestPath
,MAX_PATH
);
601 /* If the user entered two file names then form the full string path*/
602 GetFullPathName (arg
[nDes
], MAX_PATH
, szDestPath
, NULL
);
607 /* If no destination was entered then just use
608 the current directory as the destination */
609 GetCurrentDirectory (MAX_PATH
, szDestPath
);
613 /* Get the full string of the path to the source file*/
614 if(_tcschr (arg
[nSrc
], _T('+')) != NULL
)
616 _tcscpy(tmpName
,_T("\0"));
617 /* Loop through the source file name and copy all
618 the chars one at a time until it gets too + */
621 if(!_tcsncmp (appendPointer
,_T("+"),1) || !_tcsncmp (appendPointer
,_T("\0"),1))
623 /* Now that the pointer is on the + we
624 need to go to the start of the next filename */
625 if(!_tcsncmp (appendPointer
,_T("+"),1))
633 _tcsncat(tmpName
,appendPointer
,1);
637 /* Finish the string off with a null char */
638 _tcsncat(tmpName
,_T("\0"),1);
640 if(_tcschr (arg
[nSrc
], _T(',')) != NULL
)
642 /* Only time there is a , in the source is when they are using touch
643 Cant have a destination and can only have on ,, at the end of the string
644 Cant have more then one file name */
645 szTouch
= _tcsstr (arg
[nSrc
], _T("+"));
646 if(_tcsncmp (szTouch
,_T("+,,\0"),4) || nDes
!= -1)
648 LoadString(CMD_ModuleHandle
, STRING_ERROR_INVALID_PARAM_FORMAT
, szMsg
, RC_STRING_MAX_SIZE
);
649 ConErrPrintf(szMsg
,arg
[nSrc
]);
658 if(_tcslen(tmpName
) == 2)
660 if(tmpName
[1] == _T(':'))
663 GetRootPath(tmpName
,szSrcPath
,MAX_PATH
);
667 /* Get the full path to first file in the string of file names */
668 GetFullPathName (tmpName
, MAX_PATH
, szSrcPath
, NULL
);
673 if(_tcslen(arg
[nSrc
]) == 2 && arg
[nSrc
][1] == _T(':'))
675 GetRootPath(arg
[nSrc
],szSrcPath
,MAX_PATH
);
678 /* Get the full path of the source file */
679 GetFullPathName (arg
[nSrc
], MAX_PATH
, szSrcPath
, NULL
);
683 /* From this point on, we can assume that the shortest path is 3 letters long
684 and that would be [DriveLetter]:\ */
686 /* If there is no * in the path name and it is a folder
687 then we will need to add a wildcard to the pathname
688 so FindFirstFile comes up with all the files in that
690 if(_tcschr (szSrcPath
, _T('*')) == NULL
&&
691 IsExistingDirectory (szSrcPath
))
693 /* If it doesnt have a \ at the end already then on needs to be added */
694 if(szSrcPath
[_tcslen(szSrcPath
) - 1] != _T('\\'))
695 _tcscat (szSrcPath
, _T("\\"));
696 /* Add a wildcard after the \ */
697 _tcscat (szSrcPath
, _T("*"));
699 /* Make sure there is an ending slash to the path if the dest is a folder */
700 if(_tcschr (szDestPath
, _T('*')) == NULL
&&
701 IsExistingDirectory(szDestPath
))
703 if(szDestPath
[_tcslen(szDestPath
) - 1] != _T('\\'))
704 _tcscat (szDestPath
, _T("\\"));
708 /* Get a list of all the files */
709 hFile
= FindFirstFile (szSrcPath
, &findBuffer
);
712 /* We need to figure out what the name of the file in the is going to be */
713 if((szDestPath
[_tcslen(szDestPath
) - 1] == _T('*') && szDestPath
[_tcslen(szDestPath
) - 2] == _T('\\')) ||
714 szDestPath
[_tcslen(szDestPath
) - 1] == _T('\\'))
716 /* In this case we will be using the same name as the source file
717 for the destination file because destination is a folder */
722 /* Save the name the user entered */
723 UseThisName
= _tcsrchr(szDestPath
,_T('\\'));
725 _tcscpy(PreserveName
,UseThisName
);
728 /* Strip the paths back to the folder they are in */
729 for(i
= (_tcslen(szSrcPath
) - 1); i
> -1; i
--)
730 if(szSrcPath
[i
] != _T('\\'))
731 szSrcPath
[i
] = _T('\0');
735 for(i
= (_tcslen(szDestPath
) - 1); i
> -1; i
--)
736 if(szDestPath
[i
] != _T('\\'))
737 szDestPath
[i
] = _T('\0');
744 if(CheckCtrlBreak(BREAK_INPUT
))
749 /* Set the override to yes each new file */
752 /* If it couldnt open the file handle, print out the error */
753 if(hFile
== INVALID_HANDLE_VALUE
)
755 ConOutFormatMessage (GetLastError(), szSrcPath
);
761 /* Ignore the . and .. files */
762 if(!_tcscmp (findBuffer
.cFileName
, _T(".")) ||
763 !_tcscmp (findBuffer
.cFileName
, _T(".."))||
764 findBuffer
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
767 /* Copy the base folder over to a tmp string */
768 _tcscpy(tmpDestPath
,szDestPath
);
770 /* Can't put a file into a folder that isnt there */
771 if(!IsExistingDirectory(szDestPath
))
773 ConOutFormatMessage (GetLastError (), szSrcPath
);
778 /* Copy over the destination path name */
780 _tcscat (tmpDestPath
, findBuffer
.cFileName
);
783 /* If there is no wildcard you can use the name the user entered */
784 if(_tcschr (PreserveName
, _T('*')) == NULL
)
786 _tcscat (tmpDestPath
, PreserveName
);
790 /* The following lines of copy were written by someone else
791 (most likely Eric Khoul) and it was taken from ren.c */
793 TCHAR DoneFile
[MAX_PATH
];
794 /* build destination file name */
795 p
= findBuffer
.cFileName
;
803 while (*p
!= 0 && *p
!= *q
)
830 /* Add the filename to the tmp string path */
831 _tcscat (tmpDestPath
, DoneFile
);
837 /* Build the string path to the source file */
838 _tcscpy(tmpSrcPath
,szSrcPath
);
839 _tcscat (tmpSrcPath
, findBuffer
.cFileName
);
841 /* Check to see if the file is the same file */
842 if(!bTouch
&& !_tcscmp (tmpSrcPath
, tmpDestPath
))
845 /* Handle any overriding / prompting that needs to be done */
846 if(((!(dwFlags
& COPY_NO_PROMPT
) && IsExistingFile (tmpDestPath
)) || dwFlags
& COPY_PROMPT
) && !bTouch
)
847 nOverwrite
= CopyOverwrite(tmpDestPath
);
848 if(nOverwrite
== PROMPT_NO
|| nOverwrite
== PROMPT_BREAK
)
850 if(nOverwrite
== PROMPT_ALL
|| (nOverwrite
== PROMPT_YES
&& bAppend
))
851 dwFlags
|= COPY_NO_PROMPT
;
853 /* Tell weather the copy was successful or not */
854 if(copy(tmpSrcPath
,tmpDestPath
, bAppend
, dwFlags
, bTouch
))
857 /* only print source name when more then one file */
858 if(_tcschr (arg
[nSrc
], _T('+')) != NULL
|| _tcschr (arg
[nSrc
], _T('*')) != NULL
)
859 ConOutPrintf(_T("%s\n"),findBuffer
.cFileName
);
860 //LoadString(CMD_ModuleHandle, STRING_MOVE_ERROR1, szMsg, RC_STRING_MAX_SIZE);
864 /* print out the error message */
865 LoadString(CMD_ModuleHandle
, STRING_COPY_ERROR3
, szMsg
, RC_STRING_MAX_SIZE
);
867 ConOutFormatMessage (GetLastError(), szSrcPath
);
871 /* Loop through all wildcard files */
872 }while(FindNextFile (hFile
, &findBuffer
));
873 /* Loop through all files in src string with a + */
876 /* print out the number of files copied */
877 LoadString(CMD_ModuleHandle
, STRING_COPY_FILE
, szMsg
, RC_STRING_MAX_SIZE
);
878 ConOutPrintf(szMsg
, nFiles
);
888 #endif /* INCLUDE_CMD_COPY */