// (c) 1992-2007 Electronic Arts Inc.

const char *SPIT_history[] =
{
"------------------------------------------------------------------",
"                                                                  ",
"                           CMP v1.17                              ",
"                                                                  ",
"                     by FrANK G. Barchard, EAC                    ",
"                                                                  ",
"                   Main program - Oct 02, 2006                    ",
"                                                                  ",
"------------------------------------------------------------------",
"                                                                  ",
" Version Date   History:                                          ",
" ------- ------ --------                                          ",
"    1.00 920201 CMP based on huff compare code                    ",
"    1.01 920212 return number of different files to DOS           ",
"    1.02 920215 bugfix - hex len                                  ",
"    1.04 920401 two column usage/auto opts environment var        ",
"    1.05 920407 hex checksums first                               ",
"    1.06 920524 hex offset                                        ",
"    1.07 920529 aztec style options list                          ",
"    1.08 920801 compare to directory                              ",
"    1.09 920806 compare pathname to pathname                      ",
"    1.10 920914 spit 2.0                                          ",
"    1.11 921006 -n differences/xd output style/reordered files    ",
"    1.12 931015 blank out stuff after end of file in hex dump     ",
"    1.13 950605 6 digits for offset                               ",
"    1.14 950705 terse output for matches                          ",
"    1.15 950912 -t arithmetic difference tolerance                ",
"    1.16 951002 spit 2.5, message for missing destination files   ",
"    1.17 061002 fix names in 1:1 compares                         ",
"                                                                  ",
"------------------------------------------------------------------",
"\0"
};

/****************************************************************/
/*  Spit Setup                                                  */
/****************************************************************/

#include    "spit.h"

const char TextVersion[]="1.17 (Samba) 2007.12.4";
#define TOOL_VERSION 117
#define TOOL_NAME "cmp"

int OPTndiff;
int OPTtolerance;

/* CRC computation logic

   The logic for this method of calculating the CRC 16 bit polynomial
   is taken from an article by David Schwaderer in the April 1985
   issue of PC Tech Journal.
*/

unsigned short crctab[256]=      /* CRC combining table */
{
    0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
    0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
    0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
    0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
    0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
    0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
    0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
    0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
    0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
    0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
    0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
    0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
    0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
    0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
    0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
    0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
    0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
    0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
    0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
    0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
    0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
    0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
    0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
    0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
    0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
    0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
    0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
    0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
    0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
    0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
    0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
    0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040,
};


int SPIT_crc(register char *s, int len)
{
    unsigned short     crc;
    char               *end;

    end = s+len;
    crc = 0;
    if (s<end)
    {
        do
        {
            crc = (unsigned short) (crctab[(unsigned char) (crc ^ *s++)] ^ (crc>>8));
        } while (s<end);
    }
    return((int)crc);
}


void SQfileinfo(int verbose,char *buf,int len)
{
    char                    *ptr1;
    int                    i;
    int                     eorchk;
    int                     addchk;
    int                     crcchk;

    /*** eor/add calculate checksum ***/

    eorchk = addchk = 0;
    i=len;
    ptr1=buf;
    while (i--)
    {
        eorchk = eorchk ^ (int) (*ptr1);
        addchk = addchk + (int) (*ptr1);
        ++ptr1;
    }
    eorchk = eorchk & 255;

    /*** CRC checksum ***/

    crcchk = SPIT_crc(buf, len);

    if (verbose>=4)
    {
        printf("length %ld ($%lx)\n", len, len);
        printf(" EOR checksum: $%04x(%d)\n", eorchk, eorchk);
        printf(" ADD checksum: $%04x(%d)\n", addchk, addchk);
        printf(" CRC checksum: $%04x(%d)\n", crcchk, crcchk);
    }
    else
    {
        if (verbose>=3)
        {
            printf("length %ld, crc %d ($%x)\n",len,crcchk,crcchk);
        }
    }
    SPIT_flush();
}



/* xd output model
00000: 61616162 63636364 6565650d 0a        aaabcccdeee..
*/

void dumpline(char *s, char *e)
{
    int                     j;
    int                     c;

    for (j=0;j<16;++j)
    {
        if (s+j<e)
            printf("%02x", (int) (*(s+j)) & 255);
        else
            printf("  ");
        if (!((j+1)&3))
            printf(" ");
    }

    printf("  ");

    for (j=0;j<16;++j)
    {
        c = (int) (*(s+j)) & 127;
        if ((unsigned char)c<(unsigned char)' ')
            c = '.';
        if (c>'z')
            c = '.';
        if (s+j<e)
            printf("%c", c);
    }
    puts("");
}

int SQcompare(int  difOPTverbose,
              char *name1,
              char *buf1,
              int len1,
              char *name2,
              char *buf2,
              int len2)
{
    unsigned char           *ptr1;
    unsigned char           *ptr2;
    int                    i;
    int                     j;
    int                     sameq;
    int                     backup;
    int                     ndiff;

    /*** compare files ***/

    ptr1 = (unsigned char *)buf1;
    ptr2 = (unsigned char *)buf2;
    i = 0;
    ndiff = 0;
    while ((ndiff<OPTndiff) && (i<len2))
    {
        /* find the next difference */

        while ((abs((int)(signed char)*ptr1-(int)(signed char)*ptr2)<=OPTtolerance) && (i<len2))
        {
            ++ptr1;
            ++ptr2;
            ++i;
        }

        /*** dump 24 bytes from each file ***/

        if (i<len2)
        {
            ++ndiff;
            if (difOPTverbose>=4)
            {
                if (ndiff==1)
                    printf("\nFiles differ at byte %ld ($%lx): %s/%s\n", i,i, name1, name2);
                else
                    printf("\nDifference %d at byte %ld ($%lx): %s/%s\n", ndiff, i,i, name1, name2);
                backup = (i>=8) ? 8 : i;
                printf("%06lx: ",i);
                dumpline((char *)(ptr1-backup),(char *)(buf1+len1));
                printf("        ");
                dumpline((char *)(ptr2-backup),(char *)(buf2+len2));
                printf("        ");

                j = 0;
                while (j<backup)                /* move cursor to hex difference */
                {
                    printf("  ");
                    if (!(j+1&3))
                        printf(" ");
                    ++j;
                }
                printf("^");                    /* pointer to hex differnce */

                while (j<16)                    /* move cursor to end of hex */
                {
                    printf("  ");
                    if (!(j+1&3))
                        printf(" ");
                    ++j;
                }

                j = 0;
                while (j<backup)                /* move cursor to ascii difference */
                {
                    printf(" ");
                    ++j;
                }
                puts(" ^");                     /* pointer to ascii difference */

            }
            ++ptr1;
            ++ptr2;
            ++i;
        }
    }
    if (len1!=len2)
    {
        if (difOPTverbose>=4)
        {
            printf("Different Lengths\n");
        }
        ++ndiff;
    }
    if (ndiff)
    {
        sameq = SPITNO;
        if (difOPTverbose>=4)
        {
            printf("%s: ", name1);
            SQfileinfo(4, buf1, len1);
            printf("%s: ", name2);
            SQfileinfo(4, buf2, len2);
        }
        else
            if (difOPTverbose>=3)
            {
                printf("Different, ");
                SQfileinfo(OPTverbose, buf2, len2);
            }
            else
                if (OPTverbose>=2)
                    printf("<> ");
    }
    else
    {
        sameq = SPITYES;
        if (OPTverbose>=4)
        {
            printf("Identical, ");
            SQfileinfo(OPTverbose,buf2, len2);
        }
        else
        {
            if (OPTverbose>=3)
                printf("same\n");
            else
            {
                if (OPTverbose)
                    printf("= ");
            }
        }
    }
    return(sameq);
}

/*
;
; ABSTRACT
;
; cmp - compare files to each other and calculate checksums
;
; SUMMARY
;
; cmp -options file1 file2
;     @file     response file, containing a list of options and files
;     -nologo   disable banner
;     -v0       no console output
;     -v1       prints source file name and errors
;     -v2       status line for each file (default)
;     -v3       additional info
;     -r        recurse subdirectories
;
; DESCRIPTION
;
; This utility does a byte by byte comparison between binary files
;  and hex dumps the first section of the files that differ.
;  If one file is given, CMP gives checksums for that file.  If more
;  than two files are given, the first file if compared to all of
;  the other files.
;
; RETURNS
;
; 0 - all files are identical
; n - number of files that we different
;
; SEE ALSO
;
; gcmp, crc, xd, Spit
;
; SAMPLE OUTPUT
;
; >cmp spit.h spit.h
;
;   CMP v1.07 (Queen Elizabeth) (c) 1992 FrANK Barchard
;   spit.h: Reading, length 9397
;   spit.h: Reading, Different
;   Files differ at byte 449 ($1c1)
;   20 20 20 20 20 20 20 20 48 65 61 64 65 72 20 4d 6f 64 75 6c
;   20 20 20 20 20 20 20 20 20 20 4d 61 69 6e 20 43 6f 64 65 20
;                           ^
;   spit.h: length 9397 ($24b5)
;    EOR checksum: $0046(70)
;    ADD checksum: $f038(-4040)
;    CRC checksum: $2b62(11106)
;   spit.h: length 64826 ($fd3a)
;    EOR checksum: $0075(117)
;    ADD checksum: $806f(-32657)
;    CRC checksum: $ae42(-20926)
;
; END ABSTRACT
;
*/

/****************************************************************/
/*  SPIT Routines                                               */
/****************************************************************/

void SPIT_reset(void)
{
    OPTwrite = 0;
    OPTndiff = 1;
    OPTtolerance = 0;
    return;
}

void SPIT_usage(const char *prgname, const char *envname, const char *urlname, int utype)
{
    printf("CMP v%s (C) 1995 FrANK G. Barchard, EAC\n",TextVersion);

    if (utype)
        printf("USAGE: %s -options file1 file2\n", prgname);

    switch (utype)
    {
        case 0:
        case 1:
            break;

        case 2:
            puts("     @ response file       ");
            puts("    -n number of difs to find    -v verbose");
            puts("    -r recurse subdirectories");
          printf("type '%s -h' for more help\n", prgname);
          printf("type '%s -help' for web page (%s)\n", prgname, urlname);
            break;

        case 3:
            puts("");
          printf("VERSION: %s v%s, Spit v%.2f\n",SPIT_MACHINE,TextVersion,(float)SPIT_VERSION/100.0f);
            puts("SUMMARY: CMP compares a file against other files");
          printf("         SPIT environment variable is %s\n",envname);
            puts("");
            puts("    @file     response file, containing a list of options and files");
            puts("    -nologo   disable banner");
            puts("    -n<n>     number of differences to find");
            puts("    -v0       no console output");
            puts("    -v1       prints source file name and errors");
            puts("    -v2       status line for each file (default)");
            puts("    -v3       additional info");
            puts("    -r        recurse subdirectories");
            break;

        default:
            break;
    }
}

char *SPIT_opt(const char *optptr)
{
    switch (*optptr|(char)' ')
    {
        case 'n': return(SPIT_getopt( &OPTndiff,optptr+1, "number of difs", 0, 32000));
        case 't': return(SPIT_getopt( &OPTtolerance,optptr+1, "tolerance", 0, 256));
            break;

        default: ;
    }
    return((char *) 0);
}

int main(int argc,char *argv[])
{
    int more;
    int              i;
    int              ok;
    struct SPITMEM infile;
    struct SPITMEM cmpfile;
    int              difcode;
    int              manytoone;
    int              difOPTverbose;

    SPIT_init(SPIT_reset, SPIT_usage, SPIT_opt, SPIT_history);
    do
    {
        more = SPIT_parse(argc, argv, TOOL_NAME, TOOL_VERSION);

        difOPTverbose = OPTverbose;
        difcode = 0;
        if (SPITargnum)
        {
            if ((SPITargnum==1) && !stricmp(SPITsargv[0], SPITdargv[0]))
            {
                if (SPIT_loadfilev(&infile, SPITsargv[0], OPTverbose))
                {
                    if (OPTverbose)
                        SQfileinfo(OPTverbose+2, infile.ptr, infile.len);
                    SPIT_free(&infile);
                }
            }
            else
            {
                /* figure out if its a many to 1 compare */

                if (difOPTverbose==2 && (SPITargnum==1 || (SPITargnum==2 && !stricmp(SPITsargv[0], SPITdargv[0]))))
                    difOPTverbose = 3;

                manytoone = SPITNO;
                if (SPITargnum>=2)
                {
                    if (!stricmp(SPITsargv[0], SPITdargv[0]))
                    {
                        manytoone = SPITYES;
                    }
                }

                /* compare 1 file to many others */

                if (manytoone)
                {
                    if (OPTverbose>=6)
                        printf("many to one\n");
                    if (SPIT_loadfilev(&cmpfile,SPITsargv[0], OPTverbose))
                    {
                        for (i=1; i<SPITargnum; ++i)
                        {
                            if (SPIT_loadfilev(&infile, SPITsargv[i], 0))
                            {
                                if (OPTverbose>=2)
                                {
                                    printf("Comparing, ");
                                    SPIT_flush();
                                }

                                if (!SQcompare(difOPTverbose, SPITsargv[0], cmpfile.ptr, cmpfile.len, SPITdargv[i], infile.ptr, infile.len))
                                    ++difcode;
                                SPIT_free(&infile);
                            }
                            else
                            {
                                if (OPTverbose>=1)
                                    printf("File not found '%s'\n",SPITsargv[i]);
                                SPIT_flush();
                                ++difcode;
                            }

                        }
                        SPIT_free(&cmpfile);
                    }
                }

                /* compare each file to another */

                else
                {
                    if (OPTverbose>=6)
                        printf("one to one\n");
                    for (i=0; i<SPITargnum; ++i)
                    {
                        ok = (int)(intptr_t)SPIT_loadfilev(&cmpfile, SPITsargv[i], OPTverbose);
                        if (ok)
                        {
                            ok = (int)(intptr_t)SPIT_loadfilev(&infile, SPITdargv[i],OPTverbose-1);
                            if (ok)
                            {
                                if (OPTverbose>=4)
                                    printf("Comparing, ");
                                SPIT_flush();

                                if (!SQcompare(difOPTverbose, SPITsargv[i], cmpfile.ptr, cmpfile.len, SPITdargv[i], infile.ptr, infile.len))
                                    ++difcode;
                                SPIT_free(&infile);
                            }
                            else
                            {
                                if (OPTverbose>=1)
                                    printf("File not found '%s'\n",SPITdargv[i]);
                                SPIT_flush();
                                ++difcode;
                            }

                            SPIT_free(&cmpfile);
                        }
                    }
                }
            }
//            if (OPTverbose==1)
//                puts("");
        }
    } while (more>0);

    return(difcode);
}

