2 * PROJECT: ReactOS Command shell
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/shell/cmd/replace.c
5 * PURPOSE: Implements 'replace' cmd command
6 * PROGRAMMERS: Samuel Erdtman (samuel@erdtman.se)
9 /* INCLUDES ******************************************************************/
13 #ifdef INCLUDE_CMD_REPLACE
15 /* GLOBALS *******************************************************************/
19 REPLACE_ADD
= 0x001, /* /A */
20 REPLACE_CONFIRM
= 0x002, /* /P */
21 REPLACE_READ_ONLY
= 0x004, /* /R */
22 REPLACE_SUBDIR
= 0x008, /* /S */
23 REPLACE_DISK
= 0x010, /* /W */
24 REPLACE_UPDATE
= 0x020, /* /U */
27 /* FUNCTIONS *****************************************************************/
29 /*just makes a print out if there is a problem with the switches*/
30 void invalid_switch(LPTSTR is
)
32 ConOutResPrintf(STRING_REPLACE_ERROR1
,is
);
33 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
36 /*retrives the pathe dependen om the input file name*/
37 void getPath(TCHAR
* out
, LPTSTR in
)
39 if(_tcslen(in
) == 2 && in
[1] == _T(':'))
40 GetRootPath(in
,out
,MAX_PATH
);
42 GetFullPathName (in
, MAX_PATH
, out
, NULL
);
47 INT
replace(TCHAR source
[MAX_PATH
], TCHAR dest
[MAX_PATH
], DWORD dwFlags
, BOOL
*doMore
)
51 HANDLE hFileSrc
, hFileDest
;
52 DWORD dwAttrib
, dwRead
, dwWritten
;
55 FILETIME srcCreationTime
, destCreationTime
, srcLastAccessTime
, destLastAccessTime
;
56 FILETIME srcLastWriteTime
, destLastWriteTime
;
57 GetPathCase(source
, s
);
59 s
[0] = _totupper(s
[0]);
60 d
[0] = _totupper(d
[0]);
61 // ConOutPrintf(_T("old-src: %s\n"), s);
62 // ConOutPrintf(_T("old-dest: %s\n"), d);
63 // ConOutPrintf(_T("src: %s\n"), source);
64 // ConOutPrintf(_T("dest: %s\n"), dest);
66 /* Open up the sourcefile */
67 hFileSrc
= CreateFile (source
, GENERIC_READ
, FILE_SHARE_READ
,NULL
, OPEN_EXISTING
, 0, NULL
);
68 if (hFileSrc
== INVALID_HANDLE_VALUE
)
70 ConOutResPrintf(STRING_COPY_ERROR1
, source
);
74 /* Get the time from source file to be used in the comparison with
75 dest time if update switch is set */
76 GetFileTime (hFileSrc
, &srcCreationTime
, &srcLastAccessTime
, &srcLastWriteTime
);
78 /* Retrieve the source attributes so that they later on can be
79 inserted in to the destination */
80 dwAttrib
= GetFileAttributes (source
);
82 if(IsExistingFile (dest
))
84 /* Resets the attributes to avoid probles with read only files,
85 checks for read only has been made earlier */
86 SetFileAttributes(dest
,FILE_ATTRIBUTE_NORMAL
);
87 /* Is the update flas set? The time has to be controled so that
88 only older files are replaced */
89 if(dwFlags
& REPLACE_UPDATE
)
91 /* Read destination time */
92 hFileDest
= CreateFile(dest
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
,
95 if (hFileSrc
== INVALID_HANDLE_VALUE
)
97 ConOutResPrintf(STRING_COPY_ERROR1
, dest
);
102 GetFileTime (hFileDest
, &destCreationTime
, &destLastAccessTime
, &destLastWriteTime
);
103 if(!((srcLastWriteTime
.dwHighDateTime
> destLastWriteTime
.dwHighDateTime
) ||
104 ( srcLastWriteTime
.dwHighDateTime
== destLastWriteTime
.dwHighDateTime
&&
105 srcLastWriteTime
.dwLowDateTime
> destLastWriteTime
.dwLowDateTime
)))
107 CloseHandle (hFileSrc
);
108 CloseHandle (hFileDest
);
111 CloseHandle (hFileDest
);
113 /* Delete the old file */
117 /* Check confirm flag, and take appropriate action */
118 if(dwFlags
& REPLACE_CONFIRM
)
120 /* Output depending on add flag */
121 if(dwFlags
& REPLACE_ADD
)
122 ConOutResPrintf(STRING_REPLACE_HELP9
, dest
);
124 ConOutResPrintf(STRING_REPLACE_HELP10
, dest
);
125 if( !FilePromptYNA (0))
129 /* Output depending on add flag */
130 if(dwFlags
& REPLACE_ADD
)
131 ConOutResPrintf(STRING_REPLACE_HELP11
, dest
);
133 ConOutResPrintf(STRING_REPLACE_HELP5
, dest
);
135 /* Make sure source and destination is not the same */
138 ConOutResPaging(TRUE
, STRING_REPLACE_ERROR7
);
139 CloseHandle (hFileSrc
);
144 /* Open destination file to write to */
145 hFileDest
= CreateFile (dest
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
146 if (hFileDest
== INVALID_HANDLE_VALUE
)
148 CloseHandle (hFileSrc
);
149 ConOutResPaging(TRUE
, STRING_REPLACE_ERROR7
);
154 /* Get buffer for the copy process */
155 buffer
= (LPBYTE
)VirtualAlloc(NULL
, BUFF_SIZE
, MEM_COMMIT
, PAGE_READWRITE
);
158 CloseHandle (hFileDest
);
159 CloseHandle (hFileSrc
);
160 ConOutResPaging(TRUE
, STRING_ERROR_OUT_OF_MEMORY
);
164 /* Put attribute and time to the new destination file */
165 SetFileAttributes (dest
, dwAttrib
);
166 SetFileTime (hFileDest
, &srcCreationTime
, &srcLastAccessTime
, &srcLastWriteTime
);
169 /* Read data from source */
170 ReadFile (hFileSrc
, buffer
, BUFF_SIZE
, &dwRead
, NULL
);
176 /* Write to destination file */
177 WriteFile (hFileDest
, buffer
, dwRead
, &dwWritten
, NULL
);
179 /* Done! or ctrl break! */
180 if (dwWritten
!= dwRead
|| CheckCtrlBreak(BREAK_INPUT
))
182 ConOutResPuts(STRING_COPY_ERROR3
);
184 CloseHandle (hFileDest
);
185 CloseHandle (hFileSrc
);
192 /* Return memory and close files */
193 VirtualFree (buffer
, 0, MEM_RELEASE
);
194 CloseHandle (hFileDest
);
195 CloseHandle (hFileSrc
);
197 /* Return one file replaced */
202 /* Function to iterate over source files and call replace for each of them */
203 INT
recReplace(DWORD dwFlags
, TCHAR szSrcPath
[MAX_PATH
], TCHAR szDestPath
[MAX_PATH
], BOOL
*doMore
)
205 TCHAR tmpDestPath
[MAX_PATH
], tmpSrcPath
[MAX_PATH
];
206 INT filesReplaced
=0, i
;
209 WIN32_FIND_DATA findBuffer
;
211 /* Get file handel to the sourcefile(s) */
212 hFile
= FindFirstFile (szSrcPath
, &findBuffer
);
214 /* Strip the paths back to the folder they are in, so that the diffrent
215 filenames can be added if more than one */
216 for(i
= (_tcslen(szSrcPath
) - 1); i
> -1; i
--)
217 if(szSrcPath
[i
] != _T('\\'))
218 szSrcPath
[i
] = _T('\0');
222 /* Go through all the soursfiles and copy/replace them */
225 if(CheckCtrlBreak(BREAK_INPUT
))
227 return filesReplaced
;
230 /* Problem with file handler */
231 if(hFile
== INVALID_HANDLE_VALUE
)
232 return filesReplaced
;
234 /* We do not want to replace any .. . ocr directory */
235 if(!_tcscmp (findBuffer
.cFileName
, _T(".")) ||
236 !_tcscmp (findBuffer
.cFileName
, _T(".."))||
237 findBuffer
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
240 /* Add filename to destpath */
241 _tcscpy(tmpDestPath
,szDestPath
);
242 _tcscat (tmpDestPath
, findBuffer
.cFileName
);
244 dwAttrib
= GetFileAttributes(tmpDestPath
);
246 if(dwFlags
& REPLACE_ADD
)
248 if(IsExistingFile(tmpDestPath
))
255 if(!IsExistingFile(tmpDestPath
))
259 /* Check if file is read only, if so check if that should be ignored */
260 if(dwAttrib
& FILE_ATTRIBUTE_READONLY
)
262 if(!(dwFlags
& REPLACE_READ_ONLY
))
264 ConOutResPrintf(STRING_REPLACE_ERROR5
, tmpDestPath
);
270 /* Add filename to sourcepath, insted of wildcards */
271 _tcscpy(tmpSrcPath
,szSrcPath
);
272 _tcscat (tmpSrcPath
, findBuffer
.cFileName
);
274 /* Make the replace */
275 if(replace(tmpSrcPath
,tmpDestPath
, dwFlags
, doMore
))
281 /* The file to be replaced was the same as the source */
286 /* Take next sourcefile if any */
287 }while(FindNextFile (hFile
, &findBuffer
));
289 return filesReplaced
;
292 /* If /s switch is specifyed all subdirs has to be considered */
293 INT
recFindSubDirs(DWORD dwFlags
,
294 TCHAR szSrcPath
[MAX_PATH
],
295 TCHAR szDestPath
[MAX_PATH
],
299 WIN32_FIND_DATA findBuffer
;
300 TCHAR tmpDestPath
[MAX_PATH
], tmpSrcPath
[MAX_PATH
];
301 INT filesReplaced
= 0, i
;
303 /* Add a wildcard to dest end so the it will be easy to itterate
304 over all the files and directorys in the dest directory */
305 _tcscat(szDestPath
, _T("*"));
307 /* Get the first file in the directory */
308 hFile
= FindFirstFile (szDestPath
, &findBuffer
);
310 /* Remove the star added earlyer to dest path */
311 for(i
= (_tcslen(szDestPath
) - 1); i
> -1; i
--)
312 if(szDestPath
[i
] != _T('\\'))
313 szDestPath
[i
] = _T('\0');
317 /* Iterate over all filed directories in the dest dir */
320 /* Save the source path so that it will not be wrecked */
321 _tcscpy(tmpSrcPath
,szSrcPath
);
322 /* Check for reading problems */
323 if(hFile
== INVALID_HANDLE_VALUE
)
325 ConOutFormatMessage (GetLastError(), tmpSrcPath
);
326 return filesReplaced
;
329 /* Check if the we should enter the dir or if it is a file
330 or . or .. if so thake the next object to process */
331 if(!_tcscmp (findBuffer
.cFileName
, _T(".")) ||
332 !_tcscmp (findBuffer
.cFileName
, _T(".."))||
333 IsExistingFile(findBuffer
.cFileName
))
335 /* Add the destpath and the new dir path to tempDestPath */
336 _tcscpy(tmpDestPath
,szDestPath
);
337 _tcscat (tmpDestPath
, findBuffer
.cFileName
);
338 /* Make sure that we have a directory */
339 if(IsExistingDirectory(tmpDestPath
))
341 /* Add a \ to the end or the path */
342 if(szDestPath
[_tcslen(tmpDestPath
) - 1] != _T('\\'))
343 _tcscat(tmpDestPath
, _T("\\"));
344 /* Call the function to replace files in the new directory */
345 filesReplaced
+= recReplace(dwFlags
, tmpSrcPath
, tmpDestPath
, doMore
);
346 /* If there were problems break e.g. read-only file */
349 _tcscpy(tmpSrcPath
,szSrcPath
);
350 /* Controle the next level of subdirs */
351 filesReplaced
+= recFindSubDirs(dwFlags
,tmpSrcPath
,tmpDestPath
, doMore
);
355 /* Get the next handle */
356 } while(FindNextFile (hFile
, &findBuffer
));
358 return filesReplaced
;
361 INT
cmd_replace (LPTSTR cmd
, LPTSTR param
)
364 INT argc
, i
,filesReplaced
= 0, nFiles
, srcIndex
= -1, destIndex
= -1;
366 TCHAR szDestPath
[MAX_PATH
], szSrcPath
[MAX_PATH
], tmpSrcPath
[MAX_PATH
];
370 if (!_tcsncmp (param
, _T("/?"), 2))
372 ConOutResPaging(TRUE
,STRING_REPLACE_HELP1
);
376 /* Divide the argument in to an array of c-strings */
377 arg
= split (param
, &argc
, FALSE
);
381 for (i
= 0; i
< argc
; i
++)
383 if (arg
[i
][0] == _T('/'))
385 if (_tcslen(arg
[i
]) == 2)
387 switch (_totupper(arg
[i
][1]))
390 dwFlags
|= REPLACE_ADD
;
393 dwFlags
|= REPLACE_CONFIRM
;
396 dwFlags
|= REPLACE_READ_ONLY
;
399 dwFlags
|= REPLACE_SUBDIR
;
402 dwFlags
|= REPLACE_DISK
;
405 dwFlags
|= REPLACE_UPDATE
;
408 invalid_switch(arg
[i
]);
414 invalid_switch(arg
[i
]);
426 else if(destIndex
== -1)
432 invalid_switch(arg
[i
]);
439 /* See so that at least source is there */
442 ConOutResPaging(TRUE
,STRING_REPLACE_HELP2
);
443 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
447 /* Check so that not both update and add switch is added and subdir */
448 if((dwFlags
& REPLACE_UPDATE
|| dwFlags
& REPLACE_SUBDIR
) && (dwFlags
& REPLACE_ADD
))
450 ConOutResPaging(TRUE
,STRING_REPLACE_ERROR4
);
451 ConOutResPaging(TRUE
,STRING_REPLACE_HELP7
);
456 /* If we have a destination get the full path */
459 if(_tcslen(arg
[destIndex
]) == 2 && arg
[destIndex
][1] == ':')
460 GetRootPath(arg
[destIndex
],szDestPath
,MAX_PATH
);
463 /* Check for wildcards in destination directory */
464 if (_tcschr (arg
[destIndex
], _T('*')) != NULL
||
465 _tcschr (arg
[destIndex
], _T('?')) != NULL
)
467 ConOutResPrintf(STRING_REPLACE_ERROR2
,arg
[destIndex
]);
468 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
472 getPath(szDestPath
, arg
[destIndex
]);
473 /* Make sure that destination exists */
474 if(!IsExistingDirectory(szDestPath
))
476 ConOutResPrintf(STRING_REPLACE_ERROR2
, szDestPath
);
477 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
485 /* Dest is current dir */
486 GetCurrentDirectory(MAX_PATH
,szDestPath
);
489 /* Get the full source path */
490 if(!(_tcslen(arg
[srcIndex
]) == 2 && arg
[srcIndex
][1] == ':'))
491 getPath(szSrcPath
, arg
[srcIndex
]);
493 _tcscpy(szSrcPath
,arg
[srcIndex
]);
495 /* Source does not have wildcards */
496 if (_tcschr (arg
[srcIndex
], _T('*')) == NULL
&&
497 _tcschr (arg
[srcIndex
], _T('?')) == NULL
)
499 /* Check so that source is not a directory, because that is not allowed */
500 if(IsExistingDirectory(szSrcPath
))
502 ConOutResPrintf(STRING_REPLACE_ERROR6
, szSrcPath
);
503 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
507 /* Check if the file exists */
508 if(!IsExistingFile(szSrcPath
))
510 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
515 /* /w switch is set so wait for any key to be pressed */
516 if(dwFlags
& REPLACE_DISK
)
522 /* Add an extra \ to the destination path if needed */
523 if(szDestPath
[_tcslen(szDestPath
) - 1] != _T('\\'))
524 _tcscat(szDestPath
, _T("\\"));
526 /* Save source path */
527 _tcscpy(tmpSrcPath
,szSrcPath
);
528 /* Replace in dest dir */
529 filesReplaced
+= recReplace(dwFlags
, tmpSrcPath
, szDestPath
, &doMore
);
530 /* If subdir switch is set replace in the subdirs to */
531 if(dwFlags
& REPLACE_SUBDIR
&& doMore
)
533 filesReplaced
+= recFindSubDirs(dwFlags
, szSrcPath
, szDestPath
, &doMore
);
536 /* If source == dest write no more */
537 if(filesReplaced
!= -1)
539 /* No files replaced */
542 /* Add switch dependent output */
543 if(dwFlags
& REPLACE_ADD
)
544 ConOutResPaging(TRUE
,STRING_REPLACE_HELP7
);
546 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
548 /* Some files replaced */
551 /* Add switch dependent output */
552 if(dwFlags
& REPLACE_ADD
)
553 ConOutResPrintf(STRING_REPLACE_HELP8
, filesReplaced
);
555 ConOutResPrintf(STRING_REPLACE_HELP4
, filesReplaced
);
562 #endif /* INCLUDE_CMD_REPLACE */