/***********************************************************************
 *
 *  avra - Assembler for the Atmel AVR microcontroller series
 *
 *  Copyright (C) 1998-2004 Jon Anders Haugum, Tobias Weber
 *
 * coff.c - Common Object File Format (COFF) support
 *
 *	This file was developed for the avra assembler in order to produce COFF output files 
 *  for use with the Atmel AVR Studio.  The Lean C Compiler (LCC) debugging stabs 
 *	output was used as input to the assembler.  The information used to develop this file
 *  was obtained from various sources on the Internet, most notably, the Free Software Foundation,
 *  The "stabs" debug format, ??? Chapter 7: Common Object File Format (COFF), 
 *
 *	This software has absolutely no warrantee!  The money you paid for this will be 
 *  promptly refunded if not fully satisfied.
 * 
 *	Beta release 1/20/2000 by Bob Harris
 *
 *	This software has not been fully tested and probably has a few software deficiencies.
 *  Some software support may be possible by sending a problem description report to 
 *  rth@mclean.sparta.com
 *
 *  Made the recommended change in write_coff_program().
 *  Fixed an obvious typo in SkipPastDigits().  The if() statement was terminated 
 *  with a semicolon, which terminated the if(); early.  JEG 4-01-03
 */

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

#include "misc.h"
#include "avra.h"
#include "args.h"

#include "coff.h"
#include "device.h"	/* device flash and eeprom size */


struct FundamentalType {

    char const *pString;
    int Type;
    int Size;
};

struct FundamentalType FundamentalTypes[] = {
    {"null", T_NULL, 0},
    {"void", T_VOID, 0},
    {"char", T_CHAR, 1},
    {"short", T_SHORT, 1},
    {"int", T_INT, 1},
    {"long", T_LONG, 2}, 
    {"float", T_FLOAT, 4},
    {"double", T_DOUBLE, 4},
    {"struct", T_STRUCT, 0},
    {"union", T_UNION, 0},
    {"enum",  T_ENUM, 0},
    {"moe", T_MOE, 0},
    {"unsigned char", T_UCHAR, 1},
    {"unsigned short", T_USHORT, 1},
    {"unsigned int", T_UINT, 1},
    {"unsigned long", T_ULONG, 2},
    {"long double", T_LNGDBL, 2},
    {"long long int", T_LONG, 2},
    {"long int", T_LONG, 2},
    {"unsigned long long", T_ULONG, 2}, 
    {"signed char", T_CHAR, 1},
    {0, 0}
};


struct coff_info *ci;

/****************************************************************************************/

FILE *open_coff_file(struct prog_info *pi, char *filename){

    int ok /*, i*/;
    FILE *fp;
    //unsigned long *pu4;
    char*p;


    ci = calloc( 1, sizeof(struct coff_info) );
    if ( !ci )
        return( 0 );

    ok = True;
    /* default values */
    ci->CurrentFileNumber = 0;
    ci->pRomMemory = 0; 
    ci->pEEPRomMemory = 0;
    ci->MaxRomAddress = 0;
    ci->MaxEepromAddress = 0;
    ci->NeedLineNumberFixup = 0;
    ci->GlobalStartAddress = -1;
    ci->GlobalEndAddress = 0;

    /* Linked lists start out at zero */
    InitializeList( &ci->ListOfSectionHeaders );
    InitializeList( &ci->ListOfRawData );
    InitializeList( &ci->ListOfRelocations );
    InitializeList( &ci->ListOfLineNumbers );
    InitializeList( &ci->ListOfSymbols );
    InitializeList( &ci->ListOfGlobals );
    InitializeList( &ci->ListOfSpecials );
    InitializeList( &ci->ListOfUndefined );
    InitializeList( &ci->ListOfStrings );
    InitializeList( &ci->ListOfTypes );
    InitializeList( &ci->ListOfSplitLines );

    /* add two default sections to SectionHeaders */
    if (  !AllocateListObject( &ci->ListOfSectionHeaders, sizeof(struct external_scnhdr) ) ||
          !AllocateListObject( &ci->ListOfSectionHeaders, sizeof(struct external_scnhdr) ) ) {

        fprintf(stderr, "\nOut of memory allocating section headers!");
        return( 0 );
    }

    /* add to string table */
    p = (char *)AllocateListObject( &ci->ListOfStrings,  4 );
    if ( !p ) {
        fprintf(stderr, "\nOut of memory allocating string table space!");
        return( 0 );
    }

    /* Allocate space for binary output into ROM, and EEPROM memory buffers for COFF output */
    /* ASSUMES ci->device is accurate */
    if ( (ci->pRomMemory = AllocateListObject( &ci->ListOfRawData, pi->device->flash_size * 2 ) ) != 0) {
        if ( (ci->pEEPRomMemory = AllocateListObject( &ci->ListOfRawData, pi->device->eeprom_size )) != 0) {
            ok = True; /* only true if both buffers are properly allocated */
            /* now fill them with 0xff's to simulate flash erasure */
            memset( (void *)ci->pRomMemory, 0xff, pi->device->flash_size * 2 );
            memset(    (   void    *)ci->pEEPRomMemory,    0xff,    pi->device->eeprom_size    );
        }
    }
    if ( ok != True )
        return( 0 );

    fp = fopen(filename,"wb");
    if ( fp == NULL ) {
        fprintf(stderr,"Error: cannot write coff file\n");
        return( fp );
    }
    /* simulate void type .stabs void:t15=r1;*/
    stab_add_local_type( "void", "15=r1;0;0;" );

    return( fp );   
}

/****************************************************************************************/
void write_coff_file(struct prog_info *pi){

    //FILE *fp;
    //struct label *label;
    char /* File[256],*/*p;
    struct external_scnhdr *pSectionHdr;
    struct syment *pEntry;
    union auxent *pAux;
    unsigned long *plong;
    int NumberOfSymbols, SymbolIndex, LastFileIndex, LastFunctionIndex, LastFunctionAddress;
    LISTNODE *pNode;
    int LinesOffset, SymbolsOffset, StringsOffset, RawOffset;
    struct lineno *pLine; 

    /* add two special sections */
    /* one for .text */
    if (  ( pEntry = (struct syment*)AllocateTwoListObjects( &ci->ListOfSpecials, sizeof(struct syment) * 2 ) ) == 0 ) {
        fprintf(stderr, "\nOut of memory allocating special headers for .text!");
        return;
    }
    memset( pEntry->n_name, 0, 8 );
    strcpy( pEntry->n_name, ".text" );
    pEntry->n_value = 0;
    pEntry->n_scnum = 1;
    pEntry->n_type = 0;
    pEntry->n_sclass = C_STAT;
    pEntry->n_numaux = 1;
    pEntry++;
    pAux = (union auxent *)pEntry;
    pAux->x_scn.x_scnlen = ci->MaxRomAddress + 2;
    pAux->x_scn.x_nreloc = 0;
    pAux->x_scn.x_nlinno = ci->ListOfLineNumbers.TotalItems;
    /* one for .bss */
    if (  ( pEntry = (struct syment*)AllocateTwoListObjects( &ci->ListOfSpecials, sizeof(struct syment) * 2 ) ) == 0 ) {
        fprintf(stderr, "\nOut of memory allocating special header for .bss!");
        return;
    }
    memset( pEntry->n_name, 0, 8 );
    strcpy( pEntry->n_name, ".bss" );
    if ( ci->GlobalStartAddress == -1 ) {
        ci->GlobalEndAddress = ci->GlobalStartAddress = 0x60;
    }
    pEntry->n_value = ci->GlobalStartAddress;
    pEntry->n_scnum = 2;
    pEntry->n_type = 0;
    pEntry->n_sclass = C_STAT;
    pEntry->n_numaux = 1;
    pEntry++;
    pAux = (union auxent *)pEntry;
    pAux->x_scn.x_scnlen = 0; /* we don't store any data here */
    pAux->x_scn.x_nreloc = 0;
    pAux->x_scn.x_nlinno = 0;

    /* one more for .data - eeprom ??? */

    /* Calculate common offsets into the file */
    RawOffset = sizeof(struct external_filehdr) + ci->ListOfSectionHeaders.TotalBytes;
    LinesOffset = RawOffset + ci->MaxRomAddress + 2; /* ignore eeprom for now */
    SymbolsOffset = LinesOffset + ci->ListOfLineNumbers.TotalBytes;
    StringsOffset = SymbolsOffset + ci->ListOfSymbols.TotalBytes + ci->ListOfSpecials.TotalBytes + ci->ListOfGlobals.TotalBytes;

    /* Clean up loose ends in string table */
    if ( !(plong  = (unsigned long *)FindFirstListObject(&ci->ListOfStrings))  ) {
        fprintf(stderr,"\nInternal error in string table!");
        return;
    }
    *plong = ci->ListOfStrings.TotalBytes; /* Size of string table */

    /* Clean up loose ends in symbol table */

    /*	symbol table - Filename value - index to next .file or global symbol */
    /* The value of that symbol equals the symbol table entry index of the next .file symbol or .global */
    LastFunctionAddress = ci->MaxRomAddress;
    NumberOfSymbols = ci->ListOfSymbols.TotalItems + ci->ListOfSpecials.TotalItems + ci->ListOfGlobals.TotalItems;
    SymbolIndex = LastFileIndex = NumberOfSymbols;
    LastFunctionIndex = 0; /* set to zero on last function */
    for ( pEntry = (struct syment *)FindLastListObject(&ci->ListOfSymbols);
        pEntry != 0;
        pEntry = (struct syment *)FindNextLastListObject(&ci->ListOfSymbols) ) {

        /* Search for .file entries designated by C_FILE */
        if ( pEntry->n_sclass == C_FILE ) {
            pEntry->n_value = LastFileIndex;
            LastFileIndex = SymbolIndex; /* save current index */
        }
        /* Search for Function entries C_EXT */
        else if (  pEntry->n_sclass == C_EXT ) {
            pEntry++;
            pAux = (union auxent *)pEntry;
            pAux->x_sym.x_misc.x_fsize = LastFunctionAddress - pEntry->n_value; /* function updated size */
            pAux->x_sym.x_fcnary.x_fcn.x_lnnoptr += LinesOffset;
            LastFunctionAddress = pEntry->n_value;
            pAux->x_sym.x_fcnary.x_fcn.x_endndx = LastFunctionIndex; /* point to next function index */
            pAux->x_sym.x_tvndx = 0; /* ??? */
            LastFunctionIndex = SymbolIndex;
        } else if (  (pEntry->n_sclass == C_FCN ) || ( pEntry->n_sclass == C_BLOCK) ) {
            if ( pEntry->n_name[1] == 'b' ) {
                /* .bf and .bb */
                pEntry++;
                pAux = (union auxent *)pEntry;
                pAux->x_sym.x_fcnary.x_fcn.x_endndx = LastFunctionIndex;
            }
        }
        /* else do nothing */

        /* update current symbol index */
        pNode = GetCurrentNode( &ci->ListOfSymbols );
        SymbolIndex -= ( pNode->Size / sizeof(struct syment) );
    }

    // File Header
    ci->FileHeader.f_magic = MAGIC_NUMBER_AVR;
    ci->FileHeader.f_nscns = 2;
    ci->FileHeader.f_timdat = time( (time_t *)&ci->FileHeader.f_timdat);
    ci->FileHeader.f_symptr = SymbolsOffset;
    ci->FileHeader.f_nsyms = NumberOfSymbols;
    ci->FileHeader.f_opthdr = 0;
    ci->FileHeader.f_flags = 0xff; /*F_RELFLG;*/ /* No relocation information available */

    /* write it out */
    if ( fwrite(&ci->FileHeader, 1, sizeof(struct external_filehdr), pi->coff_file ) !=  sizeof(struct external_filehdr) ) {
        fprintf(stderr,"\nFile error writing header ...(disk full?)");
        return;
    }

    // Optional Information

    // Section 1 Header
    pSectionHdr = (struct external_scnhdr *)FindFirstListObject(&ci->ListOfSectionHeaders);
    if ( !pSectionHdr ) {
        fprintf(stderr, "\nInternal Coff error - cannot find section header .text!");
        return;
    }
    memset( &pSectionHdr->s_name[0], 0, sizeof(struct external_scnhdr) );
    strcpy( &pSectionHdr->s_name[0], ".text");
    pSectionHdr->s_paddr = 0;
    pSectionHdr->s_vaddr = 0;
    pSectionHdr->s_size = ci->MaxRomAddress + 2; /* remember the last instruction */
    pSectionHdr->s_scnptr = RawOffset;
    pSectionHdr->s_relptr = 0;
    pSectionHdr->s_lnnoptr = LinesOffset;
    pSectionHdr->s_nreloc = 0;
    pSectionHdr->s_nlnno = ci->ListOfLineNumbers.TotalBytes/sizeof(struct lineno);
    pSectionHdr->s_flags = STYP_TEXT;

    /* write it out */
    if ( fwrite(&pSectionHdr->s_name[0], 1, sizeof(struct external_scnhdr), pi->coff_file ) !=  sizeof(struct external_scnhdr) ) {
        fprintf(stderr,"\nFile error writing section header ...(disk full?)");
        return;
    }

    // Section 2 Header
    pSectionHdr = (struct external_scnhdr *)FindNextListObject(&ci->ListOfSectionHeaders);
    if ( !pSectionHdr ) {
        fprintf(stderr, "\nInternal Coff error - cannot find section header .bss!");
        return;
    }
    memset( &pSectionHdr->s_name[0], 0, sizeof(struct external_scnhdr) );
    strcpy( &pSectionHdr->s_name[0], ".bss");
    /* later expansion */
    pSectionHdr->s_paddr = ci->GlobalStartAddress;
    pSectionHdr->s_vaddr = ci->GlobalStartAddress;
    pSectionHdr->s_flags = STYP_DATA; /* seems it should be STYP_BSS */

    /* write it out */
    if ( fwrite(&pSectionHdr->s_name[0], 1, sizeof(struct external_scnhdr), pi->coff_file ) !=  sizeof(struct external_scnhdr) ) {
        fprintf(stderr,"\nFile error writing section header ...(disk full?)");
        return;
    }

    /* Section N Header - .data or eeprom */

    // Raw Data for Section 1
    if ( (p = (unsigned char *)FindFirstListObject(&ci->ListOfRawData) ) == 0 ) {
        fprintf(stderr,"\nInternal error - unable to find binary data!");
        return;
    }

    /* write it out */
    if ( fwrite( p, 1, ci->MaxRomAddress + 2, pi->coff_file ) != (size_t)(ci->MaxRomAddress + 2) ) {
        fprintf(stderr,"\nFile error writing raw .text data ...(disk full?)");
        return;
    }
    // Raw data for section n

    // Relocation Info for section 1

    // Relocation info for section n

    // Line numbers for section 1
    for ( pLine = (struct lineno *)FindFirstListObject( &ci->ListOfLineNumbers );
        pLine != 0;
        pLine = (struct lineno *)FindNextListObject( &ci->ListOfLineNumbers ) ) {

        pNode = GetCurrentNode( &ci->ListOfLineNumbers );

        /* write it out */
        if ( fwrite( pLine, 1, pNode->Size, pi->coff_file ) != pNode->Size ) {
            fprintf(stderr,"\nFile error writing line numbers ...(disk full?)");
            return;
        }
    }


    // Line numbers for section n

    // Symbol table
    for ( pEntry = (struct syment *)FindFirstListObject( &ci->ListOfSymbols );
        pEntry != 0;
        pEntry = (struct syment *)FindNextListObject( &ci->ListOfSymbols ) ) {

        pNode = GetCurrentNode( &ci->ListOfSymbols );

        /* write it out */
        if ( fwrite( pEntry, 1, pNode->Size, pi->coff_file ) != pNode->Size ) {
            fprintf(stderr,"\nFile error writing symbol table ...(disk full?)");
            return;
        }
    }

    // Symbol table of Globals
    for ( pEntry = (struct syment *)FindFirstListObject( &ci->ListOfGlobals );
        pEntry != 0;
        pEntry = (struct syment *)FindNextListObject( &ci->ListOfGlobals ) ) {

        pNode = GetCurrentNode( &ci->ListOfGlobals );

        /* write it out */
        if ( fwrite( pEntry, 1, pNode->Size, pi->coff_file ) != pNode->Size ) {
            fprintf(stderr,"\nFile error writing global symbols ...(disk full?)");
            return;
        }
    }

    /* Specials .text, .bss, .data */

    for ( pEntry = (struct syment *)FindFirstListObject( &ci->ListOfSpecials );
        pEntry != 0;
        pEntry = (struct syment *)FindNextListObject( &ci->ListOfSpecials ) ) {

        pNode = GetCurrentNode( &ci->ListOfSpecials );

        /* write it out */
        if ( fwrite( pEntry, 1, pNode->Size, pi->coff_file ) != pNode->Size ) {
            fprintf(stderr,"\nFile error writing special symbols ...(disk full?)");
            return;
        }
    }

    // String Table
    for ( p = (char *)FindFirstListObject( &ci->ListOfStrings );
        p != 0;
        p = (char *)FindNextListObject( &ci->ListOfStrings ) ) {

        pNode = GetCurrentNode( &ci->ListOfStrings );

        /* write it out */
        if ( fwrite( p, 1, pNode->Size, pi->coff_file ) != pNode->Size ) {
            fprintf(stderr,"\nFile error writing strings data ...(disk full?)");
            return;
        }
    }

    return;
}

/****************************************************************************************/

void write_coff_eeprom( struct prog_info *pi, int address, unsigned char data){

    if ( !GET_ARG(pi->args, ARG_COFF) )
        return;

    /* Coff output keeps track of binary data in memory buffers */
    if ( ci->pEEPRomMemory ) {
        if ( address <= pi->device->eeprom_size ) {
            *(ci->pEEPRomMemory + address) = data;
            if ( address >= ci->MaxEepromAddress )
                ci->MaxEepromAddress = address;   /* keep high water mark */
        } else {
            pi->error_count++;
            fprintf(stderr, "Error: EEPROM address %d exceeds max range %d", address, pi->device->eeprom_size );
        }
    }
}
/****************************************************************************************/

void write_coff_program( struct prog_info *pi, int address, unsigned int data){

    unsigned char *pByte;

    if ( !GET_ARG(pi->args, ARG_COFF) )
        return;

    /* Coff output keeps track of binary data in memory buffers, address is in bytes not words */
    if ( ci->pRomMemory ) {
/* JEG	if ( address <= pi->device->flash_size ) {  */  /* JEG 4-23-03 */
		if ( address <= pi->device->flash_size*2 ) {
            pByte = (unsigned char *)(ci->pRomMemory + address); /* point to low byte in memory */
            *pByte++ = (data & 0xff);   /* low byte */
            *pByte = ((data >> 8) & 0xff); /* high byte */

            if ( address >= ci->MaxRomAddress )
                ci->MaxRomAddress = address;   /* keep high water mark */
        } else {
            pi->error_count++;
/* JEG		fprintf(stderr, "Error: FLASH address %d exceeds max range %d", address, pi->device->flash_size ); */
			fprintf(stderr, "Error: FLASH address %d exceeds max range %d", address, pi->device->flash_size*2 );
        }
    }
}

/****************************************************************************************/

void close_coff_file(struct prog_info *pi, FILE *fp){

    /* close the output file */
    fclose( fp );
    pi->coff_file = 0;

    /* free all the internal memory buffers used by ci */

    FreeList( &ci->ListOfSectionHeaders );
    FreeList( &ci->ListOfRawData );
    FreeList( &ci->ListOfRelocations );
    FreeList( &ci->ListOfLineNumbers );
    FreeList(    &ci->ListOfSymbols    );
    FreeList( &ci->ListOfGlobals );
    FreeList( &ci->ListOfUndefined );
    FreeList( &ci->ListOfStrings );
    FreeList( &ci->ListOfTypes );
    FreeList( &ci->ListOfSplitLines );

    /* now free ci */
    free( ci );
    ci = 0;
}

/****************************************************************************************/

int parse_stabs( struct prog_info *pi, char *p, int pass ){

    int ok = True;
    int TypeCode, n;
    char *pString, *p2, *p3, *p4, *p5, *pType, *pEnd, *pp, *pJoined;


    if ( !GET_ARG(pi->args, ARG_COFF) || ( pass == PASS_1 ) )
        return(True);

    /* stabs debugging information is in the form:
    .stabs "symbolic info string", HexorDecimalTypecode, parm3, parm4, parm5
    parm1, parm2, parm3 depend on typecode

    N_LSYM	0x80		local sym: name,,0,type,offset
    N_OPT	0x3c		compiler options
    N_SO	0x64		source file name: name,,0,0,address
    N_SOL	0x84		#included file name: name,,0,0,address
    N_FUN	0x24		procedure: name,,0,linenumber,address
    N_GSYM	0x20		global symbol: name,,0,type,0
    N_LCSYM	0x28		.lcomm symbol: name,,0,type,address
    N_STSYM	0x26		static symbol: name,,0,type,address
    N_RSYM	0x40		register sym: name,,0,type,register
    N_PSYM	0xa0		parameter: name,,0,type,offset

    */

    /* Look for multiple commands per line */

    /* .stabs "linktag:T19=s46next:20=*19,0,16;last:20,16,16;a:21=ar1;0;2;22=ar1;0;3;1,32,96;\\",128,0,0,0 */
    /* .stabs "b:23=ar1;0;4;24=ar1;0;5;2,128,240;;",128,0,0,0 */


    /* Look for continuation lines per line */

    /* Get String information as a token */
    /* Parse the tokens in the stabn line buffer */
    pString = get_next_token(p, TERM_DOUBLEQUOTE );     /* zap first doublequote */
    p2 = get_next_token(pString, TERM_DOUBLEQUOTE );    /* zap last doublequote */
    p2 = get_next_token(p2, TERM_COMMA );               /* zap comma */
    p3 = get_next_token(p2, TERM_COMMA );
    p4 = get_next_token(p3, TERM_COMMA );
    p5 = get_next_token(p4, TERM_COMMA );
    pEnd = get_next_token(p5, TERM_END ); /* zap CR LF, make ASCIIZ */

    if ( !pString || !p2 || !p3 || !p4 || !p5 )
        return( False );

    /* Check for split lines */
    n = strlen( pString );
    if ( ( pString[n - 1] == '\\' ) && (pString[n - 2] == '\\') ) {
        /* We have a continuation string here */
        pString[n - 2] = 0;
        n -= 2;
        if ( !(pp = (char *)AllocateListObject( &ci->ListOfSplitLines, n + 1 ))  ) {
            fprintf(stderr, "\nOut of memory allocating continuation line!");
            return( False );
        }
        strcpy( pp, pString ); /* loose the continuation characters */
        return(True);
    }
    if ( ci->ListOfSplitLines.TotalItems > 0 ) {
        /* Join lines together and process */
        if ( !(pJoined = calloc( 1, n + ci->ListOfSplitLines.TotalBytes ) ) ) {
            fprintf(stderr, "\nOut of memory joining continuation lines!");
            return( False );
        }
        for ( pp = (char *)FindFirstListObject( &ci->ListOfSplitLines );
            pp != 0;
            pp = (char *)FindNextListObject( &ci->ListOfSplitLines ) ) {

            strcat( pJoined, pp ); /* connect the lines */
        }
        strcat( pJoined, pString );
        FreeList( &ci->ListOfSplitLines );
        if ( !AddListObject( &ci->ListOfSplitLines, pJoined, n + ci->ListOfSplitLines.TotalBytes )  ) {
            fprintf(stderr, "\nUnable to add joined continuation line");
            return( False );
        }
        pString = pJoined;
    }


    if ( *p2 == '0' )
        TypeCode = atox(p2);    /* presume to be hex 0x */
    else
        TypeCode = atoi(p2);

    switch ( TypeCode ) {

    case N_OPT:     /* compiler options */
        break;      /* nothing used here */

    case N_SO:      /* source file name: name,,0,0,address */
        ok = stab_add_filename( pString, p5 );
        break;

    case N_GSYM:    /* global symbol: name,,0,type,0 */
        pType = get_next_token(pString, TERM_COLON );   /* separate at colon */
        ok = stab_add_global( pi, pString, pType );
        break;

    case N_FUN:     /* procedure: name,,0,linenumber,address */
        ok = stab_add_function( pi, pString, p5 );
        break;

    case N_LSYM:    /* local sym: name,,0,type,offset */
        /* pString, p2 = TypeCode, p3 = 0, p4 = 0, p5 = offset */
        pType = get_next_token(pString, TERM_COLON );   /* pType = symbol descriptor (character after the colon) */
        if ( *pType == 't')
            ok = stab_add_local_type( pString, ++pType );
        else if (*pType == 'T')
            ok = stab_add_tag_type( pString, ++pType );
        else
            ok = stab_add_local( pi, pString, pType, p5 );
        break;

    case N_RSYM:    /* Symbol:[P|r]type,0,size,register */
        pType = get_next_token(pString, TERM_COLON );   /* separate at colon */
        ok = stab_add_local_register( pi, pString, pType, p5 );
        break;

    case N_LCSYM:   /* .lcomm symbol: name,,0,type,address */
        ok = True;
        break;  /* ignore constants */

    case N_STSYM:   /* static symbol: name,,0,type,address */
        pType = get_next_token(pString, TERM_COLON );   /* separate at colon */
        ok = stab_add_static_symbol( pi, pString, pType, p5 );
        break;

    case N_PSYM:    /* parameter: name,,0,type,offset */
        pType = get_next_token(pString, TERM_COLON );   /* separate at colon */
        ok = stab_add_parameter_symbol( pi, pString, pType, p5 );
        break;

    case N_SOL:     /* #included file name: name,,0,0,address */
        ok = True;
        break;  /* ignore include files */

    default:
        ok = False;
    }

    if ( ci->ListOfSplitLines.TotalItems > 0 )
        FreeList( &ci->ListOfSplitLines );

    return( ok );
}
/****************************************************************************************/

int parse_stabn( struct prog_info *pi, char *p, int pass ){

    int ok = True;
    int TypeCode /* , LineNumber */, Level;
    char *p1, *p2, *p3, *p4, *pLabel, *pFunction, *pEnd;

    /* stabn debugging information is in the form:
    .stabn TypeCode, 0, parm1, parm2
    parm1 is level
    parm2 is Label-Function

    compiler currently produces the following TypeCodes:
    N_LBRAC	0xc0		left bracket: 0,,0,nesting level,address
    N_RBRAC	0xe0		right bracket: 0,,0,nesting level,address
    N_SLINE	0x44		src line: 0,,0,linenumber,address
    */

    if ( !GET_ARG(pi->args, ARG_COFF) || ( pass == PASS_1 ) )
        return(True);

    /* Parse the tokens in the stabn line buffer */
    p1 = get_next_token(p, TERM_SPACE );
    p2 = get_next_token(p1, TERM_COMMA );
    p3 = get_next_token(p2, TERM_COMMA );
    p4 = get_next_token(p3, TERM_COMMA );
    pEnd = get_next_token(p4, TERM_END ); /* zap CR LF, make ASCIIZ */

    if ( !p1 || !p2 || !p3 || !p4 )
        return( False );

    /* first convert TypeCode to binary */
    if ( *p1 == '0' )
        TypeCode = atox(p1);    /* presume to be hex 0x */
    else
        TypeCode = atoi(p1);

    Level = atoi(p3);   /* line number or level */
    pLabel = p4;                /* Assembly label */
    pFunction = get_next_token( pLabel, TERM_DASH ); /* Function */

    switch ( TypeCode ) {
    case N_SLINE:           /* src line: 0,,0,linenumber,address */
        ok = stab_add_lineno( pi, Level, pLabel, pFunction );
        break;

    case N_LBRAC:           /* left bracket: 0,,0,nesting level,address */
        ok = stab_add_lbracket( pi, Level, pLabel, pFunction );
        break;

    case N_RBRAC:           /* right bracket: 0,,0,nesting level,address */
        ok = stab_add_rbracket( pi, Level, pLabel, pFunction );
        break;

    default:
        fprintf(stderr, "\nUnknown .stabn TypeCode = 0x%x", TypeCode );
        ok = False;
    }
    return( ok );
}

/****************************************************************************************/
int stab_add_lineno(  struct prog_info *pi, int LineNumber, char *pLabel, char *pFunction ){

    int Address;
    struct lineno *pln;
    struct syment *pEntry;
    union auxent *pAux;

    /* Allocate LineNumber Table entry and fill it in */
    pln = (struct lineno *)AllocateListObject(&ci->ListOfLineNumbers, sizeof(struct lineno)  );
    if ( !pln ) {
        fprintf(stderr, "\nOut of memory allocating lineno table for function %s", pFunction );
        return( False );
    }
    /* set value field to be address of label in bytes */
    if ( !get_symbol(pi, pLabel, &Address) ) {
        fprintf(stderr, "\nUnable to locate label %s", pLabel );
        return( False );
    }
    pln->l_addr.l_paddr = Address * 2; /* need byte quanities */

    /* Line number is relative to beginning of function, starts at 1 */
    if ( ci->FunctionStartLine == 0 ) {
        /* This line number is that same as the function start */
        ci->FunctionStartLine = LineNumber;
    }
    pln->l_lnno = LineNumber - ci->FunctionStartLine + 1;
    ci->CurrentSourceLine = LineNumber; /* keep track of source line for .eb .ef arrays */
    if ( ci->NeedLineNumberFixup ) {
        /* need to go into symbol table and fix last NeedLineNumberFixup entries */
        for ( pEntry = (struct syment *)FindLastListObject(&ci->ListOfSymbols);
            (pEntry != 0) && ( ci->NeedLineNumberFixup != 0);
            pEntry = (struct syment *)FindNextLastListObject(&ci->ListOfSymbols) ) {

            /* Fix up line number entries */
            if ( (pEntry->n_sclass == C_FCN ) || ( pEntry->n_sclass == C_BLOCK ) || ( pEntry->n_sclass == C_EXT) ) {
                pEntry++;
                pAux = (union auxent *)pEntry;
                pAux->x_sym.x_misc.x_lnsz.x_lnno = LineNumber;
                ci->NeedLineNumberFixup--; 
            }
        }
    }

    return(True);
}
/****************************************************************************************/

int stab_add_lbracket( struct prog_info *pi, int Level, char *pLabel, char *pFunction ){

    int Address;
    struct syment *pEntry;
    union auxent *pAux;
    //char *p;
    //struct lineno *pln;

    if ( !get_symbol(pi, pLabel, &Address) ) {
        fprintf(stderr, "\nUnable to locate label %s", pLabel );
        return( False );
    }

    /* Now create a .bb symbol table entry and aux entry too */
    pEntry = (struct syment *)AllocateTwoListObjects( &ci->ListOfSymbols,  sizeof(struct syment) * 2 );
    if ( !pEntry ) {
        fprintf(stderr, "\nOut of memory allocating symbol table entry for .bb %s", pLabel );
        return( False );
    }
    /* n_name */
    memset( pEntry->n_name, 0, 8 );
    strcpy( pEntry->n_name, ".bb" );

    pEntry->n_value = Address * 2;  /* bytes not words */
    pEntry->n_scnum = 1;    /* .text */
    pEntry->n_type = 0;
    pEntry->n_sclass = C_BLOCK;
    pEntry->n_numaux = 1;
    pEntry++;   /* point to aux entry */
    pAux = (union auxent *)pEntry; 
    pAux->x_sym.x_misc.x_lnsz.x_lnno = 0;   /* UNKNOWN - post process */
    pAux->x_sym.x_misc.x_lnsz.x_size = 0;   /* UNKNOWN - post process */
    ci->NeedLineNumberFixup++; /* once for .bb block */
    return(True);
}

/****************************************************************************************/
int stab_add_rbracket( struct prog_info *pi, int Level, char *pLabel, char *pFunction ){

    int Address;
    struct syment *pEntry;
    union auxent *pAux;
    //char *p;
    //struct lineno *pln;

    if ( !get_symbol(pi, pLabel, &Address) ) {
        fprintf(stderr, "\nUnable to locate label %s", pLabel );
        return( False );
    }

    /* Now create a .eb symbol table entry */
    pEntry = (struct syment *)AllocateTwoListObjects( &ci->ListOfSymbols,  sizeof(struct syment) * 2 );
    if ( !pEntry ) {
        fprintf(stderr, "\nOut of memory allocating symbol table entry for .eb %s", pLabel );
        return( False );
    }
    /* n_name */
    memset( pEntry->n_name, 0, 8 );
    strcpy( pEntry->n_name, ".eb" );
    pEntry->n_sclass = C_BLOCK;
    pEntry->n_value = Address * 2;  /* bytes not words */
    pEntry->n_scnum = 1;    /* .text */
    pEntry->n_type = 0;
    pEntry->n_numaux = 1;
    pEntry++;   /* point to aux entry */
    pAux = (union auxent *)pEntry; 
    pAux->x_sym.x_misc.x_lnsz.x_lnno = ci->CurrentSourceLine;

    /* create an .ef if at level 0 */
    if ( Level == 0 ) {

        /* Now create a .ef symbol table entry */
        pEntry = (struct syment *)AllocateTwoListObjects( &ci->ListOfSymbols,  sizeof(struct syment) * 2 );
        if ( !pEntry ) {
            fprintf(stderr, "\nOut of memory allocating symbol table entry for .ef %s", pLabel );
            return( False );
        }
        /* n_name */
        memset( pEntry->n_name, 0, 8 );
        strcpy( pEntry->n_name, ".ef" );
        pEntry->n_sclass = C_FCN;
        pEntry->n_value = Address * 2;  /* bytes not words */
        pEntry->n_scnum = 1;    /* .text */
        pEntry->n_type = 0;
        pEntry->n_numaux = 1;
        pEntry++;   /* point to aux entry */
        pAux = (union auxent *)pEntry; 
        pAux->x_sym.x_misc.x_lnsz.x_lnno = ci->CurrentSourceLine;
    }
    return(True);
}

/****************************************************************************************/
int stab_add_filename( char *pName, char *pLabel ){

    int ok, n;
    struct syment *pEntry;
    union auxent *pAux;
    char *p;

    /* if( pLabel == "Ltext0" ) then beginning of .text, pName = cwd, next pName = file */

    /* if( pLabel == "Letext" ) then end of .text , pName == NULL */

    /* we only need the one not ending in Slash */
    ok = True;
    n = strlen(pName);
    if ( n > 0 ) {
        if ( ( pName[ n - 1] == '\\') || (pName[ n - 1] == '/') )
            return(True); /* ignore */
    } else
        return(True);


    /* allocate entry in symbol table list */
    pEntry = (struct syment *)AllocateTwoListObjects(
                                                    &ci->ListOfSymbols,  sizeof(struct syment) * 2 ); /* aux entry too */
    if ( !pEntry ) {
        fprintf(stderr, "\nOut of memory allocating symbol table entry for global %s", pName );
        return( False );
    }
    /* n_name */
    memset( pEntry->n_name, 0, 8 );
    strcpy( pEntry->n_name, ".file" );
    /* n_value is determined after processing done UNKNOWN - post process */
    /* The value of that symbol equals the symbol table entry index of the next .file symbol or .global */
    /* post process */
    pEntry->n_scnum = N_DEBUG;
    pEntry->n_sclass = C_FILE;
    pEntry->n_numaux = 1;
    pEntry++;   /* point to aux entry */
    pAux = (union auxent *)pEntry; 

    /* Add Label name to symbol table */
    if ( n <= FILNMLEN ) {
        /* inline filename */
        memset( pAux->x_file.x_fname, 0, FILNMLEN );
        strncpy( pAux->x_file.x_fname, pName, n ); /* might not be zero terminated */
    } else {
        pAux->x_file.x_n.x_zeroes = 0;  /* symbol name is in string table */
        pAux->x_file.x_n.x_offset = ci->ListOfStrings.TotalBytes;

        /* add to string table */
        p = (char *)AllocateListObject( &ci->ListOfStrings,  n + 1 );
        if ( !p ) {
            fprintf(stderr, "\nOut of memory allocating string table space!");
            return( False );
        }
        strcpy( p, pName );
    }
    return( ok );
}

/****************************************************************************************/
int stab_add_function( struct prog_info *pi, char *pName, char *pLabel ){

    int n, Address;
    unsigned short CoffType, Type;
    struct syment *pEntry;
    char *pType;
    struct lineno *pln;
    union auxent *pAux;
    int SymbolIndex;

    pType = get_next_token(pName, TERM_COLON ); /* pType = symbol descriptor (character after the colon) */
    Type = atoi(pType + 1);     /* skip past F, predefined variable type */
    if ( (CoffType = GetCoffType( Type )) == 0 ) {
        fprintf(stderr, "\nUnrecognized return type found for function %s = %d", pName, Type );
        return(False);
    }
    /* Get Current Symbol Index, Allocate Symbol Table entry and fill it in */
    SymbolIndex = ci->ListOfSymbols.TotalItems;
    pEntry = (struct syment *)AllocateTwoListObjects( &ci->ListOfSymbols, sizeof(struct syment) * 2 );
    if ( !pEntry ) {
        fprintf(stderr, "\nOut of memory allocating symbol table entry for function %s", pName );
        return( False );
    }
    if ( (n = AddNameToEntry( pName, pEntry )) == 0 ) {
        fprintf(stderr,"\nOut of memory adding local %s to string table", pName );
    }
    if ( !get_symbol(pi, pLabel, &Address) ) {
        fprintf(stderr, "\nUnable to locate function %s", pName );
        return( False );
    }
    pEntry->n_value = Address * 2;  /* convert words to bytes */
    pEntry->n_scnum = 2;    /* .bss */
    if ( (CoffType = GetCoffType( Type )) == 0 ) {
        fprintf(stderr, "\nUnrecognized type found for function %s = %d", pName, Type );
        return(False);
    }
    pEntry->n_type = (unsigned short)(CoffType | (DT_FCN << 4));
    pEntry->n_sclass = C_EXT;
    pEntry->n_numaux = 1;
    pEntry++;   /* point to aux entry */
    pAux = (union auxent *)pEntry; 
    pAux->x_sym.x_tagndx = SymbolIndex + 1; /* point to the .bf entry index */
    // wrong!
    //	pAux->x_sym.x_misc.x_lnsz.x_lnno = ci->ListOfLineNumbers.TotalBytes; /* Relative Fixup point to where line numbers start */
    //	pAux->x_sym.x_misc.x_lnsz.x_size = 0; /* UNKNOWN till next function called */
    pAux->x_sym.x_misc.x_fsize = 0; /* unknown till end */
    pAux->x_sym.x_fcnary.x_fcn.x_lnnoptr = ci->ListOfLineNumbers.TotalBytes; /* relative offset to line number entry */
    pAux->x_sym.x_fcnary.x_fcn.x_endndx = 0; /* index to next entry */

    /* Now add function entry into the line number table */
    /* Allocate Symbol Table entry and fill it in */
    pln = (struct lineno *)AllocateListObject(&ci->ListOfLineNumbers, sizeof(struct lineno)  );
    if ( !pln ) {
        fprintf(stderr, "\nOut of memory allocating lineno table for function %s", pName );
        return( False );
    }
    pln->l_lnno = 0;
    pln->l_addr.l_symndx = SymbolIndex;

    /* Initialize the FunctionStartLine from the beginning of the function */
    ci->FunctionStartLine = 0;

    /* Allocate Symbol Table entry and fill it in */
    pEntry = (struct syment *)AllocateTwoListObjects( &ci->ListOfSymbols, sizeof(struct syment) * 2 );
    if ( !pEntry ) {
        fprintf(stderr, "\nOut of memory allocating symbol table entry .bf for function %s", pName );
        return( False );
    }
    memset( pEntry->n_name, 0, 8 );
    strcpy( pEntry->n_name, ".bf" );
    pEntry->n_value = Address * 2;  /* bytes not words */
    pEntry->n_scnum = 1;    /* .text */
    pEntry->n_type = 0;
    pEntry->n_sclass = C_FCN;
    pEntry->n_numaux = 1;
    pEntry++;   /* point to aux entry */
    pAux = (union auxent *)pEntry; 
    pAux->x_sym.x_misc.x_lnsz.x_lnno = 0;   /* UNKNOWN - post process */
    pAux->x_sym.x_misc.x_lnsz.x_size = 0;   /* UNKNOWN - post process */

    ci->NeedLineNumberFixup++; /* once for function C_EXT symbol */
    ci->NeedLineNumberFixup++; /* once for .bf block */
    return( True );
}
/****************************************************************************************/

int stab_add_global( struct prog_info *pi, char *pName, char *pType ){

    int n, Address, IsArray, SymbolIndex;
    unsigned short CoffType, Type;
    struct syment *pEntry;
    char *p;
    STABCOFFMAP *pMap;


    n = strlen( pName );    /* see if it's 8 bytes or less */
    Type = atoi(pType + 1);     /* skip past G, predefined variable type */
    if ( (CoffType = GetCoffType( Type )) == 0 ) {
        fprintf(stderr, "\nUnrecognized type found for global %s = %d", pName, Type );
        return(False);
    }
    pMap = (STABCOFFMAP *)GetCurrentListObject( &ci->ListOfTypes );

    SymbolIndex = ci->ListOfSymbols.TotalItems;
    /* Allocate Symbol Table entry and fill it in, Auxiliary table if its an array */
    if ( IsTypeArray( CoffType ) == True ) {
        IsArray = True;
        pEntry = (struct syment *)AllocateTwoListObjects( &ci->ListOfGlobals, sizeof(struct syment) * 2 );
    } else {
        IsArray = False;
        pEntry = (struct syment *)AllocateListObject( &ci->ListOfGlobals, sizeof(struct syment) );
    }
    if ( (n = AddNameToEntry( pName, pEntry )) == 0 ) {
        fprintf(stderr,"\nOut of memory adding local %s to string table", pName );
    }
    /* set value field to be address of label in bytes */
    /* add underscore to lookup label */
    if ( (p = calloc( 1, n + 2)) == 0 ) {
        fprintf(stderr,"\nOut of memory adding global %s", pName );
        return(False);
    }
    *p = '_';
    strcpy( p + 1, pName ); 
    if ( !get_symbol(pi, p, &Address) ) {
        fprintf(stderr, "\nUnable to locate global %s", p );
        free( p );
        return( False );
    }
    free( p );
    pEntry->n_value = Address;  /* already in bytes */
    if ( ci->GlobalStartAddress == -1 ) {
        ci->GlobalStartAddress = Address;
    }
    if ( Address < ci->GlobalStartAddress )
        ci->GlobalStartAddress = Address;
    if ( Address > ci->GlobalEndAddress )
        ci->GlobalEndAddress = Address;

    pEntry->n_scnum = 2;    /* .bss */
    pEntry->n_type = CoffType;
    pEntry->n_sclass = C_STAT;
    if ( IsArray == False )
        pEntry->n_numaux = 0;
    else {
        pEntry->n_numaux = 1;
        pEntry++;
        AddArrayAuxInfo( (union auxent *)pEntry, (unsigned short)SymbolIndex, pMap );
    }
    return( True );
}

/****************************************************************************************/
int stab_add_local(  struct prog_info *pi, char *pName, char *pType, char *pOffset  ){

    int n, Offset, IsArray;
    unsigned short CoffType, Type, SymbolIndex;
    struct syment *pEntry;
    STABCOFFMAP *pMap;

    n = strlen( pName );    /* see if it's 8 bytes or less */
    Type = atoi(pType);     /* predefined variable type */
    Offset = atoi(pOffset); /* offset in stack frame */
    if ( (CoffType = GetCoffType( Type )) == 0 ) {
        fprintf(stderr, "\nUnrecognized type found for local %s = %d", pName, Type );
        return(False);
    }
    pMap = (STABCOFFMAP *)GetCurrentListObject( &ci->ListOfTypes );
    SymbolIndex = ci->ListOfSymbols.TotalItems;
    /* Allocate Symbol Table entry and fill it in, Auxiliary table if its an array */
    if ( IsTypeArray( CoffType ) == True ) {
        IsArray = True;
        pEntry = (struct syment *)AllocateTwoListObjects( &ci->ListOfGlobals, sizeof(struct syment) * 2 );
    } else {
        IsArray = False;
        pEntry = (struct syment *)AllocateListObject( &ci->ListOfSymbols, sizeof(struct syment) );
    }
    if ( (n = AddNameToEntry( pName, pEntry )) == 0 ) {
        fprintf(stderr,"\nOut of memory adding local %s to string table", pName );
    }
    pEntry->n_type = CoffType;
    pEntry->n_sclass = C_AUTO;
    pEntry->n_scnum = N_ABS;
    pEntry->n_value = Offset + 1; /* Silly avr studio is set in its ways */
    if ( IsArray == False )
        pEntry->n_numaux = 0;
    else {
        pEntry->n_numaux = 1;
        pEntry++;
        AddArrayAuxInfo( (union auxent *)pEntry, SymbolIndex, pMap );
    }
    return( True );
}

/****************************************************************************************/
int stab_add_parameter_symbol( struct prog_info *pi, char *pName, char *pType, char *pOffset  ){

    int n, Offset;
    unsigned short CoffType, Type;
    struct syment *pEntry;

    n = strlen( pName );    /* see if it's 8 bytes or less */
    Type = atoi(pType);     /* predefined variable type */
    Offset = atoi(pOffset); /* offset in stack frame */
    if ( (CoffType = GetCoffType( Type )) == 0 ) {
        fprintf(stderr, "\nUnrecognized type found for %s = %d", pName, Type );
        return(False);
    }
    /* Allocate Symbol Table entry and fill it in */
    pEntry = (struct syment *)AllocateListObject( &ci->ListOfSymbols, sizeof(struct syment) );
    if ( (n = AddNameToEntry( pName, pEntry )) == 0 ) {
        fprintf(stderr,"\nOut of memory adding local %s to string table", pName );
    }
    pEntry->n_type = CoffType;
    pEntry->n_sclass = C_ARG;
    pEntry->n_scnum = N_ABS;
    pEntry->n_value = Offset;
    pEntry->n_numaux = 0;
    return( True );
}
/****************************************************************************************/
int stab_add_static_symbol(  struct prog_info *pi, char *pName, char *pType, char *pLabel  ){

    int n, Address;
    unsigned short CoffType, Type;
    struct syment *pEntry;

    n = strlen( pName );    /* see if it's 8 bytes or less */
    Type = atoi(pType + 1);     /* skip past S, predefined variable type */
    if ( (CoffType = GetCoffType( Type )) == 0 ) {
        fprintf(stderr, "\nUnrecognized type found for %s = %d", pName, Type );
        return(False);
    }
    /* Allocate Symbol Table entry and fill it in */
    pEntry = (struct syment *)AllocateListObject( &ci->ListOfSymbols, sizeof(struct syment) );
    if ( (n = AddNameToEntry( pName, pEntry )) == 0 ) {
        fprintf(stderr,"\nOut of memory adding local %s to string table", pName );
    }
    pEntry->n_type = CoffType;
    pEntry->n_sclass = C_STAT;
    pEntry->n_scnum = N_ABS;
    if ( !get_symbol(pi, pLabel, &Address) ) {
        fprintf(stderr, "\nUnable to locate label %s", pLabel );
        return( False );
    }
    pEntry->n_value = Address * 2;  /* find address of variable in bytes */
    pEntry->n_numaux = 0;
    return( True );
}
/****************************************************************************************/

int stab_add_local_register(  struct prog_info *pi, char *pName, char *pType, char *pRegister  ){

    int n, Register, Size;
    unsigned short CoffType, Type;
    struct syment *pEntry;

    n = strlen( pName );    /* see if it's 8 bytes or less */
    Type = (unsigned short)atoi(pType + 1);     /* skip past P, predefined variable type */
    Register = atoi(pRegister); /* offset in stack frame */
    if ( (CoffType = GetCoffType( Type )) == 0 ) {
        fprintf(stderr, "\nUnrecognized type found for %s = %d", pName, Type );
        return(False);
    }
    Size = GetCoffTypeSize( Type ); /* Silly requirement for avr studio */
    /* Allocate Symbol Table entry and fill it in */
    pEntry = (struct syment *)AllocateListObject( &ci->ListOfSymbols, sizeof(struct syment) );
    if ( (n = AddNameToEntry( pName, pEntry )) == 0 ) {
        fprintf(stderr,"\nOut of memory adding local %s to string table", pName );
        return(False);
    }
    pEntry->n_type = CoffType;
    //	if( (*pType == 'r') || (*pType == 'R') )
    //		pEntry->n_sclass = C_REG;
    //	else if( (*pType == 'p') || (*pType == 'P') )
    pEntry->n_sclass = C_REGPARM;   /* Silly avr studio only accepts this for registers */
    //	else{
    //	    fprintf(stderr,"\nUnknown register type -> %s", pType );
    //		return(False);
    //	}
    pEntry->n_scnum = N_ABS;
    pEntry->n_numaux = 0;
    if ( Size == 1 )
        pEntry->n_value = 0xffffff00 | Register; /* Silly requirement for avr studio */
    else if ( Size == 2 )
        pEntry->n_value = 0xffff0000 | ((Register + 1) << 8) | Register; /* Silly requirement for avr studio */
    else if ( Size == 4 )
        pEntry->n_value = ((Register + 3) << 24) | ((Register + 3) << 16) | ((Register + 1) << 8) | Register; /* Silly requirement for avr studio */
    else {
        fprintf(stderr,"\nUnknown register size (%d) and coff type (%d)", Size, CoffType );
        return(False);
    }
    return( True );
}

/****************************************************************************************/

int stab_add_local_type( char *pName, char *pType ){

    char *p;
    unsigned short StabType;

    /* .stabs "int:t1=r1;-128;127;",128,0,0,0 */
    /* .stabs ":t20=ar1;0;1;21=ar1;0;1;2",128,0,0,0 */
    /* pType-----^                                   */
    /* Stab Type - convert to Coff type at end (after inline assignments */
    if ( GetStabType( pType, &StabType, &p ) != True ) {
        fprintf(stderr,"\nInvalid tag type found in structure item -> %s", p);
        return(False);      
    }

    return(True);
}

/****************************************************************************************/

int GetStructUnionTagItem( char *p, char **pEnd, char **pName, unsigned short *pType, unsigned short *pBitOffset, unsigned short *pBitSize) {

    unsigned short StabType;
    /* Structure or Union Tag Item consists of -> name:type,bitoffset,bitsize; */

    /* name */
    *pName = p;
    while ( *p && (*p != ':') ) p++; // locate colon
    if ( *p != ':' ) {
      fprintf(stderr,"\nNo colon found in structure item -> %s", *pName);
      return(False);      
    }
    *p++ = 0; // Asciiz
 
    /* Stab Type - convert to Coff type at end (after inline assignments */
    if ( GetStabType( p, &StabType, &p ) != True ) {
        fprintf(stderr,"\nInvalid tag type found in structure item -> %s", p);
        return(False);      
    }

    /* BitSize */
    if ( *p != ',' ) {
        fprintf(stderr,"\nNo Bit size found in structure item -> %s", p );
        return(False);      
    }
    *pBitOffset = (unsigned short)atoi( ++p );
    while ( *p && (*p >= '0') && (*p <= '9') ) p++; // locate end of digits

    /* BitOffset */
    if ( *p != ',' ) {
        fprintf(stderr,"\nNo Bit offset found in structure item -> %s", p );
        return(False);      
    }
    *pBitSize =  (unsigned short)atoi( ++p );
    while ( *p && (*p >= '0') && (*p <= '9') ) p++; // locate end of digits

    /* Now convert stab type to COFF */
    if ( (*pType = GetCoffType( (unsigned short)StabType)) == 0 ) {
        fprintf(stderr,"\nNo COFF type found for stab type %d", StabType );
        return(   False);          
    }
    if ( *++p == ';' ) /* Now eat last semicolon(s) */
        p++;
    *pEnd = p;

    return( True );
}
/****************************************************************************************/

int GetEnumTagItem( char *p, char **pEnd, char **pEnumName, int *pEnumValue ) {

    /* Enum Tag Item consists of -> member1:value,member2:value2,; */
    *pEnumName = p;
    while ( *p && (*p != ':') ) p++; // locate colon
    if ( *p != ':' ) {
      fprintf(stderr,"\nNo colon found in enum item -> %s", *pEnumName);
      return(False);      
    }
    *p++ = 0; // Asciiz
    *pEnumValue = atoi(p);

    while ( *p && (*p >= '0') && (*p <= '9') ) p++; // locate end of digits
    if ( *p != ',' ) {
        fprintf(stderr,"\nNo comma found after enum value -> %s", p );
        return(False);      
    }
    if ( *++p ==';' )
        p++; /* eat last semicolon */
    *pEnd = p;
    return( True );
}

/****************************************************************************************/
int GetArrayType( char *p, char **pEnd, STABCOFFMAP *pMap, unsigned short *DerivedBits, int ExtraLevels ){

    int MinIndex, MaxIndex, Result, Size, i;
    char *pMinIndex, *pMaxIndex, *pType;
    unsigned short Type;

    Result = True;

    pMinIndex = pMaxIndex = pType = 0;    
    while ( *p && (*p != ';') ) p++;   /* find min index */
    pMinIndex = ++p;
    while ( *p && (*p != ';') ) p++;   /* find max index */
    pMaxIndex = ++p;
    while ( *p && (*p != ';') ) p++;   /* find type index */
    pType = ++p;

    /* bump the pointers to the digits */
    if ( !isdigit(*pMinIndex) )
        Result = False;
    if ( !isdigit(*pMaxIndex) )
        Result = False;
    if ( !isdigit(*pType) )
        Result = False;
    /* Is syntax ok ? */
    if ( Result != True ) {
        fprintf(stderr,"\nSyntax error on array parameters %s%s%s", pMinIndex, pMaxIndex, pType );
        return(False);
    }
    MinIndex = atoi(pMinIndex);
    MaxIndex = atoi(pMaxIndex);

    if ( GetStabType( p, &Type, &p ) != True )
        return(False);

    if ( !SetupDefinedType( Type, pMap, DerivedBits, ExtraLevels ) )
        return( False );

    /* Now update the size based on the indicies */
    Size = (MaxIndex - MinIndex) + 1;
    pMap->ByteSize *= Size;
    pMap->Line = ci->CurrentSourceLine;
    /* add the dimension information */
    for ( i = 5; i >= 0; i-- ) {
        if ( pMap->Dimensions[i] != 0 ) {
            i++;
            pMap->Dimensions[i] = Size;
            break;
        }
    }

    *pEnd = p;
    return(True);
}

/****************************************************************************************/
int GetStabType( char *p, unsigned short *pType, char **pEnd ) {

    STABCOFFMAP *pMap;
    int extra, ok;
    unsigned short derivedbits[6];
    unsigned short LStabType, RStabType;
    char *pHigh, *pLow;


    LStabType = atoi( p );
    while ( *p && (*p >= '0') && (*p <= '9') ) p++; // locate end of digits

    *pType = LStabType;

    if ( GetCoffType( LStabType ) != 0 ) {
        *pEnd = p;
        return(True);
    }
    if ( *p != '=' ) {
        fprintf(stderr, "\nSyntax error in type assignment -> %s", p );
        return(False);
    }
    p++;

    /* Allocate space for new internal type */
    if ( !(pMap = (STABCOFFMAP *)AllocateListObject(&ci->ListOfTypes, sizeof(STABCOFFMAP)) ) ) {
        fprintf(stderr, "\nOut of memory allocating type info!");
        return(False);
    }
    pMap->StabType = LStabType;

    /* process items to right of equals */
    for ( extra = 0; extra < 6; extra++ ) {

        if ( isdigit( *p ) ) {
            /* Finally found base type, try to terminate loop */
            GetStabType( p, &RStabType, &p );
            //			RStabType = atoi( p );
            while ( *p && (*p >= '0') && (*p <= '9') ) p++; // locate end of digits
            if ( SetupDefinedType( RStabType, pMap, &derivedbits[0], extra ) != True )
                return( False );
            break;
        } else if ( *p == 'a' ) {
            derivedbits[extra] = DT_ARY;
            p++;
            /* Calculate size */
            /* Since type assignment will be made we need to set extra bits here */
            extra++;
            /* =ar1;MinIndex;MaxIndex;BaseType */
            if ( GetArrayType( p, &p, pMap, &derivedbits[0], extra  ) != True )
                return(False);
            break;

        } else if ( *p == 'f' ) {
            derivedbits[extra] = DT_FCN;
            p++;
        } else if ( *p == '*' ) {
            derivedbits[extra] = DT_PTR;
            p++;
        } else if ( *p == 'r' ) {
            //			if( LStabType < 15 )
            //				ok = GetInternalType( pString, pMap ); /* internal types not yet installed */
            //			else
            while ( *p && (*p != ';' ) ) p++;
            pLow = p++;
            while ( *p && (*p != ';' ) ) p++;
            pHigh = p++;
            ok = GetSubRangeType( LStabType, pMap, pLow, pHigh );
            if ( ok != True )
                return(False);
            while ( *p && (*p != ';' ) ) p++; /* find end of range */
            p++;
            break;
        } else {
            fprintf(stderr, "\nUnrecognized Type modifier %c!", *p );
            return(False);
        }
    }
    *pEnd = p; /* Update return pointer */

    return(True);
}


/****************************************************************************************/
int stab_add_tag_type( char *pName, char *pString ){

    int SymbolIndex, StabType, TotalSize, n, EnumValue;
    unsigned short TagType, ItemType, BitOffset, BitSize;
    char *p;
    struct syment* pEntry;
    union auxent *pAux;
    STABCOFFMAP *pMap;

    /* We arrived here due to :T defining either a structure, union or enumeration */
    /* store the basic type as for internals and emit coff structures for debugging */
    /* .stabs "stag:T17=s2i:1,0,8;c:2,8,8;;",128,0,0,0 */
    /* .stabs "2:T18=u2a:2,0,8;b:1,0,8;c:6,0,16;;",128,0,0,0 */
    /* .stabs "1:T19=eenum1:1,enum2:2,enum3:3,;",128,0,0,0 */
    /* we don't care about the name */


    /* check for bogus errors */
    if ( !pName || !pString ) {
        fprintf(stderr,"\nInvalid .stabs type format - no information!");
        return(False);
    }

    p = pString;
    /* Stab Type - convert to Coff type at end (after inline assignments */
    if ( (StabType = (unsigned short)atoi(p)) == 0 ) {
        fprintf(stderr,"\nInvalid .stabs type format - no information! - > %s", p );
        return(False);
    }
    while ( *p && (*p >= '0') && (*p <= '9') ) p++; // locate end of digits
    if ( *p != '=' ) {
        fprintf(stderr,"\nInvalid .stabs type format - no equals - > %s", p );
        return(False);
    }
    SymbolIndex = ci->ListOfSymbols.TotalItems;
    if (  ( pEntry = (struct syment*)AllocateTwoListObjects( &ci->ListOfGlobals, sizeof(struct syment) * 2 ) ) == 0 ) {
        fprintf(stderr, "\nOut of memory allocating symbol tag entries");
        return(False);
    }
    /* Prepare Tag Header */
    if ( (n = AddNameToEntry( pName, pEntry )) == 0 ) {
        fprintf(stderr,"\nOut of memory adding local %s to string table", pString );
        return(False);
    }
    if ( !(pMap = (STABCOFFMAP *)AllocateListObject(&ci->ListOfTypes, sizeof(STABCOFFMAP)) ) ) {
        fprintf(stderr, "\nOut of memory allocating type info!");
        return(False);
    }
    pMap->StabType = StabType;
    pEntry->n_value = 0;
    pEntry->n_scnum = N_DEBUG;
    pEntry->n_numaux = 1;
    if ( *++p == 's' ) {
        TagType = pEntry->n_type = pMap->CoffType = T_STRUCT;
        pEntry->n_sclass = C_STRTAG;
        TotalSize = (unsigned short)atoi(++p);
    } else if ( *p == 'u' ) {
        TagType = pEntry->n_type = pMap->CoffType = T_UNION;
        pEntry->n_sclass = C_UNTAG;
        TotalSize = (unsigned short)atoi(++p);
    } else if ( *p == 'e' ) {
        TagType = pEntry->n_type = pMap->CoffType = T_ENUM;
        pEntry->n_sclass = C_ENTAG;
        TotalSize = FundamentalTypes[T_INT].Size; /* use size of int for enums */
    } else {
        fprintf(stderr,"\nUnknown tag type -> %s", p );
        return(False);
    }
    while ( *p && (*p >= '0') && (*p <= '9') ) p++; // locate end of digits
    pEntry++;   /* point to aux entry */
    pAux = (union auxent *)pEntry; 
    pAux->x_sym.x_tagndx = SymbolIndex; 
    pAux->x_sym.x_misc.x_lnsz.x_size = TotalSize;

    /* update our local knowledge of tag type */
    pMap->CoffType = TagType;
    pMap->ByteSize = TotalSize;
    pMap->Line = ci->CurrentSourceLine;

    /* Process the items until the end of the line */
    while ( *pName ) {

        if (  ( pEntry = (struct syment*)AllocateTwoListObjects( &ci->ListOfGlobals, sizeof(struct syment) * 2 ) ) == 0 ) {
            fprintf(stderr, "\nOut of memory allocating symbol tag member entries");
            return(False);
        }

        if ( TagType == T_STRUCT ) {
            if ( GetStructUnionTagItem( p, &p, &pName, &ItemType, &BitOffset, &BitSize) != True ) {
                return(False);
            }
            pEntry->n_value = BitOffset/8;
            pEntry->n_type = ItemType;
            pEntry->n_sclass = C_MOS;
        } else if ( TagType == T_UNION ) {
            if ( GetStructUnionTagItem(  p, &p, &pName, &ItemType, &BitOffset, &BitSize) != True ) {
                return(False);
            }
            pEntry->n_value = BitOffset/8;
            pEntry->n_type = ItemType;
            pEntry->n_sclass = C_MOU;
        } else { /* T_ENUM */
            if ( GetEnumTagItem( p, &p, &pName, &EnumValue ) != True ) {
                return(False);
            }
            pEntry->n_value = EnumValue;
            pEntry->n_type = TotalSize;
            pEntry->n_sclass = C_MOE;
        }

        /* Prepare Common Tag Header items */
        if ( (n = AddNameToEntry( pName, pEntry )) == 0 ) {
            fprintf(stderr,"\nOut of memory adding local %s to string table", pString );
            return(False);
        }
        pEntry->n_scnum = N_ABS;
        pEntry->n_numaux = 1;
        pEntry++;   /* point to aux entry */
        pAux = (union auxent *)pEntry; 
        pAux->x_sym.x_tagndx = SymbolIndex; 
        pAux->x_sym.x_misc.x_lnsz.x_size = TotalSize;
        pName = p;
    }

    /* End of Structures/Unions/Enumberations */
    if (  ( pEntry = (struct syment*)AllocateTwoListObjects( &ci->ListOfGlobals, sizeof(struct syment) * 2 ) ) == 0 ) {
        fprintf(stderr, "\nOut of memory allocating special headers for structure!");
        return(False);
    }
    strcpy( pEntry->n_name, ".eos" );
    pEntry->n_value = TotalSize;
    pEntry->n_scnum = N_ABS;
    pEntry->n_type = 0;
    pEntry->n_sclass = C_EOS;
    pEntry->n_numaux = 1;
    pEntry++;   /* point to aux entry */
    pAux = (union auxent *)pEntry; 
    pAux->x_sym.x_tagndx = SymbolIndex; /* point to the .bf entry index */
    pAux->x_sym.x_misc.x_lnsz.x_size = TotalSize;

    return(True);
}

/****************************************************************************************/
int SetupDefinedType( unsigned short Type, STABCOFFMAP *pMap, unsigned short *DerivedBits, int ExtraLevels ){

    int i, Dlimit, Dstart;
    unsigned short StabType;

    StabType = pMap->StabType; /* save the new type we found earlier */
    if ( CopyStabCoffMap( Type, pMap ) != True ) {
        fprintf(stderr, "\nCould not find defined type %d", Type );
        return(False);
    }
    pMap->StabType = StabType; /* save the new type we found earlier */

    /* Determine existing derived types for base class */
    for ( i = 0; i < 6; i++ ) {
        if ( (pMap->CoffType & ( 3 << (4 + i + i))) == 0 )
            break;
    }
    Dstart = i;
    Dlimit = i + ExtraLevels;
    if ( (Dlimit) >= 6 ) {
        fprintf(stderr, "\nStab Type %d has too many derived (%d) types!", pMap->StabType, Dlimit );
        return(False);
    }
    /* Add the new derived levels */
    for ( ; i < Dlimit; i++ ) {
        pMap->CoffType |= ( ( DerivedBits[i - Dstart] & 3) << (4 + i + i) ); /* add in the derived bits */
    }
    return(True);
}

/****************************************************************************************/
int GetArrayDefinitions( STABCOFFMAP *pMap , char *pMinIndex, char *pMaxIndex, char *pType, unsigned short *DerivedBits, int ExtraLevels ){

    int MinIndex, MaxIndex, Result, Size, i;
    unsigned short Type;

    Result = True;
    if ( (*pMinIndex != ';') || (*pMaxIndex != ';') || (*pType != ';') )
        Result = False;
    /* bump the pointers to the digits */
    pMinIndex++;
    if ( !isdigit(*pMinIndex) )
        Result = False;
    pMaxIndex++;
    if ( !isdigit(*pMaxIndex) )
        Result = False;
    pType++;
    if ( !isdigit(*pType) )
        Result = False;
    /* Is syntax ok ? */
    if ( Result != True ) {
        fprintf(stderr,"\nSyntax error on array parameters %s%s%s", pMinIndex, pMaxIndex, pType );
        return(False);
    }
    MinIndex = atoi(pMinIndex);
    MaxIndex = atoi(pMaxIndex);
    Type = (unsigned short)atoi(pType);
    if (    SetupDefinedType(    Type,    pMap,    DerivedBits,    ExtraLevels    )    !=    True    )
        return( False );
    /* Now update the size based on the indicies */
    Size = (MaxIndex - MinIndex) + 1;
    pMap->ByteSize *= Size;
    pMap->Line = ci->CurrentSourceLine;
    /* add the dimension information */
    for ( i = 5; i >= 0; i-- ) {
        if ( pMap->Dimensions[i] != 0 ) {
            i++;
            pMap->Dimensions[i] = Size;
            break;
        }
    }
    return(True);
}

/****************************************************************************************/

int GetInternalType( char *pName, STABCOFFMAP *pMap ){

    int n, found, i;

    if ( !pName ) {
        return(False);
    }

    found = False;
    n = strlen(pName);
    /* Find out if it is a local type */
    for (i = 0; FundamentalTypes[i].pString != 0; i++) {
        if ( !strncmp(pName, FundamentalTypes[i].pString, n) ) {
            /* found an internal type */
            pMap->CoffType = FundamentalTypes[i].Type;
            pMap->ByteSize = FundamentalTypes[i].Size;
            found = True;
        }
    }
    return(found);
}

/****************************************************************************************/
int GetSubRangeType( unsigned short Type, STABCOFFMAP *pMap , char *pLow, char *pHigh ){

    int Result, i;
    long High, Low;
    unsigned long Test;

    Result = True;
    if ( (*pLow != ';') || (*pHigh != ';') || (Type <= 0) )
        Result = False;

    /* Is syntax ok ? */
    if ( Result != True ) {
        fprintf(stderr,"\nSyntax error on sub range parameters!" );
        return(False);
    }
    Low = atol(++pLow);
    High = atol(++pHigh);

    /* Special handling of type void */
    if ( (Low == 0) && (High == 0) ) {
        /* Declare type void */
        pMap->ByteSize =0;
        pMap->CoffType = T_VOID;
        pMap->Line = ci->CurrentSourceLine;
        return(True);
    }

    if ( (pMap->CoffType = GetCoffType( Type )) != 0 ) {
        pMap->ByteSize = GetCoffTypeSize( Type );
    } else {
        /* Try to base everything off integer */
        pMap->ByteSize = FundamentalTypes[T_INT].Size;
    }

    /* Now calculate the byte size */
    if ( High == 0 ) {
        pMap->ByteSize = (unsigned short)Low; /* floating point */
    } else {
        if ( Low == 0 ) {
            /* Unsigned */
            Test = (unsigned long)High;
        } else if ( Low < 0 ) {
            /* signed */
            Test = (unsigned long)High << 1;
        } else {
            if ( Low <= High )
                Test = (unsigned long)High;
            else
                Test = (unsigned long)Low;
        }
        if ( pMap->ByteSize == 0 ) {
            fprintf(stderr,"\nType Range Error 1, need previous type %d size!", pMap->CoffType );
            return(False);
        }
        for ( i = 0; i < sizeof(unsigned long); i++ ) {
            if ( !(Test & (0xff << (i * 8))) )
                break;
        }
        pMap->ByteSize = i; 
    }
    /* Now determine the best fit based on byte size, compare against IAR Compiler */
    if ( pMap->ByteSize == 1 ) {
        if ( Low < 0 )
            pMap->CoffType = T_CHAR;
        else
            pMap->CoffType = T_UCHAR;
    } else if ( pMap->ByteSize == 2 ) {
        if ( Low < 0 )
            pMap->CoffType = T_INT;
        else
            pMap->CoffType = T_UINT;
    } else if ( pMap->ByteSize == 4 ) {
        if ( Low == 0 )
            pMap->CoffType = T_FLOAT;
        if ( Low < 0 )
            pMap->CoffType = T_LONG;
        else
            pMap->CoffType = T_ULONG;
    } else {
        fprintf(stderr,"\nGetSubRangeType failure - byte size %d", pMap->ByteSize );
        return(False);
    }
    return(True);
}

/****************************************************************************************/
int CopyStabCoffMap( unsigned short StabType, STABCOFFMAP *pMap ){

    STABCOFFMAP *p;

    for ( p = FindFirstListObject( &ci->ListOfTypes ); p != 0; p = FindNextListObject( &ci->ListOfTypes) ) {
        if ( p->StabType == StabType ) {
            memcpy( pMap, p, sizeof(STABCOFFMAP) );
            return(True);
        }
    }
    return( False ); /* Nothing found */
}

/****************************************************************************************/
unsigned short GetCoffType( unsigned short StabType ){

    STABCOFFMAP *p;

    for ( p = FindFirstListObject( &ci->ListOfTypes ); p != 0; p = FindNextListObject( &ci->ListOfTypes) ) {
        if ( p->StabType == StabType )
            return( p->CoffType );
    }
    return( 0 ); /* Nothing found */
}

/****************************************************************************************/
unsigned short GetCoffTypeSize( unsigned short StabType ){

    STABCOFFMAP *p;

    for ( p = FindFirstListObject( &ci->ListOfTypes ); p != 0; p = FindNextListObject( &ci->ListOfTypes) ) {
        if ( p->StabType == StabType )
            return( p->ByteSize );
    }
    return( 0 ); /* Nothing found */
}


/****************************************************************************************/
int GetDigitLength( char *p ){

    int i;

    if ( p == 0 )
        return(0);

    for ( i = 0; (*p != 0) && ( *p >= '0' ) && ( *p <= '9' ); i++ );

    return( i );

}

/****************************************************************************************/
int GetStringDelimiters( char *pString, char **pTokens, int MaxTokens ){

    int i;
    char *p;

    p = pString;

    if ( !p )
        return( 0 );

    for ( i = 0; i < MaxTokens; i++ ) {
        while ( True ) {
            if ( (*p == ':') || (*p == ';') || (*p == '=') || (*p == ',') || (*p == '"') || (*p == 0 ) ) {
                *(pTokens + i) = p; /* Remember this location */
                p++;
                if ( *p == 0 )
                    return( i );
                break;
            }
            p++;
        }
    }
    return( i );
}

/****************************************************************************************/
int IsTypeArray( unsigned short CoffType ){

    int Result;

    Result = False;

    if ( (CoffType & (DT_ARY << 4 )) == (DT_ARY << 4 ) )
        Result = True;
    if ( (CoffType & (DT_ARY << 6 )) == (DT_ARY << 6 ) )
        Result = True;
    if ( (CoffType & (DT_ARY << 8 )) == (DT_ARY << 8 ) )
        Result = True;
    if ( (CoffType & (DT_ARY << 10 )) == (DT_ARY << 10 ) )
        Result = True;
    if ( (CoffType & (DT_ARY << 12 )) == (DT_ARY << 12 ) )
        Result = True;
    if ( (CoffType & (DT_ARY << 14 )) == (DT_ARY << 14 ) )
        Result = True;

    return(Result);
}

/****************************************************************************************/
void AddArrayAuxInfo( union auxent *pAux, unsigned short SymbolIndex, STABCOFFMAP *pMap ){

    int i;

    pAux->x_sym.x_tagndx = SymbolIndex; /* point to the .bf entry index */
    pAux->x_sym.x_misc.x_lnsz.x_lnno =  pMap->Line;
    pAux->x_sym.x_misc.x_lnsz.x_size = pMap->ByteSize;
    for ( i = 0; i < 4; i++ )
        pAux->x_sym.x_fcnary.x_ary.x_dimen[i] = pMap->Dimensions[i];
}

/****************************************************************************************/
int AddNameToEntry( char *pName, struct syment *pEntry ) {

    int n;
    char *p;

    n = strlen( pName );    /* see if it's 8 bytes or less */
    if ( n <= 8 ) {
        strncpy( pEntry->n_name, pName, 8 );
    } else {
        /* point to current offset in string table */
        pEntry->n_offset = ci->ListOfStrings.TotalBytes;        
        /* Allocate string table entry */
        if ( (p = (char *)AllocateListObject( &ci->ListOfStrings, n + 1 )) == 0 ) {
            return(0);
        }
        strcpy( p, pName );
    }
    return(n); /* return size of string */
}

/****************************************************************************************/

char *SkipPastDigits( char *p ){

    if ( !p )
        return(p);
/*	if ( *p == 0 );  */  /* JEG 5-01-03 */
	if ( *p == 0 )
    return(p);   /* This line s/b indented JEG */
    for ( p--; (*p >= '0') && (*p <= '9') && (*p != 0); p-- );
    return(p);
}

/****************************************************************************************/

/****************************************************************************************/
/****************************************************************************************/
/* List management routines */
/****************************************************************************************/
/****************************************************************************************/

/****************************************************************************************/

/****************************************************************************************/
void InitializeList( LISTNODEHEAD *pHead ){

    pHead->Node.Next = &pHead->Node;
    pHead->Node.Last = &pHead->Node;
    pHead->TotalBytes = 0;
    pHead->TotalItems = 0;
    pHead->current = &pHead->Node;
    return;
}

/****************************************************************************************/

void *AllocateTwoListObjects( LISTNODEHEAD *pHead, int size ){

    void *p;

    if ( (p = AllocateListObject( pHead, size ) ) )
        pHead->TotalItems++;   /* already incremented once in addtolist */
    return( p );
}

/****************************************************************************************/
void *AllocateListObject( LISTNODEHEAD *pHead, int size ){

    void *pObject;

    LISTNODE *pNode;

    if ( (pObject = calloc( 1, size )) != 0 ) {
        if ( !(pNode = AddListObject( pHead, pObject, size )) ) {
            free( pObject );
            pObject = 0;
        }
    }
    return( pObject );
}

/****************************************************************************************/
LISTNODE  *AddListObject(LISTNODEHEAD *pHead, void *pObject, int size ){

    LISTNODE *pNode;

    if ( (pNode = calloc( 1, sizeof(LISTNODE) )) != 0 ) {
        pNode->pObject = pObject;
        pNode->Size = size;
        pNode->FileNumber = ci->CurrentFileNumber;
        AddNodeToList( pHead, pNode );
    }
    return( pNode );
}

/****************************************************************************************/
LISTNODE *AllocateListNode( void *pObject, int size ){

    LISTNODE *pNew;

    if ( (pNew = calloc( 1, sizeof( LISTNODE ) ) ) != 0 ) {
        /* Then we initialize the node */
        pNew->pObject = pObject;
        pNew->Size = size;
        pNew->FileNumber = ci->CurrentFileNumber;
    }
    return(pNew);
}

/****************************************************************************************/
void AddNodeToList( LISTNODEHEAD *pHead, LISTNODE *pNode ){

    LISTNODE *p;

    p = &pHead->Node;

    pNode->Next = p->Last->Next;
    p->Last->Next = pNode;
    pNode->Last = p->Last;
    p->Last = pNode;

    /* and update current size of data contained in the list */
    pHead->TotalBytes += pNode->Size;
    pHead->TotalItems++;
}

/****************************************************************************************/
void RemoveNodeFromList( LISTNODEHEAD *pHead, LISTNODE *pNode ){

    pNode->Last->Next = pNode->Next;
    pNode->Next->Last = pNode->Last;

    pHead->TotalBytes -= pNode->Size;
    pHead->TotalItems--;
}


/****************************************************************************************/
void *FindFirstListObject( LISTNODEHEAD *pHead ){

    if ( pHead->Node.Next == &pHead->Node )
        return(0);  /* Nothing in list */

    pHead->current = pHead->Node.Next;
    return( pHead->current->pObject );
}
/****************************************************************************************/
void *FindNextListObject( LISTNODEHEAD *pHead ){

    if ( pHead->current->Next == &pHead->Node )
        return( 0 );

    pHead->current = pHead->current->Next;

    return( pHead->current->pObject );
}
/****************************************************************************************/

LISTNODE *GetCurrentNode( LISTNODEHEAD *pHead ){

    return( pHead->current );
}

/****************************************************************************************/
void *GetCurrentListObject( LISTNODEHEAD *pHead ){

    return( pHead->current->pObject );
}


/****************************************************************************************/
void *FindLastListObject( LISTNODEHEAD *pHead ){

    if ( pHead->Node.Last == &pHead->Node )
        return(0);  /* Nothing in list */

    pHead->current = pHead->Node.Last;
    return( pHead->current->pObject );
}
/****************************************************************************************/
void *FindNextLastListObject( LISTNODEHEAD *pHead ){

    if ( pHead->current->Last == &pHead->Node )
        return( 0 );

    pHead->current = pHead->current->Last;

    return( pHead->current->pObject );
}

/****************************************************************************************/

void FreeList( LISTNODEHEAD *pHead ){

    LISTNODE *pNode;

    for ( pNode = pHead->Node.Last; pNode->Next != &pHead->Node; pNode = pHead->Node.Last ) {

        RemoveNodeFromList( pHead, pNode );
        free( pNode->pObject );
        free( pNode );
    } 
    pHead->TotalBytes = 0;
    pHead->TotalItems = 0;
    pHead->current = &pHead->Node;
}
/****************************************************************************************/


