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

const char *SPIT_history[] =
{
"------------------------------------------------------------------",
"                                                                  ",
"                             ZCut v1.08                           ",
"                                                                  ",
"                     by FrANK G. Barchard, EAC                    ",
"                                                                  ",
"                    Main program - Jul 28, 2005                   ",
"                                                                  ",
"------------------------------------------------------------------",
"                                                                  ",
" Version Date   SE History:                                       ",
" ------- ----   -- --------                                       ",
"    1.00 950305 FB cut initial version based on runpack           ",
"    1.01 951116 FB use range to select                            ",
"    1.02 960918 FB changed -n to -f for consistancy with gx       ",
"    1.03 961009 FB extract selected bytes/new range api           ",
"    1.04 000327 FB ranger API and -time                           ",
"    1.05 001127 FB 64 bit file io                                 ",
"    1.06 010620 FB len-1 for max inclusive                        ",
"    1.07 030512 FB -chop file into multiple smaller files         ",
"    1.08 050728 FB file based for bigger chopping                 ",
"                                                                  ",
"------------------------------------------------------------------",
"\0"
};

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

#include    "spit.h"
#include    "ranger.h"

extern "C" char *SPIT_getrangeopt(char *optvalptr,const char *optptr, const char *optname, int optmin, int optmax);

const char TextVersion[]="1.08 (Samba) 2007.12.4";
#define TOOL_VERSION 108
#define TOOL_NAME "zcut"

char OPTbytes[SPITMAXLINE];
static int OPTchopsize;

double TIMEload;
double TIMEcut;
double TIMEsave;

/****************************************************************/
/*  Internal Functions                                          */
/****************************************************************/

static char *SPIT_loadfilerange(SPITMEM *mem, const char *fname, char *OPTbytes, int verbose)
{
    GPOS flen;
    GPOS  len;
    GSTREAM *fhdl;
    char statbuf[SPITMAXLINE];
    GPOS first;
    GPOS last;
    int ok=0;

    flen = 0;
    mem->ptr = 0;
    mem->len = 0;

    printf(SPIT_loadstr(statbuf,fname,verbose));
    SPIT_flush();

    TIMEload = SPIT_gettime();
    fhdl = gopen(fname);

    if (fhdl)
    {
        RANGER *r;
        EXPR *expr;
        double fmin,fmax;
        INT64 fcount;

        flen = glen(fhdl);

        expr = EXPR_open();
        EXPR_set(expr,"len",(double)flen);
        EXPR_set(expr,"n",(double)flen);
        EXPR_set(expr,"last",(double)(flen-1));

        r = RANGER_open(expr, OPTbytes, 0.0, (double)flen-1.0);
        if (!RANGER_info(r,&fmin,&fmax,&fcount))
        {
            if (OPTverbose)
                printf("zcut - '-n' OPTION SPECIFIED BYTES OUT OF RANGE.\nSpecified %s. Valid range is %d..%d\n",OPTbytes,0,(int)flen-1);
            exit(10);
        }

        if (OPTverbose>=5)
            printf("extracting %.0f..%.0f (%.0f bytes)\n",fmin,fmax,(double)fcount);

        first = (GPOS)fmin;
        last  = (GPOS)fmax;
        len =   (GPOS)fcount;

        if (len)
        {
            unsigned char *s;
            s = (unsigned char *)galloc((int)(last-first+1));
            if (s)
            {
                gseek(fhdl,first);
                ok = gread(fhdl,s, (int)(last-first+1));

                TIMEcut = SPIT_gettime();
                TIMEload = TIMEcut-TIMEload;

                if (ok)
                {
                    unsigned char *d;

                    d = (unsigned char *)galloc((int)(len+1));
                    if (d)
                    {
                        double next;
                        mem->ptr = (char *) d;
                        mem->len = (int)len;

                        while (RANGER_next(r, &next))
                        {
                            *d++ = s[(int)(next-first)];
                        }
                    }
                    TIMEcut = SPIT_gettime()-TIMEcut;
                }
                gfree(s);
            }
        }
        gclose(fhdl);
    }

    if (!mem->ptr)
    {
        if (verbose)
            printf("SPIT_loadfilev - COULD NOT READ FILE '%s'\n", fname);
        SPIT_flush();
    }

    return(mem->ptr);
}


#if 0
/*
;
; ABSTRACT
;
; zcut - extract and save a range of bytes from a file
;
; SUMMARY
;
; zcut -options [files]
;   @file     response file, containing a list of options and files
;   =file     specify a output file ie zcut source=dest
;   -f<range> range of bytes to extract (default all)
;   -chop<size> chop file into multiple smaller files of specified byte count
;   -nologo   disable banner
;   -time     display timing diagnostic
;   -v0       no console output
;   -v1       prints source file name and errors
;   -v2       status line for each file (default)
;   -v3       additional info
;   -w0       don't output file
;   -w1       output to file (default)
;
; RANGE SYNTAX
;
;    [[!][<start>][..][<end>][+=<step>][,]]*
;    ie -f             all bytes
;       -f5            frame 5
;       -f5..9         bytes 5..9 inclusive
;       -f!10          all bytes except 10
;       -f5,7,9        bytes 5,7 and 9
;       -f10..98+=2    even bytes in range between 10 and 98
;
; DESCRIPTION
;
; ZCut extracts the specified bytes from a file and writes the
; results to a specified destination file.  The original file
; is not touched.  The specify the bytes to extract, range
; syntax is used.
;
;
; RETURNS
;
; no return code
;
; SEE ALSO
;
; copy, xd, Spit
;
; SAMPLE OUTPUT
;
; >zcut zcut.c =bob -n10..49
;
;   Cut v1.01 (Axe) 951116 by FrANK Barchard, EAC
;   zcut.c: Reading, Cutting, Writing, 40 bytes extracted.
;
; END ABSTRACT
;
*/
#endif

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

void SPIT_reset(void)
{
    OPTbytes[0] = 0;
    return;
}


void SPIT_usage(const char *prgname, const char *envname, const char *urlname, int utype)
{
    printf("ZCut v%s by FrANK Barchard, EAC\n",TextVersion);
    if (utype)
        printf("USAGE: %s -options [files]\n", prgname);

    switch (utype)
    {
        case 2:
            puts("    -f range of bytes to cut     -v verbose");
            puts("    -chop file into pieces       -w write file");
            puts("     @ response file              = output file");
          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: Cut file into two files at specified location");
          printf("         SPIT environment variable is %s\n",envname);
            puts("");
            puts("    @file     response file, containing a list of options and files");
          printf("    =file     specify a output file ie %s source=dest\n", prgname);
            puts("    -f<range> range of bytes to extract (default all)");
            puts("    -chop<size> chop file into multiple smaller files of specified byte count");
            puts("    -nologo   disable banner");
            puts("    -time     display timing diagnostic");
            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("    -w0       don't output file");
            puts("    -w1       output to file (default)");
            puts("");
            puts("RANGE SYNTAX:");
            puts("    [[!][<start>][..][<end>][+=<step>][,]]*");
            puts("    ie -f             all bytes");
            puts("       -f5            frame 5");
            puts("       -f5..9         bytes 5..9 inclusive");
            puts("       -f!10          all bytes except 10");
            puts("       -f5,7,9        bytes 5,7 and 9");
            puts("       -f10..98+=2    even bytes in range between 10 and 98");

        default:
            break;
    }
}

char *SPIT_opt(const char* optptr)
{
    char *d=(char *)0;
    switch (*optptr|(char)' ')
    {
        case 'n':
        case 'f':
            d = SPIT_getrangeopt( OPTbytes,optptr+1, "range of bytes to extract",0,0x7fffffff);
            break;

        default: ;
    }
    return(d);
}

int main(int argc,char **argv)
{
    int more;
    int i;
    struct SPITMEM infile;

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

        for (i=0; i<SPITargnum; ++i)
        {
            if (SPIT_loadfilerange(&infile, SPITsargv[i],OPTbytes,OPTverbose))
            {
                if (OPTverbose>=3)
                {   printf("Cutting, ");
                    SPIT_flush();
                }

                TIMEsave = SPIT_gettime();

                SPIT_savefile(&infile, SPITdargv[i]);

                TIMEsave = SPIT_gettime()-TIMEsave;

                if (OPTverbose>=3)
                {
                    printf("%ld bytes extracted.\n", infile.len);
                    if (OPTtime && OPTverbose)
                        printf(" Time load %.2fms, cut %.2fms, save %.2fms\n",TIMEload,TIMEcut,TIMEsave);
                    SPIT_flush();
                }
                SPIT_free(&infile);
            }
        }
        if (OPTverbose==1)
            puts("");

    } while (more>0);

    return(0);
}

