root/third_party/codesighs/msdump2symdb.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. trimWhite
  2. skipWhite
  3. skipNonWhite
  4. slash2bs
  5. skipToArg
  6. getLastArg
  7. processLine
  8. dumpCleanup
  9. qsortSymOffset
  10. calcContainer
  11. reportContainer
  12. dump2symdb
  13. initOptions
  14. cleanOptions
  15. showHelp
  16. main

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is msdump2symdb.c code, released
 * Jan 16, 2003.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 2002
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Garrett Arch Blythe, 16-January-2003
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <errno.h>

#define ERROR_REPORT(num, val, msg)   fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg));
#define CLEANUP(ptr)    do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0)


typedef struct __struct_Options
/*
**  Options to control how we perform.
**
**  mProgramName    Used in help text.
**  mInput          File to read for input.
**                  Default is stdin.
**  mInputName      Name of the file.
**  mOutput         Output file, append.
**                  Default is stdout.
**  mOutputName     Name of the file.
**  mHelp           Whether or not help should be shown.
*/
{
    const char* mProgramName;
    FILE* mInput;
    char* mInputName;
    FILE* mOutput;
    char* mOutputName;
    int mHelp;
}
Options;


typedef struct __struct_Switch
/*
**  Command line options.
*/
{
    const char* mLongName;
    const char* mShortName;
    int mHasValue;
    const char* mValue;
    const char* mDescription;
}
Switch;

#define DESC_NEWLINE "\n\t\t"

static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."};
static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."};
static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."};

static Switch* gSwitches[] = {
        &gInputSwitch,
        &gOutputSwitch,
        &gHelpSwitch
};


typedef struct __struct_MSDump_Symbol
/*
**  Struct to hold infomration on a symbol.
**
**  mSize               Size of the symbol once all work is complete.
**  mOffset             Offset of the symbol in the section.
**  mName               Symbolic name.
*/
{
    unsigned    mSize;
    unsigned    mOffset;
    char*       mName;
}
MSDump_Symbol;


typedef struct __struct_MSDump_Section
/*
**  Struct for holding information on a section.
**
**  mLength             Length of the section in bytes.
**  mUsed               Number of bytes used in the section thus far.
**                      Should eventually match mLength after work is done.
**  mType               Type of section, as string (.data, .text, et. al.)
**  mSymbols            Symbols found inside the section.
**  mSymbolCount        Number of symbols in array.
*/
{
    unsigned            mLength;
    unsigned            mUsed;
    char*               mType;

    MSDump_Symbol*      mSymbols;
    unsigned            mSymbolCount;
}
MSDump_Section;


typedef struct __struct_MSDump_Object
/*
**  Struct for holding object's data.
*/
{
    char*   mObject;

    MSDump_Section*     mSections;
    unsigned            mSectionCount;
}
MSDump_Object;


typedef struct __struct_MSDump_ReadState
/*
**  State flags while reading the input gives us hints on what to do.
**
**  mSkipLines                  Number of lines to skip without parsing.
**  mSectionDetails             Section information next, like line length.
**  mCurrentObject              Object file we are dealing with.
*/
{
    unsigned            mSkipLines;
    unsigned            mSectionDetails;
    MSDump_Object*      mCurrentObject;
}
MSDump_ReadState;


typedef struct __struct_MSDump_Container
/*
**  Umbrella container for all data encountered.
*/
{
    MSDump_ReadState    mReadState;

    MSDump_Object*      mObjects;
    unsigned            mObjectCount;
}
MSDump_Container;


void trimWhite(char* inString)
/*
**  Remove any whitespace from the end of the string.
*/
{
    int len = strlen(inString);

    while(len)
    {
        len--;

        if(isspace(*(inString + len)))
        {
            *(inString + len) = '\0';
        }
        else
        {
            break;
        }
    }
}


const char* skipWhite(const char* inString)
/*
**  Return pointer to first non white space character.
*/
{
    const char* retval = inString;

    while('\0' != *retval && isspace(*retval))
    {
        retval++;
    }

    return retval;
}


const char* skipNonWhite(const char* inString)
/*
**  Return pointer to first white space character.
*/
{
    const char* retval = inString;

    while('\0' != *retval && !isspace(*retval))
    {
        retval++;
    }

    return retval;
}


void slash2bs(char* inString)
/*
**  Change any forward slash to a backslash.
*/
{
    char* slash = inString;

    while(NULL != (slash = strchr(slash, '/')))
    {
        *slash = '\\';
        slash++;
    }
}


const char* skipToArg(const char* inString, unsigned inArgIndex)
/*
**  Return pointer either to the arg or NULL.
**  1 indexed.
*/
{
    const char* retval = NULL;

    while(0 != inArgIndex && '\0' != *inString)
    {
        inArgIndex--;

        inString = skipWhite(inString);
        if(0 != inArgIndex)
        {
            inString = skipNonWhite(inString);
        }
    }

    if('\0' != *inString)
    {
        retval = inString;
    }

    return retval;
}


const char* getLastArg(const char* inString)
/*
**  Return pointer to last arg in string.
*/
{
    const char* retval = NULL;
    int length = 0;
    int sawString = 0;

    length = strlen(inString);
    while(0 != length)
    {
        length--;

        if(0 == sawString)
        {
            if(0 == isspace(inString[length]))
            {
                sawString = __LINE__;
            }
        }
        else
        {
            if(0 != isspace(inString[length]))
            {
                retval = inString + length + 1;
            }
        }
    }

    return retval;
}


int processLine(Options* inOptions, MSDump_Container* inContainer, const char* inLine)
/*
**  Handle one line at a time.
**  Looking for several different types of lines.
**  Ignore all other lines.
**  The container is the state machine.
**  returns 0 on no error.
*/
{
    int retval = 0;

    /*
    **  Check to see if we were expecting section details.
    */
    if(0 != inContainer->mReadState.mSectionDetails)
    {
        const char* length = NULL;
        unsigned sectionIndex = 0;

        /*
        **  Detail is a 1 based index....
        **  Reset.
        */
        sectionIndex = inContainer->mReadState.mSectionDetails - 1;
        inContainer->mReadState.mSectionDetails = 0;

        if(0 == strncmp("    Section length", inLine, 18))
        {
            const char* sectionLength = NULL;
            unsigned numericLength = 0;
            char* endScan = NULL;

            sectionLength = skipWhite(inLine + 18);

            errno = 0;
            numericLength = strtoul(sectionLength, &endScan, 16);
            if(0 == errno && endScan != sectionLength)
            {
                inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mLength = numericLength;
            }
            else
            {
                retval = __LINE__;
                ERROR_REPORT(retval, inLine, "Cannot scan for section length.");
            }
        }
        else
        {
            retval = __LINE__;
            ERROR_REPORT(retval, inLine, "Cannot parse section line.");
        }
    }
    /*
    **  Check for switching object file symbols.
    */
    else if(0 == strncmp("Dump of file ", inLine, 13))
    {
        const char* dupMe = inLine + 13;
        char* dup = NULL;
        
        dup = strdup(dupMe);
        if(NULL != dup)
        {
            void* growth = NULL;
            
            trimWhite(dup);
            slash2bs(dup);
            
            
            growth = realloc(inContainer->mObjects, (inContainer->mObjectCount + 1) * sizeof(MSDump_Object));
            if(NULL != growth)
            {
                unsigned int index = inContainer->mObjectCount;
                
                inContainer->mObjectCount++;
                inContainer->mObjects = growth;
                memset(inContainer->mObjects + index, 0, sizeof(MSDump_Object));
                
                inContainer->mObjects[index].mObject = dup;

                /*
                **  Reset the read state for this new object.
                */
                memset(&inContainer->mReadState, 0, sizeof(MSDump_ReadState));

                /*
                **  Record our current object file.
                */
                inContainer->mReadState.mCurrentObject = inContainer->mObjects + index;

                /*
                **  We can skip a few lines.
                */
                inContainer->mReadState.mSkipLines = 4;
            }
            else
            {
                retval = __LINE__;
                ERROR_REPORT(retval, dup, "Unable to grow object array.");
                free(dup);
            }
        }
        else
        {
            retval = __LINE__;
            ERROR_REPORT(retval, dupMe, "Unable to copy string.");
            
        }
    }
    /*
    **  Check for a symbol dump or a section header.
    */
    else if(isxdigit(*inLine) && isxdigit(*(inLine + 1)) && isxdigit(*(inLine + 2)))
    {
        const char* sectionString = NULL;

        /*
        **  Determine the section for this line.
        **  Ignore DEBUG sections.
        */
        sectionString = skipToArg(inLine, 3);
        if(NULL != sectionString)
        {
            if(0 != strncmp(sectionString, "DEBUG", 5) && 0 != strncmp(sectionString, "ABS", 3) && 0 != strncmp(sectionString, "UNDEF", 5))
            {
                /*
                **  MUST start with "SECT"
                */
                if(0 == strncmp(sectionString, "SECT", 4))
                {
                    unsigned sectionIndex1 = 0;

                    char *endScan = NULL;

                    sectionString += 4;

                    /*
                    **  Convert the remaining string to an index.
                    **  It will be 1 based.
                    */
                    errno = 0;
                    sectionIndex1 = strtoul(sectionString, &endScan, 16);
                    if(0 == errno && endScan != sectionString && 0 != sectionIndex1)
                    {
                        unsigned sectionIndex = sectionIndex1 - 1;

                        /*
                        **  Is this a new section? Assumed to be ascending.
                        **  Or is this a symbol in the section?
                        */
                        if(sectionIndex1 > inContainer->mReadState.mCurrentObject->mSectionCount)
                        {
                            const char* typeArg = NULL;

                            /*
                            **  New Section, figure out the type.
                            */
                            typeArg = skipToArg(sectionString, 5);
                            if(NULL != typeArg)
                            {
                                char* typeDup = NULL;

                                /*
                                **  Skip the leading period before duping.
                                */
                                if('.' == *typeArg)
                                {
                                    typeArg++;
                                }
                                typeDup = strdup(typeArg);

                                if(NULL != typeDup)
                                {
                                    void* moved = NULL;
                                    char* nonWhite = NULL;

                                    /*
                                    **  Terminate the duplicate after the section type.
                                    */
                                    nonWhite = (char*)skipNonWhite(typeDup);
                                    if(NULL != nonWhite)
                                    {
                                        *nonWhite = '\0';
                                    }

                                    /*
                                    **  Create more space for the section in the object...
                                    */
                                    moved = realloc(inContainer->mReadState.mCurrentObject->mSections, sizeof(MSDump_Section) * sectionIndex1);
                                    if(NULL != moved)
                                    {
                                        unsigned oldCount = inContainer->mReadState.mCurrentObject->mSectionCount;

                                        inContainer->mReadState.mCurrentObject->mSections = (MSDump_Section*)moved;
                                        inContainer->mReadState.mCurrentObject->mSectionCount = sectionIndex1;
                                        memset(&inContainer->mReadState.mCurrentObject->mSections[oldCount], 0, sizeof(MSDump_Section) * (sectionIndex1 - oldCount));
                                        
                                        /*
                                        **  Other section details.
                                        */
                                        inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mType = typeDup;
                                            
                                            
                                        /*
                                        **  Mark it so that we look for the length on the next line.
                                        **  This happens on next entry into the read state.
                                        */
                                        inContainer->mReadState.mSectionDetails = sectionIndex1;
                                    }
                                    else
                                    {
                                        retval = __LINE__;
                                        ERROR_REPORT(retval, inLine, "Unable to grow for new section.");
                                        free(typeDup);
                                    }
                                }
                                else
                                {
                                    retval = __LINE__;
                                    ERROR_REPORT(retval, typeArg, "Unable to duplicate type.");
                                }
                            }
                            else
                            {
                                retval = __LINE__;
                                ERROR_REPORT(retval, inLine, "Unable to determine section type.");
                            }

                        }
                        else
                        {
                            const char* offsetArg = NULL;
                            const char* classArg = NULL;
                            unsigned classWords = 1;
                            const char* symbolArg = NULL;

                            /*
                            **  This is an section we've seen before, and must list a symbol.
                            **  Figure out the things we want to know about the symbol, e.g. size.
                            **  We will ignore particular classes of symbols.
                            */

                            offsetArg = skipToArg(inLine, 2);

                            classArg = skipToArg(offsetArg, 4);
                            if(0 == strncmp(classArg, "()", 2))
                            {
                                classArg = skipToArg(classArg, 2);
                            }
                            if(0 == strncmp(classArg, ".bf or.ef", 9))
                            {
                                classWords = 2;
                            }

                            symbolArg = skipToArg(classArg, 3 + (classWords - 1));

                            /*
                            **  Skip particular lines/items.
                            */
                            if(
                                0 != strncmp(classArg, "Label", 5) &&
                                0 != strncmp(symbolArg, ".bf", 3) &&
                                0 != strncmp(symbolArg, ".lf", 3) &&
                                0 != strncmp(symbolArg, ".ef", 3)
                                )
                            {
                                char* endOffsetArg = NULL;
                                unsigned offset = 0;
                                
                                /*
                                ** Convert the offset to something meaninful (size).
                                */
                                errno = 0;
                                offset = strtoul(offsetArg, &endOffsetArg, 16);
                                if(0 == errno && endOffsetArg != offsetArg)
                                {
                                    void* moved = NULL;
                                    
                                    /*
                                    **  Increase the size of the symbol array in the section.
                                    **  Assumed symbols are unique within each section.
                                    */
                                    moved = realloc(inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols, sizeof(MSDump_Symbol) * (inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbolCount + 1));
                                    if(NULL != moved)
                                    {
                                        unsigned symIndex = 0;

                                        /*
                                        **  Record symbol details.
                                        **  Assumed symbols are encountered in order for their section (size calc depends on it).
                                        */
                                        symIndex = inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbolCount;
                                        inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbolCount++;
                                        inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols = (MSDump_Symbol*)moved;
                                        memset(&inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols[symIndex], 0, sizeof(MSDump_Symbol));
                                        
                                        inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols[symIndex].mOffset = offset;
                                        
                                        /*
                                        **  We could allocate smarter here if it ever mattered.
                                        */
                                        inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols[symIndex].mName = strdup(symbolArg);
                                        if(NULL != inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols[symIndex].mName)
                                        {
                                            char* trim = NULL;

                                            trim = (char*)skipNonWhite(inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols[symIndex].mName);
                                            if(NULL != trim)
                                            {
                                                *trim = '\0';
                                            }
                                        }
                                        else
                                        {
                                            retval = __LINE__;
                                            ERROR_REPORT(retval, inLine, "Unable to duplicate symbol name.");
                                        }
                                    }
                                    else
                                    {
                                        retval = __LINE__;
                                        ERROR_REPORT(retval, inLine, "Unable to grow symbol array for section.");
                                    }
                                }
                                else
                                {
                                    retval = __LINE__;
                                    ERROR_REPORT(retval, inLine, "Unable to convert offset to a number.");
                                }
                            }
                        }
                    }
                    else
                    {
                        retval = __LINE__;
                        ERROR_REPORT(retval, inLine, "Unable to determine section index.");
                    }
                }
                else
                {
                    retval = __LINE__;
                    ERROR_REPORT(retval, inLine, "No match for section prefix.");
                }
            }
        }
        else
        {
            retval = __LINE__;
            ERROR_REPORT(retval, inLine, "Unable to scan for section.");
        }
    }

    return retval;
}


void dumpCleanup(MSDump_Container* inContainer)
/*
**  Attempt to be nice and free up what we have allocated.
*/
{
    unsigned objectLoop = 0;
    unsigned sectionLoop = 0;
    unsigned symbolLoop = 0;

    for(objectLoop = 0; objectLoop < inContainer->mObjectCount; objectLoop++)
    {
        for(sectionLoop = 0; sectionLoop < inContainer->mObjects[objectLoop].mSectionCount; sectionLoop++)
        {
            for(symbolLoop = 0; symbolLoop < inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbolCount; symbolLoop++)
            {
                CLEANUP(inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mName);
            }
            inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbolCount = 0;
            CLEANUP(inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols);
            CLEANUP(inContainer->mObjects[objectLoop].mSections[sectionLoop].mType);
        }
        inContainer->mObjects[objectLoop].mSectionCount = 0;
        CLEANUP(inContainer->mObjects[objectLoop].mSections);
    }
    CLEANUP(inContainer->mObjects);
    inContainer->mObjectCount = 0;
}


int qsortSymOffset(const void* in1, const void* in2)
/*
**  qsort callback to sort the symbols by their offset.
*/
{
    MSDump_Symbol* sym1 = (MSDump_Symbol*)in1;
    MSDump_Symbol* sym2 = (MSDump_Symbol*)in2;
    int retval = 0;

    if(sym1->mOffset < sym2->mOffset)
    {
        retval = 1;
    }
    else if(sym1->mOffset > sym2->mOffset)
    {
        retval = -1;
    }

    return retval;
}


int calcContainer(Options* inOptions, MSDump_Container* inContainer)
/*
**  Resposible for doing any size calculations based on the offsets known.
**  After this calculation, each sections mUsed will match mSize.
**  After this calculation, all symbols should know how big they are.
*/
{
    int retval = 0;
    unsigned objectLoop = 0;
    unsigned sectionLoop = 0;
    unsigned symbolLoop = 0;


    /*
    **  Need to sort all symbols by their offsets.
    */
    for(objectLoop = 0; 0 == retval && objectLoop < inContainer->mObjectCount; objectLoop++)
    {
        for(sectionLoop = 0; 0 == retval && sectionLoop < inContainer->mObjects[objectLoop].mSectionCount; sectionLoop++)
        {
            qsort(
                inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols,
                inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbolCount,
                sizeof(MSDump_Symbol),
                qsortSymOffset
                );
        }
    }


    /*
    **  Need to go through all symbols and calculate their size.
    */
    for(objectLoop = 0; 0 == retval && objectLoop < inContainer->mObjectCount; objectLoop++)
    {
        for(sectionLoop = 0; 0 == retval && sectionLoop < inContainer->mObjects[objectLoop].mSectionCount; sectionLoop++)
        {
            for(symbolLoop = 0; 0 == retval && symbolLoop < inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbolCount; symbolLoop++)
            {
                inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mSize =
                    inContainer->mObjects[objectLoop].mSections[sectionLoop].mLength -
                    inContainer->mObjects[objectLoop].mSections[sectionLoop].mUsed -
                    inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mOffset;

                inContainer->mObjects[objectLoop].mSections[sectionLoop].mUsed += 
                    inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mSize;
            }
        }
    }


    return retval;
}


int reportContainer(Options* inOptions, MSDump_Container* inContainer)
/*
**  Display all symbols and their data.
**  We'll use a tsv format.
*/
{
    int retval = 0;
    unsigned objectLoop = 0;
    unsigned sectionLoop = 0;
    unsigned symbolLoop = 0;
    int printRes = 0;

    for(objectLoop = 0; 0 == retval && objectLoop < inContainer->mObjectCount; objectLoop++)
    {
        for(sectionLoop = 0; 0 == retval && sectionLoop < inContainer->mObjects[objectLoop].mSectionCount; sectionLoop++)
        {
            for(symbolLoop = 0; 0 == retval && symbolLoop < inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbolCount; symbolLoop++)
            {
                printRes = fprintf(inOptions->mOutput, "%s\t%s\t%.8X\t%s\n",
                    inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mName,
                    inContainer->mObjects[objectLoop].mSections[sectionLoop].mType,
                    inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mSize,
                    inContainer->mObjects[objectLoop].mObject
                    );

                if(0 > printRes)
                {
                    retval = __LINE__;
                    ERROR_REPORT(retval, inOptions->mOutputName, "Unable to write to file.");
                }
            }
        }
    }

    return retval;
}


int dump2symdb(Options* inOptions)
/*
**  Convert the input into the output, respecting the options.
**  Returns 0 on success.
*/
{
    int retval = 0;
    char lineBuffer[0x800];
    MSDump_Container container;

    memset(&container, 0, sizeof(container));

    /*
    **  Read the file line by line.
    */
    while(0 == retval && NULL != fgets(lineBuffer, sizeof(lineBuffer), inOptions->mInput))
    {
        if(0 != container.mReadState.mSkipLines)
        {
            container.mReadState.mSkipLines--;
            continue;
        }
        retval = processLine(inOptions, &container, lineBuffer);
    }

    /*
    **  Perform whatever calculations desired.
    */
    if(0 == retval)
    {
        retval = calcContainer(inOptions, &container);
    }

    /*
    **  Output what we know.
    */
    if(0 == retval)
    {
        retval = reportContainer(inOptions, &container);
    }

    /*
    **  Cleanup what we've done.
    */
    dumpCleanup(&container);

    return retval;
}


int initOptions(Options* outOptions, int inArgc, char** inArgv)
/*
**  returns int     0 if successful.
*/
{
    int retval = 0;
    int loop = 0;
    int switchLoop = 0;
    int match = 0;
    const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
    Switch* current = NULL;

    /*
    **  Set any defaults.
    */
    memset(outOptions, 0, sizeof(Options));
    outOptions->mProgramName = inArgv[0];
    outOptions->mInput = stdin;
    outOptions->mInputName = strdup("stdin");
    outOptions->mOutput = stdout;
    outOptions->mOutputName = strdup("stdout");

    if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName)
    {
        retval = __LINE__;
        ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup.");
    }

    /*
    **  Go through and attempt to do the right thing.
    */
    for(loop = 1; loop < inArgc && 0 == retval; loop++)
    {
        match = 0;
        current = NULL;

        for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++)
        {
            if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop]))
            {
                match = __LINE__;
            }
            else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop]))
            {
                match = __LINE__;
            }

            if(match)
            {
                if(gSwitches[switchLoop]->mHasValue)
                {
                    /*
                    **  Attempt to absorb next option to fullfill value.
                    */
                    if(loop + 1 < inArgc)
                    {
                        loop++;

                        current = gSwitches[switchLoop];
                        current->mValue = inArgv[loop];
                    }
                }
                else
                {
                    current = gSwitches[switchLoop];
                }

                break;
            }
        }

        if(0 == match)
        {
            outOptions->mHelp = __LINE__;
            retval = __LINE__;
            ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch.");
        }
        else if(NULL == current)
        {
            outOptions->mHelp = __LINE__;
            retval = __LINE__;
            ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value.");
        }
        else
        {
            /*
            ** Do something based on address/swtich.
            */
            if(current == &gInputSwitch)
            {
                CLEANUP(outOptions->mInputName);
                if(NULL != outOptions->mInput && stdin != outOptions->mInput)
                {
                    fclose(outOptions->mInput);
                    outOptions->mInput = NULL;
                }

                outOptions->mInput = fopen(current->mValue, "r");
                if(NULL == outOptions->mInput)
                {
                    retval = __LINE__;
                    ERROR_REPORT(retval, current->mValue, "Unable to open input file.");
                }
                else
                {
                    outOptions->mInputName = strdup(current->mValue);
                    if(NULL == outOptions->mInputName)
                    {
                        retval = __LINE__;
                        ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
                    }
                }
            }
            else if(current == &gOutputSwitch)
            {
                CLEANUP(outOptions->mOutputName);
                if(NULL != outOptions->mOutput && stdout != outOptions->mOutput)
                {
                    fclose(outOptions->mOutput);
                    outOptions->mOutput = NULL;
                }

                outOptions->mOutput = fopen(current->mValue, "a");
                if(NULL == outOptions->mOutput)
                {
                    retval = __LINE__;
                    ERROR_REPORT(retval, current->mValue, "Unable to open output file.");
                }
                else
                {
                    outOptions->mOutputName = strdup(current->mValue);
                    if(NULL == outOptions->mOutputName)
                    {
                        retval = __LINE__;
                        ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
                    }
                }
            }
            else if(current == &gHelpSwitch)
            {
                outOptions->mHelp = __LINE__;
            }
            else
            {
                retval = __LINE__;
                ERROR_REPORT(retval, current->mLongName, "No handler for command line switch.");
            }
        }
    }

    return retval;
}


void cleanOptions(Options* inOptions)
/*
**  Clean up any open handles.
*/
{
    CLEANUP(inOptions->mInputName);
    if(NULL != inOptions->mInput && stdin != inOptions->mInput)
    {
        fclose(inOptions->mInput);
    }
    CLEANUP(inOptions->mOutputName);
    if(NULL != inOptions->mOutput && stdout != inOptions->mOutput)
    {
        fclose(inOptions->mOutput);
    }

    memset(inOptions, 0, sizeof(Options));
}


void showHelp(Options* inOptions)
/*
**  Show some simple help text on usage.
*/
{
    int loop = 0;
    const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
    const char* valueText = NULL;

    printf("usage:\t%s [arguments]\n", inOptions->mProgramName);
    printf("\n");
    printf("arguments:\n");

    for(loop = 0; loop < switchCount; loop++)
    {
        if(gSwitches[loop]->mHasValue)
        {
            valueText = " <value>";
        }
        else
        {
            valueText = "";
        }

        printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText);
        printf("\t %s%s", gSwitches[loop]->mShortName, valueText);
        printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription);
    }

    printf("This tool takes the output of \"dumpbin /symbols\" to produce a simple\n");
    printf("tsv db file of symbols and their respective attributes, like size.\n");
}


int main(int inArgc, char** inArgv)
{
    int retval = 0;
    Options options;

    retval = initOptions(&options, inArgc, inArgv);
    if(options.mHelp)
    {
        showHelp(&options);
    }
    else if(0 == retval)
    {
        retval = dump2symdb(&options);
    }

    cleanOptions(&options);
    return retval;
}


/* [<][>][^][v][top][bottom][index][help] */