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 /* retrieves the path dependent on 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
);
46 /* makes the replace */
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
);
75 * Get the time from source file to be used in the comparison
76 * with dest time if update switch is set.
78 GetFileTime (hFileSrc
, &srcCreationTime
, &srcLastAccessTime
, &srcLastWriteTime
);
81 * Retrieve the source attributes so that they later on
82 * can be inserted in to the destination.
84 dwAttrib
= GetFileAttributes (source
);
86 if (IsExistingFile (dest
))
89 * Resets the attributes to avoid problems with read only files,
90 * checks for read only has been made earlier.
92 SetFileAttributes(dest
,FILE_ATTRIBUTE_NORMAL
);
94 * Is the update flas set? The time has to be controled so that
95 * only older files are replaced.
97 if (dwFlags
& REPLACE_UPDATE
)
99 /* Read destination time */
100 hFileDest
= CreateFile(dest
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
,
103 if (hFileDest
== INVALID_HANDLE_VALUE
)
105 ConOutResPrintf(STRING_COPY_ERROR1
, dest
);
106 CloseHandle (hFileSrc
);
111 GetFileTime (hFileDest
, &destCreationTime
, &destLastAccessTime
, &destLastWriteTime
);
112 if (!((srcLastWriteTime
.dwHighDateTime
> destLastWriteTime
.dwHighDateTime
) ||
113 (srcLastWriteTime
.dwHighDateTime
== destLastWriteTime
.dwHighDateTime
&&
114 srcLastWriteTime
.dwLowDateTime
> destLastWriteTime
.dwLowDateTime
)))
116 CloseHandle (hFileSrc
);
117 CloseHandle (hFileDest
);
120 CloseHandle (hFileDest
);
122 /* Delete the old file */
126 /* Check confirm flag, and take appropriate action */
127 if (dwFlags
& REPLACE_CONFIRM
)
129 /* Output depending on add flag */
130 if (dwFlags
& REPLACE_ADD
)
131 ConOutResPrintf(STRING_REPLACE_HELP9
, dest
);
133 ConOutResPrintf(STRING_REPLACE_HELP10
, dest
);
134 if ( !FilePromptYNA (0))
136 CloseHandle (hFileSrc
);
141 /* Output depending on add flag */
142 if (dwFlags
& REPLACE_ADD
)
143 ConOutResPrintf(STRING_REPLACE_HELP11
, dest
);
145 ConOutResPrintf(STRING_REPLACE_HELP5
, dest
);
147 /* Make sure source and destination is not the same */
150 ConOutResPaging(TRUE
, STRING_REPLACE_ERROR7
);
151 CloseHandle (hFileSrc
);
156 /* Open destination file to write to */
157 hFileDest
= CreateFile (dest
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
158 if (hFileDest
== INVALID_HANDLE_VALUE
)
160 CloseHandle (hFileSrc
);
161 ConOutResPaging(TRUE
, STRING_REPLACE_ERROR7
);
166 /* Get buffer for the copy process */
167 buffer
= VirtualAlloc(NULL
, BUFF_SIZE
, MEM_COMMIT
, PAGE_READWRITE
);
170 CloseHandle (hFileDest
);
171 CloseHandle (hFileSrc
);
172 ConOutResPaging(TRUE
, STRING_ERROR_OUT_OF_MEMORY
);
176 /* Put attribute and time to the new destination file */
177 SetFileAttributes (dest
, dwAttrib
);
178 SetFileTime (hFileDest
, &srcCreationTime
, &srcLastAccessTime
, &srcLastWriteTime
);
181 /* Read data from source */
182 ReadFile (hFileSrc
, buffer
, BUFF_SIZE
, &dwRead
, NULL
);
188 /* Write to destination file */
189 WriteFile (hFileDest
, buffer
, dwRead
, &dwWritten
, NULL
);
191 /* Done! or ctrl break! */
192 if (dwWritten
!= dwRead
|| CheckCtrlBreak(BREAK_INPUT
))
194 ConOutResPuts(STRING_COPY_ERROR3
);
195 VirtualFree (buffer
, 0, MEM_RELEASE
);
196 CloseHandle (hFileDest
);
197 CloseHandle (hFileSrc
);
204 /* Return memory and close files */
205 VirtualFree (buffer
, 0, MEM_RELEASE
);
206 CloseHandle (hFileDest
);
207 CloseHandle (hFileSrc
);
209 /* Return one file replaced */
214 /* Function to iterate over source files and call replace for each of them */
215 INT
recReplace(DWORD dwFlags
,
216 TCHAR szSrcPath
[MAX_PATH
],
217 TCHAR szDestPath
[MAX_PATH
],
220 TCHAR tmpDestPath
[MAX_PATH
], tmpSrcPath
[MAX_PATH
];
225 WIN32_FIND_DATA findBuffer
;
227 /* Get file handle to the sourcefile(s) */
228 hFile
= FindFirstFile (szSrcPath
, &findBuffer
);
231 * Strip the paths back to the folder they are in, so that
232 * the different filenames can be added if more than one.
234 for(i
= (_tcslen(szSrcPath
) - 1); i
> -1; i
--)
236 if (szSrcPath
[i
] != _T('\\'))
237 szSrcPath
[i
] = _T('\0');
242 /* Go through all the sourcefiles and copy/replace them */
245 if (CheckCtrlBreak(BREAK_INPUT
))
247 return filesReplaced
;
250 /* Problem with file handler */
251 if (hFile
== INVALID_HANDLE_VALUE
)
252 return filesReplaced
;
254 /* We do not want to replace any .. . ocr directory */
255 if (!_tcscmp (findBuffer
.cFileName
, _T(".")) ||
256 !_tcscmp (findBuffer
.cFileName
, _T(".."))||
257 findBuffer
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
260 /* Add filename to destpath */
261 _tcscpy(tmpDestPath
,szDestPath
);
262 _tcscat (tmpDestPath
, findBuffer
.cFileName
);
264 dwAttrib
= GetFileAttributes(tmpDestPath
);
266 if (dwFlags
& REPLACE_ADD
)
268 if (IsExistingFile(tmpDestPath
))
275 if (!IsExistingFile(tmpDestPath
))
279 /* Check if file is read only, if so check if that should be ignored */
280 if (dwAttrib
& FILE_ATTRIBUTE_READONLY
)
282 if (!(dwFlags
& REPLACE_READ_ONLY
))
284 ConOutResPrintf(STRING_REPLACE_ERROR5
, tmpDestPath
);
290 /* Add filename to sourcepath, insted of wildcards */
291 _tcscpy(tmpSrcPath
,szSrcPath
);
292 _tcscat (tmpSrcPath
, findBuffer
.cFileName
);
294 /* Make the replace */
295 if (replace(tmpSrcPath
,tmpDestPath
, dwFlags
, doMore
))
301 /* The file to be replaced was the same as the source */
306 /* Take next sourcefile if any */
307 } while(FindNextFile (hFile
, &findBuffer
));
311 return filesReplaced
;
314 /* If /s switch is specifyed all subdirs has to be considered */
315 INT
recFindSubDirs(DWORD dwFlags
,
316 TCHAR szSrcPath
[MAX_PATH
],
317 TCHAR szDestPath
[MAX_PATH
],
321 WIN32_FIND_DATA findBuffer
;
322 TCHAR tmpDestPath
[MAX_PATH
], tmpSrcPath
[MAX_PATH
];
323 INT filesReplaced
= 0;
327 * Add a wildcard to dest end so the it will be easy to iterate
328 * over all the files and directorys in the dest directory.
330 _tcscat(szDestPath
, _T("*"));
332 /* Get the first file in the directory */
333 hFile
= FindFirstFile (szDestPath
, &findBuffer
);
335 /* Remove the star added earlier to dest path */
336 for(i
= (_tcslen(szDestPath
) - 1); i
> -1; i
--)
338 if (szDestPath
[i
] != _T('\\'))
339 szDestPath
[i
] = _T('\0');
344 /* Iterate over all filed directories in the dest dir */
347 /* Save the source path so that it will not be wrecked */
348 _tcscpy(tmpSrcPath
,szSrcPath
);
349 /* Check for reading problems */
350 if (hFile
== INVALID_HANDLE_VALUE
)
352 ConOutFormatMessage (GetLastError(), tmpSrcPath
);
353 return filesReplaced
;
357 * Check if the we should enter the dir or if it is a file
358 * or . or .. if so thake the next object to process.
360 if (!_tcscmp (findBuffer
.cFileName
, _T(".")) ||
361 !_tcscmp (findBuffer
.cFileName
, _T(".."))||
362 IsExistingFile(findBuffer
.cFileName
))
364 /* Add the destpath and the new dir path to tempDestPath */
365 _tcscpy(tmpDestPath
,szDestPath
);
366 _tcscat (tmpDestPath
, findBuffer
.cFileName
);
367 /* Make sure that we have a directory */
368 if (IsExistingDirectory(tmpDestPath
))
370 /* Add a \ to the end or the path */
371 if (szDestPath
[_tcslen(tmpDestPath
) - 1] != _T('\\'))
372 _tcscat(tmpDestPath
, _T("\\"));
373 /* Call the function to replace files in the new directory */
374 filesReplaced
+= recReplace(dwFlags
, tmpSrcPath
, tmpDestPath
, doMore
);
375 /* If there were problems break e.g. read-only file */
378 _tcscpy(tmpSrcPath
,szSrcPath
);
379 /* Control the next level of subdirs */
380 filesReplaced
+= recFindSubDirs(dwFlags
,tmpSrcPath
,tmpDestPath
, doMore
);
384 /* Get the next handle */
385 } while(FindNextFile (hFile
, &findBuffer
));
389 return filesReplaced
;
392 INT
cmd_replace (LPTSTR param
)
395 INT argc
, i
,filesReplaced
= 0, nFiles
, srcIndex
= -1, destIndex
= -1;
397 TCHAR szDestPath
[MAX_PATH
], szSrcPath
[MAX_PATH
], tmpSrcPath
[MAX_PATH
];
401 if (!_tcsncmp (param
, _T("/?"), 2))
403 ConOutResPaging(TRUE
,STRING_REPLACE_HELP1
);
407 /* Divide the argument in to an array of c-strings */
408 arg
= split (param
, &argc
, FALSE
, FALSE
);
412 for (i
= 0; i
< argc
; i
++)
414 if (arg
[i
][0] == _T('/'))
416 if (_tcslen(arg
[i
]) == 2)
418 switch (_totupper(arg
[i
][1]))
421 dwFlags
|= REPLACE_ADD
;
424 dwFlags
|= REPLACE_CONFIRM
;
427 dwFlags
|= REPLACE_READ_ONLY
;
430 dwFlags
|= REPLACE_SUBDIR
;
433 dwFlags
|= REPLACE_DISK
;
436 dwFlags
|= REPLACE_UPDATE
;
439 invalid_switch(arg
[i
]);
445 invalid_switch(arg
[i
]);
457 else if (destIndex
== -1)
463 invalid_switch(arg
[i
]);
470 /* See so that at least source is there */
473 ConOutResPaging(TRUE
,STRING_REPLACE_HELP2
);
474 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
478 /* Check so that not both update and add switch is added and subdir */
479 if ((dwFlags
& REPLACE_UPDATE
|| dwFlags
& REPLACE_SUBDIR
) && (dwFlags
& REPLACE_ADD
))
481 ConOutResPaging(TRUE
,STRING_REPLACE_ERROR4
);
482 ConOutResPaging(TRUE
,STRING_REPLACE_HELP7
);
487 /* If we have a destination get the full path */
490 if (_tcslen(arg
[destIndex
]) == 2 && arg
[destIndex
][1] == ':')
491 GetRootPath(arg
[destIndex
],szDestPath
,MAX_PATH
);
494 /* Check for wildcards in destination directory */
495 if (_tcschr (arg
[destIndex
], _T('*')) != NULL
||
496 _tcschr (arg
[destIndex
], _T('?')) != NULL
)
498 ConOutResPrintf(STRING_REPLACE_ERROR2
,arg
[destIndex
]);
499 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
503 getPath(szDestPath
, arg
[destIndex
]);
504 /* Make sure that destination exists */
505 if (!IsExistingDirectory(szDestPath
))
507 ConOutResPrintf(STRING_REPLACE_ERROR2
, szDestPath
);
508 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
516 /* Dest is current dir */
517 GetCurrentDirectory(MAX_PATH
,szDestPath
);
520 /* Get the full source path */
521 if (!(_tcslen(arg
[srcIndex
]) == 2 && arg
[srcIndex
][1] == ':'))
522 getPath(szSrcPath
, arg
[srcIndex
]);
524 _tcscpy(szSrcPath
,arg
[srcIndex
]);
526 /* Source does not have wildcards */
527 if (_tcschr (arg
[srcIndex
], _T('*')) == NULL
&&
528 _tcschr (arg
[srcIndex
], _T('?')) == NULL
)
530 /* Check so that source is not a directory, because that is not allowed */
531 if (IsExistingDirectory(szSrcPath
))
533 ConOutResPrintf(STRING_REPLACE_ERROR6
, szSrcPath
);
534 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
538 /* Check if the file exists */
539 if (!IsExistingFile(szSrcPath
))
541 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
546 /* /w switch is set so wait for any key to be pressed */
547 if (dwFlags
& REPLACE_DISK
)
553 /* Add an extra \ to the destination path if needed */
554 if (szDestPath
[_tcslen(szDestPath
) - 1] != _T('\\'))
555 _tcscat(szDestPath
, _T("\\"));
557 /* Save source path */
558 _tcscpy(tmpSrcPath
,szSrcPath
);
559 /* Replace in dest dir */
560 filesReplaced
+= recReplace(dwFlags
, tmpSrcPath
, szDestPath
, &doMore
);
561 /* If subdir switch is set replace in the subdirs to */
562 if (dwFlags
& REPLACE_SUBDIR
&& doMore
)
564 filesReplaced
+= recFindSubDirs(dwFlags
, szSrcPath
, szDestPath
, &doMore
);
567 /* If source == dest write no more */
568 if (filesReplaced
!= -1)
570 /* No files replaced */
571 if (filesReplaced
==0)
573 /* Add switch dependent output */
574 if (dwFlags
& REPLACE_ADD
)
575 ConOutResPaging(TRUE
,STRING_REPLACE_HELP7
);
577 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
579 /* Some files replaced */
582 /* Add switch dependent output */
583 if (dwFlags
& REPLACE_ADD
)
584 ConOutResPrintf(STRING_REPLACE_HELP8
, filesReplaced
);
586 ConOutResPrintf(STRING_REPLACE_HELP4
, filesReplaced
);
593 #endif /* INCLUDE_CMD_REPLACE */