ReactOS  0.4.14-dev-552-g2fad488
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 
51 static PWSTR
53  IN PCWSTR pszStr,
54  IN PCWSTR pszSearch,
56 {
57  if (bIgnoreCase)
58  {
59  LCID LocaleId;
60  INT i, cch1, cch2;
61 
62  LocaleId = GetThreadLocale();
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 
105 static 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 
169 int 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':
206  bCountLines = TRUE;
207  break;
208  case L'N':
210  break;
211  case L'I':
212  bIgnoreCase = TRUE;
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 }
BOOL WINAPI FindNextFileW(IN HANDLE hFindFile, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:382
#define IDS_INVALID_SWITCH
Definition: resource.h:6
static int argc
Definition: ServiceArgs.c:12
const uint16_t * PCWSTR
Definition: typedefs.h:55
#define IN
Definition: typedefs.h:38
STRSAFEAPI StringCchCopyNW(STRSAFE_LPWSTR pszDest, size_t cchDest, STRSAFE_LPCWSTR pszSrc, size_t cchToCopy)
Definition: strsafe.h:236
#define IDS_CANNOT_OPEN
Definition: resource.h:5
#define TRUE
Definition: types.h:120
static BOOL bIgnoreCase
Definition: find.c:28
static BOOL bDisplayLineNumbers
Definition: find.c:27
_Check_return_ _CRTIMP FILE *__cdecl _wfopen(_In_z_ const wchar_t *_Filename, _In_z_ const wchar_t *_Mode)
_CONST_RETURN wchar_t *__cdecl wcsstr(_In_z_ const wchar_t *_Str, _In_z_ const wchar_t *_SubStr)
uint16_t * PWSTR
Definition: typedefs.h:54
#define _countof(array)
Definition: fontsub.cpp:30
#define INVALID_HANDLE_VALUE
Definition: compat.h:399
static PWSTR StrStrCase(IN PCWSTR pszStr, IN PCWSTR pszSearch, IN BOOL bIgnoreCase)
Definition: find.c:52
FILE * stdin
#define IDS_USAGE
Definition: resource.h:3
DWORD LCID
Definition: nls.h:13
#define NORM_IGNORECASE
Definition: winnls.h:173
uint16_t * PWCHAR
Definition: typedefs.h:54
#define argv
Definition: mplay32.c:18
int32_t INT
Definition: typedefs.h:56
int wmain(int argc, WCHAR *argv[])
Definition: find.c:169
#define FIND_LINE_BUFFER_SIZE
Definition: find.c:23
STRSAFEAPI StringCchCatW(STRSAFE_LPWSTR pszDest, size_t cchDest, STRSAFE_LPCWSTR pszSrc)
Definition: strsafe.h:325
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
#define ConInitStdStreams()
Definition: stream.h:122
unsigned int BOOL
Definition: ntddk_ex.h:94
long LONG
Definition: pedump.c:60
STRSAFEAPI StringCchCopyW(STRSAFE_LPWSTR pszDest, size_t cchDest, STRSAFE_LPCWSTR pszSrc)
Definition: strsafe.h:149
static PVOID ptr
Definition: dispmode.c:27
INT __cdecl ConPrintf(IN PCON_STREAM Stream, IN LPWSTR szStr,...)
Definition: outstream.c:520
static BOOL bDoNotSkipOfflineFiles
Definition: find.c:29
smooth NULL
Definition: ftsmooth.c:416
_Check_return_ _CRTIMP _CONST_RETURN wchar_t *__cdecl wcsrchr(_In_z_ const wchar_t *_Str, _In_ wchar_t _Ch)
_CRTIMP wchar_t *__cdecl _wcsupr(_Inout_z_ wchar_t *_String)
#define FILE_ATTRIBUTE_DIRECTORY
Definition: nt_native.h:705
INT __cdecl ConResPrintf(IN PCON_STREAM Stream, IN UINT uID,...)
Definition: outstream.c:781
INT WINAPI CompareStringW(LCID lcid, DWORD flags, LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
Definition: lang.c:2275
__wchar_t WCHAR
Definition: xmlstorage.h:180
#define MAX_PATH
Definition: compat.h:26
#define StdErr
Definition: stream.h:77
INT ConResPuts(IN PCON_STREAM Stream, IN UINT uID)
Definition: outstream.c:610
static const WCHAR L[]
Definition: oid.c:1250
static BOOL bCountLines
Definition: find.c:26
#define CSTR_EQUAL
Definition: winnls.h:453
_Check_return_opt_ _CRTIMP int __cdecl fclose(_Inout_ FILE *_File)
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 StdOut
Definition: stream.h:76
wchar_t * fgetws(wchar_t *buf, int bufsize, FILE *file)
Definition: wmain.c:22
#define towupper(c)
Definition: wctype.h:99
static BOOL bInvertSearch
Definition: find.c:25
size_t __cdecl wcslen(_In_z_ const wchar_t *_Str)
_Check_return_ _CRTIMP int __cdecl _wcsicmp(_In_z_ const wchar_t *_Str1, _In_z_ const wchar_t *_Str2)
LCID WINAPI GetThreadLocale(void)
Definition: lang.c:1449
HANDLE WINAPI FindFirstFileW(IN LPCWSTR lpFileName, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:320
#define FILE_ATTRIBUTE_OFFLINE
Definition: nt_native.h:712
BOOL WINAPI FindClose(HANDLE hFindFile)
Definition: find.c:502
PULONG MinorVersion OPTIONAL
Definition: CrossNt.h:68