Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/programs/findstr/main.c
4389 views
1
/*
2
* Copyright 2012 Qian Hong
3
* Copyright 2023 Zhiyi Zhang for CodeWeavers
4
*
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Lesser General Public
7
* License as published by the Free Software Foundation; either
8
* version 2.1 of the License, or (at your option) any later version.
9
*
10
* This library is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Lesser General Public License for more details.
14
*
15
* You should have received a copy of the GNU Lesser General Public
16
* License along with this library; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18
*/
19
20
#include <windows.h>
21
#include <stdio.h>
22
#include <io.h>
23
#include <fcntl.h>
24
#include "findstr.h"
25
#include "wine/debug.h"
26
27
WINE_DEFAULT_DEBUG_CHANNEL(findstr);
28
29
static int WINAPIV findstr_error_wprintf(int msg, ...)
30
{
31
WCHAR msg_buffer[MAXSTRING];
32
va_list va_args;
33
int ret;
34
35
LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer));
36
va_start(va_args, msg);
37
ret = vfwprintf(stderr, msg_buffer, va_args);
38
va_end(va_args);
39
return ret;
40
}
41
42
static int findstr_message(int msg)
43
{
44
WCHAR msg_buffer[MAXSTRING];
45
46
LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer));
47
return wprintf(msg_buffer);
48
}
49
50
static BOOL add_file(struct findstr_file **head, const WCHAR *path)
51
{
52
struct findstr_file **ptr, *new_file;
53
54
ptr = head;
55
while (*ptr)
56
ptr = &((*ptr)->next);
57
58
new_file = calloc(1, sizeof(*new_file));
59
if (!new_file)
60
{
61
WINE_ERR("Out of memory.\n");
62
return FALSE;
63
}
64
65
if (!path)
66
{
67
new_file->file = stdin;
68
_setmode(_fileno(stdin), _O_BINARY);
69
}
70
else
71
{
72
new_file->file = _wfopen(path, L"rb");
73
if (!new_file->file)
74
{
75
findstr_error_wprintf(STRING_CANNOT_OPEN, path);
76
return FALSE;
77
}
78
}
79
80
*ptr = new_file;
81
return TRUE;
82
}
83
84
static inline char *strdupWA(const WCHAR *src)
85
{
86
char *dst = NULL;
87
if (src)
88
{
89
int len = WideCharToMultiByte(GetOEMCP(), 0, src, -1, NULL, 0, NULL, NULL);
90
if ((dst = malloc(len))) WideCharToMultiByte(GetOEMCP(), 0, src, -1, dst, len, NULL, NULL);
91
}
92
return dst;
93
}
94
95
static void add_string(struct findstr_string **head, const WCHAR *string)
96
{
97
struct findstr_string **ptr, *new_string;
98
char *str = strdupWA(string);
99
100
if (!str)
101
{
102
WINE_ERR("Out of memory.\n");
103
return;
104
}
105
106
ptr = head;
107
while (*ptr)
108
ptr = &((*ptr)->next);
109
110
new_string = calloc(1, sizeof(*new_string));
111
if (!new_string)
112
{
113
WINE_ERR("Out of memory.\n");
114
return;
115
}
116
117
new_string->string = str;
118
*ptr = new_string;
119
}
120
121
static BOOL match_substring(const char *str, const char *substr, BOOL case_sensitive)
122
{
123
if (case_sensitive) return !!strstr(str, substr);
124
125
while (*str)
126
{
127
const char *p1 = str, *p2 = substr;
128
129
while (*p1 && *p2 && tolower(*p1) == tolower(*p2))
130
{
131
p1++;
132
p2++;
133
}
134
if (!*p2) return TRUE;
135
str++;
136
}
137
return FALSE;
138
}
139
140
static inline BOOL is_op(char c, const char *regexp, UINT pos)
141
{
142
if (!pos) return (*regexp == c);
143
return (regexp[pos] == c && regexp[pos - 1] != '\\');
144
}
145
146
static inline BOOL match_char(char c1, char c2, BOOL case_sensitive)
147
{
148
if (case_sensitive) return c1 == c2;
149
return tolower(c1) == tolower(c2);
150
}
151
152
static BOOL match_star(char, const char *, const char *, UINT, BOOL);
153
154
static BOOL match_here(const char *str, const char *regexp, UINT pos, BOOL case_sensitive)
155
{
156
if (regexp[pos] == '\\' && regexp[pos + 1]) pos++;
157
if (!regexp[pos]) return TRUE;
158
if (is_op('*', regexp, pos + 1)) return match_star(regexp[pos], str, regexp, pos + 2, case_sensitive);
159
if (is_op('$', regexp, pos) && !regexp[pos + 1]) return (str[0] == '\n' || (str[0] == '\r' && str[1] == '\n'));
160
if (*str && (is_op('.', regexp, pos) || match_char(*str, regexp[pos], case_sensitive)))
161
return match_here(str + 1, regexp, pos + 1, case_sensitive);
162
return FALSE;
163
}
164
165
static BOOL match_star(char c, const char *str, const char *regexp, UINT pos, BOOL case_sensitive)
166
{
167
do { if (match_here(str, regexp, pos, case_sensitive)) return TRUE; }
168
while (*str && (match_char(*str++, c, case_sensitive) || c == '.'));
169
return FALSE;
170
}
171
172
static BOOL match_regexp(const char *str, const char *regexp, BOOL case_sensitive)
173
{
174
if (strstr(regexp, "[")) FIXME("character ranges (i.e. [abc], [^a-z]) are not supported\n");
175
if (strstr(regexp, "\\<") || strstr(regexp, "\\>")) FIXME("word position (i.e. \\< and \\>) not supported\n");
176
177
if (regexp[0] == '^') return match_here(str, regexp, 1, case_sensitive);
178
do { if (match_here(str, regexp, 0, case_sensitive)) return TRUE; } while (*str++);
179
return FALSE;
180
}
181
182
int __cdecl wmain(int argc, WCHAR *argv[])
183
{
184
struct findstr_string *string_head = NULL, *current_string, *next_string;
185
struct findstr_file *file_head = NULL, *current_file, *next_file;
186
char line[MAXSTRING];
187
WCHAR *string, *ptr, *buffer;
188
BOOL has_string = FALSE, has_file = FALSE, case_sensitive = TRUE, regular_expression = TRUE;
189
int ret = 1, i, j;
190
191
for (i = 0; i < argc; i++)
192
WINE_TRACE("%s ", wine_dbgstr_w(argv[i]));
193
WINE_TRACE("\n");
194
195
if (argc == 1)
196
{
197
findstr_error_wprintf(STRING_BAD_COMMAND_LINE);
198
return 2;
199
}
200
201
for (i = 1; i < argc; i++)
202
{
203
if (argv[i][0] == '/')
204
{
205
if (argv[i][1] == '\0')
206
{
207
findstr_error_wprintf(STRING_BAD_COMMAND_LINE);
208
return 2;
209
}
210
211
j = 1;
212
while (argv[i][j] != '\0')
213
{
214
switch (argv[i][j])
215
{
216
case '?':
217
findstr_message(STRING_USAGE);
218
ret = 0;
219
goto done;
220
case 'C':
221
case 'c':
222
if (argv[i][j + 1] == ':')
223
{
224
ptr = argv[i] + j + 2;
225
if (*ptr == '"')
226
ptr++;
227
228
string = ptr;
229
while (*ptr != '"' && *ptr != '\0' )
230
ptr++;
231
*ptr = '\0';
232
j = ptr - argv[i] - 1;
233
add_string(&string_head, string);
234
has_string = TRUE;
235
}
236
break;
237
case 'I':
238
case 'i':
239
case_sensitive = FALSE;
240
break;
241
case 'L':
242
case 'l':
243
regular_expression = FALSE;
244
break;
245
case 'R':
246
case 'r':
247
regular_expression = TRUE;
248
break;
249
default:
250
findstr_error_wprintf(STRING_IGNORED, argv[i][j]);
251
break;
252
}
253
254
j++;
255
}
256
}
257
else if (!has_string)
258
{
259
string = wcstok(argv[i], L" ", &buffer);
260
if (string)
261
{
262
add_string(&string_head, string);
263
has_string = TRUE;
264
}
265
while ((string = wcstok(NULL, L" ", &buffer)))
266
add_string(&string_head, string);
267
}
268
else
269
{
270
if (!add_file(&file_head, argv[i]))
271
goto done;
272
has_file = TRUE;
273
}
274
}
275
276
if (!has_string)
277
{
278
findstr_error_wprintf(STRING_BAD_COMMAND_LINE);
279
ret = 2;
280
goto done;
281
}
282
283
if (!has_file)
284
add_file(&file_head, NULL);
285
286
_setmode(_fileno(stdout), _O_BINARY);
287
288
current_file = file_head;
289
while (current_file)
290
{
291
while (fgets(line, ARRAY_SIZE(line), current_file->file))
292
{
293
current_string = string_head;
294
while (current_string)
295
{
296
BOOL match;
297
298
if (regular_expression)
299
match = match_regexp(line, current_string->string, case_sensitive);
300
else
301
match = match_substring(line, current_string->string, case_sensitive);
302
if (match)
303
{
304
printf("%s",line);
305
if (current_file->file == stdin && line[0] && line[strlen(line) - 1] != '\n')
306
printf("\r\n");
307
ret = 0;
308
}
309
current_string = current_string->next;
310
}
311
}
312
current_file = current_file->next;
313
}
314
315
done:
316
current_file = file_head;
317
while (current_file)
318
{
319
next_file = current_file->next;
320
if (current_file->file != stdin)
321
fclose(current_file->file);
322
free(current_file);
323
current_file = next_file;
324
}
325
326
current_string = string_head;
327
while (current_string)
328
{
329
next_string = current_string->next;
330
free(current_string);
331
current_string = next_string;
332
}
333
return ret;
334
}
335
336