/**************************************************************
 *
 *	CRISP - Custom Reduced Instruction Set Programmers Editor
 *
 *	(C) Paul Fox, 1989
 *
 *    Please See COPYRIGHT notice.
 *
 **************************************************************/
# include	"list.h"
# include	"foxlib/pfloat.h"

int	 fstat();

# define	FBUFSIZ		1024
# define	MAX_CM_SIZE	65530
# define	YYMAX	128

# define	_XDIGIT		0x01
# define	_XSYMBOL	0x02
# define	_XWS		0x04
# define	_XISPRINT	0x08	/* Used by printable_char() function */
					/* in display.c			     */
# define	_XTABESC	0x10 	/* Set for tabs and <Esc>. Used by   */
					/* char_width() in map.c	     */
# define	TOKEN_EOF	0
# define	OPEN_PAREN	1
# define	CLOSE_PAREN	2
# define	ID		3
# define	INT		4
# define	STR		5
# define	FLOAT		6

# define	NATOMS	2048
# define	NMACROS	256

/**********************************************************************/
/*   Prototypes.						      */
/**********************************************************************/
void	init_fp1 PROTO((char *));
int	get_char1();
int	yyparse();
void	free_defines();
int	yyparse1();
List_p	decl_lookup();
void	decl_enter();
OPCODE	decl_gettype PROTO((char *));
int	yylook PROTO((void));
int	yylex PROTO((void));
static void	get_escaped_character();
int	do_comment();
int	get_quoted_string();
void	get_until();
void	yyerror();
void	cpp();
void	do_include();
void	do_define();
int	do_number();
int	do_symbol();
int	read_cm();
void	swap_cm_header();

u_int16 WGET16();
u_int32 WGET32();

Head_p		hd_syms;
int	pending_macros[NMACROS];	/* List of macros to be inserted. */
int	npending;
LIST	*first_atom;
int	sizeof_macro;			/* Size of macro in atoms.	  */
int	finding_macro = FALSE;	/* TRUE whilst find_macro() primitive being */
				/* executed.				    */

/**********************************************************************/
/*   Following  table  is used to make it fast to process characters  */
/*   from m files.						      */
/**********************************************************************/
unsigned char _chars_[256] = {
/* | nul| soh| stx| etx| eot| enq| ack| bel| */
	0,  0,  0,  0,  0,  0,  0,  0,	/* 0x00 - 0x07 */
/* | bs | ht | nl | vt | np | cr | so | si | */
    0x10,0x14,  0,   0,   4,   0,   0,   0,/* 0x08 - 0x0f */
/* | dle| dc1| dc2| dc3| dc4| nak| syn| etb| */
	0,  0,  0,  0,  0,  0,  0,  0,	/* 0x10 - 0x17 */
/* | can| em | sub| esc| fs | gs | rs | us | */
	0,  0,  0,0x10,  0,  0,  0,  0,	/* 0x18 - 0x0f */
/* | sp |  ! |  " |  # |  $ |  % |  & |  ' | */
    0x0c,0x0a,0x08,0x08,0x0a,0x0a,0x0a,0x08,
/* |  ( |  ) |  * |  + |  , |  - |  . |  / | */
    0x08,0x08,0x0a,0x0a,0x08,0x0a,0x0a,0x0a,
/* |  0 |  1 |  2 |  3 |  4 |  5 |  6 |  7 | */
    0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,
/* |  8 |  9 |  : |  ; |  < |  = |  > |  ? | */
    0x0b,0x0b,0x08,0x08,0x0a,0x0a,0x0a,0x08,
/* |  @ |  A |  B |  C |  D |  E |  F |  G | */
    0x08,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0a,
/* |  H |  I |  J |  K |  L |  M |  N |  O | */
    0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,
/* |  P |  Q |  R |  S |  T |  U |  V |  W | */
    0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,
/* |  X |  Y |  Z |  [ |  \ |  ] |  ^ |  _ | */
    0x0b,0x0a,0x0a,0x08,0x08,0x08,0x0a,0x0a,
/* |  ` |  a |  b |  c |  d |  e |  f |  g | */
    0x08,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0a,
/* |  h |  i |  j |  k |  l |  m |  n |  o | */
    0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,
/* |  p |  q |  r |  s |  t |  u |  v |  w | */
    0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,
/* |  x |  y |  z |  { |  | |  } |  ~ | del| */
    0x0b,0x0a,0x0a,0x08,0x0a,0x08,0x0a,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 0x80-0x87 */
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 0x88-0x8f */
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 0x90-0x97 */
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 0x98-0x9f */
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 0xa0-0xa7 */
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 0xa8-0xaf */
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 0xb0-0xb7 */
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 0xb8-0xbf */
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 0xc0-0xc7 */
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 0xc8-0xcf */
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 0xd0-0xd7 */
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 0xd8-0xdf */
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 0xe0-0xe7 */
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* 0xe8-0xef */
    0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,	/* 0xf0-0xf7 */
    0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00,	/* 0xf8-0xff */
	};

# define	newline() {fp_ptr->line_no++; }
# define	get_char() (yytchar = (yytchar = *fp_ptr->bufp++) ?\
				yytchar : get_char1() )
/**********************************************************************/
/*   Following  function  is  used to initialise the data structures  */
/*   used  to  start  compiling  a '.m' file. Returns -1 if file not  */
/*   available. Returns zero if file successfully opened or found.    */
/**********************************************************************/
int
init_fp(f, filename)
int	f;
char	*filename;
{	register int yytchar;
	static int first_time = TRUE;
	struct stat stat_buf;

	if (first_time) {
		int	i;
		first_time = FALSE;
		for (i = 0; i < MAX_FILES; i++)
			fps[i].fd = -1;
		hd_syms = ll_init();
		}
	/***********************************************/
	/*   If   we  are  executing  a  find_macro()  */
	/*   primitive  then  we don't need to go any  */
	/*   further.				       */
	/***********************************************/
	if (finding_macro)
		return 0;
	fp_ptr++;
	if (fp_ptr - fps >= MAX_FILES-1) {
		errorf("Include files nested too deeply");
		fp_ptr--;
		return 0;
		}
	if (fp_ptr->fd >= 0)
		close(fp_ptr->fd);
	if ((fp_ptr->fd = open(filename, OPEN_R_BINARY | O_RDONLY)) < 0) {
		fp_ptr--;
		return -1;
		}
	if (fstat(fp_ptr->fd, &stat_buf) < 0 ||
		(stat_buf.st_mode & S_IFMT) != S_IFREG) {
		fp_ptr--;
		return -1;
		}
	strcpy(fp_ptr->name, filename);
	fp_ptr->size = stat_buf.st_size;
	fp_ptr->line_no = 1;
	fp_ptr->flags = f;
	fp_ptr->bufp = fp_ptr->buf = ((char *) chk_alloc(FBUFSIZ+3))+2;
	*fp_ptr->bufp = NULL;

	if (stat_buf.st_size)
		get_char();
	else {
		fp_ptr->bufp[-1] = NULL;
		fp_ptr->bufp[-2] = NULL;
		fp_ptr->bufp--;
		}
	fp_ptr->bufp--;
	return 0;
}
void
init_fp1(buf)
char	*buf;
{
	fp_ptr++;
	if (fp_ptr - fps >= MAX_FILES-1) {
		errorf("File is too complex to parse.\n");
		fp_ptr--;
		return;
		}
	strcpy(fp_ptr->name, fp_ptr[-1].name);
	fp_ptr->line_no = fp_ptr[-1].line_no;
	fp_ptr->fd = -1;
	fp_ptr->flags = 0;
	fp_ptr->bufp = fp_ptr->buf = buf;
	fp_ptr->bufend = &buf[strlen(buf)+1];
}
int
get_char1()
{
	register int ch;
	register int	n;

	fp_ptr->bufp--;
again:
	if (*fp_ptr->bufp == NULL)
		if (fp_ptr->fd >= 0 && 
		    (n = sys_read(fp_ptr->fd, fp_ptr->bufp = fp_ptr->buf, FBUFSIZ)) > 0) {
			fp_ptr->bufend = &fp_ptr->buf[n];
			*fp_ptr->bufend = NULL;
			}
		else {
			if (fp_ptr->fd >= 0) {
				close(fp_ptr->fd);
				chk_free(fp_ptr->buf-2);
				}
			fp_ptr->fd = -1;
			if (fp_ptr->flags == TERMINAL) {
				fp_ptr--;
				return 0;
				}
			fp_ptr--;
			goto again;
			}
	ch = *fp_ptr->bufp++;
	return ch;
}
char	yytext[YYMAX];
static long	yyint;
static double	yyfloat;
int	llevel = 0;
int	cm_running = FALSE;
DEFINE	*def_head,
	*def_ptr;

int	token;
int	malloc_size = NATOMS;
int	parse_error;
int
yyparse()
{
	CM	*cm = (CM *) fp_ptr->bufp;
	int	atom;
	int	i;

	if (cm->cm_magic == CM_MAGIC) {
		i = read_cm();
		close(fp_ptr->fd);
		fp_ptr->fd = -1;
		fp_ptr--;
		return i;
		}

	parse_error = 0;

	def_head = def_ptr = NULL;
	npending = 0;
	if ((first_atom = (LIST *) chk_alloc(malloc_size)) == NULL) {
		yyerror("Cannot allocate room for macro\n", (char *) NULL);
		return -1;
		}

	atom = 0;

	while (1) {
		pending_macros[npending] = atom;
		token = yylex();
		if (token == TOKEN_EOF)
			break;
		if (token != OPEN_PAREN) {
			yyerror("Macro does not start with a '('", (char *) NULL);
			break;
			}
		atom = yyparse1(atom);
		if (token < 0)
			break;
		if (token != CLOSE_PAREN) {
			yyerror("Macro does not end with a ')'", (char *) NULL);
			break;
			}
		first_atom[atom++] = F_END;
		npending++;
		}
	free_defines(def_head);
	ll_clear(hd_syms);

	if (parse_error) {
		if (first_atom)
			chk_free((char *) first_atom);
		}
	else if (atom) {
		first_atom = (LIST *) chk_realloc((char *) first_atom, atom+1);
		for (i = 0; i < npending; i++) {
			sizeof_macro = pending_macros[i+1] - pending_macros[i];
			execute_macro(first_atom + pending_macros[i]);
			}
		}
	return parse_error;
}
void
free_defines(ptr)
register DEFINE *ptr;
{
	if (ptr == NULL)
		return;
	free_defines(ptr->next);
	chk_free(ptr->name);
	chk_free(ptr->value - 1);
	chk_free((void *) ptr);
}
int
yyparse1(base_atom)
register int base_atom;
{	register int	atom = base_atom;
	register int	new_atom;
	register LIST	*ap;
	int	first_token = TRUE;
	int	decl = 0;

	while (1) {
		if (atom > malloc_size - 10) {
			malloc_size += NATOMS;
			if ((first_atom = (LIST *) chk_realloc((char *) first_atom, 
					malloc_size)) == NULL) {
				yyerror("Cannot allocate room for macro\n", (char *) NULL);
				return -1;
				}
			}
		token = yylex();
		if (token == OPEN_PAREN) {
			if (decl) {
				yyerror("Cannot nest declarations.", (char *) NULL);
				return NULL;
				}
			first_atom[atom] = F_LIST;
			if ((new_atom = yyparse1(atom + sizeof_atoms[F_LIST])) == NULL)
				return NULL;
			if (token == TOKEN_EOF) {
				yyerror("Missing close parenthesis.", (char *) NULL);
				return NULL;
				}
			LPUT16(&first_atom[atom], new_atom - atom);
			atom = new_atom;
			continue;
			}
		ap = &first_atom[atom];
		if (token == CLOSE_PAREN) {
			*ap = F_HALT;
			return ++atom;
			}
		if (decl || token == ID) {
			BUILTIN *bp;
			extern BUILTIN builtin[];
			if (bp = lookup_builtin(yytext)) {
				if (first_token) {
					if (strcmp(yytext, "int") == 0)
						decl = F_INT;
					else if (strcmp(yytext, "string") == 0)
						decl = F_STR;
					else if (strcmp(yytext, "list") == 0)
						decl = F_LIST;
					else if (strcmp(yytext, "float") == 0)
						decl = F_FLOAT;
					else if (strcmp(yytext, "global") == 0)
						decl = -1;
					first_token = FALSE;
					}
				*ap = F_ID;
				LPUT16(ap, bp - builtin);
				atom += sizeof_atoms[F_ID];
				continue;
				}
			if (decl == -1) {
				OPCODE type;
				type = decl_gettype(yytext);
				if ((int) type == 0) {
					yyerror("Undefined symbol %s", yytext);
					return NULL;
					}
				if (type == F_ERROR) {
					yyerror(
"Trying to globalise symbol declared with different types: %s", yytext);
					return NULL;
					}
				*ap = F_INT;
				LPUT32(ap, (long) type);
				atom += sizeof_atoms[F_INT];
				ap = &first_atom[atom];
				}
			else if (decl)
				decl_enter(yytext, decl);
			if (yytext[0] == 'N' && strcmp(yytext, "NULL") == 0)
				*ap = F_NULL;
			else if (yytext[0] != '"') {
				*ap = F_STR;
				LPUT32(ap, (long) strdup(yytext));
				}
			else {
				*ap = F_LIT;
				LPUT32(ap, (long) strdup(yytext+1));
				}
			atom += sizeof_atoms[*ap];
			first_token = FALSE;
			continue;
			}
		first_token = FALSE;
		if (token == INT) {
			*ap = F_INT;
			LPUT32(ap, yyint);
			atom += sizeof_atoms[F_INT];
			continue;
			}
		if (token == FLOAT) {
			*ap = F_FLOAT;
			LPUT_FLOAT(ap, yyfloat);
			atom += sizeof_atoms[F_FLOAT];
			continue;
			}
		if (token == TOKEN_EOF)
			return atom;
		token = -1;
		yyerror("Invalid token", (char *) NULL);
		return NULL;
		}
}
List_p
decl_lookup(sym)
char	*sym;
{	List_p	lp;
	SYMBOL	*sp;

	for (lp = ll_first(hd_syms); lp; lp = ll_next(lp)) {
		sp = (SYMBOL *) ll_elem(lp);
		if (strcmp(sp->s_name, sym) == 0)
			return lp;
		}
	return NULL;
}
void
decl_enter(sym, type)
char	*sym;
OPCODE	type;
{	List_p	lp = decl_lookup(sym);
	SYMBOL	*sp;
	if (lp) {
		sp = (SYMBOL *) ll_elem(lp);
		if (sp->s_type != type)
			sp->s_type = F_ERROR;
		}
	else {
		sp = (SYMBOL *) chk_alloc(sizeof (SYMBOL));
		strcpy(sp->s_name, yytext);
		sp->s_type = type;
		ll_append(hd_syms, (char *) sp);
		}
}
OPCODE
decl_gettype(sym)
char	*sym;
{	List_p	lp = decl_lookup(sym);
	SYMBOL	*sp;
	if (lp == NULL)
		return (OPCODE) 0;
	sp = (SYMBOL *) ll_elem(lp);
	return sp->s_type;
}
int
yylex()
{
	register int yytchar;
	register int	ch;
	register int	i;

again:
	while (_chars_[get_char()] & _XWS)
		;

	switch (yytchar) {
	case 0x04: /* CTRL-D */
	case 0x1a: /* CTRL-Z */
	case 0:
		return TOKEN_EOF;
	case '\r':
		goto again;
	case '(':	
		return OPEN_PAREN;
	case ')':
		return CLOSE_PAREN;
	case '-':
		ch = yytchar;
		i = _chars_[get_char()] & _XDIGIT;
		*--fp_ptr->bufp = (char) yytchar;
		yytchar = ch;
		if (i == 0)
			goto alpha;
	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
		return do_number(yytchar);
	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
	case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
	case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
	case 'V': case 'W': case 'X': case 'Y': case 'Z':
	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
	case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
	case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
	case 'v': case 'w': case 'x': case 'y': case 'z': 
	case '_': case '$':
		i = do_symbol(yytchar);
		if (i == TRUE)
			goto again;
		return token = ID;
	case '/':
		yytext[0] = (char) yytchar;
		if (*fp_ptr->bufp == '*') {
			if (do_comment(TRUE) == 0)
				return TOKEN_EOF;
			goto again;
			}
		if (*fp_ptr->bufp == NULL) {
			if (get_char() == '*') {
				if (do_comment(TRUE) == 0)
					return TOKEN_EOF;
				goto again;
				}
			--fp_ptr->bufp;
			}
		get_until(yytext+1, _XSYMBOL);
		if (yytext[1] != '/' || yytext[2] != NULL)
			return ID;
		/* Fall into... (allows // as a comment). */
	case ';':
		if (do_comment(FALSE) == 0)
			return TOKEN_EOF;
		goto again;
	case '+': case '*': case '%': case '<': case '>': case '^':
	case '=': case '!': case '|': case '&': case '.': case '~':
alpha:
		yytext[0] = (char) yytchar;
		get_until(yytext+1, _XSYMBOL);
		return ID;
	case '\'':
		if (get_quoted_string('\'') == FALSE) {
			yyerror("Character constant too long or unterminated.", (char *) NULL);
			return -1;
			}
		yyint = yytext[0];
		return token = INT;
	case '"':
		if (get_quoted_string('"') == FALSE) {
			yyerror("String literal not terminated.", (char *) NULL);
			return -1;
			}
		return token;
	case '#':
		cpp();
		goto again;
	case '\n':
		newline();
		goto again;
	default:
		sprintf(yytext, "illegal character: 0x%02x (%c)",
			yytchar & 0xff, yytchar);
		yyerror(yytext, (char *) NULL);
		return -1;
	}
}
static void
get_escaped_character(str)
char	*str;
{	int	byte;
	char	*charp = str++;
	char ch = *str++;

	switch (ch) {
	  case 't':	byte = '\t'; break;
	  case 'n':	byte = '\n'; break;
	  case 'f':	byte = '\f'; break;
	  case 'r':	byte = '\r'; break;
	  case 'x':		
		byte = *str++;
		if (isdigit(byte))
			byte -= '0';
		else if (byte >= 'A' && byte <= 'F')
			byte = byte - 'A' + 10;
		else if (byte >= 'a' && byte <= 'f')
			byte = byte - 'a' + 10;
		else {
			str--;
			break;
			}
		/*--------------------------
		 *   Second digit.
		 *--------------------------*/
		ch = *str++;
		if (isdigit(ch))
			byte = (byte << 4) + ch - '0';
		else if (ch >= 'A' && ch <= 'F')
			byte = (byte << 4) + ch - 'A' + 10;
		else if (ch >= 'a' && ch <= 'f')
			byte = (byte << 4) + ch - 'a' + 10;
		else
			str--;
		break;
	  default:
		byte = ch;
		break;
	  }
	*charp++ = byte;
	strcpy(charp, str);
}
int
do_comment(C_comment)
int	C_comment;
{	register int yytchar;
	int	end_ch = C_comment ? '*' : '\n';
	int	lineno = fp_ptr->line_no;
	
	while (1) {
		if (get_char() == 0) {
			yyerror("Unterminated comment at line %d", lineno);
			return 0;
			}
second_char:
		if (yytchar == '\n')
			newline();
		if (yytchar == end_ch) {
			if (!C_comment)
				return 1;
			if (get_char() == 0)
				return 0;
			if (yytchar == '/')
				return 1;
			goto second_char;
			}
		}
}
int
get_quoted_string(quote)
int	quote;
{	register unsigned char *cp = (unsigned char *) yytext;
	register int yytchar;

	if (quote == '"')
		*cp++ = '"';
	while (1) {
		if (get_char() == 0) {
			*cp = NULL;
			return FALSE;
			}
		if (yytchar == quote) {
			*cp = NULL;
			break;
			}
		if (yytchar == '\n') {
			*cp = NULL;
			return FALSE;
			}
		if (yytchar != '\\') {
			*cp++ = (char) yytchar;
			continue;
			}
		*cp++ = '\\';
		*cp++ = get_char();
		}

	for (cp = (unsigned char *) yytext; *cp; cp++) {
		if (*cp != '\\')
			continue;
		get_escaped_character(cp);
		}
		
	token = ID;
	return TRUE;
}
void
get_until(str, mask)
register char	*str;
register int mask;
{
	register int yytchar;

	while (1) {
		if (get_char() == 0) {
			*str = NULL;
			return;
			}
		if ((_chars_[yytchar] & mask) == 0) {
			--fp_ptr->bufp;
			*str = NULL;
			return;
			}
		*str++ = (char) yytchar;
		}
}


void
yyerror(str, str1)
char	*str, *str1;
{	char	buf[256];

	parse_error = -1; 
	if (fp_ptr >= fps)
		sprintf(buf, "%s(%d): %s", fp_ptr->name, fp_ptr->line_no, str);
	else
		strcpy(buf, str);
	ewprintf(buf, str1);
	llevel = 0;
	token = -1;
}
void
cpp()
{	register char *cp;
	register int yytchar;

	yytext[0] = '#';
	for (cp = yytext+1; ; *cp++ = (char) yytchar)
		if (get_char() == 0 || yytchar == '\n' || yytchar == ';') {
			if (yytchar)
				--fp_ptr->bufp;
			*cp = NULL;
			break;
			}

	for (cp = yytext+1; *cp == ' ' || *cp == '\t'; )
		cp++;
	if (strncmp(cp, "define", 6) == 0) {
		do_define();
		return;
		}
	if (strncmp(cp, "include", 7) == 0) {
		do_include();
		return;
		}
	yyerror("pre-processor command not recognized");
}
void
do_include()
{	extern	char	*bpath;
	char	inc_file[128];
	char	buf[128];
	register char	*cp, *bp;
	int	delim = 0;
	extern char *strrchr();
	
	for (cp = yytext+8; *cp && *cp != '<' && *cp != '"'; )
		cp++;
	if (*cp)
		delim = *cp++;
	for (bp = inc_file; *cp && *cp != '>' && *cp != '"'; )
		*bp++ = *cp++;
	*bp = NULL;

	if (delim == '"' && init_fp(0, inc_file) >= 0)
		return;
	if (cp = strrchr(fp_ptr->name, '/')) {
		strcpy(buf, fp_ptr->name);
		strcpy(&buf[cp - fp_ptr->name + 1], inc_file);
		if (init_fp(0, buf) >= 0)
			return;
		}

	for (cp = bpath; *cp && inc_file[0] != '/' ; ) {
		bp = buf;
		while (*cp && *cp != ';')
			*bp++ = *cp++;
		if (*cp == ';')
			cp++;
		*bp++ = '/';
		strcpy(bp, inc_file);
		if (init_fp(0, buf) >= 0)
			return;
		}
	ewprintf("Cannot read %s", inc_file);
}
void
do_define()
{	char	*symbol;
	char	*value;
	register DEFINE	*dp = def_head;
	extern char	*strtok();
	register int	l;
	register char *cp = yytext;

	while (strncmp(cp, "define", 6) != 0)
		cp++;
	cp += 6;
	while (isspace(*cp))
		cp++;
	symbol = strtok(cp, " \t");
	cp = strtok((char *) NULL, "\n");
	while (*cp && isspace(*cp))
		cp++;
	if (*cp == '"') {
		value = cp++;
		for (; *cp && *cp != '"'; cp++)
			if (*cp == '\\')
				cp++;
		if (*cp == '"')
			*++cp = NULL;
		}
	else
		value = strtok(cp, " \t\n");

	l = strlen(value);

	for (; dp; dp=dp->next)
		if (strcmp(dp->name, symbol) == 0)
			break;
	if (dp == NULL) {
		if (def_ptr == NULL)
			def_head = def_ptr = (DEFINE *) chk_alloc(sizeof (DEFINE));
		else {
			def_ptr->next = (DEFINE *) chk_alloc(sizeof (DEFINE));
			def_ptr = def_ptr->next;
			}
		def_ptr->name = strdup(symbol);
		def_ptr->value = ((char *) chk_alloc(l + 4)) + 1;
		def_ptr->next = NULL;
		dp = def_ptr;
		}
	else if (strlen(dp->value + 1) > l) {
		chk_free(dp->value - 1);
		dp->value = ((char *) chk_alloc(l + 4)) + 1;
		}

	memcpy(dp->value + 1, value, l + 1);
}
int
do_number(ch)
int	ch;
{
	int	ret;
	
	ret = parse_number(ch, &yyfloat, &yyint);
	switch (ret) {
	  case PARSE_ERR_ELLIPSIS_ERROR:
		yyerror("Ellipsis has the syntax '...'");
		break;
	  default:
	  case PARSE_ERR_FLOAT_SYNTAX_ERROR:
		yyerror("Syntax error reading floating point constant");
		break;
	  case PARSE_ERR_EOF_IN_FLOAT:
		yyerror("EOF detected reading floating point constant");
		break;
	  case PARSE_INTEGER:
	  	return token = INT;
	  case PARSE_FLOAT:
	  	return token = FLOAT;
	  }
  	return token = INT;
}
/**********************************************************************/
/*   Function called by parse_number() to get next character.	      */
/**********************************************************************/
int
input()
{	int	yytchar;

	return get_char();
}

/**********************************************************************/
/*   Function called by parse_number() to unget a character.	      */
/**********************************************************************/
void
ungetchar()
{
	--fp_ptr->bufp;
}
int
do_symbol(ch)
int	ch;
{	register DEFINE *dp = def_head;

	yytext[0] = (char) ch;
	get_until(yytext+1, _XSYMBOL);

	for (; dp; dp = dp->next)
		if (dp->name[0] == yytext[0] && strcmp(dp->name, yytext) == 0) {
			init_fp1(dp->value + 1);
			return TRUE;
			}
	return FALSE;
}
int
read_cm()
{	
	register LIST	*lp;
	register LIST	*lpend;
	CM	*cm = (CM *) fp_ptr->bufp;
	u_int32	*vm_offsets;
	u_int32	*globals;
	int	i;
	LIST	*base_list;
	char	*str_table;
	u_int32	*soffsets;
	int	offset;
	extern int cm_version;


	if (cm_running)
		exit(0);

	if (fp_ptr->size > MAX_CM_SIZE ||
		(cm = (CM *) chk_alloc((unsigned) fp_ptr->size)) == NULL) {
		yyerror("Macro file too big to read");
		return -1;
		}
	lseek(fp_ptr->fd, 0l, 0);
	if (sys_read(fp_ptr->fd, (char *) cm, (int) fp_ptr->size) != 
	    (int) fp_ptr->size) {
		yyerror("Read() error on .cm file");
		return -1;
		}
	swap_cm_header(cm);
	if (cm->cm_version != cm_version) {
		errorf(".cm version %d not supported", cm->cm_version);
		return -1;
		}

	vm_offsets = (u_int32 *) (cm + 1);

	swap_words(vm_offsets, cm->cm_num_macros + 2);
	base_list = (LIST *) (vm_offsets + cm->cm_num_macros + 2);

	soffsets = (u_int32 *) (((char *) base_list) + 
			vm_offsets[cm->cm_num_macros]);

	str_table = (char *) (soffsets + cm->cm_num_strings);

	swap_words(soffsets, cm->cm_num_strings);
	globals = (u_int32 *) ( ((char *) cm) + cm->cm_globals );
	swap_words(globals, cm->cm_num_globals);			

	if (cm->cm_globals & 1) {
		errorf("Global decls not on even boundary.");
		return -1;
		}
	
	lpend = base_list + cm->cm_num_atoms;

	for (lp = base_list; lp < lpend; lp += sizeof_atoms[*lp]) {
		if (*lp == F_STR || *lp == F_LIT) {
			offset = (int) LGET32(lp);
			LPUT32(lp, (long) (str_table + soffsets[offset]));
			}
		}

	for (i = 0; i < cm->cm_num_globals; i++) {
		lp = (LIST *) (base_list + *globals++);
		trace_list(lp);
		exec1(lp, lp + sizeof_atoms[*lp]);
		}
	for (i = 0; i < cm->cm_num_macros; i++)
		execute_macro(base_list + vm_offsets[i]);
	return 0;		
}
void
swap_cm_header(cm)
CM *cm;
{	
	cm->cm_magic = WGET16(cm->cm_magic);
	cm->cm_version = WGET16(cm->cm_version);
	cm->cm_num_macros = WGET16(cm->cm_num_macros);
	cm->cm_num_atoms = WGET16(cm->cm_num_atoms);
	cm->cm_globals = WGET32(cm->cm_globals);
	cm->cm_num_globals = WGET16(cm->cm_num_globals);
	cm->cm_num_strings = WGET16(cm->cm_num_strings);
}
