/*
    Numdiff - compare putatively similar files, 
    ignoring small numeric differences
    Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017  Ivano Primi  <ivprimi@libero.it>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include"numdiff.h"
#include"linesplit.h"

/* See io.c */
extern char* read_line (FILE* pf, int* errcode); 
extern void print_lines (const char* line1, const char* line2,
			 unsigned long lineno1, unsigned long lineno2, 
			 int delimiter_only);
extern void print_fields (const char* field1, const char* field2,
			  size_t l1, size_t l2, 
			  unsigned long lineno1, unsigned long lineno2, 
			  unsigned long fieldno1, unsigned long fieldno2);
extern void print_errors (Real abserr, Real relerr, int human_readable_format);
extern void print_separator (void);

static
void field2cx (const char* field, size_t length, char** tail, 
	       int iscale, const struct numfmt* pnf, Complex* pz)
{
  char* ptr = (char*) field;
  char ch;

  ch = ptr[length];
  ptr[length] = '\0';
  str2C (ptr, tail, iscale, pnf, pz);
  ptr[length] = ch;
}

/*
  Be careful ! strNcmp() and strNcasecmp() are only used
  when n <= strlen (s) == strlen (t).
*/

static int strNcmp (const char* s, const char* t, size_t n)
{
  const char *p, *q;

  for (p = s, q = t; p < s + n && *p == *q; p++, q++);
  return p < s + n;
}

static int strNcasecmp (const char* s, const char* t, size_t n)
{
  const char *p, *q;

  for (p = s, q = t; p < s + n && TOLOWER(*p) == TOLOWER(*q); p++, q++);
  return p < s + n;
}

static
int 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)
{
  char *tail1, *tail2;
  Complex z1, z2;
  
  field2cx (field1, l1, &tail1, argl->iscale, &argl->nf1, &z1);
  field2cx (field2, l2, &tail2, argl->iscale, &argl->nf2, &z2);

#ifdef __DEBUG__
  fprintf (stderr, "l1 = %zu, tail1 - field1 = %zu\n", l1, tail1 - field1);
  fprintf (stderr, "l2 = %zu, tail2 - field2 = %zu\n", l2, tail2 - field2);
#endif  

  if (tail1 - field1 == l1 && tail2 - field2 == l2)
    {
      /*
	This second test manages the options -P
	and -N . If neither of them has been set,
	then the condition is always TRUE.
      */
      if ( (smart_cmp (&z2, &z1, argl->flag)) )
	{
	  int exit_code;

	  /* Numeric comparison */
	  Complex w;
	  Real x1, x2;
	  int iscale = argl->iscale;

	  initC (&w);
	  initR (&x1);
	  initR (&x2);
	  Csub (z1, z2, &w, iscale);
	  Cabs (w, abserr, iscale);
#ifdef _MPA_DEBUG
	  fputs ("*** MPA Debug output\n", stderr);
	  fputs ("1st number= ( ", stderr);
	  debug_printno (z1.re, 20);
	  fputs (", ", stderr);
	  debug_printno (z1.im, 20);
	  fputs (" )\n", stderr);
	  fputs ("2nd number= ( ", stderr);
	  debug_printno (z2.re, 20);
	  fputs (", ", stderr);
	  debug_printno (z2.im, 20);
	  fputs (" )\n", stderr);
	  fputs ("abs. err= ", stderr);
	  debug_printno (*abserr, 20);
	  fputs ("\n***     ***     ***\n", stderr);
#endif
	  if (argl->relerr_formula == CLASSIC_FORMULA)
	    {
	      Cabs (z1, &x1, iscale);
	      Cabs (z2, &x2, iscale);
	      if ( cmp (x1, x2) > 0 )
		copyR (&x1, x2);
	      if ( (is0(x1)) )
		(is0(*abserr)) ? copyR(relerr, Zero) : copyR(relerr, Inf);
	      else
		divide (*abserr, x1, relerr, iscale);
	    }
	  else if (argl->relerr_formula == WR_TO_FIRST_FILE)
	    {
	      Cabs (z1, &x1, iscale);
	      if ( (is0(x1)) )
		(is0(*abserr)) ? copyR(relerr, Zero) : copyR(relerr, Inf);
	      else
		divide (*abserr, x1, relerr, iscale);
	    }
	  else if (argl->relerr_formula == WR_TO_SECOND_FILE)
	    {
	      Cabs (z2, &x2, iscale);
	      if ( (is0(x2)) )
		(is0(*abserr)) ? copyR(relerr, Zero) : copyR(relerr, Inf);
	      else
		divide (*abserr, x2, relerr, iscale);
	    }
	  delR (&x2);
	  delR (&x1);
	  delC (&w);
	  delC (&z2);
	  delC (&z1);

	  if (getBitAtPosition (&argl->optmask, _2_MASK) == BIT_OFF)
	    exit_code = 
	      thrlist_cmp (*relerr, argl->maxrelerr, fieldno1, fieldno2) > 0 && 
	      thrlist_cmp (*abserr, argl->maxabserr, fieldno1, fieldno2) > 0 ? 2:0;
	  else
	    exit_code =
	      thrlist_cmp (*relerr, argl->maxrelerr, fieldno1, fieldno2) > 0 || 
	      thrlist_cmp (*abserr, argl->maxabserr, fieldno1, fieldno2) > 0 ? 2:0;
	  if (getBitAtPosition (&argl->optmask, _SS_MASK) == BIT_ON)
	    {
	      statres->Nentries++;
	      initR (&x1);
	      initR (&x2);
	      /* To compute the 1-norm of all errors */
	      add (*abserr, statres->N1abserr, &x1, iscale);
	      copyR (&statres->N1abserr, x1);
	      /* To compute the 2-norm of all errors */
	      square (*abserr, &x1, iscale);
	      add (x1, statres->N2abserr, &x2, iscale);
	      copyR (&statres->N2abserr, x2);
	      if ((exit_code))
		{
		  int test;

		  statres->Ndisperr++;
		  /* To compute the 1-norm of the displayed errors */
		  add (*abserr, statres->N1disperr, &x1, iscale);
		  copyR (&statres->N1disperr, x1);
		  /* To compute the 2-norm of the displayed errors */
		  square (*abserr, &x1, iscale);
		  add (x1, statres->N2disperr, &x2, iscale);
		  copyR (&statres->N2disperr, x2);
		  if ((test = cmp (*abserr, statres->Labserr)) > 0)
		    {
		      copyR (&statres->Labserr, *abserr);
		      copyR (&statres->Crelerr, *relerr);
		      *Labserr_exceeded = 1;
		    }
		  else if (test == 0 && cmp (*relerr, statres->Crelerr) > 0)
		    {
		      copyR (&statres->Crelerr, *relerr);
		      *Labserr_exceeded = 1;
		    }
		  if ((test = cmp (*relerr, statres->Lrelerr)) > 0)
		    {
		      copyR (&statres->Lrelerr, *relerr);
		      copyR (&statres->Cabserr, *abserr);
		      *Rabserr_exceeded = 1;
		    }
		  else if (test == 0 && cmp (*abserr, statres->Cabserr) > 0)
		    {
		      copyR (&statres->Cabserr, *abserr);
		      *Rabserr_exceeded = 1;
		    }
		}
	      delR (&x2);
	      delR (&x1);
	    }
	  return exit_code;
	}
      else
	return 0;
    }
  else
    {
      delC (&z2);
      delC (&z1);
      /* Byte by byte comparison */
      if (getBitAtPosition (&argl->optmask, _SI_MASK) == BIT_ON)
	return (l1 != l2 || strNcasecmp (field1, field2, l1) != 0 ? 1 : 0);
      else
	return (l1 != l2 || strNcmp (field1, field2, l1) != 0 ? 1 : 0);
    }
}

static
int 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)
{
  const unsigned long fieldno_upper_limit = 8*FIELDMASK_SIZE;
  int output_mode = argl->output_mode;
  
  if (!line1 && !line2)
    return 0;
  else if (!line1)
    {
      if (output_mode >= OUTMODE_NORMAL)
	print_lines (line1, line2, lineno1, lineno2, 0);
      else if (output_mode == OUTMODE_RAW)
	printf ("*:%lu\n", lineno2);
      return 1;
    }
  else if (!line2)
    {
      if (output_mode >= OUTMODE_NORMAL)
	print_lines (line1, line2, lineno1, lineno2, 0);
      else if (output_mode == OUTMODE_RAW)
	printf ("%lu:*\n", lineno1);
      return 1;
    }
  else
    {
      const char *field1, *field2;
      char *end_field1, *end_field2;
      size_t l1, l2;
      unsigned long fieldno1, fieldno2;
      Real abserr, relerr;
      int Labserr_exceeded, Rabserr_exceeded;
      int rv, lines_differ = 0, _1sttime = 1;

      initR (&abserr);
      initR (&relerr);
      field1 = string_spn (line1, ifs1, '\0');
      field2 = string_spn (line2, ifs2, '\0');
      fieldno1 = fieldno2 = 0;

      while (*field1 != '\0' && *field2 != '\0')
	{
	  /*
	    Ignore the fields selected through the option -X 1:
	   */
	  while ( *field1 != '\0' && fieldno1 < fieldno_upper_limit && 
		  (argl->ghostmask1[fieldno1 >> 3] & 0x80 >> (fieldno1 & 0x7)) )
	    {
              end_field1 = string_cspn (field1, ifs1, '\0');
	      field1 = string_spn (end_field1, ifs1, '\0');
	      fieldno1++; 
	    }
	  if ( fieldno1 >= fieldno_upper_limit )
	    {
	      printf (_("@ Line %lu in file \"%s\"\n  contains too many fields to be properly processed!\n"),
		      lineno1, argl->file1);
	      exit (EXIT_TROUBLE);
	    }
	  /*
	    Ignore the fields selected through the option -X 2:
	   */
	  while ( *field2 != '\0' && fieldno2 < fieldno_upper_limit && 
		  (argl->ghostmask2[fieldno2 >> 3] & 0x80 >> (fieldno2 & 0x7)) )
	    {
	      end_field2 = string_cspn (field2, ifs2, '\0');
	      field2 = string_spn (end_field2, ifs2, '\0');
	      fieldno2++; 
	    }
	  if ( fieldno2 >= fieldno_upper_limit )
	    {
	      printf (_("@ Line %lu in file \"%s\"\n  contains too many fields to be properly processed!\n"), 
		      lineno2, argl->file2);
	      exit (EXIT_TROUBLE);
	    }
	  if (*field1 != '\0' && *field2 != '\0')
	    {
              end_field1 = string_cspn (field1, ifs1, '\0');
	      end_field2 = string_cspn (field2, ifs2, '\0');
	      l1 = end_field1 - field1;
	      l2 = end_field2 - field2;
	      Labserr_exceeded = Rabserr_exceeded = 0;
	      rv = cmp_fields (field1, field2, fieldno1, fieldno2, l1, l2, argl, statres,
			       &abserr, &relerr, &Labserr_exceeded, &Rabserr_exceeded);
	      if ((Labserr_exceeded))
		{
		  statres->Labserr_location.lineno1 = lineno1;
		  statres->Labserr_location.fieldno1 = fieldno1;
		  statres->Labserr_location.lineno2 = lineno2;
		  statres->Labserr_location.fieldno2 = fieldno2;
		}
	      if ((Rabserr_exceeded))
		{
		  statres->Rabserr_location.lineno1 = lineno1;
		  statres->Rabserr_location.fieldno1 = fieldno1;
		  statres->Rabserr_location.lineno2 = lineno2;
		  statres->Rabserr_location.fieldno2 = fieldno2;
		}	      
	      if ( rv >= 1 )
		{
		  if ( (output_mode > OUTMODE_QUIET) && 
		       !(rv == 1 &&
			 getBitAtPosition (&argl->optmask, _SE_MASK) ==
			 BIT_ON) &&
		       !(rv == 2 &&
			 getBitAtPosition (&argl->optmask, _SU_MASK) ==
			 BIT_ON) )
		    {
		      if ((_1sttime))
			{
			  print_lines (line1, line2, lineno1, lineno2,  
				       output_mode != OUTMODE_VERBOSE &&
				       output_mode != OUTMODE_COINCISE);
			  _1sttime = 0;
			}
		      print_fields (field1, field2, l1, l2, lineno1, lineno2, fieldno1, fieldno2);
		      if ( rv == 2 )
			print_errors (abserr, relerr, 1);
		      else
			print_separator ();
		    }
		  else if ( (output_mode == OUTMODE_RAW) && 
		       !(rv == 1 &&
			 getBitAtPosition (&argl->optmask, _SE_MASK) ==
			 BIT_ON) &&
		       !(rv == 2 &&
			 getBitAtPosition (&argl->optmask, _SU_MASK) ==
			 BIT_ON) )
		    {
		      printf ("%lu:%lu:%lu:%lu:", lineno1, lineno2, fieldno1+1, fieldno2+1);
		      if (rv == 1)
			{
			  fputs ("*:*:", stdout);
			}
		      else /* rv == 2 */
			{
			  print_errors (abserr, relerr, 0);
			}
		      printf ("%.*s ==> ", l1, field1);
		      printf ("%.*s\n", l2, field2); 
		    }
		  lines_differ = 1;
		}
	      field1 = string_spn (end_field1, ifs1, '\0');
	      fieldno1++; 
	      field2 = string_spn (end_field2, ifs2, '\0');
	      fieldno2++; 
	    }
	} /* end  while (*field1 != '\0' && *field2 != '\0') */

      delR (&abserr);
      delR (&relerr);

      /*
	Ignore the fields selected through the option -X 1:
      */
      while ( *field1 != '\0' && fieldno1 < fieldno_upper_limit && 
	      (argl->ghostmask1[fieldno1 >> 3] & 0x80 >> (fieldno1 & 0x7)) )
	{
	  end_field1 = string_cspn (field1, ifs1, '\0');
	  field1 = string_spn (end_field1, ifs1, '\0');
	  fieldno1++; 
	}
      if ( fieldno1 >= fieldno_upper_limit )
	{
	  printf (_("@ Line %lu in file \"%s\"\n  contains too many fields to be properly processed!\n"),
		  lineno1, argl->file1);
	  exit (EXIT_TROUBLE);
	}
      /*
	Ignore the fields selected through the option -X 2:
      */
      while ( *field2 != '\0' && fieldno2 < fieldno_upper_limit && 
	      (argl->ghostmask2[fieldno2 >> 3] & 0x80 >> (fieldno2 & 0x7)) )
	{
	  end_field2 = string_cspn (field2, ifs2, '\0');
	  field2 = string_spn (end_field2, ifs2, '\0');
	  fieldno2++; 
	}
      if ( fieldno2 >= fieldno_upper_limit )
	{
	  printf (_("@ Line %lu in file \"%s\"\n  contains too many fields to be properly processed!\n"),
		  lineno2, argl->file2);
	  exit (EXIT_TROUBLE);
	}
      
      if (*field1 != '\0')
	{
	  if (output_mode >= OUTMODE_NORMAL)
	    {
	      if ((_1sttime))
		{
		  print_lines (line1, line2, lineno1, lineno2, 
			       output_mode < OUTMODE_VERBOSE);
		  _1sttime = 0;
		}
	      print_fields (field1, field2, 0, 0, lineno1, lineno2, fieldno1, fieldno2);
	      printf (_("@ Line %lu in file \"%s\" is shorter than expected!\n"), 
		      lineno2, argl->file2);
	    }
	  else if (output_mode == OUTMODE_RAW)
	    {
	      printf ("%lu:%lu:%lu:*:%s", lineno1, lineno2, fieldno1+1, field1);
	    }
	  return 1;
	}
      else if (*field2 != '\0')
	{
	  if (output_mode >= OUTMODE_NORMAL)
	    {
	      if ((_1sttime))
		{
		  print_lines (line1, line2, lineno1, lineno2,
			       output_mode < OUTMODE_VERBOSE);
		  _1sttime = 0;
		}
	      print_fields (field1, field2, 0, 0, lineno1, lineno2, fieldno1, fieldno2);
	      printf (_("@ Line %lu in file \"%s\" is shorter than expected!\n"), 
		      lineno1, argl->file1);
	    }
	  else if (output_mode == OUTMODE_RAW)
	    {
	      printf ("%lu:%lu:*:%lu:%s", lineno1, lineno2, fieldno2+1, field2);
	    }	  
	  return 1;
	}
      else
	return lines_differ;
    }
}

extern char** def_ifs;

int cmp_files (FILE* pf1, FILE* pf2, const argslist* argl,
	       statlist* statres)
{
  char *line1, *line2, **ifs1, **ifs2;
  int err1, err2, files_differ = 0;
  unsigned long lineno1 = 1, lineno2 = 1;
  size_t n;
  unsigned short i;
  unsigned char byte, elem;
  flg_array table = copy_of_intflagtab();

  ifs1 = (!argl->ifs1) ? def_ifs : argl->ifs1;
  ifs2 = (!argl->ifs2) ? def_ifs : argl->ifs2;
  if ( (table.ptr) )
    {
      /*
	Filter on
       */
      for (n = 0, err1 = err2 = OK; n < table.len; n += 4U)
	{
	  byte = table.ptr[n/4U];
	  for (i = 0; i < 4U; i++)
	    {
	      elem = (byte & 0x03 << 2*i) >> 2 * i;
	      switch (elem)
		{
		case 1:
		  line1 = read_line (pf1, &err1);
		  line2 = NULL;
		  break;
		case 2:
		  line1 = NULL;
		  line2 = read_line (pf2, &err2);
		  break;
		case 3:
		  line1 = read_line (pf1, &err1);
		  line2 = read_line (pf2, &err2);
		  break;
		case 0:
		  goto catch_error;
		}
	      if ( (cmp_lines (line1, line2, 
                               lineno1, lineno2, 
                               (const char**) ifs1, (const char**) ifs2, 
                               argl, statres)) )
		{
		  files_differ = 1;
		  if (argl->output_mode == OUTMODE_OVERVIEW)
		    print_1overview_line (line1, 1, line2);
		}
	      else
		{
		  if (argl->output_mode == OUTMODE_OVERVIEW && !suppress_common_lines)
		    print_1overview_line (line1, 0, line2);
		}
	      if ((line1))
		{
		  lineno1++;
		  free ((void*)line1);
		}
	      if ((line2))
		{
		  lineno2++;
		  free ((void*)line2);
		}
	      if (err1 == OUT_OF_MEMORY || 
                  err2 == OUT_OF_MEMORY || 
                  err1 == FILE_IS_BINARY || 
                  err2 == FILE_IS_BINARY || 
                  err1 == READING_ERROR || 
                  err2 == READING_ERROR)
		goto catch_error;
	    }
	}
    } /* end of `if ( (table.ptr) )'*/
  else
    {
      /*
	Filter off
       */
      do
	{
	  line1 = read_line (pf1, &err1);
	  line2 = read_line (pf2, &err2);
	  if ( (cmp_lines (line1, line2, 
                           lineno1, lineno2, 
                           (const char**) ifs1, (const char**) ifs2, 
                           argl, statres)) )
	    {
	      files_differ = 1;
	      if (argl->output_mode == OUTMODE_OVERVIEW)
		print_1overview_line (line1, 1, line2);
	    }
	  else
	    {
	      if (argl->output_mode == OUTMODE_OVERVIEW && !suppress_common_lines)
		print_1overview_line (line1, 0, line2);
	    }
	  if ((line1))
	    free ((void*)line1);
	  if ((line2))
	    free ((void*)line2);
	  lineno1++, lineno2++;
	} while (err1 == OK && err2 == OK);
    }

 catch_error:
  /* 
   * If we arrive here, then
   *
   * either ist n == table.len,
   * or elem == 0,
   * or either err1 or err2 is different from OK.
   */
  fflush (stdout);
  if ( (table.ptr) )
    {
      if (n >= table.len || !elem)
	return files_differ;
    }
  if (err1 == OK)
    {
      switch (err2)
	{
        case FILE_IS_BINARY:
          fprintf (stderr, _("\n***  File \"%s\" is binary,\n***  cannot read from it\n"), argl->file2);
	  return FILE_IS_BINARY;          
	case READING_ERROR:
	  fprintf (stderr, _("\n***  Error while reading from file \"%s\"\n"), argl->file2);
	  return READING_ERROR;
	case OUT_OF_MEMORY:
	  fprintf (stderr, _("\n***  Out of memory while reading from file \"%s\"\n"), argl->file2);
	  return OUT_OF_MEMORY;
	case LINE_INTERR:
	  if (getc (pf1) == EOF)
	    break;
	  /* default == EOF_REACHED */
	default: 
	  fprintf (stderr, _("\n***  End of file \"%s\" reached while trying to read line %lu.\n"), argl->file2, lineno2-1);
	  fprintf (stderr, _("***  File \"%s\" has more lines than file \"%s\",\n"),
		   argl->file1, argl->file2);
	  fprintf (stderr, _("***  line %lu is the last one read from file \"%s\"\n\n"), lineno1-1, argl->file1);
	}
      return files_differ;
    }
  else if (err1 == LINE_INTERR)
    {
      switch (err2)
	{
        case FILE_IS_BINARY:
          fprintf (stderr, _("\n***  File \"%s\" is binary,\n***  cannot read from it\n"), argl->file2);
	  return FILE_IS_BINARY;          
	case READING_ERROR:
	  fprintf (stderr, _("\n***  Error while reading from file \"%s\"\n"), argl->file2);
	  return READING_ERROR;
	case OUT_OF_MEMORY:
	  fprintf (stderr, _("\n***  Out of memory while reading from file \"%s\"\n"), argl->file2);
	  return OUT_OF_MEMORY;
	case OK:
	  if (getc (pf2) != EOF)
	    {
	      fprintf (stderr, _("\n***  End of file \"%s\" reached while trying to read line %lu.\n"), argl->file1, lineno1-1);
	      fprintf (stderr, _("***  File \"%s\" has more lines than file \"%s\",\n"),
		       argl->file2, argl->file1);
	      fprintf (stderr, _("***  line %lu is the last one read from file \"%s\"\n\n"), lineno2-1, argl->file2);
	    }
	  break;
	case EOF_REACHED:
	  fprintf (stderr, _("\n***  End of file \"%s\" reached while trying to read line %lu.\n"), argl->file2, lineno2-1);
	  fprintf (stderr, _("***  File \"%s\" has more lines than file \"%s\",\n"),
		   argl->file1, argl->file2);
	  fprintf (stderr, _("***  line %lu is the last one read from file \"%s\"\n\n"), lineno1-1, argl->file1);
	  /*
	    No particular action to do if err2 == LINE_INTERR
	  */
	}
      return files_differ;
    }
  else if (err1 == EOF_REACHED)
    {
      switch (err2)
	{
        case FILE_IS_BINARY:
          fprintf (stderr, _("\n***  File \"%s\" is binary,\n***  cannot read from it\n"), argl->file2);
	  return FILE_IS_BINARY;          
	case READING_ERROR:
	  fprintf (stderr, _("\n***  Error while reading from file \"%s\"\n"), argl->file2);
	  return READING_ERROR;
	case OUT_OF_MEMORY:
	  fprintf (stderr, _("\n***  Out of memory while reading from file \"%s\"\n"), argl->file2);
	  return OUT_OF_MEMORY;
	case OK:
	case LINE_INTERR:
	  fprintf (stderr, _("\n***  End of file \"%s\" reached while trying to read line %lu.\n"), argl->file1, lineno1-1);
	  fprintf (stderr, _("***  File \"%s\" has more lines than file \"%s\",\n"),
		   argl->file2, argl->file1);
	  fprintf (stderr, _("***  line %lu is the last one read from file \"%s\"\n\n"), lineno2-1, argl->file2);
	}
      return files_differ;
    }
  else if (err1 == FILE_IS_BINARY)
    {
      fprintf (stderr, _("\n***  File \"%s\" is binary,\n***  cannot read from it\n"), argl->file1);
      return FILE_IS_BINARY;          
    }
  else if (err1 == READING_ERROR)
    {
      fprintf (stderr, _("\n***  Error while reading from file \"%s\"\n"), argl->file1);
      return READING_ERROR;
    }
  else /* err1 == OUT_OF_MEMORY */
    {
      fprintf (stderr, _("\n***  Out of memory while reading from file \"%s\"\n"), argl->file1);
      return OUT_OF_MEMORY;
    }
}
