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.
33 #ifdef INCLUDE_CMD_COPY
37 COPY_ASCII
= 0x001, /* /A */
38 COPY_DECRYPT
= 0x004, /* /D : Not Impleneted */
39 COPY_VERIFY
= 0x008, /* /V : Dummy, Never will be Impleneted */
40 COPY_SHORTNAME
= 0x010, /* /N : Not Impleneted */
41 COPY_NO_PROMPT
= 0x020, /* /Y */
42 COPY_PROMPT
= 0x040, /* /-Y */
43 COPY_RESTART
= 0x080, /* /Z : Not Impleneted */
44 COPY_BINARY
= 0x100, /* /B */
47 #define BUFF_SIZE 16384 /* 16k = max buffer size */
50 int copy (LPTSTR source
, LPTSTR dest
, int append
, DWORD lpdwFlags
)
52 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
64 DebugPrintf (_T("checking mode\n"));
67 dwAttrib
= GetFileAttributes (source
);
69 hFileSrc
= CreateFile (source
, GENERIC_READ
, FILE_SHARE_READ
,
70 NULL
, OPEN_EXISTING
, 0, NULL
);
71 if (hFileSrc
== INVALID_HANDLE_VALUE
)
73 LoadString(CMD_ModuleHandle
, STRING_COPY_ERROR1
, szMsg
, RC_STRING_MAX_SIZE
);
74 ConOutPrintf(szMsg
, source
);
80 DebugPrintf (_T("getting time\n"));
83 GetFileTime (hFileSrc
, &srctime
, NULL
, NULL
);
86 DebugPrintf (_T("copy: flags has %s\n"),
87 *lpdwFlags
& ASCII
? "ASCII" : "BINARY");
90 if (!IsExistingFile (dest
))
93 DebugPrintf (_T("opening/creating\n"));
96 CreateFile (dest
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
100 if (!_tcscmp (dest
, source
))
102 LoadString(CMD_ModuleHandle
, STRING_COPY_ERROR2
, szMsg
, RC_STRING_MAX_SIZE
);
103 ConOutPrintf(szMsg
, source
);
105 CloseHandle (hFileSrc
);
111 DebugPrintf (_T("SetFileAttributes (%s, FILE_ATTRIBUTE_NORMAL);\n"), dest
);
113 SetFileAttributes (dest
, FILE_ATTRIBUTE_NORMAL
);
116 DebugPrintf (_T("DeleteFile (%s);\n"), dest
);
120 hFileDest
= CreateFile (dest
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
124 LONG lFilePosHigh
= 0;
126 if (!_tcscmp (dest
, source
))
128 CloseHandle (hFileSrc
);
133 DebugPrintf (_T("opening/appending\n"));
135 SetFileAttributes (dest
, FILE_ATTRIBUTE_NORMAL
);
138 CreateFile (dest
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
140 /* Move to end of file to start writing */
141 SetFilePointer (hFileDest
, 0, &lFilePosHigh
,FILE_END
);
145 if (hFileDest
== INVALID_HANDLE_VALUE
)
147 CloseHandle (hFileSrc
);
148 error_path_not_found ();
152 buffer
= (LPBYTE
)malloc (BUFF_SIZE
);
155 CloseHandle (hFileDest
);
156 CloseHandle (hFileSrc
);
157 error_out_of_memory ();
164 ReadFile (hFileSrc
, buffer
, BUFF_SIZE
, &dwRead
, NULL
);
165 if (lpdwFlags
& COPY_ASCII
)
167 for (i
= 0; i
< dwRead
; i
++)
169 if (((LPTSTR
)buffer
)[i
] == 0x1A)
181 WriteFile (hFileDest
, buffer
, dwRead
, &dwWritten
, NULL
);
182 if (dwWritten
!= dwRead
)
184 ConOutResPuts(STRING_COPY_ERROR3
);
187 CloseHandle (hFileDest
);
188 CloseHandle (hFileSrc
);
193 while (dwRead
&& !bEof
);
196 DebugPrintf (_T("setting time\n"));
198 SetFileTime (hFileDest
, &srctime
, NULL
, NULL
);
200 if (lpdwFlags
& COPY_ASCII
)
202 ((LPTSTR
)buffer
)[0] = 0x1A;
203 ((LPTSTR
)buffer
)[1] = _T('\0');
205 DebugPrintf (_T("appending ^Z\n"));
207 WriteFile (hFileDest
, buffer
, sizeof(TCHAR
), &dwWritten
, NULL
);
211 CloseHandle (hFileDest
);
212 CloseHandle (hFileSrc
);
215 DebugPrintf (_T("setting mode\n"));
217 SetFileAttributes (dest
, dwAttrib
);
223 static INT
Overwrite (LPTSTR fn
)
225 /*ask the user if they want to override*/
226 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
228 LoadString(CMD_ModuleHandle
, STRING_COPY_HELP1
, szMsg
, RC_STRING_MAX_SIZE
);
229 ConOutPrintf(szMsg
,fn
);
230 res
= FilePromptYNA ("");
235 INT
cmd_copy (LPTSTR cmd
, LPTSTR param
)
237 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
239 INT argc
, i
, nFiles
, nOverwrite
= 0, nSrc
= -1, nDes
= -1;
240 /* this is the path up to the folder of the src and dest ie C:\windows\ */
241 TCHAR szDestPath
[MAX_PATH
];
242 TCHAR szSrcPath
[MAX_PATH
];
244 /* If this is the type of copy where we are adding files */
245 BOOL bAppend
= FALSE
;
246 WIN32_FIND_DATA findBuffer
;
248 /* Used when something like "copy c*.exe d*.exe" during the process of
249 figuring out the new name */
250 TCHAR tmpName
[MAX_PATH
] = _T("");
251 /* Pointer to keep track of how far through the append input(file1+file2+file3) we are */
252 TCHAR
* appendPointer
= _T("\0");
253 /* The full path to src and dest. This has drive letter, folders, and filename */
254 TCHAR tmpDestPath
[MAX_PATH
];
255 TCHAR tmpSrcPath
[MAX_PATH
];
256 /* A bool on weather or not the destination name will be taking from the input */
257 BOOL bSrcName
= FALSE
;
258 /* Seems like a waste but it is a pointer used to copy from input to PreserveName */
260 /* Stores the name( i.e. blah.txt or blah*.txt) which later we might need */
261 TCHAR PreserveName
[MAX_PATH
];
262 /* for CMDCOPY env */
267 /*Show help/usage info*/
268 if (!_tcsncmp (param
, _T("/?"), 2))
270 ConOutResPaging(TRUE
, STRING_COPY_HELP2
);
276 /* Get the envor value if it exists */
278 size
= GetEnvironmentVariable (_T("COPYCMD"), evar
, 512);
281 evar
= realloc(evar
,size
* sizeof(TCHAR
) );
284 size
= GetEnvironmentVariable (_T("COPYCMD"), evar
, size
);
287 /* check see if we did get any env variable */
291 /* scan and set the flags */
294 if (_tcsncicmp(_T("/A"),&evar
[t
],2)==0)
300 else if (_tcsncicmp(_T("/B"),&evar
[t
],2)==0)
302 dwFlags
|= COPY_BINARY
;
306 else if (_tcsncicmp(_T("/D"),&evar
[t
],2)==0)
308 dwFlags
|= COPY_DECRYPT
;
313 else if (_tcsncicmp(_T("/V"),&evar
[t
],2)==0)
315 dwFlags
|= COPY_VERIFY
;
320 else if (_tcsncicmp(_T("/N"),&evar
[t
],2)==0)
322 dwFlags
|= COPY_SHORTNAME
;
327 else if (_tcsncicmp(_T("/Y"),&evar
[t
],2)==0)
329 dwFlags
|= COPY_NO_PROMPT
;
334 else if (_tcsncicmp(_T("/-Y"),&evar
[t
],3)==0)
336 dwFlags
|= COPY_PROMPT
;
342 else if (_tcsncicmp(_T("/Z"),&evar
[t
],2)==0)
344 dwFlags
|= COPY_PROMPT
;
353 /*Split the user input into array*/
354 arg
= split (param
, &argc
, FALSE
);
358 /*Read switches and count files*/
359 for (i
= 0; i
< argc
; i
++)
361 if (*arg
[i
] == _T('/'))
363 if (_tcslen(arg
[i
]) >= 2)
365 switch (_totupper(arg
[i
][1]))
369 dwFlags
|= COPY_ASCII
;
373 dwFlags
|= COPY_BINARY
;
377 dwFlags
|= COPY_DECRYPT
;
381 dwFlags
|= COPY_VERIFY
;
385 dwFlags
|= COPY_SHORTNAME
;
389 dwFlags
|= COPY_NO_PROMPT
;
390 dwFlags
&= ~COPY_PROMPT
;
394 if(_tcslen(arg
[i
]) >= 3)
395 if(_totupper(arg
[i
][2]) == _T('Y'))
397 dwFlags
&= ~COPY_NO_PROMPT
;
398 dwFlags
|= COPY_PROMPT
;
404 dwFlags
|= COPY_RESTART
;
409 error_invalid_switch(_totupper(arg
[i
][1]));
414 /*If it was a switch, subtract from total arguments*/
419 /*if it isnt a switch then it is the source or destination*/
430 /* There is not enough files, there has to be at least 1 */
431 error_req_param_missing();
437 /* there is too many file names in command */
438 error_too_many_parameters("");
444 ((_tcschr (arg
[nSrc
], _T('+')) != NULL
) ||
445 (_tcschr (arg
[nSrc
], _T('*')) != NULL
&& _tcschr (arg
[nDes
], _T('*')) == NULL
) ||
446 (IsExistingDirectory (arg
[nSrc
]) && (_tcschr (arg
[nDes
], _T('*')) == NULL
&& !IsExistingDirectory (arg
[nDes
])))
449 /* There is a + in the source filename, this means
450 that there is more then one file being put into
453 if(_tcschr (arg
[nSrc
], _T('+')) != NULL
)
454 appendPointer
= arg
[nSrc
];
457 /* Reusing the number of files variable */
463 /* Set up the string that is the path to the destination */
466 /* Check to make sure if they entered c:, if they do then GFPN
467 return current directory even though msdn says it will return c:\ */
468 if(_tcslen(arg
[nDes
]) == 2)
470 if(arg
[nDes
][1] == _T(':'))
472 _tcscpy (szDestPath
, arg
[nDes
]);
473 _tcscat (szDestPath
, _T("\\"));
477 /* If the user entered two file names then form the full string path*/
478 GetFullPathName (arg
[nDes
], MAX_PATH
, szDestPath
, NULL
);
483 /* If no destination was entered then just use
484 the current directory as the destination */
485 GetCurrentDirectory (MAX_PATH
, szDestPath
);
489 /* Get the full string of the path to the source file*/
490 if(_tcschr (arg
[nSrc
], _T('+')) != NULL
)
493 _tcscpy(tmpName
,_T("\0"));
494 /* Loop through the source file name and copy all
495 the chars one at a time until it gets too + */
499 if(!_tcsncmp (appendPointer
,_T("+"),1) || !_tcsncmp (appendPointer
,_T("\0"),1))
501 /* Now that the pointer is on the + we
502 need to go to the start of the next filename */
503 if(!_tcsncmp (appendPointer
,_T("+"),1))
507 _tcsncat(tmpName
,appendPointer
,1);
510 /* Finish the string off with a null char */
511 _tcsncat(tmpName
,_T("\0"),1);
512 /* Check to make sure if they entered c:, if they do then GFPN
513 return current directory even though msdn says it will return c:\ */
514 if(_tcslen(tmpName
) == 2)
516 if(tmpName
[1] == _T(':'))
518 _tcscpy (szSrcPath
, tmpName
);
519 _tcscat (szSrcPath
, _T("\\"));
523 /* Get the full path to first file in the string of file names */
524 GetFullPathName (tmpName
, MAX_PATH
, szSrcPath
, NULL
);
528 /* Check to make sure if they entered c:, if they do then GFPN
529 return current directory even though msdn says it will return c:\ */
530 if(_tcslen(arg
[nSrc
]) == 2)
532 if(arg
[nSrc
][1] == _T(':'))
534 _tcscpy (szSrcPath
, arg
[nSrc
]);
535 _tcscat (szSrcPath
, _T("\\"));
539 /* Get the full path of the source file */
540 GetFullPathName (arg
[nSrc
], MAX_PATH
, szSrcPath
, NULL
);
544 /* From this point on, we can assume that the shortest path is 3 letters long
545 and that would be [DriveLetter]:\ */
547 /* If there is no * in the path name and it is a folder
548 then we will need to add a wildcard to the pathname
549 so FindFirstFile comes up with all the files in that
551 if(_tcschr (szSrcPath
, _T('*')) == NULL
&&
552 IsExistingDirectory (szSrcPath
))
554 /* If it doesnt have a \ at the end already then on needs to be added */
555 if(szSrcPath
[_tcslen(szSrcPath
) - 1] != _T('\\'))
556 _tcscat (szSrcPath
, _T("\\"));
557 /* Add a wildcard after the \ */
558 _tcscat (szSrcPath
, _T("*"));
560 /* Make sure there is an ending slash to the path if the dest is a folder */
561 if(_tcschr (szDestPath
, _T('*')) == NULL
&&
562 IsExistingDirectory(szDestPath
))
564 if(szDestPath
[_tcslen(szDestPath
) - 1] != _T('\\'))
565 _tcscat (szDestPath
, _T("\\"));
569 /* Get a list of all the files */
570 hFile
= FindFirstFile (szSrcPath
, &findBuffer
);
573 /* We need to figure out what the name of the file in the is going to be */
574 if((szDestPath
[_tcslen(szDestPath
) - 1] == _T('*') && szDestPath
[_tcslen(szDestPath
) - 2] == _T('\\')) ||
575 szDestPath
[_tcslen(szDestPath
) - 1] == _T('\\'))
577 /* In this case we will be using the same name as the source file
578 for the destination file because destination is a folder */
583 /* Save the name the user entered */
584 UseThisName
= _tcsrchr(szDestPath
,_T('\\'));
586 _tcscpy(PreserveName
,UseThisName
);
589 /* Strip the paths back to the folder they are in */
590 for(i
= (_tcslen(szSrcPath
) - 1); i
> -1; i
--)
591 if(szSrcPath
[i
] != _T('\\'))
592 szSrcPath
[i
] = _T('\0');
596 for(i
= (_tcslen(szDestPath
) - 1); i
> -1; i
--)
597 if(szDestPath
[i
] != _T('\\'))
598 szDestPath
[i
] = _T('\0');
605 /* Set the override to yes each new file */
608 /* If it couldnt open the file handle, print out the error */
609 if(hFile
== INVALID_HANDLE_VALUE
)
611 ConOutFormatMessage (GetLastError(), szSrcPath
);
617 /* Ignore the . and .. files */
618 if(!_tcscmp (findBuffer
.cFileName
, _T(".")) ||
619 !_tcscmp (findBuffer
.cFileName
, _T(".."))||
620 findBuffer
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
623 /* Copy the base folder over to a tmp string */
624 _tcscpy(tmpDestPath
,szDestPath
);
626 /* Can't put a file into a folder that isnt there */
627 if(!IsExistingDirectory(szDestPath
))
629 ConOutFormatMessage (GetLastError (), szSrcPath
);
634 /* Copy over the destination path name */
636 _tcscat (tmpDestPath
, findBuffer
.cFileName
);
639 /* If there is no wildcard you can use the name the user entered */
640 if(_tcschr (PreserveName
, _T('*')) == NULL
)
642 _tcscat (tmpDestPath
, PreserveName
);
646 /* The following lines of copy were written by someone else
647 (most likely Eric Khoul) and it was taken from ren.c */
649 TCHAR DoneFile
[MAX_PATH
];
650 /* build destination file name */
651 p
= findBuffer
.cFileName
;
659 while (*p
!= 0 && *p
!= *q
)
686 /* Add the filename to the tmp string path */
687 _tcscat (tmpDestPath
, DoneFile
);
693 /* Build the string path to the source file */
694 _tcscpy(tmpSrcPath
,szSrcPath
);
695 _tcscat (tmpSrcPath
, findBuffer
.cFileName
);
697 /* Check to see if the file is the same file */
698 if(!_tcscmp (tmpSrcPath
, tmpDestPath
))
701 /* Handle any overriding / prompting that needs to be done */
702 if((!(dwFlags
& COPY_NO_PROMPT
) && IsExistingFile (tmpDestPath
)) || dwFlags
& COPY_PROMPT
)
703 nOverwrite
= Overwrite(tmpDestPath
);
704 if(nOverwrite
== PROMPT_NO
|| nOverwrite
== PROMPT_BREAK
)
706 if(nOverwrite
== PROMPT_ALL
|| (nOverwrite
== PROMPT_YES
&& bAppend
))
707 dwFlags
|= COPY_NO_PROMPT
;
709 /* Tell weather the copy was successful or not */
710 if(copy(tmpSrcPath
,tmpDestPath
, bAppend
, dwFlags
))
713 /* only print source name when more then one file */
714 if(_tcschr (arg
[nSrc
], _T('+')) != NULL
|| _tcschr (arg
[nSrc
], _T('*')) != NULL
)
715 ConOutPrintf("%s\n",findBuffer
.cFileName
);
716 //LoadString(CMD_ModuleHandle, STRING_MOVE_ERROR1, szMsg, RC_STRING_MAX_SIZE);
720 /* print out the error message */
721 LoadString(CMD_ModuleHandle
, STRING_COPY_ERROR3
, szMsg
, RC_STRING_MAX_SIZE
);
726 /* Loop through all wildcard files */
727 }while(FindNextFile (hFile
, &findBuffer
));
728 /* Loop through all files in src string with a + */
729 }while(_tcsncmp (appendPointer
,_T("\0"),1));
731 /* print out the number of files copied */
732 LoadString(CMD_ModuleHandle
, STRING_COPY_FILE
, szMsg
, RC_STRING_MAX_SIZE
);
733 ConOutPrintf(szMsg
, nFiles
);
741 #endif /* INCLUDE_CMD_COPY */