Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345/* * PROJECT: ReactOS Find Command * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * 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 <stdio.h>#include <stdlib.h>
#include <windef.h>#include <winbase.h>#include <winnls.h>#include <winuser.h>
#include <conutils.h>#include <strsafe.h>
#include "resource.h"
#define FIND_LINE_BUFFER_SIZE 4096
static BOOL bInvertSearch = FALSE;static BOOL bCountLines = FALSE;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 PWSTRStrStrCase( 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[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 intFindString( IN FILE* pStream, IN PCWSTR pszFilePath OPTIONAL, IN PCWSTR pszSearchString){ LONG lLineCount = 0; LONG lLineNumber = 0; BOOL bSubstringFound; int iReturnValue = 1; WCHAR szLineBuffer[FIND_LINE_BUFFER_SIZE];
if (pszFilePath != NULL) { /* Print the file's header */ ConPrintf(StdOut, L"\n---------- %s%s", pszFilePath, bCountLines ? L": " : L"\n"); }
/* Loop through every line in the file */ // FIXME: What if the string we search for crosses the boundary of our szLineBuffer ? while (fgetws(szLineBuffer, _countof(szLineBuffer), pStream) != NULL) { ++lLineNumber;
bSubstringFound = (StrStrCase(szLineBuffer, pszSearchString, bIgnoreCase) != NULL);
/* Check if this line can be counted */ if (bSubstringFound != bInvertSearch) { iReturnValue = 0;
if (bCountLines) { ++lLineCount; } else { /* Display the line number if needed */ if (bDisplayLineNumbers) { ConPrintf(StdOut, L"[%ld]", lLineNumber); } ConPrintf(StdOut, L"%s", szLineBuffer); } } }
if (bCountLines) { /* Print the matching line count */ ConPrintf(StdOut, L"%ld\n", lLineCount); }#if 0 else if (pszFilePath != NULL && iReturnValue == 0) { /* Print a newline for formatting */ ConPrintf(StdOut, L"\n"); }#endif
return iReturnValue;}
int wmain(int argc, WCHAR* argv[]){ int i; int iReturnValue = 2; int iSearchedStringIndex = -1; BOOL bFoundFileParameter = FALSE; HANDLE hFindFile; WIN32_FIND_DATAW FindData; FILE* pOpenedFile; PWCHAR ptr; WCHAR szFullFilePath[MAX_PATH];
/* Initialize the Console Standard Streams */ ConInitStdStreams();
if (argc == 1) { /* If no argument were provided by the user, display program usage and exit */ ConResPuts(StdOut, IDS_USAGE); return 0; }
/* Parse the command line arguments */ for (i = 1; i < argc; ++i) { /* Check if this argument contains a switch */ if (wcslen(argv[i]) == 2 && argv[i][0] == L'/') { switch (towupper(argv[i][1])) { case L'?': ConResPuts(StdOut, IDS_USAGE); return 0; case L'V': bInvertSearch = TRUE; break; case L'C': bCountLines = TRUE; break; case L'N': bDisplayLineNumbers = TRUE; break; case L'I': bIgnoreCase = TRUE; break; default: /* Report invalid switch error */ ConResPuts(StdErr, IDS_INVALID_SWITCH); return 2; } } else if (wcslen(argv[i]) > 2 && argv[i][0] == L'/') { /* Check if this parameter is /OFF or /OFFLINE */ if (_wcsicmp(argv[i], L"/off") == 0 || _wcsicmp(argv[i], L"/offline") == 0) { bDoNotSkipOfflineFiles = TRUE; } else { /* Report invalid switch error */ ConResPuts(StdErr, IDS_INVALID_SWITCH); return 2; } } else { if (iSearchedStringIndex == -1) { iSearchedStringIndex = i; } else { /* There's a file specified in the parameters, no need to read from stdin */ bFoundFileParameter = TRUE; } } }
if (iSearchedStringIndex == -1) { /* User didn't provide the string to search for, display program usage and exit */ 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) { /* If the value is a switch or the searched string, continue */ if ((wcslen(argv[i]) > 0 && argv[i][0] == L'/') || i == iSearchedStringIndex) { continue; }
hFindFile = FindFirstFileW(argv[i], &FindData); if (hFindFile == INVALID_HANDLE_VALUE) { ConResPrintf(StdErr, IDS_NO_SUCH_FILE, argv[i]); continue; }
do { /* Check if the file contains offline attribute and should be skipped */ if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) && !bDoNotSkipOfflineFiles) { continue; }
/* 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(StdErr, IDS_CANNOT_OPEN, szFullFilePath); continue; }
/* NOTE: Convert the file path to uppercase for formatting */ if (FindString(pOpenedFile, _wcsupr(szFullFilePath), argv[iSearchedStringIndex]) == 0) { iReturnValue = 0; } else if (iReturnValue != 0) { iReturnValue = 1; }
fclose(pOpenedFile); } while (FindNextFileW(hFindFile, &FindData));
FindClose(hFindFile); } } else { iReturnValue = FindString(stdin, NULL, argv[iSearchedStringIndex]); }
return iReturnValue;}