/***************************************************************f
  
        bwb_int.c       Line Interpretation Routines
                        for Bywater BASIC Interpreter
  
                        Copyright (c) 1993, Ted A. Campbell
                        Bywater Software
  
                        email: tcamp@delphi.com
  
        Copyright and Permissions Information:
  
        All U.S. and international rights are claimed by the author,
        Ted A. Campbell.
  
   This software is released under the terms of the GNU General
   Public License (GPL), which is distributed with this software
   in the file "COPYING".  The GPL specifies the terms under
   which users may copy and use the software in this distribution.
  
   A separate license is available for commercial distribution,
   for information on which you should contact the author.
  
***************************************************************/

/*---------------------------------------------------------------*/
/* NOTE: Modifications marked "JBV" were made by Jon B. Volkoff, */
/* 11/1995 (eidetics@cerf.net).                                  */
/*                                                               */
/* Those additionally marked with "DD" were at the suggestion of */
/* Dale DePriest (daled@cadence.com).                            */
/*                                                               */
/* Version 3.00 by Howard Wulf, AF5NE                            */
/*                                                               */
/* Version 3.10 by Howard Wulf, AF5NE                            */
/*                                                               */
/*---------------------------------------------------------------*/



#include "bwbasic.h"

int
buff_is_eol( char *buffer, int * position )
{
   /*
   determines whether 'position' is at the NUL (BasicNulChar) in 'buffer'.
   if successful then returns TRUE
   otherwise returns FALSE.
   'position' is unchanged.
   */
   int p;
   
   p = *position;
   if( buffer[p] == BasicNulChar )
   {
      return TRUE;
   }
   return FALSE;
}

int
line_is_eol( LineType * line )
{
   return buff_is_eol( line->buffer, &(line->position) );
}

int
buff_skip_eol( char *buffer, int * position )
{
   /*
   skip to the NUL (BasicNulChar) in 'buffer'.
   if successful then 'position' is updated and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   */
   int p;
   
   p = *position;
   if( buffer[p] != BasicNulChar )
   {
      while( buffer[p] != BasicNulChar )
      {
         p++;
      }
      *position = p;
      return TRUE;
   }
   return FALSE;
}

int
line_skip_eol( LineType * line )
{
   return buff_skip_eol( line->buffer, &(line->position) );
}

int
buff_skip_spaces( char *buffer, int * position )
{
   /*
   skip spaces in 'buffer'.
   if successful then 'position' is updated and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   */
   int p;
   
   p = *position;
   
   if( buffer[p] == ' ' )
   {
      while( buffer[p] == ' ' )
      {
         p++;
      }
      *position = p;
      return TRUE;
   }
   return FALSE;
}

int
line_skip_spaces( LineType * line )
{
   return buff_skip_spaces( line->buffer, &(line->position) );
}


int
buff_peek_char( char *buffer, int * position, char find )
{
   /*
   determine whether the next non-space character in 'buffer' is 'find'.
   if successful then returns TRUE
   otherwise returns FALSE.
   'position' is unchanged.
   */
   int p;
   
   p = *position;
   
   
   buff_skip_spaces( buffer, &p );
   if( bwb_toupper(buffer[p]) == bwb_toupper(find) )
   {
      return TRUE;
   }
   return FALSE;
}

int
line_peek_char( LineType * line, char find )
{
   return buff_peek_char( line->buffer, &(line->position), find );
}

int
buff_skip_char( char *buffer, int * position, char find )
{
   /*
   skip the next non-space character in 'buffer' if it is 'find'.
   if successful then 'position' is updated past 'find' and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   */
   int p;
   
   p = *position;
   
   
   buff_skip_spaces( buffer, &p );
   if( bwb_toupper(buffer[p]) == bwb_toupper(find) )
   {
      p++;
      *position = p;
      return TRUE;
   }
   return FALSE;
}


int
line_skip_char( LineType * line, char find )
{
   return buff_skip_char( line->buffer, &(line->position), find );
}


int
buff_skip_comma( char *buffer, int * position )
{
   /*
   skip the next non-space character in 'buffer' if it is a comma, semicolon, or colon.
   if successful then 'position' is updated past the character and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   */
   int p;
   
   p = *position;
   
   
   buff_skip_spaces( buffer, &p );
   switch( buffer[p] )
   {
   case ',': /* COMMA */
   case ';': /* SEMICOLON */
   case ':': /* COLON */
      p++;
      buff_skip_spaces( buffer, &p );
      *position = p;
      return TRUE;
   }
   return FALSE;
}


int
line_skip_comma( LineType * line )
{
   return buff_skip_comma( line->buffer, &(line->position) );
}

static int char_is_varfirst( char C )
{
   /*
   determine whether the character is allowed to be the first character of a BASIC variable name.
   if successful then returns TRUE
   otherwise returns FALSE.
   */
   if( bwb_isalpha( C ) )
   {
      return TRUE;
   }
   return FALSE;
}

static int char_is_varnext( char C )
{
   /*
   determine whether the character is allowed to be the second character of a BASIC variable name.
   if successful then returns TRUE
   otherwise returns FALSE.
   */
   if( bwb_isalnum( C ) )
   {
      return TRUE;
   }
   switch(C)
   {
   case '.':
   case '_':
      if( My->CurrentVersion->OptionFlags & (OPTION_BUGS_ON) /* varnamex: period and underscore are allowed */ )
      {
         return TRUE;
      }
   }
   return FALSE;
}

int char_is_vartype( char C )
{
   /*
   determine whether the character is allowed to be the type character of a BASIC variable name.
   if successful then returns TRUE
   otherwise returns FALSE.
   */
   switch( C )
   {
   case BasicStringSuffix:
      return TRUE;
   case BasicDoubleSuffix:
   case BasicSingleSuffix:
   case BasicCurrencySuffix:
   case BasicLongSuffix:
   case BasicIntegerSuffix:
   case BasicByteSuffix:
      if( My->CurrentVersion->OptionFlags & (OPTION_BUGS_ON) /* varname: numeric type-chars allowed */ )
      {
         return TRUE;
      }
   }
   return FALSE;
}

static int char_is_varhead( char C )
{
   if( char_is_varfirst( C ) )
   {
      return TRUE;
   }
   if( char_is_varnext( C ) )
   {
      return TRUE;
   }
   return FALSE;
}

int char_is_vartail( char C )
{
   if( char_is_varnext( C ) )
   {
      return TRUE;
   }
   if( char_is_vartype( C ) )
   {
      return TRUE;
   }
   return FALSE;
}

int
buff_peek_word( char *buffer, int * position, char * find )
{
   /*
   determine whether the next non-space word in 'buffer' is 'find';
   the word 'find' is not a sub-string of a bigger word.
   if successful then returns TRUE
   otherwise returns FALSE.
   'position' is unchanged.
   */
   int p;
   int n;
   p = *position;
   
   
   buff_skip_spaces( buffer, &p );
   if( buff_is_eol( buffer, &p ) )
   {
      return FALSE;
   }
   n = bwb_strlen(find);
   if( bwb_strnicmp( &(buffer[p]), find, n) == 0 )
   {
      if( p > 0 )
      {
         if( char_is_varhead( buffer[p-1] ) )
         {
            /* _TO */
            return FALSE;
         }
      }
      if( char_is_vartail( buffer[p+n] ) )
      {
         /* TO_ */
         return FALSE;
      }
      return TRUE;
   }
   return FALSE;
}

int
line_peek_word( LineType * line, char * find )
{
   return buff_peek_word( line->buffer, &(line->position), find );
}

int
buff_skip_word( char *buffer, int * position, char * find )
{
   /*
   skip the next non-space word in 'buffer' if it is 'find';
   the word 'find' is not a sub-string of a bigger word.
   if successful then 'position' is updated past 'find' and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   */
   int p;
   int n;
   p = *position;
   
   
   buff_skip_spaces( buffer, &p );
   if( buff_is_eol( buffer, &p ) )
   {
      return FALSE;
   }
   n = bwb_strlen(find);
   if( bwb_strnicmp( &(buffer[p]), find, n) == 0 )
   {
      if( p > 0 )
      {
         if( char_is_varhead( buffer[p-1] ) )
         {
            /* _TO */
            return FALSE;
         }
      }
      if( char_is_vartail( buffer[p+n] ) )
      {
         /* TO_ */
         return FALSE;
      }
      p += n;
      *position = p;
      return TRUE;
   }
   return FALSE;
}

int
line_skip_word( LineType * line, char * find )
{
   return buff_skip_word( line->buffer, &(line->position), find );
}

int
buff_read_varname( char *buffer, int * position, char * varname )
{
   /*
   read the next non-space word in 'buffer' that conforms to a BASIC variable name into 'varname'.
   if successful then 'position' is updated past 'varname' and returns TRUE
   otherwise 'position' is unchanged ('varname' is truncated) and returns FALSE.
   'varname' shall be declared "char varname[BasicNameLengthMax + 1]".
   */
   int p;
   
   p = *position;
   
   buff_skip_spaces( buffer, &p );
   if( char_is_varfirst( buffer[p] ) )
   {
      int i;
      i = 0;
      
      if( i > BasicNameLengthMax )
      {
         i = BasicNameLengthMax;
      }
      varname[i] = buffer[p];
      p++;
      i++;
      while (char_is_varnext(buffer[p]))
      {
         if( i > BasicNameLengthMax )
         {
            i = BasicNameLengthMax;
         }
         varname[i] = buffer[p];
         p++;
         i++;
      }
      if (char_is_vartype(buffer[p]))
      {
         if( i > BasicNameLengthMax )
         {
            i = BasicNameLengthMax;
         }
         varname[i] = buffer[p];
         p++;
         i++;
      }
      varname[i] = BasicNulChar;
      *position = p;
      return TRUE;
   }
   varname[0] = BasicNulChar;
   return FALSE;
}

int
line_read_varname( LineType * line, char * varname )
{
   return buff_read_varname( line->buffer, &(line->position), varname );
}

int
buff_read_label( char * buffer, int * position, char * label )
{
   /*
   read the next non-space word in 'buffer' that conforms to a BASIC label name into 'label'.
   if successful then 'position' is updated past 'label' and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   'label' shall be declared "char label[BasicNameLengthMax + 1]".
   */
   int p;
   
   p = *position;
   
   buff_skip_spaces( buffer, &p );

   if( char_is_varfirst( buffer[p] ) )
   {
      int i;
      i = 0;
      
      if( i > BasicNameLengthMax )
      {
         i = BasicNameLengthMax;
      }
      label[i] = buffer[p];
      p++;
      i++;
      while (char_is_varnext(buffer[p]))
      {
         if( i > BasicNameLengthMax )
         {
            i = BasicNameLengthMax;
         }
         label[i] = buffer[p];
         p++;
         i++;
      }
      label[i] = BasicNulChar;
      *position = p;
      return TRUE;
   }
   return FALSE;
}

int
line_read_label( LineType * line, char * label )
{
   return buff_read_label( line->buffer, &(line->position), label );
}

static int buff_read_keyword( char * buffer, int * position, char * keyword )
{
   /*
   read the next non-space word in 'buffer' that conforms to a BASIC keyword into 'keyword'.
   if successful then 'position' is updated past 'keyword' and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   'label' shall be declared "char keyword[BasicNameLengthMax + 1]".
   */
   int p;
   
   p = *position;
   
   buff_skip_spaces( buffer, &p );
   if( char_is_varfirst( buffer[p] ) )
   {
      int i;
      i = 0;
      
      if( i > BasicNameLengthMax )
      {
         i = BasicNameLengthMax;
      }
      keyword[i] = buffer[p];
      p++;
      i++;
      while (char_is_varnext(buffer[p]))
      {
         if( i > BasicNameLengthMax )
         {
            i = BasicNameLengthMax;
         }
         keyword[i] = buffer[p];
         p++;
         i++;
      }
      if( buffer[p] == BasicStringSuffix )
      {
         if( i > BasicNameLengthMax )
         {
            i = BasicNameLengthMax;
         }
         keyword[i] = buffer[p];
         p++;
         i++;
      }
      keyword[i] = BasicNulChar;
      *position = p;
      return TRUE;
   }
   keyword[0] = BasicNulChar;
   return FALSE;
}


VariableType *
buff_read_scalar( char *buffer, int * position )
{
   /*
   read the next non-space word in 'buffer' that conforms to a BASIC variable name,
   including both scalar variables and subscripted array variables.
   if successful then 'position' is updated 
   past 'varname' for scalar variables
   (past ')' for subscripted array variables).
   and returns a pointer to the variable.
   otherwise 'position' is unchanged and returns NULL.
   */

   int p;
   VariableType *v;
   char varname[BasicNameLengthMax + 1];

   
   p = *position;

   /* Read a variable name */
   buff_skip_spaces( buffer, &p );   
   if( buff_read_varname(buffer, &p, varname) == FALSE )
   {
      WARN_VARIABLE_NOT_DECLARED;
      return NULL;
   }
   if( buff_peek_char( buffer, &p, BasicLeftParenChar ) )
   {
      /* MUST be a an array */
      register int    n;
      int             n_params;  /* number of parameters */
      int             pp[ MAX_DIMS ];
           
      /* get parameters because the variable is dimensioned */
      if( buff_read_array_dimensions(buffer, &p, &n_params, pp) == FALSE )
      {
         WARN_SUBSCRIPT_OUT_OF_RANGE;
         return NULL;
      }
      /* get the array variable */
      if( (v = var_find(varname,n_params,TRUE)) == NULL )
      {
         WARN_VARIABLE_NOT_DECLARED;
         return NULL;
      }
      for (n = 0; n < v->dimensions; n++)
      {
         v->array_pos[n] = pp[n];
      }
   }
   else
   {
      /* simple scalar variable */
      
      if( (v = var_find(varname,0,TRUE)) == NULL )
      {
         WARN_VARIABLE_NOT_DECLARED;
         return NULL;
      }
   }
   *position = p;
   return v;
}

VariableType *
line_read_scalar( LineType * line )
{
   return buff_read_scalar( line->buffer, &(line->position) );
}

VariableType *
buff_read_matrix( char *buffer, int * position )
{
   /*
   read the next non-space word in 'buffer' that conforms to a BASIC matrix name,
   including both simple matrix variables and redimensioned matrix variables.
   if successful then 'position' is updated 
   past 'varname' for matrix variables
   (past ')' for redimensioned matrix variables).
   and returns a pointer to the variable.
   otherwise 'position' is unchanged and returns NULL.
   */

   int p;
   VariableType *v;
   char varname[BasicNameLengthMax + 1];

   
   p = *position;

   /* Read a variable name */
   buff_skip_spaces( buffer, &p );   
   if( buff_read_varname(buffer, &p, varname) == FALSE )
   {
      WARN_SYNTAX_ERROR;
      return NULL;
   }
   buff_skip_spaces( buffer, &p );   
   v = mat_find( varname );
   if( v == NULL)
   {
      WARN_VARIABLE_NOT_DECLARED;
      return NULL;
   }
   if (buff_peek_char(buffer, &p, BasicLeftParenChar))
   {
      /* get requested matrix size, which is <= original matrix size */
      size_t array_units;
      int    n;
      int    n_params;  /* number of parameters */
      int    pp[ MAX_DIMS ];

      for (n = 0; n < MAX_DIMS; n++)
      {
         pp[n] = 0;
      }
      if( buff_read_array_dimensions(buffer, &p, &n_params, pp) == FALSE )
      {
         WARN_SYNTAX_ERROR;
         return NULL;
      }
      /* update array dimensions */
      array_units = 1;
      for (n = 0; n < n_params; n++)
      {
         if( pp[n] < My->CurrentVersion->OptionBaseValue )
         {
            WARN_SUBSCRIPT_OUT_OF_RANGE;
            return NULL;
         }
         array_units *= pp[n] - My->CurrentVersion->OptionBaseValue + 1;
      }
      if( array_units > v->array_units )
      {
         WARN_SUBSCRIPT_OUT_OF_RANGE;
         return NULL;
      }
      v->dimensions = n_params;
      for (n = 0; n < v->dimensions; n++)
      {
         v->LBOUND[n] = My->CurrentVersion->OptionBaseValue;
         v->UBOUND[n] = pp[n];
      }
   }
   *position = p;
   return v;
}

VariableType *
line_read_matrix( LineType * line )
{
   return buff_read_matrix( line->buffer, &(line->position) );
}

int
buff_read_line_number( char * buffer, int * position, int * linenum )
{
   /*
   read the next non-space word in 'buffer' that conforms to a BASIC line number.
   if successful then 'position' is updated past 'linenum' and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   */
   int p;
   
   p = *position;
   
   buff_skip_spaces( buffer, &p );

   if( bwb_isdigit( buffer[p] ) )
   {
      int i;
      char label[BasicNameLengthMax + 1];
      
      i = 0;
      while (bwb_isdigit(buffer[p]))
      {
         if( i > BasicNameLengthMax )
         {
            i = BasicNameLengthMax;
         }
         label[i] = buffer[p];
         p++;
         i++;
      }
      label[i] = BasicNulChar;
      *linenum = atoi( label );
      *position = p;
      return TRUE;
   }
   return FALSE;
}

int
line_read_line_number( LineType * line, int * linenum )
{
   return buff_read_line_number( line->buffer, &(line->position), linenum );
}

int
buff_read_line_sequence( char *buffer, int * position, int * head, int * tail )
{
   /*
   read the next non-space words in 'buffer' that conforms to a BASIC line number sequnence.
   if successful then 'position' is updated past the line number sequence and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   */
   /*
   ###          head == tail
   ### -        head to BMAX
   ### - ###    head to tail
       - ###    BMIN to tail
   */
   int p;
   int h;
   int t;
   
   p = *position;
   
   buff_skip_spaces( buffer, &p );
   if( buff_skip_char( buffer, &p, '-' ) )
   {
      /* - ... */
      if( buff_read_line_number( buffer, &p, &t ) )
      {
         /* - ### */
         *head = BasicLineNumberMin;
         *tail = t;
         *position = p;
         return TRUE;
      }
   }
   else
   if( buff_read_line_number( buffer, &p, &h ) )
   {
      /* ### ... */
      if( buff_skip_char( buffer, &p, '-' ) )
      {
         /* ### - ... */
         if( buff_read_line_number( buffer, &p, &t ) )
         {
            /* ### - ### */
            *head = h;
            *tail = t;
            *position = p;
            return TRUE;
         }
         else
         {
            /* ### - */
            *head = h;
            *tail = BasicLineNumberMax;
            *position = p;
            return TRUE;
         }
      }
      else
      {
         /* ### */
         *head = h;
         *tail = h;
         *position = p;
         return TRUE;
      }
   }
   return FALSE;
}

int
line_read_line_sequence( LineType * line, int * head, int * tail )
{
   return buff_read_line_sequence( line->buffer, &(line->position), head, tail );
}


 
int
buff_read_integer_expression( char * buffer, int * position, int * Value )
{
   /*
   read the next non-space words in 'buffer' that conforms to a BASIC integer expression into 'Value'.
   if successful then 'position' is updated past 'Value' and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   'Value' shall be declared "int Value".
   */
   BasicNumberType X;
   int p;
   bwx_DEBUG(__FUNCTION__);

   p = *position;
   buff_skip_spaces( buffer, &p );
   if( buff_read_numeric_expression( buffer, &p, &X ) )
   {
      /* we want the rounded value */
      X = bwb_rint( X );
      if( INT_MIN <= X && X <= INT_MAX )
      {
         /* OK */
         *Value = (int) bwb_rint( X );
         *position = p;
         return TRUE;
      }
   }
   /* ERROR */
   if( My->MaintainerDebugOn )
   {
      sprintf( My->bwb_ebuf, "%s, BUFFER:%s\n", __FUNCTION__, &( buffer[ p ] ) ); fputs( My->bwb_ebuf, My->SYSPRN->cfp );
   }
   return FALSE;
}

int
line_read_integer_expression( LineType * line, int * Value )
{
   return buff_read_integer_expression( line->buffer, &(line->position), Value );
}


int
buff_read_numeric_expression( char * buffer, int * position, BasicNumberType * Value )
{
   /*
   read the next non-space words in 'buffer' that conforms to a BASIC numeric expression into 'Value'.
   if successful then 'position' is updated past 'Value' and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   'Value' shall be declared "BasicNumberType Value".
   */
   int p;
   VariantType x;
   VariantType * X = &x;

   p = *position;
   CLEAR_VARIANT( X );
   
   buff_skip_spaces( buffer, &p );
   if( buff_read_expression( buffer, &p, X ) == FALSE )
   {
      return FALSE;   
   }
   if( X->TypeChar != BasicStringSuffix ) 
   {
      /* OK */
      *Value = X->Number;
      *position = p;
      return TRUE;
   }
   RELEASE( X );
   return FALSE;
}


int
line_read_numeric_expression( LineType * line, BasicNumberType * Value )
{
   return buff_read_numeric_expression( line->buffer, &(line->position), Value );
}



int
buff_read_string_expression( char * buffer, int * position, char ** Value )
{
   /*
   read the next non-space words in 'buffer' that conforms to a BASIC string expression into 'Value'.
   if successful then 'position' is updated past 'Value' and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   'Value' shall be declared "char * Value = NULL".
   */
   int p;
   VariantType x;
   VariantType * X = &x;

   p = *position;
   CLEAR_VARIANT( X );
   
   buff_skip_spaces( buffer, &p );
   if( buff_read_expression( buffer, &p, X ) == FALSE )
   {
      return FALSE;   
   }
   if( X->TypeChar == BasicStringSuffix ) 
   {
      /* OK */
      X->Buffer[ X->Length ] = BasicNulChar;
      *Value = X->Buffer;
      *position = p;
      return TRUE;
   }
   return FALSE;
}

int
line_read_string_expression( LineType * line, char ** Value )
{
   return buff_read_string_expression( line->buffer, &(line->position), Value );
}

int
buff_read_index_item( char * buffer, int * position, int Index, int * Value )
{
   /*
   read the next non-space words in 'buffer' that conforms to a BASIC integer expression list into 'Value',
   selecting the item matching 'Index'.  The first 'Index' value is one; 
   if successful then 'position' is updated past 'Value' and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   'Value' shall be declared "int Value".
   */
   int p;
   int i;
      
   p = *position;
   
   buff_skip_spaces( buffer, &p );
   if( buff_is_eol( buffer, &p ) )
   {
      return FALSE;
   }
   if( Index < 1 )
   {
      return FALSE;
   }
   /* Index >= 1 */
   i = 0;
   do
   {
      int v;

      if( buff_read_integer_expression( buffer, &p, &v ) )
      {
         i++;
         if( i == Index )
         {
            *Value = v;
            *position = p;
            return TRUE;
         }
      }
      else
      {
         return FALSE;
      }
   }
   while( buff_skip_comma( buffer, &p ) );
   return FALSE;
}

int
line_read_index_item( LineType * line, int Index, int * Value )
{
   return buff_read_index_item( line->buffer, &(line->position), Index, Value );
}


int
buff_read_element( char * buffer, int * position, char * element )
{
   /*
   read the next non-space word in 'buffer' that conforms to a BASIC element.
   if successful then 'position' is updated past the element and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   'element' shall be declared "char element[BasicStringLengthMax + 1]".
   */
   int p;
   int i;
      
   p = *position;
   i = 0;
   
   buff_skip_spaces( buffer, &p );
   if( buff_is_eol( buffer, &p ) )
   {
      return FALSE;
   }

   while( TRUE )
   {

      switch (buffer[p])
      {
      case BasicNulChar:
      case ',': /* COMMA */   
      case ';': /* SEMICOLON */
      case ':': /* COLON */
      case '=':
      case ' ':
         /* element terminators */
         element[i] = BasicNulChar;
         *position = p;
         return TRUE;
         /* break; */
      case BasicQuoteChar:  
         /* string constant */
         if( i > 0 )
         {  
            /* terminates previous element */
            element[i] = BasicNulChar;
            *position = p;
            return TRUE;
         }
         /* return the string constant */
         if( i > BasicStringLengthMax )
         {
            i = BasicStringLengthMax;
         }
         element[i] = buffer[p];
         i++;
         p++;
         while( buffer[p] != BasicNulChar && buffer[p] != BasicQuoteChar )
         {
            if( i > BasicStringLengthMax )
            {
               i = BasicStringLengthMax;
            }
            element[i] = buffer[p];
            i++;
            p++;
         }
         if( buffer[p] == BasicQuoteChar )
         {
            p++;
         }
         if( i > BasicStringLengthMax )
         {
            i = BasicStringLengthMax;
         }
         element[i] = BasicQuoteChar;
         i++;
         element[i] = BasicNulChar;
         *position = p;
         return TRUE;
         /* break; */
      default:
         if( i > BasicStringLengthMax )
         {
            i = BasicStringLengthMax;
         }
         element[i] = buffer[p];
         i++;
         p++;
         break;
      }
   }
   /* return FALSE; */ /* never reached */
}

int
line_read_element( LineType * line, char * element )
{
   return buff_read_element( line->buffer, &(line->position), element );
}

int
buff_read_letter_sequence(char *buffer, int *position, char *start, char *end)
{
   /*
   read the next non-space alphabetic character in 'buffer' into 'start';
   if seperated by a '-' then read the next non-space alphabetic character into 'end'.
   if successful then 'position' is updated past 'start' (or 'end') and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   */
   int p;
   char s;
   char e;

   p = *position;
   
   buff_skip_spaces(buffer, &p);
   if ( ! bwb_isalpha(buffer[p]) )
   {
      /* character at this position must be a letter */
      return FALSE;
   }
   s = buffer[p];
   p++;

   /* check for hyphen, indicating sequence of more than one letter */
   if ( buff_skip_char( buffer, &p, '-') )
   {
      buff_skip_spaces(buffer, &p);
      if ( ! bwb_isalpha(buffer[p]) )
      {
         /* character at this position must be a letter */
         return FALSE;
      }
      e = buffer[p];
      p++;
   }
   else
   {
      e = s;
   }
   *start = s;
   *end = e;
   *position = p;
   return TRUE;
}

int
line_read_letter_sequence( LineType * line, char *start, char *end)
{
   return buff_read_letter_sequence( line->buffer, &(line->position), start, end );
}


int
buff_read_array_dimensions(char *buffer, int *position, int *n_params , int params[ /* MAX_DIMS */ ] )
{
   /*
   read the next non-space words in 'buffer' that conform to BASIC array index values;
   if successful then 'position' is updated past the ending ')' and returns TRUE
   otherwise 'position' is unchanged and returns FALSE.
   */
   int p;
   int n;
   
   p = *position;
   n = 0;
   
   buff_skip_spaces(buffer, &p);
   if ( buff_skip_char(  buffer, &p, BasicLeftParenChar) )
   {
      /* matrix */
      do
      {
         int Value;

         if( n < MAX_DIMS )
         {
            /* OK */
            if( buff_read_integer_expression( buffer, &p, &Value) )
            {
               params[n] = Value;
               n++;
            }
            else
            {
               return FALSE;
            }
         }
         else
         {
            /* ERROR */
            return FALSE;
         }
      }
      while( buff_skip_comma(  buffer, &p ) );
      
      if ( buff_skip_char(  buffer, &p, BasicRightParenChar) == FALSE )
      {
         /* ERROR */
         return FALSE;
      }
   }
   else
   {
      /* scalar */
      n = 0;
   }
   *n_params = n;
   *position = p;
   return TRUE;   
}

int
line_read_array_dimensions( LineType * line, int *n_params , int params[ /* MAX_DIMS */ ] )
{
   return buff_read_array_dimensions(line->buffer, &(line->position), n_params , params );
}

int
buff_peek_array_dimensions(char *buffer, int *position, int *n_params )
{
   /*
   peek the next non-space words in 'buffer' that conform to BASIC array index values;
   if successful then 'n_params' is updated and returns TRUE
   otherwise 'n_params' is unchanged and returns FALSE.
   'position' is always unchanged.
   */
   int p;
   int             ParenLevel = 0;
   int             NumDimensions = 1;

   p = *position;
   buff_skip_spaces( buffer, &p );   
   while( buffer[p] != BasicNulChar )
   {
      /* check the current character */
      buff_skip_spaces( buffer, &p );   
      if ( buff_skip_char(  buffer, &p, BasicLeftParenChar) )
      {
         ParenLevel++;
      }
      else
      if ( buff_skip_char(  buffer, &p, BasicRightParenChar) )
      {
         ParenLevel--;
         if (ParenLevel < 0)
         {
            return FALSE;
         }
         if (ParenLevel == 0)
         {
            *n_params = NumDimensions;
            return TRUE;
         }
      }
      else
      if ( buff_skip_comma(  buffer, &p) )
      {
         if (ParenLevel == 1)
         {
            NumDimensions++;
         }
      }
      else
      if( buffer[p] == BasicQuoteChar )
      {
         /* embedded string constant */
         p++;
         while ((buffer[p] != BasicQuoteChar) && (buffer[p] != BasicNulChar))
         {
            p++;
         }
         if (buffer[p] == BasicQuoteChar)
         {
            p++;
         }
      }
      else
      {
         /* normal character */
         p++;
      }
   }
   return FALSE;
}

int
line_peek_array_dimensions( LineType * line, int *n_params )
{
   return buff_peek_array_dimensions(line->buffer, &(line->position), n_params );
}

char
buff_read_type_declaration( char *buffer, int * position )
{
   /*
   skip the next non-space words in 'buffer' if it is a BASIC type declaration.
   if successful then 'position' is updated past the BASIC type declaration and returns the TypeChar
   otherwise 'position' is unchanged and returns BasicNulChar.
   */
   int p;
   char TypeChar;
   p = *position;
   TypeChar = BasicNulChar; 
   
   
   buff_skip_spaces( buffer, &p );
   if( buff_is_eol( buffer, &p ) )
   {
      return FALSE;
   }

   if( buff_skip_word(buffer, &p, "AS") == TRUE )
   {
      /* AS ... */
      
      if( buff_skip_word(buffer, &p, "STRING" ) )
      {
         /* AS STRING */
         TypeChar = BasicStringSuffix;
      }
      else
      if( buff_skip_word(buffer, &p, "DOUBLE" ) )
      {
         /* AS DOUBLE */
         TypeChar = BasicDoubleSuffix;
      }
      else
      if( buff_skip_word(buffer, &p, "SINGLE" ) )
      {
         /* AS SINGLE */
         TypeChar = BasicSingleSuffix;
      }
      else
      if( buff_skip_word(buffer, &p, "CURRENCY" ) )
      {
         /* AS CURRENCY */
         TypeChar = BasicCurrencySuffix;
      }
      else
      if( buff_skip_word(buffer, &p, "LONG" ) )
      {
         /* AS LONG */
         TypeChar = BasicLongSuffix;
      }
      else
      if( buff_skip_word(buffer, &p, "INTEGER" ) )
      {
         /* AS INTEGER */
         TypeChar = BasicIntegerSuffix;
      }
      else
      if( buff_skip_word(buffer, &p, "BYTE" ) )
      {
         /* AS BYTE */
         TypeChar = BasicByteSuffix;
      }
      else
      {
         /* invalid type */
      }
   }
   if( TypeChar != BasicNulChar )
   {
      /* success */
      *position = p;
   }
   return TypeChar;
}

char
line_read_type_declaration( LineType * line )
{
   return buff_read_type_declaration(line->buffer, &(line->position) );
}



/***************************************************************
  
        FUNCTION:       line_start()
  
        DESCRIPTION:    This function reads a line buffer in
                        <buffer> beginning at the position
                        <pos> and attempts to determine (a)
                        the position of the line number in the
                        buffer (returned in <lnpos>), (b) the
                        line number at this position (returned
                        in <lnum>), (c) the position of the
                        BASIC command in the buffer (returned
                        in <cmdpos>), (d) the position of this
                        BASIC command in the command table
                        (returned in <cmdnum>), and (e) the
                        position of the beginning of the rest
                        of the line (returned in <Startpos>).
                        Although <Startpos> must be returned
                        as a positive integer, the other
                        searches may fail, in which case FALSE
                        will be returned in their positions.
                        <pos> is not incremented.
  
***************************************************************/


int
line_start(LineType * l)
{
   int             position;
   char            tbuf[BasicNameLengthMax + 1];


   bwx_DEBUG(__FUNCTION__);


   /* set initial values */
   l->Startpos = 0;
   l->cmdnum = 0;    /* not found */
   position = 0;

   buff_skip_spaces(l->buffer, &position);

   /* handle special cases */
   if (l->buffer[position] == BasicNulChar)
   {
      /* the NUL (0) char must be handled first */
      l->cmdnum = C_REM;
      return TRUE;
   }
   if (l->buffer[position] == My->CurrentVersion->OptionCommentChar)
   {
      l->cmdnum = C_REM;
      buff_skip_eol(l->buffer, &(position));
      l->Startpos = position;
      return TRUE;
   }
   if (l->buffer[position] == My->CurrentVersion->OptionPrintChar)
   {
      l->cmdnum = C_PRINT;
      l->Startpos = position;
      /* skip past '?' */
      l->Startpos++;
      return TRUE;
   }
   if (l->buffer[position] == My->CurrentVersion->OptionImageChar)
   {
      l->cmdnum = C_IMAGE;
      l->Startpos = position;
      /* skip past ':' */
      l->Startpos++;
      return TRUE;
   }
   if (bwb_strnicmp(&l->buffer[position], "REM", 3) == 0)
   {
      l->cmdnum = C_REM;
      buff_skip_eol(l->buffer, &(position));
      l->Startpos = position;
      return TRUE;
   }
   /* not a SPECIAL */  
   
   /* get the first keyword */
   if( buff_read_keyword(l->buffer, &position, tbuf) == FALSE )
   {
      /* ERROR */
      return FALSE;   
   }
   buff_skip_spaces(l->buffer, &position);


   /* check for COMMAND */
   if ((l->cmdnum = is_cmd(tbuf)) > 0)
   {
      /* it is a COMMAND */
      /* 
       * NOTE:  This is NOT a full parser, this exists only to 
       * handle STRUCTURED commands. It is true that we also handle 
       * some other easy cases, but remember that this only exists 
       * to support STRUCTURED commands. Whether any other commands 
       * get processed here is simply because it was easy to do so. 
       */

      int cmdnum = 0;

#define MAX_COMMAND_LENGTH 63       
      char xbuf[ MAX_COMMAND_LENGTH + 1 /* NUL */ ]; /* ON ERROR RESUME NEXT */
      
      bwb_strcpy( xbuf, tbuf );
      
      do
      {
         cmdnum = 0;
      
         buff_skip_spaces(l->buffer, &position);
         l->Startpos = position;
         if( buff_read_keyword(l->buffer, &position, tbuf) )
         {
            int n;
            n = bwb_strlen( xbuf ) + 1 + bwb_strlen( tbuf );
            if( n < MAX_COMMAND_LENGTH )
            {
               /* not too long */            
               bwb_strcat( xbuf, " "  );
               bwb_strcat( xbuf, tbuf );
               cmdnum = is_cmd( xbuf );
               if( cmdnum > 0 )
               {
                  /* longer command is valid */
                  buff_skip_spaces(l->buffer, &position);
                  l->cmdnum = cmdnum;
                  l->Startpos = position;
               }
            }
         }
      }
      while( cmdnum > 0 );

      /* process special cases here */
      switch (l->cmdnum)
      {
      case C_CLOAD:
         {
            position = l->Startpos;
            if( buff_skip_char(l->buffer, &position, '*' ) )
            {
               /* CLOAD* */
               buff_skip_spaces(l->buffer, &position);
               l->cmdnum = C_CLOAD8;
               l->Startpos = position;
            }
         }
         break;
      case C_CSAVE:
         {
            position = l->Startpos;
            if( buff_skip_char(l->buffer, &position, '*' ) )
            {
               /* CSAVE* */
               buff_skip_spaces(l->buffer, &position);
               l->cmdnum = C_CSAVE8;
               l->Startpos = position;
            }
         }
         break;
      case C_DEF:
         if( bwb_strchr( l->buffer, '=' ) == NULL )
         {
            /* multi-line DEF ... FNEND */
            l->cmdnum = C_FUNCTION;
         }
         fslt_add(l);
         break;
      case C_FEND: 
         l->cmdnum = C_END_FUNCTION;
         break;
      case C_FNEND:
         l->cmdnum = C_END_FUNCTION;
         break;
      case C_FUNCTION:
         fslt_add(l);
         break;
      case C_IF:
         if( IsLastKeyword( l, " THEN" )  )
         {
            /* STRUCTURED */
            l->cmdnum = C_IF_THEN;
         }
         break;
      case C_OPEN:
         {
   
            if( My->CurrentVersion->OptionVersionBitmask & ( I70 | I73 ) )
            {
               /* OPEN filenum, filename$, INPUT | OUTPUT */
            }
            else
            if (GetKeyword(l, NULL, " AS "))
            {
               /* STRUCTURED */
            }
            else
            {
               /* CLASSIC */
               l->cmdnum = C_DEF_SUB;
               l->Startpos = 0;
            }
         }
         break;
      case C_SUB:
         fslt_add(l);
         break;
      case C_SUBEND:
         l->cmdnum = C_END_SUB;
         break;
      case C_USER_LBL:
         fslt_add(l);
         break;
      }
      /* done */
      return TRUE;


#if FALSE      
      if (l->cmdnum == C_CASE)
      {
         position = l->Startpos;
         buff_read_keyword(l->buffer, &position, tbuf);
         buff_skip_spaces(l->buffer, &position);
         if (bwb_stricmp(tbuf, "ELSE") == 0)
         {
            l->cmdnum = C_CASE_ELSE;
            l->Startpos = position;
         }
         else
         {
            /* CASE ... */
            /* CASE IF ... */
            /* CASE IS ... */
         }
      }
      else
      if (l->cmdnum == C_CLOAD)
      {
         position = l->Startpos;
         if( buff_skip_char(l->buffer, &position, '*' ) )
         {
            /* CLOAD* */
            buff_skip_spaces(l->buffer, &position);
            l->cmdnum = C_CLOAD8;
            l->Startpos = position;
         }
         else
         {
            /* CLOAD ... */
         }
      }
      else
      if (l->cmdnum == C_CSAVE)
      {
         position = l->Startpos;
         if( buff_skip_char(l->buffer, &position, '*' ) )
         {
            /* CSAVE* */
            buff_skip_spaces(l->buffer, &position);
            l->cmdnum = C_CSAVE8;
            l->Startpos = position;
         }
         else
         {
            /* CSAVE ... */
         }
      }
      else
      if (l->cmdnum == C_DEF)
      {
         if(bwb_strchr(l->buffer,'=') == NULL)
         {
            /* multi-line DEF ... FNEND */
            l->cmdnum = C_FUNCTION;
            l->Startpos = position;
         }
         else
         {
            /* single line DEF */
         }
      }
      else
      if (l->cmdnum == C_FNEND)
      {
         l->cmdnum = C_END_FUNCTION;
      }
      else
      if (l->cmdnum == C_FEND)
      {
         l->cmdnum = C_END_FUNCTION;
      }
      else
      if (l->cmdnum == C_END)
      {
         position = l->Startpos;
         buff_read_keyword(l->buffer, &position, tbuf);
         buff_skip_spaces(l->buffer, &position);
         if (bwb_stricmp(tbuf, "FUNCTION") == 0)
         {
            l->cmdnum = C_END_FUNCTION;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "IF") == 0)
         {
            l->cmdnum = C_END_IF;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "SELECT") == 0)
         {
            l->cmdnum = C_END_SELECT;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "SUB") == 0)
         {
            l->cmdnum = C_END_SUB;
            l->Startpos = position;
         }
         else
         {
            /* plain END */
         }
      }
      else
      if (l->cmdnum == C_EXIT)
      {
         position = l->Startpos;
         buff_read_keyword(l->buffer, &position, tbuf);
         buff_skip_spaces(l->buffer, &position);
         if (bwb_stricmp(tbuf, "DO") == 0)
         {
            l->cmdnum = C_EXIT_DO;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "FOR") == 0)
         {
            l->cmdnum = C_EXIT_FOR;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "FUNCTION") == 0)
         {
            l->cmdnum = C_EXIT_FUNCTION;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "SUB") == 0)
         {
            l->cmdnum = C_EXIT_SUB;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "UNTIL") == 0)
         {
            l->cmdnum = C_EXIT_UNTIL;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "WHILE") == 0)
         {
            l->cmdnum = C_EXIT_WHILE;
            l->Startpos = position;
         }
         else
         {
            /* EXIT line */
         }
      }
      else
      if (l->cmdnum == C_GO)
      {
         position = l->Startpos;
         buff_read_keyword(l->buffer, &position, tbuf);
         buff_skip_spaces(l->buffer, &position);
         if (bwb_stricmp(tbuf, "SUB") == 0)
         {
            l->cmdnum = C_GOSUB;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "TO") == 0)
         {
            l->cmdnum = C_GOTO;
            l->Startpos = position;
         }
         else
         {
            /* syntax error */
            l->cmdnum = 0;
         }
      }
      else
      if (l->cmdnum == C_IF)
      {
         if (IsLastKeyword(l, " THEN") == TRUE)
         {
            /* STRUCTURED */
            l->cmdnum = C_IF_THEN;
         }
         else
         {
            /* CLASSIC */
            position = l->Startpos;
            buff_read_keyword(l->buffer, &position, tbuf);
            buff_skip_spaces(l->buffer, &position);
            if (bwb_stricmp(tbuf, "END") == 0)
            {
               /* IF END # X THEN ... */
               l->cmdnum = C_IF_END;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "MORE") == 0)
            {
               /* IF MORE # X THEN ... */
               l->cmdnum = C_IF_MORE;
               l->Startpos = position;
            }
            else
            {
               /* normal */
            }
         }
      }
      else
      if (l->cmdnum == C_MAT)
      {
         position = l->Startpos;
         buff_read_keyword(l->buffer, &position, tbuf);
         buff_skip_spaces(l->buffer, &position);
         if (bwb_stricmp(tbuf, "GET") == 0)
         {
            l->cmdnum = C_MAT_GET;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "INPUT") == 0)
         {
            l->cmdnum = C_MAT_INPUT;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "PRINT") == 0)
         {
            l->cmdnum = C_MAT_PRINT;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "PUT") == 0)
         {
            l->cmdnum = C_MAT_PUT;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "READ") == 0)
         {
            l->cmdnum = C_MAT_READ;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "WRITE") == 0)
         {
            l->cmdnum = C_MAT_WRITE;
            l->Startpos = position;
         }
         else
         {
            /* plain MAT */
         }
      }
      else
      if (l->cmdnum == C_ON)
      {
         position = l->Startpos;
         buff_read_keyword(l->buffer, &position, tbuf);
         buff_skip_spaces(l->buffer, &position);
         if (bwb_stricmp(tbuf, "ERROR") == 0)
         {
            buff_read_keyword(l->buffer, &position, tbuf);
            buff_skip_spaces(l->buffer, &position);
            if (bwb_stricmp(tbuf, "GOTO") == 0)
            {
               l->cmdnum = C_ON_ERROR_GOTO;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "GOSUB") == 0)
            {
               l->cmdnum = C_ON_ERROR_GOSUB;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "RESUME") == 0)
            {
                buff_read_keyword(l->buffer, &position, tbuf);
                buff_skip_spaces(l->buffer, &position);
                if (bwb_stricmp(tbuf, "NEXT") == 0)
                {
                   l->cmdnum = C_ON_ERROR_RESUME_NEXT;
                   l->Startpos = position;
                }
                else
                {
                  /* syntax error */
                   l->cmdnum = 0;
                }
            }
            else
            if (bwb_stricmp(tbuf, "RETURN") == 0)
            {
                buff_read_keyword(l->buffer, &position, tbuf);
                buff_skip_spaces(l->buffer, &position);
                if (bwb_stricmp(tbuf, "NEXT") == 0)
                {
                   l->cmdnum = C_ON_ERROR_RETURN_NEXT;
                   l->Startpos = position;
                }
                else
                {
                  /* syntax error */
                   l->cmdnum = 0;
                }
            }
            else
            {
               /* syntax error */
               l->cmdnum = 0;
            }
         }
         else
         if (bwb_stricmp(tbuf, "TIMER") == 0)
         {
            l->cmdnum = C_ON_TIMER;
            l->Startpos = position;
         }
         else
         {
            /* ON ... GO ... */
         }
      }
      else
      if (l->cmdnum == C_OPEN)
      {

         if( My->CurrentVersion->OptionVersionBitmask & ( I70 | I73 ) )
         {
            /* OPEN filenum, filename$, INPUT | OUTPUT */
         }
         else
         if (GetKeyword(l, NULL, " AS "))
         {
            /* STRUCTURED */
         }
         else
         {
            /* CLASSIC */
            l->cmdnum = C_DEF_SUB;
            l->Startpos = 0;
         }
      }
      else
      if (l->cmdnum == C_OPTION)
      {
         position = l->Startpos;
         buff_read_keyword(l->buffer, &position, tbuf);
         buff_skip_spaces(l->buffer, &position);
         if (bwb_stricmp(tbuf, "ANGLE") == 0)
         {
            buff_read_keyword(l->buffer, &position, tbuf);
            buff_skip_spaces(l->buffer, &position);
            if (bwb_stricmp(tbuf, "DEGREES") == 0)
            {
               l->cmdnum = C_OPTION_ANGLE_DEGREES;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "GRADIANS") == 0)
            {
               l->cmdnum = C_OPTION_ANGLE_GRADIANS;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "RADIANS") == 0)
            {
               l->cmdnum = C_OPTION_ANGLE_RADIANS;
               l->Startpos = position;
            }
            else
            {
               /* syntax error */
               l->cmdnum = 0;
            }
         }
         else
         if (bwb_stricmp(tbuf, "ARITHMETIC") == 0)
         {
            buff_read_keyword(l->buffer, &position, tbuf);
            buff_skip_spaces(l->buffer, &position);
            if (bwb_stricmp(tbuf, "DECIMAL") == 0)
            {
               l->cmdnum = C_OPTION_ARITHMETIC_DECIMAL;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "FIXED") == 0)
            {
               l->cmdnum = C_OPTION_ARITHMETIC_FIXED;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "NATIVE") == 0)
            {
               l->cmdnum = C_OPTION_ARITHMETIC_NATIVE;
               l->Startpos = position;
            }
            else
            {
               /* syntax error */
               l->cmdnum = 0;
            }
         }
         else
         if (bwb_stricmp(tbuf, "BASE") == 0)
         {
            l->cmdnum = C_OPTION_BASE;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "BUGS") == 0)
         {
            buff_read_keyword(l->buffer, &position, tbuf);
            buff_skip_spaces(l->buffer, &position);
            if (bwb_stricmp(tbuf, "OFF") == 0)
            {
               l->cmdnum = C_OPTION_BUGS_OFF;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "ON") == 0)
            {
               l->cmdnum = C_OPTION_BUGS_ON;
               l->Startpos = position;
            }
            else
            {
               /* syntax error */
               l->cmdnum = 0;
            }
         }
         else
         if (bwb_stricmp(tbuf, "COMMENT") == 0)
         {
            l->cmdnum = C_OPTION_COMMENT;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "COMPARE") == 0)
         {
            buff_read_keyword(l->buffer, &position, tbuf);
            buff_skip_spaces(l->buffer, &position);
            if (bwb_stricmp(tbuf, "BINARY") == 0)
            {
               l->cmdnum = C_OPTION_COMPARE_BINARY;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "DATABASE") == 0)
            {
               l->cmdnum = C_OPTION_COMPARE_DATABASE;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "TEXT") == 0)
            {
               l->cmdnum = C_OPTION_COMPARE_TEXT;
               l->Startpos = position;
            }
            else
            {
               /* syntax error */
               l->cmdnum = 0;
            }
         }
         else
         if (bwb_stricmp(tbuf, "COVERAGE") == 0)
         {
            buff_read_keyword(l->buffer, &position, tbuf);
            buff_skip_spaces(l->buffer, &position);
            if (bwb_stricmp(tbuf, "OFF") == 0)
            {
               l->cmdnum = C_OPTION_COVERAGE_OFF;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "ON") == 0)
            {
               l->cmdnum = C_OPTION_COVERAGE_ON;
               l->Startpos = position;
            }
            else
            {
               /* syntax error */
               l->cmdnum = 0;
            }
         }
         else
         if (bwb_stricmp(tbuf, "DATE") == 0)
         {
            l->cmdnum = C_OPTION_DATE;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "DISABLE") == 0)
         {
            buff_read_keyword(l->buffer, &position, tbuf);
            buff_skip_spaces(l->buffer, &position);
            if (bwb_stricmp(tbuf, "COMMAND") == 0)
            {
               l->cmdnum = C_OPTION_DISABLE_COMMAND;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "FUNCTION") == 0)
            {
               l->cmdnum = C_OPTION_DISABLE_FUNCTION;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "OPERATOR") == 0)
            {
               l->cmdnum = C_OPTION_DISABLE_OPERATOR;
               l->Startpos = position;
            }
            else
            {
               /* syntax error */
               l->cmdnum = 0;
            }
         }
         else
         if (bwb_stricmp(tbuf, "ENABLE") == 0)
         {
            buff_read_keyword(l->buffer, &position, tbuf);
            buff_skip_spaces(l->buffer, &position);
            if (bwb_stricmp(tbuf, "COMMAND") == 0)
            {
               l->cmdnum = C_OPTION_ENABLE_COMMAND;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "FUNCTION") == 0)
            {
               l->cmdnum = C_OPTION_ENABLE_FUNCTION;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "OPERATOR") == 0)
            {
               l->cmdnum = C_OPTION_ENABLE_OPERATOR;
               l->Startpos = position;
            }
            else
            {
               /* syntax error */
               l->cmdnum = 0;
            }
         }
         else
         if (bwb_stricmp(tbuf, "ERROR") == 0)
         {
            buff_read_keyword(l->buffer, &position, tbuf);
            buff_skip_spaces(l->buffer, &position);
            if (bwb_stricmp(tbuf, "GOSUB") == 0)
            {
               l->cmdnum = C_OPTION_ERROR_GOSUB;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "GOTO") == 0)
            {
               l->cmdnum = C_OPTION_ERROR_GOTO;
               l->Startpos = position;
            }
            else
            {
               /* syntax error */
               l->cmdnum = 0;
            }
         }
         else
         if (bwb_stricmp(tbuf, "EXPLICIT") == 0)
         {
            l->cmdnum = C_OPTION_EXPLICIT;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "IMAGE") == 0)
         {
            l->cmdnum = C_OPTION_IMAGE;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "IMPLICIT") == 0)
         {
            l->cmdnum = C_OPTION_IMPLICIT;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "INDENT") == 0)
         {
            l->cmdnum = C_OPTION_INDENT;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "LABELS") == 0)
         {
            buff_read_keyword(l->buffer, &position, tbuf);
            buff_skip_spaces(l->buffer, &position);
            if (bwb_stricmp(tbuf, "OFF") == 0)
            {
               l->cmdnum = C_OPTION_LABELS_OFF;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "ON") == 0)
            {
               l->cmdnum = C_OPTION_LABELS_ON;
               l->Startpos = position;
            }
            else
            {
               /* syntax error */
               l->cmdnum = 0;
            }
         }
         else
         if (bwb_stricmp(tbuf, "PRINT") == 0)
         {
            l->cmdnum = C_OPTION_PRINT;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "ROUND") == 0)
         {
            buff_read_keyword(l->buffer, &position, tbuf);
            buff_skip_spaces(l->buffer, &position);
            if (bwb_stricmp(tbuf, "BANK") == 0)
            {
               l->cmdnum = C_OPTION_ROUND_BANK;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "MATH") == 0)
            {
               l->cmdnum = C_OPTION_ROUND_MATH;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "TRUNCATE") == 0)
            {
               l->cmdnum = C_OPTION_ROUND_TRUNCATE;
               l->Startpos = position;
            }
            else
            {
               /* syntax error */
               l->cmdnum = 0;
            }
         }
         else
         if (bwb_stricmp(tbuf, "SLEEP") == 0)
         {
            l->cmdnum = C_OPTION_SLEEP;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "STATEMENT") == 0)
         {
            l->cmdnum = C_OPTION_STATEMENT;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "STRICT") == 0)
         {
            buff_read_keyword(l->buffer, &position, tbuf);
            buff_skip_spaces(l->buffer, &position);
            if (bwb_stricmp(tbuf, "OFF") == 0)
            {
               l->cmdnum = C_OPTION_STRICT_OFF;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "ON") == 0)
            {
               l->cmdnum = C_OPTION_STRICT_ON;
               l->Startpos = position;
            }
            else
            {
               /* syntax error */
               l->cmdnum = 0;
            }
         }
         else
         if (bwb_stricmp(tbuf, "TERMINAL") == 0)
         {
            buff_read_keyword(l->buffer, &position, tbuf);
            buff_skip_spaces(l->buffer, &position);
            if (bwb_stricmp(tbuf, "NONE") == 0)
            {
               l->cmdnum = C_OPTION_TERMINAL_NONE;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "ADM-3A") == 0)
            {
               l->cmdnum = C_OPTION_TERMINAL_ADM;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "ANSI") == 0)
            {
               l->cmdnum = C_OPTION_TERMINAL_ANSI;
               l->Startpos = position;
            }
            else
            {
               /* syntax error */
               l->cmdnum = 0;
            }
         }
         else
         if (bwb_stricmp(tbuf, "TIME") == 0)
         {
            l->cmdnum = C_OPTION_TIME;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "TRACE") == 0)
         {
            buff_read_keyword(l->buffer, &position, tbuf);
            buff_skip_spaces(l->buffer, &position);
            if (bwb_stricmp(tbuf, "OFF") == 0)
            {
               l->cmdnum = C_OPTION_TRACE_OFF;
               l->Startpos = position;
            }
            else
            if (bwb_stricmp(tbuf, "ON") == 0)
            {
               l->cmdnum = C_OPTION_TRACE_ON;
               l->Startpos = position;
            }
            else
            {
               /* syntax error */
               l->cmdnum = 0;
            }
         }
         else
         if (bwb_stricmp(tbuf, "VERSION") == 0)
         {
            l->cmdnum = C_OPTION_VERSION;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "USING") == 0)
         {
            l->cmdnum = C_OPTION_USING;
            l->Startpos = position;
         }
         else
         {
            /* syntax error */
            l->cmdnum = 0;
         }
      }
      else
      if (l->cmdnum == C_SELECT)
      {
         position = l->Startpos;
         buff_read_keyword(l->buffer, &position, tbuf);
         buff_skip_spaces(l->buffer, &position);
         if (bwb_stricmp(tbuf, "CASE") == 0)
         {
            l->cmdnum = C_SELECT_CASE;
            l->Startpos = position;
         }
         else
         {
            /* syntax error */
            l->cmdnum = 0;
         }
      }
      else
      if (l->cmdnum == C_SUBEND)
      {
         l->cmdnum = C_END_SUB;
      }
      else
      if (l->cmdnum == C_TIMER)
      {
         position = l->Startpos;
         buff_read_keyword(l->buffer, &position, tbuf);
         buff_skip_spaces(l->buffer, &position);
         if (bwb_stricmp(tbuf, "OFF") == 0)
         {
            l->cmdnum = C_TIMER_OFF;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "ON") == 0)
         {
            l->cmdnum = C_TIMER_ON;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "STOP") == 0)
         {
            l->cmdnum = C_TIMER_STOP;
            l->Startpos = position;
         }
         else
         {
            /* syntax error */
            l->cmdnum = 0;
         }
      }
      else
      if (l->cmdnum == C_TRACE)
      {
         position = l->Startpos;
         buff_read_keyword(l->buffer, &position, tbuf);
         buff_skip_spaces(l->buffer, &position);
         if (bwb_stricmp(tbuf, "OFF") == 0)
         {
            l->cmdnum = C_TRACE_OFF;
            l->Startpos = position;
         }
         else
         if (bwb_stricmp(tbuf, "ON") == 0)
         {
            l->cmdnum = C_TRACE_ON;
            l->Startpos = position;
         }
         else
         {
            /* plain TRACE */
         }
      }
      /* check for a bogus assignment to a command */
      /* 100 PRINT = X */
      if (l->cmdnum == 0)
      {
         /* syntax error */
         return FALSE;
      }
      /* add to the Function-Sub-Label list */
      switch (l->cmdnum)
      {
      case C_DEF:
      case C_FUNCTION:
      case C_SUB:
      case C_USER_LBL:
         fslt_add(l);
         break;
      }
      /* done */
      return TRUE;
#endif
   }
   /* not a COMMAND */  

   /* check for implied LET */
   if (is_let(l->buffer) == TRUE)
   {
      /* this is an implied LET */
      /* 100 A = 123 */
      l->cmdnum = C_LET;   /* bwb_LET */
      l->Startpos = 0;
      return TRUE;
   }
   /* not an implied LET */  

   /* check for FUNCTION */
   if (fnc_find(tbuf) != NULL)
   {
      /* check for a bogus assignment to a function */
      /* 100 COS = X */
      if( buff_peek_char(l->buffer, &position, '=' ) )
      {
         /* syntax error */
         l->cmdnum = 0;
         return FALSE;
      }
      /* user is executing a function, such as 100 COS X */
      l->cmdnum = C_DEF_SUB;  /* bwb_DEF_SUB, in bwb_tbl.c */
      l->Startpos = 0;
      return TRUE;
   }
   /* not a FUNCTION */  

   /* check for LABEL */
   if (My->CurrentVersion->OptionFlags & OPTION_LABELS_ON)
   {
      /* labels are enabled */
      if( My->CurrentVersion->OptionStatementChar != BasicNulChar )
      {
         /* a Statement seperator exists */
         if( buff_skip_char(l->buffer, &position, My->CurrentVersion->OptionStatementChar) )
         {
            /* this is a label */
            /* 100 MyLabel: */
            l->cmdnum = C_USER_LBL; /* bwb_USER_LBL */
            buff_skip_spaces(l->buffer, &position);
            l->Startpos = position;
            return TRUE;
         }
      }
   }
   /* not a LABEL */  


   /* ERROR */
   return FALSE;

}


/***************************************************************
  
        FUNCTION:       is_cmd()
  
   DESCRIPTION:    This function determines whether the
         string in 'buffer' is a BASIC command
         statement, returning 'id' or 0.
         
  
***************************************************************/




int
is_cmd(char *buffer)
{
#if THE_PRICE_IS_RIGHT
   char C;
   
   bwx_DEBUG(__FUNCTION__);

#if FALSE
printf( "check [%s]\n", buffer ); /* HWULF */
#endif
   
   C = buffer[0];

   if( bwb_isalpha( C ) )
   {
      /* start with the closest command, without going over */
      int n;
      n = My->CommandStart[ VarTypeIndex( C ) ]; /* first command starting with this letter, assigned in SortAllCommands() */
      if( n < 0 )
      {
         /* NOT FOUND */
         return 0;
      }
      for( ; n < NUM_COMMANDS; n++ )
      {
         if (My->CurrentVersion->OptionVersionBitmask & bwb_cmdtable[n].OptionVersionBitmask)
         {
            int result;
            result = bwb_stricmp(bwb_cmdtable[n].name, buffer);
            if( result == 0 )
            {
               /* FOUND */
#if FALSE
printf( "found [%s]\n", buffer ); /* HWULF */
#endif
               return bwb_cmdtable[n].id;
            }
            if( result > 0 /* found > searched */ )
            {
               /* NOT FOUND */
               return 0;
            }
         }
      }
   }
   /* NOT FOUND */
   return 0;

#else
   register int    n;
   bwx_DEBUG(__FUNCTION__);


   /* Go through the command table and search for a match. */

   for (n = 0; n < NUM_COMMANDS; ++n)
   {
      if (My->CurrentVersion->OptionVersionBitmask & bwb_cmdtable[n].OptionVersionBitmask)
         if (bwb_stricmp(bwb_cmdtable[n].name, buffer) == 0)
         {
            return bwb_cmdtable[n].id;
         }
   }
   /* No command name was found */
   return 0;
#endif
}

/***************************************************************
  
        FUNCTION:       is_let()
  
        DESCRIPTION:    This function tries to determine if the
                        expression in <buffer> is a LET statement
                        without the LET command specified.
  
***************************************************************/
int
is_let(char *buffer)
{
   register int    n;

   bwx_DEBUG(__FUNCTION__);

   /* Go through the expression and search for an unquoted assignment operator. */

   for (n = 0; buffer[n] != BasicNulChar; ++n)
   {
      switch (buffer[n])
      {
      case BasicQuoteChar:  /* string constant */
         ++n;
         while (buffer[n] != BasicQuoteChar)
         {
            ++n;
            if (buffer[n] == BasicNulChar)
            {
               WARN_SYNTAX_ERROR;
               return FALSE;
            }
         }
         ++n;
         break;
      case '=':
         return TRUE;
      }
   }

   /* No command name was found */

   return FALSE;

}


/***************************************************************
  
        FUNCTION:       bwb_stripcr()
  
   DESCRIPTION:    This function strips the carriage return
         or line-feed from the end of a string.
  
***************************************************************/

int
bwb_stripcr(char *s)
{
    char *E;

   bwx_DEBUG(__FUNCTION__);

    E = bwb_strchr(s, '\r');
    if( E != NULL )
    {
       *E = BasicNulChar;
    }
    E = bwb_strchr(s, '\n');
    if( E != NULL )
    {
       *E = BasicNulChar;
    }

   while (*s)
   {
      if ( ! bwb_isprint( *s ) )
      {
         *s = ' ';
      }
      s++;
   }
   return TRUE;
}

/***************************************************************
  
        FUNCTION:       is_numconst()
  
        DESCRIPTION:    This function reads the string in <buffer>
                        and returns TRUE if it is a numerical
                        constant and FALSE if it is not. At
                        this point, only decimal (base 10)
                        constants are detected.
  
***************************************************************/

int
is_numconst(char *buffer)
{
   char           *p;

   bwx_DEBUG(__FUNCTION__);

   /* Return FALSE for empty buffer */

   if (buffer[0] == BasicNulChar)
   {
      return FALSE;
   }
   /* else check digits */

   p = buffer;
   while (*p != BasicNulChar)
   {
      switch (*p)
      {
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
         break;
      default:
         return FALSE;
      }
      ++p;
   }

   /* only numerical characters detected */

   return TRUE;

}


/***************************************************************
  
        FUNCTION:       bwb_freeline()
  
   DESCRIPTION:    This function frees memory associated
         with a program line in memory.
  
***************************************************************/

int
bwb_freeline(LineType * l)
{

   bwx_DEBUG(__FUNCTION__);

   /* free arguments if there are any */

   /* Revised to FREE pass-thru calls by JBV */
   if (l->buffer != NULL)
   {
      FREE(l->buffer, "bwb_freeline");
      l->buffer = NULL; /* JBV */
   }
   FREE(l, "bwb_freeline");
   /* l = NULL; *//* Assignment of function parameter has no effect
    * outside the function. */

   return TRUE;
}


#if FALSE
/***************************************************************
  
   FUNCTION:       is_eol()
  
   DESCRIPTION:    This function determines whether the buffer
         is at the end of a line.
  
***************************************************************/

int
is_eol(char *buffer, int *position)
{
   char            c;

   bwx_DEBUG(__FUNCTION__);


   buff_skip_spaces(buffer, position);
   c = buffer[*position];


   if (c == BasicNulChar || c == My->CurrentVersion->OptionCommentChar)
   {
      return TRUE;
   }
   return FALSE;
}

char           *
GetRestOfLine(LineType * l, char *OutputBuffer)
{
   char           *S;
   char           *C;

   bwx_DEBUG(__FUNCTION__);


   S = l->buffer;
   S += l->position;
   C = S;
   while (*C != BasicNulChar && *C != My->CurrentVersion->OptionCommentChar)
   {
      C++;
   }
   /* FOUND */
   if (OutputBuffer != NULL)
   {
      char            c;

      c = *C;
      *C = BasicNulChar;
      l->position += bwb_strlen(S);
      bwb_strcpy(OutputBuffer, S);
      *C = c;
   }
   return C;
}
#endif /* FALSE */

char           *
GetKeyword(LineType * l, char *OutputBuffer, char *Keyword)
{
   /* if OutputBuffer is NULL then we just want to know if a keyword
    * exists, and if so where to look after the keyword. this is used by
    * line_start() in bwb_int.c to determine various command flavors.
    * else we want to extract the expression before the keyword and
    * update the position to after the keyword. this is used by various
    * bwb_XXX() when executing BASIC commands. end if 
    *
    * if FOUND then if OutputBuffer != NULL then copy line's buffer BEFORE
    * keyword into OutputBuffer update line's position to AFTER keyword
    * end if return pointer AFTER keyword else return NULL end if    */
   char           *S;
   char           *C;
   int             n;

   bwx_DEBUG(__FUNCTION__);

   S = l->buffer;
   S += l->position;
   C = S;
   n = bwb_strlen(Keyword);


   while (*C != BasicNulChar && *C != My->CurrentVersion->OptionCommentChar)
   {
      if (bwb_strnicmp(C, Keyword, n) == 0)
      {
         /* FOUND */
         char            c;

         c = *C;
         *C = BasicNulChar;
         if (OutputBuffer != NULL)
         {
            l->position += bwb_strlen(S);
            l->position += n;
            bwb_strcpy(OutputBuffer, S);
         }
         *C = c;
         C += n;
         return C;
      }
      else
      if (*C == BasicQuoteChar)
      {
         /* skip string constant */
         C++;
         while (*C != BasicNulChar && *C != BasicQuoteChar)
         {
            C++;
         }
         if (*C == BasicQuoteChar)
         {
            C++;
         }
      }
      else
      {
         /* skip normal character */
         C++;
      }
   }
   /* NOT FOUND */
   return NULL;
}

int
IsLastKeyword(LineType * l, char *Keyword)
{

   /* find the end of the line ( 0 or ' ) */
   /* backup thru spaces */
   int             n;
   char           *S;
   char           *C;

   bwx_DEBUG(__FUNCTION__);


   S = l->buffer;
   S += l->position;
   C = S;
   n = bwb_strlen(Keyword);
   S += n;
   /* IF x THEN0 IF x THEN'0 IF    x    THEN    '     0 */
   while (*C != BasicNulChar && *C != My->CurrentVersion->OptionCommentChar)
   {
      /* skip string constants */
      if( *C == BasicQuoteChar )
      {
          /* skip leading quote */
          C++;
          while (*C != BasicNulChar && *C != BasicQuoteChar)
          {
              C++;
          }
          /* skip trailing quote */
          if( *C == BasicQuoteChar )
          {
              C++;
          }
      }
      else
      {
          C++;
      }
   }
   if (C > S)
   {
      C--;
      while (C > S && *C == ' ')
      {
         C--;
      }
      C++;
      if (C > S)
      {
         C -= n;
         if (bwb_strnicmp(C, Keyword, n) == 0)
         {
            /* FOUND */
            return TRUE;
         }
      }
   }
   /* NOT FOUND */
   return FALSE;
}


int bwb_isalnum( int C )
{
   /*
   4.3.1.1 The isalnum function
   
   Synopsis
   
            #include <ctype.h>
            int isalnum(int c);
   
   Description
   
      The isalnum function tests for any character for which isalpha or
   isdigit is true.
   */
  switch( C )
  {
  case '0':
  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
  case 'A':
  case 'B':
  case 'C':
  case 'D':
  case 'E':
  case 'F':
  case 'G':
  case 'H':
  case 'I':
  case 'J':
  case 'K':
  case 'L':
  case 'M':
  case 'N':
  case 'O':
  case 'P':
  case 'Q':
  case 'R':
  case 'S':
  case 'T':
  case 'U':
  case 'V':
  case 'W':
  case 'X':
  case 'Y':
  case 'Z':
  case 'a':
  case 'b':
  case 'c':
  case 'd':
  case 'e':
  case 'f':
  case 'g':
  case 'h':
  case 'i':
  case 'j':
  case 'k':
  case 'l':
  case 'm':
  case 'n':
  case 'o':
  case 'p':
  case 'q':
  case 'r':
  case 's':
  case 't':
  case 'u':
  case 'v':
  case 'w':
  case 'x':
  case 'y':
  case 'z':
    return TRUE;
  }
  return FALSE;
}

int bwb_isalpha( int C )
{
   /*
   4.3.1.2 The isalpha function
   
   Synopsis
   
            #include <ctype.h>
            int isalpha(int c);
   
   Description
   
      The isalpha function tests for any character for which isupper or
   islower is true, or any of an implementation-defined set of characters
   for which none of iscntrl , isdigit , ispunct , or isspace is true.
   In the C locale, isalpha returns true only for the characters for
   which isupper or islower is true.
   */
  switch( C )
  {
  case 'A':
  case 'B':
  case 'C':
  case 'D':
  case 'E':
  case 'F':
  case 'G':
  case 'H':
  case 'I':
  case 'J':
  case 'K':
  case 'L':
  case 'M':
  case 'N':
  case 'O':
  case 'P':
  case 'Q':
  case 'R':
  case 'S':
  case 'T':
  case 'U':
  case 'V':
  case 'W':
  case 'X':
  case 'Y':
  case 'Z':
  case 'a':
  case 'b':
  case 'c':
  case 'd':
  case 'e':
  case 'f':
  case 'g':
  case 'h':
  case 'i':
  case 'j':
  case 'k':
  case 'l':
  case 'm':
  case 'n':
  case 'o':
  case 'p':
  case 'q':
  case 'r':
  case 's':
  case 't':
  case 'u':
  case 'v':
  case 'w':
  case 'x':
  case 'y':
  case 'z':
    return TRUE;
  }
  return FALSE;
}

int bwb_iscntrl( int C )
{
   /*
   4.3.1.3 The iscntrl function
   
   Synopsis
   
            #include <ctype.h>
            int iscntrl(int c);
   
   Description
   
      The iscntrl function tests for any control character.  
   */
  switch( C )
  {
  case ' ':
  case '!':
  case '"':
  case '#':
  case '$':
  case '%':
  case '&':
  case '\'':
  case '(':
  case ')':
  case '*':
  case '+':
  case ',':
  case '-':
  case '.':
  case '/':
  case '0':
  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
  case ':':
  case ';':
  case '<':
  case '=':
  case '>':
  case '?':
  case '@':
  case 'A':
  case 'B':
  case 'C':
  case 'D':
  case 'E':
  case 'F':
  case 'G':
  case 'H':
  case 'I':
  case 'J':
  case 'K':
  case 'L':
  case 'M':
  case 'N':
  case 'O':
  case 'P':
  case 'Q':
  case 'R':
  case 'S':
  case 'T':
  case 'U':
  case 'V':
  case 'W':
  case 'X':
  case 'Y':
  case 'Z':
  case '[':
  case '\\':
  case ']':
  case '^':
  case '_':
  case '`':
  case 'a':
  case 'b':
  case 'c':
  case 'd':
  case 'e':
  case 'f':
  case 'g':
  case 'h':
  case 'i':
  case 'j':
  case 'k':
  case 'l':
  case 'm':
  case 'n':
  case 'o':
  case 'p':
  case 'q':
  case 'r':
  case 's':
  case 't':
  case 'u':
  case 'v':
  case 'w':
  case 'x':
  case 'y':
  case 'z':
  case '{':
  case '|':
  case '}':
  case '~':
    return FALSE;
  }
  return TRUE;
}

int bwb_isdigit( int C )
{
   /*
   4.3.1.4 The isdigit function
   
   Synopsis
   
            #include <ctype.h>
            int isdigit(int c);
   
   Description
   
      The isdigit function tests for any decimal-digit character (as
   defined in $2.2.1).
   */
  switch( C )
  {
  case '0':
  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
    return TRUE;
  }
  return FALSE;
}

int bwb_isgraph( int C )
{
   /*
   4.3.1.5 The isgraph function
   
   Synopsis
   
            #include <ctype.h>
            int isgraph(int c);
   
   Description
   
      The isgraph function tests for any printing character except space (' ').  
   */
  switch( C )
  {
  case '!':
  case '"':
  case '#':
  case '$':
  case '%':
  case '&':
  case '\'':
  case '(':
  case ')':
  case '*':
  case '+':
  case ',':
  case '-':
  case '.':
  case '/':
  case '0':
  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
  case ':':
  case ';':
  case '<':
  case '=':
  case '>':
  case '?':
  case '@':
  case 'A':
  case 'B':
  case 'C':
  case 'D':
  case 'E':
  case 'F':
  case 'G':
  case 'H':
  case 'I':
  case 'J':
  case 'K':
  case 'L':
  case 'M':
  case 'N':
  case 'O':
  case 'P':
  case 'Q':
  case 'R':
  case 'S':
  case 'T':
  case 'U':
  case 'V':
  case 'W':
  case 'X':
  case 'Y':
  case 'Z':
  case '[':
  case '\\':
  case ']':
  case '^':
  case '_':
  case '`':
  case 'a':
  case 'b':
  case 'c':
  case 'd':
  case 'e':
  case 'f':
  case 'g':
  case 'h':
  case 'i':
  case 'j':
  case 'k':
  case 'l':
  case 'm':
  case 'n':
  case 'o':
  case 'p':
  case 'q':
  case 'r':
  case 's':
  case 't':
  case 'u':
  case 'v':
  case 'w':
  case 'x':
  case 'y':
  case 'z':
  case '{':
  case '|':
  case '}':
  case '~':
    return TRUE;
  }
  return FALSE;
}

int bwb_islower( int C )
{
   /*
   4.3.1.6 The islower function
   
   Synopsis
   
            #include <ctype.h>
            int islower(int c);
   
   Description
   
      The islower function tests for any lower-case letter or any of an
   implementation-defined set of characters for which none of iscntrl ,
   isdigit , ispunct , or isspace is true.  In the C locale, islower
   returns true only for the characters defined as lower-case letters (as
   defined in $2.2.1).
   */
  switch( C )
  {
  case 'a':
  case 'b':
  case 'c':
  case 'd':
  case 'e':
  case 'f':
  case 'g':
  case 'h':
  case 'i':
  case 'j':
  case 'k':
  case 'l':
  case 'm':
  case 'n':
  case 'o':
  case 'p':
  case 'q':
  case 'r':
  case 's':
  case 't':
  case 'u':
  case 'v':
  case 'w':
  case 'x':
  case 'y':
  case 'z':
    return TRUE;
  }
  return FALSE;
}

int bwb_isprint( int C )
{
   /*
   4.3.1.7 The isprint function
   
   Synopsis
   
            #include <ctype.h>
            int isprint(int c);
   
   Description
   
      The isprint function tests for any printing character including
   space (' ').
   */
  switch( C )
  {
  case ' ':
  case '!':
  case '"':
  case '#':
  case '$':
  case '%':
  case '&':
  case '\'':
  case '(':
  case ')':
  case '*':
  case '+':
  case ',':
  case '-':
  case '.':
  case '/':
  case '0':
  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
  case ':':
  case ';':
  case '<':
  case '=':
  case '>':
  case '?':
  case '@':
  case 'A':
  case 'B':
  case 'C':
  case 'D':
  case 'E':
  case 'F':
  case 'G':
  case 'H':
  case 'I':
  case 'J':
  case 'K':
  case 'L':
  case 'M':
  case 'N':
  case 'O':
  case 'P':
  case 'Q':
  case 'R':
  case 'S':
  case 'T':
  case 'U':
  case 'V':
  case 'W':
  case 'X':
  case 'Y':
  case 'Z':
  case '[':
  case '\\':
  case ']':
  case '^':
  case '_':
  case '`':
  case 'a':
  case 'b':
  case 'c':
  case 'd':
  case 'e':
  case 'f':
  case 'g':
  case 'h':
  case 'i':
  case 'j':
  case 'k':
  case 'l':
  case 'm':
  case 'n':
  case 'o':
  case 'p':
  case 'q':
  case 'r':
  case 's':
  case 't':
  case 'u':
  case 'v':
  case 'w':
  case 'x':
  case 'y':
  case 'z':
  case '{':
  case '|':
  case '}':
  case '~':
    return TRUE;
  }
  return FALSE;
}

int bwb_ispunct( int C )
{
   /*
   4.3.1.8 The ispunct function
   
   Synopsis
   
            #include <ctype.h>
            int ispunct(int c);
   
   Description
   
      The ispunct function tests for any printing character except space
   (' ') or a character for which isalnum is true.
   */
  switch( C )
  {
  case '!':
  case '"':
  case '#':
  case '$':
  case '%':
  case '&':
  case '\'':
  case '(':
  case ')':
  case '*':
  case '+':
  case ',':
  case '-':
  case '.':
  case '/':
  case ':':
  case ';':
  case '<':
  case '=':
  case '>':
  case '?':
  case '@':
  case '[':
  case '\\':
  case ']':
  case '^':
  case '_':
  case '`':
  case '{':
  case '|':
  case '}':
  case '~':
    return TRUE;
  }
  return FALSE;
}

int bwb_isspace( int C )
{
   /*
   4.3.1.9 The isspace function
   
   Synopsis
   
            #include <ctype.h>
            int isspace(int c);
   
   Description
   
      The isspace function tests for the standard white-space characters
   or for any of an implementation-defined set of characters for which
   isalnum is false.  The standard white-space characters are the
   following: space (' '), form feed ('\f'), new-line ('\n'), carriage
   return ('\r'), horizontal tab ('\t'), and vertical tab ('\v').  In the
   C locale, isspace returns true only for the standard white-space
   characters.
   */
  switch( C )
  {
  case '\f':
  case '\n':
  case '\r':
  case '\t':
  case '\v':
  case ' ':
    return TRUE;
  }
  return FALSE;
}

int bwb_isupper( int C )
{
   /*
   4.3.1.10 The isupper function
   
   Synopsis
   
            #include <ctype.h>
            int isupper(int c);
   
   Description
   
      The isupper function tests for any upper-case letter or any of an
   implementation-defined set of characters for which none of iscntrl ,
   isdigit , ispunct , or isspace is true.  In the C locale, isupper
   returns true only for the characters defined as upper-case letters (as
   defined in $2.2.1).
   */
  switch( C )
  {
  case 'A':
  case 'B':
  case 'C':
  case 'D':
  case 'E':
  case 'F':
  case 'G':
  case 'H':
  case 'I':
  case 'J':
  case 'K':
  case 'L':
  case 'M':
  case 'N':
  case 'O':
  case 'P':
  case 'Q':
  case 'R':
  case 'S':
  case 'T':
  case 'U':
  case 'V':
  case 'W':
  case 'X':
  case 'Y':
  case 'Z':
    return TRUE;
  }
  return FALSE;
}

int bwb_isxdigit( int C )
{
   /*
   4.3.1.11 The isxdigit function
   
   Synopsis
   
            #include <ctype.h>
            int isxdigit(int c);
   
   Description
   
      The isxdigit function tests for any hexadecimal-digit character (as
   defined in $3.1.3.2).
   */
  switch( C )
  {
  case '0':
  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
  case 'A':
  case 'B':
  case 'C':
  case 'D':
  case 'E':
  case 'F':
  case 'a':
  case 'b':
  case 'c':
  case 'd':
  case 'e':
  case 'f':
    return TRUE;
  }
  return FALSE;
}

int bwb_tolower( int C )
{
   /*
   4.3.2.1 The tolower function
   
   Synopsis
   
            #include <ctype.h>
            int tolower(int c);
   
   Description
   
      The tolower function converts an upper-case letter to the
   corresponding lower-case letter.
   
   Returns
   
      If the argument is an upper-case letter, the tolower function
   returns the corresponding lower-case letter if there is one; otherwise
   the argument is returned unchanged.  In the C locale, tolower maps
   only the characters for which isupper is true to the corresponding
   characters for which islower is true.
   */
  switch( C )
  {
  case 'A':
    return 'a';
  case 'B':
    return 'b';
  case 'C':
    return 'c';
  case 'D':
    return 'd';
  case 'E':
    return 'e';
  case 'F':
    return 'f';
  case 'G':
    return 'g';
  case 'H':
    return 'h';
  case 'I':
    return 'i';
  case 'J':
    return 'j';
  case 'K':
    return 'k';
  case 'L':
    return 'l';
  case 'M':
    return 'm';
  case 'N':
    return 'n';
  case 'O':
    return 'o';
  case 'P':
    return 'p';
  case 'Q':
    return 'q';
  case 'R':
    return 'r';
  case 'S':
    return 's';
  case 'T':
    return 't';
  case 'U':
    return 'u';
  case 'V':
    return 'v';
  case 'W':
    return 'w';
  case 'X':
    return 'x';
  case 'Y':
    return 'y';
  case 'Z':
    return 'z';
  }
  return C;
}

int bwb_toupper( int C )
{
   /*
   4.3.2.2 The toupper function
   
   Synopsis
   
            #include <ctype.h>
            int toupper(int c);
   
   Description
   
      The toupper function converts a lower-case letter to the corresponding upper-case letter.  
   
   Returns
   
      If the argument is a lower-case letter, the toupper function
   returns the corresponding upper-case letter if there is one; otherwise
   the argument is returned unchanged.  In the C locale, toupper maps
   only the characters for which islower is true to the corresponding
   characters for which isupper is true.
   */
  switch( C )
  {
  case 'a':
    return 'A';
  case 'b':
    return 'B';
  case 'c':
    return 'C';
  case 'd':
    return 'D';
  case 'e':
    return 'E';
  case 'f':
    return 'F';
  case 'g':
    return 'G';
  case 'h':
    return 'H';
  case 'i':
    return 'I';
  case 'j':
    return 'J';
  case 'k':
    return 'K';
  case 'l':
    return 'L';
  case 'm':
    return 'M';
  case 'n':
    return 'N';
  case 'o':
    return 'O';
  case 'p':
    return 'P';
  case 'q':
    return 'Q';
  case 'r':
    return 'R';
  case 's':
    return 'S';
  case 't':
    return 'T';
  case 'u':
    return 'U';
  case 'v':
    return 'V';
  case 'w':
    return 'W';
  case 'x':
    return 'X';
  case 'y':
    return 'Y';
  case 'z':
    return 'Z';
  }
  return C;
}

 
void *
bwb_memcpy(void *s1, const void *s2, size_t n)
{
   /*
   4.11.2.1 The memcpy function

   Synopsis
   
            #include <string.h>
            void *memcpy(void *s1, const void *s2, size_t n);
   
   Description
   
      The memcpy function copies n characters from the object pointed to
   by s2 into the object pointed to by s1 .  If copying takes place
   between objects that overlap, the behavior is undefined.
   
   Returns
   
      The memcpy function returns the value of s1 .  
   */
   if( n > 0 )
   {   
      char * Target;
      char * Source;
      int p;
      
      
      Target = (char *) s1;
      Source = (char *) s2;
      p = 0;
      while( p < n )
      {
         Target[p] = Source[p];
         p++;
      }
   }
   return s1;
}


void *
bwb_memmove(void *s1, const void *s2, size_t n)
{
   /*
   4.11.2.2 The memmove function
   
   Synopsis
   
            #include <string.h>
            void *memmove(void *s1, const void *s2, size_t n);
   
   Description
   
      The memmove function copies n characters from the object pointed to
   by s2 into the object pointed to by s1 .  Copying takes place as if
   the n characters from the object pointed to by s2 are first copied
   into a temporary array of n characters that does not overlap the
   objects pointed to by s1 and s2 , and then the n characters from the
   temporary array are copied into the object pointed to by s1 .
   
   Returns
   
      The memmove function returns the value of s1 .  
   */
   
   if( n > 0 )
   {   
      char * Target;
      char * Source;
      char * Temp;

      Target = (char *) s1;
      Source = (char *) s2;
      Temp   = (char *) malloc(n);
      if( Temp != NULL )
      {
         int p;

         p = 0;
         while( p < n )
         {
            Temp[p] = Source[p];
            p++;
         }
         p = 0;
         while( p < n )
         {
            Target[p] = Temp[p];
            p++;
         }
         free(Temp);
      }
   }
   return s1;
}


char *
bwb_strcpy(char *s1, const char *s2)
{
   /*
   4.11.2.3 The strcpy function
   
   Synopsis
   
            #include <string.h>
            char *strcpy(char *s1, const char *s2);
   
   Description
   
      The strcpy function copies the string pointed to by s2 (including
   the terminating null character) into the array pointed to by s1 .  If
   copying takes place between objects that overlap, the behavior is
   undefined.
   
   Returns
   
      The strcpy function returns the value of s1 .  
   */
   char C;
   int p;
   
   p = 0;
   do
   {
      C = s2[p];
      s1[p] = C;
      p++;
   }
   while( C != BasicNulChar );
   return s1;
}


char *
bwb_strncpy(char *s1, const char *s2, size_t n)
{
   /*
   4.11.2.4 The strncpy function
   
   Synopsis
   
            #include <string.h>
            char *strncpy(char *s1, const char *s2, size_t n);
   
   Description
   
      The strncpy function copies not more than n characters (characters
   that follow a null character are not copied) from the array pointed to
   by s2 to the array pointed to by s1 ./120/ If copying takes place
   between objects that overlap, the behavior is undefined.
   
      If the array pointed to by s2 is a string that is shorter than n
   characters, null characters are appended to the copy in the array
   pointed to by s1 , until n characters in all have been written.
   
   Returns
   
      The strncpy function returns the value of s1 .  
   */
   if( n > 0 )
   {
      char C;
      int p;
      
      p = 0;
      do
      {
         C = s2[p];
         s1[p] = C;
         p++;
      }
      while( C != BasicNulChar && p < n );
      while( p < n )
      {
         s1[p] = BasicNulChar;
         p++;
      }
   }   
   return s1;
}


char *
bwb_strcat(char *s1, const char *s2)
{
   /*
   4.11.3.1 The strcat function
   
   Synopsis
   
            #include <string.h>
            char *strcat(char *s1, const char *s2);
   
   Description
   
      The strcat function appends a copy of the string pointed to by s2
   (including the terminating null character) to the end of the string
   pointed to by s1 .  The initial character of s2 overwrites the null
   character at the end of s1 .  If copying takes place between objects
   that overlap, the behavior is undefined.
   
   Returns
   
      The strcat function returns the value of s1 .  
   */
   char * Temp;
   
   Temp = bwb_strchr( s1, BasicNulChar );
   bwb_strcpy( Temp, s2 );
   return s1;
}



char *
bwb_strncat(char *s1, const char *s2, size_t n)
{
   /*
   4.11.3.2 The strncat function
   
   Synopsis
   
            #include <string.h>
            char *strncat(char *s1, const char *s2, size_t n);
   
   Description
   
      The strncat function appends not more than n characters (a null
   character and characters that follow it are not appended) from the
   array pointed to by s2 to the end of the string pointed to by s1 .
   The initial character of s2 overwrites the null character at the end
   of s1 .  A terminating null character is always appended to the
   result./121/ If copying takes place between objects that overlap, the
   behavior is undefined.
   
   Returns
   
      The strncat function returns the value of s1 .  
   */
   char * Temp;
   
   Temp = bwb_strchr( s1, BasicNulChar );
   bwb_strncpy( Temp, s2, n );
   return s1;
}


int 
bwb_memcmp(const void *s1, const void *s2, size_t n)
{
   /*
   4.11.4.1 The memcmp function
   
   Synopsis
   
            #include <string.h>
            int memcmp(const void *s1, const void *s2, size_t n);
   
   Description
   
      The memcmp function compares the first n characters of the object
   pointed to by s1 to the first n characters of the object pointed to by
   s2 ./122/
   
   Returns
   
      The memcmp function returns an integer greater than, equal to, or
   less than zero, according as the object pointed to by s1 is greater
   than, equal to, or less than the object pointed to by s2 .
   */
   if( n > 0 )
   {
      int p;
      char * L;
      char * R;
      
      p = 0;
      L = (char *) s1;
      R = (char *) s2;
      while( p < n )
      {
         if( L[p] > R[p] )
         {
            return 1;
         }
         if( L[p] < R[p] )
         {
            return -1;
         }
         /* L[p] == R[p] */
         p++;
      }
   }
   return 0;
}


int 
bwb_strcmp(const char *s1, const char *s2)
{
   /*
   4.11.4.2 The strcmp function
   
   Synopsis
   
            #include <string.h>
            int strcmp(const char *s1, const char *s2);
   
   Description
   
      The strcmp function compares the string pointed to by s1 to the
   string pointed to by s2 .
   
   Returns
   
      The strcmp function returns an integer greater than, equal to, or
   less than zero, according as the string pointed to by s1 is greater
   than, equal to, or less than the string pointed to by s2 .
   */
   char C;
   int p;
   
   p = 0;
   do
   {
      if( s1[p] > s2[p] )
      {
         return 1;
      }
      if( s1[p] < s2[p] )
      {
         return -1;
      }
      /* s1[p] == s2[p] */
      C = s1[p];
      p++;
   }
   while( C != BasicNulChar );
   return 0;
}


int 
bwb_strncmp(const char *s1, const char *s2, size_t n)
{
   /*
   4.11.4.4 The strncmp function
   
   Synopsis
   
            #include <string.h>
            int strncmp(const char *s1, const char *s2, size_t n);
   
   Description
   
      The strncmp function compares not more than n characters
   (characters that follow a null character are not compared) from the
   array pointed to by s1 to the array pointed to by s2 .
   
   Returns
   
      The strncmp function returns an integer greater than, equal to, or
   less than zero, according as the possibly null-terminated array
   pointed to by s1 is greater than, equal to, or less than the possibly
   null-terminated array pointed to by s2 .
   */
   if( n > 0 )
   {
      char C;
      int p;
      
      p = 0;
      do
      {
         if( s1[p] > s2[p] )
         {
            return 1;
         }
         if( s1[p] < s2[p] )
         {
            return -1;
         }
         /* s1[p] == s2[p] */
         C = s1[p];
         p++;
      }
      while( C != BasicNulChar && p < n );
   }
   return 0;
}


void *
bwb_memchr(const void *s, int c, size_t n)
{
   /*
   4.11.5.1 The memchr function
   
   Synopsis
   
            #include <string.h>
            void *memchr(const void *s, int c, size_t n);
   
   Description
   
      The memchr function locates the first occurrence of c (converted to
   an unsigned char ) in the initial n characters (each interpreted as
   unsigned char ) of the object pointed to by s .
   
   Returns
   
      The memchr function returns a pointer to the located character, or
   a null pointer if the character does not occur in the object.
   */
   if( n > 0 )
   {
      int p;
      unsigned char *Check;
      unsigned char Find;
      
      p = 0;
      Check = (unsigned char *)s;
      Find = (unsigned char) c;
      do
      {
         if( Check[p] == Find )
         {
            return (void *) &(Check[p]);
         }
         p++;
      }
      while( p < n );
   }
   return NULL;
}


char *
bwb_strchr(const char *s, int c)
{
   /*
   4.11.5.2 The strchr function
   
   Synopsis
   
            #include <string.h>
            char *strchr(const char *s, int c);
   
   Description
   
      The strchr function locates the first occurrence of c (converted to
   a char ) in the string pointed to by s .  The terminating null
   character is considered to be part of the string.
   
   Returns
   
      The strchr function returns a pointer to the located character, or
   a null pointer if the character does not occur in the string.
   */
   int p;
   char Find;
   char C;

   p = 0;   
   Find = (char) c;
   do
   {
      C = s[p];
      if( C == Find )
      {
         return (char *) &(s[p]);
      }
      p++;
   }
   while( C != BasicNulChar );
   return NULL;
}


char *
bwb_strrchr(const char *s, int c)
{
   /*
   4.11.5.5 The strrchr function
   
   Synopsis
   
            #include <string.h>
            char *strrchr(const char *s, int c);
   
   Description
   
      The strrchr function locates the last occurrence of c (converted to
   a char ) in the string pointed to by s .  The terminating null
   character is considered to be part of the string.
   
   Returns
   
      The strrchr function returns a pointer to the character, or a null
   pointer if c does not occur in the string.
   */
   int p;
   char Find;
   char * Found;
   char C;

   p = 0;   
   Find = (char) c;
   Found = NULL;
   do
   {
      C = s[p];
      if( C == Find )
      {
         Found = (char *) &(s[p]);
      }
      p++;
   }
   while( C != BasicNulChar );
   return Found;
}


void *
bwb_memset(void *s, int c, size_t n)
{
   /*
   4.11.6.1 The memset function
   
   Synopsis
   
            #include <string.h>
            void *memset(void *s, int c, size_t n);
   
   Description
   
      The memset function copies the value of c (converted to an unsigned
   char ) into each of the first n characters of the object pointed to by
   s .
   
   Returns
   
      The memset function returns the value of s .  
   */
   if( n > 0 )
   {
      int p;
      unsigned char * Target;
      unsigned char Value;
      
      p = 0;
      Target = (unsigned char *)s;
      Value = (unsigned char) c;
      do
      {
         Target[p] = Value;
         p++;
      }
      while( p < n );
   }
   return s;
}



size_t 
bwb_strlen(const char *s)
{
   /*
   4.11.6.3 The strlen function
   
   Synopsis
   
            #include <string.h>
            size_t strlen(const char *s);
   
   Description
   
      The strlen function computes the length of the string pointed to by s .  
   
   Returns
   
      The strlen function returns the number of characters that precede
   the terminating null character.
   */
   size_t p;
   p = 0;
   while( s[p] != BasicNulChar )
   {
      p++;
   }
   return p;
}


#if HAVE_UNIX_GCC

/* these are intrinsic C functions in my environment using -ansi */

#else          /* ! HAVE_UNIX_GCC */

unsigned int
sleep( unsigned int X )
{
   return X;
}

#endif            /* ! HAVE_UNIX_GCC */


double
bwb_rint(double x)
{
   /* BASIC dialects have different rounding rules */
   double Result;
   if( x < 0 )
   {
      return - bwb_rint(-x);
   }
   /* x >= 0 */
   switch( My->OptionRoundType )
   {
   case C_OPTION_ROUND_BANK:
      /* normal financial rounding */
      Result = floor(x + 0.5);
      if( x - floor( x ) == 0.5 )
      {
         /* midway */
         double Half;
         Half = Result / 2.0;
         if( Half != floor( Half ) )
         {
            /* odd -> even */
            Result--;
         }
      }
      break;
   case C_OPTION_ROUND_MATH:
      /* normal mathematical rounding */
      Result = floor(x + 0.5);
      break;
   case C_OPTION_ROUND_TRUNCATE:
      /* simple truncation */
      Result = floor(x);
      break;
   }
   return Result;
}

int
bwb_stricmp(const char *s1, const char *s2)
{
   const unsigned char *p1;
   const unsigned char *p2;

   p1 = (const unsigned char *) s1;
   p2 = (const unsigned char *) s2;
   while (*p1 != BasicNulChar)
   {
      char            c1;
      char            c2;
      c1 = bwb_toupper(*p1);
      c2 = bwb_toupper(*p2);
      if (c1 < c2)
      {
         return -1;
      }
      if (c1 > c2)
      {
         return 1;
      }
      p1++;
      p2++;
   }
   if (*p2 == BasicNulChar)
   {
      return 0;
   }
   return -1;
}

int
bwb_strnicmp(const char *s1, const char *s2, size_t n)
{
   const unsigned char *p1;
   const unsigned char *p2;
   size_t          x = 0;

   p1 = (const unsigned char *) s1;
   p2 = (const unsigned char *) s2;
   while (x < n)
   {
      char            c1;
      char            c2;
      c1 = bwb_toupper(p1[x]);
      c2 = bwb_toupper(p2[x]);
      if (c1 < c2)
      {
         return -1;
      }
      if (c1 > c2)
      {
         return 1;
      }
      if (c1 == BasicNulChar)
      {
         return 0;
      }
      x++;
   }
   return 0;
}


/* EOF */

