/**********************************************************************/
/*								      */
/*	CRISP - Custom Reduced Instruction Set Programmers Editor     */
/*								      */
/*	(C) Paul Fox, 1989					      */
/*								      */
/*    Please See COPYRIGHT notice.				      */
/*								      */
/*   This file contains code for manipulating macro definitions.      */
/**********************************************************************/
# include	"list.h"

SPTREE	*macro_tbl = NULL;
int	macro_cnt = 0;

struct f fps[MAX_FILES];
struct	f	*fp_ptr;
int	init_defined;
int	yyparse();
extern int	finding_macro;

/**********************************************************************/
/*   Function  called  at  startup  time  to  initialise  the macros  */
/*   splay tree.						      */
/**********************************************************************/
void
init_macros()
{
	macro_tbl = spinit();
}

/**********************************************************************/
/*   Return pointer to a macro.					      */
/**********************************************************************/
MACRO	*
lookup_macro(name)
char	*name;
{
	SPBLK *sp = splookup(name, macro_tbl);
	if (sp)
		return (MACRO *) sp->data;
	return NULL;
}
int
enter_macro()
{	LIST	*lp = argv[1].l_list;
	char	*macro_name;
	static LIST dummy_macro = F_HALT;
	LIST	*macro_body = argv[2].l_list;
	
	if (*lp == F_STR)
		macro_name = (char *) LGET32(lp);
	else if (*lp == F_ID)
		macro_name = builtin[LGET16(lp)].name;
	else
		return 0;
	
	return ins_macro(macro_name, 
		macro_body ? macro_body : &dummy_macro, 0);
}
int
ins_macro(name, list, flags)
char	*name;
LIST	*list;
int	flags;
{	register MACRO	*mptr;
	MACRO	*mp_new;
	extern	BUILTIN *lookup_builtin();
	BUILTIN	*bp = lookup_builtin(name);

	if (mptr = lookup_macro(name)) {
		int	f = mptr->m_flags & M_AUTOLOAD;
		if (f == 0) {
			if (bp == NULL)
				delete_macro(mptr->m_list);
			else {
				mp_new = (MACRO *) chk_alloc(sizeof (MACRO));
				mp_new->m_next = mptr;
				mptr = mp_new;
				mptr->m_flags = 0;
				bp->first_macro = mptr;
				bp->macro = mptr;
				bp->flags |= B_REDEFINE;
				}
			}
		}
	else {
		SPBLK *sp = (SPBLK *) spblk(sizeof (MACRO));
		mptr = (MACRO *) sp->data;
		mptr->m_name = strdup(name);
		sp->key = mptr->m_name;
		sp->data = (char *) mptr;
		mptr->m_next = NULL;
		mptr->m_flags = 0;
		if (bp) {
			bp->first_macro = mptr;
			bp->macro = mptr;
			bp->flags |= B_REDEFINE;
			}
		spenq(sp, macro_tbl);
		macro_cnt++;
		}
	mptr->m_ftime = TRUE;
	mptr->m_list = list;
	mptr->m_flags = (u_int16) flags;
	init_defined |= strcmp(name, "_init") == 0;
	return 0;
}
int
delete_macro(list)
register LIST	*list;
{
	return 0;
}
/**********************************************************************/
/*   Function  called  at  startup  to  get the show going...need to  */
/*   load the crisp' macro and execute it.			      */
/**********************************************************************/
int
startupfile() 
{
	char		*bpath = ggetenv("BPATH");

	fp_ptr = &fps[0]-1;

	if (bpath == NULL || strcmp(bpath, "/") == 0)
		bpath = NULL;

	str_exec("crisp");
 
	/***********************************************/
	/*   If  crisp  macro  not  defined  then  we  */
	/*   must   have   had  a  problem  with  our  */
	/*   environment   variables,   so   let  the  */
	/*   caller know what happened.		       */
	/***********************************************/
	return lookup_macro("crisp") == NULL ? -1 : 0;
}
int
read_macro(file_name)
char	*file_name;
{	int	len = strlen(file_name);
	int	ret;
	char	buf[128];
	int	ext = strcmp(file_name + len - 3, ".cm") == 0 ||
			strcmp(file_name + len - 3, ".cr") == 0 ||
			strcmp(file_name + len - 2, ".m") == 0;
			
	/***********************************************/
	/*   If  an  extension  is specified, then go  */
	/*   directly for that file.		       */
	/***********************************************/
	if (ext) {
		strcpy(buf, file_name);
		ret = read_macro1(file_name);
		if (ret >= 0)
			goto end_of_function;
		}
		
	/***********************************************/
	/*   We  didn't  have  an  extension,  so now  */
	/*   try  .cm,  .cr,  and  .m  files  in that  */
	/*   order.    (.cr    files    only   whilst  */
	/*   executing find_macro()).		       */
	/***********************************************/
	sprintf(buf, "%s.cm", file_name);
	if ((ret = read_macro1(buf)) >= 0)
		goto end_of_function;
		
	if (finding_macro) {
		sprintf(buf, "%s.cr", file_name);
		if ((ret = read_macro1(buf)) >= 0)
			goto end_of_function;
		}

	sprintf(buf, "%s.m", file_name);
	ret = read_macro1(buf);
	
end_of_function:
	/***********************************************/
	/*   If   we  had  a  successfull  load,  and  */
	/*   we're    executing    the   find_macro()  */
	/*   primitive,  then  assign  the  file name  */
	/*   to the accumulator.		       */
	/***********************************************/
	if (finding_macro && ret >= 0)
		acc_assign_str(buf, -1);
	return ret;
}
int
read_macro1(filename)
char *filename;
{	int	ret;

	if (init_fp(TERMINAL, filename) >= 0) {
		if (finding_macro)
			return 1;
		init_defined = FALSE;
		ret = yyparse();
		if (init_defined)
			str_exec("_init");
		return ret ? 1 : 0;
		}
	return -1;
}		
void
autoload()
{	char	*module = get_str(1);
	LIST	*lp;

	for (lp = argv[2].l_list; *lp != F_HALT; lp = next_atom(lp)) {
		LISTV	result;
		int type = eval(lp, &result);
		char	*macro_name;

		if (type == F_LIT || type == F_STR)
			macro_name = result.l_str;
		else if (type == F_RSTR)
			macro_name = result.l_ref->r_ptr;
		else
			return;
		if (lookup_macro(macro_name) == NULL)
			ins_macro(macro_name, (LIST *) strdup(module), M_AUTOLOAD);
		}
}
void
load_macro()
{	char	*filename;
	char	buf[BUFSIZ];

	acc_assign_int((long) 0);
	if ((filename = get_arg1("Macro file: ", buf, sizeof buf)) == NULL)
		return;
	if (ld_macro(filename) == 0) {
		acc_assign_int((long) 1);
		infof("Macro file loaded successfully.");
		}
}
int
ld_macro(filename)
char	*filename;
{	int	ret;
	char	buf[128];
	char	buf1[128];
	char	fn[80];
	extern	char	*bpath;
	extern	int m_flag;
	char	*cp;
	char	*home;
	int	len = strlen(filename);

	if (len > sizeof fn - 20)
		goto error;
	memcpy(fn, filename, len + 1);
/*	if ((ret = read_macro(fn)) >= 0)
		return ret;
	if (fn[0] == '/')
		goto error;
*/		
	strcpy(buf1, bpath);
	for (cp = strtok(buf1, ";"); cp; cp = strtok((char *) NULL, ";")) {
		sprintf(buf, "%s/%s", cp, fn);
		if ((ret = read_macro(buf)) >= 0)
			return ret;
		}
	if ((ret = read_macro(fn)) >= 0)
		return ret;
	if ((home = ggetenv("HOME")) == NULL)
		goto error;
	sprintf(buf, "%s/%s", home, fn);
	if ((ret = read_macro(buf)) >= 0)
		return ret;
error:
	if (m_flag == FALSE && !finding_macro)
		infof("load_macro: file %s not found", fn);
	return -1;
}
/**********************************************************************/
/*   Macro  implementing  the  find_macro() primitive. Pretend to do  */
/*   a load_macro() but avoid any compilation or loading effort.      */
/**********************************************************************/
void
find_macro()
{
	finding_macro = TRUE;
	if (ld_macro(get_str(1)) < 0)
		acc_assign_str("", 0);
	finding_macro = FALSE;
}
