/
Sapphire/bin/
Sapphire/db/
Sapphire/db/OLC_rooms/
Sapphire/db/abi/
Sapphire/db/em_src/
Sapphire/db/helps/
Sapphire/db/helps/emman/ifunc/
Sapphire/db/npcs/Tatt/
Sapphire/db/objects/Tatt/
Sapphire/db/q_data/
Sapphire/db/rooms/Tatt/
Sapphire/doc/
Sapphire/doc/em/
Sapphire/etc/
Sapphire/src/abic/
Sapphire/src/areacon/
Sapphire/src/client/
Sapphire/src/embc/
Sapphire/src/emi/
Sapphire/src/emi/test/
Sapphire/src/include/
Sapphire/src/sapphire/em/
Sapphire/src/tcon/
/*
 * Copyright (C) 1995-1997 Christopher D. Granz
 *
 * This header may not be removed.
 *
 * Refer to the file "License" included in this package for further
 * information and before using any of the following.
 */

#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>

#include "emerald.h"


/*
 * Structures
 */
typedef struct _fake_value
{
    intt                                                          iType;
    int                                                iArrayDimensions;
    intt                                                iArrayFinalType;
} fake_value;


/*
 * Prototypes
 */
static void      fake_var_stack_push                   ( fake_value * );
static void      fake_var_stack_pop                       ( short int );
static bool      fake_var_stack_overflow                       ( void );
static int       fake_var_stack_usage                          ( void );
static void      fake_interp_stack_push                ( fake_value * );
static void      fake_interp_stack_pop                    ( short int );
static bool      fake_interp_stack_overflow                    ( void );
static int       fake_interp_stack_usage                       ( void );


/*
 * Globals
 */
char                                                    cErrorBuf[1024];
int                                                            iEmError;
jmp_buf                                                     jbAbortJump;

static FILE *                                                pUsageFile;

static fake_value *                                          pFakeStack;
static fake_value *                                       pFakeStackEnd;
static fake_value *                                 pFakeInterpStackPos;
static fake_value *                                  pFakeVarStackBegin;
static fake_value *                                    pFakeVarStackPos;

extern int                                                 iCurrentFunc;

extern char ***                                          pppSymbolTable;


/*
 * Functions
 */
void print_error( void )
{
    int i;

    for ( i = 0; cErrorBuf[i] != '\0'; i++ )
    {
        if ( i == 75 )
        {
            while ( cErrorBuf[i] != ' ' )
            {
                if ( i == 0 )
                {
                 /* fprintf( stderr, "\nNo spaces in log string.\n" ); */
                    break;
                }

                i--;
            }

            cErrorBuf[i] = '\n';
            break;
        }
    }

    lprintf( "**** [EMERALD]:\n  %s", cErrorBuf );
}


/*
 * Used to setup a timer for a time limit on the Emerald Interpreter.
 */
void em_set_timer( int iMilSec )
{
    struct itimerval itv;

    itv.it_interval.tv_sec  = 0;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec     = 0;
    itv.it_value.tv_usec    = iMilSec;

    if ( setitimer( ITIMER_PROF, &itv, NULL ) < 0 )
        sap_fatal( "Set timer: %s.", strerror( errno ) );
}


void em_clear_timer( void )
{
    struct itimerval itv;

    itv.it_interval.tv_sec  = 0;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec     = 0;
    itv.it_value.tv_usec    = 0;

    if ( setitimer( ITIMER_PROF, &itv, NULL ) < 0 )
        sap_fatal( "Set timer: %s.", strerror( errno ) );
}


/*
 * Proforms all byte code error checking.
 */
void error_check( void )
{
    fake_value fv;
    byte *p;
    int i;

    if ( ppFuncs == NULL )
        return;

    if ( bEmFuncUsage == TRUE
      && ( pUsageFile = fopen( pEmUsageFilename, "w" ) ) == NULL )
    {
        sprintf( cErrorBuf, "Open file: %s: %s.", pEmUsageFilename,
          strerror( errno ) );
        print_error( );
        bEmFuncUsage = FALSE;
    }

    /*
     * Init the fake stacks.
     */
    if ( iEmInterpStackSize > 0 || iEmVarStackSize > 0 )
    {
        pFakeStack          = alloc_mem( sizeof( fake_value )
                                * ( iEmInterpStackSize
                                + iEmVarStackSize ) );
        pFakeStackEnd       = ( pFakeStack + iEmInterpStackSize
                              + iEmVarStackSize - 1 );
        pFakeInterpStackPos = pFakeStack;
        pFakeVarStackBegin  = ( pFakeStack + iEmInterpStackSize );
        pFakeVarStackPos    = pFakeVarStackBegin;
    }

    iEmError                = 0;

    for ( iCurrentFunc = 0; iCurrentFunc <= siTopFuncIndex;
      iCurrentFunc++ )
    {
        if ( bEmFuncUsage == TRUE )
            fprintf( pUsageFile, "%s() uses:\n",
              ppFuncs[iCurrentFunc]->pName );

        /*
         * Push some fake arguments on the fake interpreter stack.
         * Nothing in error_check() is "real" ;) 
         */
        for ( i = 0; i < ppFuncs[iCurrentFunc]->iNumArgs; i++ )
        {
            fv.iType            = ppFuncs[iCurrentFunc]->pArgTypes[i];
            fv.iArrayDimensions = ppFuncs[iCurrentFunc]->pArgArrayDim[i];
            fv.iArrayFinalType = ppFuncs[iCurrentFunc]->pArgArrayTypes[i];
            fake_interp_stack_push( &fv );
        }

        p                   = ppFuncs[iCurrentFunc]->pInstrStream;

        while ( ( p - ppFuncs[iCurrentFunc]->pInstrStream )
          < ppFuncs[iCurrentFunc]->ulCodeSize )
        {
            p               = bcec( p );

            if ( iEmError < 0 )
            {
                print_error( );
                exit( 1 );
            }
        }

        bcec( p ); /* Validate return arguments. */

        if ( iEmError < 0 )
        {
            print_error( );
            exit( 1 );
        }

        /*
         * Free the fake interpreter and variable stacks so they're
         * ready for the next function to use.
         */
        fake_interp_stack_pop( fake_interp_stack_usage( ) );
        fake_var_stack_pop( fake_var_stack_usage( ) );

        if ( bEmFuncUsage == TRUE )
            putc( '\n', pUsageFile );
    }

    if ( pFakeStack != NULL )
        free_mem( (void **) &pFakeStack );

    if ( bEmFuncUsage == TRUE )
        fclose( pUsageFile );
}


/*
 * Push a new fake variable onto the fake variable stack.
 */
static void fake_var_stack_push( fake_value *p )
{
    if ( fake_var_stack_overflow( ) == TRUE )
    {
        sprintf( cErrorBuf,
          "Error checker: Variable stack will overflow in %s().",
          ppFuncs[iCurrentFunc]->pName );
        iEmError        = -1;
        return;
    }

    *pFakeVarStackPos++ = *p;
}


/*
 * Pop (delete) a number of fake variables off the fake variable
 * stack.
 */
static void fake_var_stack_pop( short int siNumber )
{
    while ( siNumber-- > 0 )
        --pFakeVarStackPos;
}


/*
 * Check for overflow in the fake variable stack.
 */
static bool fake_var_stack_overflow( void )
{
    if ( pFakeVarStackPos > pFakeStackEnd )
        return ( TRUE );
    else
        return ( FALSE );
}


/*
 * Returns the number of values currently on the fake variable stack.
 */
static int fake_var_stack_usage( void )
{
    return ( ( pFakeVarStackPos - pFakeVarStackBegin ) );
}


/*
 * Push a new value onto the fake interpreter stack.
 */
static void fake_interp_stack_push( fake_value *p )
{
    if ( fake_interp_stack_overflow( ) == TRUE )
    {
        sprintf( cErrorBuf,
          "Error checker: Interpreter stack will overflow in %s().",
          ppFuncs[iCurrentFunc]->pName );
        iEmError           = -2;
        return;
    }

    *pFakeInterpStackPos++ = *p;
}


/*
 * Pop a number of values off the fake interpreter stack.
 */
static void fake_interp_stack_pop( short int siNumber )
{
    while ( siNumber-- > 0 )
        --pFakeInterpStackPos;
}


/*
 * Check for overflow in the fake interpreter stack.
 */
static bool fake_interp_stack_overflow( void )
{
    if ( pFakeInterpStackPos > pFakeVarStackBegin )
        return ( TRUE );
    else
        return ( FALSE );
}


/*
 * Returns the number of values currently on the fake interpreter
 * stack.
 */
static int fake_interp_stack_usage( void )
{
    return ( ( pFakeInterpStackPos - pFakeStack ) );
}


/*
 * Macros for getting values (constants) off an instruction byte stream.
 */
#define GET_INT1( p )                              ( *( (byte *) (p) ) )
#define GET_INT2( p )                                ( *( (i2 *) (p) ) )
#define GET_INT4( p )                                ( *( (i4 *) (p) ) )
#define GET_FLOAT4( p )                              ( *( (f4 *) (p) ) )
#define GET_FLOAT8( p )                              ( *( (f8 *) (p) ) )

/*
 * Macros for moving instruction byte stream pointers.
 */
#define MOVEP_1( p )                                           ( (p)++ )
#define MOVEP_2( p )                             ( (p) += sizeof( i2 ) )
#define MOVEP_4( p )                             ( (p) += sizeof( i4 ) )
#define MOVEP_8( p )                             ( (p) += sizeof( f8 ) )


/*
 * BCEC - Byte Code Error Check
 *
 * Examines a byte code and finds any errors.
 */
#define p2                                   ( pFakeInterpStackPos - 1 )

byte *bcec( byte *pInstrStream )
{
    fake_value fv       = { 0, 0, 0 };
    fake_value *p;
    long l              = 0L;
    long l2             = 0L;
    long l3             = 0L;

    switch ( *pInstrStream++ )
    {
      case INSTR_END                :
          if ( fake_interp_stack_usage( )
            < ppFuncs[iCurrentFunc]->iNumReturn )
          {
              sprintf( cErrorBuf, "Error checker: "
                "Inconsistent number of return values in %s().",
                ppFuncs[iCurrentFunc]->pName );
              iEmError  = -3;
              break;
          }

          for ( l = 0L; l < ppFuncs[iCurrentFunc]->iNumReturn; l++ )
          {
              if ( ( pFakeInterpStackPos - ( l + 1 ) )->iType
                != ppFuncs[iCurrentFunc]->pReturnTypes[l] )
              {
                  sprintf( cErrorBuf,
                    "Error checker: Inconsistent return types in %s().",
                    ppFuncs[iCurrentFunc]->pName );
                  iEmError = -3;
                  break;
              }
          }

          break;

      case INSTR_NEW_INT            :
          fv.iType      = TYPE_INT;
          fake_var_stack_push( &fv );
          break;

      case INSTR_NEW_FLOAT          :
          fv.iType      = TYPE_FLOAT;
          fake_var_stack_push( &fv );
          break;

      case INSTR_NEW_STRING         :
          fv.iType                  = TYPE_STRING;
          fv.iArrayFinalType        = TYPE_INT;
          fake_var_stack_push( &fv );
          break;

      case INSTR_NEW_OBJECT         :
          fv.iType      = TYPE_OBJECT;
          fake_var_stack_push( &fv );
          break;

      case INSTR_NEW_ARRAY          :
          fv.iType            = TYPE_ARRAY;
          fv.iArrayDimensions = 1;
          fv.iArrayFinalType  = GET_INT1( pInstrStream );

          while ( ( fv.iArrayFinalType = GET_INT1( pInstrStream ) )
            == TYPE_ARRAY )
          {
              MOVEP_1( pInstrStream );
              fv.iArrayDimensions++;
          }

          fake_var_stack_push( &fv );
          break;

      case INSTR_PUSH_ZERO          :
          fv.iType      = TYPE_INT;
          fake_interp_stack_push( &fv );
          break;

      case INSTR_PUSH_ONE           :
          fv.iType      = TYPE_INT;
          fake_interp_stack_push( &fv );
          break;

      case INSTR_PUSH_INT1          :
          fv.iType      = TYPE_INT;
          MOVEP_1( pInstrStream );
          fake_interp_stack_push( &fv );
          break;

      case INSTR_PUSH_INT2          :
          fv.iType      = TYPE_INT;
          MOVEP_2( pInstrStream );
          fake_interp_stack_push( &fv );
          break;

      case INSTR_PUSH_INT4          :
          fv.iType      = TYPE_INT;
          MOVEP_4( pInstrStream );
          fake_interp_stack_push( &fv );
          break;

      case INSTR_PUSH_FLOAT4        :
          fv.iType      = TYPE_FLOAT;
          MOVEP_4( pInstrStream );
          fake_interp_stack_push( &fv );
          break;

      case INSTR_PUSH_FLOAT8        :
          fv.iType      = TYPE_FLOAT;
          MOVEP_8( pInstrStream );
          fake_interp_stack_push( &fv );
          break;

      case INSTR_PUSH_STRING        :
          fv.iType            = TYPE_STRING;
          fv.iArrayFinalType  = TYPE_INT;
          l                   = GET_INT1( pInstrStream );
          MOVEP_2( pInstrStream );
          pInstrStream       += l;
          fake_interp_stack_push( &fv );
          break;

      case INSTR_PUSH_LOCAL         :
          l                   = GET_INT2( pInstrStream );
          MOVEP_2( pInstrStream );

          if ( ( fake_var_stack_usage( ) - 1 ) < l )
          {
              sprintf( cErrorBuf, "Error checker: Reference "
                "of non-existent variable at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 3 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError        = -4;
              break;
          }

          p                   = ( pFakeVarStackBegin + l );
          fv.iType            = p->iType;
          fv.iArrayDimensions = p->iArrayDimensions;
          fv.iArrayFinalType  = p->iArrayFinalType;
          fake_interp_stack_push( &fv );
          break;

      case INSTR_PUSH_INDEX         :
          if ( fake_interp_stack_usage( ) < 2 )
          {
              sprintf( cErrorBuf, "Error checker: Too few values on "
                "interpreter stack for operation at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -5;
              break;
          }

          p            = ( pFakeInterpStackPos - 2 );

          if ( p->iType != TYPE_STRING && p->iType != TYPE_ARRAY )
          {
              sprintf( cErrorBuf, "Error checker: "
                "Index reference in non-array at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -5;
              break;
          }

          if ( p2->iType != TYPE_INT )
          {
              sprintf( cErrorBuf, "Error checker: "
                "Index reference with wrong type at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -5;
              break;
          }

          if ( p->iType == TYPE_STRING )
          {
              fv.iType            = TYPE_STRING;
              fv.iArrayFinalType  = p->iArrayFinalType;
          }
          else if ( p->iArrayDimensions > 1 )
          {
              fv.iType            = TYPE_ARRAY;
              fv.iArrayDimensions = ( p->iArrayDimensions - 1 );
              fv.iArrayFinalType  = p->iArrayFinalType;
          }
          else
              fv.iType            = p->iArrayFinalType;

          fake_interp_stack_pop( 2 );
          fake_interp_stack_push( &fv );
          break;

      case INSTR_POP                :
          if ( fake_interp_stack_usage( ) < 1 )
          {
              sprintf( cErrorBuf, "Error checker: Too few values on "
                "interpreter stack for operation at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -6;
              break;
          }

          fake_interp_stack_pop( 1 );
          break;

      case INSTR_ASSIGN_LOCAL       :
          l                   = GET_INT2( pInstrStream );
          MOVEP_2( pInstrStream );

          if ( ( fake_var_stack_usage( ) - 1 ) < l )
          {
              sprintf( cErrorBuf, "Error checker: Reference "
                "of non-existent variable at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 3 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -7;
              break;
          }

          p          = ( pFakeVarStackBegin + l );

          if ( fake_interp_stack_usage( ) < 1 )
          {
              sprintf( cErrorBuf, "Error checker: Too few values on "
                "interpreter stack for operation at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 3 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -7;
              break;
          }

          if ( p->iType != p2->iType )
          {
              sprintf( cErrorBuf, "Error checker: Incompatiable types "
                "in assignment at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 3 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -7;
              break;
          }

          p->iType            = p2->iType;
          p->iArrayDimensions = p2->iArrayDimensions;
          p->iArrayFinalType  = p2->iArrayFinalType;
          fake_interp_stack_pop( 1 );
          break;

      case INSTR_ASSIGN_INDEX       :
          if ( fake_interp_stack_usage( ) < 3 )
          {
              sprintf( cErrorBuf, "Error checker: Too few values on "
                "interpreter stack for operation at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError        = -8;
              break;
          }

          p                   = ( pFakeInterpStackPos - 3 );

          if ( p->iType != TYPE_STRING && p->iType != TYPE_ARRAY )
          {
              sprintf( cErrorBuf, "Error checker: "
                "Index reference in non-array at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError        = -8;
              break;
          }

          if ( ( pFakeInterpStackPos - 2 )->iType != TYPE_INT )
          {
              sprintf( cErrorBuf, "Error checker: "
                "Index reference with wrong type at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError        = -8;
              break;
          }

          if ( ( p->iType == TYPE_ARRAY
            && ( p->iArrayDimensions > 1 ? p2->iType != TYPE_ARRAY
            : p->iArrayFinalType != p2->iType ) )
            || ( p->iType == TYPE_STRING && p2->iType != TYPE_INT ) )
          {
              sprintf( cErrorBuf, "Error checker: Incompatiable types "
                "in assignment at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError        = -8;
              break;
          }

          fake_interp_stack_pop( 2 );
          break;

      case INSTR_MULTIPLY           :
      case INSTR_DIVIDE             :
      case INSTR_SUBTRACT           :
          if ( fake_interp_stack_usage( ) < 2 )
          {
              sprintf( cErrorBuf, "Error checker: Too few values on "
                "interpreter stack for operation at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -9;
              break;
          }

          if ( ( p2->iType != TYPE_INT && p2->iType != TYPE_FLOAT )
            || ( ( pFakeInterpStackPos - 2 )->iType != TYPE_INT
            && ( pFakeInterpStackPos - 2 )->iType != TYPE_FLOAT ) )
          {
              sprintf( cErrorBuf, "Error checker: Illegal "
                "type for math instruction at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -9;
              break;
          }

          if ( p2->iType != ( pFakeInterpStackPos - 2 )->iType )
          {
              sprintf( cErrorBuf, "Error checker: Incompatiable types "
                "in math instruction at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -9;
              break;
          }

          fake_interp_stack_pop( 1 );
          break;

      case INSTR_MODULUS            :
      case INSTR_LSHIFT             :
      case INSTR_RSHIFT             :
      case INSTR_AND                :
      case INSTR_XOR                :
      case INSTR_OR                 :
          if ( fake_interp_stack_usage( ) < 2 )
          {
              sprintf( cErrorBuf, "Error checker: Too few values on "
                "interpreter stack for operation at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -10;
              break;
          }

          if ( p2->iType != TYPE_INT
            || ( pFakeInterpStackPos - 2 )->iType != TYPE_INT )
          {
              sprintf( cErrorBuf, "Error checker: Illegal "
                "type for math instruction at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -10;
              break;
          }

          fake_interp_stack_pop( 1 );
          break;

      case INSTR_ADD                :
          if ( fake_interp_stack_usage( ) < 2 )
          {
              sprintf( cErrorBuf, "Error checker: Too few values on "
                "interpreter stack for operation at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -11;
              break;
          }

          if ( ( p2->iType != TYPE_INT && p2->iType != TYPE_FLOAT
            && p2->iType != TYPE_STRING )
            || ( ( pFakeInterpStackPos - 2 )->iType != TYPE_INT
            && ( pFakeInterpStackPos - 2 )->iType != TYPE_FLOAT
            && ( pFakeInterpStackPos - 2 )->iType != TYPE_STRING ) )
          {
              sprintf( cErrorBuf, "Error checker: Illegal "
                "type for math instruction at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -11;
              break;
          }

          if ( p2->iType != ( pFakeInterpStackPos - 2 )->iType )
          {
              sprintf( cErrorBuf, "Error checker: Incompatiable types "
                "in math instruction at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -11;
              break;
          }

          fake_interp_stack_pop( 1 );
          break;

      case INSTR_ONES_COMPLEMENT    :
          if ( fake_interp_stack_usage( ) < 1 )
          {
              sprintf( cErrorBuf, "Error checker: Too few values on "
                "interpreter stack for operation at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -12;
              break;
          }

          if ( p2->iType != TYPE_INT )
          {
              sprintf( cErrorBuf, "Error checker: Illegal "
                "type for math instruction at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -12;
              break;
          }

          break;

      case INSTR_JUMP               :
          /* We don't do jumps in the error checker. */
          l                        = GET_INT2( pInstrStream );
          MOVEP_2( pInstrStream );

          if ( l < 0 )
          {
              if ( -l > ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream ) )
                  goto jmp_instr_error;
          }
          else
          {
              if ( l > ( ppFuncs[iCurrentFunc]->ulCodeSize - 1 ) )
                  goto jmp_instr_error;
          }

          break;

jmp_instr_error:
          sprintf( cErrorBuf, "Error checker: Jump "
            "instruction points out of bounds at offset %ld in %s().",
            (long) ( pInstrStream - ppFuncs[iCurrentFunc]->pInstrStream
            - 2 ), ppFuncs[iCurrentFunc]->pName );
          iEmError                 = -13;
          break;

      case INSTR_COMPARE            :
          if ( fake_interp_stack_usage( ) < 2 )
          {
              sprintf( cErrorBuf, "Error checker: Too few values on "
                "interpreter stack for operation at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -14;
              break;
          }

          if ( ( pFakeInterpStackPos - 2 )->iType != p2->iType
            || p2->iType == TYPE_ARRAY /* Arrays can't be compared. */ )
          {
              sprintf( cErrorBuf, "Error checker: Incompatible "
                "types for compare instruction at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -14;
              break;
          }

          l                        = GET_INT2( pInstrStream );
          MOVEP_2( pInstrStream );

          if ( l < 0 )
              goto jmp_instr_error; /* No backward jumps with compare. */
          else
          {
              if ( l > ( ppFuncs[iCurrentFunc]->ulCodeSize - 1 ) )
                  goto jmp_instr_error;
          }

          switch ( GET_INT1( pInstrStream ) )
          {
            case COND_EQUAL           :
            case COND_NOT_EQUAL       : break;
            case COND_GREATER         :
            case COND_LESS            :
            case COND_GREATER_OR_EQUAL:
            case COND_LESS_OR_EQUAL   :
                if ( p2->iType != TYPE_INT && p2->iType != TYPE_FLOAT )
                {
                    sprintf( cErrorBuf, "Error checker: "
                      "Illegal conditional type at offset %ld in %s().",
                      (long) ( pInstrStream
                      - ppFuncs[iCurrentFunc]->pInstrStream - 3 ),
                      ppFuncs[iCurrentFunc]->pName );
                    iEmError = -14;
                    return ( pInstrStream );
                }

                break;

            default                   :
                sprintf( cErrorBuf, "Error checker: "
                  "Unknown conditional type at offset %ld in %s().",
                  (long) ( pInstrStream
                  - ppFuncs[iCurrentFunc]->pInstrStream - 3 ),
                  ppFuncs[iCurrentFunc]->pName );
                iEmError = -14;
                return ( pInstrStream );
          }

          MOVEP_1( pInstrStream );

          {
              fake_value *pInterpSav = pFakeInterpStackPos;
              fake_value *pVarSav    = pFakeVarStackPos;
              byte *pInstrSav        = pInstrStream;

              while ( *pInstrStream != INSTR_END )
                  pInstrStream       = bcec( pInstrStream );

              pInstrStream++; /* Skip past END instruction. */

              fake_interp_stack_pop( ( pFakeInterpStackPos
                - pInterpSav ) );
              fake_var_stack_pop( ( pFakeVarStackPos - pVarSav ) );

              if ( ( pInstrStream - pInstrSav ) < l )
              {
                  pInterpSav         = pFakeInterpStackPos;
                  pVarSav            = pFakeVarStackPos;

                  while ( *pInstrStream != INSTR_END )
                      pInstrStream   = bcec( pInstrStream );

                  pInstrStream++; /* Skip past END instruction. */

                  fake_interp_stack_pop( ( pFakeInterpStackPos
                    - pInterpSav ) );
                  fake_var_stack_pop( ( pFakeVarStackPos - pVarSav ) );
              }
          }

          break;

      case INSTR_CALL_BUILTIN_FUNC  :
          if ( fake_interp_stack_usage( ) < 1 )
          {
              sprintf( cErrorBuf, "Error checker: Too few values on "
                "interpreter stack for operation at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -15;
              break;
          }

          fake_interp_stack_pop( 1 );

          l                = GET_INT1( pInstrStream );
          MOVEP_1( pInstrStream );

          if ( fake_interp_stack_usage( ) < l )
          {
              sprintf( cErrorBuf, "Error checker: Too few arguments "
                "in call to builtin function at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 2 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -15;
              break;
          }

          for ( l2 = 2L, l3 = 1L; l > 0L; l--, l2++, l3++ )
          {
              fv.iArrayDimensions = -1;
              fv.iArrayFinalType  = 0;

              do
              {
                  fv.iType = GET_INT1( pInstrStream );
                  MOVEP_1( pInstrStream );
                  l2++;
                  fv.iArrayDimensions++;
              }
              while ( fv.iType == TYPE_ARRAY );

              switch ( fv.iType )
              {
                case TYPE_INT   :
                case TYPE_FLOAT :
                case TYPE_STRING:
                case TYPE_OBJECT: break;
                default         :
                    sprintf( cErrorBuf, "Error checker: "
                      "Unknown variable type at offset %ld in %s().",
                      (long) ( pInstrStream
                      - ppFuncs[iCurrentFunc]->pInstrStream - l2 ),
                      ppFuncs[iCurrentFunc]->pName );
                    iEmError = -15;
                    return ( pInstrStream );
              }

              if ( fv.iArrayDimensions > 0 )
              {
                  fv.iArrayFinalType = fv.iType;
                  fv.iType           = TYPE_ARRAY;
              }
              else if ( fv.iType == TYPE_STRING )
                  fv.iArrayFinalType = TYPE_INT;

              p              = ( pFakeInterpStackPos - l3 );

              if ( p->iType != fv.iType
                || p->iArrayDimensions != fv.iArrayDimensions
                || p->iArrayFinalType != fv.iArrayFinalType )
              {
                  sprintf( cErrorBuf, "Error checker: Incompatible type "
                    "in passing to builtin function at offset %ld "
                    "in %s().",
                    (long) ( pInstrStream
                    - ppFuncs[iCurrentFunc]->pInstrStream - l2 ),
                    ppFuncs[iCurrentFunc]->pName );
                  iEmError = -15;
                  return ( pInstrStream );
              }
          }

          l                = 0L;
          l                = GET_INT1( pInstrStream );
          MOVEP_1( pInstrStream );

          for ( l2++; l > 0L; l--, l2++ )
          {
              fv.iArrayDimensions = -1;
              fv.iArrayFinalType  = 0;

              do
              {
                  fv.iType = GET_INT1( pInstrStream );
                  MOVEP_1( pInstrStream );
                  l2++;
                  fv.iArrayDimensions++;
              }
              while ( fv.iType == TYPE_ARRAY );

              switch ( fv.iType )
              {
                case TYPE_INT   :
                case TYPE_FLOAT :
                case TYPE_STRING:
                case TYPE_OBJECT: break;
                default         :
                    sprintf( cErrorBuf, "Error checker: "
                      "Unknown variable type at offset %ld in %s().",
                      (long) ( pInstrStream
                      - ppFuncs[iCurrentFunc]->pInstrStream - l2 ),
                      ppFuncs[iCurrentFunc]->pName );
                    iEmError = -15;
                    return ( pInstrStream );
              }

              if ( fv.iArrayDimensions > 0 )
              {
                  fv.iArrayFinalType = fv.iType;
                  fv.iType           = TYPE_ARRAY;
              }
              else if ( fv.iType == TYPE_STRING )
                  fv.iArrayFinalType = TYPE_INT;

              fake_interp_stack_push( &fv );
          }

          break;

      case INSTR_CALL_FUNC          :
          if ( fake_interp_stack_usage( ) < 1 )
          {
              sprintf( cErrorBuf, "Error checker: Too few values on "
                "interpreter stack for operation at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 1 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -16;
              break;
          }

          fake_interp_stack_pop( 1 );

          l                = GET_INT1( pInstrStream );
          MOVEP_1( pInstrStream );

          if ( fake_interp_stack_usage( ) < l )
          {
              sprintf( cErrorBuf, "Error checker: Too few arguments "
                "in call to user function at offset %ld in %s().",
                (long) ( pInstrStream
                - ppFuncs[iCurrentFunc]->pInstrStream - 2 ),
                ppFuncs[iCurrentFunc]->pName );
              iEmError = -16;
              break;
          }

          for ( l2 = 2L, l3 = 1L; l > 0L; l--, l2++, l3++ )
          {
              fv.iArrayDimensions = -1;
              fv.iArrayFinalType  = 0;

              do
              {
                  fv.iType = GET_INT1( pInstrStream );
                  MOVEP_1( pInstrStream );
                  l2++;
                  fv.iArrayDimensions++;
              }
              while ( fv.iType == TYPE_ARRAY );

              switch ( fv.iType )
              {
                case TYPE_INT   :
                case TYPE_FLOAT :
                case TYPE_STRING:
                case TYPE_OBJECT: break;
                default         :
                    sprintf( cErrorBuf, "Error checker: "
                      "Unknown variable type at offset %ld in %s().",
                      (long) ( pInstrStream
                      - ppFuncs[iCurrentFunc]->pInstrStream - l2 ),
                      ppFuncs[iCurrentFunc]->pName );
                    iEmError = -16;
                    return ( pInstrStream );
              }

              if ( fv.iArrayDimensions > 0 )
              {
                  fv.iArrayFinalType = fv.iType;
                  fv.iType           = TYPE_ARRAY;
              }
              else if ( fv.iType == TYPE_STRING )
                  fv.iArrayFinalType = TYPE_INT;

              p              = ( pFakeInterpStackPos - l3 );

              if ( p->iType != fv.iType
                || p->iArrayDimensions != fv.iArrayDimensions
                || p->iArrayFinalType != fv.iArrayFinalType )
              {
                  sprintf( cErrorBuf, "Error checker: Incompatible type "
                    "in passing to user function at offset %ld in %s().",
                    (long) ( pInstrStream
                    - ppFuncs[iCurrentFunc]->pInstrStream - l2 ),
                    ppFuncs[iCurrentFunc]->pName );
                  iEmError = -16;
                  return ( pInstrStream );
              }
          }

          l                = 0L;
          l                = GET_INT1( pInstrStream );
          MOVEP_1( pInstrStream );

          for ( l2++; l > 0L; l--, l2++ )
          {
              fv.iArrayDimensions = -1;
              fv.iArrayFinalType  = 0;

              do
              {
                  fv.iType = GET_INT1( pInstrStream );
                  MOVEP_1( pInstrStream );
                  l2++;
                  fv.iArrayDimensions++;
              }
              while ( fv.iType == TYPE_ARRAY );

              switch ( fv.iType )
              {
                case TYPE_INT   :
                case TYPE_FLOAT :
                case TYPE_STRING:
                case TYPE_OBJECT: break;
                default         :
                    sprintf( cErrorBuf, "Error checker: "
                      "Unknown variable type at offset %ld in %s().",
                      (long) ( pInstrStream
                      - ppFuncs[iCurrentFunc]->pInstrStream - l2 ),
                      ppFuncs[iCurrentFunc]->pName );
                    iEmError = -16;
                    return ( pInstrStream );
              }

              if ( fv.iArrayDimensions > 0 )
              {
                  fv.iArrayFinalType = fv.iType;
                  fv.iType           = TYPE_ARRAY;
              }
              else if ( fv.iType == TYPE_STRING )
                  fv.iArrayFinalType = TYPE_INT;

              fake_interp_stack_push( &fv );
          }

          break;

      case TRANS_BUILTIN_FUNC       :
          if ( bEmFuncUsage == TRUE )
              fprintf( pUsageFile, "  Builtin %s();\n",
                pppSymbolTable[ppFuncs[iCurrentFunc]->siFileNumber]
                [GET_INT2( pInstrStream )] );

          MOVEP_2( pInstrStream );
          fv.iType      = TYPE_INT;
          fake_interp_stack_push( &fv );
          break;

      case TRANS_FUNC               :
          if ( bEmFuncUsage == TRUE )
              fprintf( pUsageFile, "  User %s();\n",
                pppSymbolTable[ppFuncs[iCurrentFunc]->siFileNumber]
                [GET_INT2( pInstrStream )] );

          MOVEP_2( pInstrStream );
          fv.iType      = TYPE_INT;
          fake_interp_stack_push( &fv );
          break;

      default                       :
          sprintf( cErrorBuf, "Error checker: "
            "Illegal instruction (0x%.2X) at offset %ld in %s().",
            (int) *--pInstrStream, (long) ( pInstrStream
            - ppFuncs[iCurrentFunc]->pInstrStream ),
            ppFuncs[iCurrentFunc]->pName );
          iEmError = -17;
          break;
    }

    return ( pInstrStream );
}

#undef p2

#undef GET_INT1
#undef GET_INT2
#undef GET_INT4
#undef GET_FLOAT4
#undef GET_FLOAT8
#undef MOVEP_1
#undef MOVEP_2
#undef MOVEP_4
#undef MOVEP_8


/*
 * End of error.c
 */