#include <stdio.h>
#include <string.h>
#include "defs.h"
#include "externs.h"

int   mopt;
int   in_macro;
int   expand_macro;
char  marg[8][10][80];
int   midx;
int   mcounter, mcntmax;
int   mcntstack[8];
struct t_line  *mstack[8];
struct t_line  *mlptr;
struct t_macro *macro_tbl[256];
struct t_macro *mptr;

/* .macro pseudo */

do_macro(int *ip)
{
	if (pass == LAST_PASS)
		println();
	else {
		if (expand_macro) {
			error("Can not nest macro definitions!");
			return;
		}
		if (lablptr == NULL) {
			error("No name for the macro!");
			return;
		}
		if (!stremove())
			return;
		if (!check_eol(ip))
			return;
		if (!macro_install())
			return;
	} 
	in_macro = 1;
}

/* .endm pseudo */

do_endm(int *ip)
{
	error("Unexpected ENDM!");
	return;
}

/* search for a macro */

macro_look(int idx)
{
	char name[32];
	char c;
	int  hash;
	int  i;

	i = 0;
	hash = 0;
	while(c = prlnbuf[idx]) {
		if (c == ' ' || c == '\t' || c == ';')
			break;
		if (!isalnum(c) && c != '_')
			return (0);
		if (isdigit(c) && i == 0)
			return (0);
		name[i++] = c;
		hash += c;
		hash = (hash << 3) + (hash >> 5) + c;
		idx++;
		if (i > 31)
			return (0);
	}

	name[i] = '\0';
	hash &= 0xFF;
	mptr = macro_tbl[hash];
	while (mptr) {
		if (!strcmp(name, mptr->name))
			break;			
		mptr = mptr->next;
	}
	if (mptr)
		return (macro_extract(idx));
	else
		return (0);
}

/* extract macro arguments */

macro_extract(int idx)
{
	char *ptr;
	char  c, t;
	int   i, j, f, arg;

	if (midx == 7) {
		error("Too many nested macro calls!");
		return (-1);
	}
	mcntstack[midx] = mcounter;
	mstack[midx++] = mlptr;
	arg = 0;
	ptr = marg[midx][0];
	for (i = 0; i < 9; i++) {
	   *ptr  = '\0';
	    ptr += 80;
	}
	ptr = marg[midx][0];
	for (;;) {
		while (c = prlnbuf[idx]) {
			if (c != ' ' && c != '\t')
				break;
			idx++;
		}
		switch (c) {
		case ',':
			arg++;
			idx++;
			ptr += 80;
			if (arg == 9) {
				error("Too many arguments for a macro!");
				return (-1);
			}
			break;
			
		case '{':
			idx++;
		case '\"':
			i = 0;
			if (c == '{')
				t = '}';
			else
				t = '\"';
			while (c = prlnbuf[idx]) {
				ptr[i++] = c;
				if (i == 80) {
					error("Invalid macro argument length!");
					return (-1);
				}
				if (c == t)
					break;
				idx++;
			}
			switch (c) {
			case '\0':
				error("Unterminated ASCII string!");
				return (-1);
			case '}':
				i--;
				break;
			}
			while (c = prlnbuf[++idx]) {
				if (c != ' ' && c != '\t')
					break;
			}
			if (c != ',' && c != ';') {
				error("Syntax error!");
				return (-1);
			}
			ptr[i] = '\0';
			break;
	
		case ';':
		case '\0':
			return (1);

		default:
			i = 0;
			j = 0;
			f = 0;
			while (c = prlnbuf[idx]) {
				if (c == ',' || c == ';')
					break;
				if (f) {
					if (c != ' ') {
						for (;i < j; i++)
							ptr[i++] = ' ';
						ptr[i++] = c;
						f = 0;
					}
				}
				else if (c == ' ')
					f = 1;
				else 
					ptr[i++] = c;
				if (i == 80) {
					error("Invalid macro argument length!");
					return (-1);
				}
				idx++;
				j++;
			}
			ptr[i] = '\0';
			break;
		}
	}
}

/* install a macro in the hash table */

macro_install()
{
	char c;
	int  hash;
	int  i;

	hash = 0;
	for (i = 1; i <= symbol[0]; i ++) {
		c = symbol[i];
		if (!isalnum(c) && c != '_') {
			error("Invalid macro name!");
			return (0);
		}
		hash += c;
		hash  = (hash << 3) + (hash >> 5) + c;
	}
	hash &= 0xFF;
	mptr  = macro_tbl[hash];
	while (mptr) {
		if (!strcmp(mptr->name, &symbol[1]))
			break;			
		mptr = mptr->next;
	}
	if (mptr) {
		error("Macro defined twice!");
		return (0);
	}
	if ((mptr = (void *)malloc(sizeof(struct t_macro))) == NULL) {
		error("Out of memory!");
		return (0);
	}
	strcpy(mptr->name, &symbol[1]);
	mptr->line = NULL;
	mptr->next = macro_tbl[hash];
	macro_tbl[hash] = mptr;
	mlptr = NULL;
	return (1);
}

