https://git.reactos.org/?p=reactos.git;a=commitdiff;h=7a133609e774126c7077a1...
commit 7a133609e774126c7077a1c055efd44137c6f1c9 Author: Hermès Bélusca-Maïto hermes.belusca-maito@reactos.org AuthorDate: Sun May 12 01:05:53 2019 +0200 Commit: Hermès Bélusca-Maïto hermes.belusca-maito@reactos.org CommitDate: Tue May 14 20:37:46 2019 +0200
[FIND] Improvements / bug-fixes. (#1553)
- Only include the strictly necessary headers. - Get rid of the dependency on shell and user DLLs. - fgetws() gets the string buffer size in number of characters. - We can use the CRT functions for lengths of the arguments etc.
- The cFileName member of the WIN32_FIND_DATAW structure does not contain the full PATH to the enumerated file, but only its name. In order to use _wfopen(), build a full file path out of the directory part of the file specification and the full file name.
- Simplify a ConPrintf() call to make it "atomic". - Fix the "confusion" lLineCount vs. lLineNumber vocable in the code. - Do not emit an extra newline after having displayed the results for a given file. - Uppercase the switches for performing the comparisons. - Send the errors to the StdErr stream. - Remove trailing whitespace. --- base/applications/cmdutils/find/CMakeLists.txt | 2 +- base/applications/cmdutils/find/find.c | 231 +++++++++++++++++-------- 2 files changed, 157 insertions(+), 76 deletions(-)
diff --git a/base/applications/cmdutils/find/CMakeLists.txt b/base/applications/cmdutils/find/CMakeLists.txt index 1df4d1b98df..e39eb506097 100644 --- a/base/applications/cmdutils/find/CMakeLists.txt +++ b/base/applications/cmdutils/find/CMakeLists.txt @@ -4,5 +4,5 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils) add_executable(find find.c find.rc) set_module_type(find win32cui UNICODE) target_link_libraries(find conutils ${PSEH_LIB}) -add_importlibs(find user32 msvcrt kernel32 shlwapi) +add_importlibs(find msvcrt kernel32) add_cd_file(TARGET find DESTINATION reactos/system32 FOR all) diff --git a/base/applications/cmdutils/find/find.c b/base/applications/cmdutils/find/find.c index 66cb2ef6245..9dd003fc07f 100644 --- a/base/applications/cmdutils/find/find.c +++ b/base/applications/cmdutils/find/find.c @@ -4,12 +4,19 @@ * PURPOSE: Prints all lines of a file that contain a string. * COPYRIGHT: Copyright 1994-2002 Jim Hall (jhall@freedos.org) * Copyright 2019 Paweł Cholewa (DaMcpg@protonmail.com) + * Copyright 2019 Hermes Belusca-Maito */
-#include <windows.h> #include <stdio.h> +#include <stdlib.h> + +#include <windef.h> +#include <winbase.h> +#include <winnls.h> +#include <winuser.h> + #include <conutils.h> -#include <shlwapi.h> /* StrStrW and StrStrIW */ +#include <strsafe.h>
#include "resource.h"
@@ -21,65 +28,106 @@ static BOOL bDisplayLineNumbers = FALSE; static BOOL bIgnoreCase = FALSE; static BOOL bDoNotSkipOfflineFiles = FALSE;
+/** + * @name StrStrCase + * @implemented + * + * Locates a substring inside a NULL-terminated wide string. + * + * @param[in] pszStr + * The NULL-terminated string to be scanned. + * + * @param[in] pszSearch + * The NULL-terminated string to search for. + * + * @param[in] bIgnoreCase + * TRUE if case has to be ignored, FALSE otherwise. + * + * @return + * Returns a pointer to the first occurrence of pszSearch in pszStr, + * or NULL if pszSearch does not appear in pszStr. If pszSearch points + * to a string of zero length, the function returns pszStr. + */ +static PWSTR +StrStrCase( + IN PCWSTR pszStr, + IN PCWSTR pszSearch, + IN BOOL bIgnoreCase) +{ + if (bIgnoreCase) + { + LCID LocaleId; + INT i, cch1, cch2; + + LocaleId = GetThreadLocale(); + + cch1 = wcslen(pszStr); + cch2 = wcslen(pszSearch); + + if (cch2 == 0) + return (PWSTR)pszStr; + + for (i = 0; i <= cch1 - cch2; ++i) + { + if (CompareStringW(LocaleId /* LOCALE_SYSTEM_DEFAULT */, + NORM_IGNORECASE /* | NORM_LINGUISTIC_CASING */, + pszStr + i, cch2, pszSearch, cch2) == CSTR_EQUAL) + { + return (PWSTR)(pszStr + i); + } + } + return NULL; + } + else + { + return wcsstr(pszStr, pszSearch); + } +} + /** * @name FindString * @implemented - * + * * Prints all lines of the stream that contain a string. - * - * @param pStream - * Stream to read from. - * - * @param szFilePath - * Filename to print in console. Can be NULL. - * - * @param szSearchedString - * String to search for. - * - * @return - * 0 if the string was found at least once, 1 otherwise. * + * @param[in] pStream + * The stream to read from. + * + * @param[in] pszFilePath + * The file name to print out. Can be NULL. + * + * @param[in] pszSearchString + * The NULL-terminated string to search for. + * + * @return + * 0 if the string was found at least once, 1 otherwise. */ -static int FindString(FILE* pStream, LPWSTR szFilePath, LPWSTR szSearchedString) +static int +FindString( + IN FILE* pStream, + IN PCWSTR pszFilePath OPTIONAL, + IN PCWSTR pszSearchString) { - WCHAR szLineBuffer[FIND_LINE_BUFFER_SIZE]; LONG lLineCount = 0; LONG lLineNumber = 0; BOOL bSubstringFound; int iReturnValue = 1; + WCHAR szLineBuffer[FIND_LINE_BUFFER_SIZE];
- if (szFilePath != NULL) + if (pszFilePath != NULL) { - /* Convert the filename to uppercase (for formatting) */ - CharUpperW(szFilePath); - /* Print the file's header */ - ConPrintf(StdOut, L"\n---------- %s", szFilePath); - - if (bCountLines) - { - ConPrintf(StdOut, L": "); - } - else - { - ConPrintf(StdOut, L"\n"); - } + ConPrintf(StdOut, L"\n---------- %s%s", + pszFilePath, bCountLines ? L": " : L"\n"); }
/* Loop through every line in the file */ - while (fgetws(szLineBuffer, sizeof(szLineBuffer), pStream) != NULL) + // FIXME: What if the string we search for crosses the boundary of our szLineBuffer ? + while (fgetws(szLineBuffer, _countof(szLineBuffer), pStream) != NULL) { - lLineCount++; + ++lLineNumber;
- if (bIgnoreCase) - { - bSubstringFound = StrStrIW(szLineBuffer, szSearchedString) != NULL; - } - else - { - bSubstringFound = StrStrW(szLineBuffer, szSearchedString) != NULL; - } - + bSubstringFound = (StrStrCase(szLineBuffer, pszSearchString, bIgnoreCase) != NULL);
/* Check if this line can be counted */ if (bSubstringFound != bInvertSearch) @@ -88,14 +136,14 @@ static int FindString(FILE* pStream, LPWSTR szFilePath, LPWSTR szSearchedString)
if (bCountLines) { - lLineNumber++; + ++lLineCount; } else { - /* Display the line on the screen */ + /* Display the line number if needed */ if (bDisplayLineNumbers) { - ConPrintf(StdOut, L"[%ld]", lLineCount); + ConPrintf(StdOut, L"[%ld]", lLineNumber); } ConPrintf(StdOut, L"%s", szLineBuffer); } @@ -105,13 +153,15 @@ static int FindString(FILE* pStream, LPWSTR szFilePath, LPWSTR szSearchedString) if (bCountLines) { /* Print the matching line count */ - ConPrintf(StdOut, L"%ld\n", lLineNumber); + ConPrintf(StdOut, L"%ld\n", lLineCount); } - else if (szFilePath != NULL && iReturnValue == 0) +#if 0 + else if (pszFilePath != NULL && iReturnValue == 0) { /* Print a newline for formatting */ ConPrintf(StdOut, L"\n"); } +#endif
return iReturnValue; } @@ -121,14 +171,14 @@ int wmain(int argc, WCHAR* argv[]) int i; int iReturnValue = 2; int iSearchedStringIndex = -1; - BOOL bFoundFileParameter = FALSE; - - HANDLE hFindFileHandle; + HANDLE hFindFile; WIN32_FIND_DATAW FindData; - FILE* pOpenedFile; + PWCHAR ptr; + WCHAR szFullFilePath[MAX_PATH];
+ /* Initialize the Console Standard Streams */ ConInitStdStreams();
if (argc == 1) @@ -139,49 +189,45 @@ int wmain(int argc, WCHAR* argv[]) }
/* Parse the command line arguments */ - for (i = 1; i < argc; i++) + for (i = 1; i < argc; ++i) { /* Check if this argument contains a switch */ - if (lstrlenW(argv[i]) == 2 && argv[i][0] == L'/') + if (wcslen(argv[i]) == 2 && argv[i][0] == L'/') { - switch (argv[i][1]) + switch (towupper(argv[i][1])) { case L'?': ConResPuts(StdOut, IDS_USAGE); return 0; - case L'v': case L'V': bInvertSearch = TRUE; break; - case L'c': case L'C': bCountLines = TRUE; break; - case L'n': case L'N': bDisplayLineNumbers = TRUE; break; - case L'i': case L'I': bIgnoreCase = TRUE; break; default: /* Report invalid switch error */ - ConResPuts(StdOut, IDS_INVALID_SWITCH); + ConResPuts(StdErr, IDS_INVALID_SWITCH); return 2; } } - else if (lstrlenW(argv[i]) > 2 && argv[i][0] == L'/') + else if (wcslen(argv[i]) > 2 && argv[i][0] == L'/') { /* Check if this parameter is /OFF or /OFFLINE */ - if (lstrcmpiW(argv[i], L"/off") == 0 || lstrcmpiW(argv[i], L"/offline") == 0) + if (_wcsicmp(argv[i], L"/off") == 0 || _wcsicmp(argv[i], L"/offline") == 0) { bDoNotSkipOfflineFiles = TRUE; } else { /* Report invalid switch error */ - ConResPuts(StdOut, IDS_INVALID_SWITCH); + ConResPuts(StdErr, IDS_INVALID_SWITCH); return 2; } } @@ -202,28 +248,28 @@ int wmain(int argc, WCHAR* argv[]) if (iSearchedStringIndex == -1) { /* User didn't provide the string to search for, display program usage and exit */ - ConResPuts(StdOut, IDS_USAGE); + ConResPuts(StdErr, IDS_USAGE); return 2; }
if (bFoundFileParameter) { /* After the command line arguments were parsed, iterate through them again to get the filenames */ - for (i = 1; i < argc; i++) + for (i = 1; i < argc; ++i) { /* If the value is a switch or the searched string, continue */ - if ((lstrlenW(argv[i]) > 0 && argv[i][0] == L'/') || i == iSearchedStringIndex) + if ((wcslen(argv[i]) > 0 && argv[i][0] == L'/') || i == iSearchedStringIndex) { continue; }
- hFindFileHandle = FindFirstFileW(argv[i], &FindData); - if (hFindFileHandle == INVALID_HANDLE_VALUE) + hFindFile = FindFirstFileW(argv[i], &FindData); + if (hFindFile == INVALID_HANDLE_VALUE) { - ConResPrintf(StdOut, IDS_NO_SUCH_FILE, argv[i]); + ConResPrintf(StdErr, IDS_NO_SUCH_FILE, argv[i]); continue; } - + do { /* Check if the file contains offline attribute and should be skipped */ @@ -232,14 +278,49 @@ int wmain(int argc, WCHAR* argv[]) continue; }
- pOpenedFile = _wfopen(FindData.cFileName, L"r"); + /* Skip directory */ + // FIXME: Implement recursivity? + if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + continue; + } + + /* + * Build the full file path from the file specification pattern. + * + * Note that we could use GetFullPathName() instead, however + * we want to keep compatibility with Windows' find.exe utility + * that does not use this function as it keeps the file name + * directly based on the pattern. + */ + ptr = wcsrchr(argv[i], L'\'); // Check for last directory. + if (!ptr) + ptr = wcsrchr(argv[i], L':'); // Check for drive. + if (ptr) + { + /* The pattern contains a drive or directory part: keep it and concatenate the full file name */ + StringCchCopyNW(szFullFilePath, _countof(szFullFilePath), + argv[i], ptr + 1 - argv[i]); + StringCchCatW(szFullFilePath, _countof(szFullFilePath), + FindData.cFileName); + } + else + { + /* The pattern does not contain any drive or directory part: just copy the full file name */ + StringCchCopyW(szFullFilePath, _countof(szFullFilePath), + FindData.cFileName); + } + + // FIXME: Windows' find.exe supports searching inside binary files. + pOpenedFile = _wfopen(szFullFilePath, L"r"); if (pOpenedFile == NULL) { - ConResPrintf(StdOut, IDS_CANNOT_OPEN, FindData.cFileName); + ConResPrintf(StdErr, IDS_CANNOT_OPEN, szFullFilePath); continue; }
- if (FindString(pOpenedFile, FindData.cFileName, argv[iSearchedStringIndex]) == 0) + /* NOTE: Convert the file path to uppercase for formatting */ + if (FindString(pOpenedFile, _wcsupr(szFullFilePath), argv[iSearchedStringIndex]) == 0) { iReturnValue = 0; } @@ -249,15 +330,15 @@ int wmain(int argc, WCHAR* argv[]) }
fclose(pOpenedFile); - } while (FindNextFileW(hFindFileHandle, &FindData)); + } while (FindNextFileW(hFindFile, &FindData));
- FindClose(hFindFileHandle); + FindClose(hFindFile); } } else { FindString(stdin, NULL, argv[iSearchedStringIndex]); } - + return iReturnValue; }