#include <stdio.h>
#include "make.h"

/*
 * Macro processing
 */

/*
 * Perform macro substitution from 'orig' to 'dest'.
 * Return number of macro substitutions made.
 * A macro reference is in one of two forms:
 *		<MACCHAR>(macro-name)
 *  	or	<MACCHAR><single-character>
 *
 * "<MACCHAR><MACCHAR>" expands to a single '<MACCHAR>'
 */

mexpand (orig, dest, destsiz, macchar)
char *orig;
char *dest;
int destsiz;
char macchar;
{
    register char *s;
    register char *d;
    auto char mname[STRSIZ];
    register int di;
    register int count;
    /* MACRO *m; */

    DBUG_ENTER ("mexpand");
    di = count = 0;
    for (s = orig; *s;) {
	if (*s == macchar) {
	    if (*++s == macchar) {
		if (di < destsiz - 1) {
		    dest[di++] = *s++;
		}
		continue;
	    }
	    if (!*s) {
		break;
	    }
	    d = mname;
	    if (*s != '(') {
		*d++ = *s++;
	    } else {
		for (++s; *s && *s != ')';) {
		    *d++ = *s++;
		}
		if (*s != ')') {
		    puts ("Missed matching ')'");
		} else {
		    ++s;
		}
	    }
	    *d = 0;
	    if ((d = gmacro (mname)) == NULL) {
#ifdef VAXVMS || MSDOS
		/* Preserve old behavior.  Someone else can remove it */
		/* if desired.  The prefered behavior, at least for */
		/* unix types, is to simply ignore undefined macros. */
		/* Fred Fish, 28-Nov-85 */
		fputs ("Undefined macro: ", stderr);
		fputs (mname, stderr);
		fputc ('\n', stderr);
#endif	
	    } else {
		while (*d && di < (destsiz - 1)) {
		    dest[di++] = *d++;
		}
		++count;
	    }
	} else if (di < destsiz - 1) {
	    dest[di++] = *s++;
	}
    }
    dest[di] = 0;
    DBUG_3 ("mac", "expanded %d macros", count);
    DBUG_RETURN (count);
}

/*
 * Define a macro.
 * Give the macro called 'name' the string expansion 'def'.
 * Old macro-names are superseded, NOT replaced.
 */

void defmac (name, def)
char *name;
char *def;
{
    register MACRO *m;

    DBUG_ENTER ("defmac");
    DBUG_4 ("mdef", "define macro '%s' to be '%s'", name, def);
    if ((m = (MACRO *) Calloc (1, sizeof (MACRO))) == (MACRO *) NULL) {
	allerr ();
    }
    if ((m -> mname = (char *) Calloc (1, strlen (name) + 1)) == NULL) {
	allerr ();
    }
    if ((m -> mvalue = (char *) Calloc (1, strlen (def) + 1)) == NULL) {
	allerr ();
    }
    strcpy (m -> mname, name);
    strcpy (m -> mvalue, def);
    m -> mnext = mroot;
    mroot = m;
    DBUG_VOID_RETURN;
}


/*
 * undefmac - undefine a macro.
 * Return 0 if macro was successfully undefined, -1 if not found.
 */

int undefmac (name)
char *name;
{
    register MACRO *m = mroot;
    register MACRO *prev = (MACRO *) NULL;
    int result = -1;
    extern void free ();

    DBUG_ENTER ("undefmac");
    DBUG_3 ("mundef", "undefine macro '%s'", name);
    while (m != (MACRO *) NULL && !STRSAME (name, m -> mname)) {
	prev = m;
	m = m -> mnext;
    }
    if (m != (MACRO *) NULL) {
	result = 0;
	if (prev == (MACRO *) NULL) {
	    mroot = m -> mnext;
	} else {
	    prev -> mnext = m -> mnext;
	}
	free (m -> mname);
	free (m -> mvalue);
	free (m);
    }
    DBUG_RETURN (result);
}


/*
 * Lookup a macro called 'name'.
 * Return a pointer to its definition,
 * or NULL if it does not exist.
 */

char *gmacro (name)
char *name;
{
    register MACRO *m;
    register char *def = NULL;

    DBUG_ENTER ("gmacro");
    DBUG_3 ("mname", "look up macro '%s'", name);
    for (m = mroot; m != (MACRO *) NULL; m = m -> mnext) {
	if (STRSAME (name, m -> mname)) {
	    def = m -> mvalue;
	    DBUG_3 ("mexp", "found expansion '%s'", def);
	    break;
	}
    }
    DBUG_RETURN (def);
}
