/*--------------------------------------------------------------------------*
         ______
        / ____/                          $RCSfile: fcmp.c,v $
       / /_ ______ _   _   ______        $Release$
      / __// ____// \_/ \ / __  /        $Revision: 1.6 $
     / /  / /___ / /__/ // /_/ /         $Date: 92/12/04 07:10:12 $
    /_/  /_____//_/  /_// ____/          $Author: tf $
                       / /               $State: Exp $
      Revision 1.6    /_/

         (c) Copyright 1989-92 Tobias Ferber, All Rights Reserved.

 *--------------------------------------------------------------------------*/
   
#include <exec/types.h>
#include <stdio.h>
#include <stdarg.h>

extern long hextol(char *); /* instead of xtoy.h */

static char rcs_id[]= "$Id: fcmp.c,v 1.6 92/12/04 07:10:12 tf Exp $";
char *whoami;

BOOL silent=FALSE,   /* don't produce any output but the result */
     brkdiff=FALSE;  /* break on the first non-equivalence */

FILE *fp0, *fp1;

void perror(const char *fmt, ...)
{
  va_list argp;
  va_start(argp,fmt); /* name the last known argument */
  fprintf(stderr,"\r%s: ",whoami);
  vfprintf(stderr,fmt,argp);
  fprintf(stderr,"\n");
  fflush(stderr);
  va_end(argp); /* just to be sure... */
}

void closeall(void)
{ if(fp0) fclose(fp0);
  if(fp1) fclose(fp1);
}

void _abort(void)
{ printf("\33[31m^C\n\n** BREAK\n");
  closeall();
  exit(5);
}

int openall(char *fname0,char *fname1,int rpos)
{
  if(!(fp0=fopen(fname0,"rb")))
  { perror("Can't open %s for input - object not found",fname0);
    return(0);
  }
  if(!(fp1=fopen(fname1,"rb")))
  { perror("Can't open %s for input - object not found",fname1);
    fclose(fp0);
    return(0);
  }
  if(rpos>0)
  { if(fseek(fp0,rpos,0L)<0||fseek(fp1,rpos,1L)<0)
    { closeall();
      return(0);
    }
  }
  return(1);
}

#define DUMP(x)  ((32<=(x)&&(x)<127)||(x)>161)?(x):('.')
#define COLOR(x) (x)?("\33[31m"):("\33[33m")

/* single file hex dump */

long hexdump(char *fname0, char* fname1, long rpos)
{
  unsigned long bc=rpos,  /* byte counter */
                dc=0;     /* difference counter */

  BOOL abort=FALSE;

  while(!abort)
  { unsigned long i,j,k; /* general counters */
    unsigned char c0[16],c1[16]; /* char buffer */
    BOOL d[16]; /* difference indicator */
    for(j=0,k=0;(j<16&!abort);j++,bc++)
    { c0[j]=fgetc(fp0);
      c1[j]=fgetc(fp1);
      abort=(feof(fp0)||feof(fp1));
      if(!(d[j]=(c0[j]==c1[j]))) k++;
    }
    dc+=k;
    if(j>0 && (!silent || (silent && k>0)))
    { printf("%08lx: ",bc-j);
      for(i=0;i<16;i++)
      { if(i<j) printf("%s%02x%s",COLOR(d[i]),c0[i],(((i+1)%4)==0)?(" "):(""));
        else printf("  %s",(((i+1)%4)==0)?(" "):(""));
      }
      printf("  ");
      for(i=0;i<j;i++) printf("%s%c",COLOR(d[i]),DUMP(c0[i]));
      printf("\33[31m\n");
    }
    if(brkdiff && dc>0) abort=TRUE;
  }
  if(brkdiff && dc>0)
    perror("terminated; files %s and %s differ.",fname0,fname1);
  else
  { if(!(feof(fp0)&&feof(fp1)))
      printf("Unexpected EOF \"%s\" at seek offset %d; ",feof(fp0)?fname0:fname1,bc);
    else printf("Equal length: %ld bytes, ",bc);
    printf("%d %s.\n",dc,(dc==1)?"difference":"differences");
  }
  return(dc);
}

/* hex dump of both files */

long dumpdiff(char *fname0, char* fname1, long rpos)
{
  unsigned long bc=rpos,  /* byte counter */
                dc=0;     /* difference counter */

  BOOL abort=FALSE;

  while(!abort)
  { unsigned long i,j,k; /* general counters */
    unsigned char c0[8],c1[8]; /* char buffer */
    BOOL d[8]; /* difference indicator */
    for(j=0,k=0;(j<8&!abort);j++,bc++)
    { c0[j]=fgetc(fp0);
      c1[j]=fgetc(fp1);
      abort=(feof(fp0)||feof(fp1));
      if(!(d[j]=(c0[j]==c1[j]))) k++;
    }
    dc+=k;
    if(j>0 && (!silent || (silent && k>0)))
    { printf("%08lx: ",bc-j);
      for(i=0;i<8;i++)
      { if(i<j) printf("%s%02x%s",COLOR(d[i]),c0[i],(i==3)?(" "):(""));
        else printf("  %s",((i==3)?(" "):("")));
      }
      printf("  ");
      for(i=0;i<8;i++) printf("%s%c",COLOR(d[i]),((i<j)?(DUMP(c0[i])):(' ')));
      printf("\33[31m  |  ");
      for(i=0;i<8;i++)
      { if(i<j) printf("%s%02x%s",COLOR(d[i]),c1[i],(i==3)?(" "):(""));
        else printf("  %s",((i==3)?(" "):("")));
      }
      printf("  ");
      for(i=0;i<j;i++) printf("%s%c",COLOR(d[i]),DUMP(c1[i]));
      printf("\33[31m\n");
    }
    if(brkdiff && dc>0) abort=TRUE;
  }
  if(brkdiff && dc>0)
    perror("terminated; files %s and %s differ.",fname0,fname1);
  else
  { if(!(feof(fp0)&&feof(fp1)))
      printf("Unexpected EOF \"%s\" at seek offset %d; ",feof(fp0)?fname0:fname1,bc);
    else printf("Equal length: %ld bytes, ",bc);
    printf("%d %s.\n",dc,(dc==1)?"difference":"differences");
  }
  return(dc);
}

/* single byte dump */

long sdbdump(char *fname0, char *fname1, long rpos)
{ unsigned char c0,c1;
  unsigned long bc=rpos, /* byte counter */
                dc=0;    /* difference counter */

  BOOL abort=FALSE;

  do
  { c0=fgetc(fp0);
    c1=fgetc(fp1);
    if(!(feof(fp0)||feof(fp1)))
    { bc++;
      if(c0!=c1)
      { dc++;
        if(!silent)
          printf("%08lx:  #%03d ($%02x) \"%c\" !=  #%03d ($%02x) \"%c\"\n",
            bc,c0,c0,DUMP(c0),c1,c1,DUMP(c1));
        else printf("%08lx\n",bc);
        if(brkdiff) abort=TRUE;
      }
    }
  } while(!(feof(fp0)||feof(fp1)||abort));

  if(brkdiff && dc>0)
    perror("terminated; files %s and %s differ.",fname0,fname1);
  else
  { if(!(feof(fp0)&&feof(fp1)))
      printf("Unexpected EOF \"%s\" at seek offset %d; ",feof(fp0)?fname0:fname1,bc);
    else printf("Equal length: %ld bytes, ",bc);
    printf("%d %s.\n",dc,(dc==1)?"difference":"differences");
  }
  return(dc);
}

/* simple compare */

long compare(char *fname0, char *fname1, long rpos)
{ unsigned char c0,c1;
  unsigned long bc=rpos, /* byte counter */
                dc=0;    /* difference counter */

  BOOL abort=FALSE;

  do
  { c0=fgetc(fp0);
    c1=fgetc(fp1);
    if(!(feof(fp0)||feof(fp1)))
    { bc++;
      if(c0!=c1)
      { dc++;
        if(brkdiff) abort=TRUE;
      }
    }
  } while(!(feof(fp0)||feof(fp1)||abort));
  if(!silent)
  { if(abort)
      perror("terminated; files %s and %s differ.",fname0,fname1);
    else
    { if(!(feof(fp0)&&feof(fp1)))
        printf("Unexpected EOF \"%s\" at seek offset %d; ",feof(fp0)?fname0:fname1,bc);
      else printf("Equal length: %ld bytes, ",bc);
      printf("%d %s.\n",dc,(dc==1)?"difference":"differences");
    }
  }
  return(dc);
}


main(int argc,char *argv[])
{
  long (*action)()= compare;
  char *fname[2]; /* file names */
  long f=0,       /* filename nš */
       skip=0,    /* #of bytes to skip */
       rc=0;      /* return code */

  onbreak(_abort);
  whoami= argv[0];

  if(argc>1)
  { --argc;
    ++argv;
    while(argc>0 && rc==0)
    { char *arg=argv[0];
      if(*arg=='-')
      { arg++;
        switch(*arg)
        { case 'd': case 'D': /* dump */
            switch(*++arg)
            { case '0':  action=sdbdump;   /* single byte dump */
                         break;
              case '1':  action=hexdump;   /* hex dump */
                         break;
              case '2':  action=dumpdiff;  /* dump diff */
                         break;
              default:   perror("Bad dump option '-d%c'.",*arg);
                         rc=5;
                         break;
            }
            break;
          case 'b': case 'B': /* break on diff */
            brkdiff=TRUE;
            break;          
          case 'q': case 'Q': /* quiet (brief) */
            silent=TRUE;
            break;
          case 's': case 'S': /* seek offset */
            if(arg[1]) ++arg;
            else
            { ++argv;
              --argc;
              arg=argv[0];
            }
            if(arg[0]=='$')
              skip= hextol(&arg[1]);
            else if(arg[0]=='0' && (arg[1]=='x'||arg[1]=='X'))
              skip= hextol(&arg[2]);
            else skip= atol(arg);
            if(skip<0)
            { perror("Error: Negative begin-offset; can't skip %d bytes",skip);
              rc=5;
            }
            break;
          default:
            perror("Bad option: -%c",*arg);
            rc=5;
            break;
        }
      }
      else if(f<2) fname[f++]= arg;
      else
      { perror("Can't compare more than two files yet --Sorry!");
        rc=5;
      }
      --argc;
      ++argv;
    }
    if(rc==0)
    { if(!silent) puts(rcs_id);
      if(f==0) perror("requires two file names.");
      else
      { if(f==1) fname[1]=fname[0];
        if(openall(fname[0],fname[1],skip))
        { rc=action(fname[0],fname[1],skip);
          closeall();
        }
      }
    }
  }
  else /* no args */
  { puts(rcs_id);
    puts("(c)Copyright 1989-92 by Tobias Ferber, All Rights Reserved");
    puts("FCMP <files> [-q] [-b] [-d0] [-d1] [-d2] [-s[$|0x]<n>]");
  }
  exit((rc==0)?0:5);
}
