/*
	maccer - simple macro pre-processor based around a state machine
	(C) 1998 Michael Hope
	michaelh@earthling.net
*/


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

#ifdef _AMIGA
#undef toupper
#define VERSION_STRING "0.21"
#endif

typedef unsigned long	ULONG;
typedef unsigned short	UWORD;

/* #define for copus quantities of debugging info */
//#define DEBUG

/* Error vals */
#define	EOK				0
#define EFAIL			-1
#define ENOTFOUND		-2
#define EFILE			-3

/* List of states */
#define RST                   0
#define SOPCODE_OR_DIRECTIVE  1
#define SOPCODE               2
#define SDIRECTIVE            3
#define SLABEL_OR_MACRO       4
#define SCOMMENT              5
#define SMACRO                6
#define SLABEL                7
#define SMACRO_WS             8
#define SMACRO_ARGS           9
#define SMACRO_ARGS_WS        10
#define SLABEL_WS             11
#define SOPCODE_ARGS          12
#define SOPCODE_ARGS_WS       13
#define SOPCODE_ARGS_SPECIAL	14
#define SOPCODE_ARGS_STRING	15
#define SOPCODE_COMMA		16
#define SOPCODE_ARGS_ENDSTRING	17
#define SOPCODE_ARGS_SPECIAL2	18
#define SERROR					19
#define SLABEL_OR_DIR		20

/* List of the textual names of the states for debugging */
char state_names[][23] =
{
    "RST", "SOPCODE_OR_DIRECTIVE", "SOPCODE", "SDIRECTIVE",
    "SLABEL_OR_MACRO", "SCOMMENT", "SMACRO", "SLABEL", 
	"SMACRO_WS", "SMACRO_ARGS", "SMACRO_ARGS_WS", "SLABEL_WS",
	"SOPCODE_ARGS", "SOPCODE_ARGS_WS", "SOPCODE_ARGS_SPECIAL", "SOPCODE_ARGS_STRING",
    "SOPCODE_COMMA", "SOPCODE_ARGS_ENDSTRING", "SOPCODE_ARGS_SPECIAL2", "SERROR",
	"SLABEL_OR_DIR"
};

/* String to prepend to the output for a given state */
char prepend[][3] =
{
    "", "", "\t", "\t",
    "", "", "", "", 
	"", " ", "", "", 
	"", "", "", "",
    "", "", "", "",
	""
};

/* String to postpend to the output */
char postpend[][3] =
{
    "", "", "\t", "\t",
    "", "", "", "", 
	"", "", "", "", 
	"", "", "", "",
    "", "", "", "", 
	""
};

/* 1 is the corresponding state should be kept, 0 to drop it */
int remotely_interesting[] =
{
    1, 0, 1, 1,
    1, 0, 1, 1, 
	0, 1, 0, 0, 
	1, 0, 1, 1,
    1, 1, 1, 0,
	1
};

/* The various character classes for state transitions */
#define TDEFAULT            0
#define TWHITESPACE         1
#define TALPHA              2
#define TCOMMENT            3
#define TDOT                4
#define TEOL                5
#define TCOLON              6
#define TCOMMA				7
#define TSPECIAL			8
#define TSPEECHMK			9
#define TNUMBER				10

typedef struct sstate mstate;
typedef struct sstate *pmstate;

/* A mstate contains the edges to the state transition table
   eg if the current state is current and the character calss is tokenclass
   then go to state next */
struct sstate {
    int current;
    int tokenclass;
    int next;
};

typedef struct stoken mtoken;
typedef struct stoken *pmtoken;

/* A token is a sequence of characters.  The list is doubly so that I can
   do evil things to it 
   Note that a list of tokens is not cicrular - the first token has previous
   set to NULL and the last has next set to NULL
*/
struct stoken {
    int state;
    char text[100];
    pmtoken next;
    pmtoken previous;
};

/* A mdefine is a token based search and replace.  If the tokens text equals
   find then the text replace is printed instead 
*/
typedef struct sdefine mdefine;
typedef struct sdefine *pmdefine;

struct sdefine {
    char find[100];
    char replace[100];
    pmdefine next;
};

/* A macro is a name and a list of tokens that should be printed instead
   of the macro token
*/
typedef struct smacro mmacro;
typedef struct smacro *pmmacro;

struct smacro {
    char name[100];
    pmtoken macro;
    pmdefine args;
    pmmacro next;
};

/* An error is a state, character class and string which describes what type of
    error has occured when your in state state and you try to follow the non-existant
    edge described by character class
*/
typedef struct serror merror;
typedef struct serror *pmerror;

struct serror {
	int	state;
	int charclass;
	char description[100];
};

/* A style defines assembler specific ways of inserting data eg how to start
   a hex number or how to write binary data */
typedef struct sstyle mstyle;
typedef struct sstyle *pmstyle;

struct sstyle {
	char	name[8];
	char	hex_prefix[8];
	char	db[8];
};

/* Style in use for this file */
pmstyle current_style;

/* Currently known styles */
static mstyle styles[] = {
	{ "asgb",	"0x",	".db" },
	{ "jas",	"$",	"dc.b" },
	{ "END",	"",		"" }		/* Terminator */
};

/* states is a list of all the state transitions
   Note that currently there's alot of duplication
   Note that a default action _must_ be last in the transitions for that state
*/
static mstate states[] =
{
    {RST,						TWHITESPACE,	SOPCODE_OR_DIRECTIVE },
    {RST,						TCOMMENT,		SCOMMENT },
    {RST,						TALPHA,			SLABEL_OR_MACRO },
    {RST,						TNUMBER,		SLABEL_OR_MACRO },
	{RST,						TDOT,			SLABEL_OR_DIR },
    {RST,						TEOL,			RST },
    {SOPCODE_OR_DIRECTIVE,		TWHITESPACE,	SOPCODE_OR_DIRECTIVE},
    {SOPCODE_OR_DIRECTIVE,		TDOT,			SDIRECTIVE},
    {SOPCODE_OR_DIRECTIVE,		TALPHA,			SOPCODE},
    {SOPCODE_OR_DIRECTIVE,		TCOMMENT,		SCOMMENT},
    {SOPCODE_OR_DIRECTIVE,		TEOL,			RST},
    {SOPCODE,					TEOL,			RST},
    {SOPCODE,					TWHITESPACE,	SOPCODE_ARGS_WS},
	{SOPCODE,					TCOMMENT,		SCOMMENT},
    {SOPCODE,					TDEFAULT,		SOPCODE},
    {SOPCODE_ARGS_WS,			TWHITESPACE,	SOPCODE_ARGS_WS},
    {SOPCODE_ARGS_WS,			TEOL,			RST},
    {SOPCODE_ARGS_WS,			TALPHA,			SOPCODE_ARGS},
    {SOPCODE_ARGS_WS,			TNUMBER,		SOPCODE_ARGS},
    {SOPCODE_ARGS_WS,			TSPEECHMK,		SOPCODE_ARGS_STRING},
    {SOPCODE_ARGS_WS,			TDOT,			SOPCODE_ARGS},
    {SOPCODE_ARGS_WS,			TSPECIAL,		SOPCODE_ARGS_SPECIAL},
    {SOPCODE_ARGS_WS,			TCOMMENT,		SCOMMENT},
    {SOPCODE_ARGS,				TEOL,			RST},
	{SOPCODE_ARGS,				TDOT,			SOPCODE_ARGS },
    {SOPCODE_ARGS,				TCOMMA,			SOPCODE_COMMA},
    {SOPCODE_ARGS,				TSPECIAL,		SOPCODE_ARGS_SPECIAL},
    {SOPCODE_ARGS,				TWHITESPACE,	SOPCODE_ARGS_WS},	/* xxx not really */
	{SOPCODE_ARGS,				TCOMMENT,		SCOMMENT },
	{SOPCODE_ARGS,				TALPHA,			SOPCODE_ARGS },
	{SOPCODE_ARGS,				TNUMBER,		SOPCODE_ARGS },
	{SOPCODE_ARGS,				TDOT,			SOPCODE_ARGS },
/*    {SOPCODE_ARGS,				TDEFAULT,		SOPCODE_ARGS},*/
    {SOPCODE_COMMA,				TWHITESPACE,	SOPCODE_ARGS_WS},
    {SOPCODE_COMMA,				TSPECIAL,		SOPCODE_ARGS_SPECIAL},
    {SOPCODE_COMMA,				TALPHA,			SOPCODE_ARGS},
    {SOPCODE_COMMA,				TNUMBER,		SOPCODE_ARGS},
    {SOPCODE_COMMA,				TSPEECHMK,		SOPCODE_ARGS_STRING},
    {SOPCODE_ARGS_SPECIAL,		TEOL,			RST},
	{SOPCODE_ARGS_SPECIAL,		TSPECIAL,		SOPCODE_ARGS_SPECIAL2 },
	{SOPCODE_ARGS_SPECIAL,		TCOMMENT,		SCOMMENT},
	{SOPCODE_ARGS_SPECIAL,		TDEFAULT,		SOPCODE_ARGS},
    {SOPCODE_ARGS_SPECIAL2,		TEOL,			RST},
	{SOPCODE_ARGS_SPECIAL2,		TCOMMENT,		SCOMMENT},
	{SOPCODE_ARGS_SPECIAL2,		TSPECIAL,		SOPCODE_ARGS_SPECIAL },
    {SOPCODE_ARGS_SPECIAL2,		TDEFAULT,		SOPCODE_ARGS},
    {SOPCODE_ARGS_STRING,		TSPEECHMK,		SOPCODE_ARGS_ENDSTRING},
    {SOPCODE_ARGS_STRING,		TEOL,			SERROR},
    {SOPCODE_ARGS_STRING,		TDEFAULT,		SOPCODE_ARGS_STRING},
    {SOPCODE_ARGS_ENDSTRING,	TCOMMA,			SOPCODE_COMMA},
    {SOPCODE_ARGS_ENDSTRING,	TEOL,			RST},
    {SOPCODE_ARGS_ENDSTRING,	TDEFAULT,		SOPCODE_ARGS},
    {SDIRECTIVE,				TEOL,			RST},
    {SDIRECTIVE,				TWHITESPACE,	SOPCODE_ARGS_WS},
	{SDIRECTIVE,				TCOMMENT,		SCOMMENT},
	{SDIRECTIVE,				TALPHA,			SDIRECTIVE},
    {SLABEL_OR_MACRO,			TWHITESPACE,	SMACRO_WS},
    {SLABEL_OR_MACRO,			TCOLON,			SLABEL},
    {SLABEL_OR_MACRO,			TALPHA,			SLABEL_OR_MACRO},
    {SLABEL_OR_MACRO,			TNUMBER,		SLABEL_OR_MACRO},
    {SLABEL_OR_MACRO,			TSPECIAL,		SLABEL_OR_MACRO},
    {SLABEL_OR_DIR,				TWHITESPACE,	SOPCODE_ARGS_WS},
    {SLABEL_OR_DIR,				TCOLON,			SLABEL},
    {SLABEL_OR_DIR,				TALPHA,			SLABEL_OR_DIR},
    {SLABEL_OR_DIR,				TNUMBER,		SLABEL_OR_DIR},
    {SLABEL_OR_DIR,				TSPECIAL,		SLABEL_OR_DIR},
    {SLABEL_OR_DIR,				TEOL,			RST},
    {SCOMMENT,					TEOL,			RST},
    {SCOMMENT,					TDEFAULT,		SCOMMENT},
    {SLABEL,					TCOLON,			SLABEL},
    {SLABEL,					TWHITESPACE,	SLABEL_WS},
	{SLABEL,					TCOMMENT,		SCOMMENT},
    {SLABEL,					TEOL,			RST},
    {SLABEL_WS,					TEOL,			RST},
    {SLABEL_WS,					TWHITESPACE,	SLABEL_WS},
    {SLABEL_WS,					TALPHA,			SOPCODE},
    {SLABEL_WS,					TDOT,			SDIRECTIVE},
    {SLABEL_WS,					TCOMMENT,		SCOMMENT},
    {SMACRO_WS,					TWHITESPACE,	SMACRO_WS},
    {SMACRO_WS,					TALPHA,			SMACRO},
	{SMACRO_WS,					TSPECIAL,		SOPCODE_ARGS },	/* CHECK */
	{SMACRO_WS,					TEOL,			RST },
    {SMACRO,					TEOL,			RST},
    {SMACRO,					TWHITESPACE,	SMACRO_ARGS_WS},
	{SMACRO,					TALPHA,			SMACRO},
	{SMACRO,					TNUMBER,		SMACRO},
    {SMACRO_ARGS_WS,			TWHITESPACE,	SMACRO_ARGS_WS},
    {SMACRO_ARGS_WS,			TALPHA,			SMACRO_ARGS},
    {SMACRO_ARGS_WS,			TNUMBER,		SMACRO_ARGS},
    {SMACRO_ARGS_WS,			TCOMMENT,		SCOMMENT},
    {SMACRO_ARGS,				TEOL,			RST},
    {SMACRO_ARGS,				TCOMMA,			SMACRO_ARGS_WS},
	{SMACRO_ARGS,				TALPHA,			SMACRO_ARGS},
	{SMACRO_ARGS,				TNUMBER,		SMACRO_ARGS},
	{-1, -1, -1}
};

merror errors[] = {
	{SOPCODE_ARGS_STRING,	TEOL,		"Unterminated string" },
	{SMACRO_ARGS,			TDEFAULT,	"Only alphanumberics are allowed in a macro argument name."},
	{SMACRO_ARGS_WS,		TDEFAULT,	"A macro argument must start with a letter or number"},
	{SMACRO,				TDEFAULT,	"Unexpected character in second field."},
	{RST,					TDEFAULT,	"Unexpected character at start of line."},
	{SOPCODE_OR_DIRECTIVE,	TDEFAULT,	"Unexpected character before start of opcode."},
	{SOPCODE_ARGS_WS,		TDEFAULT,	"Weird character present."},
	{SOPCODE_ARGS_WS,		TDEFAULT,	"Weird character present in argument."},
	{SDIRECTIVE,			TDEFAULT,	"Only letters can be used in a .directive."},
	{SLABEL_OR_MACRO,		TDEFAULT,	"Unexpected character in label."},
	{SLABEL,				TDEFAULT,	"Unexpected character following label."},
	{-1,					-1,			""}
};
	
int parse_in( pmtoken *first_token, char *file_name, FILE *fp );

int output_float( pmtoken token )
{
	ULONG mantissa;
	UWORD exponent;
	double f1, f2;
	char arg[100];

	strcpy(arg, token->text);

	if (token->text[0]=='-') {
		if (token->next) {
			strcat( arg, token->next->text );
		}
	}

	f1 = strtod( arg, (char **)NULL );
	/* Convert f1 to a gb-lib type fp
	 * 24 bit mantissa followed by 7 bit exp and 1 bit sign
	*/
	if (f1!=0) {
		f2 = floor(log(fabs(f1))/log((double)2))+1;
		mantissa = (0x1000000*fabs(f1))/exp(f2*log((double)2));
		mantissa &=0xffffff;
		exponent = f2 + 0x40;
		if (f1<0)
			exponent |=0x80;
	}
	else {
		mantissa=0;
		exponent=0;
	}
	printf("; 	.df %s\n"
		   "	%s\t%s%02X,%s%02X,%s%02X,%s%02X\n",
			arg,
		    current_style->db,
		    current_style->hex_prefix,
			(unsigned int)mantissa&0x0ff,
		    current_style->hex_prefix,
			(unsigned int)((mantissa>>8)&0x0ff),
		    current_style->hex_prefix,
			(unsigned int)((mantissa>>16)&0x0ff),
		    current_style->hex_prefix,
			exponent&0xff);
	return EOK;
}

/* Holder for something clever later */
void record_error(char *function, char *text)
{
    fprintf( stderr, "%s: %s\n", function, text);
}


void debug(char *function, char *msg)
{
#ifdef DEBUG
    fprintf(stderr, "%s: %s\n", function, msg);
#endif
}

/* Set the style to the one given in 'name' */
int set_style(char *name)
{
	pmstyle search;
	search = styles;

	while (strcmp(search->name, "END")) {
		if (!strcmp(search->name, name)) {
			current_style = search;
			return EOK;
		}
		search++;
	}
	return ENOTFOUND;
}
	
/* Return the character class of the current character */
int get_token_class(char token)
{
    if ((token == ' ') || (token == '\t'))
		return TWHITESPACE;
    if (token == ';')
		return TCOMMENT;
    if (token == '.')
		return TDOT;
    if ((token == '\n') || (token == '\0'))
		return TEOL;
    if (token == ':')
		return TCOLON;
    if (token == ',')
		return TCOMMA;
    if ((token == '\"')||(token == '\''))
		return TSPEECHMK;
    if (strchr("$#<>=+-()/*", token) != NULL)
		return TSPECIAL;
    if (isdigit(token))
		return TNUMBER;
    if (isprint(token))
		return TALPHA;
	return TDEFAULT;
}

/* Find the corresponding transition for the current character and state */
pmstate find_rule(int state, int tokenclass)
{
	int search;
    search = 0;

	while (states[search].current != -1) {
		if (states[search].current == state) {
			if ((states[search].tokenclass == tokenclass)
				|| (states[search].tokenclass == TDEFAULT)) {
				debug("find_rule", "Found a rule");
				return &states[search];
			}
		}
		search++;
    }
	debug("find_rule", "No rule found.");
	return NULL;
}

/* Create a macro from the current place in the token list
	Delete the macro definition tokens once the macro is defined
*/	
pmmacro *create_macro(pmtoken start_token, pmmacro *current_macro)
{
    pmtoken token, token_copy = NULL;		/* To pacify gcc */
    pmdefine arg = NULL;
    int first;

	
    (*current_macro) = malloc(sizeof(mmacro));
    (*current_macro)->next = NULL;

    strcpy((*current_macro)->name, start_token->text);

    /* Find the token after the next RST and copy off the args */
    token = start_token->next->next;
    first = 1;
    (*current_macro)->args = NULL;

    while ((token) && (token->state != RST)) {
		/* Add an arg to the list */
		if (first) {
			(*current_macro)->args = malloc(sizeof(mdefine));
			first = 0;
			arg = (*current_macro)->args;
		}
		else {
			arg->next = malloc(sizeof(mdefine));
			arg = arg->next;
		}
		strcpy(arg->find, token->text);
		arg->next = NULL;

		token = token->next;
	}

    if (token) {
		token = token->next;	/* Start of macro body */
		if (token) {		/* ...potentially null */
			/* Copy from here into the macro */
			first = 1;

			while ((token) && (strcmp(token->text, "ENDM"))) {
				if (first) {
					(*current_macro)->macro = malloc(sizeof(mtoken));
					first = 0;
					token_copy = (*current_macro)->macro;
					token_copy->next = NULL;
				}
				else {
					token_copy->next = malloc(sizeof(mtoken));
					token_copy = token_copy->next;
				}

				memcpy(token_copy, token, sizeof(mtoken));
				token_copy->next = NULL;

				token = token->next;
			}
			/* Strip out all the tokens that describe the macro */
			start_token->previous->next = token->next;
			return &(*current_macro)->next;
		}
		record_error("create_macro", "Improperly terminated macro.");
		return NULL;
    }
    record_error("create_macro", "Improperly terminated macro.");
    return NULL;
}

int linkcount = 0;

/* Scan the given token list and parse in any macros */
int add_macros(pmtoken current_token, pmmacro *first_macro)
{
    pmmacro *current_macro;

    current_macro = first_macro;

    while (current_token) {
		if (current_token->state == SLABEL_OR_MACRO) {
			if (current_token->next!=NULL) {
				if (current_token->next->state == SMACRO) {
					/* We have a new macro ! */
					if (strcmp(current_token->next->text, "MACRO")) {
						record_error("add_macros", "Malformed macro line");
					} 
					else {
						/* Add current_token to macro list */
						current_macro = create_macro(current_token, current_macro);
					}
				}
			}
		}
		current_token = current_token->next;
    }
	return EOK;
}

int add_includes(pmtoken first_token)
{
	pmtoken current_token;
	char filename[100];
	FILE *included;

	current_token = first_token;

    while (current_token) {
		if (current_token->state == SOPCODE) {
			if (!strcmp( current_token->text, "INCLUDE")) {
				if (current_token->next!=NULL) {
						if (current_token->next->state==SOPCODE_ARGS_ENDSTRING) {
							/* Sofar so good */
							strcpy( filename, &current_token->next->text[1] );
							filename[strlen(filename)-1]='\0';
							included = fopen( filename, "r" );
							if (included) {
								debug("add_includes", "Including file.");
								if (parse_in( &current_token->next, filename, included )) {
									record_error("add_includes", "Error parsing include file.");
									}
								else {
									/* Leave the INCLUDE in but comment it out */
									strcpy( filename, current_token->text );
									strcpy( current_token->text, "; ");
									strcat( current_token->text, filename );
								}
								fclose(included);
							}
							else {
								record_error("add_includes", "Error opening include file for reading.");
							}
						}
						else {
							record_error("add_includes", "Argument to INCLUDE is not a string.");
						}
				}
				else {
					record_error("add_includes", "No argument to INCLUDE.");
				}
			}
		}
		current_token = current_token->next;
    }
	return EOK;
}

/* Scan the current list of defines and make any required replacements 
	Return the original text if no changes were made
*/
char *replace_defines( char *text, pmdefine first_define )
{
	pmdefine scan_defines;
	
	scan_defines = first_define;
	while (scan_defines) {
	    if (!strcmp(scan_defines->find, text)) {
			/* Found it */
			return scan_defines->replace;
		}
	    scan_defines = scan_defines->next;
	}
	return text;
}

/* Create a list of defines from the macro args */
int build_macro_args(pmmacro macro, pmtoken first_token, pmdefine first_define )
{
    /* the first arg is at first_token->next.  Scan until end of line */
    pmtoken token;
    pmdefine current_define;

    current_define = macro->args;
    token = first_token->next;

    while ((token) && (token->state != RST)) {
		if ((token->state == SOPCODE_ARGS) || (token->state == SOPCODE_ARGS_ENDSTRING)) {
			strcpy(current_define->replace, replace_defines(token->text, first_define));
			current_define = current_define->next;
		}
		token = token->next;
    }
    return EOK;
}

int get_hex_byte(FILE *fp)
{
	int ret, got;

	got = toupper(fgetc(fp))-'0';
	if (got>9) {
		got-='A'-'9'-1;
	}
	if ((got>15)||(got<0)) {
		got = 0;
		record_error("get_hex_byte", "Invalid hex character found.");
	}
	ret = got;
	got = toupper(fgetc(fp))-'0';
	if (got>9) {
		got-='A'-'9'-1;
	}
	if ((got>15)||(got<0)) {
		got = 0;
		record_error("get_hex_byte", "Invalid hex character found.");
	}
	return ret<<4 | got;
}
	
int include_binary_file( pmtoken current_token )
{
	FILE *included;
	char filename[100];
	int count = 0;
	int byte;
	
	if (current_token->next) {
		current_token = current_token->next;
		if (current_token->state==SOPCODE_ARGS_ENDSTRING) {
			/* Sofar so good */
			strcpy( filename, &current_token->text[1] );
			filename[strlen(filename)-1]='\0';
			included = fopen( filename, "rb" );
			if (included) {
				while ((byte=fgetc(included))!=EOF) {
					if (count==0) {
						printf("\n\t%s\t", current_style->db);
						}
					else
						printf(", ");
					printf("%s%02X", current_style->hex_prefix, byte );
					count++;
					if (count==8)
						count = 0;
				}
				fclose( included );
			}
			else {
				record_error("include_binary_file", "Cant open include file for reading.");
			}
		}
		else
			record_error("include_binary_file", "Argument to INCBIN is not a string.");
	}
	return EFAIL;
}

int include_ihx_file( pmtoken current_token )
{
	FILE *included;
	char filename[100];
	int bytes_in_line;
	int addr = -1, next_addr, t, i;
	int done = 0;
	
	if (current_token->next) {
		current_token = current_token->next;
		if (current_token->state==SOPCODE_ARGS_ENDSTRING) {
			strcpy( filename, &current_token->text[1] );
			filename[strlen(filename)-1]='\0';
			included = fopen( filename, "rb" );
			if (included) {
				while (!done) {
					if (fgetc(included)==':') {
						/* 'Valid' ihx line :) */
						bytes_in_line = get_hex_byte(included);
						next_addr = get_hex_byte(included)<<8;
						next_addr |= get_hex_byte(included);
						if (addr!=-1) {
							if (next_addr>addr) {
								/* Pad... */
								t = 0;
								i = next_addr - addr;
								while (i>0) {
									if ((t&7)==0) {
										printf("\n\t%s\t%s00", current_style->db, current_style->hex_prefix);
									}
									else {
										printf(", %s00", current_style->hex_prefix );
									}
									t++;
									i--;
								}
							}
						}
						addr = next_addr+bytes_in_line;
						if (get_hex_byte(included)==1) {
							done = 1;				/* End of file */
						}
						else {
							if (bytes_in_line>0) {
								printf("\n\t%s\t%s%02X", current_style->db, current_style->hex_prefix, get_hex_byte(included));
								bytes_in_line--;
								while (bytes_in_line!=0) {
									printf(", %s%02X", current_style->hex_prefix, get_hex_byte(included));
									bytes_in_line--;
								}
							}
							/* Read until end of line */
							i = fgetc(included);
							while ((i!='\n')&&(i!=EOF))
								i = fgetc(included);
							if (i==EOF)
								done = 1;
						}
					}
					else {
						record_error("include_ihx_file", "Malformed IHX line (no starting :).");
						/* Read until end of line */
						i = fgetc(included);
						while ((i!='\n')&&(i!=EOF))
							i = fgetc(included);
						if (i==EOF)
							done = 1;
					}
				}
				fclose( included );
			}
			else {
				record_error("include_ihx_file", "Cant open include file for reading.");
			}
		}
		else
			record_error("include_ihx_file", "Argument to INCIHX is not a string.");
	}
	return EFAIL;
}

/* Recursivly print the token list processing any macros as they appear */
void print_token_chain(pmtoken current_token, pmmacro first_macro, pmdefine first_define)
{
    int dont_print = 0;

    pmmacro current_macro, scan_macro;
    current_macro = first_macro;

    while (current_token) {
	if (current_token->state == RST) {
	    if (current_token->next != NULL) {
		if (current_token->next->state != RST)
		    printf("\n");
	    }
	    dont_print = 0;
	} 
	else {
#define USE_MACROS
#ifdef USE_MACROS
	    if (current_token->state == SOPCODE) {
			/* Check to see if it's a macro */
			scan_macro = first_macro;
			while (scan_macro) {
				if (!strcmp(scan_macro->name, current_token->text)) {
					dont_print = 1;
					build_macro_args(scan_macro, current_token, first_define);
					print_token_chain(scan_macro->macro, first_macro, scan_macro->args);
				}
				scan_macro = scan_macro->next;
			}
			/* Check to see if it's an INCBIN */
			if (!strcmp( current_token->text, "INCBIN")) {
				include_binary_file( current_token );
				dont_print = 1;
			}
			if (!strcmp( current_token->text, "INCIHX")) {
				include_ihx_file( current_token );
				dont_print = 1;
			}
			if (!strcmp( current_token->text, "DF")) {
				if (current_token->next) {
					output_float(current_token->next);
				}
				dont_print = 1;
			}
	    }
	    /* Handle link opcodes */
	    if (!strcmp(current_token->text, "link")) {
			if (!strcmp(current_token->next->text, "DEFL")) {
				sprintf(current_token->text, "link%u", ++linkcount);
			}
			else
				sprintf(current_token->text, "link%u", linkcount);
	    }

#endif
	    if (!dont_print) {
		    printf("%s%s%s", prepend[current_token->state], replace_defines(current_token->text, first_define), postpend[current_token->state]);
			/* Special exception that doesnt easially fit in state table */
			if (current_token->next) {
				if ((current_token->state == SOPCODE_ARGS)&&(current_token->next->state == SOPCODE_ARGS)) {
					printf(" ");
				}
			}
	    }
	}
	current_token = current_token->next;
    }
}

void handle_error( int line_number, int state, int charclass, char *file_name)
{
	int search, done;

	search = 0;
	done = 0;

	debug("handle_error", "entered");
	fflush(stdout);
	while ((errors[search].state !=-1)&&(!done)) {
		debug("handle_error", state_names[errors[search].state]);
		if (errors[search].state == state) {
			if ((errors[search].charclass == TDEFAULT)||(errors[search].charclass==charclass)) {
				fprintf(stderr, "%s:%u:%s\n", file_name, line_number, errors[search].description);
				done = 1;
			}
		}
		search++;
	}
	if (!done)
		fprintf(stderr, "%s:%u:Unexpected error due to transition from %s -> ??? Class = %u.\n", file_name, line_number, state_names[state], charclass);
}

/* Read the source file from stdin */
int parse_in( pmtoken *first_token, char *file_name, FILE *fp )
{
    int state;
    char line[100], current_text[100];
    char *current, *store;
    pmstate rule;
    pmtoken current_token, insert_before;
    int tokenclass;
	int line_number, errors_found;

	state = RST;
	line_number = 0;
	errors_found = 0;

	current_text[0] = '\0';
	store = current_text;

	if (*first_token) {
		insert_before = (*first_token)->next;
		current_token = *first_token;
	}
	else {
		insert_before = NULL;
		current_token = NULL;
	}
	
	while (fgets(line,100,fp)!=NULL) {

	    current = line;
		line_number++;

		while ((*current != '\0')&&(state!=SERROR)) {
			tokenclass = get_token_class(*current);
			rule = find_rule(state, tokenclass);
#ifdef DEBUG
			fprintf(stderr, "[%s, %c] ", state_names[state], *current);
#endif
			fflush(stdout);
			
			if ((rule != NULL)&&(rule->next!=SERROR)) {
				if ((rule->current != rule->next)) {
					if (!((rule->next == SOPCODE_ARGS_ENDSTRING) && (rule->current == SOPCODE_ARGS_STRING))) {
						/* Print what was scanned and what rule it falls under */
						*store = '\0';
						if (remotely_interesting[state]) {
							/* Add it on to the list of tokens */

							if (*first_token == NULL) {
								/* First in the list */
								*first_token = malloc( sizeof( mtoken ));
								current_token = *first_token;
								current_token->previous = NULL;
								insert_before = NULL;
							}
							else {
								current_token->next = malloc(sizeof(mtoken));
								current_token->next->previous = current_token;
								current_token = current_token->next;
							}
							current_token->next = insert_before;
							
							if (insert_before)
								insert_before->previous = current_token;

							current_token->state = state;
							strcpy(current_token->text, current_text);
						}
						store = current_text;
						*store = '\0';
					}
					state = rule->next;
				}
				*store = *current;
				store++;
			}
			else {
				handle_error( line_number, state, tokenclass, file_name );
				state = RST;
				errors_found++;
			}
			current++;
		}
	}

	return errors_found;
}

int print_macros( pmmacro current_macro )
{
	pmdefine current_define;

	printf("\nDefined macros:\n");
	while (current_macro) {
	    printf("  [%s]", current_macro->name);
	    current_define = current_macro->args;
	    while (current_define) {
			printf(" %s", current_define->find);
			current_define = current_define->next;
	    }
	    printf("\n");
	    current_macro = current_macro->next;
	}
	return EOK;
}

void useage( char *program )
{
	pmstyle search;
	printf(	"maccer - assembler macro pre-processor, M. Hope 1998.\n"
			"Version " VERSION_STRING ", built " __DATE__ ".\n"
			"Useage:\n"
			"  %s [-ttype] [-o output file] [input file]\n"
			"  Will read from stdin and write to stdout otherwise.\n"
			"  'type' is the output style to use.  Currently defined styles are:\n"
			"\t"
			, program );
	search = styles;
	while (strcmp(search->name, "END")) {
		printf("%s ", search->name);
		search++;
	}
	printf("\n");
}

int main( int argc, char **argv )
{
    pmtoken first_token;
    pmmacro first_macro;
	char file_name[100] = "<stdin>";
	int i, output_set, input_set;

	current_style = &styles[0];		/* Set the default style */
	first_token = NULL;
	first_macro = NULL;
	output_set = 0;
	input_set = 0;

	/* Handle command line arguments */
	for (i=1; i<argc; i++) {
		if (!strcmp("--help", argv[i])) {
			useage(argv[0]);
			return EOK;
		}
		if (argv[i][0] == '-') {
			switch (argv[i][1]) {
				case 'h': {
					/* Help */
					useage(argv[0]);
					return EOK;
					break;
				}
				case 'o': {
					if ((i+1)<argc) {
						if (!output_set) {
							output_set = 1;
					
							if (freopen( argv[i+1], "w", stdout )==NULL) {
								record_error( argv[0], "error: cannot open file for writing." );
								return EFILE;
							}
						}
						else
							record_error( argv[0], "warning: extra -o ignored.");
						i++;
					}
					else
						record_error( argv[0], "warning: -o without an argument.");
					break;
				}
				case 't': {
					/* Set the target style */
					if (set_style(&argv[i][2])!=EOK)
						record_error( argv[0], "warning: unknown target, ignoring.");
					break;
				}
				default: {
					/* Unknown option */
					record_error(argv[0], "warning: invalid option.  Try --help.");
					break;
				}
			}
		}
		else {
			if (!input_set) {
				if (freopen( argv[i], "r", stdin )==NULL) {
					record_error( argv[0], "error: cannot open file for reading.");
					return EFILE;
				}
				input_set = 1;
				strcpy( file_name, argv[i] );
			}
			else {
				record_error( argv[0], "warning: second input file ignored.");
			}
		}
	}
		
	debug("main", "calling parse_in");
	if (parse_in( &first_token, file_name, stdin )) {
		return EFAIL;
	}

	/* Print out the token list */
	add_includes(first_token);
	debug("main", "calling add_macros");
	add_macros(first_token, &first_macro);
	debug("main", "calling print_token_chain");
	print_token_chain(first_token, first_macro, NULL);

	printf("\n");
/*	print_macros( first_macro );*/
	return EOK;

}
