#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))) == NULL) {
       allerr ();
    }
    if ((m -> mname = (char *) Calloc (1, strlen (name) + 1)) == NULL) {
       allerr ();
    }

#ifndef ORIG
    while ((*def == ' ') || (*def == '\t'))   /* Leading whitespace ? */
        def++;   /* Yes, bump pointer */
#endif

    if ((m -> mvalue = (char *) Calloc (1, strlen (def) + 1)) == NULL) {
       allerr ();
    }
    strcpy (m -> mname, name);
#ifdef ORIG
    strcpy (m -> mvalue, DEF);   /* Why is DEF upper case ?!?!?!? */
#else
    strcpy (m -> mvalue, def);
#endif
    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 = NULL;
    int result = -1;
    extern void free ();

    DBUG_ENTER ("undefmac");
    DBUG_3 ("mundef", "undefine macro '%s'", name);
    while (m != NULL && !STRSAME (name, m -> mname)) {
       prev = m;
       m = m -> mnext;
    }
    if (m != NULL) {
       result = 0;
       if (prev == 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 != NULL; m = m -> mnext) {
       if (STRSAME (name, m -> mname)) {
           def = m -> mvalue;
           DBUG_3 ("mexp", "found expansion '%s'", def);
           break;
       }
    }
    DBUG_RETURN (def);
}
