/***********************************************************************
 *
 *  AVRA - Assembler for the Atmel AVR microcontroller series
 *
 *  Copyright (C) 1998-2020 The AVRA Authors
 *
 *  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"


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;
	FILE *fp;
	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)
{

	char *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, 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;

	/* 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 = pi->time;
	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 = 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)
{

	/* 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 %ld", address, pi->device->eeprom_size);
		}
	}
}

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

	unsigned char *pByte;

	/* 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 %ld", 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 ok = True;
	int TypeCode, n;
	char *pString, *p2, *p3, *p4, *p5, *pType, *pp, *pJoined;


	if (!GET_ARG_I(pi->args, ARG_COFF) || (pi->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);
	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 ok = True;
	int TypeCode, Level;
	char *p1, *p2, *p3, *p4, *pLabel, *pFunction;

	/* 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_I(pi->args, ARG_COFF) || (pi->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);
	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;

	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;

	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 */
	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;
	pEntry->n_sclass = C_REGPARM;   /* Silly avr studio only accepts this for registers */
	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') {
			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)
		return (p);
	for (p--; (*p >= '0') && (*p <= '9') && (*p != 0); p--);
	return (p);
}

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;
}

