ReactOS 0.4.15-dev-7942-gd23573b
find.c
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS Find Command
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Prints all lines of a file that contain a string.
5 * COPYRIGHT: Copyright 1994-2002 Jim Hall (jhall@freedos.org)
6 * Copyright 2019 Paweł Cholewa (DaMcpg@protonmail.com)
7 * Copyright 2019 Hermes Belusca-Maito
8 */
9
10#include <stdio.h>
11#include <stdlib.h>
12
13#include <windef.h>
14#include <winbase.h>
15#include <winnls.h>
16#include <winuser.h>
17
18#include <conutils.h>
19#include <strsafe.h>
20
21#include "resource.h"
22
23#define FIND_LINE_BUFFER_SIZE 4096
24
30
51static PWSTR
53 IN PCWSTR pszStr,
54 IN PCWSTR pszSearch,
56{
57 if (bIgnoreCase)
58 {
60 INT i, cch1, cch2;
61
63
64 cch1 = wcslen(pszStr);
65 cch2 = wcslen(pszSearch);
66
67 if (cch2 == 0)
68 return (PWSTR)pszStr;
69
70 for (i = 0; i <= cch1 - cch2; ++i)
71 {
72 if (CompareStringW(LocaleId /* LOCALE_SYSTEM_DEFAULT */,
73 NORM_IGNORECASE /* | NORM_LINGUISTIC_CASING */,
74 pszStr + i, cch2, pszSearch, cch2) == CSTR_EQUAL)
75 {
76 return (PWSTR)(pszStr + i);
77 }
78 }
79 return NULL;
80 }
81 else
82 {
83 return wcsstr(pszStr, pszSearch);
84 }
85}
86
105static int
107 IN FILE* pStream,
108 IN PCWSTR pszFilePath OPTIONAL,
109 IN PCWSTR pszSearchString)
110{
111 LONG lLineCount = 0;
112 LONG lLineNumber = 0;
113 BOOL bSubstringFound;
114 int iReturnValue = 1;
115 WCHAR szLineBuffer[FIND_LINE_BUFFER_SIZE];
116
117 if (pszFilePath != NULL)
118 {
119 /* Print the file's header */
120 ConPrintf(StdOut, L"\n---------- %s%s",
121 pszFilePath, bCountLines ? L": " : L"\n");
122 }
123
124 /* Loop through every line in the file */
125 // FIXME: What if the string we search for crosses the boundary of our szLineBuffer ?
126 while (fgetws(szLineBuffer, _countof(szLineBuffer), pStream) != NULL)
127 {
128 ++lLineNumber;
129
130 bSubstringFound = (StrStrCase(szLineBuffer, pszSearchString, bIgnoreCase) != NULL);
131
132 /* Check if this line can be counted */
133 if (bSubstringFound != bInvertSearch)
134 {
135 iReturnValue = 0;
136
137 if (bCountLines)
138 {
139 ++lLineCount;
140 }
141 else
142 {
143 /* Display the line number if needed */
145 {
146 ConPrintf(StdOut, L"[%ld]", lLineNumber);
147 }
148 ConPrintf(StdOut, L"%s", szLineBuffer);
149 }
150 }
151 }
152
153 if (bCountLines)
154 {
155 /* Print the matching line count */
156 ConPrintf(StdOut, L"%ld\n", lLineCount);
157 }
158#if 0
159 else if (pszFilePath != NULL && iReturnValue == 0)
160 {
161 /* Print a newline for formatting */
162 ConPrintf(StdOut, L"\n");
163 }
164#endif
165
166 return iReturnValue;
167}
168
169int wmain(int argc, WCHAR* argv[])
170{
171 int i;
172 int iReturnValue = 2;
173 int iSearchedStringIndex = -1;
174 BOOL bFoundFileParameter = FALSE;
175 HANDLE hFindFile;
176 WIN32_FIND_DATAW FindData;
177 FILE* pOpenedFile;
178 PWCHAR ptr;
179 WCHAR szFullFilePath[MAX_PATH];
180
181 /* Initialize the Console Standard Streams */
183
184 if (argc == 1)
185 {
186 /* If no argument were provided by the user, display program usage and exit */
188 return 0;
189 }
190
191 /* Parse the command line arguments */
192 for (i = 1; i < argc; ++i)
193 {
194 /* Check if this argument contains a switch */
195 if (wcslen(argv[i]) == 2 && argv[i][0] == L'/')
196 {
197 switch (towupper(argv[i][1]))
198 {
199 case L'?':
201 return 0;
202 case L'V':
204 break;
205 case L'C':
207 break;
208 case L'N':
210 break;
211 case L'I':
213 break;
214 default:
215 /* Report invalid switch error */
217 return 2;
218 }
219 }
220 else if (wcslen(argv[i]) > 2 && argv[i][0] == L'/')
221 {
222 /* Check if this parameter is /OFF or /OFFLINE */
223 if (_wcsicmp(argv[i], L"/off") == 0 || _wcsicmp(argv[i], L"/offline") == 0)
224 {
226 }
227 else
228 {
229 /* Report invalid switch error */
231 return 2;
232 }
233 }
234 else
235 {
236 if (iSearchedStringIndex == -1)
237 {
238 iSearchedStringIndex = i;
239 }
240 else
241 {
242 /* There's a file specified in the parameters, no need to read from stdin */
243 bFoundFileParameter = TRUE;
244 }
245 }
246 }
247
248 if (iSearchedStringIndex == -1)
249 {
250 /* User didn't provide the string to search for, display program usage and exit */
252 return 2;
253 }
254
255 if (bFoundFileParameter)
256 {
257 /* After the command line arguments were parsed, iterate through them again to get the filenames */
258 for (i = 1; i < argc; ++i)
259 {
260 /* If the value is a switch or the searched string, continue */
261 if ((wcslen(argv[i]) > 0 && argv[i][0] == L'/') || i == iSearchedStringIndex)
262 {
263 continue;
264 }
265
266 hFindFile = FindFirstFileW(argv[i], &FindData);
267 if (hFindFile == INVALID_HANDLE_VALUE)
268 {
270 continue;
271 }
272
273 do
274 {
275 /* Check if the file contains offline attribute and should be skipped */
276 if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) && !bDoNotSkipOfflineFiles)
277 {
278 continue;
279 }
280
281 /* Skip directory */
282 // FIXME: Implement recursivity?
283 if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
284 {
285 continue;
286 }
287
288 /*
289 * Build the full file path from the file specification pattern.
290 *
291 * Note that we could use GetFullPathName() instead, however
292 * we want to keep compatibility with Windows' find.exe utility
293 * that does not use this function as it keeps the file name
294 * directly based on the pattern.
295 */
296 ptr = wcsrchr(argv[i], L'\\'); // Check for last directory.
297 if (!ptr)
298 ptr = wcsrchr(argv[i], L':'); // Check for drive.
299 if (ptr)
300 {
301 /* The pattern contains a drive or directory part: keep it and concatenate the full file name */
302 StringCchCopyNW(szFullFilePath, _countof(szFullFilePath),
303 argv[i], ptr + 1 - argv[i]);
304 StringCchCatW(szFullFilePath, _countof(szFullFilePath),
305 FindData.cFileName);
306 }
307 else
308 {
309 /* The pattern does not contain any drive or directory part: just copy the full file name */
310 StringCchCopyW(szFullFilePath, _countof(szFullFilePath),
311 FindData.cFileName);
312 }
313
314 // FIXME: Windows' find.exe supports searching inside binary files.
315 pOpenedFile = _wfopen(szFullFilePath, L"r");
316 if (pOpenedFile == NULL)
317 {
318 ConResPrintf(StdErr, IDS_CANNOT_OPEN, szFullFilePath);
319 continue;
320 }
321
322 /* NOTE: Convert the file path to uppercase for formatting */
323 if (FindString(pOpenedFile, _wcsupr(szFullFilePath), argv[iSearchedStringIndex]) == 0)
324 {
325 iReturnValue = 0;
326 }
327 else if (iReturnValue != 0)
328 {
329 iReturnValue = 1;
330 }
331
332 fclose(pOpenedFile);
333 } while (FindNextFileW(hFindFile, &FindData));
334
335 FindClose(hFindFile);
336 }
337 }
338 else
339 {
340 iReturnValue = FindString(stdin, NULL, argv[iSearchedStringIndex]);
341 }
342
343 return iReturnValue;
344}
static int argc
Definition: ServiceArgs.c:12
#define IDS_USAGE
Definition: resource.h:3
#define ConInitStdStreams()
Definition: fc.c:13
void ConPrintf(FILE *fp, LPCWSTR psz,...)
Definition: fc.c:20
#define StdOut
Definition: fc.c:14
void ConResPrintf(FILE *fp, UINT nID,...)
Definition: fc.c:33
#define StdErr
Definition: fc.c:15
void ConResPuts(FILE *fp, UINT nID)
Definition: fc.c:27
#define IDS_CANNOT_OPEN
Definition: resource.h:8
#define IDS_INVALID_SWITCH
Definition: resource.h:7
static PWSTR StrStrCase(IN PCWSTR pszStr, IN PCWSTR pszSearch, IN BOOL bIgnoreCase)
Definition: find.c:52
static BOOL bDisplayLineNumbers
Definition: find.c:27
static BOOL bDoNotSkipOfflineFiles
Definition: find.c:29
static BOOL bIgnoreCase
Definition: find.c:28
#define FIND_LINE_BUFFER_SIZE
Definition: find.c:23
static BOOL bCountLines
Definition: find.c:26
static BOOL bInvertSearch
Definition: find.c:25
static int FindString(IN FILE *pStream, IN PCWSTR pszFilePath OPTIONAL, IN PCWSTR pszSearchString)
Definition: find.c:106
#define IDS_NO_SUCH_FILE
Definition: resource.h:4
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define wcsrchr
Definition: compat.h:16
#define INVALID_HANDLE_VALUE
Definition: compat.h:731
#define MAX_PATH
Definition: compat.h:34
HANDLE WINAPI FindFirstFileW(IN LPCWSTR lpFileName, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:320
BOOL WINAPI FindClose(HANDLE hFindFile)
Definition: find.c:502
BOOL WINAPI FindNextFileW(IN HANDLE hFindFile, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:382
unsigned int BOOL
Definition: ntddk_ex.h:94
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
_Check_return_ _CRTIMP FILE *__cdecl _wfopen(_In_z_ const wchar_t *_Filename, _In_z_ const wchar_t *_Mode)
#define stdin
Definition: stdio.h:98
_Check_return_opt_ _CRTIMP int __cdecl fclose(_Inout_ FILE *_File)
_CRTIMP size_t __cdecl wcslen(_In_z_ const wchar_t *_Str)
_CONST_RETURN wchar_t *__cdecl wcsstr(_In_z_ const wchar_t *_Str, _In_z_ const wchar_t *_SubStr)
LCID WINAPI GetThreadLocale(void)
Definition: lang.c:1459
INT WINAPI CompareStringW(LCID lcid, DWORD flags, LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
Definition: lang.c:2671
static PVOID ptr
Definition: dispmode.c:27
#define argv
Definition: mplay32.c:18
#define FILE_ATTRIBUTE_OFFLINE
Definition: nt_native.h:712
#define FILE_ATTRIBUTE_DIRECTORY
Definition: nt_native.h:705
#define L(x)
Definition: ntvdm.h:50
long LONG
Definition: pedump.c:60
int wmain()
_Check_return_ _CRTIMP int __cdecl _wcsicmp(_In_z_ const wchar_t *_Str1, _In_z_ const wchar_t *_Str2)
_CRTIMP wchar_t *__cdecl _wcsupr(_Inout_z_ wchar_t *_String)
DWORD LCID
Definition: nls.h:13
#define _countof(array)
Definition: sndvol32.h:68
PULONG MinorVersion OPTIONAL
Definition: CrossNt.h:68
STRSAFEAPI StringCchCatW(STRSAFE_LPWSTR pszDest, size_t cchDest, STRSAFE_LPCWSTR pszSrc)
Definition: strsafe.h:325
STRSAFEAPI StringCchCopyW(STRSAFE_LPWSTR pszDest, size_t cchDest, STRSAFE_LPCWSTR pszSrc)
Definition: strsafe.h:149
STRSAFEAPI StringCchCopyNW(STRSAFE_LPWSTR pszDest, size_t cchDest, STRSAFE_LPCWSTR pszSrc, size_t cchToCopy)
Definition: strsafe.h:236
#define towupper(c)
Definition: wctype.h:99
uint16_t * PWSTR
Definition: typedefs.h:56
const uint16_t * PCWSTR
Definition: typedefs.h:57
int32_t INT
Definition: typedefs.h:58
#define IN
Definition: typedefs.h:39
uint16_t * PWCHAR
Definition: typedefs.h:56
_Must_inspect_result_ _In_ PWDFDEVICE_INIT _In_ PCUNICODE_STRING _In_ PCUNICODE_STRING _In_ LCID LocaleId
Definition: wdfpdo.h:437
#define NORM_IGNORECASE
Definition: winnls.h:176
#define CSTR_EQUAL
Definition: winnls.h:456
wchar_t * fgetws(wchar_t *buf, int bufsize, FILE *file)
Definition: wmain.c:22
__wchar_t WCHAR
Definition: xmlstorage.h:180