#include "make.h"

char *stpchr();

ExpandMacros( source, target, maxlen, tnode, newer)
char *source;
char *target;
int maxlen;
TNODE *tnode;
STRNODE *newer;
   {
   char *subs, *sp, tbuf[MAXBUFF], *p;
   STRNODE *strptr;
   char macrotype, specialpart, *lastdot;
   int len , tocopy;

   while( (p=stpchr(source, ESCAPE)) != NULL)
      {
      tstbreak();

      tocopy = p - source;
      if (tocopy > maxlen) tocopy = maxlen;
      movmem(source, target, tocopy);
      maxlen -= tocopy;
      target += tocopy;

      source = p + 1;

      macrotype = 0;
      specialpart = 0;

      if (stpchr("(*@<?%+", *source) != NULL)
         {
         if ((macrotype = *source) == '(')
            {
            /* special modifier macro? */
            if (stpchr("*@<?%+", *++source) != NULL)
               {
               macrotype = *source++;
               if (*source != ')')
                  {
                  specialpart = *source++;
                  if ((specialpart != 'D' && specialpart != 'F') ||
                      *source != ')' || macrotype == '?')
                     err("invalid macro call '%s'\n",source-4);
                  }
               }
            else
               {
               /* just a normal macro name, so locate it */
               if ( (p=stpchr(source, ')')) == NULL)
                  err("Missing ')' on macro '%s'", source-2);
               }
            }

         /* now process the macro we have */
#ifdef DEBUG
         if (DebugMode)
            {
            msg("Macro expanpanding macro type '%lc'\n", macrotype);
            if (macrotype == '(')
               {
               *p = 0;
               msg("      for '%s'\n", source);
               *p = ')';
               }
            }
#endif
         switch( macrotype )
            {
            case '(':  /* normal macro expansion */
               *p=0;    /* temporarily null out end of macro */
               if ((subs = (char *)find(source, Macros)) != NULL)
                  {
                  ExpandMacros(subs, target, maxlen, tnode, newer);
                  len = strlen(target);
                  target += len;
                  maxlen -= len;
                  }
               *p=')';  /* restore the parenthesis we nulled out */
               source = p;
               break;

            case '@':  /* name of current target */
               /* expand any macros in the name over */
               /* change so we pass the name later on */
               SimpleMacro(tnode->targets->string, tbuf, MAXBUFF);

               /* copy over the result to the targetut string */
               for (sp=tbuf; *sp && maxlen; *target++=*sp++, maxlen--);
               break;

            case '*':  /* root of current target */
               /* this is the same as $@ without the .c or whatever */
               /* expand any macros in the name over */
               /* change so we pass the name later on */
               SimpleMacro(tnode->targets->string, tbuf, MAXBUFF);

               /* copy over the result to the target string */
               lastdot = NULL;
               for (sp=tbuf; *sp && maxlen; *target++=*sp++, maxlen--)
                  if (*sp == '.')
                     lastdot = target;

               /* back it up to the last . found to strip off the suffix */
               if (lastdot != NULL)
                  {
                  maxlen += (target-lastdot);
                  target = lastdot;
                  }
               break;

            case '<':  /* current constructed target */
               /* expand any macros in the name over */
               /* change so we pass the name later on */
               SimpleMacro(tnode->depends->string, tbuf, MAXBUFF);

               /* copy over the result to the targetut string */
               for (sp=tbuf; *sp && maxlen; *target++=*sp++, maxlen--);
               break;

            case '?':  /* all modified sources for target */
            case '+':  /* all sources for the current rule */
               strptr = (macrotype == '?') ? newer : tnode->depends;

               /* copy over the sources one at a time */
               for (; strptr != NULL && maxlen>0; strptr=strptr->next)
                  {
                  SimpleMacro(strptr->string, tbuf, MAXBUFF);
                  for (sp=tbuf; *sp && maxlen; *target++=*sp++, maxlen--);

                  if (maxlen>0)
                     {
                     maxlen--;
                     *target++ = ' ';
                     }
                  }
               break;
            default:
               err("Invalid macro expansion '%lc'", macrotype);
            }
         source++;
         }
      else
         {
         /* copy over the single escaped character */
         if (maxlen > 0)
            {
            *target++ = *source++;
            maxlen--;
            }
         }
      }

   /* copy over the remainder of the string */
   stccpy(target, source, maxlen);
   }


/*-------------------------------------------------------------------------*/
int SimpleMacro( source, target, bufflen )
char *source;
char *target;
int  bufflen;
   {
   char *p, *subs;
   int tocopy;
   int len, maxlen;


   maxlen = bufflen;

#ifdef DEBUG
   if (DebugMode)
      msg("Expanding '%s'\n", source);
#endif
   tstbreak();

   while ( (p=stpchr(source, ESCAPE)) != NULL)
      {
      tstbreak();

      tocopy = p - source;
      if (tocopy > maxlen) tocopy = maxlen;
      movmem(source,target,tocopy);
      maxlen -= tocopy;
      target += tocopy;

      source = p+1;

      if (*source == '(')
         {
         if ( (p=stpchr(++source, ')')) == NULL)
            err("Missing ')' on macro '%s'", source-2);

         if (p != source)
            {
            *p=0;    /* temporarily null out end of macro */
            if ((subs = (char *)find(source, Macros)) != NULL)
               {
               len = SimpleMacro(subs, target, maxlen);
               target += len;
               maxlen -= len;
               }
            *p=')';  /* restore the parenthesis we nulled out */
            }
         }
      else
         {
         /* copy over the single escaped character */
         if (maxlen > 0)
            {
            *target++ = *++p;
            maxlen--;
            }
         }
      source = p+1;
      }

   /* copy over the remainder of the string */
   maxlen -= (stccpy(target, source, maxlen) - 1);

   return(bufflen-maxlen);
   }
