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 ();
151 buffer
= (LPBYTE
)malloc (BUFF_SIZE
);
154 CloseHandle (hFileDest
);
155 CloseHandle (hFileSrc
);
156 error_out_of_memory ();
162 ReadFile (hFileSrc
, buffer
, BUFF_SIZE
, &dwRead
, NULL
);
163 if (lpdwFlags
& COPY_ASCII
)
165 for (i
= 0; i
< dwRead
; i
++)
167 if (((LPTSTR
)buffer
)[i
] == 0x1A)
179 WriteFile (hFileDest
, buffer
, dwRead
, &dwWritten
, NULL
);
180 if (dwWritten
!= dwRead
)
182 ConOutResPuts(STRING_COPY_ERROR3
);
185 CloseHandle (hFileDest
);
186 CloseHandle (hFileSrc
);
191 while (dwRead
&& !bEof
);
194 DebugPrintf (_T("setting time\n"));
196 SetFileTime (hFileDest
, &srctime
, NULL
, NULL
);
198 if (lpdwFlags
& COPY_ASCII
)
200 ((LPTSTR
)buffer
)[0] = 0x1A;
201 ((LPTSTR
)buffer
)[1] = _T('\0');
203 DebugPrintf (_T("appending ^Z\n"));
205 WriteFile (hFileDest
, buffer
, sizeof(TCHAR
), &dwWritten
, NULL
);
209 CloseHandle (hFileDest
);
210 CloseHandle (hFileSrc
);
213 DebugPrintf (_T("setting mode\n"));
215 SetFileAttributes (dest
, dwAttrib
);
221 static INT
Overwrite (LPTSTR fn
)
223 /*ask the user if they want to override*/
224 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
226 LoadString(CMD_ModuleHandle
, STRING_COPY_HELP1
, szMsg
, RC_STRING_MAX_SIZE
);
227 ConOutPrintf(szMsg
,fn
);
228 res
= FilePromptYNA ("");
233 INT
cmd_copy (LPTSTR cmd
, LPTSTR param
)
235 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
237 INT argc
, i
, nFiles
, nOverwrite
= 0, nSrc
= -1, nDes
= -1;
238 /* this is the path up to the folder of the src and dest ie C:\windows\ */
239 TCHAR szDestPath
[MAX_PATH
];
240 TCHAR szSrcPath
[MAX_PATH
];
242 /* If this is the type of copy where we are adding files */
243 BOOL bAppend
= FALSE
;
244 WIN32_FIND_DATA findBuffer
;
246 /* Used when something like "copy c*.exe d*.exe" during the process of
247 figuring out the new name */
248 TCHAR tmpName
[MAX_PATH
] = _T("");
249 /* Pointer to keep track of how far through the append input(file1+file2+file3) we are */
250 TCHAR
* appendPointer
= _T("\0");
251 /* The full path to src and dest. This has drive letter, folders, and filename */
252 TCHAR tmpDestPath
[MAX_PATH
];
253 TCHAR tmpSrcPath
[MAX_PATH
];
254 /* A bool on weather or not the destination name will be taking from the input */
255 BOOL bSrcName
= FALSE
;
256 /* Seems like a waste but it is a pointer used to copy from input to PreserveName */
258 /* Stores the name( i.e. blah.txt or blah*.txt) which later we might need */
259 TCHAR PreserveName
[MAX_PATH
];
260 /* for CMDCOPY env */
265 /*Show help/usage info*/
266 if (!_tcsncmp (param
, _T("/?"), 2))
268 ConOutResPaging(TRUE
, STRING_COPY_HELP2
);
274 /* Get the envor value if it exists */
276 size
= GetEnvironmentVariable (_T("COPYCMD"), evar
, 512);
279 evar
= realloc(evar
,size
* sizeof(TCHAR
) );
282 size
= GetEnvironmentVariable (_T("COPYCMD"), evar
, size
);
285 /* check see if we did get any env variable */
289 /* scan and set the flags */
292 if (_tcsncicmp(_T("/A"),&evar
[t
],2)==0)
298 else if (_tcsncicmp(_T("/B"),&evar
[t
],2)==0)
300 dwFlags
|= COPY_BINARY
;
304 else if (_tcsncicmp(_T("/D"),&evar
[t
],2)==0)
306 dwFlags
|= COPY_DECRYPT
;
311 else if (_tcsncicmp(_T("/V"),&evar
[t
],2)==0)
313 dwFlags
|= COPY_VERIFY
;
318 else if (_tcsncicmp(_T("/V"),&evar
[t
],2)==0)
320 dwFlags
|= COPY_SHORTNAME
;
325 else if (_tcsncicmp(_T("/Y"),&evar
[t
],2)==0)
327 dwFlags
|= COPY_NO_PROMPT
;
332 else if (_tcsncicmp(_T("/-Y"),&evar
[t
],3)==0)
334 dwFlags
|= COPY_PROMPT
;
340 else if (_tcsncicmp(_T("/Z"),&evar
[t
],2)==0)
342 dwFlags
|= COPY_PROMPT
;
351 /*Split the user input into array*/
352 arg
= split (param
, &argc
, FALSE
);
356 /*Read switches and count files*/
357 for (i
= 0; i
< argc
; i
++)
359 if (*arg
[i
] == _T('/'))
361 if (_tcslen(arg
[i
]) >= 2)
363 switch (_totupper(arg
[i
][1]))
367 dwFlags
|= COPY_ASCII
;
371 dwFlags
|= COPY_BINARY
;
375 dwFlags
|= COPY_DECRYPT
;
379 dwFlags
|= COPY_VERIFY
;
383 dwFlags
|= COPY_SHORTNAME
;
387 dwFlags
|= COPY_NO_PROMPT
;
388 dwFlags
&= ~COPY_PROMPT
;
392 if(_tcslen(arg
[i
]) >= 3)
393 if(_totupper(arg
[i
][2]) == _T('Y'))
395 dwFlags
&= ~COPY_NO_PROMPT
;
396 dwFlags
|= COPY_PROMPT
;
402 dwFlags
|= COPY_RESTART
;
407 error_invalid_switch(_totupper(arg
[i
][1]));
412 /*If it was a switch, subtract from total arguments*/
417 /*if it isnt a switch then it is the source or destination*/
428 /* There is not enough files, there has to be at least 1 */
429 error_req_param_missing();
435 /* there is too many file names in command */
436 error_too_many_parameters("");
441 ((_tcschr (arg
[nSrc
], _T('+')) != NULL
) ||
442 (_tcschr (arg
[nSrc
], _T('*')) != NULL
&& _tcschr (arg
[nDes
], _T('*')) == NULL
) ||
443 (IsExistingDirectory (arg
[nSrc
]) && (_tcschr (arg
[nDes
], _T('*')) == NULL
&& !IsExistingDirectory (arg
[nDes
])))
446 /* There is a + in the source filename, this means
447 that there is more then one file being put into
450 if(_tcschr (arg
[nSrc
], _T('+')) != NULL
)
451 appendPointer
= arg
[nSrc
];
454 /* Reusing the number of files variable */
460 /* Set up the string that is the path to the destination */
463 /* Check to make sure if they entered c:, if they do then GFPN
464 return current directory even though msdn says it will return c:\ */
465 if(_tcslen(arg
[nDes
]) == 2)
467 if(arg
[nDes
][1] == _T(':'))
469 _tcscpy (szDestPath
, arg
[nDes
]);
470 _tcscat (szDestPath
, _T("\\"));
474 /* If the user entered two file names then form the full string path*/
475 GetFullPathName (arg
[nDes
], MAX_PATH
, szDestPath
, NULL
);
480 /* If no destination was entered then just use
481 the current directory as the destination */
482 GetCurrentDirectory (MAX_PATH
, szDestPath
);
486 /* Get the full string of the path to the source file*/
487 if(_tcschr (arg
[nSrc
], _T('+')) != NULL
)
490 _tcscpy(tmpName
,_T("\0"));
491 /* Loop through the source file name and copy all
492 the chars one at a time until it gets too + */
496 if(!_tcsncmp (appendPointer
,_T("+"),1) || !_tcsncmp (appendPointer
,_T("\0"),1))
498 /* Now that the pointer is on the + we
499 need to go to the start of the next filename */
500 if(!_tcsncmp (appendPointer
,_T("+"),1))
504 _tcsncat(tmpName
,appendPointer
,1);
507 /* Finish the string off with a null char */
508 _tcsncat(tmpName
,_T("\0"),1);
509 /* Check to make sure if they entered c:, if they do then GFPN
510 return current directory even though msdn says it will return c:\ */
511 if(_tcslen(tmpName
) == 2)
513 if(tmpName
[1] == _T(':'))
515 _tcscpy (szSrcPath
, tmpName
);
516 _tcscat (szSrcPath
, _T("\\"));
520 /* Get the full path to first file in the string of file names */
521 GetFullPathName (tmpName
, MAX_PATH
, szSrcPath
, NULL
);
525 /* Check to make sure if they entered c:, if they do then GFPN
526 return current directory even though msdn says it will return c:\ */
527 if(_tcslen(arg
[nSrc
]) == 2)
529 if(arg
[nSrc
][1] == _T(':'))
531 _tcscpy (szSrcPath
, arg
[nSrc
]);
532 _tcscat (szSrcPath
, _T("\\"));
536 /* Get the full path of the source file */
537 GetFullPathName (arg
[nSrc
], MAX_PATH
, szSrcPath
, NULL
);
541 /* From this point on, we can assume that the shortest path is 3 letters long
542 and that would be [DriveLetter]:\ */
544 /* If there is no * in the path name and it is a folder
545 then we will need to add a wildcard to the pathname
546 so FindFirstFile comes up with all the files in that
548 if(_tcschr (szSrcPath
, _T('*')) == NULL
&&
549 IsExistingDirectory (szSrcPath
))
551 /* If it doesnt have a \ at the end already then on needs to be added */
552 if(szSrcPath
[_tcslen(szSrcPath
) - 1] != _T('\\'))
553 _tcscat (szSrcPath
, _T("\\"));
554 /* Add a wildcard after the \ */
555 _tcscat (szSrcPath
, _T("*"));
557 /* Make sure there is an ending slash to the path if the dest is a folder */
558 if(_tcschr (szDestPath
, _T('*')) == NULL
&&
559 IsExistingDirectory(szDestPath
))
561 if(szDestPath
[_tcslen(szDestPath
) - 1] != _T('\\'))
562 _tcscat (szDestPath
, _T("\\"));
566 /* Get a list of all the files */
567 hFile
= FindFirstFile (szSrcPath
, &findBuffer
);
570 /* We need to figure out what the name of the file in the is going to be */
571 if((szDestPath
[_tcslen(szDestPath
) - 1] == _T('*') && szDestPath
[_tcslen(szDestPath
) - 2] == _T('\\')) ||
572 szDestPath
[_tcslen(szDestPath
) - 1] == _T('\\'))
574 /* In this case we will be using the same name as the source file
575 for the destination file because destination is a folder */
580 /* Save the name the user entered */
581 UseThisName
= _tcsrchr(szDestPath
,_T('\\'));
583 _tcscpy(PreserveName
,UseThisName
);
586 /* Strip the paths back to the folder they are in */
587 for(i
= (_tcslen(szSrcPath
) - 1); i
> -1; i
--)
588 if(szSrcPath
[i
] != _T('\\'))
589 szSrcPath
[i
] = _T('\0');
593 for(i
= (_tcslen(szDestPath
) - 1); i
> -1; i
--)
594 if(szDestPath
[i
] != _T('\\'))
595 szDestPath
[i
] = _T('\0');
602 /* Set the override to yes each new file */
605 /* If it couldnt open the file handle, print out the error */
606 if(hFile
== INVALID_HANDLE_VALUE
)
608 ConOutFormatMessage (GetLastError(), szSrcPath
);
613 /* Ignore the . and .. files */
614 if(!_tcscmp (findBuffer
.cFileName
, _T(".")) ||
615 !_tcscmp (findBuffer
.cFileName
, _T(".."))||
616 findBuffer
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
619 /* Copy the base folder over to a tmp string */
620 _tcscpy(tmpDestPath
,szDestPath
);
622 /* Can't put a file into a folder that isnt there */
623 if(!IsExistingDirectory(szDestPath
))
625 ConOutFormatMessage (GetLastError (), szSrcPath
);
629 /* Copy over the destination path name */
631 _tcscat (tmpDestPath
, findBuffer
.cFileName
);
634 /* If there is no wildcard you can use the name the user entered */
635 if(_tcschr (PreserveName
, _T('*')) == NULL
)
637 _tcscat (tmpDestPath
, PreserveName
);
641 /* The following lines of copy were written by someone else
642 (most likely Eric Khoul) and it was taken from ren.c */
644 TCHAR DoneFile
[MAX_PATH
];
645 /* build destination file name */
646 p
= findBuffer
.cFileName
;
654 while (*p
!= 0 && *p
!= *q
)
681 /* Add the filename to the tmp string path */
682 _tcscat (tmpDestPath
, DoneFile
);
688 /* Build the string path to the source file */
689 _tcscpy(tmpSrcPath
,szSrcPath
);
690 _tcscat (tmpSrcPath
, findBuffer
.cFileName
);
692 /* Check to see if the file is the same file */
693 if(!_tcscmp (tmpSrcPath
, tmpDestPath
))
696 /* Handle any overriding / prompting that needs to be done */
697 if((!(dwFlags
& COPY_NO_PROMPT
) && IsExistingFile (tmpDestPath
)) || dwFlags
& COPY_PROMPT
)
698 nOverwrite
= Overwrite(tmpDestPath
);
699 if(nOverwrite
== PROMPT_NO
|| nOverwrite
== PROMPT_BREAK
)
701 if(nOverwrite
== PROMPT_ALL
|| (nOverwrite
== PROMPT_YES
&& bAppend
))
702 dwFlags
|= COPY_NO_PROMPT
;
704 /* Tell weather the copy was successful or not */
705 if(copy(tmpSrcPath
,tmpDestPath
, bAppend
, dwFlags
))
708 /* only print source name when more then one file */
709 if(_tcschr (arg
[nSrc
], _T('+')) != NULL
|| _tcschr (arg
[nSrc
], _T('*')) != NULL
)
710 ConOutPrintf("%s\n",findBuffer
.cFileName
);
711 //LoadString(CMD_ModuleHandle, STRING_MOVE_ERROR1, szMsg, RC_STRING_MAX_SIZE);
715 /* print out the error message */
716 LoadString(CMD_ModuleHandle
, STRING_COPY_ERROR3
, szMsg
, RC_STRING_MAX_SIZE
);
721 /* Loop through all wildcard files */
722 }while(FindNextFile (hFile
, &findBuffer
));
723 /* Loop through all files in src string with a + */
724 }while(_tcsncmp (appendPointer
,_T("\0"),1));
726 /* print out the number of files copied */
727 LoadString(CMD_ModuleHandle
, STRING_COPY_FILE
, szMsg
, RC_STRING_MAX_SIZE
);
728 ConOutPrintf(szMsg
, nFiles
);
736 #endif /* INCLUDE_CMD_COPY */