1 /*
2 Numdiff - compare putatively similar files,
3 ignoring small numeric differences
4 Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Ivano Primi <ivprimi@libero.it>
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include<stdio.h>
21 #include<stdlib.h>
22 #include<string.h>
23 #include<ctype.h>
24 #include"numdiff.h"
25 #include"linesplit.h"
26
27 /* See io.c */
28 extern char* read_line (FILE* pf, int* errcode);
29 extern void print_lines (const char* line1, const char* line2,
30 unsigned long lineno1, unsigned long lineno2,
31 int delimiter_only);
32 extern void print_fields (const char* field1, const char* field2,
33 size_t l1, size_t l2,
34 unsigned long lineno1, unsigned long lineno2,
35 unsigned long fieldno1, unsigned long fieldno2);
36 extern void print_errors (Real abserr, Real relerr, int human_readable_format);
37 extern void print_separator (void);
38
39 static
field2cx(const char * field,size_t length,char ** tail,int iscale,const struct numfmt * pnf,Complex * pz)40 void field2cx (const char* field, size_t length, char** tail,
41 int iscale, const struct numfmt* pnf, Complex* pz)
42 {
43 char* ptr = (char*) field;
44 char ch;
45
46 ch = ptr[length];
47 ptr[length] = '\0';
48 str2C (ptr, tail, iscale, pnf, pz);
49 ptr[length] = ch;
50 }
51
52 /*
53 Be careful ! strNcmp() and strNcasecmp() are only used
54 when n <= strlen (s) == strlen (t).
55 */
56
strNcmp(const char * s,const char * t,size_t n)57 static int strNcmp (const char* s, const char* t, size_t n)
58 {
59 const char *p, *q;
60
61 for (p = s, q = t; p < s + n && *p == *q; p++, q++);
62 return p < s + n;
63 }
64
strNcasecmp(const char * s,const char * t,size_t n)65 static int strNcasecmp (const char* s, const char* t, size_t n)
66 {
67 const char *p, *q;
68
69 for (p = s, q = t; p < s + n && TOLOWER(*p) == TOLOWER(*q); p++, q++);
70 return p < s + n;
71 }
72
73 static
cmp_fields(const char * field1,const char * field2,unsigned long fieldno1,unsigned long fieldno2,size_t l1,size_t l2,const argslist * argl,statlist * statres,Real * abserr,Real * relerr,int * Labserr_exceeded,int * Rabserr_exceeded)74 int cmp_fields (const char* field1, const char* field2,
75 unsigned long fieldno1, unsigned long fieldno2,
76 size_t l1, size_t l2, const argslist* argl,
77 statlist* statres, Real* abserr, Real* relerr,
78 int* Labserr_exceeded, int* Rabserr_exceeded)
79 {
80 char *tail1, *tail2;
81 Complex z1, z2;
82
83 field2cx (field1, l1, &tail1, argl->iscale, &argl->nf1, &z1);
84 field2cx (field2, l2, &tail2, argl->iscale, &argl->nf2, &z2);
85
86 #ifdef __DEBUG__
87 fprintf (stderr, "l1 = %zu, tail1 - field1 = %zu\n", l1, tail1 - field1);
88 fprintf (stderr, "l2 = %zu, tail2 - field2 = %zu\n", l2, tail2 - field2);
89 #endif
90
91 if (tail1 - field1 == l1 && tail2 - field2 == l2)
92 {
93 /*
94 This second test manages the options -P
95 and -N . If neither of them has been set,
96 then the condition is always TRUE.
97 */
98 if ( (smart_cmp (&z2, &z1, argl->flag)) )
99 {
100 int exit_code;
101
102 /* Numeric comparison */
103 Complex w;
104 Real x1, x2;
105 int iscale = argl->iscale;
106
107 initC (&w);
108 initR (&x1);
109 initR (&x2);
110 Csub (z1, z2, &w, iscale);
111 Cabs (w, abserr, iscale);
112 #ifdef _MPA_DEBUG
113 fputs ("*** MPA Debug output\n", stderr);
114 fputs ("1st number= ( ", stderr);
115 debug_printno (z1.re, 20);
116 fputs (", ", stderr);
117 debug_printno (z1.im, 20);
118 fputs (" )\n", stderr);
119 fputs ("2nd number= ( ", stderr);
120 debug_printno (z2.re, 20);
121 fputs (", ", stderr);
122 debug_printno (z2.im, 20);
123 fputs (" )\n", stderr);
124 fputs ("abs. err= ", stderr);
125 debug_printno (*abserr, 20);
126 fputs ("\n*** *** ***\n", stderr);
127 #endif
128 if (argl->relerr_formula == CLASSIC_FORMULA)
129 {
130 Cabs (z1, &x1, iscale);
131 Cabs (z2, &x2, iscale);
132 if ( cmp (x1, x2) > 0 )
133 copyR (&x1, x2);
134 if ( (is0(x1)) )
135 (is0(*abserr)) ? copyR(relerr, Zero) : copyR(relerr, Inf);
136 else
137 divide (*abserr, x1, relerr, iscale);
138 }
139 else if (argl->relerr_formula == WR_TO_FIRST_FILE)
140 {
141 Cabs (z1, &x1, iscale);
142 if ( (is0(x1)) )
143 (is0(*abserr)) ? copyR(relerr, Zero) : copyR(relerr, Inf);
144 else
145 divide (*abserr, x1, relerr, iscale);
146 }
147 else if (argl->relerr_formula == WR_TO_SECOND_FILE)
148 {
149 Cabs (z2, &x2, iscale);
150 if ( (is0(x2)) )
151 (is0(*abserr)) ? copyR(relerr, Zero) : copyR(relerr, Inf);
152 else
153 divide (*abserr, x2, relerr, iscale);
154 }
155 delR (&x2);
156 delR (&x1);
157 delC (&w);
158 delC (&z2);
159 delC (&z1);
160
161 if (getBitAtPosition (&argl->optmask, _2_MASK) == BIT_OFF)
162 exit_code =
163 thrlist_cmp (*relerr, argl->maxrelerr, fieldno1, fieldno2) > 0 &&
164 thrlist_cmp (*abserr, argl->maxabserr, fieldno1, fieldno2) > 0 ? 2:0;
165 else
166 exit_code =
167 thrlist_cmp (*relerr, argl->maxrelerr, fieldno1, fieldno2) > 0 ||
168 thrlist_cmp (*abserr, argl->maxabserr, fieldno1, fieldno2) > 0 ? 2:0;
169 if (getBitAtPosition (&argl->optmask, _SS_MASK) == BIT_ON)
170 {
171 statres->Nentries++;
172 initR (&x1);
173 initR (&x2);
174 /* To compute the 1-norm of all errors */
175 add (*abserr, statres->N1abserr, &x1, iscale);
176 copyR (&statres->N1abserr, x1);
177 /* To compute the 2-norm of all errors */
178 square (*abserr, &x1, iscale);
179 add (x1, statres->N2abserr, &x2, iscale);
180 copyR (&statres->N2abserr, x2);
181 if ((exit_code))
182 {
183 int test;
184
185 statres->Ndisperr++;
186 /* To compute the 1-norm of the displayed errors */
187 add (*abserr, statres->N1disperr, &x1, iscale);
188 copyR (&statres->N1disperr, x1);
189 /* To compute the 2-norm of the displayed errors */
190 square (*abserr, &x1, iscale);
191 add (x1, statres->N2disperr, &x2, iscale);
192 copyR (&statres->N2disperr, x2);
193 if ((test = cmp (*abserr, statres->Labserr)) > 0)
194 {
195 copyR (&statres->Labserr, *abserr);
196 copyR (&statres->Crelerr, *relerr);
197 *Labserr_exceeded = 1;
198 }
199 else if (test == 0 && cmp (*relerr, statres->Crelerr) > 0)
200 {
201 copyR (&statres->Crelerr, *relerr);
202 *Labserr_exceeded = 1;
203 }
204 if ((test = cmp (*relerr, statres->Lrelerr)) > 0)
205 {
206 copyR (&statres->Lrelerr, *relerr);
207 copyR (&statres->Cabserr, *abserr);
208 *Rabserr_exceeded = 1;
209 }
210 else if (test == 0 && cmp (*abserr, statres->Cabserr) > 0)
211 {
212 copyR (&statres->Cabserr, *abserr);
213 *Rabserr_exceeded = 1;
214 }
215 }
216 delR (&x2);
217 delR (&x1);
218 }
219 return exit_code;
220 }
221 else
222 return 0;
223 }
224 else
225 {
226 delC (&z2);
227 delC (&z1);
228 /* Byte by byte comparison */
229 if (getBitAtPosition (&argl->optmask, _SI_MASK) == BIT_ON)
230 return (l1 != l2 || strNcasecmp (field1, field2, l1) != 0 ? 1 : 0);
231 else
232 return (l1 != l2 || strNcmp (field1, field2, l1) != 0 ? 1 : 0);
233 }
234 }
235
236 static
cmp_lines(const char * line1,const char * line2,unsigned long lineno1,unsigned long lineno2,const char ** ifs1,const char ** ifs2,const argslist * argl,statlist * statres)237 int cmp_lines (const char* line1, const char* line2,
238 unsigned long lineno1, unsigned long lineno2,
239 const char** ifs1, const char** ifs2,
240 const argslist* argl, statlist* statres)
241 {
242 const unsigned long fieldno_upper_limit = 8*FIELDMASK_SIZE;
243 int output_mode = argl->output_mode;
244
245 if (!line1 && !line2)
246 return 0;
247 else if (!line1)
248 {
249 if (output_mode >= OUTMODE_NORMAL)
250 print_lines (line1, line2, lineno1, lineno2, 0);
251 else if (output_mode == OUTMODE_RAW)
252 printf ("*:%lu\n", lineno2);
253 return 1;
254 }
255 else if (!line2)
256 {
257 if (output_mode >= OUTMODE_NORMAL)
258 print_lines (line1, line2, lineno1, lineno2, 0);
259 else if (output_mode == OUTMODE_RAW)
260 printf ("%lu:*\n", lineno1);
261 return 1;
262 }
263 else
264 {
265 const char *field1, *field2;
266 char *end_field1, *end_field2;
267 size_t l1, l2;
268 unsigned long fieldno1, fieldno2;
269 Real abserr, relerr;
270 int Labserr_exceeded, Rabserr_exceeded;
271 int rv, lines_differ = 0, _1sttime = 1;
272
273 initR (&abserr);
274 initR (&relerr);
275 field1 = string_spn (line1, ifs1, '\0');
276 field2 = string_spn (line2, ifs2, '\0');
277 fieldno1 = fieldno2 = 0;
278
279 while (*field1 != '\0' && *field2 != '\0')
280 {
281 /*
282 Ignore the fields selected through the option -X 1:
283 */
284 while ( *field1 != '\0' && fieldno1 < fieldno_upper_limit &&
285 (argl->ghostmask1[fieldno1 >> 3] & 0x80 >> (fieldno1 & 0x7)) )
286 {
287 end_field1 = string_cspn (field1, ifs1, '\0');
288 field1 = string_spn (end_field1, ifs1, '\0');
289 fieldno1++;
290 }
291 if ( fieldno1 >= fieldno_upper_limit )
292 {
293 printf (_("@ Line %lu in file \"%s\"\n contains too many fields to be properly processed!\n"),
294 lineno1, argl->file1);
295 exit (EXIT_TROUBLE);
296 }
297 /*
298 Ignore the fields selected through the option -X 2:
299 */
300 while ( *field2 != '\0' && fieldno2 < fieldno_upper_limit &&
301 (argl->ghostmask2[fieldno2 >> 3] & 0x80 >> (fieldno2 & 0x7)) )
302 {
303 end_field2 = string_cspn (field2, ifs2, '\0');
304 field2 = string_spn (end_field2, ifs2, '\0');
305 fieldno2++;
306 }
307 if ( fieldno2 >= fieldno_upper_limit )
308 {
309 printf (_("@ Line %lu in file \"%s\"\n contains too many fields to be properly processed!\n"),
310 lineno2, argl->file2);
311 exit (EXIT_TROUBLE);
312 }
313 if (*field1 != '\0' && *field2 != '\0')
314 {
315 end_field1 = string_cspn (field1, ifs1, '\0');
316 end_field2 = string_cspn (field2, ifs2, '\0');
317 l1 = end_field1 - field1;
318 l2 = end_field2 - field2;
319 Labserr_exceeded = Rabserr_exceeded = 0;
320 rv = cmp_fields (field1, field2, fieldno1, fieldno2, l1, l2, argl, statres,
321 &abserr, &relerr, &Labserr_exceeded, &Rabserr_exceeded);
322 if ((Labserr_exceeded))
323 {
324 statres->Labserr_location.lineno1 = lineno1;
325 statres->Labserr_location.fieldno1 = fieldno1;
326 statres->Labserr_location.lineno2 = lineno2;
327 statres->Labserr_location.fieldno2 = fieldno2;
328 }
329 if ((Rabserr_exceeded))
330 {
331 statres->Rabserr_location.lineno1 = lineno1;
332 statres->Rabserr_location.fieldno1 = fieldno1;
333 statres->Rabserr_location.lineno2 = lineno2;
334 statres->Rabserr_location.fieldno2 = fieldno2;
335 }
336 if ( rv >= 1 )
337 {
338 if ( (output_mode > OUTMODE_QUIET) &&
339 !(rv == 1 &&
340 getBitAtPosition (&argl->optmask, _SE_MASK) ==
341 BIT_ON) &&
342 !(rv == 2 &&
343 getBitAtPosition (&argl->optmask, _SU_MASK) ==
344 BIT_ON) )
345 {
346 if ((_1sttime))
347 {
348 print_lines (line1, line2, lineno1, lineno2,
349 output_mode != OUTMODE_VERBOSE &&
350 output_mode != OUTMODE_COINCISE);
351 _1sttime = 0;
352 }
353 print_fields (field1, field2, l1, l2, lineno1, lineno2, fieldno1, fieldno2);
354 if ( rv == 2 )
355 print_errors (abserr, relerr, 1);
356 else
357 print_separator ();
358 }
359 else if ( (output_mode == OUTMODE_RAW) &&
360 !(rv == 1 &&
361 getBitAtPosition (&argl->optmask, _SE_MASK) ==
362 BIT_ON) &&
363 !(rv == 2 &&
364 getBitAtPosition (&argl->optmask, _SU_MASK) ==
365 BIT_ON) )
366 {
367 printf ("%lu:%lu:%lu:%lu:", lineno1, lineno2, fieldno1+1, fieldno2+1);
368 if (rv == 1)
369 {
370 fputs ("*:*:", stdout);
371 }
372 else /* rv == 2 */
373 {
374 print_errors (abserr, relerr, 0);
375 }
376 printf ("%.*s ==> ", l1, field1);
377 printf ("%.*s\n", l2, field2);
378 }
379 lines_differ = 1;
380 }
381 field1 = string_spn (end_field1, ifs1, '\0');
382 fieldno1++;
383 field2 = string_spn (end_field2, ifs2, '\0');
384 fieldno2++;
385 }
386 } /* end while (*field1 != '\0' && *field2 != '\0') */
387
388 delR (&abserr);
389 delR (&relerr);
390
391 /*
392 Ignore the fields selected through the option -X 1:
393 */
394 while ( *field1 != '\0' && fieldno1 < fieldno_upper_limit &&
395 (argl->ghostmask1[fieldno1 >> 3] & 0x80 >> (fieldno1 & 0x7)) )
396 {
397 end_field1 = string_cspn (field1, ifs1, '\0');
398 field1 = string_spn (end_field1, ifs1, '\0');
399 fieldno1++;
400 }
401 if ( fieldno1 >= fieldno_upper_limit )
402 {
403 printf (_("@ Line %lu in file \"%s\"\n contains too many fields to be properly processed!\n"),
404 lineno1, argl->file1);
405 exit (EXIT_TROUBLE);
406 }
407 /*
408 Ignore the fields selected through the option -X 2:
409 */
410 while ( *field2 != '\0' && fieldno2 < fieldno_upper_limit &&
411 (argl->ghostmask2[fieldno2 >> 3] & 0x80 >> (fieldno2 & 0x7)) )
412 {
413 end_field2 = string_cspn (field2, ifs2, '\0');
414 field2 = string_spn (end_field2, ifs2, '\0');
415 fieldno2++;
416 }
417 if ( fieldno2 >= fieldno_upper_limit )
418 {
419 printf (_("@ Line %lu in file \"%s\"\n contains too many fields to be properly processed!\n"),
420 lineno2, argl->file2);
421 exit (EXIT_TROUBLE);
422 }
423
424 if (*field1 != '\0')
425 {
426 if (output_mode >= OUTMODE_NORMAL)
427 {
428 if ((_1sttime))
429 {
430 print_lines (line1, line2, lineno1, lineno2,
431 output_mode < OUTMODE_VERBOSE);
432 _1sttime = 0;
433 }
434 print_fields (field1, field2, 0, 0, lineno1, lineno2, fieldno1, fieldno2);
435 printf (_("@ Line %lu in file \"%s\" is shorter than expected!\n"),
436 lineno2, argl->file2);
437 }
438 else if (output_mode == OUTMODE_RAW)
439 {
440 printf ("%lu:%lu:%lu:*:%s", lineno1, lineno2, fieldno1+1, field1);
441 }
442 return 1;
443 }
444 else if (*field2 != '\0')
445 {
446 if (output_mode >= OUTMODE_NORMAL)
447 {
448 if ((_1sttime))
449 {
450 print_lines (line1, line2, lineno1, lineno2,
451 output_mode < OUTMODE_VERBOSE);
452 _1sttime = 0;
453 }
454 print_fields (field1, field2, 0, 0, lineno1, lineno2, fieldno1, fieldno2);
455 printf (_("@ Line %lu in file \"%s\" is shorter than expected!\n"),
456 lineno1, argl->file1);
457 }
458 else if (output_mode == OUTMODE_RAW)
459 {
460 printf ("%lu:%lu:*:%lu:%s", lineno1, lineno2, fieldno2+1, field2);
461 }
462 return 1;
463 }
464 else
465 return lines_differ;
466 }
467 }
468
469 extern char** def_ifs;
470
cmp_files(FILE * pf1,FILE * pf2,const argslist * argl,statlist * statres)471 int cmp_files (FILE* pf1, FILE* pf2, const argslist* argl,
472 statlist* statres)
473 {
474 char *line1, *line2, **ifs1, **ifs2;
475 int err1, err2, files_differ = 0;
476 unsigned long lineno1 = 1, lineno2 = 1;
477 size_t n;
478 unsigned short i;
479 unsigned char byte, elem;
480 flg_array table = copy_of_intflagtab();
481
482 ifs1 = (!argl->ifs1) ? def_ifs : argl->ifs1;
483 ifs2 = (!argl->ifs2) ? def_ifs : argl->ifs2;
484 if ( (table.ptr) )
485 {
486 /*
487 Filter on
488 */
489 for (n = 0, err1 = err2 = OK; n < table.len; n += 4U)
490 {
491 byte = table.ptr[n/4U];
492 for (i = 0; i < 4U; i++)
493 {
494 elem = (byte & 0x03 << 2*i) >> 2 * i;
495 switch (elem)
496 {
497 case 1:
498 line1 = read_line (pf1, &err1);
499 line2 = NULL;
500 break;
501 case 2:
502 line1 = NULL;
503 line2 = read_line (pf2, &err2);
504 break;
505 case 3:
506 line1 = read_line (pf1, &err1);
507 line2 = read_line (pf2, &err2);
508 break;
509 case 0:
510 goto catch_error;
511 }
512 if ( (cmp_lines (line1, line2,
513 lineno1, lineno2,
514 (const char**) ifs1, (const char**) ifs2,
515 argl, statres)) )
516 {
517 files_differ = 1;
518 if (argl->output_mode == OUTMODE_OVERVIEW)
519 print_1overview_line (line1, 1, line2);
520 }
521 else
522 {
523 if (argl->output_mode == OUTMODE_OVERVIEW && !suppress_common_lines)
524 print_1overview_line (line1, 0, line2);
525 }
526 if ((line1))
527 {
528 lineno1++;
529 free ((void*)line1);
530 }
531 if ((line2))
532 {
533 lineno2++;
534 free ((void*)line2);
535 }
536 if (err1 == OUT_OF_MEMORY ||
537 err2 == OUT_OF_MEMORY ||
538 err1 == FILE_IS_BINARY ||
539 err2 == FILE_IS_BINARY ||
540 err1 == READING_ERROR ||
541 err2 == READING_ERROR)
542 goto catch_error;
543 }
544 }
545 } /* end of `if ( (table.ptr) )'*/
546 else
547 {
548 /*
549 Filter off
550 */
551 do
552 {
553 line1 = read_line (pf1, &err1);
554 line2 = read_line (pf2, &err2);
555 if ( (cmp_lines (line1, line2,
556 lineno1, lineno2,
557 (const char**) ifs1, (const char**) ifs2,
558 argl, statres)) )
559 {
560 files_differ = 1;
561 if (argl->output_mode == OUTMODE_OVERVIEW)
562 print_1overview_line (line1, 1, line2);
563 }
564 else
565 {
566 if (argl->output_mode == OUTMODE_OVERVIEW && !suppress_common_lines)
567 print_1overview_line (line1, 0, line2);
568 }
569 if ((line1))
570 free ((void*)line1);
571 if ((line2))
572 free ((void*)line2);
573 lineno1++, lineno2++;
574 } while (err1 == OK && err2 == OK);
575 }
576
577 catch_error:
578 /*
579 * If we arrive here, then
580 *
581 * either ist n == table.len,
582 * or elem == 0,
583 * or either err1 or err2 is different from OK.
584 */
585 fflush (stdout);
586 if ( (table.ptr) )
587 {
588 if (n >= table.len || !elem)
589 return files_differ;
590 }
591 if (err1 == OK)
592 {
593 switch (err2)
594 {
595 case FILE_IS_BINARY:
596 fprintf (stderr, _("\n*** File \"%s\" is binary,\n*** cannot read from it\n"), argl->file2);
597 return FILE_IS_BINARY;
598 case READING_ERROR:
599 fprintf (stderr, _("\n*** Error while reading from file \"%s\"\n"), argl->file2);
600 return READING_ERROR;
601 case OUT_OF_MEMORY:
602 fprintf (stderr, _("\n*** Out of memory while reading from file \"%s\"\n"), argl->file2);
603 return OUT_OF_MEMORY;
604 case LINE_INTERR:
605 if (getc (pf1) == EOF)
606 break;
607 /* default == EOF_REACHED */
608 default:
609 fprintf (stderr, _("\n*** End of file \"%s\" reached while trying to read line %lu.\n"), argl->file2, lineno2-1);
610 fprintf (stderr, _("*** File \"%s\" has more lines than file \"%s\",\n"),
611 argl->file1, argl->file2);
612 fprintf (stderr, _("*** line %lu is the last one read from file \"%s\"\n\n"), lineno1-1, argl->file1);
613 }
614 return files_differ;
615 }
616 else if (err1 == LINE_INTERR)
617 {
618 switch (err2)
619 {
620 case FILE_IS_BINARY:
621 fprintf (stderr, _("\n*** File \"%s\" is binary,\n*** cannot read from it\n"), argl->file2);
622 return FILE_IS_BINARY;
623 case READING_ERROR:
624 fprintf (stderr, _("\n*** Error while reading from file \"%s\"\n"), argl->file2);
625 return READING_ERROR;
626 case OUT_OF_MEMORY:
627 fprintf (stderr, _("\n*** Out of memory while reading from file \"%s\"\n"), argl->file2);
628 return OUT_OF_MEMORY;
629 case OK:
630 if (getc (pf2) != EOF)
631 {
632 fprintf (stderr, _("\n*** End of file \"%s\" reached while trying to read line %lu.\n"), argl->file1, lineno1-1);
633 fprintf (stderr, _("*** File \"%s\" has more lines than file \"%s\",\n"),
634 argl->file2, argl->file1);
635 fprintf (stderr, _("*** line %lu is the last one read from file \"%s\"\n\n"), lineno2-1, argl->file2);
636 }
637 break;
638 case EOF_REACHED:
639 fprintf (stderr, _("\n*** End of file \"%s\" reached while trying to read line %lu.\n"), argl->file2, lineno2-1);
640 fprintf (stderr, _("*** File \"%s\" has more lines than file \"%s\",\n"),
641 argl->file1, argl->file2);
642 fprintf (stderr, _("*** line %lu is the last one read from file \"%s\"\n\n"), lineno1-1, argl->file1);
643 /*
644 No particular action to do if err2 == LINE_INTERR
645 */
646 }
647 return files_differ;
648 }
649 else if (err1 == EOF_REACHED)
650 {
651 switch (err2)
652 {
653 case FILE_IS_BINARY:
654 fprintf (stderr, _("\n*** File \"%s\" is binary,\n*** cannot read from it\n"), argl->file2);
655 return FILE_IS_BINARY;
656 case READING_ERROR:
657 fprintf (stderr, _("\n*** Error while reading from file \"%s\"\n"), argl->file2);
658 return READING_ERROR;
659 case OUT_OF_MEMORY:
660 fprintf (stderr, _("\n*** Out of memory while reading from file \"%s\"\n"), argl->file2);
661 return OUT_OF_MEMORY;
662 case OK:
663 case LINE_INTERR:
664 fprintf (stderr, _("\n*** End of file \"%s\" reached while trying to read line %lu.\n"), argl->file1, lineno1-1);
665 fprintf (stderr, _("*** File \"%s\" has more lines than file \"%s\",\n"),
666 argl->file2, argl->file1);
667 fprintf (stderr, _("*** line %lu is the last one read from file \"%s\"\n\n"), lineno2-1, argl->file2);
668 }
669 return files_differ;
670 }
671 else if (err1 == FILE_IS_BINARY)
672 {
673 fprintf (stderr, _("\n*** File \"%s\" is binary,\n*** cannot read from it\n"), argl->file1);
674 return FILE_IS_BINARY;
675 }
676 else if (err1 == READING_ERROR)
677 {
678 fprintf (stderr, _("\n*** Error while reading from file \"%s\"\n"), argl->file1);
679 return READING_ERROR;
680 }
681 else /* err1 == OUT_OF_MEMORY */
682 {
683 fprintf (stderr, _("\n*** Out of memory while reading from file \"%s\"\n"), argl->file1);
684 return OUT_OF_MEMORY;
685 }
686 }
687