Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/programs/fc/fc.c
4388 views
1
/*
2
* Copyright 2018 Fabian Maurer
3
* Copyright 2024 Hans Leidekker 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 <stdarg.h>
21
#include <unistd.h>
22
23
#include <windef.h>
24
#include <winbase.h>
25
#include <winnls.h>
26
#include <consoleapi.h>
27
28
#include "wine/debug.h"
29
#include "wine/list.h"
30
31
WINE_DEFAULT_DEBUG_CHANNEL(fc);
32
33
static BOOL option_case_insensitive;
34
35
enum section_type
36
{
37
SECTION_TYPE_COPY,
38
SECTION_TYPE_DELETE,
39
SECTION_TYPE_INSERT
40
};
41
42
struct section
43
{
44
struct list entry;
45
enum section_type type;
46
unsigned int start; /* index in line array */
47
unsigned int len; /* number of lines */
48
};
49
static struct list sections = LIST_INIT( sections );
50
51
struct common_subseq
52
{
53
unsigned int pos_first; /* position in first file */
54
unsigned int pos_second;
55
unsigned int len;
56
};
57
58
struct line
59
{
60
const char *start;
61
unsigned int len;
62
};
63
64
struct lines
65
{
66
unsigned int count;
67
unsigned int capacity;
68
struct line *entry;
69
};
70
static struct lines lines1, lines2;
71
72
static void append_line( struct lines *lines, const char *start, unsigned int len )
73
{
74
if (lines->count >= lines->capacity)
75
{
76
lines->capacity = max( 128, lines->capacity * 2 );
77
lines->entry = realloc( lines->entry, lines->capacity * sizeof(*lines->entry) );
78
}
79
lines->entry[lines->count].start = start;
80
lines->entry[lines->count].len = len;
81
lines->count++;
82
}
83
84
static void split_lines( struct lines *lines, const char *data, unsigned int len )
85
{
86
const char *p = data, *q = data;
87
88
while (len)
89
{
90
if (p[0] == '\n' || (p[0] == '\r' && p[1] == '\n'))
91
{
92
append_line( lines, q, p - q );
93
if (p[0] == '\r') { p++; len--; }
94
q = p + 1;
95
}
96
p++; len--;
97
}
98
if (p != q) append_line( lines, q, p - q );
99
}
100
101
static char *data1, *data2;
102
static unsigned int len_data1, len_data2;
103
104
static int equal_lines( const struct line *line1, const struct line *line2 )
105
{
106
if (line1->len != line2->len) return 0;
107
if (option_case_insensitive) return !memicmp( line1->start, line2->start, line1->len );
108
else return !memcmp( line1->start, line2->start, line1->len );
109
}
110
111
/* return the number of equal lines at given ranges */
112
static unsigned int common_length( unsigned int first_pos, unsigned int first_end,
113
unsigned int second_pos, unsigned int second_end )
114
{
115
unsigned int len = 0;
116
117
while (first_pos < first_end && second_pos < second_end)
118
{
119
if (!equal_lines( &lines1.entry[first_pos++], &lines2.entry[second_pos++] )) break;
120
len++;
121
}
122
return len;
123
}
124
125
/* return the longest sequence of equal lines between given ranges */
126
static int longest_common_subsequence( unsigned int first_start, unsigned int first_end,
127
unsigned int second_start, unsigned int second_end,
128
struct common_subseq *result )
129
{
130
unsigned int i, j;
131
int ret = 0;
132
133
memset( result, 0, sizeof(*result) );
134
135
for (i = first_start; i < first_end; i++)
136
{
137
for (j = second_start; j < second_end; j++)
138
{
139
unsigned int len = common_length( i, first_end, j, second_end );
140
if (len > result->len)
141
{
142
result->len = len;
143
result->pos_first = i;
144
result->pos_second = j;
145
ret = 1;
146
}
147
}
148
}
149
return ret;
150
}
151
152
static struct section *alloc_section( enum section_type type, unsigned int start, unsigned int len )
153
{
154
struct section *ret = malloc( sizeof(*ret) );
155
156
ret->type = type;
157
ret->start = start;
158
ret->len = len;
159
return ret;
160
}
161
162
static unsigned int non_matching_lines;
163
164
static void diff( unsigned int first_start, unsigned int first_end,
165
unsigned int second_start, unsigned int second_end )
166
{
167
struct common_subseq subseq;
168
struct section *section;
169
170
if (longest_common_subsequence( first_start, first_end, second_start, second_end, &subseq ))
171
{
172
/* recursively find sections before this one */
173
diff( first_start, subseq.pos_first, second_start, subseq.pos_second );
174
175
section = alloc_section( SECTION_TYPE_COPY, subseq.pos_first, subseq.len );
176
list_add_tail( &sections, &section->entry );
177
178
/* recursively find sections after this one */
179
diff( subseq.pos_first + subseq.len, first_end, subseq.pos_second + subseq.len, second_end );
180
return;
181
}
182
183
if (first_start < first_end)
184
{
185
section = alloc_section( SECTION_TYPE_DELETE, first_start, first_end - first_start );
186
list_add_tail( &sections, &section->entry );
187
non_matching_lines++;
188
}
189
190
if (second_start < second_end)
191
{
192
section = alloc_section( SECTION_TYPE_INSERT, second_start, second_end - second_start );
193
list_add_tail( &sections, &section->entry );
194
non_matching_lines++;
195
}
196
}
197
198
static struct section *find_context_before( struct section *section )
199
{
200
struct section *cursor = section;
201
202
while ((cursor = LIST_ENTRY( list_prev(&sections, &cursor->entry), struct section, entry )))
203
{
204
if (cursor->type == SECTION_TYPE_COPY) return cursor;
205
}
206
return NULL;
207
}
208
209
static struct section *find_context_after( struct section *section )
210
{
211
struct section *cursor = section;
212
213
while ((cursor = LIST_ENTRY( list_next(&sections, &cursor->entry), struct section, entry )))
214
{
215
if (cursor->type == SECTION_TYPE_COPY) return cursor;
216
}
217
return NULL;
218
}
219
220
static void output_stringW( const WCHAR *str, int len )
221
{
222
DWORD count;
223
int lenA;
224
char *strA;
225
226
if (len < 0) len = wcslen( str );
227
if (WriteConsoleW( GetStdHandle(STD_OUTPUT_HANDLE), str, len, &count, NULL )) return;
228
229
lenA = WideCharToMultiByte( GetOEMCP(), 0, str, len, NULL, 0, NULL, NULL );
230
if ((strA = malloc( lenA )))
231
{
232
WideCharToMultiByte( GetOEMCP(), 0, str, len, strA, lenA, NULL, NULL );
233
WriteFile( GetStdHandle(STD_OUTPUT_HANDLE), strA, lenA, &count, FALSE );
234
free( strA );
235
}
236
}
237
238
static void output_stringA( const char *str, int len )
239
{
240
DWORD count;
241
242
if (len < 0) len = strlen( str );
243
if (WriteConsoleA( GetStdHandle(STD_OUTPUT_HANDLE), str, len, &count, NULL )) return;
244
WriteFile( GetStdHandle(STD_OUTPUT_HANDLE), str, len, &count, FALSE );
245
}
246
247
static void output_header( const WCHAR *filename )
248
{
249
output_stringW( L"***** ", 6 );
250
output_stringW( filename, -1 );
251
output_stringW( L"\r\n", 2 );
252
}
253
254
static void output_line( const struct lines *lines, unsigned int index )
255
{
256
output_stringA( lines->entry[index].start, lines->entry[index].len );
257
output_stringA( "\r\n", 2 );
258
}
259
260
static void output_context( unsigned int line )
261
{
262
if (lines1.count < 2 || lines2.count < 2) return;
263
output_line( &lines1, line );
264
}
265
266
static char no_data[] = "";
267
268
static char *map_file( HANDLE handle, unsigned int *size )
269
{
270
HANDLE mapping;
271
char *ret;
272
273
if (!(*size = GetFileSize( handle, NULL ))) return no_data;
274
if (!(mapping = CreateFileMappingW( handle, NULL, PAGE_READONLY, 0, 0, NULL ))) return NULL;
275
ret = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 0 );
276
CloseHandle( mapping );
277
return ret;
278
}
279
280
static int compare_files( const WCHAR *filename1, const WCHAR *filename2 )
281
{
282
HANDLE handle1, handle2;
283
struct section *section;
284
unsigned int i;
285
int ret = 2;
286
287
handle1 = CreateFileW( filename1, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
288
if (handle1 == INVALID_HANDLE_VALUE) return 2;
289
290
handle2 = CreateFileW( filename2, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
291
if (handle2 == INVALID_HANDLE_VALUE)
292
{
293
CloseHandle( handle2 );
294
return 2;
295
}
296
297
if (!(data1 = map_file( handle1, &len_data1 ))) goto done;
298
if (!(data2 = map_file( handle2, &len_data2 ))) goto done;
299
300
split_lines( &lines1, data1, len_data1 );
301
split_lines( &lines2, data2, len_data2 );
302
303
diff( 0, lines1.count, 0, lines2.count );
304
305
output_stringW( L"Comparing files ", 16 );
306
output_stringW( filename1, -1 );
307
output_stringW( L" and ", 5 );
308
output_stringW( filename2, -1 );
309
output_stringW( L"\r\n", 2 );
310
311
if (!non_matching_lines)
312
{
313
output_stringW( L"FC: no differences encountered\r\n\r\n", -1 );
314
ret = 0;
315
goto done;
316
}
317
318
LIST_FOR_EACH_ENTRY( section, &sections, struct section, entry )
319
{
320
struct section *ctx_before = find_context_before( section ), *ctx_after = find_context_after( section );
321
322
if (section->type == SECTION_TYPE_DELETE)
323
{
324
struct section *next_section = LIST_ENTRY( list_next(&sections, &section->entry), struct section, entry );
325
326
output_header( filename1 );
327
if (ctx_before) output_context( ctx_before->start + ctx_before->len - 1 );
328
for (i = 0; i < section->len; i++) output_line( &lines1, section->start + i );
329
if (ctx_after) output_context( ctx_after->start );
330
331
if (next_section && next_section->type != SECTION_TYPE_INSERT)
332
{
333
output_header( filename2 );
334
if (ctx_before) output_context( ctx_before->start + ctx_before->len - 1 );
335
if (ctx_after) output_context( ctx_after->start );
336
}
337
else if (!next_section)
338
{
339
output_header( filename2 );
340
output_stringW( L"*****\r\n\r\n", 9 );
341
}
342
}
343
else if (section->type == SECTION_TYPE_INSERT)
344
{
345
struct section *prev_section = LIST_ENTRY( list_prev(&sections, &section->entry), struct section, entry );
346
347
if (prev_section && prev_section->type != SECTION_TYPE_DELETE)
348
{
349
output_header( filename1 );
350
if (ctx_before) output_context( ctx_before->start + ctx_before->len - 1 );
351
if (ctx_after) output_context( ctx_after->start );
352
}
353
else if (!prev_section) output_header( filename1 );
354
355
output_header( filename2 );
356
if (ctx_before) output_context( ctx_before->start + ctx_before->len - 1 );
357
for (i = 0; i < section->len; i++) output_line( &lines2, section->start + i );
358
if (ctx_after) output_context( ctx_after->start );
359
output_stringW( L"*****\r\n\r\n", 9 );
360
}
361
}
362
363
ret = 1;
364
365
done:
366
if (data1 != no_data) UnmapViewOfFile( data1 );
367
if (data2 != no_data) UnmapViewOfFile( data2 );
368
CloseHandle( handle1 );
369
CloseHandle( handle2 );
370
return ret;
371
}
372
373
int __cdecl wmain(int argc, WCHAR *argv[])
374
{
375
BOOL unsupported = FALSE;
376
const WCHAR *filename1 = NULL, *filename2 = NULL;
377
int i;
378
379
for (i = 1; i < argc; i++)
380
{
381
if (argv[i][0] == '/')
382
{
383
if (!wcsicmp( argv[i] + 1, L"c" )) option_case_insensitive = TRUE;
384
else
385
{
386
FIXME( "option %s not supported\n", debugstr_w(argv[i]) );
387
unsupported = TRUE;
388
}
389
}
390
else if (!filename1) filename1 = argv[i];
391
else if (!filename2) filename2 = argv[i];
392
else
393
{
394
wprintf( L"FC: Wrong number of files %s\n", argv[i] );
395
return 2;
396
}
397
}
398
399
if (unsupported) return 2;
400
401
if (!filename1 || !filename2)
402
{
403
wprintf( L"FC: Wrong number of files\n" );
404
return 2;
405
}
406
407
if (wcschr( filename1, '?' ) || wcschr( filename1, '*' ) || wcschr( filename2, '?' ) || wcschr( filename2, '*' ))
408
{
409
FIXME( "wildcards not supported\n" );
410
return 2;
411
}
412
413
return compare_files( filename1, filename2 );
414
}
415
416