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

const char *SPIT_history[] =
{
"------------------------------------------------------------------",
"                                                                  ",
"                ZRUN - Spit Preparse and Run v1.32                ",
"                                                                  ",
"                    by Frank G. Barchard, EAC                     ",
"                                                                  ",
"                   Main program - Nov 16, 2006                    ",
"                                                                  ",
"------------------------------------------------------------------",
"                                                                  ",
" Version Date   SE History:                                       ",
" ------- ------ -- --------                                       ",
"    1.00 951106 FB initial version for using response files on ss ",
"    1.01 960717 FB single default. added multi option             ",
"    1.02 970327 FB -cd, option parsing before cmd                 ",
"    1.03 970408 FB -cd off by default (slow)                      ",
"    1.04 970714 FB -out to produce 'tool in out' form lines       ",
"    1.05 990201 FB -r spit recurse is zrun option                 ",
"    1.06 990909 FB use dargv. zrun saveit *.exe=*                 ",
"    1.07 991004 FB extension rule '', not '.'                     ",
"    1.08 000117 ND -stderr= option - redirection stderr to file   ",
"    1.09 000126 ND -stdout                                        ",
"    1.10 001005 FB -v0 do stderr to default file                  ",
"    1.11 001126 FB cleanup stderr/stdout files                    ",
"    1.12 001128 FB -time                                          ",
"    1.13 010505 FB -tolower                                       ",
"    1.14 010517 FB timex tool                                     ",
"    1.15 010601 FB show in minutes                                ",
"    1.16 010926 FB show seconds if more than 1                    ",
"    1.17 010926 FB qutoes filename arguments and passes thru opts ",
"    1.18 020623 FB exception handler                              ",
"    1.19 020721 FB exception printing via error code              ",
"    1.20 030922 FB -multi<n> for max at a time                    ",
"    1.21 030929 FB -nl to printf a newline after running each tool",
"    1.22 031026 FB -h and one argument zrun fixed                 ",
"    1.30 041102 FB sum up return codes                            ",
"    1.31 060410 FB -sleep to specify a delay after each app run.  ",
"    1.32 061116 FB -sort                                          ",
"                                                                  ",
"------------------------------------------------------------------",
"\0"
};

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

#include "spit.h"
#include "stdgimex.h"

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#if defined(_MSC_VER)
#include <windows.h>
#else
#include <unistd.h>
#endif

const char TextVersion[]="1.32 (Samba) 2007.12.4";
#define TOOL_VERSION 132
#ifdef TIMEX
#define TOOL_NAME "timex"
#else
#define TOOL_NAME "zrun"
#endif

int OPTtolower;
int OPTmulti;
int OPTlast;
int OPTcd;
int OPTnl;
int OPTsleep;
int OPTout;
int OPTstderr;
char OPTstderrfile[SPITMAXLINE];
int OPTstdout;
char OPTstdoutfile[SPITMAXLINE];
int OPTzrunstartsortat;

static int ZRUN_iszrunopt(const char *s,int before)
{
    int ok=0;
    if (!strnicmp(s,"-tolower",8))
        ok = 1;
    else if (!strnicmp(s,"-multi",6))
        ok = 1;
    else if (!strnicmp(s,"-sort",5))
        ok = 1;
    else if (!strnicmp(s,"-=",2))
        ok = 1;
    else if (!strnicmp(s,"-v",2))
        ok = 1;
    else if (!strnicmp(s,"-out",3))
        ok = 1;
    else if (!strnicmp(s,"-optlast",8))
        ok = 1;
    else if (!strnicmp(s,"-cd",3))
        ok = 1;
    else if (!strnicmp(s,"-nl",3))
        ok = 1;
    else if (!strnicmp(s,"-time", 4))
        ok = 1;
    else if (!strnicmp(s,"-stderr", 7))
        ok = 1;
    else if (!strnicmp(s,"-stdout", 7))
        ok = 1;
    else if (!strnicmp(s,"-sleep",6) && before)
        ok = 1;
    else if (!strnicmp(s,"-v",2) && before)
        ok = 1;
    else if (!strnicmp(s,"-h",2) && before)
        ok = 1;
    else if (!strnicmp(s,"-r",2) && before)
        ok = 1;
    else if (!strnicmp(s,"-help",5) && before)
        ok = 1;
    return(ok);
}

static int ZRUN_isoption(const char *s)
{
    int ok=0;

    if (!ZRUN_iszrunopt(s,0))
    {
        if (s[0]=='-'
         || s[0]=='/'
         || s[0]=='\\'
         || s[0]=='+')
            ok = 1;
    }
    return(ok);
}

static int ZRUN_docd(int first, int start, int end, int startopt, int endopt)
{
    int ok=0;
    int i;
    char *cmdline;
    int cmdlinelen=0;
    char fnamebuf[SPITMAXLINE];

    for (i=start; i<end; ++i)
    {
        if (!(endopt && ZRUN_isoption(SPITdargv[i])))
        {
            if (!ZRUN_iszrunopt(SPITdargv[i],0) && i!=first)
            {
                cmdlinelen = 3+strlen(SPIT_getpath(fnamebuf,SPITdargv[i]))+1;

                cmdline = (char *) galloc(cmdlinelen);
                if (!cmdline)
                {
                    if (OPTverbose)
                        printf(TOOL_NAME " - COULD NOT ALLOCATE COMMAND LINE MEMORY\nrequested %d bytes.\n",cmdlinelen);
                    exit(10);
                }
                sprintf(cmdline,"%s",SPIT_getpath(fnamebuf,SPITdargv[i]));

                if (OPTverbose>=4)
                    printf("cd %s\n",cmdline);
                SPIT_flush();

                ok = 1;
                chdir(cmdline);

                gfree(cmdline);
            }
        }
    }
    return(ok);
}

static bool ZRUN_needsquotes(const char *path)
{
    bool wantsquotesadded=false;

    if (path[0]!='"')
    {
        if (strchr(path,' ') || strchr(path,'&'))
            wantsquotesadded = true;
    }
    return wantsquotesadded;
}

static char *ZRUN_getname(char *buf,char *path)
{
    char fnamebuf[SPITMAXLINE];
    if (OPTcd)
        path = SPIT_getname(buf,path);

    if (ZRUN_needsquotes(path))
    {
        sprintf(fnamebuf,"\"%s\"",path);
        strcpy(buf,fnamebuf);
        path = buf;
    }

    return(path);
}


static int ZRUN_launch(int first, int start, int end, int startopt, int endopt)
{
    int ok=0;
    int i;
    char *cmdline;
    int cmdlinelen=0;
    char fnamebuf[SPITMAXLINE];


    for (i=start; i<end; ++i)
    {
        if (!(endopt && ZRUN_isoption(SPITdargv[i])))
            if (!ZRUN_iszrunopt(SPITdargv[i],0) && i!=first)
            {
                cmdlinelen += strlen(ZRUN_getname(fnamebuf,SPITdargv[i]))+1;
                if (OPTout)
                    cmdlinelen += strlen(ZRUN_getname(fnamebuf,SPITdargv[i]))+1;
            }
    }

    {
        for (i=startopt; i<endopt; ++i)
        {
            if (ZRUN_isoption(SPITdargv[i]))
                cmdlinelen += strlen(SPITdargv[i])+1;
        }

        cmdlinelen += strlen(SPITsargv[first])+1+1; /* 1 extra for null */
    }

    if (cmdlinelen)
    {
        cmdline = (char *) galloc(cmdlinelen);
        if (!cmdline)
        {
            if (OPTverbose)
                printf(TOOL_NAME " - COULD NOT ALLOCATE COMMAND LINE MEMORY\nrequested %d bytes.\n",cmdlinelen);
            exit(10);
        }

        /* tool name */

        cmdline[0] = 0;
        strcat(cmdline,SPITsargv[first]);
        strcat(cmdline," ");

        /* options */

        if (!OPTlast)
        {

            for (i=startopt; i<endopt; ++i)
            {
                if (ZRUN_isoption(SPITdargv[i]))
                {
                    strcat(cmdline,SPITdargv[i]);
                    strcat(cmdline," ");
                }
            }
        }

        /* filenames */

        for (i=start; i<end; ++i)
        {
            if (!(endopt && ZRUN_isoption(SPITdargv[i])))
            {
                if (!ZRUN_iszrunopt(SPITdargv[i],0) && i!=first)
                {
                    strcat(cmdline,ZRUN_getname(fnamebuf,SPITdargv[i]));
                    strcat(cmdline," ");
                    if (OPTout)
                    {
                        strcat(cmdline,ZRUN_getname(fnamebuf,SPITdargv[i]));
                        strcat(cmdline," ");
                    }
                }
            }
        }

        /* options */

        if (OPTlast)
        {
            for (i=startopt; i<endopt; ++i)
            {
                if (ZRUN_isoption(SPITdargv[i]))
                {
                    strcat(cmdline,SPITdargv[i]);
                    strcat(cmdline," ");
                }
            }
        }

        if (OPTverbose>=4)
            printf("%s\n",cmdline);
        SPIT_flush();

        ok = system(cmdline);

        if (OPTnl && OPTverbose)
            printf("\n");
        SPIT_flush();

        gfree(cmdline);
    }
    return ok;
}


/*
;
; ABSTRACT
;
; zrun - spit parse and ZRUN_launch an application
;
; SUMMARY
;
; zrun -zrunopts <tool> -toolopts [files]
;      @<file>           response file, containing a list of options and files
;     -multi             execute once for all filenames
;     -out               produce output filenames on command line too
;     -cd0               use full pathname (default)
;     -cd                cd into directory and use simple filename
;     -nl                print a newline after running the tool
;     -optlast           put options at end of command line (for -single only)
;     -stderr=filename   send stderr output to a file
;     -stdout=filename   send stdout output to a file
;     -sort              sort arguments alphanumerically
;     -v0                no output
;     -v1                dont show command
;     -v2                show command
;     -v                 show cd and command
;     -time              show execution time
;     -tolower           lower case filenames
;
; DESCRIPTION
;
; zrun parses the command line using spit wildcards and options,
; building up a new command line, then launching the specified
; application on the new command line.
;
; RETURNS
;
; 0 success
; n return code from system() call.  sum of all calls if more than one
;
; SEE ALSO
;
; timex, Spit
;
; SAMPLE OUTPUT
;
; >zrun "ss get" @bob.rsp
; >zrun post *.exe=*
; >zrun del @rsp
;
; END ABSTRACT
;
*/

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

void SPIT_reset(void)
{
    OPTtolower = 0;
    OPTkeepopts = 1;
    OPTextrule = "";
    OPTverbose = 2;
    OPTbanner = 0;
    OPTmulti = 0;
    OPTout = 0;
    OPTcd = 0;
    OPTnl = 0;
    OPTsleep = 0;
    OPTsorttype = 0;
#ifdef TIMEX
    OPTtime = 1;
    OPTminargs = 1;
#else
    OPTtime = 0;
#endif
    OPTlast = 0;
    OPTstderr = 0;
    OPTstdout = 0;
    OPTstartsortat = OPTzrunstartsortat;
    strcpy(OPTstderrfile,"stderr.txt");
    strcpy(OPTstdoutfile,"stdout.txt");
}

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

    if (utype)
        printf("USAGE: %s -zrunopts <tool> -toolopts [files]\n", prgname);

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

        case 2:
            puts("    -multi                        -cd");
            puts("    -optlast                      -v verbose");
            puts("    -stderr=<filename>            -stdout <filename>");
            puts("    -out                          -time");
            puts("    -tolower                      -nl");
            puts("    -sleep                        -sort");
            puts("     @ include a reponse 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);
#ifdef TIMEX
            puts("SUMMARY: " TOOL_NAME " launches and times commands/scripts");
#else
            puts("SUMMARY: " TOOL_NAME " preparses with SPIT then launches commands");
#endif
          printf("         SPIT environment variable is %s\n",envname);
            puts("");
            puts("     @<file>           response file, containing a list of options and files");
            puts("    -multi             execute once for all filenames");
            puts("    -out               produce output filenames on command line too");
            puts("    -cd0               use full pathname (default)");
            puts("    -cd                cd into directory and use simple filename");
            puts("    -nl                print a newline after running the tool");
            puts("    -optlast           put options at end of command line (for -single only)");
            puts("    -stderr=filename   send stderr output to a file");
            puts("    -stdout=filename   send stdout output to a file");
            puts("    -sleep<n>          sleep for <n> seconds after each launch");
            puts("    -sort              sort arguments alphanumerically");
            puts("    -v0                no output");
            puts("    -v1                dont show command");
            puts("    -v2                show command");
            puts("    -v                 show cd and command");
            puts("    -time              show execution time");
            puts("    -tolower           lower case filenames");
            break;

        default:
            break;
    }
}

char *SPIT_opt(const char *optptr)
{
    char buf[SPITMAXLINE];
    char *r;
    char *p;

    if (*optptr<=' ')
        return(0);
    if (!strnicmp(optptr,"multi",5))
        return(SPIT_getopt( &OPTmulti,optptr+5, "multiple filenames per ZRUN_launch", 0, 1000));
    if (!strnicmp(optptr, "tolower", 7))
        return(SPIT_getopt( &OPTtolower,optptr+7, "name to lower case", 0, 1));
    if (!strnicmp(optptr,"out",3))
        return(SPIT_getopt( &OPTout,optptr+3, "output filenames", 0, 1));
    if (!strnicmp(optptr,"optlast",7))
        return(SPIT_getopt( &OPTlast,optptr+7, "put options after filenames", 0, 1));
    if (!strnicmp(optptr,"time",4))
        return(SPIT_getopt( &OPTtime,optptr+4, "time execution", 0, 1));
    if (!strnicmp(optptr,"sort",4))
        return((char *)0);
//        return(SPIT_getopt( &OPTsorttype,optptr+4, "sort arguments", 0, 1));
    if (!strnicmp(optptr,"cd",2))
        return(SPIT_getopt( &OPTcd,optptr+2, "change directory per ZRUN_launch", 0, 1));
    if (!strnicmp(optptr,"nl",2))
        return(SPIT_getopt( &OPTnl,optptr+2, "print newline after running tool", 0, 1));
    if (!strnicmp(optptr,"sleep",5))
        return(SPIT_getopt( &OPTsleep,optptr+5, "sleep for n seconds after each launch", 0, 1000));
    if (!strnicmp(optptr,"v",1)) /* let spit parse it */
        return((char *)0);
    if (!strnicmp(optptr,"h",1)) /* let spit parse it */
        return((char *)0);
    if (!strnicmp(optptr,"help",4)) /* let spit parse it */
        return((char *)0);
    if (!strnicmp(optptr, "stderr", 6))
    {
        OPTstderr = 1;
        r= SPIT_getfield( buf,optptr,1,1);
        p = strchr(buf, '=');
        if (p)
        {
            strcpy(OPTstderrfile, p+1);
        } else
            strcpy(OPTstderrfile, "stderr.txt");
        return r;
    } else
    if (!strnicmp(optptr, "stdout", 6))
    {
        OPTstdout = 1;
        r= SPIT_getfield( buf,optptr,1,1);
        p = strchr(buf, '=');
        if (p)
        {
            strcpy(OPTstdoutfile, p+1);
        } else
            strcpy(OPTstdoutfile, "stdout.txt");
        return r;
    }
    else
        return(SPIT_getfield( buf,optptr,1,1));

//      return(NULL);
}

void ZRUN_sleep(int sleeptime)
{
    #if defined(_MSC_VER)
        Sleep((int)(sleeptime*1000.0f));
    #else
        usleep(sleeptime*1000000);
    #endif
}

int main(int argc,char **argv)
{
    int more;
    int i;
    int errors=0;
    FILE *stream = NULL;
    FILE *stream2 = NULL;
    int firsttime = 1;
    double totaltime;

    SPIT_init(SPIT_reset, SPIT_usage, SPIT_opt, SPIT_history);

    totaltime = SPIT_gettime();
    do
    {
        // find first non zrun argument
        int fs = 1;
        while (fs<argc && ZRUN_iszrunopt(argv[fs],1))
            ++fs;
        OPTzrunstartsortat = fs;


        more =  SPIT_parse(argc, argv, TOOL_NAME, TOOL_VERSION);
        if (firsttime)
        {
            if (OPTstderr || OPTverbose==0)
            {
                if (OPTverbose>=3)
                    printf("redirecting stderr to '%s'\n", OPTstderrfile);
                stream = freopen(OPTstderrfile, "w", stderr);
                if (stream==NULL && OPTverbose)
                    printf("Can't redirect stderr to file '%s'\n", OPTstderrfile);
            }
            if (OPTstdout || OPTverbose==0)
            {
                if (OPTverbose>=3)
                    printf("redirecting stdout to '%s'\n", OPTstdoutfile);
                stream2 = freopen(OPTstdoutfile, "w", stdout);
                if (stream2==NULL && OPTverbose)
                    printf("Can't redirect stdout to file '%s'\n", OPTstdoutfile);
            }
            firsttime = 0;
        }

        if (!SPIThelpgiven)
        {

            if (OPTtolower)
            {
                for (i=0; i<SPITargnum; ++i)
                {
                    strlwr(SPITsargv[i]);
                    strlwr(SPITdargv[i]);
                }
            }
            // find first non zrun argument
            int f = 0;
            while (f<SPITargnum && ZRUN_iszrunopt(SPITsargv[f],1))
                ++f;

            if (!OPTmulti && SPITargnum>1)
            {
                // run one at a time with one argument
                for (i=f+1; i<SPITargnum; ++i)
                {
                    if (OPTcd)
                        errors += ZRUN_docd(f,i,i+1,f,SPITargnum);
                    errors += ZRUN_launch(f,i,i+1,f,SPITargnum);
                    ZRUN_sleep(OPTsleep);
                }
            }
            else
            {
                if (OPTmulti==1)
                    OPTmulti=SPITargnum;

                if (f+1<SPITargnum)
                {
                    // launch in groups of multi
                    for (i=f+1; i<SPITargnum; i += OPTmulti)
                    {
                        int last = gmin(SPITargnum,i+OPTmulti);
                        if (OPTcd)
                            errors += ZRUN_docd(f,i,last,f,SPITargnum);
                        errors += ZRUN_launch(f,i,last,f,SPITargnum);
                        ZRUN_sleep(OPTsleep);
                    }
                }
                else
                {
                    // just run once
                    if (OPTcd)
                        errors += ZRUN_docd(f,f+1,SPITargnum,f,SPITargnum);

                    errors += ZRUN_launch(f,f+1,SPITargnum,f,SPITargnum);
                    ZRUN_sleep(OPTsleep);
                }
            }
        }
    } while (more>0);

    if (stream)
    {
        fclose(stream);
        stream = NULL;
        if (!OPTstderr)
            unlink(OPTstderrfile);
    }
    if (stream2)
    {
        fclose(stream2);
        stream2 = NULL;
        if (!OPTstdout)
            unlink(OPTstdoutfile);
    }



    SPIT_restore();

    if (galloccount)
    {
        if (OPTverbose>=2)
        {
            printf(TOOL_NAME " - WARNING MEMORY NOT FREED TO SYSTEM %d\n",galloccount);
            errors = galloccount;
        }
    }

    totaltime = SPIT_gettime()-totaltime;

    if (OPTverbose>=2 && !SPIThelpgiven)
    {
        if (OPTtime)
        {
            if (totaltime>=3600000.0)
                printf("TIME zrun %.2f ms (%.2f seconds) (%.2f minutes) (%.2f hours)",totaltime,totaltime/1000.0,totaltime/60000.0,totaltime/3600000.0);
            else if (totaltime>=60000.0)
                printf("TIME zrun %.2f ms (%.2f seconds) (%.2f minutes)",totaltime,totaltime/1000.0,totaltime/60000.0);
            else if (totaltime>=1000.0)
                printf("TIME zrun %.2f ms (%.2f seconds)",totaltime,totaltime/1000.0);
            else
                printf("TIME zrun %.2f ms",totaltime);
        }
        if (errors || OPTverbose>=4)
        {
            if (OPTtime)
            {
                printf(", ");
            }
            printf("returncode %d",errors);
        }
        if (OPTtime || errors || OPTverbose>=4)
            printf("\n");
    }

    return(errors);
}

