/***************************************************************
  
        bwb_inp.c       Input 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"





/* Declarations of functions visible to this file only */




static LineType *bwb_xinp(LineType * l, FILE * f, char delimit);
static int       inp_str( /* LineType * l, */ char *buffer, char *var_list, int *position, int IsFake);
static int       inp_const(char *m_buffer, char *s_buffer, int *position);
static int       inp_assign(char *b, VariableType * v, int IsFake, int IsInput);
static int       read_data(VariableType *v);




/* ResultCode */
#define RESULT_OK 0
#define RESULT_UNKNOWN 1
#define RESULT_TYPE_MMISMATCH 2
#define RESULT_ARITHMETIC_OVERFLOW 3




/***************************************************************
  
      FUNCTION:       bwx_input()
  
   DESCRIPTION:    This function outputs the string pointed
         to by 'prompt', then inputs a character
         string.
  
***************************************************************/

int bwb_is_eof( FILE * fp )
{
   /* 
   Have you ever wondered why C file I/O is slow?   Here is the reason:
   feof() is not set until after a file read error occurs; sad but true. 
   In order to determine whether you are at the end-of-file,
   you have to call both ftell() and fseek() twice,
   which effectively trashes any I/O cache scheme.  
   */
   if( fp != NULL )
   {
      long current;
      long total;
   
      current = ftell( fp );
      fseek( fp, 0, SEEK_END );
      total = ftell( fp ) ;
      if( total == current )
      {
         /* EOF */
         return TRUE;
      }
      else
      {
         /* NOT EOF */
         fseek( fp, current, SEEK_SET );
         return FALSE;
      }
   }
   /* a closed file is always EOF */
   return TRUE;
}


static void clean_cr_lf( char * buffer )
{
   /* 
   some compilers remove CR, but not LF.
   some compilers remove LF, but not CR.
   some compilers remove CR/LF but not LF/CR.
   some compilers remove either CR or LF.
   some compilers remove first CR or LF, but not second LF or CR.
   */
   
    char *E;
    E = bwb_strchr(buffer, '\r');
    if( E != NULL )
    {
       *E = BasicNulChar;
    }
    E = bwb_strchr(buffer, '\n');
    if( E != NULL )
    {
       *E = BasicNulChar;
    }
}

int
bwx_input(char *prompt, char *buffer)
{

   bwx_DEBUG(__FUNCTION__);

   prn_xprintf(prompt);

   fflush( My->SYSOUT->cfp );

   /* for automated testing, TAPE command */
   if (My->IsCommandLineFile == TRUE)
   {
      if ( My->ExternalInputFile != NULL )
      {
         if( fgets(buffer, BasicStringLengthMax, My->ExternalInputFile) == NULL
         ||  feof( My->ExternalInputFile )
         )
         {
            /* stop reading from external file once all INPUT lines have been read */
            fclose(My->ExternalInputFile); /* My->ExternalInputFile != NULL */
            My->ExternalInputFile = NULL;
         }
         else
         {
            fputs( buffer, My->SYSOUT->cfp );
            fflush(My->SYSOUT->cfp);
            clean_cr_lf( buffer );
            ResetConsoleColumn();
            return TRUE;
         }
      }
   }
   fgets(buffer, BasicStringLengthMax, My->SYSIN->cfp);
   clean_cr_lf( buffer );
   ResetConsoleColumn();
   return TRUE;
}


LineType *
bwb_BACKSPACE(LineType * l)
{

   bwx_DEBUG(__FUNCTION__);

   My->CurrentFile = My->SYSIN;

   if ( line_skip_char(l,BasicFileNumberPrefix) )
   {
      /* BACKSPACE # filenum */
      int             FileNumber;

      if( line_read_integer_expression(l, &FileNumber) == FALSE )
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      } 
      if( FileNumber < 0 )
      {
         /* "BACKSPACE # -1" is silently ignored */
         return bwb_zline(l);
      }   
      if( FileNumber == 0 )
      {
         /* "BACKSPACE # 0" is silently ignored */
         return bwb_zline(l);
      }   
      My->CurrentFile = find_file_by_number( FileNumber );
      if( My->CurrentFile == NULL )
      {
         WARN_BAD_FILE_NUMBER;
         return bwb_zline(l);
      }
      if ((My->CurrentFile->mode & DEVMODE_READ) == 0)
      {
         WARN_BAD_FILE_NUMBER;
         return bwb_zline(l);
      }
      /* not for the console */
      /* if( TRUE ) */
      {
         FILE * f;
         long Offset;
         int DelimiterCount;
         int InQuote;
         int C;

         f = My->CurrentFile->cfp;
         Offset = ftell( f );
         Offset--;
         DelimiterCount = 0;
         InQuote = FALSE;
         
         AGAIN:
         if( Offset <= 0 )
         {
            goto DONE;
         }
         fseek( f, Offset, SEEK_SET );
         C = fgetc( f );
         
         if( InQuote )
         {
            if( C == BasicQuoteChar )
            {
               InQuote = FALSE;
            }
            Offset--;
            goto AGAIN;
         }
         
         if( C == BasicQuoteChar )
         {
            InQuote = TRUE;
            Offset--;
            goto AGAIN;
         }
         
         
         if( C == ',' )
         {
            DelimiterCount++;
            if( DelimiterCount > 1 )
            {
               Offset++;
               goto DONE;
            }
            Offset--;
            goto AGAIN;
         }
         
         if( C == '\n' )
         {
            DelimiterCount++;
            if( DelimiterCount > 1 )
            {
               Offset++;
               goto DONE;
            }
            Offset--;
            if( Offset <= 0 )
            {
               goto DONE;
            }
            fseek( f, Offset, SEEK_SET );
            C = fgetc( f );
            if( C == '\r' )
            {
               Offset--;
            }
            goto AGAIN;
         }
         
         if( C == '\r' )
         {
            DelimiterCount++;
            if( DelimiterCount > 1 )
            {
               Offset++;
               goto DONE;
            }
            Offset--;
            if( Offset <= 0 )
            {
               goto DONE;
            }
            fseek( f, Offset, SEEK_SET );
            C = fgetc( f );
            if( C == '\n' )
            {
               Offset--;
            }
            goto AGAIN;
         }
         
         Offset--;
         goto AGAIN;
         
         DONE:
         if( Offset < 0 )
         {
            Offset = 0;
         }
         fseek( f, Offset, SEEK_SET );
      }
   }
   /* BACKSPACE for console is silently ignored */
   return bwb_zline(l);
}




/***************************************************************
  
        FUNCTION:       bwb_read()
  
        DESCRIPTION:    This function implements the BASIC READ
                        statement.
  
        SYNTAX:         READ variable[, variable...]
  
***************************************************************/

LineType *
bwb_READ(LineType * l)
{

   bwx_DEBUG(__FUNCTION__);

   My->CurrentFile = My->SYSIN;

   if ( line_skip_char(l,BasicFileNumberPrefix) )
   {
      /* READ # filenum, varlist */
      int             FileNumber;

      if( line_read_integer_expression(l, &FileNumber) == FALSE )
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      }

      if( My->CurrentVersion->OptionVersionBitmask & ( C77 ) )
      {
         /* 
         CBASIC-II: SERIAL & RANDOM file reads
         READ  # file_number                 ; [ scalar_variable   [ , ... ] ] ' SERIAL
         READ  # file_number , record_number ; [ scalar_variable   [ , ... ] ] ' RANDOM
         */
         if( FileNumber <= 0 )
         {
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         /* normal file */
         My->CurrentFile = find_file_by_number( FileNumber );
         if( My->CurrentFile == NULL )
         {
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         if( line_skip_char( l, ',' )  /* comma-specific */ )
         {
            /* 
            READ # file_number , record_number ; scalar [, scalar] ' RANDOM read
            */
            /* get the RecordNumber */
            int RecordNumber;
            
            if( (My->CurrentFile->mode & DEVMODE_RANDOM) == 0 )
            {
               WARN_BAD_FILE_MODE;
               return bwb_zline(l);
            }
            if( My->CurrentFile->width <= 0 )
            {
               WARN_FIELD_OVERFLOW;
               return bwb_zline(l);
            }
            if( line_read_integer_expression( l, &RecordNumber ) == FALSE )
            {
               WARN_SYNTAX_ERROR;
               return bwb_zline(l);
            }
            if( RecordNumber <= 0 )
            {
               WARN_BAD_RECORD_NUMBER;
               return bwb_zline(l);
            }
            RecordNumber--; /* BASIC to C */
            /* if( TRUE ) */
            {
               long offset;
               offset = RecordNumber;
               offset *= My->CurrentFile->width;
               fseek( My->CurrentFile->cfp, offset, SEEK_SET );
            }
         }
         if( line_is_eol( l ) )
         {
            /* READ # filenum          */
            /* READ # filenum , recnum */
         }
         else
         if( line_skip_char( l, ';' ) )
         {
            /* READ # filenum          ; */
            /* READ # filenum , recnum ; */
         }
         else
         {
            WARN_SYNTAX_ERROR;
            return bwb_zline(l);
         }
         /* input is not from #0, so branch to bwb_xinp() */
         return bwb_xinp(l, My->CurrentFile->cfp, My->CurrentFile->delimit);
      }
      /* 
      SERIAL file reads:
      READ # file_number   
      READ # file_number [, scalar]
      */
      if ( line_skip_comma(l) )
      {
         /* OK */
      }
      else
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      }
      if( FileNumber < 0 )
      {
         /* "READ # -1" is an error */
         WARN_BAD_FILE_NUMBER;
         return bwb_zline(l);
      }
      if( FileNumber > 0 )
      {
         /* normal file */
         My->CurrentFile = find_file_by_number( FileNumber );
         if( My->CurrentFile == NULL )
         {
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         if ((My->CurrentFile->mode & DEVMODE_READ) == 0)
         {
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         /* input is not from #0, so branch to bwb_xinp() */
         return bwb_xinp(l, My->CurrentFile->cfp, My->CurrentFile->delimit);
      }
      /* "READ # 0, varlist" is the same as "READ varlist" */
   }
   /* READ varlist */
   do
   {
      VariableType *v;

      /* get a variable */
      if( (v = line_read_scalar( l )) == NULL )
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      }
      /* READ data into the variable */
      if( read_data(v) == FALSE )
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      }
   }
   while( line_skip_comma(l) );
   return bwb_zline(l);
}

/***************************************************************
  
        FUNCTION:       bwb_data()
  
        DESCRIPTION:    This function implements the BASIC DATA
                        statement, although at the point at which
                        DATA statements are encountered, no
                        processing is done.  All actual processing
                        of DATA statements is accomplished by READ
                        (bwb_read()).
  
        SYNTAX:         DATA constant[, constant]...
  
***************************************************************/

LineType *
bwb_DATA(LineType * l)
{

   bwx_DEBUG(__FUNCTION__);

   line_skip_eol(l);

   return bwb_zline(l);
}






/***************************************************************
  
        FUNCTION:       bwb_restore()
  
        DESCRIPTION:    This function implements the BASIC RESTORE
                        statement.
  
        SYNTAX:         RESTORE [line number]
  
***************************************************************/

LineType *
bwb_RESET(LineType * l)
{
   /* RESET filename$ [, ...] */      
   VariantType E;
   VariantType *e = &E;  /* no leaks */

   bwx_DEBUG(__FUNCTION__);

   My->CurrentFile = My->SYSIN;

   do
   {
      line_skip_spaces(l);
      if( line_read_expression( l, e ) == FALSE )
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      }
      if( e->TypeChar == BasicStringSuffix )
      {
         /* STRING */
         /* RESET filename$ ... */
         My->CurrentFile = find_file_by_name( e->Buffer );
      }
      else
      {
         /* NUMBER -- file must already be OPEN */
         /* RESET filenumber ... */
         My->CurrentFile = find_file_by_number( (int) bwb_rint( e->Number ) );
      }  
      RELEASE( e );  
      if( My->CurrentFile == NULL )
      {
         /* file not OPEN */
         /* silently ignored */
      }
      else
      if( My->CurrentFile == My->SYSIN )
      {
         /* silently ignored */
      }
      else
      if( My->CurrentFile == My->SYSOUT )
      {
         /* silently ignored */
      }
      else
      if( My->CurrentFile == My->SYSPRN )
      {
         /* silently ignored */
      }
      else
      {
         /* normal file is OPEN */
         My->CurrentFile->width = 0;
         My->CurrentFile->col = 1;
         My->CurrentFile->row = 1;
         My->CurrentFile->delimit = ',';
         fseek( My->CurrentFile->cfp, 0, SEEK_SET );
      }
   }
   while( line_skip_comma(l) );
   return bwb_zline(l);
}

LineType *
bwb_CLOSE(LineType * l)
{
   /* CLOSE filename$ [, ...] */      
   VariantType E;
   VariantType *e = &E;  /* no leaks */

   bwx_DEBUG(__FUNCTION__);

   My->CurrentFile = My->SYSIN;

   do
   {
      line_skip_spaces(l);
      if( line_read_expression( l, e ) == FALSE )
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      }
      if( e->TypeChar == BasicStringSuffix )
      {
         /* STRING */
         /* CLOSE filename$ ... */
         My->CurrentFile = find_file_by_name( e->Buffer );
      }
      else
      {
         /* NUMBER -- file must already be OPEN */
         /* CLOSE filenumber ... */
         My->CurrentFile = find_file_by_number(  (int) bwb_rint( e->Number ) );
      }  
      RELEASE( e );  
      if( My->CurrentFile == NULL )
      {
         /* file not OPEN */
         /* silently ignored */
      }
      else
      if( My->CurrentFile == My->SYSIN )
      {
         /* silently ignored */
      }
      else
      if( My->CurrentFile == My->SYSOUT )
      {
         /* silently ignored */
      }
      else
      if( My->CurrentFile == My->SYSPRN )
      {
         /* silently ignored */
      }
      else
      {
         /* normal file is OPEN */
         file_clear( My->CurrentFile );
      }
   }
   while( line_skip_comma(l) );
   return bwb_zline(l);
}


LineType *
bwb_RESTORE(LineType * l)
{
   int LineNumber;
   LineType *x;

   bwx_DEBUG(__FUNCTION__);

   My->CurrentFile = My->SYSIN;

   if( My->CurrentVersion->OptionVersionBitmask & ( I70 ) )
   {
      /* RESTORE [comment] */      
      line_skip_eol(l);
      My->data_line =  My->bwb_start.next;
      My->data_pos =  My->data_line->Startpos;
      return bwb_zline(l);
   }


   /* get the first element beyond the starting position */

   if( line_skip_char(l, BasicFileNumberPrefix ) )
   {
      /* RESTORE # X */
      int FileNumber;

      if( line_read_integer_expression( l, &FileNumber ) == FALSE )
      {
         WARN_BAD_FILE_NUMBER;
         return bwb_zline(l);
      }
      if( FileNumber < 0 )
      {
         /* "RESTORE # -1" is silently ignored */
         return bwb_zline(l);
      }
      if( FileNumber > 0 )
      {
         /* normal file */
         My->CurrentFile = find_file_by_number( FileNumber );
         if( My->CurrentFile == NULL )
         {
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         if( My->CurrentFile->mode != DEVMODE_CLOSED )
         {
            if( My->CurrentFile->cfp != NULL )
            {
               fclose( My->CurrentFile->cfp ); /* My->CurrentFile->cfp != NULL */
            }
            if( My->CurrentFile->buffer != NULL )
            {
               FREE( My->CurrentFile->buffer, "bwb_FILES" );
            }
         }
         My->CurrentFile->width = 0;
         My->CurrentFile->col = 1;
         My->CurrentFile->row = 1;
         My->CurrentFile->delimit = ',';
         My->CurrentFile->buffer = NULL;
         My->CurrentFile->mode = DEVMODE_CLOSED;
         if( bwb_strcmp( My->CurrentFile->filename, "*" ) != 0 )
         {
            if( (My->CurrentFile->cfp = fopen( My->CurrentFile->filename, "r" )) == NULL )
            {
               WARN_BAD_FILE_NAME;
               return bwb_zline(l);
            }
            My->CurrentFile->mode = DEVMODE_INPUT;
         }
         /* OK */
         return bwb_zline(l);
      }
      /* "RESTORE # 0" is the same as "RESTORE"  */
   }
   if( line_is_eol(l) == TRUE )
   {
      /* RESTORE */
      My->data_line =  My->bwb_start.next;
      My->data_pos =  My->data_line->Startpos;
      return bwb_zline(l);
   }
   /* RESTORE linenumber */
   if( line_read_integer_expression(l, &LineNumber) == FALSE )
   {
      WARN_SYNTAX_ERROR;
      return bwb_zline(l);
   }
   /* check for target label */
   x = find_line_number( LineNumber, TRUE );
   if (x != NULL)
   {
      /* reassign  My->data_line */
      My->data_line = x;
      My->data_pos = x->Startpos;
      return bwb_zline(l);
   }
   WARN_SYNTAX_ERROR;
   return bwb_zline(l);
}

/***************************************************************
  
        FUNCTION:       bwb_input()
  
        DESCRIPTION:    This function implements the BASIC INPUT
                        statement.
  
        SYNTAX:         INPUT [;][prompt$;]variable[$,variable]...
                        INPUT#n variable[$,variable]...
  
***************************************************************/

LineType *
bwb_GET(LineType * l)
{
   bwx_DEBUG(__FUNCTION__);

   if( My->CurrentVersion->OptionVersionBitmask & ( I70 | I73 ) )
   {
      /* GET filename$ , scalar [, ...] */
      VariantType E;
      VariantType *e = &E;  
   
   
      My->CurrentFile = My->SYSIN;
   
      line_skip_spaces(l);
      if( line_read_expression( l, e ) == FALSE )
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      }
      if( e->TypeChar == BasicStringSuffix )
      {
         /* STRING */
         /* GET filename$ ... */
         if( is_empty_filename( e->Buffer ) )
         {
            /* "GET # 0" is an error */
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         My->CurrentFile = find_file_by_name( e->Buffer );
         if( My->CurrentFile == NULL )
         {
            /* implicitly OPEN for reading */
            My->CurrentFile = file_new();
            My->CurrentFile->cfp = fopen(e->Buffer, "r");
            if( My->CurrentFile->cfp == NULL )
            {
               /* bad file name */
               WARN_BAD_FILE_NUMBER;
               return bwb_zline(l);
            }
            My->CurrentFile->FileNumber = file_next_number();
            My->CurrentFile->mode = DEVMODE_INPUT;
            My->CurrentFile->width = 0;
            /* WIDTH == RECLEN */
            My->CurrentFile->col = 1;
            My->CurrentFile->row = 1;
            My->CurrentFile->delimit = ',';
            My->CurrentFile->buffer = NULL;
            bwb_strcpy(My->CurrentFile->filename, e->Buffer);
         }
      }
      else
      {
         /* NUMBER -- file must already be OPEN */
         /* GET filenumber ... */
         if( e->Number < 0 )
         {
            /* "GET # -1" is an error */
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         if( e->Number == 0 )
         {
            /* "GET # 0" is an error */
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         My->CurrentFile = find_file_by_number( (int) bwb_rint( e->Number ) );
         if( My->CurrentFile == NULL )
         {
            /* file not OPEN */
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
      }  
      RELEASE( e );  
      if( My->CurrentFile == NULL )
      {
         WARN_BAD_FILE_NUMBER;
         return bwb_zline(l);
      }
      if (( My->CurrentFile->mode & DEVMODE_READ) == 0)
      {
         WARN_BAD_FILE_NUMBER;
         return bwb_zline(l);
      }
      if ( line_skip_comma(l) )
      {
         /* OK */
      }
      else
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      }
      return bwb_xinp(l, My->CurrentFile->cfp, My->CurrentFile->delimit);
   }
   else
   if( My->CurrentVersion->OptionVersionBitmask & ( D71 ) )
   {
      /* GET # file_number [ , RECORD record_number ] */
      int file_number = 0;
      if( line_skip_char( l, BasicFileNumberPrefix ) == FALSE )
      {
         /* OPTIONAL */
      }
      if( line_read_integer_expression( l, &file_number ) == FALSE )
      {
         WARN_BAD_FILE_NUMBER;
         return bwb_zline( l );
      }
      if( file_number < 1 )
      {
         WARN_BAD_FILE_NUMBER;
         return bwb_zline( l );
      }
      My->CurrentFile = find_file_by_number( file_number );
      if( My->CurrentFile == NULL )
      {
         WARN_BAD_FILE_NUMBER;
         return bwb_zline( l );
      }
      if( My->CurrentFile->mode != DEVMODE_RANDOM )
      {
         WARN_BAD_FILE_NUMBER;
         return bwb_zline( l );
      }
      if( My->CurrentFile->width <= 0 )
      {
         WARN_BAD_FILE_NUMBER;
         return bwb_zline( l );
      }
      if( line_is_eol( l ) )
      {
         /* GET # file_number */
      }
      else
      {
         /* GET # file_number , RECORD record_number */
         int record_number = 0;
         long offset = 0;
         if( line_skip_comma( l ) == FALSE )
         {
            WARN_SYNTAX_ERROR;
            return bwb_zline( l );
         }
         if( line_skip_word( l, "RECORD" ) == FALSE )
         {
            WARN_SYNTAX_ERROR;
            return bwb_zline( l );
         }
         if( line_read_integer_expression( l, &record_number ) == FALSE )
         {
            WARN_BAD_RECORD_NUMBER;
            return bwb_zline( l );
         }
         if( record_number <= 0 )
         {
            WARN_BAD_RECORD_NUMBER;
            return bwb_zline( l );
         }
         record_number--; /* BASIC to C */
         offset = record_number;
         offset *= My->CurrentFile->width;
         if (fseek(My->CurrentFile->cfp, offset, SEEK_SET) != 0)
         {
            WARN_BAD_RECORD_NUMBER;
            return bwb_zline( l );
         }
      }
      /* if( TRUE ) */
      {
         int i;
         for (i = 0; i < My->CurrentFile->width; i++)
         {
            int c;
            c = fgetc( My->CurrentFile->cfp );
            if( /* EOF */ c < 0 )
            {
               c = BasicNulChar;
            }
            My->CurrentFile->buffer[i] = c;
         }
      }
      field_get( My->CurrentFile );
      /* OK */
      return bwb_zline( l );
   }
   WARN_SYNTAX_ERROR;
   return bwb_zline(l);
}

LineType *
bwb_INPUT(LineType * l)
{
   int             is_prompt;
   int             suppress_qm;
   char            tbuf[BasicStringLengthMax + 1];
   char            pstring[BasicStringLengthMax + 1];
   int             Loop;
   int             LastPosition;

   bwx_DEBUG(__FUNCTION__);

   My->CurrentFile = My->SYSIN;

   pstring[0] = BasicNulChar;

   if ( line_skip_char(l,BasicFileNumberPrefix) )
   {
      /* INPUT # X */
      int FileNumber;

      if( line_read_integer_expression(l, &FileNumber) == FALSE )
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      }
      if ( line_skip_comma(l) )
      {
         /* OK */
      }
      else
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      }
      if( FileNumber < 0 )
      {
         /* "INPUT # -1" is an error */
         WARN_BAD_FILE_NUMBER;
         return bwb_zline(l);
      }
      if( FileNumber > 0 )
      {
         /* normal file */
         My->CurrentFile = find_file_by_number( FileNumber );
         if( My->CurrentFile == NULL )
         {
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         if ((My->CurrentFile->mode & DEVMODE_READ) == 0)
         {
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         return bwb_xinp(l, My->CurrentFile->cfp, ',' );
      }
      /* "INPUT #0, varlist" is the same as "INPUT varlist"  */
   }
   /* from this point we presume that input is from My->SYSIN */

   /* check for a semicolon or a quotation mark, not in first position:
    * this should indicate a prompt string */

   suppress_qm = is_prompt = FALSE;

   line_skip_spaces(l);

   if( line_skip_comma( l ) )
   {
      /* INPUT ; "prompt" ... */
      /* INPUT , "prompt" ... */
      suppress_qm = TRUE;
   }
   if( line_peek_char( l, BasicQuoteChar ) )
   {
      is_prompt = TRUE;
   }

   /* get prompt string and print it */

   if (is_prompt == TRUE)
   {
      /* get string element */
      inp_const(l->buffer, pstring, &(l->position)); /* bwb_INPUT prompt */

      /* advance past semicolon to beginning of variable */
      /*--------------------------------------------------------*/
      /* Since inp_const was just called and inp_adv is called  */
      /* within that, it will have already noted and passed the */
      /* comma by the time it gets here.  Therefore one must    */
      /* refer instead to the last returned value for inp_adv!  */
      /* (JBV, 10/95)                                           */
      /*--------------------------------------------------------*/
      /* suppress_qm = inp_adv( l->buffer, &( l->position ) ); */
      suppress_qm = My->last_inp_adv_rval;

   }        /* end condition: prompt string */
   /* print out the question mark delimiter unless it has been suppressed */

   if (suppress_qm != TRUE)
   {
      pstring[ BasicStringLengthMax - 2 ] = BasicNulChar;
      bwb_strcat(pstring, "? ");
   }

   if( My->CurrentVersion->OptionVersionBitmask & ( C77 ) )
   {
      /* 
      CBASIC-II: INPUT "prompt" ; LINE variable$
      */
      if( line_skip_word( l, "LINE" ) )
      {
         /* INPUT "prompt" ; LINE variable$ */
         VariableType * v;
         
         if( (v = line_read_scalar( l )) == NULL )
         {
            WARN_SYNTAX_ERROR;
            return bwb_zline( l );
         }
         if ( VAR_IS_STRING( v ) )
         {
            VariantType variant;
            
            bwx_input(pstring, tbuf);
            bwb_stripcr(tbuf);
            
            variant.TypeChar = '$';
            variant.Buffer = tbuf;
            variant.Length = bwb_strlen( variant.Buffer );
            if( var_set( v, &variant ) == FALSE )
            {
               WARN_VARIABLE_NOT_DECLARED;
               return bwb_zline( l );
            }
            return bwb_zline( l );
         }
         WARN_TYPE_MISMATCH;
         return bwb_zline( l );
      }
   }


   Loop = TRUE;
   LastPosition = l->position;
   while (Loop == TRUE)
   {
      /* read a line into the input buffer */
      int Result;
      
      bwx_input(pstring, tbuf);
      bwb_stripcr(tbuf);

      /* reset print column to account for LF at end of fgets() */
      ResetConsoleColumn();

      l->position = LastPosition;
      
      Result = inp_str( /* l, */ tbuf, l->buffer, &(l->position), TRUE); /* bwb_INPUT */
      if( Result > 0 ) /* bwb_INPUT */
      {
         /* successful input, FAKE run  */
         l->position = LastPosition;
         Result = inp_str( /* l, */ tbuf, l->buffer, &(l->position), FALSE); /* bwb_INPUT */
         if( Result > 0 ) 
         {
            /* successful input, REAL run  */
            Loop = FALSE;
         }
      }
      else
      if( Result < 0 )
      {
         /* syntax error, FAKE run  */
         Loop = FALSE;
      }
      else
      {
         fputs( "*** Retry INPUT ***\n", My->SYSOUT->cfp );
      }
   }        /* while( Loop == TRUE ) */
   return bwb_zline(l);;


}


static int file_read_value( FILE * f, char delimit, VariableType *v )
{
   char            tbuf[BasicStringLengthMax + 1];
   int             c;   /* character */


         /* advance beyond whitespace or comma in data buffer */
         /* Advance to next line if end of buffer */
         /* advance beyond whitespace in data buffer */
         /* leading whitespace is NOT part of the DATA item */
         do
         {
            c = fgetc(f);
            if (c < 0)
            {
               /* EOF */
               return FALSE;
            }
            if( c == delimit )
            {
               break;
            }
         }
         while ( ! bwb_isgraph(c) );

         /* now at last we have a variable in v that needs to
          * be assigned data from the data_buffer at position
          * My->data_pos.  What remains to be done is to
          * get one single bit of data, a string constant or
          * numerical constant, into the small buffer */

         {

            int             string; /* a quoted string */
            int             s_pos;
            int             loop;


            string = FALSE;
            s_pos = 0;
            loop = TRUE;

            /* build the constant string */
            while (loop == TRUE)
            {

               if (c == delimit)
               {
                  if (string == FALSE)
                  {
                     /* end of argument */
                     loop = FALSE;
                  }
                  else
                  {
                     /* internal comma is
                      * part of the DATA
                      * item */
                     tbuf[s_pos] = c;
                     ++s_pos;
                  }
               }
               else
               if ( ! bwb_isprint(c) )
               {
                  /* END-OF-LINE */
                  loop = FALSE;
               }
               else
               if (c == BasicQuoteChar)
               {
                  /* Once we finish reading a
                   * quoted string,  we want
                   * to continue reading spaces 
                   * until EOL or comma */
                  if (string == TRUE)
                  {
                     string = FALSE;
                  }
                  else
                  {
                     string = TRUE;
                  }
                  tbuf[s_pos] = c;
                  ++s_pos;
               }
               else
               {
                  tbuf[s_pos] = c;
                  ++s_pos;
               }
               if (loop == TRUE)
               {
                  /* read another character */
                  c = fgetc(f);
                  if (c < 0)
                  {
                     /* EOF */
                     return FALSE;
                  }
               }
            }

            if (string == FALSE)
            {
               /* trailing whitespace is NOT part of
                * the DATA item */
               while (s_pos > 0 && tbuf[s_pos - 1] == ' ')
               {
                  s_pos--;
               }
            }
            tbuf[s_pos] = BasicNulChar;
            /* clean-up quoted string */
            if (s_pos > 0 && tbuf[0] == BasicQuoteChar)
            {
               /* not an empty string */
               char           *Q;

               Q = bwb_strrchr(&tbuf[1], BasicQuoteChar);
               if (Q != NULL)
               {
                  *Q = BasicNulChar;
               }
               bwb_strcpy(tbuf, &(tbuf[1]) );
            }
         }
         /* if( TRUE ) */
         {
            VariantType variant;

            variant.TypeChar = v->VariableTypeChar;

            if ( VAR_IS_STRING( v ) )
            {
               variant.Buffer = tbuf;
               variant.Length = bwb_strlen( variant.Buffer );
            }
            else
            {
               /* N = VAL( A$ ) */
               BasicNumberType Value = 0;
               if (tbuf[0] != BasicNulChar)
               {
                  int             ScanResult;
                  ScanResult = sscanf(tbuf, BasicNumberScanFormat, &Value);
                  if (ScanResult != 1)
                  {
                     /* not a number */
                     Value = 0;
                  }
                  else
                  {
                     /* OK */
                  }
               }
               variant.Number = Value;
            }
            if( var_set( v, &variant ) == FALSE )
            {
               WARN_VARIABLE_NOT_DECLARED;
               return FALSE;
            }
            
         }
         /* OK */
         return TRUE;
}

static LineType * bwb_xinp(LineType * l, FILE * f, char delimit)
{
   /* INPUT # is similar to READ, where each file line is a DATA line */

   int             main_loop;
   VariableType *v;


   bwx_DEBUG(__FUNCTION__);

   if( My->CurrentVersion->OptionVersionBitmask & ( C77 ) )
   {
      /* 
      CBASIC-II: READ # filenumber [, recnum ] ; LINE variable$
      */
      if( line_skip_word( l, "LINE" ) )
      {
         /* INPUT "prompt" ; LINE variable$ */
         VariableType * v;
         
         if( (v = line_read_scalar( l )) == NULL )
         {
            WARN_SYNTAX_ERROR;
            return bwb_zline( l );
         }
         if ( VAR_IS_STRING( v ) )
         {
            char tbuf[ BasicStringLengthMax + 1 ];
            
            if( fgets( tbuf, BasicStringLengthMax, f ) == NULL || feof( f ) )
            {
               /* IF END # file_number THEN line_number */
               if( My->CurrentFile->EOF_LineNumber > 0 )
               {
                  LineType *x;
                  
                  x = find_line_number( My->CurrentFile->EOF_LineNumber, TRUE ); /* not found in the cache */
                  if (x != NULL)
                  {
                     /* FOUND */
                     line_skip_eol(l);
                     x->position = 0;
                     return x;
                  }
                  /* NOT FOUND */
                  WARN_UNDEFINED_LINE;
                  return bwb_zline(l);            
               }
            }
            bwb_stripcr(tbuf);
            /* if( TRUE ) */
            {
               VariantType variant;
               
               variant.TypeChar = '$';
               variant.Buffer = tbuf;
               variant.Length = bwb_strlen( variant.Buffer );
               if( var_set( v, &variant ) == FALSE )
               {
                  WARN_VARIABLE_NOT_DECLARED;
                  return bwb_zline( l );
               }
            }
            return bwb_zline( l );
         }
         WARN_TYPE_MISMATCH;
         return bwb_zline( l );
      }
   }


   /* Process each variable read from the READ statement */

   main_loop = TRUE;
   while (main_loop == TRUE)
   {
      int             adv_loop;

      /* first check position in l->buffer and advance beyond
       * whitespace */

      adv_loop = TRUE;
      while (adv_loop == TRUE)
      {
         switch (l->buffer[l->position])
         {
         case ',':   /* variable seperator */
         case ' ':   /* whitespace */
            ++l->position;
            break;
         case BasicNulChar:
            adv_loop = FALSE; /* break out of advance
                      * loop */
            main_loop = FALSE;   /* break out of main
                      * loop */
            break;
         default: /* anything else */
            adv_loop = FALSE; /* break out of advance
                      * loop */
            break;
         }
      }


      /* be sure main_loop id still valid after checking the line */

      if (main_loop == TRUE)
      {
         /* Read a variable name */
         if( (v = line_read_scalar( l )) == NULL )
         {
            WARN_SYNTAX_ERROR;
            return bwb_zline(l);
         }
         /* Read a file value */

         if( file_read_value( f, delimit, v ) == FALSE )
         {
            if( My->CurrentVersion->OptionVersionBitmask & ( C77 ) ) 
            {
               /* IF END # file_number THEN line_number */
               if( My->CurrentFile->EOF_LineNumber > 0 )
               {
                  LineType *x;
                  
                  x = find_line_number( My->CurrentFile->EOF_LineNumber, TRUE ); /* not found in the cache */
                  if (x != NULL)
                  {
                     /* FOUND */
                     line_skip_eol(l);
                     x->position = 0;
                     return x;
                  }
                  /* NOT FOUND */
                  WARN_UNDEFINED_LINE;
                  return bwb_zline(l);            
               }
            }
            WARN_INPUT_PAST_END;
            return bwb_zline(l);
         }
         /* OK */

      }     /* end of remainder of main loop */
   }        /* end of main_loop */

   if( My->CurrentVersion->OptionVersionBitmask & ( C77 ) && My->CurrentFile->mode & DEVMODE_RANDOM )
   {
      /* 
      CBASIC-II: RANDOM file reads always acccess a complete record
      */
      long ByteOffset;
      
      /* advance to the end-of-record */
      if( My->CurrentFile->width <= 0 )
      {
         WARN_FIELD_OVERFLOW;
         return bwb_zline(l);
      }
      ByteOffset =  ftell( My->CurrentFile->cfp );
      ByteOffset %= My->CurrentFile->width;
      if( ByteOffset != 0 )
      {
         long RecordNumber;
         RecordNumber =  ftell( My->CurrentFile->cfp );
         RecordNumber /= My->CurrentFile->width;
         RecordNumber ++;
         RecordNumber *= My->CurrentFile->width;
         fseek( My->CurrentFile->cfp, RecordNumber, SEEK_SET );
      }
   }

   return bwb_zline(l);

}


/***************************************************************
  
        FUNCTION:       inp_str()
  
        DESCRIPTION:    This function does INPUT processing
                        from a determined string of input
                        data and a determined variable list
                        (both in memory).  This presupposes
         that input has been taken from My->SYSIN,
         not from a disk file or device.
  
***************************************************************/

static int inp_str( /* LineType * l, */ char *input_buffer, char *var_list, int *vl_position, int IsFake)
{
   int             i;
   int             loop;
   char            ttbuf[BasicStringLengthMax + 1];   /* build element */
   int             ReadAllVars;
   int             ReadAllData;

   bwx_DEBUG(__FUNCTION__);

   ReadAllVars = FALSE;
   ReadAllData = FALSE;




   /* Read elements in input_buffer and assign them to variables in var_list */

   i = 0;
   loop = TRUE;
   while (loop == TRUE)
   {
      VariableType *v;
      register int    n;

      n = 0;
      ttbuf[0] = BasicNulChar;

      buff_skip_spaces( input_buffer, &i );
      buff_skip_spaces( var_list, vl_position );

      /* get a variable name from the list */
      if( (v = buff_read_scalar( var_list, vl_position )) == NULL )
      {
         WARN_SYNTAX_ERROR;
         return -1;   /* FATAL */
      }
      /* build string from input buffer in ttbuf */
      if( VAR_IS_STRING( v ) )
      {
         /* STRING */
         if( input_buffer[ i ] == BasicQuoteChar )
         {
            /* QUOTED STRING */
            int q = 0; /* number of quotes */
            i++;
            q++;
            while( input_buffer[ i ] )
            {
               if( input_buffer[ i ] == BasicQuoteChar )
               {
                  i++; /* quote */
                  q++;
                  if( input_buffer[ i ] == BasicQuoteChar )
                  {
                     /* embedded string "...""..." */
                     q++;
                  }
                  else
                  {
                     /* properly terminated string "...xx..." */
                     break;
                  }
               }
               ttbuf[ n ] = input_buffer[ i ];
               n++;
               i++;
            }
            ttbuf[ n ] = BasicNulChar;
            /* process QUOTED response */
            if (My->CurrentVersion->OptionFlags & OPTION_BUGS_ON /* INPUT allows empty string */ )
            {
               /* silently ignore */
            }
            else
            {
               /* an ODD number of quotes is an ERROR */
               if( q & 1 )
               {
                  fputs( "*** Type Mismatch ***\n", My->SYSOUT->cfp) ;
                  return FALSE;  /* RETRY */
               }
            }
         }
         else
         {
            /* UNQUOTED STRING */
            while( input_buffer[ i ] )
            {
               if( input_buffer[ i ] == ',' )
               {
                  break;
               }
               ttbuf[ n ] = input_buffer[ i ];
               n++;
               i++;
            }
            ttbuf[ n ] = BasicNulChar;
            /* RTRIM */
            while( n > 0 && ttbuf[ n - 1 ] == ' ' )
            {
               ttbuf[ n - 1 ] = BasicNulChar;
               n--;
            }
            /* process EMPTY response */
            if (My->CurrentVersion->OptionFlags & OPTION_BUGS_ON /* INPUT allows empty string */ )
            {
               /* silently ignore */
            }
            else
            {
               /* an EMPTY response is an ERROR */
               if (ttbuf[0] == BasicNulChar)
               {
                  fputs( "*** Type Mismatch ***\n", My->SYSOUT->cfp );
                  return FALSE;  /* RETRY */
               }
            }
            /* process UNQUOTED response */
            if (My->CurrentVersion->OptionFlags & OPTION_BUGS_ON /* INPUT allows unquoted strings */ )
            {
               /* silently ignore */
            }
            else
            {
               /* if was NOT quoted, then the only valid chars are ' ', '+', '-', '.', digit, letter */
               char           *P;
               P = ttbuf;
               while (*P != BasicNulChar)
               {
                  char            C;
      
                  C = *P;
                  P++;
                  /* switch */
                  if (C == ' ' || C == '+' || C == '-' || C == '.' || bwb_isdigit(C) || bwb_isalpha(C))
                  {
                     /* OK */
                  }
                  else
                  {
                     /* ERROR */
                     fputs( "*** Type Mismatch ***\n", My->SYSOUT->cfp );
                     return FALSE;  /* RETRY */
                  }
               }
            }
         }
      }
      else
      {
         /* NUMBER */
         while( input_buffer[ i ] )
         {
            if( input_buffer[ i ] == ',' )
            {
               break;
            }
            ttbuf[ n ] = input_buffer[ i ];
            n++;
            i++;
         }
         ttbuf[ n ] = BasicNulChar;
         /* RTRIM */
         while( n > 0 && ttbuf[ n - 1 ] == ' ' )
         {
            ttbuf[ n - 1 ] = BasicNulChar;
            n--;
         }
         /* process EMPTY response */
         if (My->CurrentVersion->OptionFlags & OPTION_BUGS_ON /* INPUT allows empty string */ )
         {
            /* silently ignore */
         }
         else
         {
            /* an EMPTY response is an ERROR */
            if (ttbuf[0] == BasicNulChar)
            {
               fputs( "*** Type Mismatch ***\n", My->SYSOUT->cfp );
               return FALSE;  /* RETRY */
            }
         }
      }


      /* perform type-specific assignment */
      {
         int ResultCode;
         
         ResultCode = inp_assign(ttbuf, v, IsFake, TRUE); /* inp_str, bwb_INPUT */
         switch (ResultCode)
         {
         case RESULT_OK:
            break;
         case RESULT_UNKNOWN:
            WARN_INTERNAL_ERROR;
            return -1;  /* FATAL */
            /* break; */
         case RESULT_TYPE_MMISMATCH:
            fputs("*** Type Mismatch ***\n", My->SYSOUT->cfp );
            return FALSE;  /* RETRY */
            /* break; */
         case RESULT_ARITHMETIC_OVERFLOW:
            fputs("*** Overflow ***\n", My->SYSOUT->cfp );
            return FALSE;  /* RETRY */
            /* break; */
         }
      }
      /* OK */


      /* check for commas in variable list and advance */
      buff_skip_spaces(var_list, vl_position);
      switch (var_list[*vl_position])
      {
      case BasicNulChar:
         loop = FALSE;
         ReadAllVars = TRUE;
         break;
      case ',':
         ++(*vl_position);
         break;
      }
      buff_skip_spaces(var_list, vl_position);

      /* check for commas in input list and advance */
      buff_skip_spaces(input_buffer, &i);
      switch (input_buffer[i])
      {
      case BasicNulChar:
         loop = FALSE;
         ReadAllData = TRUE;
         break;
      case ',':
         ++i;
         break;
      }
      buff_skip_spaces(input_buffer, &i);

   }

   /* return */
   if (ReadAllVars == TRUE && ReadAllData == TRUE)
   {
      return 1; /* SUCCESS */
   }

   /* READ/DATA mismatch */
   fputs("*** Count Mismatch ***\n", My->SYSOUT->cfp );
   return FALSE;  /* RETRY */
}

/***************************************************************
  
        FUNCTION:       inp_assign()
  
        DESCRIPTION:    This function assigns the value of a
                        numerical or string constant to a
                        variable.
  
  
***************************************************************/

static int inp_assign(char *b, VariableType * v, int IsFake, int IsInput)
{
   VariantType variant;

   bwx_DEBUG(__FUNCTION__);

   variant.TypeChar = v->VariableTypeChar;

   if( VAR_IS_STRING( v ) )
   {
      /* STRING */
      if (IsFake == TRUE)
      {
      }
      else
      {
         variant.Buffer = b;
         variant.Length = bwb_strlen( variant.Buffer );
         if( var_set( v, &variant ) == FALSE )
         {
            WARN_VARIABLE_NOT_DECLARED;
            return RESULT_UNKNOWN;
         }
      }
      
      if( My->CurrentVersion->OptionVersionBitmask & ( E78 ) && IsInput == TRUE )
      {
         if (bwb_strchr(b, BasicQuoteChar) != NULL)
         {
            /* ECMA-55 forbids embedded quotes on INPUT */
            return RESULT_TYPE_MMISMATCH;
         }
      }
      return RESULT_OK;
   }
   /* NUMBER */
   if (b[0] == BasicNulChar)
   {
      /* empty input value */
      if (IsFake == TRUE)
      {
      }
      else
      {
         variant.Number = 0.0;
         if( var_set( v, &variant ) == FALSE )
         {
            WARN_VARIABLE_NOT_DECLARED;
            return RESULT_UNKNOWN;
         }
      }
      if (My->CurrentVersion->OptionFlags & OPTION_BUGS_ON /* INPUT empty numeric is zero */ )
      {
         return RESULT_OK;
      }
      return RESULT_TYPE_MMISMATCH;
   }
   else
   {
      int ResultCode;
      if (IsFake == TRUE)
      {
         inp_numconst(b, &ResultCode);
      }
      else
      {
         variant.Number = inp_numconst(b, &ResultCode);
         if( var_set( v, &variant ) == FALSE )
         {
            WARN_VARIABLE_NOT_DECLARED;
            return RESULT_UNKNOWN;
         }
      }
      return ResultCode;
   }

   return RESULT_UNKNOWN;

}

/***************************************************************
  
        FUNCTION:       inp_adv()
  
        DESCRIPTION:    This function advances the string pointer
                        past whitespace and the item delimiter
                        (comma).
  
***************************************************************/

int
inp_adv(char *b, int *c)
{
   int             rval;

   bwx_DEBUG(__FUNCTION__);

   rval = FALSE;

   while (TRUE)
   {
      switch (b[*c])
      {
      case ' ':   /* whitespace */
      case ';':   /* semicolon, end of prompt string */
         ++*c;
         break;
      case ',':   /* comma, variable delimiter */
         rval = TRUE;
         ++*c;
         break;
      case BasicNulChar:  /* end of line */
         rval = TRUE;
         My->last_inp_adv_rval = rval;  /* JBV */
         return rval;
      default:
         My->last_inp_adv_rval = rval;  /* JBV */
         return rval;
      }
   }
   /* return 0; */ /* never reached */
}

/***************************************************************
  
        FUNCTION:       inp_const()
  
        DESCRIPTION:    This function reads a numerical or string
                        constant from <m_buffer> into <s_buffer>,
                        incrementing <position> appropriately.
  
***************************************************************/

static int inp_const(char *m_buffer, char *s_buffer, int *position)
{
   int             string;
   int             s_pos;
   int             loop;

   bwx_DEBUG(__FUNCTION__);

   /* leading whitespace is NOT part of the DATA item */
   buff_skip_spaces(m_buffer,position);


   /* first detect string constant */
   string = FALSE;
   if ( buff_skip_char( m_buffer,position, BasicQuoteChar) )
   {
      string = TRUE;
   }

   /* build the constant string */

   s_pos = 0;
   s_buffer[s_pos] = BasicNulChar;


   loop = TRUE;

   while (loop == TRUE)
   {
      switch (m_buffer[*position])
      {
      case BasicNulChar:  /* end of string */
         return TRUE;
         /* internal whitespace is part of the DATA item */
      case ',':   /* or end of argument */
         if (string == FALSE)
         {
            /* trailing whitespace is NOT part of the DATA item */
            while (s_pos > 0 && s_buffer[s_pos - 1] == ' ')
            {
               s_pos--;
               s_buffer[s_pos] = BasicNulChar;
            }
            return TRUE;
         }
         else
         {
            s_buffer[s_pos] = m_buffer[*position];
            ++(*position);
            ++s_pos;
            s_buffer[s_pos] = BasicNulChar;
         }
         break;
      case BasicQuoteChar:
         /* quote character */
         if (string == TRUE)
         {
            /* same as the starting quote character; examples are "..." and '...' */
            ++(*position); /* advance beyond quotation mark */
            if( My->CurrentVersion->OptionFlags & OPTION_BUGS_ON /* INPUT quotes */ )
            {
               if( m_buffer[*position] == BasicQuoteChar )
               {
                  /* embedded string "...""..." */
                  s_buffer[s_pos] = m_buffer[*position];
                  ++(*position);
                  ++s_pos;
                  s_buffer[s_pos] = BasicNulChar;
                  break;
               }
            }
            /* properly terminated "...xx..." */
            inp_adv(m_buffer, position);
            return TRUE;
            
         }
         else
         {
            WARN_TYPE_MISMATCH;
            return FALSE;
         }
      default:
         s_buffer[s_pos] = m_buffer[*position];
         ++(*position);
         ++s_pos;
         s_buffer[s_pos] = BasicNulChar;
         break;
      }

   }

   return FALSE;

}


/***************************************************************
  
        FUNCTION:       bwb_LINE()
  
        DESCRIPTION:    This function implements the BASIC LINE
                        INPUT statement.
  
   SYNTAX:         LINE INPUT [[#] device-number,]["prompt string";] string-variable$
  
***************************************************************/

LineType *
bwb_LINE(LineType * l)
{
   int             FileNumber;
   VariableType *v;
   FILE           *inp_device;
   char            tbuf[BasicStringLengthMax + 1];
   char            pstring[BasicStringLengthMax + 1];

   bwx_DEBUG(__FUNCTION__);

   /* assign default values */

   My->CurrentFile = My->SYSIN;

   inp_device = My->SYSIN->cfp;

   pstring[0] = BasicNulChar;

   /* advance to first element (INPUT statement) */
   if( line_skip_word(l, "INPUT") == FALSE )
   {
      WARN_SYNTAX_ERROR;
      return bwb_zline(l);
   }
   line_skip_spaces(l);

   /* check for semicolon in first position */

   if (line_skip_comma(l))
   {
      line_skip_spaces(l);
   }
   else
   if ( line_skip_char(l, BasicFileNumberPrefix) )
   {

      if( line_read_integer_expression(l, &FileNumber) == FALSE )
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      }
      if( FileNumber < 0 )
      {
         /* "LINE INPUT # -1" is an error */
         WARN_BAD_FILE_NUMBER;
         return bwb_zline(l);
      }
      if( FileNumber > 0 )
      {
         /* normal file */
         My->CurrentFile = find_file_by_number( FileNumber );
         if( My->CurrentFile == NULL )
         {
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         if ((My->CurrentFile->mode & DEVMODE_READ) == 0)
         {
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         if (My->CurrentFile->cfp == NULL)
         {
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         inp_device = My->CurrentFile->cfp;
      }
   }
   /* check for comma */

   if ( line_skip_comma(l) )
   {
      line_skip_spaces(l);
   }
   /* check for quotation mark indicating prompt */

   if ( line_peek_char(l,BasicQuoteChar))
   {
      inp_const(l->buffer, pstring, &(l->position)); /* bwb_LINE prompt */
   }

   /* read the variable for assignment */
   if( (v = line_read_scalar( l )) == NULL )
   {
      WARN_SYNTAX_ERROR;
      return bwb_zline(l);
   }
   if ( VAR_IS_STRING( v ) )
   {
      /* OK */
   }
   else
   {
      /* ERROR */
      WARN_TYPE_MISMATCH;
      return bwb_zline(l);
   }
   /* read a line of text into the bufffer */

   if (inp_device == My->SYSIN->cfp)
   {
      bwx_input(pstring, tbuf);
   }
   else
   {
      fgets(tbuf, BasicStringLengthMax, inp_device);
   }
   bwb_stripcr(tbuf);
   /* if( TRUE ) */
   {
      VariantType variant;
      
      variant.TypeChar = '$';
      variant.Buffer = tbuf;
      variant.Length = bwb_strlen( variant.Buffer );
      if( var_set( v, &variant ) == FALSE )
      {
         WARN_VARIABLE_NOT_DECLARED;
         return bwb_zline( l );
      }
   }
   return bwb_zline(l);
}


/***************************************************************
  
        FUNCTION: inp_numconst()
  
        DESCRIPTION: This function interprets a numerical
         constant.  Added by JBV 10/95
  
***************************************************************/

BasicNumberType
inp_numconst(char *expression, int *ResultCode)
{
   int             base;   /* numerical base for the constant */
   BasicNumberType mantissa;   
   int             exponent;  /* exponent for floating point number */
   int             man_start; /* starting point of mantissa */
   int             s_pos;  /* position in build string */
   int             build_loop;
   int             need_pm;
   unsigned int    u;
   int             IsRounded = FALSE;

   /* Expression stack stuff */
   /* char type; */
   BasicNumberType nval;
   char            string[BasicStringLengthMax + 1];
   int             pos_adv;

   /* initialize the variable if necessary */

   bwx_DEBUG(__FUNCTION__);

   mantissa = 0;


   need_pm = FALSE;
   nval = 0;

   /* check the first character(s) to determine numerical base and
    * starting point of the mantissa */

   switch (expression[0])
   {
   case '-':
   case '+':
   case '0':
   case '1':
   case '2':
   case '3':
   case '4':
   case '5':
   case '6':
   case '7':
   case '8':
   case '9':
   case '.':
      base = 10;  /* decimal constant */
      man_start = 0; /* starts at position 0 */
      need_pm = FALSE;
      break;
   case '&':      /* hex or octal constant */
      if (bwb_toupper(expression[1] == 'H') )
      {
         base = 16;  /* hexadecimal constant */
         man_start = 2; /* starts at position 2 */
      }
      else
      {
         base = 8;   /* octal constant */
         if (bwb_toupper(expression[1] == 'O') )
         {
            man_start = 2; /* starts at position 2 */
         }
         else
         {
            man_start = 1; /* starts at position 1 */
         }
      }
      break;
   default:
      *ResultCode = RESULT_TYPE_MMISMATCH;
      return 0;
   }

   /* now build the mantissa according to the numerical base */

   switch (base)
   {

   case 10:    /* decimal constant */

      /* initialize counters */

      pos_adv = man_start;
      /* type = NUMBER; */
      string[0] = BasicNulChar;
      s_pos = 0;
      exponent = 0;
      build_loop = TRUE;

      /* loop to build the string */

      while (build_loop == TRUE)
      {
         switch (expression[pos_adv])
         {
         case '-':   /* prefixed plus or minus */
         case '+':

            /* in the first position, a plus or minus
             * sign can be added to the beginning of the
             * string to be scanned */

            if (pos_adv == man_start)
            {
               string[s_pos] = expression[pos_adv];
               ++pos_adv;  /* advance to next
                      * character */
               ++s_pos;
               string[s_pos] = BasicNulChar;
            }
            /* but in any other position, the plus or
             * minus sign must be taken as an operator
             * and thus as terminating the string to be
             * scanned */

            else
            {
               build_loop = FALSE;
            }
            break;
         case '.':   /* note at least single precision */
         case '0':   /* or ordinary digit */
         case '1':
         case '2':
         case '3':
         case '4':
         case '5':
         case '6':
         case '7':
         case '8':
         case '9':
            string[s_pos] = expression[pos_adv];
            ++pos_adv;  /* advance to next character */
            ++s_pos;
            string[s_pos] = BasicNulChar;
            break;

         case BasicCurrencySuffix:
         case BasicLongSuffix:   
         case BasicIntegerSuffix:
         case BasicByteSuffix:
            IsRounded = TRUE;
            /* fall thru */
         case BasicDoubleSuffix:
         case BasicSingleSuffix:
            if( My->CurrentVersion->OptionFlags & OPTION_BUGS_ON /* TypeChars in constants */ )
            {
               ++pos_adv;  /* advance to next character */
               /* type = NUMBER; */
               exponent = FALSE;
               build_loop = FALSE;
            }
            else
            {
               *ResultCode = RESULT_TYPE_MMISMATCH;
               return 0;
            }
            break;

         case 'D':   /* exponential, double precision */
         case 'd':
            if( My->CurrentVersion->OptionFlags & OPTION_BUGS_ON /* 'D' is exponential */ )
            {
               ++pos_adv;  /* advance to next character */
               /* type = NUMBER; */
               exponent = TRUE;
               build_loop = FALSE;
            }
            else
            {
               *ResultCode = RESULT_TYPE_MMISMATCH;
               return 0;
            }
            break;

         case 'E':   /* exponential, single precision */
         case 'e':
            ++pos_adv;  /* advance to next character */
            /* type = NUMBER; */
            exponent = TRUE;
            build_loop = FALSE;
            break;

         case BasicNulChar:
            build_loop = FALSE;
            break;

         default:
            /* not numeric */
            /* ERROR messages are displayed by the
             * calling routine */
            *ResultCode = RESULT_TYPE_MMISMATCH;
            return 0;
            /* break; */
         }

      }

      /* assign the value to the mantissa variable */
      sscanf(string, BasicNumberScanFormat, &mantissa);


      /* test if integer bounds have been exceeded */

      /* read the exponent if there is one */

      if (exponent == TRUE)
      {

         /* allow a plus or minus once at the beginning */

         need_pm = TRUE;

         /* initialize counters */

         string[0] = BasicNulChar;
         s_pos = 0;
         build_loop = TRUE;

         /* loop to build the string */

         while (build_loop == TRUE)
         {
            switch (expression[pos_adv])
            {
            case '-':   /* prefixed plus or minus */
            case '+':

               if (need_pm == TRUE) /* only allow once */
               {
                  string[s_pos] = expression[pos_adv];
                  ++pos_adv;  /* advance to next
                         * character */
                  ++s_pos;
                  string[s_pos] = BasicNulChar;
               }
               else
               {
                  build_loop = FALSE;
               }
               break;

            case '0':   /* or ordinary digit */
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':

               string[s_pos] = expression[pos_adv];
               ++pos_adv;  /* advance to next
                      * character */
               ++s_pos;
               string[s_pos] = BasicNulChar;
               need_pm = FALSE;
               break;

            case BasicCurrencySuffix:
            case BasicLongSuffix:   
            case BasicIntegerSuffix:
            case BasicByteSuffix:
               IsRounded = TRUE;
               /* fall-thru */
            case BasicDoubleSuffix:
            case BasicSingleSuffix:
               if( My->CurrentVersion->OptionFlags & OPTION_BUGS_ON /* TypeChars in constants */ )
               {
                  ++pos_adv;  /* advance to next character */
               }
               build_loop = FALSE;
               break;

            default: /* anything else, terminate */
               build_loop = FALSE;
               break;
            }

         }  /* end of build loop for exponent */

         /* assign the value to the user variable */

         sscanf(string, BasicNumberScanFormat, &nval);


      }     /* end of exponent search */
      if (nval == 0)
      {
         nval = mantissa;
      }
      else
      {
         nval = mantissa * pow(10.0, nval);
      }
      if( My->CurrentVersion->OptionFlags & OPTION_BUGS_ON /* TypeChars in constants */ )
      {
         /* 1.2% == 1 */
         if( IsRounded )
         {
            nval = bwb_rint( nval );  
         }
      }
      break;

   case 8:     /* octal constant */

      /* initialize counters */

      pos_adv = man_start;
      /* type = NUMBER; */
      string[0] = BasicNulChar;
      s_pos = 0;
      exponent = 0;
      build_loop = TRUE;

      /* loop to build the string */

      while (build_loop == TRUE)
      {
         switch (expression[pos_adv])
         {
         case '0':   /* or ordinary digit */
         case '1':
         case '2':
         case '3':
         case '4':
         case '5':
         case '6':
         case '7':
            string[s_pos] = expression[pos_adv];
            ++pos_adv;  /* advance to next character */
            ++s_pos;
            string[s_pos] = BasicNulChar;
            break;

         case BasicDoubleSuffix:
         case BasicSingleSuffix:
         case BasicCurrencySuffix:
         case BasicLongSuffix:   
         case BasicIntegerSuffix:
         case BasicByteSuffix:
            if( My->CurrentVersion->OptionFlags & OPTION_BUGS_ON /* TypeChars in constants */ )
            {
               ++pos_adv;  /* advance to next character */
            }
            build_loop = FALSE;
            break;

         default: /* anything else, terminate */
            build_loop = FALSE;
            break;
         }

      }

      /* now scan the string to determine the number */

      sscanf(string, "%o", &u);
      nval = u;

      break;

   case 16:    /* hexadecimal constant */

      /* initialize counters */

      pos_adv = man_start;
      /* type = NUMBER; */
      string[0] = BasicNulChar;
      s_pos = 0;
      exponent = 0;
      build_loop = TRUE;

      /* loop to build the string */

      while (build_loop == TRUE)
      {
         switch (expression[pos_adv])
         {
         case '0':   /* or ordinary digit */
         case '1':
         case '2':
         case '3':
         case '4':
         case '5':
         case '6':
         case '7':
         case '8':
         case '9':
         case 'A':
         case 'a':
         case 'B':
         case 'b':
         case 'C':
         case 'c':
         case 'D':
         case 'd':
         case 'E':
         case 'e':
         case 'F':   /* Don't forget these! (JBV) */
         case 'f':
            string[s_pos] = expression[pos_adv];
            ++pos_adv;  /* advance to next character */
            ++s_pos;
            string[s_pos] = BasicNulChar;
            break;

         case BasicDoubleSuffix:
         case BasicSingleSuffix:
         case BasicCurrencySuffix:
         case BasicLongSuffix:   
         case BasicIntegerSuffix:
         case BasicByteSuffix:
            if( My->CurrentVersion->OptionFlags & OPTION_BUGS_ON /* TypeChars in constants */ )
            {
               ++pos_adv;  /* advance to next character */
            }
            build_loop = FALSE;
            break;

         default: /* anything else, terminate */
            build_loop = FALSE;
            break;
         }

      }

      /* now scan the string to determine the number */

      sscanf(string, "%x", &u);
      nval = u;
      break;
   }

   /* check Value */
   if (isnan(nval))
   {
      nval = 0;
      /* ERROR messages are displayed by the calling routine */
      *ResultCode = RESULT_TYPE_MMISMATCH;
   }
   else
   if (isinf(nval))
   {
      if (nval < 0)
      {
         nval = -(DBL_MAX);   /* FLT_MAX */
      }
      else
      {
         nval = (DBL_MAX); /* FLT_MAX */
      }
      /* ERROR messages are displayed by the calling routine */
      *ResultCode = RESULT_ARITHMETIC_OVERFLOW;
   }
   else
   {
      *ResultCode = RESULT_OK;
   }

   return nval;

}

static int read_data(VariableType *v)
{
   int             ResultCode;
   char            tbuf[BasicStringLengthMax + 1];

     /* advance beyond whitespace or comma in data buffer */
     inp_adv( My->data_line->buffer, &My->data_pos );

     /* Advance to next line if end of buffer */
     if ( buff_is_eol( My->data_line->buffer, &My->data_pos ) )
     {
        /* end of buffer */
        My->data_line =  My->data_line->next;
        My->data_pos =  My->data_line->Startpos;
     }
     while ( My->data_line->cmdnum != C_DATA)
     {
        if ( My->data_line == &My->bwb_end )
        {
           /* halt */
           WARN_OUT_OF_DATA;
           return FALSE;
        }
        My->data_line =  My->data_line->next;
        My->data_pos =  My->data_line->Startpos;
     }

     /* leading whitespace is NOT part of the DATA item */
     buff_skip_spaces(  My->data_line->buffer, &My->data_pos );

     if ( buff_is_eol( My->data_line->buffer, &My->data_pos ) )
     {
        /* end of buffer */
        WARN_OUT_OF_DATA;
        return FALSE;
     }


     /* now at last we have a variable in v that needs to
      * be assigned data from the data_buffer at position
      * My->data_pos. What remains to be done is to
      * get one single bit of data, a string constant or
      * numerical constant, into the small buffer */

     if( buff_peek_char(  My->data_line->buffer, &My->data_pos, BasicQuoteChar) )
     {
        if ( VAR_IS_STRING( v ) )
        {
           /* OK */
        }
        else
        {
           /* ERROR */
           WARN_TYPE_MISMATCH;
           return FALSE;
        }
     }
     inp_const(My->data_line->buffer, tbuf, &My->data_pos); /* read_data , bwb_READ , bwb_MAT_READ */
     if (My->CurrentVersion->OptionFlags & OPTION_COVERAGE_ON)
     {
        /* this line has been READ */
        My->data_line->LineFlags |= LINE_EXECUTED;
     }

     /* finally assign the data to the variable */
     ResultCode = inp_assign(tbuf, v, FALSE, FALSE);  /* read_data , bwb_READ , bwb_MAT_READ */
     switch (ResultCode)
     {
     case RESULT_OK:
        break;
     case RESULT_UNKNOWN:
        WARN_INTERNAL_ERROR;
        return FALSE;
        /* break; */
     case RESULT_TYPE_MMISMATCH:
        WARN_TYPE_MISMATCH;
        return FALSE;
        /* break; */
     case RESULT_ARITHMETIC_OVERFLOW:
        bwb_Warning_Overflow("*** Arithmetic Overflow ***");
        break;
     }
     /* OK */
     return TRUE;
}

static LineType * file_read_matrix( LineType * l )
{
   /* MAT READ arrayname [;|,] */
   /* Array must be 1, 2 or 3 dimensions */
   /* Array may be either NUMBER or STRING */

   VariableType *v;

   bwx_DEBUG(__FUNCTION__);

   My->LastInputCount = 0;   

   line_skip_spaces( l );
   
   while( bwb_isalpha( l->buffer[l->position] ) )
   {
       My->LastInputCount = 0;   
       if( (v = line_read_matrix( l )) == NULL)
       {
          WARN_SUBSCRIPT_OUT_OF_RANGE;
          return bwb_zline(l);
       }
       /* variable MUST be an array of 1, 2 or 3 dimensions */
       if (v->dimensions < 1)
       {
          WARN_SUBSCRIPT_OUT_OF_RANGE;
          return bwb_zline(l);
       }
       if(v->dimensions > 3)
       {
          WARN_SUBSCRIPT_OUT_OF_RANGE;
          return bwb_zline(l);
       }

       /* both arrays are of the same size */
       /* allow user to use either item seperator */
       if( line_skip_comma(l) )
       {
           /* force printing col-by-col */
       }
       else
       {
           /* force concatenating the columns */
       }

       /* READ array */
       switch( v->dimensions )
       {
       case 1:
           {
               /*
               OPTION BASE 0
               DIM A(5)
               ...
               MAT READ A 
               ...
               FOR I = 0 TO 5
                 READ A(I)
               NEXT I
               ...
               */
               for( v->array_pos[0] = v->LBOUND[0]; v->array_pos[0] <= v->UBOUND[0]; v->array_pos[0]++ )
               {
                  if ( My->CurrentFile == My->SYSIN)
                  {
                      if( read_data(v) == FALSE )
                      {
                         WARN_SUBSCRIPT_OUT_OF_RANGE;
                         return bwb_zline(l);
                      }
                  }
                  else
                  {
                     if( file_read_value( My->CurrentFile->cfp, My->CurrentFile->delimit, v  ) == FALSE )
                     {
                        WARN_INPUT_PAST_END;
                        return bwb_zline(l);
                     }
                  }
                  /* OK */
                  My->LastInputCount++;   
               }
           }
           break;
       case 2:
           {
               /*
               OPTION BASE 0
               DIM B(2,3)
               ...
               MAT READ B 
               ...
               FOR I = 0 TO 2
                   FOR J = 0 TO 3
                       READ B(I,J)
                   NEXT J
                   PRINT
               NEXT I
               ...
               */
               for( v->array_pos[0] = v->LBOUND[0]; v->array_pos[0] <= v->UBOUND[0]; v->array_pos[0]++ )
               {
               for( v->array_pos[1] = v->LBOUND[1]; v->array_pos[1] <= v->UBOUND[1]; v->array_pos[1]++ )
               {
                  if ( My->CurrentFile == My->SYSIN)
                  {
                      if( read_data(v) == FALSE )
                      {
                         WARN_SUBSCRIPT_OUT_OF_RANGE;
                         return bwb_zline(l);
                      }
                  }
                  else
                  {
                     if( file_read_value( My->CurrentFile->cfp, My->CurrentFile->delimit, v  ) == FALSE )
                     {
                        WARN_INPUT_PAST_END;
                        return bwb_zline(l);
                     }
                  }
                  /* OK */
                  My->LastInputCount++;   
               }
               }
           }
           break;
       case 3:
           {
               /*
               OPTION BASE 0
               DIM C(2,3,4)
               ...
               MAT READ C 
               ...
               FOR I = 0 TO 2
                   FOR J = 0 TO 3
                       FOR K = 0 TO 4
                           READ C(I,J,K)
                       NEXT K
                       PRINT
                   NEXT J
                   PRINT
               NEXT I
               ...
               */
               for( v->array_pos[0] = v->LBOUND[0]; v->array_pos[0] <= v->UBOUND[0]; v->array_pos[0]++ )
               {
               for( v->array_pos[1] = v->LBOUND[1]; v->array_pos[1] <= v->UBOUND[1]; v->array_pos[1]++ )
               {
               for( v->array_pos[2] = v->LBOUND[2]; v->array_pos[2] <= v->UBOUND[2]; v->array_pos[2]++ )
               {
                  if ( My->CurrentFile == My->SYSIN)
                  {
                      if( read_data(v) == FALSE )
                      {
                         WARN_SUBSCRIPT_OUT_OF_RANGE;
                         return bwb_zline(l);
                      }
                  }
                  else
                  {
                     if( file_read_value( My->CurrentFile->cfp, My->CurrentFile->delimit, v  ) == FALSE )
                     {
                        WARN_INPUT_PAST_END;
                        return bwb_zline(l);
                     }
                  }
                  /* OK */
                  My->LastInputCount++;   
               }
               }
               }
           }
           break;
       }
       /* skip spaces */
       line_skip_spaces(l);
       /* process the next variable, if any  */
   }
   return bwb_zline(l);
}

LineType *
bwb_MAT_GET(LineType * l)
{
   /* MAT GET filename$ , matrix [, ...] */
   VariantType E;
   VariantType *e = &E;  

   bwx_DEBUG(__FUNCTION__);

   My->CurrentFile = My->SYSIN;

   line_skip_spaces(l);
   if( line_read_expression( l, e ) == FALSE )
   {
      WARN_SYNTAX_ERROR;
      return bwb_zline(l);
   }
   if( e->TypeChar == BasicStringSuffix )
   {
      /* STRING */
      /* MAT GET filename$ ... */
      if( is_empty_filename( e->Buffer ) )
      {
         /* "MAT GET # 0" is an error */
         WARN_BAD_FILE_NUMBER;
         return bwb_zline(l);
      }
      My->CurrentFile = find_file_by_name( e->Buffer );
      if( My->CurrentFile == NULL )
      {
         /* implicitly OPEN for reading */
         My->CurrentFile = file_new();
         My->CurrentFile->cfp = fopen(e->Buffer, "r");
         if( My->CurrentFile->cfp == NULL )
         {
            /* bad file name */
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         My->CurrentFile->FileNumber = file_next_number();
         My->CurrentFile->mode = DEVMODE_INPUT;
         My->CurrentFile->width = 0;
         /* WIDTH == RECLEN */
         My->CurrentFile->col = 1;
         My->CurrentFile->row = 1;
         My->CurrentFile->delimit = ',';
         My->CurrentFile->buffer = NULL;
         bwb_strcpy(My->CurrentFile->filename, e->Buffer);
      }
   }
   else
   {
      /* NUMBER -- file must already be OPEN */
      /* GET filenumber ... */
      if( e->Number < 0 )
      {
         /* "MAT GET # -1" is an error */
         WARN_BAD_FILE_NUMBER;
         return bwb_zline(l);
      }
      if( e->Number == 0 )
      {
         /* "MAT GET # 0" is an error */
         WARN_BAD_FILE_NUMBER;
         return bwb_zline(l);
      }
      /* normal file */
      My->CurrentFile = find_file_by_number( (int) bwb_rint( e->Number ) );
      if( My->CurrentFile == NULL )
      {
         /* file not OPEN */
         WARN_BAD_FILE_NUMBER;
         return bwb_zline(l);
      }
   }  
   RELEASE( e );  
   if( My->CurrentFile == NULL )
   {
      WARN_BAD_FILE_NUMBER;
      return bwb_zline(l);
   }
   if (( My->CurrentFile->mode & DEVMODE_READ) == 0)
   {
      WARN_BAD_FILE_NUMBER;
      return bwb_zline(l);
   }
   if ( line_skip_comma(l) )
   {
      /* OK */
   }
   else
   {
      WARN_SYNTAX_ERROR;
      return bwb_zline(l);
   }
   return file_read_matrix( l );
}


LineType *
bwb_MAT_READ(LineType * l)
{
   /* MAT READ arrayname [;|,] */
   /* Array must be 1, 2 or 3 dimensions */
   /* Array may be either NUMBER or STRING */


   bwx_DEBUG(__FUNCTION__);

   My->CurrentFile = My->SYSIN;

   My->LastInputCount = 0;   

   if ( line_skip_char(l,BasicFileNumberPrefix) )
   {
      /* MAT READ # filenum, varlist */
      int FileNumber;

      if( line_read_integer_expression(l, &FileNumber) == FALSE )
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      }
      
      if ( line_skip_comma(l) )
      {
         /* OK */
      }
      else
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      }
      My->CurrentFile = find_file_by_number( FileNumber );
      if( My->CurrentFile == NULL )
      {
         WARN_BAD_FILE_NUMBER;
         return bwb_zline(l);
      }
      if ( My->CurrentFile != My->SYSIN)
      {
         if ((My->CurrentFile->mode & DEVMODE_READ) == 0)
         {
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         if (My->CurrentFile->cfp == NULL)
         {
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
      }
      /* "MAT READ # 0, varlist" is the same as "MAT READ varlist" */
      line_skip_spaces(l);
   }
   return file_read_matrix( l );
}


static int input_data(VariableType *v, char *Buffer)
{
    char *C;
    char c;
    int ResultCode;
    
    if( Buffer[0] == BasicNulChar )
    {
        /* Get more data */
        bwx_input("?", Buffer );
        if( Buffer[0] == BasicNulChar )
        {
            return FALSE;
        }
    }
    /* process data */

    /* data seperator is an unquoted comma (,) */

    C = Buffer;
    while( *C != BasicNulChar && *C != ',' )
    {
        if( *C == BasicQuoteChar )
        {
            /* skip leading quote */
            C++;
            while( *C != BasicNulChar && *C != BasicQuoteChar )
            {
                /* skip string constant */
                C++;
            }
            if( *C == BasicQuoteChar )
            {
                /* skip trailing quote */
                C++;
            }
        }
        else
        {
            C++;
        }
    }
    c = *C; /* either a comma (,) or a NUL (0) */
    *C = BasicNulChar;
    CleanLine( Buffer );
    if( Buffer[0] == BasicQuoteChar )
    {
        /* remove quotes */
        char *E;
        
        E = Buffer;
        E++;
        E = bwb_strchr(E, BasicQuoteChar);
        if( E != NULL )
        {
            *E = BasicNulChar;
        }
        E = Buffer;
        E++;
        bwb_strcpy(Buffer,E);
    }
   ResultCode = inp_assign(Buffer, v, FALSE, FALSE);  /* input_data , bwb_MAT_INPUT */
   switch (ResultCode)
   {
   case RESULT_OK:
      break;
   case RESULT_UNKNOWN:
      WARN_INTERNAL_ERROR;
      return FALSE;
      /* break; */
   case RESULT_TYPE_MMISMATCH:
      fputs("*** Type Mismatch ***\n", My->SYSOUT->cfp );
      return FALSE;
      /* break; */
   case RESULT_ARITHMETIC_OVERFLOW:
      fputs("*** Overflow ***\n", My->SYSOUT->cfp );
      return FALSE;
      /* break; */
   }
   /* OK */
   if( c == BasicNulChar )
   {
       /* we have consumed the entire buffer */
       Buffer[0] = BasicNulChar;
   }
   else
   if( c == ',' )
   {
       /* shift the buffer left, just past the comma (,) */
       C++;
       bwb_strcpy(Buffer,C);
   }
   else
   {
      WARN_INTERNAL_ERROR;
      return FALSE;
   }
   
   return TRUE;
}

LineType *
bwb_MAT_INPUT(LineType * l)
{
   /* MAT INPUT arrayname [;|,] */
   /* Array must be 1, 2 or 3 dimensions */
   /* Array may be either NUMBER or STRING */
   
   VariableType *v;
   char            tbuf[BasicStringLengthMax + 1];

   bwx_DEBUG(__FUNCTION__);
   
   My->CurrentFile = My->SYSIN;

   My->LastInputCount = 0;   

   if ( line_skip_char(l,BasicFileNumberPrefix) )
   {
      /* MAT INPUT # filenum, varlist */
      int FileNumber;

      if( line_read_integer_expression(l, &FileNumber) == FALSE )
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      }
      
      if ( line_skip_comma(l) )
      {
         /* OK */
      }
      else
      {
         WARN_SYNTAX_ERROR;
         return bwb_zline(l);
      }
      My->CurrentFile = find_file_by_number( FileNumber );
      if( My->CurrentFile == NULL )
      {
         WARN_BAD_FILE_NUMBER;
         return bwb_zline(l);
      }
      if ( My->CurrentFile != My->SYSIN)
      {
         if ((My->CurrentFile->mode & DEVMODE_READ) == 0)
         {
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
         if (My->CurrentFile->cfp == NULL)
         {
            WARN_BAD_FILE_NUMBER;
            return bwb_zline(l);
         }
      }
      /* "MAT INPUT # 0, varlist" is the same as "MAT INPUT varlist" */
      line_skip_spaces(l);
   }


   while( bwb_isalpha( l->buffer[l->position] ) )
   {
       My->LastInputCount = 0;   
       if( (v = line_read_matrix( l )) == NULL)
       {
          WARN_SUBSCRIPT_OUT_OF_RANGE;
          return bwb_zline(l);
       }
       /* variable MUST be an array of 1, 2 or 3 dimensions */
       if (v->dimensions < 1)
       {
          WARN_SUBSCRIPT_OUT_OF_RANGE;
          return bwb_zline(l);
       }
       if(v->dimensions > 3)
       {
          WARN_SUBSCRIPT_OUT_OF_RANGE;
          return bwb_zline(l);
       }

       /* allow user to use either item seperator */
       if( line_skip_comma(l) )
       {
           /* force printing col-by-col */
       }
       else
       {
           /* force concatenating the columns */
       }

       /* INPUT array */
       tbuf[0] = BasicNulChar;
       switch( v->dimensions )
       {
       case 1:
           {
               /*
               OPTION BASE 0
               DIM A(5)
               ...
               MAT INPUT A 
               ...
               FOR I = 0 TO 5
                 INPUT A(I)
               NEXT I
               ...
               */
               My->LastInputCount = 0;   
               for( v->array_pos[0] = v->LBOUND[0]; v->array_pos[0] <= v->UBOUND[0]; v->array_pos[0]++ )
               {
                  if ( My->CurrentFile == My->SYSIN)
                  {
                      if( input_data(v,tbuf) == FALSE )
                      {
                         /*
                         WARN_INPUT_PAST_END;
                         */
                         return bwb_zline(l);
                      }
                  }
                  else
                  {
                     if( file_read_value( My->CurrentFile->cfp, ',', v  ) == FALSE )
                     {
                        WARN_INPUT_PAST_END;
                        return bwb_zline(l);
                     }
                  }
                  /* OK */
                  My->LastInputCount++;   
               }
           }
           break;
       case 2:
           {
               /*
               OPTION BASE 0
               DIM B(2,3)
               ...
               MAT INPUT B 
               ...
               FOR I = 0 TO 2
                   FOR J = 0 TO 3
                       INPUT B(I,J)
                   NEXT J
                   PRINT
               NEXT I
               ...
               */
               My->LastInputCount = 0;   
               for( v->array_pos[0] = v->LBOUND[0]; v->array_pos[0] <= v->UBOUND[0]; v->array_pos[0]++ )
               {
               for( v->array_pos[1] = v->LBOUND[1]; v->array_pos[1] <= v->UBOUND[1]; v->array_pos[1]++ )
               {
                  if ( My->CurrentFile == My->SYSIN)
                  {
                      if( input_data(v,tbuf) == FALSE )
                      {
                         /*
                         WARN_INPUT_PAST_END;
                         */
                         return bwb_zline(l);
                      }
                  }
                  else
                  {
                     if( file_read_value( My->CurrentFile->cfp, ',', v  ) == FALSE )
                     {
                        WARN_INPUT_PAST_END;
                        return bwb_zline(l);
                     }
                  }
                  /* OK */
                  My->LastInputCount++;   
               }
               }
           }
           break;
       case 3:
           {
               /*
               OPTION BASE 0
               DIM C(2,3,4)
               ...
               MAT INPUT C 
               ...
               FOR I = 0 TO 2
                   FOR J = 0 TO 3
                       FOR K = 0 TO 4
                           INPUT C(I,J,K)
                       NEXT K
                       PRINT
                   NEXT J
                   PRINT
               NEXT I
               ...
               */
               My->LastInputCount = 0;   
               for( v->array_pos[0] = v->LBOUND[0]; v->array_pos[0] <= v->UBOUND[0]; v->array_pos[0]++ )
               {
               for( v->array_pos[1] = v->LBOUND[1]; v->array_pos[1] <= v->UBOUND[1]; v->array_pos[1]++ )
               {
               for( v->array_pos[2] = v->LBOUND[2]; v->array_pos[2] <= v->UBOUND[2]; v->array_pos[2]++ )
               {
                  if ( My->CurrentFile == My->SYSIN)
                  {
                      if( input_data(v,tbuf) == FALSE )
                      {
                         /*
                         WARN_INPUT_PAST_END;
                         */
                         return bwb_zline(l);
                      }
                  }
                  else
                  {
                     if( file_read_value( My->CurrentFile->cfp, ',', v  ) == FALSE )
                     {
                        WARN_INPUT_PAST_END;
                        return bwb_zline(l);
                     }
                  }
                  /* OK */
                  My->LastInputCount++;   
               }
               }
               }
           }
           break;
       }
       /* skip spaces */
       line_skip_spaces(l);
       /* process the next variable, if any  */
   }
   return bwb_zline(l);
}

/* EOF */
