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

const char *SPIT_history[] =
{
"------------------------------------------------------------------",
"                                                                  ",
"                          Cymbal v1.54                            ",
"                                                                  ",
"                       by Yggy King, EAC                          ",
"                                                                  ",
"                   Main program - Oct 17, 2001                    ",
"                                                                  ",
"------------------------------------------------------------------",
"                                                                  ",
" Version Date   SE History:                                       ",
" ------- ----   -- --------                                       ",
"    1.00 950725 YK initial version                                ",
"    1.10 951211 YK Watcom .LIB support                            ",
"    1.11 951212 YK OPTunderscores, OPTappend, other tweaks        ",
"    1.12 951212 FB use spit Zfindfirst for win95 support          ",
"    1.13 951213 YK fix bug in parsing Microsoft objects           ",
"    1.14 951213 FB CYMfindfirst use Zfindfirst                    ",
"    1.15 951213 YK show symbols in 'LINKS IN', OPThtml            ",
"    1.16 951213 FB remove blank line after headings               ",
"    1.17 951214 YK -sall option                                   ",
"    1.18 951218 FB psx .obj support                               ",
"    1.19 951218 FB sgi .o/.a                                      ",
"    1.20 960724 FB -v1 with -sall to generate terse report        ",
"    1.21 960909 FB PC extract source file name                    ",
"    1.22 960910 FB PSX extract source file name/href to source    ",
"    1.23 961030 FB remove @ obj name                              ",
"    1.24 970130 FB remove @ in shown text too                     ",
"    1.25 970306 FB wdis                                           ",
"    1.26 970416 KW parsing of wdis output                         ",
"    1.27 970903 YK added symbol reference count to report         ",
"    1.28 980529 FB refer to ftp site for source                   ",
"    1.29 980601 SK uses FindFile module (not CYMfindfirst anymore)",
"    1.30 980707 SK fixed problem with dir\\dir\\*.obj paths       ",
"    1.31 980717 SK psx can do libs now.  fixed psx relative paths ",
"    1.32 981009 FB title for pop on ftp reference                 ",
"    1.33 981026 FB -tn64,pc                                       ",
"    1.34 981211 KW Added MS Dumpbin support                       ",
"    1.35 981211 KW Dumpbin externals,references and file size     ",
"    1.36 981211 KW Function size, fix for external definitions    ",
"    1.37 990208 FB SPIT api change                                ",
"    1.38 991223 JF Playstation 2                                  ",
"    1.39 001003 KW PS2 - increased section name buffer size       ",
"    1.40 001017 KW removed ftp reference from source links        ",
"    1.41 001017 KW added -l (source link) and -m (module name)    ",
"    1.42 001018 KW use only -l to specify link path prefix        ",
"    1.43 010206 KW added xbox support                             ",
"    1.44 010214 KW added </PRE> and <PRE> around <H4></H4> tags to fix Opera formatting ",
"    1.45 010409 KW -lib option                                    ",
"    1.46 010411 KW added list of libraries to LINKS IN section    ",
"    1.47 010428 KW added extracting of source file in ps2cym      ",
"    1.50 011017 FB sn elf tools work on gamecube object files     ",
"    1.51 011212 KW fixed getlibfrompathname when path has drive letter ",
"    1.52 010108 KW fixed code data size calculations from dumpbin ",
"    1.53 020215 KW added gccym                                    ",
"    1.54 020218 KW pccym - fixed parse of exported functions      ",
"                                                                  ",
"------------------------------------------------------------------",
"\0"
};


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

#if defined(_MSC_VER) && _MSC_VER<1400
#pragma optimize("g", off)
#endif

#include <stdio.h>
#include <limits.h>
#include "spit.h"
#include "findfile.h"
#include "wildmatch.h"
#include "stdgimex.h"
#include "cymbal.h"

#ifndef F_OK
#define F_OK 0
#endif
#ifndef R_OK
#define R_OK 4
#endif

const char TextVersion[]="1.54 (Samba) 2007.12.4";
#define TOOL_VERSION 154
#define TOOL_NAME "cymbal"

/* global option flags */
int     OPTobjtype = WATCOM;        /* object file type; 0 for .cym file input */
char    OPTsourcelinkprefix[SPITMAXLINE]; /* prefix to be appended to source code link (used with -html) e.g.: "http://www.eac.ea.com/" */
char   *OPTsymbols[MAX_SYMBOLS];    /* symbols to analyze */
int     OPTnsymbols = 0;            /* number of symbols in array */
char   *OPTfiles[MAX_FILES];        /* files to analyze */
char    OPTstatfile[SPITMAXLINE];   /* stats files to generate */
int     OPTnfiles = 0;              /* number of files in array */
int     OPTdefine = 0;              /* define query */
int     OPTrefer = 0;               /* reference query */
int     OPTlinks = 0;               /* "links" query */
int     OPTrecurselevel = -1;       /* levels of recursion, -1 for complete */
int     OPTunderscores = 0;         /* strip underscores from symbols */
int     OPTappend = 0;              /* append symbol info to .cym file */
int     OPThtml = 0;                /* HTML output. */
char    OPTlibrary[SPITMAXLINE];    /* library from which the obj files came */
int     OPTlibnamefrompath = 0;     /* get library name from first directory in obj file path */

#define TMPDIR      "cym0000"
#define TMPFILE     "cym0001.tmp"

/* These are modified if HTML is turned on. */

static char *preheader = "";
static char *postheader = "";


/* File Name Type Checking */

#define SPITPATHNORM        1                       /* Normal:              file.ext */
#define SPITPATHABS         2                       /* Absolute paths:      d:... OR d:\... OR \... OR \... */
#define SPITPATHDIR         3                       /* Multiple dir paths:  dir1\dir2\dir3\file.ext */
#define SPITPATHOPT         4                       /* Options:             -f5, .f4, ... */
char    SPIT_typeofname(char *nm);


char SPIT_typeofname(char *nm)
{
    char type = SPITPATHNORM;

    if (strlen(nm) > 1)
    {
        if ((nm[0] == '-') || (nm[0] == '+') || (nm[0] == '.' && nm[1] != '\\' && nm[1] != '/' && nm[1] != '.'))
            type = SPITPATHOPT;
        /* This covers the <drive>:\<path> and <drive>:<filename> possibilities */
        else if (isalpha((int)nm[0]) && nm[1] == ':')
            type = SPITPATHABS;
        /* This covers the \<path> possibility */
        else if (nm[0] == '\\' || nm[0] == '/')
            type = SPITPATHABS;
        else
        {
            /* Check if it has multiple directories */
            while (*nm != '\0')
            {
                if (*nm == '\\' || *nm == '/')
                    type = SPITPATHDIR;
                ++nm;
            }
        }
    }
    return type;
}


#if 0
/*
;
; ABSTRACT
;
; cymbal - symbol file analysis tool extraordinaire
;
; SUMMARY
;
; cymbal [-options] [files]
;   -t<type>          object file type (sgi, u64, ...)
;   -s<symbol>        symbol to search for (default all)
;   -r                recurse subdirectories
;   -f<file>          file to search for (default all)
;   -def              look for symbol definitions
;   -ref              look for symbol references
;   -links            look for files that symbol/file links in
;   -all              def + ref + links (default)
;   -under            keep underscores (default strip 'em)
;   -html             HTML output
;   -l<linkpath>      link path to be prepended to source code link (use with -html only)
;   -lib              get library name from path of object file(s) or library filename(s)
;   -lib<libraryname> assign libraryname to each object in cym file
;   -append           append symbol info to .cym file
;   -<n>              n levels of recursion (default complete)
;
;  @file              response file, containing a list of options and files
;   -=<name>          specifies output file name for interleaving
;
; DESCRIPTION
;
; This utility analyzes symbol information to determine how much
; code gets brought in when a symbol is referenced.
;
; RETURNS
;
; 0 if no errors else number of errors
;
; SEE ALSO
;
;
;
; SAMPLE OUTPUT
;
;
; END ABSTRACT
;
*/
#endif

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

void SPIT_reset(void)
{
    *OPTsourcelinkprefix = 0;
    *OPTlibrary = 0;

    return;
}

void SPIT_usage(const char *prgname, const char *envname, const char *urlname, int utype)
{
    printf("\n%s v%s %s by Yggy King, EAC\n\n", prgname, TextVersion, __DATE__);

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

    switch (utype)
    {
        case 0:
            return;

        case 1:
            break;

        case 3:
            printf("VERSION: %s v%s %s, Spit v%.2f\n", SPIT_MACHINE, TextVersion, __DATE__, (float)SPIT_VERSION/100.0f);
            printf("SUMMARY: %s is a symbol analysis tool\n", prgname);
            printf("         SPIT environment variable is %s\n",envname);
            puts("");
            puts("    -t<type>          object file type (sgi, u64, pc; default pc)");
            puts("    -s<symbol>        symbol to search for, repeat as desired (default all)");
            puts("    -f<file>          file to search for, repeat as desired (default all)");
            puts("    -def              look for symbol definitions");
            puts("    -ref              look for symbol references");
            puts("    -links            look for files that symbol/file links in");
            puts("    -all              same as \"-def -ref -links\" (default)");
            puts("    -under            keep underscores (default strip 'em)");
            puts("    -html             generate HTML output (default plain text)");
            puts("    -l<linkpath>      link path to be prepended to source code link (use with -html only)");
            puts("    -lib              get library name from path of object file(s) or library filename(s)");
            puts("    -lib<libraryname> assign libraryname to each object in cym file");
            puts("    -append           append symbol info to .cym file (default overwrite)");
            puts("    -<n>              n levels of recursion (default complete)");

        case 2:
            puts("");
            if (utype == 2)
            {
                printf("%s -h for more help\n", prgname);
                printf("type '%s -help' for web page (%s)\n", prgname,urlname);
                break;
            }
            else
            {
                puts("   @file          response file, containing a list of options and files");
                puts("    -=<name>      specifies output file name");
                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("");
                puts("Examples: ");
                puts("    Find definition of and references to symbol abortmessage:");
                puts("        cymbal -sabortmessage -ref -def libeac.a");
                puts("    Find all symbols defined in files key.o and remkey.o within library:");
                puts("        cymbal -fkey.o -fremkey.o -def libeac.a");
                puts("    List files linked in by symbol joystick in pre-built symbol file:");
                puts("        cymbal -sjoystickn -links symbols.cym=report.txt");
                puts("    List files brought in if init.o linked, checking info in all .obj files:");
                puts("        cymbal -finit.o -links *.obj");
            }
            break;

        default:
            return;
    }

    exit(0);
}

char *SPIT_opt(const char* optptr)
{
    switch (*optptr|(char)' ')
    {
        case 't':
            if (strnicmp(optptr+1, "sgi", 3) == 0)
            {
                OPTobjtype = SGI;
                return((char *)optptr+4);
            }
            else if (strnicmp(optptr+1, "n64", 3) == 0)
            {
                OPTobjtype = U64;
                return((char *)optptr+4);
            }
            else if (strnicmp(optptr+1, "pc", 2) == 0)
            {
                OPTobjtype = WATCOM;
                return((char *)optptr+3);
            }
            else if (strnicmp(optptr+1, "gc", 2) == 0)
            {
                OPTobjtype = GC;
                return((char *)optptr+3);
            }
            else if (strnicmp(optptr+1, "psx", 3) == 0)
            {
                OPTobjtype = PSX;
                return((char *)optptr+4);
            }
            else if (strnicmp(optptr+1, "ps2", 3) == 0)
            {
                OPTobjtype = PS2;
                return((char *)optptr+4);
            }
            else if (strnicmp(optptr+1, "xbox", 2) == 0)
            {
                OPTobjtype = WATCOM;
                return((char *)optptr+5);
            }
            break;

        case 's':
        {
            char *endword;

            endword = (char *)++optptr;
            while (*endword && !isspace(*endword))
                ++endword;

            if (OPTnsymbols < MAX_SYMBOLS)
            {
                    /* add symbol to array */
                OPTsymbols[OPTnsymbols] = (char *) galloc((int)(endword - optptr) + 1);
                strncpy(OPTsymbols[OPTnsymbols], optptr, (int)(endword - optptr));
                OPTsymbols[OPTnsymbols][endword - optptr] = '\0';
                OPTnsymbols++;
            }
            else
            {
                char saveend = *endword;

                *endword = '\0';
                fprintf(stderr, "cymbal: ERROR: out of symbol space, ignoring \'%s\'\n", optptr + 1);
                *endword = saveend;
            }

            return((char *)endword);
        }

        case 'f':
        {
            char *endword;

            endword = (char *)(++optptr);
            while (!isspace(*endword))
                ++endword;

            if (OPTnfiles < MAX_FILES)
            {
                /* add filename to array */

                OPTfiles[OPTnfiles] = (char *) galloc((int)(endword - optptr) + 1);
                strncpy(OPTfiles[OPTnfiles], optptr, (int)(endword - optptr));
                OPTfiles[OPTnfiles][endword - optptr] = '\0';
                OPTnfiles++;
            }
            else
            {
                char saveend = *endword;

                *endword = '\0';
                fprintf(stderr, "cymbal: ERROR: out of file space, ignoring \'%s\'\n", optptr + 1);
                *endword = saveend;
            }

            return((char *)endword);
        }

        case 'd':
            if (strnicmp(optptr, "def", 3) == 0)
            {
                OPTdefine = 1;
                return((char *)optptr + 3);
            }
            break;

        case 'r':
            if (strnicmp(optptr, "ref", 3) == 0)
            {
                OPTrefer = 1;
                return((char *)optptr + 3);
            }
            break;

        case 'l':
            if (strnicmp(optptr, "links", 5) == 0)
            {
                OPTlinks = 1;
                return((char *)optptr + 5);
            }
            else if (strnicmp(optptr, "lib", 3) == 0)
            {
                char *endword;

                optptr+=3;
                endword = (char *)(optptr);
                while (!isspace(*endword))
                    ++endword;

                strncpy(OPTlibrary, optptr, endword - optptr);
                OPTlibrary[endword - optptr] = '\0';

                if(!OPTlibrary[0])
                    OPTlibnamefrompath = 1;

                return((char *)endword);
            }
            else    /* source link prefix */
            {
                char *endword;

                endword = (char *)(++optptr);
                while (!isspace(*endword))
                    ++endword;

                strncpy(OPTsourcelinkprefix, optptr, endword - optptr);
                OPTsourcelinkprefix[endword - optptr] = '\0';

                return((char *)endword);
            }

            break;

        case 'u':
            if (strnicmp(optptr, "under", 5) == 0)
            {
                OPTunderscores = 1;
                return((char *)optptr + 5);
            }
            break;

        case 'h':
            if (strnicmp(optptr, "html", 4) == 0)
            {
                OPThtml = 1;
                return((char *)optptr + 4);
            }
            break;

        case 'a':
            if (strnicmp(optptr, "all", 3) == 0)
            {
                OPTrefer = 1;
                OPTdefine = 1;
                OPTlinks = 1;
                return((char *)optptr + 3);
            }
            else if (strnicmp(optptr, "append", 6) == 0)
            {
                OPTappend = 1;
                return((char *)optptr + 6);
            }
            break;

        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            return((char *)SPIT_getopt( &OPTrecurselevel,optptr, "-<n>", 0, 100));

        default:
            break;
    }

    return((char *)NULL);
}

#if 0
/*
;
;   cleanup
;
*/
#endif

void cleanup()
{
    RFindData c_file;
    RHandle   hFile;

    if (access(TMPDIR, F_OK) == 0)
    {
        RChDir(TMPDIR);
        if ((hFile = RFindFirst(&c_file)) != RNOTFOUND)
        {
            do
            {
                remove(RName(c_file));
            }while(RFindNext(hFile, &c_file));
            RFindClose(hFile, &c_file);
        }
        RChDir("..");
        (void)RRmDir(TMPDIR);
    }
}

#if 0
/*
;
;   cymbal - symbol analysis routine
;
*/
#endif

int cymbal(char *cymfname, char *reportfname)
{
    CYMINFO *cyminfo;

        /* Load symbol information. */
    cyminfo = cymload(cymfname);

        /* Generate report for selected symbols or files. */
    return cymreport(reportfname, cyminfo,
                     OPTsymbols, OPTnsymbols,
                     OPTfiles, OPTnfiles,
                     OPTdefine, OPTrefer, OPTlinks, OPTrecurselevel);
}

#if 0
/*
;
;   cymload - load symbol info from .cym file
;
*/
#endif

CYMINFO *cymload(char *cymfname)
{
    FILE        *cym;
    CYMINFO     *cyminfo = NULL;
    FILEINFO    *fileinfo;
    SYMBOL      *symbol;
    SYMLIST     *sl;
    char        buf[256];

        /* Open file. */
    cym = fopen(cymfname, "r");
    if (!cym)
        return NULL;

        /* Create info structure. */
    cyminfo = cinew();
    if (!cyminfo)
    {
        fclose(cym);
        return NULL;
    }

        /* Parse the file. */
    buf[0] = '\0';
    while (!feof(cym))
    {
        char *startname, *endname;
        char *startlib,  *endlib;

            /* Get a filename line. */
        while (!feof(cym) && (buf[0] != '['))
        {
            fgets(buf, sizeof(buf), cym);
        }

        if (feof(cym))
            break;

        /* Create file info structure and add to head of
           cyminfo list. */
        for (startname=&buf[1], endname=startname; *endname && (*endname != ']'); ++endname)
            ;
        *endname = '\0';

        /* get library name too */
        if(endname[1]=='[')
        {
            for (startlib=&endname[2], endlib=startlib; *endlib && (*endlib != ']'); ++endlib)
            ;
            *endlib = '\0';
        }
        else
        {
            startlib = endname;
        }

        fileinfo = finew(startname,startlib);
        fileinfo->next = cyminfo->files;
        cyminfo->files = fileinfo;

            /* Parse information for file. This loop ends
               at end of file or when next filename is reached. */
        do
        {
            fgets(buf, sizeof(buf), cym);
            if (strncmp(buf, "size=", 5) == 0)
            {
                sscanf(buf+5, "%d", &fileinfo->size);
            }
            else if (strncmp(buf, "source=", 7) == 0)
            {
                char tempname[512];
                char *t;

                sscanf(buf+7, "%s", tempname);
                t = strchr(tempname,',');
                if (t)
                {
                    *t++ = 0;
                    sscanf(t, "%d", &fileinfo->sourcedate);
                }

                fileinfo->sourcename = (char *) galloc(strlen(tempname)+1);
                strcpy(fileinfo->sourcename,tempname);

            }
            else if (strncmp(buf, "define=", 7) == 0)
            {
                char        *name;
                SYMTYPE     type = FUNCTION;        /* Default type */
                SYMSCOPE    scope = GLOBAL;         /* Default scope */
                int         size = 0;
                char        *ptr;

                name = buf+7;
                ptr = name;
                /* Find end of name and null-terminate if needed. */
                while (*ptr && (*ptr != ','))
                    ++ptr;

                /* Type. */
                if (*ptr == ',')
                {
                    *ptr++ = '\0';      /* null-terminate name */

                    if (strncmp(ptr, "FUNCTION", 8) == 0)
                    {
                        type = FUNCTION;
                        ptr += 8;
                    }
                    else if (strncmp(ptr, "DATA", 4) == 0)
                    {
                        type = DATA;
                        ptr += 4;
                    }

                    /* Scope */
                    if (*ptr == ',')
                    {
                        ++ptr;
                        if (strncmp(ptr, "LOCAL", 5) == 0)
                        {
                            scope = LOCAL;
                            ptr += 5;
                        }
                        else if (strncmp(ptr, "GLOBAL", 6) == 0)
                        {
                            scope = GLOBAL;
                            ptr += 6;
                        }

                            /* Size. */
                        if (*ptr == ',')
                        {
                            ++ptr;
                            sscanf(ptr, "%d", &size);
                        }
                    }
                }

                /* Add symbol to symbol table. */
                symbol = symnew(name, type, scope, size);
                symbol = symadd(cyminfo->symtable, symbol);

                /* Reference symbol to file. */
                symbol->file = fileinfo;
                sl = slnew();
                sl->symbol = symbol;
                sl->next = fileinfo->defines;
                fileinfo->defines = sl;
            }
            else if (strncmp(buf, "refer=", 6) == 0)
            {
                char    *endname;

                /* null-terminate the name. */
                endname = buf + 6;
                while (*endname && !isspace(*endname))
                    ++endname;
                *endname = '\0';

                /* Add symbol reference to symbol table. Type
                   unknown, assume global scope. */
                symbol = symnew(buf+6, UNKNOWN, GLOBAL, 0);
                symbol = symadd(cyminfo->symtable, symbol);

                /* Reference symbol to file. */
                sl = slnew();
                sl->symbol = symbol;
                sl->next = fileinfo->refers;
                fileinfo->refers = sl;
            }
        }
        while (!feof(cym) && (buf[0] != '['));
    }

    fclose(cym);
    return cyminfo;
}

#if 0
/*
;
;   Symbol info internal functions.
;
*/
#endif

CYMINFO     *cinew()
{
    CYMINFO *ci;
    int     i;

    ci = (CYMINFO *) galloc(sizeof(CYMINFO));
    if (!ci)
        return NULL;

    ci->files = NULL;
    ci->symtable = (SYMLIST *) galloc(HASH_BUCKETS*sizeof(SYMLIST));
    if (!ci->symtable)
    {
        gfree(ci);
        return NULL;
    }

    for (i = 0; i < HASH_BUCKETS; ++i)
    {
        ci->symtable[i].symbol = NULL;      /* Symbols for this bucket. */
        ci->symtable[i].next = NULL;        /* Not used. */
    }

    return ci;
}

FILEINFO    *finew(const char *fname, const char *libname)
{
    FILEINFO    *fi;

    fi = (FILEINFO *) galloc(sizeof(FILEINFO));
    fi->name = (char *) galloc(strlen(fname) + 1);
    fi->libraryname = (char *) galloc(strlen(libname) + 1);
    fi->sourcename = 0;
    fi->sourcedate = 0;
    if (!fi->name)
    {
        gfree(fi);
        return NULL;
    }
    strcpy(fi->name, fname);
    strcpy(fi->libraryname, libname);
    fi->size = 0;
    fi->defines = NULL;
    fi->refers = NULL;

    return fi;
}

SYMBOL      *symnew(char *name, SYMTYPE type, SYMSCOPE scope, int size)
{
    SYMBOL  *sym;

    sym = (SYMBOL *) galloc(sizeof(SYMBOL));
    sym->name = (char *) galloc(strlen(name) + 1);
    if (!sym->name)
    {
        gfree(sym);
        return NULL;
    }

        /* Copy name, stripping underscores if requested. */
    if (OPTunderscores)
    {
        strcpy(sym->name, name);
    }
    else
    {
        char    *s, *d;

        s = name;
        d = sym->name;
        while (*s == '_')       /* Skip leading underscores. */
            ++s;
        while (*s != '\0')
            *d++ = *s++;
        *d-- = '\0';
        while (*d == '_')       /* Remove trailing underscores. */
            *d-- = '\0';
    }

    sym->type = type;
    sym->scope = scope;
    sym->size = size;
    sym->file = NULL;
    sym->next = NULL;

    return sym;
}

SYMLIST     *slnew()
{
    SYMLIST *sl;

    sl = (SYMLIST *) galloc(sizeof(SYMLIST));
    sl->symbol = NULL;
    sl->next = NULL;

    return sl;
}

FILELIST    *flnew()
{
    FILELIST *fl;

    fl = (FILELIST *) galloc(sizeof(FILELIST));
    fl->file = NULL;
    fl->info = 0;
    fl->symbol = NULL;
    fl->next = NULL;

    return fl;
}

SYMBOL      *symadd(SYMLIST *table, SYMBOL *newsymbol)
{
    SYMBOL  *oldsymbol;

        /* Is there already a symbol with the same name? If so
           then we merge the two symbols, free the new symbol,
           and return the old symbol.

           Otherwise we add the new symbol to the head
           of the appropriate hash bucket and return
           the new symbol. */
    oldsymbol = symfind(table, newsymbol->name);
    if (oldsymbol)
    {
        if (!oldsymbol->type)
            oldsymbol->type = newsymbol->type;
        if (!oldsymbol->scope)
            oldsymbol->scope = newsymbol->scope;
        if (!oldsymbol->size)
            oldsymbol->size = newsymbol->size;

        gfree(newsymbol);
        return oldsymbol;
    }
    else
    {
        int hash;

        hash = symhash(newsymbol->name);
        newsymbol->next = table[hash].symbol;
        table[hash].symbol = newsymbol;
        return newsymbol;
    }
}

SYMBOL      *symfind(SYMLIST *table, char *name)
{
    char    underscorename[256];
    SYMBOL  *sym;

        /* Look for symbol, if not found then try adding
           underscores. */
    sym = symfindnth(table, name, 1);
    if (!sym)
    {
        sprintf(underscorename, "_%s", name);
        sym = symfindnth(table, underscorename, 1);

        if (!sym)
        {
            sprintf(underscorename, "%s_", name);
            sym = symfindnth(table, underscorename, 1);
        }
    }

    return sym;
}

SYMBOL      *symfindnth(SYMLIST *table, char *name, int n)
{
    SYMBOL  *list;

    list = table[symhash(name)].symbol;

    while (list)
    {
        if (strcmp(list->name, name) == 0)
        {
            if (--n == 0)
                break;
        }

        list = list->next;
    }

    return list;
}

int         symhash(char *name)
{
    unsigned int hash = 0;

    while (*name)
    {
        hash <<= 2;
        hash += *name;
        ++name;
    }

    return hash % HASH_BUCKETS;
}

#if 0
/*
;
;   printheader - format section header to stream
;
*/
#endif

void    printheader(FILE *f, char *head)
{
    if (OPTverbose>=3)
        fprintf(f, "\n%s%s%s", preheader, head, postheader);
}

#if 0
/*
;
;   cymreport - report on requested symbol information
;
*/
#endif

int cymreport(char *reportfname, CYMINFO *cyminfo,
              char *symbols[], int nsymbols,
              char *files[], int nfiles,
              int define, int refer, int link, int recurse)
{
    FILE    *report;
    int     ok = 0;

    if (!cyminfo)
        return 0;

    if (reportfname)
    {
        report = fopen(reportfname, "w");
        if (!report)
            return 0;
    }
    else
    {
        report = stdout;
    }

        /* If no symbols requested then we do a file report,
           otherwise we do a symbol report. */
    if (nsymbols == 0)
    {
        ok = cymfilereport(report, cyminfo, files, nfiles, define, refer, link, recurse);
    }
    else
    {
        ok = cymsymbolreport(report, cyminfo, symbols, nsymbols, define, refer, link, recurse);
    }

    if (reportfname)
        fclose(report);

    return ok;
}

/*
;
;   cymfilereport - report on symbols defined or referred to in given files.
;
*/

int cymfilereport(FILE *report, CYMINFO *cyminfo,
                  char *files[], int nfiles,
                  int define, int refer, int link, int recurse)
{
    FILEINFO    *fi;
    int         doit;
    int         i;
    int         totalpos, curpos;

    /* Loop over fileinfo structures in cyminfo. */
    for (fi = cyminfo->files; fi != NULL; fi = fi->next)
    {
        /* Report on file if no files are specified (do all
           files) or if file is in list. */
        doit = 1;
        if (nfiles > 0)
        {
            doit = 0;
            for (i = 0; (i < nfiles) && !doit; ++i)
            {
                if (stricmp(files[i], fi->name) == 0)
                    doit = 1;
            }
        }

        if (doit)
        {
            SYMLIST *sl;

            printheader(report, "FILE");
            if (fi->libraryname[0])
                fprintf(report, "    %s (%s, %d bytes", fi->name, fi->libraryname, fi->size);
            else
                fprintf(report, "    %s (%d bytes", fi->name, fi->size);
            totalpos = ftell(report);

            /* Leave some spaces--will be overwritten with link size. */

            fprintf(report, ")                                \n");

            if (fi->sourcename)
            {
                fprintf(report, "    ");
                if (OPThtml)
                {
                    char *fn;
                    char *t;
                    char lib[SPITMAXLINE];
                    char plat[SPITMAXLINE];

                    strcpy(plat,fi->sourcename);
                    strcpy(lib,fi->libraryname);
                    fn = t = plat;
                    while (*t)
                    {
                        if (*t=='/' || *t=='\\' || *t==':')
                        {
                            *t = 0;
                            fn = t+1;
                        }
                        if (*t=='@')
                        {
                            *t = 0;
                            break;
                        }

                        ++t;
                    }
                    /* use openfile JavaScript to link to a local file from CHM files */
                    if (lib[0])
                    {
                        fprintf(report, "<a onclick=\"openfile('%s%s/%s/%s')\" style=\"text-decoration: none; color:#003399; cursor: hand\" title=\"%s Source\">",OPTsourcelinkprefix,lib,plat,fn,fn);
                        fprintf(report, "%s/%s/%s",lib,plat,fn);
                    }
                    else
                    {
                        fprintf(report, "<a onclick=\"openfile('%s%s/%s')\" style=\"text-decoration: none; color:#003399; cursor: hand\" title=\"%s Source\">",OPTsourcelinkprefix,plat,fn,fn);
                        fprintf(report, "%s/%s",plat,fn);
                    }
                    fprintf(report, "</a>");
                }
                else
                {
                    fprintf(report, "%s", fi->sourcename);
                }
                fprintf(report, " ");
                if (fi->sourcedate)
                {
                    char *months[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec", };

                    fprintf(report, "(");
                    fprintf(report, "%s %d,%d",months[fi->sourcedate/100%100-1],fi->sourcedate%100,fi->sourcedate/10000);
                    fprintf(report, ")");
                }
                fprintf(report, "\n");
            }


            if (define)
            {
                printheader(report, "DEFINES");
                for (sl = fi->defines; sl != NULL; sl = sl->next)
                {
                    char ssize[16];
                    int  sz = symsize(sl->symbol);
                    if (sz)
                        sprintf(ssize, " [%d]", sz);
                    else
                        ssize[0] = '\0';

                    fprintf(report, "    %s%s%s\n", symname(sl->symbol),
                                                    symtype(sl->symbol),
                                                    ssize);
                }
            }

            if (refer)
            {
                printheader(report, "REFERS TO");
                for (sl = fi->refers; sl != NULL; sl = sl->next)
                {
                    fprintf(report, "    %s%s (%s)\n", symname(sl->symbol),
                                                       symtype(sl->symbol),
                                                       symfile(sl->symbol));
                }
            }

            if (link)
            {
                FILELIST    *fl = NULL, *flhead = NULL;
                int         totalsize;
                int         spaces;
                char        libsstr[SPITMAXLINE];
                char        libmatchstr[SPITMAXLINE];
                char        *libfind;

                printheader(report, "LINKS IN");

                /* list of libraries linked in */
                strcpy(libsstr,"<FONT COLOR=\"#990000\">");
                strcat(libsstr,fi->libraryname);
                strcat(libsstr,"</FONT>,");

                    /* See which files are brought in by referencing this
                       file (including the current file), and the total size
                       of the files. */
                if (fi->defines)
                {
                        /* Don't recurse, just add the file at level 0. */
                    fl = symneedfiles(fi->defines->symbol, fl, 0, 0);
                }

                for (sl = fi->refers; sl != NULL; sl = sl->next)
                {
                        /* Add files starting at level 1. */
                    fl = symneedfiles(sl->symbol, fl, recurse, 1);
                }

                flhead = fl;
                /* get list of libraries linked in */
                while (fl != NULL)
                {
                    if (fl->file->libraryname[0])
                    {
                        strcpy(libmatchstr,"<FONT COLOR=\"#990000\">");
                        strcat(libmatchstr,fl->file->libraryname);
                        strcat(libmatchstr,"</FONT>,");
                        libfind = strstr(libsstr,libmatchstr);
                        if (!libfind)       /* not in list - add to list */
                            strcat(libsstr,libmatchstr);
                    }
                    fl = fl->next;
                }
                libsstr[strlen(libsstr)-1] = '\0';
                libfind = strstr(libsstr,"#990000");
                strncpy(libfind,"#000000",7);
                fprintf(report, "    Libraries: %s\n\n", libsstr);

                fl = flhead;
                totalsize = 0;
                while (fl != NULL)
                {
                    spaces = 4 + fl->info*4;
                    while (spaces--)
                        fputc(' ', report);
                    if (fl->file->libraryname[0])
                    {
                        if (strcmp(fl->file->libraryname,fi->libraryname)!=0)
                            fprintf(report, "%s (<FONT COLOR=\"#990000\">%s</FONT>, %d) [%s]\n", fl->file->name, fl->file->libraryname, fl->file->size, fl->symbol->name);
                        else
                            fprintf(report, "%s (%s, %d) [%s]\n", fl->file->name, fl->file->libraryname, fl->file->size, fl->symbol->name);
                    }
                    else
                        fprintf(report, "%s (%d) [%s]\n", fl->file->name, fl->file->size, fl->symbol->name);
                    totalsize += fl->file->size;
                    fl = fl->next;
                }

                if (totalsize > 0)
                {
                    if (report == stdout)
                    {
                        fprintf(report, "\n    Links %d bytes total", totalsize);
                    }
                    else
                    {
                            /* Backtrack and fill in total size. */
                        curpos = ftell(report);
                        fseek(report, totalpos, SEEK_SET);
                        fprintf(report, ", links %d bytes total)", totalsize);
                        fseek(report, curpos, SEEK_SET);
                    }
                }
            }

            fprintf(report, "\n");
        }
    }

    return 1;
}

#if 0
/*
;
;   printsymbolreport - print report on one symbol.
;
*/
#endif

void    printsymbolreport(FILE *report, SYMBOL *sym, CYMINFO *cyminfo,
                          int define, int refer, int link, int recurse)
{
    FILEINFO    *fi;
    SYMLIST     *sl;
    FILELIST    *fl;
    int         totalsize = 0;
    int         spaces;
    int objsize=0;

    fl = symneedfiles(sym, NULL, recurse, 0);
    if (fl != NULL)
        objsize = fl->file->size;

    if (!objsize && OPTverbose==1)
        return;


    printheader(report, "SYMBOL");
    if (OPTverbose>=3)
        fprintf(report, "    %s%s\n", sym->name, symtype(sym));
    else
        fprintf(report, "%-32s ", sym->name);

    if (define)
    {
        printheader(report, "DEFINED IN");
        if (OPTverbose>=3)
            fprintf(report, "    %s\n", symfile(sym));
        else
            fprintf(report, "%16s", symfile(sym));
    }

    if (refer)
    {
        int refs = 0;

        printheader(report, "REFERRED TO IN");

        /* Search reference list of each file. */
        for (fi = cyminfo->files; fi != NULL; fi = fi->next)
        {
            for (sl = fi->refers; sl != NULL; sl = sl->next)
            {
                if (sl->symbol == sym)
                {
                    if (OPTverbose>=3)
                        fprintf(report, "    %s\n", fi->name);
                    ++refs;
                }
            }
        }

        if (OPTverbose>=3)
            fprintf(report, "    %d Files\n", refs);
        else
            fprintf(report, " %8d refs", refs);
    }

    if (link)
    {
        printheader(report, "LINKS IN");

            /* See which files are brought in by referencing this
               symbol, and the total size of the files. */

            /* Go through once to find total size. */
        totalsize = 0;
        while (fl != NULL)
        {
            spaces = 4 + fl->info*4;
            if (OPTverbose>=3)
            {
                while (spaces--)
                    fputc(' ', report);

                fprintf(report, "%s (%d) [%s]\n", fl->file->name, fl->file->size, fl->symbol->name);
            }
            totalsize += fl->file->size;
            fl = fl->next;
        }

        if (totalsize > 0)
        {
            if (OPTverbose>=3)
            {
                fprintf(report, "    Total: %d bytes\n", totalsize);
            }
            else
            {
                fprintf(report, "%8ld %8ld ",objsize,totalsize);
            }
        }
    }

    fprintf(report, "\n");
}

#if 0
/*
;
;   cymsymbolsreport - report on symbols.
;
*/
#endif

int cymsymbolreport(FILE *report, CYMINFO *cyminfo,
                    char *symbols[], int nsymbols,
                    int define, int refer, int link, int recurse)
{
    int         i;
    SYMBOL      *sym;
    char        *name;

        /* If first symbol name is "all" then print report
           for all symbols. */
    if ((nsymbols > 0) && (strcmp(symbols[0], "all") == 0))
    {
        for (i = 0; i < HASH_BUCKETS; ++i)
        {
            for (sym = cyminfo->symtable[i].symbol; sym != NULL; sym = sym->next)
            {
                printsymbolreport(report, sym, cyminfo, define, refer, link, recurse);
            }
        }
    }
    else
    {
            /* Report on requested symbols. */
        for (i = 0; i < nsymbols; ++i)
        {
            name = symbols[i];
            sym = symfind(cyminfo->symtable, name);
            if (!sym)
            {
                fprintf(stderr, "cymbal: symbol \'%s\' not found\n", name);
                continue;
            }

            printsymbolreport(report, sym, cyminfo, define, refer, link, recurse);
        }
    }

    return 1;
}

#if 0
/*
;
;   symneedfiles - recursively determine files needed by symbol
;
*/
#endif

FILELIST    *symneedfiles(SYMBOL *sym, FILELIST *files, int recurse, int level)
{
    FILELIST    *fl;
    FILELIST    *end;

        /* If symbol has no attached file then we can't determine
           anything about what files it needs. */
    if (!sym->file)
        return files;

        /* Check for presence of symbol's file in list. If found
           then return immediately, as we've already covered it. */
    end = files;
    fl = files;
    while (fl != NULL)
    {
        if (fl->file == sym->file)
            return files;
        end = fl;
        fl = fl->next;
    }

        /* Symbol's file is not in list. Add to end of list then recurse
           for each symbol referenced in the file. */
    fl = flnew();
    fl->info = level;
    fl->file = sym->file;
    fl->symbol = sym;
    if (end)
        end->next = fl;
    else
        files = fl;

    if (level < recurse)
    {
        SYMLIST *sl;

        ++level;
        for (sl = sym->file->refers; sl != NULL; sl = sl->next)
        {
            files = symneedfiles(sl->symbol, files, recurse, level);
        }
    }

        /* Return accumulated files. */
    return files;
}

#if 0
/*
;
;   symbol info functions
;
*/
#endif

char        *symname(SYMBOL *sym)
{
    return sym->name;
}

char        *symfile(SYMBOL *sym)
{
    if (sym && sym->file)
        return sym->file->name;
    else
        return "unknown";
}

char        *symtype(SYMBOL *sym)
{
    switch (sym->type)
    {
        case FUNCTION:  return "()";
        case DATA:      return "";
        case UNKNOWN:   return "";

        default:        return "????";
    }
}

char        *symscope(SYMBOL *sym)
{
    switch (sym->scope)
    {
        case LOCAL:     return "local";
        case GLOBAL:    return "global";
        case UNKNOWN:   return "unknown";

        default:        return "????";
    }
}

int         symsize(SYMBOL *sym)
{
    return sym->size;
}

#if 0
/*
;
;   pathfilename - returns a pointer to the filename portion of a path
;
*/
#endif

char *pathfilename(char *path)
{
    char    *pname;

    if (path == NULL)
        return NULL;

    /* Start at the end of the pathname and work backwards
       until reaching a slash or ':'. */

    pname = path + strlen(path) - 1;
    while ((pname >= path)  &&
           (*pname != '\\') &&
           (*pname != '/')  &&
           (*pname != ':'))
    {
        --pname;
    }

    return pname + 1;
}

/*******************************************************************
 getlibnamefrompath
 *******************************************************************/

void getlibnamefrompath(const char *srcname)
{
    const char *findptr,*srcptr;

    findptr = strchr(srcname, ':');
    if (findptr)
        srcname = findptr + 1;

    findptr = strchr(srcname, '.');
    if (findptr && ((stricmp(findptr, ".lib") == 0) || (stricmp(findptr, ".a") == 0)))   // a library
    {
        srcptr = findptr;
        while(srcptr>=srcname && *srcptr!='\\')
            srcptr--;
        if (*srcptr=='\\')
            srcptr++;
        strncpy(OPTlibrary,srcptr,findptr-srcptr);
        OPTlibrary[findptr-srcptr]='\0';
    }
    else        // assume an object file
    {
        srcptr = srcname;
        if (*srcptr=='\\') srcptr++;
        findptr = strchr(srcptr,'\\');
        if (findptr)
        {
            strncpy(OPTlibrary,srcptr,findptr-srcptr);
            OPTlibrary[findptr-srcptr]='\0';
        }
    }
}

#if 0
/*
;
;   pccym - PC preprocessor; generates .cym file from object/library
;           file input
;
*/
#endif

int pccym(char *srcname, char *dstname)
{
    FILE      *src, *dst;
    RFindData c_file;
    RHandle   hFile;
    char     srcfile[SPITMAXLINE];
    char     buf[SPITMAXLINE];
    char     *bufptr;
    char     tmpdir[SPITMAXLINE];
    char     tmpfile[SPITMAXLINE];
    char     pathname[SPITMAXLINE];
    char     curdir[SPITMAXLINE];
    char     *tmp;
    int      filesymsize;
    int      symsize;
    char     symname[SPITMAXLINE];

        /* Make sure requested file is readable. */
    if (access(srcname, R_OK) != 0)
        return 0;

        /* Open destination to write .cym info. We open for appending, so that
           info from multiple files all goes into same .cym file. */
    dst = fopen(dstname, "a");
    if (dst == NULL)
    {
        return 0;
    }

        /* If this is a library then we execute system command to put
           object files into temporary directory, otherwise we just
           read the one object file.
         */
    tmp = strchr(srcname, '.');
    if (tmp && (stricmp(tmp, ".lib") == 0))
    {
        if (OPTverbose>2)
            fprintf(stderr, "cymbal: extracting files from %s\n", srcname);

        /* get library name */
        if (OPTlibnamefrompath)
            getlibnamefrompath(srcname);

        /* Create temporary files. */
        cleanup();          /* Make sure tmp directory is empty. */

        /* TMPFILE is in TMPDIR (but will change to that directory) */
        strcpy(tmpdir, TMPDIR);
        strcpy(tmpfile, TMPFILE);
        RGetCWD(curdir, 256);

        /* Make TMPDIR */
        if (!RMkDir(tmpdir))
        {
            fclose(dst);
            return 0;
        }

        sprintf(buf, "wlib -d=%s -x %s", tmpdir, srcname);
        if (OPTverbose>=5)
            printf("system(%s)\n", buf);
        system(buf);

        /* Dump each object file and extract symbols, appending the
           results to the .cym file. */
        strcpy(pathname, "*.o*");
    }
    else
    {
        /* Assume srcname is the object file we want. */
        SPIT_getname( pathname,srcname);
        SPIT_getpath( tmpdir,srcname);

        /* get library name */
        if (OPTlibnamefrompath)
            getlibnamefrompath(srcname);

        if (tmpdir[0] == '\0')
            strcpy(tmpdir, ".");
        RGetCWD(curdir, 256);

        /* Put temp file in object file directory. */
        strcpy(tmpfile, TMPFILE);
    }

    if (RChDir(tmpdir))
    {
        /* Find all the object files in this loop */
        if ((hFile = RFindFirst(&c_file)) != RNOTFOUND)
        {
            do
            {
                /* Match found files to pathname */
                if (WILD_match(srcfile, RName(c_file), pathname, pathname))
                {

                    /* Convert to lower case. */
                    strlwr(srcfile);

                    /* Write filename to output. */
                    tmp = pathfilename(srcfile);
                    if (*OPTlibrary)
                        fprintf(dst, "[%s][%s]\n", tmp, OPTlibrary);
                    else
                        fprintf(dst, "[%s]\n", tmp);

                    if (OPTverbose>2)
                        printf("cymbal: extracting symbols from %s\n", tmp);

                    /* Use disassembler to create file containing symbol info. */
                    sprintf(buf, "dumpbin /symbols /out:%s %s", tmpfile, srcfile);
                    system(buf);

                    /* Open temp file for reading. */
                    src = fopen(tmpfile, "r");
                    if (!src)
                    {
                        fprintf(stderr, "cymbal: ERROR: couldn\'t open temp file for reading\n");
                        break;
                    }

                    filesymsize = 0;

                    /* Find segments and symbols. */
                    do
                    {
                        if (fgets(buf, sizeof(buf), src))
                        {
                            if (strncmp(buf, "COFF SYMBOL TABLE", 17) == 0)
                            {
                                char *sname;
                                int  sdate;
                                char *t;

                                /* Source module. */
                                fgets(buf, sizeof(buf), src);
                                fgets(buf, sizeof(buf), src);

                                sname = buf+4;
                                sdate = SPIT_filedate(sname);
                                if (sname[1]==':')
                                    sname += 2;
                                if (sname[0]=='/' || sname[0]=='\\' || sname[0]==':')
                                    sname += 1;
                                if (!strnicmp(sname,"lib",3))
                                    sname += strlen("lib/");
                                if (!strnicmp(sname,"real",4))
                                    sname += strlen("real/");
                                t = sname;
                                while (*t)
                                {
                                    if (*t==10 || *t==13)
                                        *t = 0;
                                    ++t;
                                }

                                fprintf(dst, "source=%s,%d\n", sname,sdate);
                            }
                            /* look for an external function */
                            else if (strlen(buf)>49 &&
                                strncmp(&buf[13],"SECT",4)==0 &&
                                strncmp(&buf[33],"External",8)==0)
                            {
                                /* fast forward to symbol name */
                                sscanf(&buf[49],"%s\n",symname);
                                if (strchr(symname,'@'))
                                    *strchr(symname,'@') = '\0';
                                symsize = 0;
                                fprintf(dst, "define=%s,FUNCTION,GLOBAL,%d\n", symname,symsize);
                            }
                            /* look for a function reference */
                            else if (strlen(buf)>41 &&
                                strncmp(&buf[13],"UNDEF",5)==0 &&
                                strncmp(&buf[33],"External",8)==0)
                            {
                                /* fast forward to symbol name */
                                bufptr = &buf[49];
                                sscanf(bufptr,"%s\n",symname);
                                if(!strchr(symname,'@'))
                                    fprintf(dst, "refer=%s\n", symname);
                            }
                            /* look for summary info */
                            else if (strncmp(&buf[2],"Summary",7)==0)
                            {
                                char symtype[32];

                                symsize=0;
                                fgets(buf, sizeof(buf), src);
                                while (fgets(buf, sizeof(buf), src))
                                {
                                    sscanf(buf, "%x .%s", &symsize, symtype);
                                    if (!strcmp(symtype,"text") || !strcmp(symtype,"data") ||
                                        !strcmp(symtype,".sdata") || !strcmp(symtype,"idata") ||
                                        !strcmp(symtype,"bss"))
                                        filesymsize += symsize;
                                }
                            }
                        }
                    }
                    while (!feof(src));

                    if (src)
                    {
                            /* Write file size. */
                        fprintf(dst, "size=%d\n\n", filesymsize);

                            /* Close source file, get next source file name. */
                        fclose(src);
                    }

                }
            } while (RFindNext(hFile, &c_file));
            RFindClose(hFile, &c_file);
        }
    }

    /* Close destination file, remove temp file */
    fclose(dst);
    remove(tmpfile);

    /* Go back to original directory */
    RChDir(curdir);

    return 1;
}

#if 0
/*
;
;   gccym - GC preprocessor; generates .cym file from object/library
;           file input
;
*/
#endif

int gccym(char *srcname, char *dstname)
{
    FILE      *src, *dst;
    RFindData c_file;
    RHandle   hFile;
    char     srcfile[256];
    char     buf[256];
    char     tmpdir[32];
    char     tmpfile[32];
    char     pathname[256];
    char     curdir[256];
    char     *tmp;

        /* Make sure requested file is readable. */
    if (access(srcname, R_OK) != 0)
        return 0;

        /* Open destination to write .cym info. We open for appending, so that
           info from multiple files all goes into same .cym file. */
    dst = fopen(dstname, "a");
    if (dst == NULL)
    {
        return 0;
    }

        /* If this is a library then we execute system command to put
           object files into temporary directory, otherwise we just
           read the one object file.
         */
    tmp = strchr(srcname, '.');
    if (tmp && (stricmp(tmp, ".a") == 0))
    {
        if (OPTverbose>2)
            fprintf(stderr, "cymbal: extracting files from %s\n", srcname);

        /* get library name */
        if (OPTlibnamefrompath)
            getlibnamefrompath(srcname);

        /* Create temporary files. */
        cleanup();          /* Make sure tmp directory is empty. */

        /* TMPFILE is in TMPDIR (but will change to that directory) */
        strcpy(tmpdir, TMPDIR);
        strcpy(tmpfile, TMPFILE);
        RGetCWD(curdir, 256);

        /* Make TMPDIR */
        if (!RMkDir(tmpdir))
        {
            fclose(dst);
            return 0;
        }

        RChDir(tmpdir);
        sprintf(buf, "ar -x %s", srcname);
        if (OPTverbose>=5)
            printf("system(%s)\n", buf);
        system(buf);
        RChDir(curdir);

        /* Dump each object file and extract symbols, appending the
           results to the .cym file. */
        strcpy(pathname, "*.o*");
    }
    else
    {
        /* Assume srcname is the object file we want. */
        SPIT_getname( pathname,srcname);
        SPIT_getpath( tmpdir,srcname);

        /* get library name */
        if (OPTlibnamefrompath)
            getlibnamefrompath(srcname);

        if (tmpdir[0] == '\0')
            strcpy(tmpdir, ".");
        RGetCWD(curdir, 256);

        /* Put temp file in object file directory. */
        strcpy(tmpfile, TMPFILE);
    }

    if (RChDir(tmpdir))
    {
        /* Find all the object files in this loop */
        if ((hFile = RFindFirst(&c_file)) != RNOTFOUND)
        {
            do
            {
                /* Match found files to pathname */
                if (WILD_match(srcfile, RName(c_file), pathname, pathname))
                {
                    int      filesymsize;
                    int      symsize;
                    char     symname[128];
                    char     symscope[2];
                    char     symtype[3];
                    char     symtype2[8];
                    char    *symnamemungeptr;

                    /* Convert to lower case. */
                    strlwr(srcfile);

                    /* Write filename to output. */
                    tmp = pathfilename(srcfile);
                    if (*OPTlibrary)
                        fprintf(dst, "[%s][%s]\n", tmp, OPTlibrary);
                    else
                        fprintf(dst, "[%s]\n", tmp);

                    if (OPTverbose>2)
                        printf("cymbal: extracting symbols from %s\n", tmp);

                    /* Use disassembler to create file containing symbol info. */
                    sprintf(buf, "gc-objdump -t %s >%s", srcfile, tmpfile);
                    system(buf);

                    /* Open temp file for reading. */
                    src = fopen(tmpfile, "r");
                    if (!src)
                    {
                        fprintf(stderr, "cymbal: ERROR: couldn\'t open temp file for reading\n");
                        break;
                    }

                    filesymsize = 0;

                    /* Find segments and symbols. */
                    fgets(buf, sizeof(buf), src);
                    fgets(buf, sizeof(buf), src);
                    fgets(buf, sizeof(buf), src);
                    fgets(buf, sizeof(buf), src);
                    do
                    {
                        fgets(buf, sizeof(buf), src);
                        if (!feof(src) && strlen(buf)>23)
                        {
                            symscope[0] = buf[9];  symscope[1] = '\0';
                            symtype[0] = buf[14];  symtype[1] = buf[15];  symtype[2] = '\0';
                            sscanf(&buf[17],"%s\t%x %s",symtype2,&symsize,symname);
                            symnamemungeptr = strstr(symname,"__F");
                            if (symnamemungeptr>symname)
                                *symnamemungeptr = '\0';

                            // debug
                            /*
                            printf ("symname = %s\n",symname);
                            printf ("symscope = %s\n",symscope);
                            printf ("symtype = %s\n",symtype);
                            printf ("symtype2 = %s\n",symtype2);
                            printf ("symsize = %d\n",symsize);
                            printf ("\n");
                            */

                            if (strcmp(symtype,"df")==0)                    // source filename
                                fprintf(dst, "source=%s\n", symname);
                            else if (symscope[0]=='g' && symtype[1]=='F')    // external function
                                fprintf(dst, "define=%s,FUNCTION,GLOBAL,%d\n", symname, symsize);
                            else if (strcmp(symtype2,"*UND*")==0)            // external reference
                                fprintf(dst, "refer=%s\n", symname);
                            filesymsize += symsize;
                        }
                    }
                    while (!feof(src));

                    if (src)
                    {
                            /* Write file size. */
                        fprintf(dst, "size=%d\n\n", filesymsize);

                            /* Close source file, get next source file name. */
                        fclose(src);
                    }
                }
            } while (RFindNext(hFile, &c_file));
            RFindClose(hFile, &c_file);
        }
    }

    /* Close destination file, remove temp file */
    fclose(dst);
    remove(tmpfile);

    /* Go back to original directory */
    RChDir(curdir);

    return 1;
}

#if 0
/*
;
;   sgicym - SGI preprocessor; generates .cym file from object file input
;
*/
#endif

int sgicym(char *srcname, char *dstname)
{
    FILE    *src, *dst;
    char    buf[256];
    char    tmpfile[9];
    char    *tmp;
    int     filesymsize;
    int     symsize;
    char    symname[64];
    char    symscope[32];
    char    symtype[32];
    char    symsection[32];

        /* Make sure requested file is readable. */
    if (access(srcname, R_OK) != 0)
        return 0;

        /* Find a file name that's not in use. */
    sprintf(tmpfile, TMPFILE);

        /* Execute system command to generate symbol table listing
           in temporary file. */
    sprintf(buf, "elfdump -t %s | grep -v NOTYPE > %s", srcname, tmpfile);
    system(buf);

        /* Open temp file to read symtab dump. */
    src = fopen(tmpfile, "r");
    if (src == NULL)
        return 0;

        /* Open destination to write .cym info; open for appending so that
           symbols from multiple files can go into one cym file. */
    dst = fopen(dstname, "a");
    if (dst == NULL)
    {
        fclose(src);
        return 0;
    }

        /* Parse input file, write relevant info to output file. */
    while (!feof(src))
    {
            /* Find file name. */
        do
        {
            fgets(buf, sizeof(buf), src);
        }
        while (!feof(src) && isspace(buf[0]));

        if (feof(src))
            break;

            /* Write filename to output. */
        fputc('[', dst);
        tmp = buf;
        while (*tmp && (*tmp != ':'))
            fputc(*tmp++, dst);
        if (*OPTlibrary)
        {
            fputs("][", dst);
            fputs(OPTlibrary, dst);
        }
        fputs("]\n", dst);
        filesymsize = 0;

            /* Find symbols. */
        do
        {
            fgets(buf, sizeof(buf), src);
        }
        while (!feof(src) && (buf[0] != '['));

            /* Write symbols. */
        while (!feof(src) && (buf[0] == '['))
        {
                /* Scan for symbol info. */
            sscanf(buf, "[ %*d] %s %*s %d %s %s %s", symname, &symsize, symscope, symtype, symsection);
            if (strcmp(symtype, "SECTION") == 0)
            {
                filesymsize += symsize;
            }
            else
            {
                if (strcmp(symsection, "UNDEF") == 0)
                {
                    fprintf(dst, "refer=%s\n", symname);
                }
                else
                {
                        /* Translate type names to cymbal standards. */
                    if (strcmp(symtype, "FUNC") == 0)
                        strcpy(symtype, "FUNCTION");
                    else if (strcmp(symtype, "OBJECT") == 0)
                        strcpy(symtype, "DATA");
                    fprintf(dst, "define=%s,%s,%s,%d\n", symname, symtype, symscope, symsize);
                }
            }

            fgets(buf, sizeof(buf), src);
        }

            /* Write file size. */
        fprintf(dst, "size=%d\n\n", filesymsize);
    }

        /* Close files and exit. */
    fclose(src);
    fclose(dst);
    remove(tmpfile);
    return 1;
}

#if 0
/*
;
;   psxcym - PSX preprocessor; generates .cym file from object
;           file input
;
*/
#endif

char *CYMmemstr(char *buf,char *str,int n)
{
    while (n--)
    {
        if (!strncmp(buf,str,strlen(str)))
            return(buf);
        ++buf;
    }
    return(0);
}



int psxcym(char *srcname, char *dstname)
{
    FILE      *src, *dst;
    RFindData c_file;
    RHandle   hFile;
    char     srcfile[256];
    char     buf[256];
    char     tmpdir[32];
    char     tmpfile[32];
    char     pathname[256];
    char     curdir[256];
    char     *tmp;
    int      filesymsize;
    int      symsize;
    char     symname[64];

    /* Make sure requested file is readable. */
    if (access(srcname, R_OK) != 0)
        return 0;

    /* Open destination to write .cym info. We open for appending, so that
       info from multiple files all goes into same .cym file. */
    dst = fopen(dstname, "a");
    if (dst == NULL)
    {
        return 0;
    }

    /* If this is a library then we execute system command to put
       object files into temporary directory, otherwise we just
       read the one object file.
    */
    tmp = strchr(srcname, '.');
    if (tmp && (stricmp(tmp, ".lib") == 0))
    {
        if (OPTverbose>2)
            fprintf(stderr, "cymbal: extracting files from %s\n", srcname);

        /* get library name */
        if (OPTlibnamefrompath)
            getlibnamefrompath(srcname);

        /* Create temporary files. */
        cleanup();          /* Make sure tmp directory is empty. */

        /* TMPFILE is in TMPDIR (but will change to that directory) */
        strcpy(tmpdir, TMPDIR);
        strcpy(tmpfile, TMPFILE);
        RGetCWD(curdir, 256);

        /* Make TMPDIR */
        if (!RMkDir(tmpdir))
        {
            fclose(dst);
            return 0;
        }

        if (RChDir(tmpdir))
        {
            char type;

            type = SPIT_typeofname(srcname);
            if (type == SPITPATHNORM || type == SPITPATHDIR)
                sprintf(buf, "psylib /x /q ..\\%s", srcname);
            else
                sprintf(buf, "psylib /x /q %s", srcname);
            if (OPTverbose>=5)
                printf("system(%s)\n", buf);
            system(buf);

            /* Dump each object file and extract symbols, appending the
               results to the .cym file. */
            strcpy(pathname, "*.o*");

            RChDir("..");
        }
        else
        {
            fclose(dst);
            return 0;
        }
    }
    else
    {
        /* Assume srcname is the object file we want. */
        SPIT_getname( pathname,srcname);
        SPIT_getpath( tmpdir,srcname);

        /* get library name */
        if (OPTlibnamefrompath)
            getlibnamefrompath(srcname);

        if (tmpdir[0] == '\0')
            strcpy(tmpdir, ".");
        RGetCWD(curdir, 256);

        /* Put temp file in object file directory. */
        strcpy(tmpfile, TMPFILE);
    }

    if (RChDir(tmpdir))
    {
        /* Find all the object files in this loop */
        if ((hFile = RFindFirst(&c_file)) != RNOTFOUND)
        {
            do
            {
                /* Match found files to pathname */
                if (WILD_match(srcfile, RName(c_file), pathname, pathname))
                {

                    /* Convert to lower case. */
                    strlwr(srcfile);

                    /* Write filename to output. */
                    tmp = pathfilename(srcfile);
                    if (*OPTlibrary)
                        fprintf(dst, "[%s][%s]\n", tmp, OPTlibrary);
                    else
                        fprintf(dst, "[%s]\n", tmp);

                    if (OPTverbose>2)
                        printf("cymbal: extracting symbols from %s\n", tmp);

                    /* Use disassembler to create file containing symbol info. */
                    sprintf(buf, "reverse /nop /macro %s>%s", srcfile,tmpfile);
                    system(buf);

                    /* Get the source name from the object directly */
                    {
                        char *sname;
                        int  sdate;
                        char *t;

                        src = fopen(srcfile, "r");
                        fread(buf, sizeof(buf), 1, src);
                        fclose(src);
                        buf[255] = 0;

                        sname = CYMmemstr(buf,"\\LIB\\",256);
                        if (sname)
                        {
                            /* Source module. */

                            sname += strlen("\\LIB\\");
                            t = sname;
                            while ((unsigned char)*t>=(unsigned char)' ')
                            {
                                if (*t==':')
                                    *t = 0;
                                *t = (unsigned char)tolower(*t);
                                ++t;
                            }
                            *t = 0;

                            if (sname[0])
                            {
                                sdate = SPIT_filedate(sname);
                                fprintf(dst, "source=%s,%d\n", sname,sdate);
                            }
                        }
                    }

                    /* Open temp file for reading. */
                    src = fopen(tmpfile, "r");
                    if (!src)
                    {
                        fprintf(stderr, "cymbal: ERROR: couldn\'t open temp file for reading\n");
                        break;
                    }

                    filesymsize = 0;

                    /* Find segments and symbols. */
                    do
                    {
                        fgets(buf, sizeof(buf), src);

                        /* ; block addr=00000000, size=000000d8 */

                        tmp = strstr(buf,"; block addr=");
                        if (tmp)
                        {
                            symsize = 0;
                            tmp = strstr(buf,"size=");
                            if (tmp)
                                sscanf(tmp+5, "%x", &symsize);

                            /* Add segment size to file size. */
                            filesymsize += symsize;
                        }

                        /* XREF getm */
                        tmp = strstr(buf,"XREF");
                        if (tmp)
                        {
                            sscanf(tmp, "%*s %s", symname);
                            fprintf(dst, "refer=%s\n", symname);
                        }

                        /* XDEF getm */
                        tmp = strstr(buf,"XDEF");
                        if (tmp)
                        {
                            sscanf(tmp, "%*s %s", symname);
                            fprintf(dst, "define=%s,UNKNOWN,GLOBAL,0\n", symname);
                        }
                    }
                    while (!feof(src));

                    if (src)
                    {
                            /* Write file size. */
                        fprintf(dst, "size=%d\n\n", filesymsize);

                            /* Close source file, get next source file name. */
                        fclose(src);
                    }

                }
            } while (RFindNext(hFile, &c_file));
            RFindClose(hFile, &c_file);
        }
    }

    /* Close destination file, remove temp file */
    fclose(dst);
    remove(tmpfile);

    /* Go back to original directory */
    RChDir(curdir);

    return 1;
}

static int ps2_parse_line(char *buf, char *symname, char *symtype, char *symscope, int *symsize)
{
    /* 0000000000000004 g     O .sdata 0000000000000004 REALabortfilename */
    /* start address    flags   section size             symbol name       */
    char section[64];
    char size[12];
    char *bufptr;
    int nbytes;

    switch(buf[17])
    {
    case 'l':       // local variable
        return(0);
        break;

    case 'g':       // global variable
        bufptr = &buf[25];
        nbytes = (int)(strchr(bufptr, '\t')-bufptr);
        strncpy(section, bufptr, nbytes);
        section[nbytes] = '\0';

        bufptr = strchr(buf, '\t') + 9;
        size[0] = '0';
        size[1] = 'x';
        strncpy(size+2, bufptr, 8);
        size[10] = '\0';

        bufptr += 9;
        nbytes = (int)(strchr(bufptr, '\n')-bufptr);
        strncpy(symname, bufptr, nbytes);
        symname[nbytes] = '\0';

        if (!strcmp(section, ".text"))
            strcpy(symtype, "FUNCTION");
        else
            strcpy(symtype, "DATA");

        strcpy(symscope, "global");
        sscanf(size, "%x", symsize);
        break;

    case ' ':       // undefined reference
        bufptr = strchr(buf, '\t') + 18;
        nbytes = (int)(strchr(bufptr, '\n')-bufptr);
        strncpy(symname, bufptr, nbytes);
        symname[nbytes] = '\0';
        strcpy(symtype, "unknown");
        strcpy(symscope, "global");
        *symsize = 0;
        break;
    }

    return(1);
}

/* reads in the ASCII text version of a section from .o dump
   (ee-objdump --syms --full-contents) into buffer
*/
char *ps2fgetsection(char *dest, int destsize, FILE *f, const char *section)
{
    char    buf[256];
    char    *bufptr,*destptr;
    int     eos;

    dest[0] = '\0';
    rewind(f);

    /* forward to section */
    do
    {
        fgets(buf, sizeof(buf), f);
        if (buf[0])
            bufptr = buf + 21;
    }
    while (!feof(f) && strncmp(bufptr,section,strlen(section)));

    if (!feof(f))
    {
        /* extract text from last column into dest buffer */
        destptr = dest;
        eos = 0;
        do
        {
            fgets(buf, sizeof(buf), f);
            if (!feof(f) && buf[0]!='\n')
            {
                bufptr = buf+43;    /* column 43 */
                strncpy(destptr,bufptr,16);
                destptr += 16;
                if(destptr-dest+16 > destsize-1)
                    eos = 1;
            }
            else
                eos = 1;
        }
        while (!eos);
    }
    else
        return 0;

    *destptr = '\0';

    return dest;
}

/* scans for source code file name in the format: <platform>/<filename> */
char *ps2fgetsourcename(char *dest, int destsize, char *sectionsrc)
{
    char    *platptr,*fnameptr;
    int     destn;

    dest[0] = '\0';

    /* check for valid platform */
    if (!(platptr = strstr(sectionsrc,"cmn/")))
        if (!(platptr = strstr(sectionsrc,"ps2/")))
            return 0;

    if (fnameptr = strchr(platptr,'.'))
        fnameptr = strchr(fnameptr+1,'.');
    if (!fnameptr)
        return 0;

    destn = (int)(fnameptr-platptr);
    if (destn > destsize-1)
        destn = destsize-1;
    strncpy(dest,platptr,destn);
    dest[destn] = '\0';

    return dest;
}

int ps2cym(char *srcname, char *dstname)
{
    FILE      *src, *dst;
    RFindData c_file;
    RHandle   hFile;
    char    srcfile[256];
    char    buf[256];
    char    tmpdir[32];
    char    tmpfile[32];
    char    pathname[256];
    char    curdir[256];
    char    *tmp;
    int     filesymsize;
    int     symsize;
    char    symname[64];
    char    symtype[32];
    char    symscope[32];

    /* Make sure requested file is readable. */
    if (access(srcname, R_OK) != 0)
        return 0;

    /* Open destination to write .cym info. We open for appending, so that
       info from multiple files all goes into same .cym file. */
    dst = fopen(dstname, "a");
    if (dst == NULL)
    {
        return 0;
    }

    /* If this is a library then we execute system command to put
       object files into temporary directory, otherwise we just
       read the one object file.
    */
    tmp = strchr(srcname, '.');
    if (tmp && (stricmp(tmp, ".a") == 0))
    {
        if (OPTverbose>2)
            fprintf(stderr, "cymbal: extracting files from %s\n", srcname);

        /* get library name */
        if (OPTlibnamefrompath)
            getlibnamefrompath(srcname);

        /* Create temporary files. */
        cleanup();          /* Make sure tmp directory is empty. */

        /* TMPFILE is in TMPDIR (but will change to that directory) */
        strcpy(tmpdir, TMPDIR);
        strcpy(tmpfile, TMPFILE);
        RGetCWD(curdir, 256);

        /* Make TMPDIR */
        if (!RMkDir(tmpdir))
        {
            fclose(dst);
            return 0;
        }

        if (RChDir(tmpdir))
        {
            char type;

            type = SPIT_typeofname(srcname);
            if (type == SPITPATHNORM || type == SPITPATHDIR)
                sprintf(buf, "ee-ar x ..\\%s", srcname);
            else
                sprintf(buf, "ee-ar x %s", srcname);
            if (OPTverbose>=5)
                printf("system(%s)\n", buf);
            system(buf);

            /* Dump each object file and extract symbols, appending the
               results to the .cym file. */
            strcpy(pathname, "*.o");

            RChDir("..");
        }
        else
        {
            fclose(dst);
            return 0;
        }
    }
    else
    {
        /* Assume srcname is the object file we want. */
        SPIT_getname( pathname,srcname);
        SPIT_getpath( tmpdir,srcname);

        /* get library name */
        if (OPTlibnamefrompath)
            getlibnamefrompath(srcname);

        if (tmpdir[0] == '\0')
            strcpy(tmpdir, ".");
        RGetCWD(curdir, 256);

        /* Put temp file in object file directory. */
        strcpy(tmpfile, TMPFILE);
    }

    if (RChDir(tmpdir))
    {
        /* Find all the object files in this loop */
        if ((hFile = RFindFirst(&c_file)) != RNOTFOUND)
        {
            do
            {
                /* Match found files to pathname */
                if (WILD_match(srcfile, RName(c_file), pathname, pathname))
                {
                    /* Convert to lower case. */
                    strlwr(srcfile);

                    /* Write filename to output. */
                    tmp = pathfilename(srcfile);
                    if (*OPTlibrary)
                        fprintf(dst, "[%s][%s]\n", tmp, OPTlibrary);
                    else
                        fprintf(dst, "[%s]\n", tmp);

                    if (OPTverbose>2)
                        printf("cymbal: extracting symbols from %s\n", tmp);

                    /* Use disassembler to create file containing symbol info. */
                    sprintf(buf, "ee-objdump --syms --full-contents %s>%s", srcfile,tmpfile);
                    system(buf);

                    /* Open temp file for reading. */
                    src = fopen(tmpfile, "r");
                    if (!src)
                    {
                        fprintf(stderr, "cymbal: ERROR: couldn\'t open temp file for reading\n");
                        break;
                    }
                    filesymsize = 0;

                    /* skip the first four lines, which contain stuff we're not interested in. */
                    fgets(buf, sizeof(buf), src);
                    fgets(buf, sizeof(buf), src);
                    fgets(buf, sizeof(buf), src);
                    fgets(buf, sizeof(buf), src);

                    /* Find segments and symbols. */
                    do
                    {
                        fgets(buf, sizeof(buf), src);
                        if (buf[0] != '\n' &&
                            ps2_parse_line(buf, symname, symtype, symscope, &symsize))
                        {
                            if (!strcmp(symtype, "unknown"))
                            {
                                fprintf(dst, "refer=%s\n", symname);
                            }
                            else
                            {
                                fprintf(dst, "define=%s,%s,%s,%d\n",
                                    symname, symtype, symscope, symsize);
                                filesymsize += symsize;
                            }
                        }
                    }
                    while (!feof(src) && buf[0] != '\n');

                    /* Find source file name. */
                    {
                        char sectionsrc[16384];
                        char sname[256];

                        sname[0] = '\0';
                        if (ps2fgetsection(sectionsrc, sizeof(sectionsrc), src, "rodata"))
                            ps2fgetsourcename(sname, sizeof(sname), sectionsrc);
                        if (!sname[0])
                            if (ps2fgetsection(sectionsrc, sizeof(sectionsrc), src, "mdebug"))
                                ps2fgetsourcename(sname, sizeof(sname), sectionsrc);

                        if (sname[0])
                            fprintf(dst, "source=%s\n", sname);
                    }

                    if (src)
                    {
                        /* Write file size. */
                        fprintf(dst, "size=%d\n\n", filesymsize);
                        /* Close source file, get next source file name. */
                        fclose(src);
                    }
                }
            } while(RFindNext(hFile, &c_file));
            RFindClose(hFile, &c_file);
        }
    }

    /* Close destination file, remove temp file */
    fclose(dst);
    remove(tmpfile);

    /* Go back to original directory */
    RChDir(curdir);

    return(1);
}

#if 0
/*
;
;   main - parse options and get things going
;
*/
#endif

int main(int argc, char* argv[])
{
    int more;
    int     i;
    char    cymfile[256];
    char    *ext;
    int     exttype = 0;
    int     doreport = 0;

    /* Command line options. */
    SPIT_init(SPIT_reset, SPIT_usage, SPIT_opt, SPIT_history);
    do
    {
        more = SPIT_parse(argc, argv, TOOL_NAME, TOOL_VERSION);

            /* If recursion < 0 then go maximum. */
        if (OPTrecurselevel < 0)
        {
            OPTrecurselevel = INT_MAX;
        }

            /* If none of define, refer, link then do all. */
        if (!OPTdefine && !OPTrefer && !OPTlinks)
        {
            OPTdefine = 1;
            OPTrefer = 1;
            OPTlinks = 1;
        }

            /* If HTML output then update some formatting strings. */
        if (OPThtml)
        {
            preheader = "</PRE><H4>";
            postheader = "</H4><PRE>";
        }
        else
        {
            preheader = "";
            postheader = "\n";
        }


            /* Figure out the .cym file to be used. If SPITsargv[0] is a .cym file
               then use it, else if SPITdargv[0] is a .cym file then use it, otherwise
               use SPITsargv[0] with a .cym extension. */
        if (SPITargnum > 0)
        {
            ext = strchr(SPITsargv[0], '.');
            if (ext && (stricmp(ext, ".cym") == 0))
            {
                strcpy(cymfile, SPITsargv[0]);
                doreport = 1;
            }
            else
            {
                ext = strchr(SPITdargv[0], '.');
                if (ext && (stricmp(ext, ".cym") == 0))
                {
                    strcpy(cymfile, SPITdargv[0]);
                    /* NOTE: in this case we've been explicitly told to
                       generate a .cym file, so we won't do a report. */
                    doreport = 0;
                }
                else
                {
                        /* Copy SPITsargv[0] and overwrite extension. */
                    strcpy(cymfile, SPITsargv[0]);
                    ext = strchr(cymfile, '.');
                    if (ext)
                        strcpy(ext, ".cym");
                    else
                        strcat(cymfile, ".cym");
                    doreport = 1;
                }

                    /* If file exists and we're not appending then remove it. */
                if (access(cymfile, F_OK) == 0)
                {
                    if (!OPTappend)
                    {
                        if (OPTverbose>2)
                            fprintf(stderr, "cymbal: WARNING: removing existing file \'%s\'\n", cymfile);
                        remove(cymfile);
                    }
                }
            }
        }

            /* Generate symbol info to .cym file:
                    SPITsargv[i] is a file to extract symbols from;
                    SPITdargv[i] is a file to generate a report to (if different
                        from SPITsargv[i] and not a .cym file), though only
                        SPITdargv[0] is actually used;
                    cymfile is the symbol info file. */
        for (i = 0; i < SPITargnum; ++i)
        {
                /* If source == destination or destination is a .cym
                   then set destination to NULL; output will go to stdout. */
            if (stricmp(SPITsargv[i], SPITdargv[i]) == 0)
                SPITdargv[i][0] = '\0';
    /*            SPITdargv[i] = NULL; */
            else
            {
                ext = strchr(SPITdargv[i], '.');
                if (ext && (stricmp(ext, ".cym") == 0))
                    SPITdargv[i][0] = '\0';
    /*                SPITdargv[i] = NULL; */
            }

                /* Check extension. If .a or .o then preprocess to produce .cym
                   file, then analyze. If .cym then analyze immediately. */
            ext = strchr(SPITsargv[i], '.');
            exttype = 0;
            if (ext == NULL)
            {
                fprintf(stderr, "cymbal: ERROR: no file extension for \'%s\'\n", SPITsargv[i]);
                continue;
            }
            /*
            Removed so it works with PS2, which also use .a and .o
            else if ((stricmp(ext, ".a") == 0) ||
                     (stricmp(ext, ".o") == 0))
            {
                exttype = SGI;
            }
            */
            else if ((stricmp(ext, ".lib") == 0) ||
                     (stricmp(ext, ".obj") == 0))
            {
                exttype = OPTobjtype; /* defaults to WATCOM */
            }
            else if (stricmp(ext, ".cym") != 0)
            {
                    /* Assume object file of specified (or default) type. */
                exttype = OPTobjtype;
            }

                /* If extension type set then perform appropriate pre-processing. */
            if (exttype)
            {
                    /* Preprocess. */
                switch (exttype)
                {
                    case SGI:
                    case U64:
                        if (!sgicym(SPITsargv[i], cymfile))
                        {
                            fprintf(stderr, "cymbal: ERROR reading symbols from \'%s\'\n", SPITsargv[i]);
                            continue;
                        }
                        break;

                    case WATCOM:
                        if (!pccym(SPITsargv[i], cymfile))
                        {
                            fprintf(stderr, "cymbal: ERROR reading symbols from \'%s\'\n", SPITsargv[i]);
                            continue;
                        }
                        break;

                    case PSX:
                        if (!psxcym(SPITsargv[i], cymfile))
                        {
                            fprintf(stderr, "cymbal: ERROR reading symbols from \'%s\'\n", SPITsargv[i]);
                            continue;
                        }
                        break;

                    case PS2:
                        if (!ps2cym(SPITsargv[i], cymfile))
                        {
                            fprintf(stderr, "cymbal: ERROR reading symbols from \'%s\'\n", SPITsargv[i]);
                            continue;
                        }
                        break;

                    case GC:
                        if (!gccym(SPITsargv[i], cymfile))
                        {
                            fprintf(stderr, "cymbal: ERROR reading symbols from \'%s\'\n", SPITsargv[i]);
                            continue;
                        }
                        break;

                    default:
                        fprintf(stderr, "cymbal: unknown extension type %d\n", exttype);
                        break;
                }
            }
        }
    } while (more>0);


        /* Preprocessing complete, cymfile now contains all
           the symbols. SPITdargv[0] is either a report filename
           or NULL (report to stdout).

           Perform symbol analysis using global options. */
    if (doreport)
        cymbal(cymfile, SPITdargv[0]);

    cleanup();

    return 0;
}
