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

const char *SPIT_history[] =
{
"--------------------------------------------------------------------",
"                                                                    ",
"    Authors:    Frank Barchard and Brian Plank                      ",
"    Build with: nmake /f pc.mak                                     ",
"                                                                    ",
"    BGP - 1992/07/27 - Initial version.                             ",
"    BGP - 1992/12/10 - added MAKEFILE style comments for .mak       ",
"    FGB - 1993/05/27 - Spit/Mac version. Backup if source=dest      ",
"    FGB - 1995/01/01 - Rebuilt, updated date                        ",
"    FGB - 1995/11/04 - Allow no extension (used to crash)           ",
"    FGB - 1998/11/17 - Removed strlwr code                          ",
"    FGB - 1999/11/30 - Java/cpp                                     ",
"    FGB - 2000/12/21 - look for 20xx existing dates                 ",
"    FGB - 2002/03/14 - v1.04 sha shader assembler                   ",
"    FGB - 2002/10/17 - v1.05 cs (C Sharp) same as cpp               ",
"    FGB - 2003/01/06 - v1.06 py (Python)                            ",
"    FGB - 2003/01/08 - v1.07 tcl use # & cxx, // (c) 1992-2003 EA   ",
"    FGB - 2003/01/12 - v1.08 ©, bat and scan for any digit not 20xx ",
"    FGB - 2003/01/14 - v1.08b enums                                 ",
"    FGB - 2003/01/20 - v1.09 check for Electronic Arts, no .def     ",
"    FGB - 2003/01/22 - v1.10 backup use binary copy                 ",
"    FGB - 2003/01/23 - v1.11 message shows skip existing non-ea     ",
"    FGB - 2003/01/24 - v1.12 mel vbs ms pl pm                       ",
"    FGB - 2003/01/27 - v1.13 tcl py mak keep first line too         ",
"    FGB - 2003/01/29 - v1.14 (c) for nant/xml and perforce          ",
"    FGB - 2003/01/31 - v1.15 -return for stats/info                 ",
"    FGB - 2003/02/04 - v1.16 scan 40 lines, -w0, reupdate           ",
"    FGB - 2003/02/16 - v1.17 .cp metrowerks                         ",
"    FGB - 2003/03/03 - v1.18 copyleft added, remove .s (asm/cpp)    ",
"    FGB - 2003/03/13 - v1.19 -remove to remove an existing copyr    ",
"    FGB - 2003/03/27 - v1.20 :: comment for .bat files              ",
"    FGB - 2003/09/07 - v1.21 -time/-return more vars                ",
"    FGB - 2004/01/07 - v1.22 .inl (c++)                             ",
"    FGB - 2005/02/01 - v1.23 criterion maps to ea, -year            ",
"    FGB - 2005/02/21 - v1.24 -force non ea copyr to update          ",
"    FGB - 2005/04/03 - v1.25 © and -nodate                          ",
"    FGB - 2005/08/24 - -comma                                       ",
"    FGB - 2005/09/02 - .fx .cg .cgfx .lua, -csl criterion           ",
"                                                                    ",
"--------------------------------------------------------------------",
"\0"
};

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

/* comment styles */
enum CommentStyle { CS_UNKNOWN, CS_C, CS_ASM, CS_MAKE, CS_CPP, CS_BAT, CS_VBS, CS_MAX };

/* add copyright results */
enum AddCopyright { AC_ADD, AC_SKIP, AC_LEAVE, AC_UPDATE, AC_REUPDATE, AC_REMOVE };

const char TextVersion[]="1.25b (2005.9.7)";
#define TOOL_VERSION 125
#define TOOL_NAME "copyr"

char OPTcompany[SPITMAXLINE];
char OPTreturn[SPITMAXLINE];
int OPTyear;
int OPTremove;
int OPTforce;
int OPTnodate;
int OPTcomma;

/*
;
; ABSTRACT
;
; copyr - add/update copyright notice in source files
;
; SUMMARY
;
; copyr -options [files]
;     -c<str> company name (default is Electronic Arts Inc.)
;       -csl Criterion Software Limited and its licensors. All Rights Reserved
;       -ea  Electronic Arts Inc.
;     -r recurse subdirectories. ie copyr -r *.cpp
;     -remove        remove existing copyright notice
;     -force         force copyright update
;     -time          display timing diagnostic
;     -year<n>       specify the current year
;     -return<expr>  return expression (default errorcount)
;                    variables available:
;                      addcount
;                      updatecount (includes reupdatecount)
;                      reupdatecount
;                      skipcount
;                      leavecount
;                      errorcount
;                      filecount
;                      TOOL_VERSION
;                      SPIT_VERSION
;                      year
;                      firstyear
;
; DESCRIPTION
;
; copyr adds or updates a copyright message in source code.  The copyright
; will be in the form:
; // (c) 1992-2005 Electronic Arts Inc.
;
; Copyr is intended to be used on entire projects with wildcards.  So the first step is to determine
; if a file should contain a copyright, and in what form.  copyr bases this decision on the file extension.
; The current list of extensions supported are:
;
;            .c       // C++
;            .h       // C++
;            .cpp     // C++
;            .hpp     // C++
;            .c++     // C++
;            .h++     // C++
;            .cxx     // C++
;            .hxx     // C++
;            .inl     // C++
;            .cc      // C++
;            .hh      // C++
;            .cp      // C++ (metrowerks)
;            .rc      // C++
;            .fx      // C++ (shaders)
;            .cg      // C++
;            .cgfx    // C++
;            .cs      // C#
;            .java    // Java
;            .mel     // Mel Script (Maya)
;            .mak     # Make
;            .py      # Python
;            .tcl     # tcl/tk
;            .pl      # Perl
;            .pm      # Perl
;            .bat     :: batch file
;            .vbs     ' Visual Basic Script
;            .ms      -- MAX Script
            .lua     -- LUA Script


;   .c       // C++
;   .h       // C++
;   .cpp     // C++
;   .hpp     // C++
;   .c++     // C++
;   .h++     // C++
;   .cxx     // C++
;   .hxx     // C++
;   .inl     // C++
;   .cc      // C++
;   .hh      // C++
;   .cp      // C++ (metrowerks)
;   .rc      // C++
;   .fx      // C++ (shaders)
;   .cg      // C++
;   .cgfx    // C++
;   .cs      // C#
;   .java    // Java
;   .mel     // Mel Script (Maya)
;   .mak     # Make
;   .py      # Python
;   .tcl     # tcl/tk
;   .pl      # Perl
;   .pm      # Perl
;   .bat     :: batch file
;   .vbs     ' Visual Basic Script
;   .ms      -- MAX Script
;   .lua     -- LUA Script
;
; The file is scanned for an existing copyright notice.  If one exists, its starting date and line number
; are noted.  The notice will be updated in place.
; Otherwise a new notice will be added to the first line of the file.
; For unix scripts (mak, py, tcl, pl, pm) if the first line starts with #! the copyright added to the second
; line of the file instead of the first.
;
; The copyright symbol (c) is used.  This is a ANSI 8 bit standard code 169 (0xa9).  On windows you can use
; charmap.exe to type/copy/paste or press ALT+0169
;
; RETURNS
;
; error count by default (0 for no errors)
; -return value if specified
;
; SEE ALSO
;
; gsr, Spit
;
; SAMPLE OUTPUT
;
; D:\tools\util>copyr copyr.cpp
; Processing copyr.cpp , backing up, updating copyright, done.
;
; END ABSTRACT
;
*/

const static struct
{
    const char *ext;
    CommentStyle cstyle;
} languageExtensions[]=
{
    { ".c",   CS_CPP },
    { ".h",   CS_CPP },
    { ".cpp", CS_CPP },
    { ".hpp", CS_CPP },
    { ".c++", CS_CPP },
    { ".h++", CS_CPP },
    { ".cxx", CS_CPP },
    { ".hxx", CS_CPP },
    { ".inl", CS_CPP },
    { ".cc",  CS_CPP },
    { ".hh",  CS_CPP },
    { ".cp",  CS_CPP }, // metrowerks
    { ".rc",  CS_CPP },
    { ".cs",  CS_CPP },
    { ".java",CS_CPP },
    { ".mel", CS_CPP },
    { ".fx",  CS_CPP }, // microsoft shaders (pc/xbox)
    { ".cg",  CS_CPP },
    { ".cgfx",CS_CPP },

    { ".mak", CS_MAKE },
    { ".py",  CS_MAKE },
    { ".tcl", CS_MAKE },
    { ".pl",  CS_MAKE },
    { ".pm",  CS_MAKE },

    { ".bat", CS_BAT },

    { ".vbs", CS_VBS },

    { ".ms",  CS_MAX },
    { ".lua", CS_MAX }
};

static CommentStyle commentstyle(const char *s)
{

    int i;

    if (s)
    {
        for (i=0; i<sizeof(languageExtensions)/sizeof(languageExtensions[0]); i++)
        {
            if (!strcmp(s, languageExtensions[i].ext))
            {
                return languageExtensions[i].cstyle;
            }
        }
    }
    return CS_UNKNOWN;
}

static const int LINE_SIZE=SPITMAXLINE; // 16k as of 2005.01.07
static const int SEARCH_LINES=40;   // scan 40 lines for copyright.  Any beyond that should be left alone.

static int GetCurrentYear(void)
{
    time_t  ltime;
    struct tm *today;

    time(&ltime);
    today = localtime(&ltime);
    int todayyear = 1900 + today->tm_year;
    return todayyear;
}

static AddCopyright addcopyright(char *inpath, char *outpath, CommentStyle type, int *minfirstyear, int OPTremove, int OPTforce, int OPTyear, int OPTcomma)
{
    FILE *fi;
    FILE *fo=NULL;
    int c;
    int ok=0;
    int found;
    int foundline;
    int line;
    int todayyear, firstyear=0;
    static char linebuf[LINE_SIZE+1];
    static char tmpbuf[LINE_SIZE+1];
    AddCopyright result = AC_SKIP;

    fi=fo=NULL;

    if ( (fi = fopen(inpath, "r")) == NULL )
    {
        perror(inpath);
        goto err;
    }

    if (OPTwrite)
    {
        if ( (fo = fopen(outpath, "w")) == NULL )
        {
            perror(outpath);
            goto err;
        }
    }

    todayyear = OPTyear;
    if (!todayyear)
        todayyear = GetCurrentYear();

    found = 0;
    foundline = 0;
    for (line=0; line<SEARCH_LINES; line++)
    {
        if (fgets(linebuf,LINE_SIZE,fi))
        {
            strcpy(tmpbuf,linebuf);
            strlwr(tmpbuf);

            if (strstr(tmpbuf, "copyright") ||
                strstr(tmpbuf, "copyleft") ||
                strstr(tmpbuf, "(c)") ||
                strchr(linebuf, '©'))
            {
                found = 1;

                if (!OPTforce &&
                    !(strstr(tmpbuf, "electronic arts")
                   || strstr(tmpbuf, "maxis")
                   || strstr(tmpbuf, "westwood")
                   || strstr(tmpbuf, "blackbox")
                   || strstr(tmpbuf, "tiberon")
                   || strstr(tmpbuf, "criterion")
                    ))
                {
                    found = 2; // a non-EA copyright?  Better leave it alone
                }
                else
                {
                    foundline=line;
                    firstyear = 0;
                    for ( char *datepos = tmpbuf; *datepos; ++datepos)
                    {
                        if (*datepos>='0' && *datepos<='9')
                        {
                            firstyear = atoi(datepos);
                            break;
                        }
                    }
                    if (firstyear) break;
                }
            }
        }
    }
    rewind(fi);

    if (OPTwrite)
    {
        // copy lines up to line that copyright was found on

        if (found==1)
        {
            for (line=0; line<foundline; ++line)
            {
                if (fgets(linebuf,LINE_SIZE,fi))
                {
                    fputs(linebuf,fo);
                }
            }
            fgets(linebuf,LINE_SIZE,fi);
        }

        // if adding to unix perl script, first line should remain
        // #!perl -w

        if (found==0 && type==CS_MAKE)
        {
            if (fgets(linebuf,LINE_SIZE,fi))
            {
                if (linebuf[0]=='#' && linebuf[1]=='!')
                    fputs(linebuf,fo);
                else
                    rewind(fi);
            }
        }
    }


    // output copyright

    if ((firstyear >= 1986) && (firstyear < todayyear))
    {
        if (firstyear<*minfirstyear)
            *minfirstyear = firstyear;

        if (OPTremove)
        {
            result = AC_REMOVE;
        }
        else
        {
            result = AC_UPDATE;
            if (OPTwrite)
            {
                switch (type)
                {
                    case CS_C:
                        fprintf(fo,
                        "/* (c) %d-%d %s*/\n",
                        firstyear, todayyear, OPTcompany);
                        break;
                    case CS_ASM:
                        fprintf(fo,
                        "; (c) %d-%d %s\n",
                        firstyear, todayyear, OPTcompany);
                        break;

                    case CS_MAKE:
                        fprintf(fo,
                        "# (c) %d-%d %s\n",
                        firstyear, todayyear, OPTcompany);
                        break;
                    case CS_CPP:
                        fprintf(fo,
                        "// (c) %d-%d %s\n",
                        firstyear, todayyear, OPTcompany);
                        break;

                    case CS_BAT:
                        fprintf(fo,
                        ":: (c) %d-%d %s\n",
                        firstyear, todayyear, OPTcompany);
                        break;

                    case CS_VBS:
                        fprintf(fo,
                        "' (c) %d-%d %s\n",
                        firstyear, todayyear, OPTcompany);
                        break;

                    case CS_MAX:
                        fprintf(fo,
                        "-- (c) %d-%d %s\n",
                        firstyear, todayyear, OPTcompany);
                        break;
                }
            }
        }
    }
    else
    {
        if (found==2)
            result = AC_LEAVE;
        else if (found==1)
        {
            if (OPTremove)
            {
                result = AC_REMOVE;
            }
            else
            {
                if (todayyear==firstyear)
                    result = AC_UPDATE;
                else
                    result = AC_REUPDATE;
            }
        }
        else
            result = AC_ADD;

        if (found!=2 && OPTwrite && result != AC_REMOVE)
        {
            switch (type)
            {
                case CS_C:
                    fprintf(fo,
                    "/* (c) %d %s */\n",
                    todayyear, OPTcompany);
                    break;
                case CS_ASM:
                    fprintf(fo,
                    "; (c) %d %s\n",
                    todayyear, OPTcompany);
                    break;
                case CS_MAKE:
                    fprintf(fo,
                    "# (c) %d %s\n",
                    todayyear, OPTcompany);
                    break;
                case CS_CPP:
                    fprintf(fo,
                    "// (c) %d %s\n",
                    todayyear, OPTcompany);
                    break;
                case CS_BAT:
                    fprintf(fo,
                    ":: (c) %d %s\n",
                    todayyear, OPTcompany);
                    break;

                case CS_VBS:
                    fprintf(fo,
                    "' (c) %d %s\n",
                    todayyear, OPTcompany);
                    break;

                case CS_MAX:
                    fprintf(fo,
                    "-- (c) %d %s\n",
                    todayyear, OPTcompany);
                    break;

            }
        }
    }

    if (OPTwrite)
    {
        // copy rest of file as is

        while ((c=getc(fi))!=EOF)
        {
            putc(c,fo);
        }
    }

    ok=1;
err:
    if (fi) fclose(fi);
    if (fo) fclose(fo);

    return result;
}

int copyfile(char *inpath, char *outpath)
{
    FILE *fi;
    FILE *fo;
    int c;
    int ok=0;

    fi=fo=NULL;

    if ( (fi = fopen(inpath, "rb")) == NULL )
    {
        perror(inpath);
        goto err;
    }

    if ( (fo = fopen(outpath, "wb")) == NULL )
    {
        perror(outpath);
        goto err;
    }

    while((c=getc(fi))!=EOF)
    {
        putc(c,fo);
    }
    ok=1;
err:
    if (fi) fclose(fi);
    if (fo) fclose(fo);

    return ok;
}

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

void SPIT_reset(void)
{
    strcpy(OPTcompany,"Electronic Arts Inc.");
    strcpy(OPTreturn,"");
    OPTyear = 0;
    OPTremove = 0;
    OPTforce = 0;
    return;
}

void SPIT_usage(const char *prgname, const char *envname, const char *urlname, int utype)
{
    if (utype)
    {
        printf("(c) 1992-2005 Electronic Arts Inc.\n");
        printf("Rev %s\n", TextVersion);
        printf("\n");
        printf("name:      copyr\n");
        printf("purpose:   add/update EA Copyright message to the start of source files\n");
        printf("usage:     copyr <file1> <file2>...\n");
        printf("author:    Frank Barchard\n");
        printf("options:   copyr supports Spit option parsing\n");
        printf("           -c<str> company name (default is Electronic Arts Inc.)\n");
        printf("             -csl Criterion Software Limited and its licensors. All Rights Reserved\n");
        printf("             -ea  Electronic Arts Inc.\n");
        printf("           -r recurse subdirectories. ie copyr -r *.cpp\n");
        printf("           -remove        remove existing copyright notice\n");
        printf("           -force         force copyright update\n");
        printf("           -time          display timing diagnostic\n");
        printf("           -year<n>       specify the current year\n");
        printf("           -return<expr>  return expression (default errorcount)\n");
        printf("                          variables available: \n");
        printf("                            addcount\n");
        printf("                            updatecount (includes reupdatecount)\n");
        printf("                            reupdatecount\n");
        printf("                            skipcount\n");
        printf("                            leavecount\n");
        printf("                            errorcount\n");
        printf("                            filecount\n");
        printf("                            TOOL_VERSION\n");
        printf("                            SPIT_VERSION\n");
        printf("                            year\n");
        printf("                            firstyear\n");

        printf("notes:     Supports C/C++, C#, RC, Make, Tcl and Python.\n");
        printf("           Files with unknown extensions are skipped.\n");
        printf("           Files with existing non-EA copyrights will be 'left'.\n");
        printf("            .c       // C++ \n");
        printf("            .h       // C++ \n");
        printf("            .cpp     // C++ \n");
        printf("            .hpp     // C++ \n");
        printf("            .c++     // C++ \n");
        printf("            .h++     // C++ \n");
        printf("            .cxx     // C++ \n");
        printf("            .hxx     // C++ \n");
        printf("            .inl     // C++ \n");
        printf("            .cc      // C++ \n");
        printf("            .hh      // C++ \n");
        printf("            .cp      // C++ (metrowerks)\n");
        printf("            .rc      // C++ \n");
        printf("            .fx      // C++ (shaders)\n");
        printf("            .cg      // C++\n");
        printf("            .cgfx    // C++\n");

        printf("            .cs      // C#\n");
        printf("            .java    // Java \n");
        printf("            .mel     // Mel Script (Maya) \n");

        printf("            .mak     # Make\n");
        printf("            .py      # Python\n");
        printf("            .tcl     # tcl/tk\n");

        printf("            .pl      # Perl\n");
        printf("            .pm      # Perl\n");

        printf("            .bat     :: batch file\n");

        printf("            .vbs     ' Visual Basic Script\n");

        printf("            .ms      -- MAX Script\n");
        printf("            .lua     -- LUA Script\n");
        printf("\n");
    }
}

char *SPIT_opt(const char *optptr)
{
    char *d=NULL;
    switch (tolower(*optptr))
    {
        case 'c':
            if (!strnicmp(optptr,"csl",3))
            {
                strcpy(OPTcompany,"Criterion Software Limited and its licensors. All Rights Reserved");
                d = (char *)optptr+3;
            }
            else
                d = SPIT_getstropt( OPTcompany,optptr+1, "company name", "", SPITYES, SPITMAXLINE);
            break;

        case 'e':
            if (!strnicmp(optptr,"ea",2))
            {
                strcpy(OPTcompany,"Electronic Arts Inc.");
                d = (char *)optptr+2;
            }
            break;

        case 'f':
            if (!strnicmp(optptr,"force",5))
            {
                d = SPIT_getopt( &OPTforce,optptr+5, "force copyright", 0, 1);
            }
            break;

        case 'r':
            if (!strnicmp(optptr,"return",6))
            {
                d = SPIT_getfield( OPTreturn,optptr+6,1,0);
            }
            else if (!strnicmp(optptr,"remove",6))
            {
                d = SPIT_getopt( &OPTremove,optptr+6, "remove copyright", 0, 1);
            }
            break;

        case 'y':
            if (!strnicmp(optptr,"year",4))
            {
                d = SPIT_getopt( &OPTyear,optptr+4, "current year", 1981, 2400);
            }
            break;

        default: ;
    }
    return(d);
}

int main(int argc, char *argv[])
{
    int more;
    char inpath[SPITMAXLINE];
    char backpath[SPITMAXLINE];
    char outpath[SPITMAXLINE];
    char *ext;
    int arg;
    int errorcount=0;
    int addcount=0;
    int updatecount=0;
    int reupdatecount=0;
    int leavecount=0;
    int skipcount=0;
    int filecount=0;
    int firstyear=GetCurrentYear();

    SPIT_init(SPIT_reset, SPIT_usage, SPIT_opt, SPIT_history);
    double totaltime = SPIT_gettime();

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

        for (arg=0; arg < SPITargnum; ++arg)
        {
            strcpy(inpath, SPITsargv[arg]);
            strlwr(inpath);
            strcpy(outpath, SPITdargv[arg]);
            strlwr(outpath);
            ext = strrchr(inpath,'.');
            printf("Processing %s ", inpath);
            fflush(stdout);

            AddCopyright result = AC_SKIP;
            CommentStyle cstyle = commentstyle(ext);

            if (cstyle!=CS_UNKNOWN)
            {
                if (OPTwrite && !strcmp(inpath,outpath))
                {
                    strcpy(backpath,outpath);
                    if (strrchr(backpath,'.'))
                        *strrchr(backpath,'.')='\0';
                    strcat(backpath,".old");

                    /* make backup of file */
                    printf(", backing up");
                    fflush(stdout);

                    if (!copyfile(inpath, backpath))
                    {
                        printf(", FILE ERROR!\n");
                        ++errorcount;
                        continue;
                    }
                }
                else
                    strcpy(backpath,inpath);

                result = addcopyright(backpath, outpath, cstyle, &firstyear, OPTremove, OPTforce, OPTyear, 0);
            }
            switch (result)
            {
                case AC_ADD:
                    printf(", added.\n");
                    ++addcount;
                    break;

                case AC_UPDATE:
                    printf(", updated.\n");
                    ++updatecount;
                    break;

                case AC_REUPDATE:
                    printf(", reupdated.\n");
                    ++updatecount;
                    ++reupdatecount;
                    break;

                case AC_SKIP:
                    printf(", skipped.\n");
                    ++skipcount;
                    break;

                case AC_LEAVE:
                    printf(", leaving NON-EA Copyright.\n");
                    ++leavecount;
                    break;

                case AC_REMOVE:
                    printf(", remove.\n");
                    ++updatecount;
                    break;

            }
            ++filecount;
            fflush(stdout);
        }
    } while (more>0);

    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,"addcount",     addcount);
        EXPR_set(expr,"updatecount",  updatecount);
        EXPR_set(expr,"reupdatecount",reupdatecount);
        EXPR_set(expr,"skipcount",    skipcount);
        EXPR_set(expr,"leavecount",   leavecount);
        EXPR_set(expr,"filecount",    filecount);
        EXPR_set(expr,"year",         GetCurrentYear());
        EXPR_set(expr,"firstyear",    firstyear);

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

        EXPR_close(expr);
        RANGER_close(r);
    }

    if (OPTverbose==1)
        puts("");

    totaltime = SPIT_gettime()-totaltime;
    if (OPTtime)
    {
        if (totaltime>=3600000.0)
            printf("TIME " TOOL_NAME " %.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 " TOOL_NAME " %.2f ms (%.2f seconds) (%.2f minutes)",totaltime,totaltime/1000.0,totaltime/60000.0);
        else if (totaltime>=1000.0)
            printf("TIME " TOOL_NAME " %.2f ms (%.2f seconds)",totaltime,totaltime/1000.0);
        else
            printf("TIME " TOOL_NAME " %.2f ms",totaltime);
    }

    SPIT_restore();

    if (galloccount)
        printf("galloccount = %i\n", galloccount);

    return returncode;
}

