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

const char *SPIT_history[] =
{
"------------------------------------------------------------------",
"                                                                  ",
"                           CRC v1.18                              ",
"                                                                  ",
"                     by FrANK G. Barchard, EAC                    ",
"                                                                  ",
"                   Main program - May 29, 2005                    ",
"                                                                  ",
"------------------------------------------------------------------",
"                                                                  ",
" Version Date   History:                                          ",
" ------- ------ --------                                          ",
"    1.00 910130 CRC table/code grabbed from Mike Benna's 'Pack'   ",
"    1.02 910401 two column usage/auto opts environment var        ",
"    1.03 910529 aztec style options list                          ",
"    1.04 911018 padded hex CRCs                                   ",
"    1.05 910914 spit 2.0                                          ",
"    1.06 950309 new initializer for library compatibility         ",
"    1.07 950331 Generate table                                    ",
"    1.08 960204 Append a CRC chunk to the file (-w)               ",
"    1.09 980303 keeptime                                          ",
"    1.10 980818 Added -s<range> to avoid using entire file        ",
"    1.11 011024 -time, c++ default                                ",
"    1.12 020719 -seed                                             ",
"    1.13 020801 64 file sizes                                     ",
"    1.14 020802 crc32 from zlib                                   ",
"    1.15 020805 running total combine crcs - patricks algorithm   ",
"    1.16 020806 block io total crc                                ",
"    1.17 040224 Update to use teal-1.00.01                        ",
"    1.18 050529 -return                                           ",
"                                                                  ",
" CRC information:                                                 ",
" http://www.acte.no/teknisk/html/crcguide.htm                     ",
"                                                                  ",
"------------------------------------------------------------------",
"\0"
};

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

#include <sys/stat.h>

#include "spit.h"
#include "stdgimex.h"
#include "range.h"
#include "ranger.h"
#include "expr.h"

const char TextVersion[]="1.18 (Samba) 2007.12.4";
#define TOOL_VERSION 118
#define TOOL_NAME "crc"

int OPTkeeptime;
int OPTblocksize;
int OPTcrcseed;
int OPTcrc32;
char OPTrange[SPITMAXLINE];
char OPTreturn[SPITMAXLINE];

/* ========================================================================
 * Table of CRC-32's of all single-byte values (made by make_crc_table)
 */

static unsigned int crc_table[256] = {
    0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L,
    0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L,
    0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L,
    0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L,
    0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
    0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L,
    0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL,
    0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL,
    0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L,
    0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
    0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L,
    0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L,
    0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL,
    0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L,
    0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
    0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL,
    0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L,
    0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L,
    0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L,
    0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
    0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL,
    0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L,
    0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL,
    0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL,
    0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
    0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L,
    0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L,
    0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L,
    0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL,
    0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
    0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL,
    0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL
};

/* ========================================================================= */
#define DO1(buf) crc = crc_table[((unsigned int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
#define DO2(buf)  DO1(buf); DO1(buf);
#define DO4(buf)  DO2(buf); DO2(buf);
#define DO8(buf)  DO4(buf); DO4(buf);
/* ========================================================================= */

unsigned int SPIT_crc32(const unsigned char *s,int len,unsigned int crc=0xfbea)
{
    crc = crc ^ 0xffffffffL;

    while (len >= 8)
    {
      DO8(s);
      len -= 8;
    }

    if (len) do {
      DO1(s);
    } while (--len);
    return crc ^ 0xffffffffL;
}


// from GNU

static unsigned int const Crc32Table[256] =
{
  0x0,
  0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B,
  0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6,
  0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD,
  0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, 0x5F15ADAC,
  0x5BD4B01B, 0x569796C2, 0x52568B75, 0x6A1936C8, 0x6ED82B7F,
  0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A,
  0x745E66CD, 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039,
  0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58,
  0xBAEA46EF, 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033,
  0xA4AD16EA, 0xA06C0B5D, 0xD4326D90, 0xD0F37027, 0xDDB056FE,
  0xD9714B49, 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95,
  0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, 0xE13EF6F4,
  0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, 0x30476DC0,
  0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5,
  0x2AC12072, 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16,
  0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA, 0x7897AB07,
  0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, 0x6F52C06C,
  0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1,
  0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA,
  0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B,
  0xBB60ADFC, 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698,
  0x832F1041, 0x87EE0DF6, 0x99A95DF3, 0x9D684044, 0x902B669D,
  0x94EA7B2A, 0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E,
  0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, 0xC6BCF05F,
  0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34,
  0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80,
  0x644FC637, 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB,
  0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, 0x5C007B8A,
  0x58C1663D, 0x558240E4, 0x51435D53, 0x251D3B9E, 0x21DC2629,
  0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C,
  0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF,
  0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E,
  0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65,
  0xEBA91BBC, 0xEF68060B, 0xD727BBB6, 0xD3E6A601, 0xDEA580D8,
  0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3,
  0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, 0xAE3AFBA2,
  0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, 0x9FF77D71,
  0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74,
  0x857130C3, 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640,
  0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C, 0x7B827D21,
  0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, 0x6C47164A,
  0x61043093, 0x65C52D24, 0x119B4BE9, 0x155A565E, 0x18197087,
  0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC,
  0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D,
  0x2056CD3A, 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE,
  0xCC2B1D17, 0xC8EA00A0, 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB,
  0xDBEE767C, 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18,
  0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, 0x89B8FD09,
  0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662,
  0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF,
  0xA2F33668, 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4
};


// This is supposedly from the gnu textutils version 1.22.  This is the algorithm for binary files.
unsigned int CalculateCrc32(const void *data, int size, unsigned int prev_crc32=0xfbea)
{
    unsigned int crc  = prev_crc32;
    unsigned char *cp = (unsigned char *)data;

    for (int i = 0; i < size; i++)
    {
        int c = *cp++;
        crc = (crc << 8) ^ Crc32Table[((crc >> 24) ^ c) & 0xff];
    }

    // Encode the size
    while (size > 0)
    {
        int c = size & 0xff;
        crc = (crc << 8) ^ Crc32Table[((crc >> 24) ^ c) & 0xff];
        size = size >> 8;
    }

    return (crc ^ 0xFFFFFFFF);
}




/* 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.
*/

// note works marginally faster on PC as unsigned int, but unsigned short saves space

static const unsigned int 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,
};

#define DOO1(buf) crc = crctab[((unsigned int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
#define DOO2(buf)  DOO1(buf); DOO1(buf);
#define DOO4(buf)  DOO2(buf); DOO2(buf);
#define DOO8(buf)  DOO4(buf); DOO4(buf);

/* ========================================================================= */

unsigned int SPIT_crc16(const unsigned char *s,int len,unsigned int crc=0xfbea)
{
    while (len >= 8)
    {
      DOO8(s);
      len -= 8;
    }
    if (len) do {
      DOO1(s);
    } while (--len);
    return crc;
}

/* ========================================================================= */
// file crc 16 or 32
/* ========================================================================= */

static unsigned int CRC_filecrc(GSTREAM *g,GPOS fmin, GPOS fmax, unsigned int crc=0xfbea, unsigned int *zerocrc=NULL)
{
    unsigned char *crcblockbuf = (unsigned char *) galloc(OPTblocksize);

    if (crcblockbuf)
    {
        GPOS p=fmin;

        while (p<fmax)
        {
            int blockamount=(fmax-p)>(GPOS)OPTblocksize ? OPTblocksize : (int)(fmax-p);

            gread(g,crcblockbuf,blockamount);
            if (OPTcrc32==1)
            {
                crc = SPIT_crc32(crcblockbuf,blockamount,crc);
                if (zerocrc)
                    *zerocrc = SPIT_crc32(crcblockbuf,blockamount,*zerocrc);
            }
            else if (OPTcrc32==2)
            {
                crc = CalculateCrc32(crcblockbuf,blockamount,crc);
                if (zerocrc)
                    *zerocrc = CalculateCrc32(crcblockbuf,blockamount,*zerocrc);
            }
            else
            {
                crc = SPIT_crc16(crcblockbuf,blockamount,crc);
                if (zerocrc)
                    *zerocrc = SPIT_crc16(crcblockbuf,blockamount,*zerocrc);
            }
            p += blockamount;
        }

        gfree(crcblockbuf);
    }
    else
    {
        if (OPTverbose)
            printf("CRC out of memory?! requested %d bytes\n",OPTblocksize);
    }
    return crc;
}

/*
;
; ABSTRACT
;
; crc - calculate crc checksum for a file
;
; SUMMARY
;
; crc -options [files]
;    @file     response file, containing a list of options and files
;    -nologo   disable banner
;    -v1       printfs source file name and errorcount
;    -v2       status line for each file (default)
;    -v3       additional info
;    -16       crc16 (default)
;    -32       crc32 (from zlib)
;    -32g      crc32 (from gnu)
;    -blocksize<n>  copy in blocks of this size (32K default)
;    -seed<n>  crc seed value (0xfbea default)
;    -time     time how long crc takes
;    -r        recurse subdirectories
;    -return<expr>  return expression (default errorcount). See VARIABLES
;
; VARIABLES:
;     errorcount, TOOL_VERSION, SPIT_VERSION, crc, time, totalsize, count
;
; DESCRIPTION
;      This utility calculates and displays a 16 bit crc for each file.
;      Optionally, it can append a CRC chunk to the end of the file.
;      The chunk format is [CRCF] [12] [CRC] where each field is 4 bytes,
;      store with the length and CRC in Intel order.
;
; RETURNS
;
; no return code
;
; SEE ALSO
;
; cmp, xd, Spit
;
; SAMPLE OUTPUT
;
; >crc eabrand.vp6
;         0x7a25, 1251284, "eabrand.vp6",
;
;
; END ABSTRACT
;
*/

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

void SPIT_reset(void)
{
    OPTwrite = 0;
    OPTbanner = 0;
    OPTkeeptime = 1;
    OPTrange[0] = 0;
    OPTblocksize = 32*1024;
    OPTcrcseed = 0xfbea;
    strcpy(OPTreturn,"");
}

void SPIT_usage(const char *prgname, const char *envname, const char *, int utype)
{
    printf("CRC v%s by FrANK G. Barchard\n",TextVersion);

    if (utype)
        printf("USAGE: %s -options [files]\n", prgname);

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

        default:
            puts("     @ response file       ");
            puts("    -r recurse subdirectories");
            puts("    -f<range> selected range of data to use (aka -s)");
            puts("    -v verbose");
            puts("    -16  crc16 (default)");
            puts("    -32  crc32 (from zlib)");
            puts("    -32g crc32 (from gnu)");
            puts("    -seed crc seed value");
            puts("    -time time how long crc takes to execute");
            puts("    -return expression using the following variables");
            puts("       errorcount, TOOL_VERSION, SPIT_VERSION, crc, time, totalsize, count");
            printf("type '%s -help' for full documentation in HTML format\n", prgname);
            break;
    }
}

char *SPIT_opt(const char* optptr)
{
    char *d=NULL;
    switch (tolower(*optptr))
    {
        case '1':
            if (!strnicmp(optptr, "16", 2))
            {
                OPTcrc32 ^= 1;
                d = SPIT_getopt( &OPTcrc32,optptr+2, "crc16", 0, 1);
                OPTcrc32 ^= 1;
            }
            break;

        case '3':
            if (!strnicmp(optptr, "32g", 3))
            {
                d = SPIT_getopt( &OPTcrc32,optptr+3, "crc32", 0, 2);
                if (OPTcrc32) OPTcrc32=2;
                if (OPTcrcseed == 0xfbea)
                    OPTcrcseed = 0;
            }
            else if (!strnicmp(optptr, "32", 2))
            {
                d = SPIT_getopt( &OPTcrc32,optptr+2, "crc32", 0, 1);
                if (OPTcrcseed == 0xfbea)
                    OPTcrcseed = 0;
            }
            break;


        case 'b':
            if (!strnicmp(optptr, "blocksize", 9))
                d = SPIT_getopt( &OPTblocksize,optptr+9, "copy blocksize", 1, 0x7fffffff);
            break;


        case 'f':
            d = SPIT_getstropt( OPTrange,optptr+1, "range string", "", SPITYES, SPITMAXLINE);
            break;

        case 'k':
            if (!strnicmp(optptr,"keeptime",8))
                d = SPIT_getopt( &OPTkeeptime,optptr+8, "keep time/date of original file", 0, 1);
            break;

        case 'r':
            if (!strnicmp(optptr,"return",6))
                d = SPIT_getfield( OPTreturn,optptr+6,1,0);
            break;

        case 's':
            if (!strnicmp(optptr, "seed", 4))
                d = SPIT_getopt( &OPTcrcseed,optptr+4, "crc seed value", 0, 0xffff);
            d = SPIT_getstropt( OPTrange,optptr+1, "range string", "", SPITYES, SPITMAXLINE);
            break;
    }
    return(d);
}

int main(int argc,char *argv[])
{
    int more;
    int i;
    int errorcount=0;
    double totaltime;
    unsigned int totalcrc2 = 0;
    GPOS totalbytes=0;

    SPIT_init(SPIT_reset, SPIT_usage, SPIT_opt, NULL);

    totaltime = SPIT_gettime();
    do
    {
        more = SPIT_parse(argc, argv, TOOL_NAME, TOOL_VERSION);

        totalcrc2 = OPTcrcseed;
        totalbytes=0;

        for (i=0; i<SPITargnum; ++i)
        {
            unsigned int crc=OPTcrcseed;
            bool ok=true;

            GSTREAM *g = gopen(SPITsargv[i]);
            if (!g)
            {
                if (OPTverbose)
                    printf("crc - ERROR OPENING FILE %s\n",SPITsargv[i]);
                ++errorcount;
                ok = false;
            }
            else
            {
                GPOS fmax = glen(g);
                GPOS fmin = 0;

                if (OPTrange[0])
                {
                    /* Check that range is valid */
                    if (!RANGE_check(OPTrange, 0, (int)fmax))
                    {
                        if (OPTverbose)
                            printf("Invalid range syntax.  Use #..#\n");
                        ++errorcount;
                        ok = false;
                        gclose(g);
                        g = NULL;
                    }
                    else
                    {
                        /* Get range minimum/maximum */
                        fmin = RANGE_min(OPTrange, 0);
                        fmax = RANGE_max(OPTrange, (int)fmax-1)+1;   // ranges are inclusive
                    }
                }
                if (ok)
                {
                    gseek(g,fmin);
                    if ((SPITargnum>1 || OPTverbose>=4))
                        crc = CRC_filecrc(g,fmin,fmax,crc,&totalcrc2);
                    else
                        crc = CRC_filecrc(g,fmin,fmax,crc);

                    if (OPTverbose)
                    {
                        if (OPTverbose>=3)
                            printf("\t0x%04x, %" FORMAT64 "d, \"%s\", // %d %" FORMAT64 "d..%" FORMAT64 "d,\n",
                                crc,fmax-fmin,SPITdargv[i],crc,fmin,fmax-1);
                        else if (OPTverbose>=2)
                            printf("\t0x%04x, %" FORMAT64 "d, \"%s\",\n",
                                crc,fmax-fmin,SPITdargv[i]);
                        else if (OPTverbose>=1)
                        {
                            if (i) printf(", ");
                            printf("0x%04x", crc);
                        }
                    }
                    totalbytes += fmax-fmin;
                }
                gclose(g);
            }
        }
        if (OPTverbose==1)
            puts("");

        if ((SPITargnum>1 && OPTverbose) || OPTverbose>=4)
        {
            if (OPTverbose>=2)
            {
                printf("// TOTAL 0x%04x, %" FORMAT64 "d bytes, %d files\n",
                    totalcrc2,totalbytes,SPITargnum);
            }
            else if (OPTverbose>=2)
            {
                printf("TOTAL 0x%04x\n", totalcrc2);
            }
        }
    } while (more>0);

    SPIT_restore();

    totaltime = SPIT_gettime()-totaltime;



    int returncode = errorcount;
    if (OPTreturn[0])
    {
        EXPR *expr = EXPR_open();
        RANGER *r = RANGER_open(expr,OPTreturn,0.0,1000000.0);  // min/max mostly ignored

        EXPR_set(expr,"errorcount",   errorcount);
        EXPR_set(expr,"TOOL_VERSION", TOOL_VERSION);
        EXPR_set(expr,"SPIT_VERSION", SPIT_VERSION);
        EXPR_set(expr,"crc",          (double) totalcrc2);
        EXPR_set(expr,"count",        SPITargnum);
        EXPR_set(expr,"totalsize",    (double) totalbytes);
        EXPR_set(expr,"time",         totaltime);

        returncode = (int) RANGER_closest(r, (double) returncode, RANGER_ROUND_CLOSEST);

        EXPR_close(expr);
        RANGER_close(r);
    }


    if (OPTtime)
    {
        if (totaltime>=60000.0)
            printf("TIME " TOOL_NAME " totaltime %.2f ms (%.2f seconds) (%.2f minutes)\n",
                totaltime,totaltime/1000.0,totaltime/60000.0);
        else if (totaltime>=1000.0)
            printf("TIME " TOOL_NAME " totaltime %.2f ms (%.2f seconds)\n",
                totaltime,totaltime/1000.0);
        else
            printf("TIME " TOOL_NAME " totaltime %.2f ms\n",totaltime);
    }

    if (galloccount)
    {
        if (OPTverbose)
            gprintf("galloccount = %i\n", galloccount);
        ++errorcount;

    }
    if (!OPTreturn[0])
        returncode = errorcount;

    return returncode;
}
