/* These subroutines perform macro processing.
 *
 * $Log: macros.c,v $
 * Revision 1.2  1997/06/04 01:13:26  david
 * ANSI-fied the program.
 *
 */

#include <stdio.h>
#include <string.h>
#include "xasm.h"
#include "macros.h"

static char *RcsId = "$Id: macros.c,v 1.2 1997/06/04 01:13:26 david Exp $";

static struct macros *macroot = NULL; /* Pointer to first macro */

void macro( char *label, char *opcode, char *operand, char *lptr )
{
struct macros *tmac; /* Temporary macro pointer */
struct lines *mlptr, *lastline = NULL;
char *sptr;
	
if ( pass != 1 )
   {
   printl( (long) -1, NULL, -1, lptr );
   macste( 1 ); /* Skip to end of macro definition, printing */
   return;
   }

if ( findmac( label ) != NULL )
   {
   xerror( "macro: Multiply defined MACRO." );
   printl( (long) -1, NULL, -1, lptr );
   macste( 1 ); /* Skip to end of macro definition, printing */
   return;
   }
   
printl( (long) -1, NULL, 1, lptr ); /* Print line */
tmac = newmac( label ); /* Allocate space and init. new macro */
macparse( tmac, operand ); /* Parse macro operands */

/* Now copy lines into macro body */
do {
   if ( (lptr = getline()) == NULL )
      {
      xerror( "macros: Encountered End-Of-File while searching for 'endm'." );
      return;
      }
      
   if ( (sptr = (char *) malloc( strlen( lptr ) ) ) == NULL )
      {
      xerror( "macros: Out of Memory." );
      exit( 1 );
      }
   strcpy( sptr, lptr ); /* Save line in memory */
   
   if ( (mlptr = (struct lines *) malloc( sizeof(struct lines) ) ) == NULL )
      {
      xerror( "macros: Out of Memory." );
      exit( 1 );
      }
   mlptr->line = line;
   mlptr->lptr = sptr;
   mlptr->next = NULL;

   if ( tmac->maclines == NULL )
      tmac->maclines = mlptr;

   if ( lastline != NULL )
      lastline->next = mlptr;
   lastline = mlptr;
   
   doparse( lptr, &label, &opcode, &operand );
   lower( opcode );
   } while ( strcmp( opcode, "endm" ) != 0 );
}

/* Parse the operands into the macros parameters list */
void macparse( struct macros *macptr, char *operand )
{
char tparm[80]; /* Maximum of 80 characters for parameter */
struct parms *tpptr, *lastparm = NULL; /* Pointer to a new parameter */

while ( *operand != '\0' )
   {
   while ( *operand == ' ' ) /* Skip past any spaces */
      operand++;
   
   if ( parse( &operand, tparm, sizeof(tparm)-1, ',' ) )
      {
      xerror( "macparse: Invalid Macro Parameter." );
      return;
      }

   if ( (tpptr = (struct parms *) malloc( sizeof(struct parms) ) ) == NULL )
      {
      xerror( "macparse: Out of Memory." );
      exit( 1 );
      }
      
   if ( macptr->parm == NULL )
      macptr->parm = tpptr;
	 
   tpptr->parname = strdup( tparm ); /* Initialize parameter */
   if ( tpptr->parname == NULL )
      {
      xerror( "macparse: Out of Memory." );
      exit( 1 );
      }
   tpptr->next = NULL; /* Initialize pointer to next */
      
   /* Add parameter on to end of parameter list */
   if ( lastparm != NULL )
      lastparm->next = tpptr;
   lastparm = tpptr;
   operand++; /* Bump operand ptr past ',' */
   } /* End Forever Loop */
}

/* Create a new macro */
struct macros *newmac( char *macname )
{
register struct macros *tmac, *tptr;
	
if ( (tmac = (struct macros *) malloc( sizeof(struct macros) ) ) == NULL )
   {
   xerror( "macros: Out of Memory." );
   exit( 1 );
   }
tmac->name = strdup( macname ); /* Initialize name of macro */
if ( tmac->name == NULL )
   {
   xerror( "newmac: Out of memory." );
   exit( 1 );
   }
tmac->line = line; /* Initialize macro definition line number */
tmac->parm = NULL; /* Initialize parameter list */
tmac->maclines = NULL; /* Initialize macro body list */
tmac->next = NULL; /* Initialize pointer to next macro */

/* Now insert macro into list of macros */
if ( macroot == NULL )
   macroot = tmac;
 else
   for ( tptr = macroot; tptr != NULL; tptr = tptr->next )
      {
      if ( strcmp( tmac->name, tptr->name ) > 0 )
         { /* Insert macro into list */
         tmac->next = tptr->next; /* Save old pointer */
         tptr->next = tmac; /* Make new pointer following old */
         break;
         }
      }
   
return tmac; /* Return pointer to new macro definition */
}

/* Skip to end of MACRO */
void macste( int printflag )
{
char *lptr, *label, *opcode, *operand;
	
do {
   if ( (lptr = getline()) == NULL )
      {
      xerror( "macro: Encountered End-Of-File while searching for 'endm'." );
      return;
      }
   if ( printflag )
      printl( (long) -1, NULL, -1, lptr );
   doparse( lptr, &label, &opcode, &operand );
   lower( opcode );
   } while ( strcmp( opcode, "endm" ) );
}

struct macros *findmac( char *macname )
{
struct macros *tmac = macroot; /* Initialize to start of list */
	
while ( tmac != NULL )
   {
   if ( strcmp( tmac->name, macname ) == 0 )
      return tmac;
   tmac = tmac->next;
   }
return NULL;
}

/* Perform a MACRO expansion */
void domac( char *label, struct macros *macptr, char *operand, char *lptr1 )
{
char lptr[81]; /* Line for expanding macros */
char *opcode;
struct parms *parmptr; /* Pointer to formal parameters */
struct lines *aparmroot = NULL, *tparm; /* Pointer to list of actual parms */
struct lines *lineptr; /* Pointer to next macro line */

labproc( label );

printl( (long) -1, NULL, 1, lptr1 );

/* Now Get actual parameters */
while ( *operand != '\0' )
   {
   parse( &operand, lptr, sizeof(lptr), ',' );
   makeaparm( &aparmroot, lptr );
   operand++; /* Skip past ',' */
   }

/* Now for each line in the macro substitute formal parameters (if any)
   with actual parameters and do the line.
*/
for ( lineptr = macptr->maclines; lineptr != NULL; lineptr = lineptr->next )
   {
   strcpy( lptr, lineptr->lptr ); /* Copy line of macro into work area */

   /* For each Formal Parameter */
   parmptr = macptr->parm;
   tparm = aparmroot;
   while ( 1 )
      {
      if ( parmptr == NULL || tparm == NULL )
         break;

      /* Replace the formal parameter with the actual parameter */
      srchrep( lptr, sizeof(lptr), parmptr->parname, tparm->lptr );

      parmptr = parmptr->next;
      tparm = tparm->next;
      }
   
   if ( parmptr != NULL && tparm != NULL )
      xerror( "domac: Differing number of formal and actual parameters." );

   doparse( lptr, &label, &opcode, &operand );
   lower( opcode );
   if ( strcmp( opcode, "endm" ) != 0 )
      doline( lptr );
    else
      break;
   }
}

/* This routine creates a new actual parameter and adds it to the list */
void makeaparm( struct lines **parmroot, char *parm )
{
struct lines *temparm, *tptr;
char *tempstr;

if ( (tempstr = (char *) malloc( strlen( parm ) + 1 )) == NULL )
   {
   fprintf( stderr, "makeaparm: Out of Memory." );
   exit( 1 );
   }

strcpy( tempstr, parm );

if ( (temparm = (struct lines *) malloc( sizeof(struct lines) ) ) == NULL )
   {
   fprintf( stderr, "makeaparm: Out of Memory." );
   exit( 1 );
   }

temparm->lptr = tempstr;
temparm->next = NULL;

if ( *parmroot == NULL )
   *parmroot = temparm;
 else
   {
   for ( tptr = *parmroot; tptr->next != NULL; tptr = tptr->next )
      ;
   tptr->next = temparm;
   }
}
