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