https://git.reactos.org/?p=reactos.git;a=commitdiff;h=7a133609e774126c7077a…
commit 7a133609e774126c7077a1c055efd44137c6f1c9
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Sun May 12 01:05:53 2019 +0200
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)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(a)freedos.org)
* Copyright 2019 Paweł Cholewa (DaMcpg(a)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;
}