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
);
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 probles 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 (hFileSrc
== INVALID_HANDLE_VALUE
)
105 ConOutResPrintf(STRING_COPY_ERROR1
, dest
);
110 GetFileTime (hFileDest
, &destCreationTime
, &destLastAccessTime
, &destLastWriteTime
);
111 if (!((srcLastWriteTime
.dwHighDateTime
> destLastWriteTime
.dwHighDateTime
) ||
112 (srcLastWriteTime
.dwHighDateTime
== destLastWriteTime
.dwHighDateTime
&&
113 srcLastWriteTime
.dwLowDateTime
> destLastWriteTime
.dwLowDateTime
)))
115 CloseHandle (hFileSrc
);
116 CloseHandle (hFileDest
);
119 CloseHandle (hFileDest
);
121 /* Delete the old file */
125 /* Check confirm flag, and take appropriate action */
126 if (dwFlags
& REPLACE_CONFIRM
)
128 /* Output depending on add flag */
129 if (dwFlags
& REPLACE_ADD
)
130 ConOutResPrintf(STRING_REPLACE_HELP9
, dest
);
132 ConOutResPrintf(STRING_REPLACE_HELP10
, dest
);
133 if ( !FilePromptYNA (0))
137 /* Output depending on add flag */
138 if (dwFlags
& REPLACE_ADD
)
139 ConOutResPrintf(STRING_REPLACE_HELP11
, dest
);
141 ConOutResPrintf(STRING_REPLACE_HELP5
, dest
);
143 /* Make sure source and destination is not the same */
146 ConOutResPaging(TRUE
, STRING_REPLACE_ERROR7
);
147 CloseHandle (hFileSrc
);
152 /* Open destination file to write to */
153 hFileDest
= CreateFile (dest
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
154 if (hFileDest
== INVALID_HANDLE_VALUE
)
156 CloseHandle (hFileSrc
);
157 ConOutResPaging(TRUE
, STRING_REPLACE_ERROR7
);
162 /* Get buffer for the copy process */
163 buffer
= VirtualAlloc(NULL
, BUFF_SIZE
, MEM_COMMIT
, PAGE_READWRITE
);
166 CloseHandle (hFileDest
);
167 CloseHandle (hFileSrc
);
168 ConOutResPaging(TRUE
, STRING_ERROR_OUT_OF_MEMORY
);
172 /* Put attribute and time to the new destination file */
173 SetFileAttributes (dest
, dwAttrib
);
174 SetFileTime (hFileDest
, &srcCreationTime
, &srcLastAccessTime
, &srcLastWriteTime
);
177 /* Read data from source */
178 ReadFile (hFileSrc
, buffer
, BUFF_SIZE
, &dwRead
, NULL
);
184 /* Write to destination file */
185 WriteFile (hFileDest
, buffer
, dwRead
, &dwWritten
, NULL
);
187 /* Done! or ctrl break! */
188 if (dwWritten
!= dwRead
|| CheckCtrlBreak(BREAK_INPUT
))
190 ConOutResPuts(STRING_COPY_ERROR3
);
191 VirtualFree (buffer
, 0, MEM_RELEASE
);
192 CloseHandle (hFileDest
);
193 CloseHandle (hFileSrc
);
200 /* Return memory and close files */
201 VirtualFree (buffer
, 0, MEM_RELEASE
);
202 CloseHandle (hFileDest
);
203 CloseHandle (hFileSrc
);
205 /* Return one file replaced */
210 /* Function to iterate over source files and call replace for each of them */
211 INT
recReplace(DWORD dwFlags
,
212 TCHAR szSrcPath
[MAX_PATH
],
213 TCHAR szDestPath
[MAX_PATH
],
216 TCHAR tmpDestPath
[MAX_PATH
], tmpSrcPath
[MAX_PATH
];
221 WIN32_FIND_DATA findBuffer
;
223 /* Get file handel to the sourcefile(s) */
224 hFile
= FindFirstFile (szSrcPath
, &findBuffer
);
227 * Strip the paths back to the folder they are in, so that
228 * the different filenames can be added if more than one.
230 for(i
= (_tcslen(szSrcPath
) - 1); i
> -1; i
--)
232 if (szSrcPath
[i
] != _T('\\'))
233 szSrcPath
[i
] = _T('\0');
238 /* Go through all the soursfiles and copy/replace them */
241 if (CheckCtrlBreak(BREAK_INPUT
))
243 return filesReplaced
;
246 /* Problem with file handler */
247 if (hFile
== INVALID_HANDLE_VALUE
)
248 return filesReplaced
;
250 /* We do not want to replace any .. . ocr directory */
251 if (!_tcscmp (findBuffer
.cFileName
, _T(".")) ||
252 !_tcscmp (findBuffer
.cFileName
, _T(".."))||
253 findBuffer
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
256 /* Add filename to destpath */
257 _tcscpy(tmpDestPath
,szDestPath
);
258 _tcscat (tmpDestPath
, findBuffer
.cFileName
);
260 dwAttrib
= GetFileAttributes(tmpDestPath
);
262 if (dwFlags
& REPLACE_ADD
)
264 if (IsExistingFile(tmpDestPath
))
271 if (!IsExistingFile(tmpDestPath
))
275 /* Check if file is read only, if so check if that should be ignored */
276 if (dwAttrib
& FILE_ATTRIBUTE_READONLY
)
278 if (!(dwFlags
& REPLACE_READ_ONLY
))
280 ConOutResPrintf(STRING_REPLACE_ERROR5
, tmpDestPath
);
286 /* Add filename to sourcepath, insted of wildcards */
287 _tcscpy(tmpSrcPath
,szSrcPath
);
288 _tcscat (tmpSrcPath
, findBuffer
.cFileName
);
290 /* Make the replace */
291 if (replace(tmpSrcPath
,tmpDestPath
, dwFlags
, doMore
))
297 /* The file to be replaced was the same as the source */
302 /* Take next sourcefile if any */
303 } while(FindNextFile (hFile
, &findBuffer
));
305 return filesReplaced
;
308 /* If /s switch is specifyed all subdirs has to be considered */
309 INT
recFindSubDirs(DWORD dwFlags
,
310 TCHAR szSrcPath
[MAX_PATH
],
311 TCHAR szDestPath
[MAX_PATH
],
315 WIN32_FIND_DATA findBuffer
;
316 TCHAR tmpDestPath
[MAX_PATH
], tmpSrcPath
[MAX_PATH
];
317 INT filesReplaced
= 0;
321 * Add a wildcard to dest end so the it will be easy to iterate
322 * over all the files and directorys in the dest directory.
324 _tcscat(szDestPath
, _T("*"));
326 /* Get the first file in the directory */
327 hFile
= FindFirstFile (szDestPath
, &findBuffer
);
329 /* Remove the star added earlyer to dest path */
330 for(i
= (_tcslen(szDestPath
) - 1); i
> -1; i
--)
332 if (szDestPath
[i
] != _T('\\'))
333 szDestPath
[i
] = _T('\0');
338 /* Iterate over all filed directories in the dest dir */
341 /* Save the source path so that it will not be wrecked */
342 _tcscpy(tmpSrcPath
,szSrcPath
);
343 /* Check for reading problems */
344 if (hFile
== INVALID_HANDLE_VALUE
)
346 ConOutFormatMessage (GetLastError(), tmpSrcPath
);
347 return filesReplaced
;
351 * Check if the we should enter the dir or if it is a file
352 * or . or .. if so thake the next object to process.
354 if (!_tcscmp (findBuffer
.cFileName
, _T(".")) ||
355 !_tcscmp (findBuffer
.cFileName
, _T(".."))||
356 IsExistingFile(findBuffer
.cFileName
))
358 /* Add the destpath and the new dir path to tempDestPath */
359 _tcscpy(tmpDestPath
,szDestPath
);
360 _tcscat (tmpDestPath
, findBuffer
.cFileName
);
361 /* Make sure that we have a directory */
362 if (IsExistingDirectory(tmpDestPath
))
364 /* Add a \ to the end or the path */
365 if (szDestPath
[_tcslen(tmpDestPath
) - 1] != _T('\\'))
366 _tcscat(tmpDestPath
, _T("\\"));
367 /* Call the function to replace files in the new directory */
368 filesReplaced
+= recReplace(dwFlags
, tmpSrcPath
, tmpDestPath
, doMore
);
369 /* If there were problems break e.g. read-only file */
372 _tcscpy(tmpSrcPath
,szSrcPath
);
373 /* Controle the next level of subdirs */
374 filesReplaced
+= recFindSubDirs(dwFlags
,tmpSrcPath
,tmpDestPath
, doMore
);
378 /* Get the next handle */
379 } while(FindNextFile (hFile
, &findBuffer
));
381 return filesReplaced
;
384 INT
cmd_replace (LPTSTR param
)
387 INT argc
, i
,filesReplaced
= 0, nFiles
, srcIndex
= -1, destIndex
= -1;
389 TCHAR szDestPath
[MAX_PATH
], szSrcPath
[MAX_PATH
], tmpSrcPath
[MAX_PATH
];
393 if (!_tcsncmp (param
, _T("/?"), 2))
395 ConOutResPaging(TRUE
,STRING_REPLACE_HELP1
);
399 /* Divide the argument in to an array of c-strings */
400 arg
= split (param
, &argc
, FALSE
, FALSE
);
404 for (i
= 0; i
< argc
; i
++)
406 if (arg
[i
][0] == _T('/'))
408 if (_tcslen(arg
[i
]) == 2)
410 switch (_totupper(arg
[i
][1]))
413 dwFlags
|= REPLACE_ADD
;
416 dwFlags
|= REPLACE_CONFIRM
;
419 dwFlags
|= REPLACE_READ_ONLY
;
422 dwFlags
|= REPLACE_SUBDIR
;
425 dwFlags
|= REPLACE_DISK
;
428 dwFlags
|= REPLACE_UPDATE
;
431 invalid_switch(arg
[i
]);
437 invalid_switch(arg
[i
]);
449 else if (destIndex
== -1)
455 invalid_switch(arg
[i
]);
462 /* See so that at least source is there */
465 ConOutResPaging(TRUE
,STRING_REPLACE_HELP2
);
466 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
470 /* Check so that not both update and add switch is added and subdir */
471 if ((dwFlags
& REPLACE_UPDATE
|| dwFlags
& REPLACE_SUBDIR
) && (dwFlags
& REPLACE_ADD
))
473 ConOutResPaging(TRUE
,STRING_REPLACE_ERROR4
);
474 ConOutResPaging(TRUE
,STRING_REPLACE_HELP7
);
479 /* If we have a destination get the full path */
482 if (_tcslen(arg
[destIndex
]) == 2 && arg
[destIndex
][1] == ':')
483 GetRootPath(arg
[destIndex
],szDestPath
,MAX_PATH
);
486 /* Check for wildcards in destination directory */
487 if (_tcschr (arg
[destIndex
], _T('*')) != NULL
||
488 _tcschr (arg
[destIndex
], _T('?')) != NULL
)
490 ConOutResPrintf(STRING_REPLACE_ERROR2
,arg
[destIndex
]);
491 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
495 getPath(szDestPath
, arg
[destIndex
]);
496 /* Make sure that destination exists */
497 if (!IsExistingDirectory(szDestPath
))
499 ConOutResPrintf(STRING_REPLACE_ERROR2
, szDestPath
);
500 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
508 /* Dest is current dir */
509 GetCurrentDirectory(MAX_PATH
,szDestPath
);
512 /* Get the full source path */
513 if (!(_tcslen(arg
[srcIndex
]) == 2 && arg
[srcIndex
][1] == ':'))
514 getPath(szSrcPath
, arg
[srcIndex
]);
516 _tcscpy(szSrcPath
,arg
[srcIndex
]);
518 /* Source does not have wildcards */
519 if (_tcschr (arg
[srcIndex
], _T('*')) == NULL
&&
520 _tcschr (arg
[srcIndex
], _T('?')) == NULL
)
522 /* Check so that source is not a directory, because that is not allowed */
523 if (IsExistingDirectory(szSrcPath
))
525 ConOutResPrintf(STRING_REPLACE_ERROR6
, szSrcPath
);
526 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
530 /* Check if the file exists */
531 if (!IsExistingFile(szSrcPath
))
533 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
538 /* /w switch is set so wait for any key to be pressed */
539 if (dwFlags
& REPLACE_DISK
)
545 /* Add an extra \ to the destination path if needed */
546 if (szDestPath
[_tcslen(szDestPath
) - 1] != _T('\\'))
547 _tcscat(szDestPath
, _T("\\"));
549 /* Save source path */
550 _tcscpy(tmpSrcPath
,szSrcPath
);
551 /* Replace in dest dir */
552 filesReplaced
+= recReplace(dwFlags
, tmpSrcPath
, szDestPath
, &doMore
);
553 /* If subdir switch is set replace in the subdirs to */
554 if (dwFlags
& REPLACE_SUBDIR
&& doMore
)
556 filesReplaced
+= recFindSubDirs(dwFlags
, szSrcPath
, szDestPath
, &doMore
);
559 /* If source == dest write no more */
560 if (filesReplaced
!= -1)
562 /* No files replaced */
563 if (filesReplaced
==0)
565 /* Add switch dependent output */
566 if (dwFlags
& REPLACE_ADD
)
567 ConOutResPaging(TRUE
,STRING_REPLACE_HELP7
);
569 ConOutResPaging(TRUE
,STRING_REPLACE_HELP3
);
571 /* Some files replaced */
574 /* Add switch dependent output */
575 if (dwFlags
& REPLACE_ADD
)
576 ConOutResPrintf(STRING_REPLACE_HELP8
, filesReplaced
);
578 ConOutResPrintf(STRING_REPLACE_HELP4
, filesReplaced
);
585 #endif /* INCLUDE_CMD_REPLACE */