/***************************************************************

        bwb_cnd.c       Conditional Expressions and Commands
                        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).                            */
/*---------------------------------------------------------------*/

#include <stdio.h>
#include <math.h>
#include <ctype.h>

#include "bwbasic.h"
#include "bwb_mes.h"

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

#if ANSI_C
static int cnd_thenels( char *buffer, int position, int *then, int *els );
static int cnd_tostep( char *buffer, int position, int *to, int *step );
static struct bwb_line *find_wend( struct bwb_line *l );
static struct bwb_line *find_endif( struct bwb_line *l,
   struct bwb_line **else_line );
static int is_endif( struct bwb_line *l );
extern int var_setnval( struct bwb_variable *v, bnumber i );
static int case_eval( struct exp_ese *expression, struct exp_ese *minval,
   struct exp_ese *maxval );
static struct bwb_line *find_case( struct bwb_line *l );
static struct bwb_line *find_endselect( struct bwb_line *l );
static int is_endselect( struct bwb_line *l );
static struct bwb_line *bwb_caseif( struct bwb_line *l );

#if STRUCT_CMDS
static struct bwb_line *find_next( struct bwb_line *l );
#endif

#else
static int cnd_thenels();
static int cnd_tostep();
static struct bwb_line *find_wend();
static struct bwb_line *find_endif();
static int is_endif();
extern int var_setnval();
static int case_eval();
static struct bwb_line *find_case();
static struct bwb_line *find_endselect();
static int is_endselect();
static struct bwb_line *bwb_caseif();

#if STRUCT_CMDS
static struct bwb_line *find_next();
#endif

#endif                          /* ANSI_C for prototypes */

/***    IF-THEN-ELSE ***/

/***************************************************************

        FUNCTION:       bwb_if()

        DESCRIPTION:    This function handles the BASIC IF
                        statement.

   	SYNTAX:		IF expression THEN [statement [ELSE statement]]

***************************************************************/

#if ANSI_C
struct bwb_line *
bwb_if( struct bwb_line *l )
#else
struct bwb_line *
bwb_if( l )
   struct bwb_line *l;
#endif
   {
   int then, els;
   struct exp_ese *e;
   int glnumber;
   int tpos;
   static char tbuf[ MAXSTRINGSIZE + 1 ];
   static struct bwb_line gline;
#if STRUCT_CMDS
   static struct bwb_line *else_line;
   static struct bwb_line *endif_line;
#endif

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_if(): entry, line <%d> buffer <%s>",
      l->number, &( l->buffer[ l->position ] ) );
   bwb_debug( bwb_ebuf );
   getchar();
#endif

#if INTENSIVE_DEBUG
   if ( l == &gline )
      {
      sprintf( bwb_ebuf, "in bwb_if(): recursive call, l = &gline" );
      bwb_debug( bwb_ebuf );
      }
#endif

   /* Call bwb_exp() to evaluate the condition. This should return
      with position set to the "THEN" statement */

   e = bwb_exp( l->buffer, FALSE, &( l->position ) );

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_if(): line <%d> condition returns <%d>",
      l->number, exp_getnval( e ) );
   bwb_debug( bwb_ebuf );
#endif

   /* test for "THEN" and "ELSE" statements */

   cnd_thenels( l->buffer, l->position, &then, &els );

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_if(): return from cnd_thenelse, line is <%s>",
      l->buffer );
   bwb_debug( bwb_ebuf );
#endif

   /* test for multiline IF statement: this presupposes ANSI-compliant
      structured BASIC */

#if STRUCT_CMDS
   tpos = then + strlen( CMD_THEN ) + 1;
   if ( is_eol( l->buffer, &tpos ) == TRUE )
      {

#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in bwb_if(): found multi-line IF statement, line <%d>",
         l->number );
      bwb_debug( bwb_ebuf );
#endif

      /* find END IF and possibly ELSE[IF] line(s) */

      else_line = NULL;
      endif_line = find_endif( l, &else_line );

      /* evaluate the expression */

      if ( (int) exp_getnval( e ) != FALSE )
         {
         bwb_incexec();
         bwb_setexec( l->next, 0, EXEC_IFTRUE );

#if MULTISEG_LINES
         adv_eos( l->buffer, &( l->position ));
#endif
         return bwb_zline( l );
         }

      else if ( else_line != NULL )
         {
         bwb_incexec();
         bwb_setexec( else_line, 0, EXEC_IFFALSE );
         else_line->position = 0;
         return else_line;
         }
      else
         {
         /* Following line incorrect, replaced by next two (bug found by DD) */
         /* bwb_setexec( endif_line, 0, CURTASK excs[ CURTASK exsc ].code ); */

         bwb_incexec(); /* JBV */
         bwb_setexec( endif_line, 0, EXEC_IFFALSE ); /* JBV */
         endif_line->position = 0;
         return endif_line;
         }
      }

#endif			/* STRUCT_CMDS for Multi-line IF...THEN */

   /* Not a Multi-line IF...THEN: test for THEN line-number */

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_if(): not multi-line; line is <%s>",
      l->buffer );
   bwb_debug( bwb_ebuf );
#endif

   /* evaluate and execute */

   if ( (int) exp_getnval( e ) != FALSE )
      {

#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in bwb_if(): expression is TRUE" );
      bwb_debug( bwb_ebuf );
#endif

      if ( then == FALSE )
         {
#if PROG_ERRORS
	 sprintf( bwb_ebuf, "in bwb_if(): IF without THEN" );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_syntax );
#endif
         }
      else
         {

	 /* check for THEN followed by literal line number */

	 tpos = then + strlen( CMD_THEN ) + 1;
	 adv_element( l->buffer, &tpos, tbuf );

	 if ( isdigit( tbuf[ 0 ] ) != 0 )
	    {

	    glnumber = atoi( tbuf );

#if INTENSIVE_DEBUG
	    sprintf( bwb_ebuf, "Detected THEN followed by line number <%d>",
	       glnumber );
	    bwb_debug( bwb_ebuf );
#endif

	    sprintf( tbuf, "%s %d", CMD_GOTO, glnumber );
	    gline.buffer = tbuf;
	    gline.marked = FALSE;
	    gline.position = 0;
	    gline.next = l->next;
	    bwb_setexec( &gline, 0, CURTASK excs[ CURTASK exsc ].code );
	    return &gline;
	    }

	 /* form is not THEN followed by line number */

	 else
	    {
	    bwb_setexec( l, then, CURTASK excs[ CURTASK exsc ].code );
	    l->position = then + strlen( CMD_THEN ) + 1;
	    }

	 return l;
	 }
      }
   else
      {

#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in bwb_if(): expression is FALSE" );
      bwb_debug( bwb_ebuf );
#endif

      if ( els != FALSE )
         {
         l->position = els + strlen( CMD_ELSE ) + 1;
         /* bwb_setexec( l, els, EXEC_NORM ); */ /* Nope (JBV) */
         bwb_setexec( l, els, CURTASK excs[ CURTASK exsc ].code ); /* JBV */
	 return l;
	 }
      }

   /* if neither then nor else were found, advance to next line */
   /* DO NOT advance to next segment (only if TRUE should we do that) */

   l->next->position = 0;
   return l->next;

   }

/***************************************************************

        FUNCTION:       cnd_thenelse()

        DESCRIPTION:    This function searches through the
                        <buffer> beginning at point <position>
                        and attempts to find positions of THEN
                        and ELSE statements.

***************************************************************/

#if ANSI_C
static int
cnd_thenels( char *buffer, int position, int *then, int *els )
#else
static int
cnd_thenels( buffer, position, then, els )
   char *buffer;
   int position;
   int *then;
   int *els;
#endif
   {
   int loop, t_pos, b_pos, p_word;
   char tbuf[ MAXSTRINGSIZE + 1 ];

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in cnd_thenels(): entry, line is <%s>",
      &( buffer[ position ] ) );
   bwb_debug( bwb_ebuf );
#endif

   /* set then and els to 0 initially */

   *then = *els = 0;

   /* loop to find words */

   p_word = b_pos = position;
   t_pos = 0;
   tbuf[ 0 ] = '\0';
   loop = TRUE;
   while( loop == TRUE )
      {

      switch( buffer[ b_pos ] )
         {
         case '\0':                     /* end of string */
         case ' ':                      /* whitespace = end of word */
         case '\t':

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in cnd_thenels(): word is <%s>", tbuf );
            bwb_debug( bwb_ebuf );
#endif

            if ( strncmp( tbuf, CMD_THEN, (size_t) strlen( CMD_THEN ) ) == 0 )
               {

#if INTENSIVE_DEBUG
               sprintf( bwb_ebuf, "in cnd_thenels(): THEN found at position <%d>.",
                  p_word );
               bwb_debug( bwb_ebuf );
               sprintf( bwb_ebuf, "in cnd_thenelse(): after THEN, line is <%s>", buffer );
               bwb_debug( bwb_ebuf );
#endif

               *then = p_word;
               }
            else if ( strncmp( tbuf, CMD_ELSE, (size_t) strlen( CMD_ELSE ) ) == 0 )
               {

#if INTENSIVE_DEBUG
               sprintf( bwb_ebuf, "in cnd_thenels(): ELSE found at position <%d>.",
                  p_word );
               bwb_debug( bwb_ebuf );
               sprintf( bwb_ebuf, "in cnd_thenelse(): after ELSE, line is <%s>", buffer );
               bwb_debug( bwb_ebuf );
#endif

               *els = p_word;
               }

            /* check for end of the line */

            if ( buffer[ b_pos ] == '\0' )
               {
#if INTENSIVE_DEBUG
               sprintf( bwb_ebuf, "in cnd_thenels(): return: end of string" );
               bwb_debug( bwb_ebuf );
#endif
               return TRUE;
               }

            ++b_pos;
            p_word = b_pos;
            t_pos = 0;
            tbuf[ 0 ] = '\0';
            break;

         default:
            if ( islower( buffer[ b_pos ] ) != FALSE )
               {
               tbuf[ t_pos ] = (char) toupper( buffer[ b_pos ] );
               }
            else
               {
               tbuf[ t_pos ] = buffer[ b_pos ];
               }
            ++b_pos;
            ++t_pos;
            tbuf[ t_pos ] = '\0';
            break;
         }

      }

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in cnd_thenelse(): exit, line is <%s>", buffer );
   bwb_debug( bwb_ebuf );
#endif

   return FALSE;

   }

#if STRUCT_CMDS

/***************************************************************

        FUNCTION:       bwb_else()

        DESCRIPTION:    This function handles the BASIC ELSE
                        statement.

   	SYNTAX:		ELSE

***************************************************************/

#if ANSI_C
struct bwb_line *
bwb_else( struct bwb_line *l )
#else
struct bwb_line *
bwb_else( l )
   struct bwb_line *l;
#endif
   {
   struct bwb_line *endif_line;
   struct bwb_line *else_line;

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_else(): entered function" );
   bwb_debug( bwb_ebuf );
#endif

   /* If the code is EXEC_NORM, then this is a continuation of a single-
      line IF...THEN...ELSE... statement and we should return */

   /*----------------------------------------------------------------------*/
   /* Well, not really... better to check for EXEC_IFTRUE or EXEC_IFFALSE, */
   /* and if not equal, then blow entirely out of current line (JBV)       */
   /*----------------------------------------------------------------------*/

   /* Section removed by JBV */
   /* if ( CURTASK excs[ CURTASK exsc ].code == EXEC_NORM )
      {

#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in bwb_else(): detected EXEC_NORM" );
      bwb_debug( bwb_ebuf );
#endif

      return bwb_zline( l );
      } */

   /* Section added by JBV */
   if (( CURTASK excs[ CURTASK exsc ].code != EXEC_IFTRUE ) &&
   ( CURTASK excs[ CURTASK exsc ].code != EXEC_IFFALSE ))
      {

#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in bwb_else(): no EXEC_IFTRUE or EXEC_IFFALSE" );
      bwb_debug( bwb_ebuf );
#endif

      l->next->position = 0;
      return l->next;
      }

   endif_line = find_endif( l, &else_line );

   if ( CURTASK excs[ CURTASK exsc ].code == EXEC_IFTRUE )
      {
      endif_line->position = 0;
      return endif_line;
      }
   else if ( CURTASK excs[ CURTASK exsc ].code == EXEC_IFFALSE )
      {

      return bwb_zline( l );
      }

#if PROG_ERRORS
   sprintf( bwb_ebuf, "in bwb_else(): ELSE without IF" );
   bwb_error( bwb_ebuf );
#else
   bwb_error( err_syntax );
#endif


   return bwb_zline( l );
   }

/***************************************************************

        FUNCTION:       bwb_elseif()

        DESCRIPTION:    This function handles the BASIC ELSEIF
                        statement.

	SYNTAX:		ELSEIF

***************************************************************/

#if ANSI_C
struct bwb_line *
bwb_elseif( struct bwb_line *l )
#else
struct bwb_line *
bwb_elseif( l )
   struct bwb_line *l;
#endif
   {
   struct bwb_line *endif_line;
   struct bwb_line *else_line;
   struct exp_ese *e;

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_elseif(): entered function" );
   bwb_debug( bwb_ebuf );
#endif

   else_line = NULL;
   endif_line = find_endif( l, &else_line );

   if ( CURTASK excs[ CURTASK exsc ].code == EXEC_IFTRUE )
      {
      endif_line->position = 0;
      return endif_line;
      }

   else if ( CURTASK excs[ CURTASK exsc ].code == EXEC_IFFALSE )
      {

      /* Call bwb_exp() to evaluate the condition. This should return
         with position set to the "THEN" statement */

      e = bwb_exp( l->buffer, FALSE, &( l->position ) );

      if ( (int) exp_getnval( e ) == TRUE )
         {

         /* ELSEIF condition is TRUE: proceed to the next line */

         CURTASK excs[ CURTASK exsc ].code = EXEC_IFTRUE;

#if MULTISEG_LINES
         adv_eos( l->buffer, &( l->position ));
#endif
         return bwb_zline( l );

         }

      /* ELSEIF condition FALSE: proceed to next ELSE line if there is one */

      else if ( else_line != NULL )
         {
         bwb_setexec( else_line, 0, EXEC_IFFALSE );
         else_line->position = 0;
         return else_line;
         }

      /* ELSEIF condition is FALSE and no more ELSExx lines: proceed to END IF */

      else
         {
         bwb_setexec( endif_line, 0, CURTASK excs[ CURTASK exsc ].code );
         endif_line->position = 0;
         return endif_line;
         }

      }

#if PROG_ERRORS
   sprintf( bwb_ebuf, "in bwb_elseif(): ELSEIF without IF" );
   bwb_error( bwb_ebuf );
#else
   bwb_error( err_syntax );
#endif


#if MULTISEG_LINES
   adv_eos( l->buffer, &( l->position ));
#endif
   return bwb_zline( l );

   }

/***************************************************************

        FUNCTION:       bwb_endif()

        DESCRIPTION:    This function handles the BASIC END IF
                        statement.

	SYNTAX:		END IF

***************************************************************/

#if ANSI_C
struct bwb_line *
bwb_endif( struct bwb_line *l )
#else
struct bwb_line *
bwb_endif( l )
   struct bwb_line *l;
#endif
   {

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_endif(): entered function" );
   bwb_debug( bwb_ebuf );
#endif

   if (( CURTASK excs[ CURTASK exsc ].code != EXEC_IFTRUE )
      && ( CURTASK excs[ CURTASK exsc ].code != EXEC_IFFALSE ))
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "in bwb_endif(): END IF without IF" );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_syntax );
#endif
      }

   bwb_decexec();


#if MULTISEG_LINES
   adv_eos( l->buffer, &( l->position ));
#endif
   return bwb_zline( l );
   }

/***************************************************************

        FUNCTION:       find_endif()

        DESCRIPTION:    This C function attempts to find an
			END IF statement.

***************************************************************/

#if ANSI_C
static struct bwb_line *
find_endif( struct bwb_line *l, struct bwb_line **else_line )
#else
static struct bwb_line *
find_endif( l, else_line )
   struct bwb_line *l;
   struct bwb_line **else_line;
#endif
   {
   struct bwb_line *current;
   register int i_level;
   int position;

   *else_line = NULL;
   i_level = 1;
   for ( current = l->next; current != &CURTASK bwb_end; current = current->next )
      {
      position = 0;
      if ( current->marked != TRUE )
         {
         line_start( current->buffer, &position, &( current->lnpos ),
            &( current->lnum ),
            &( current->cmdpos ),
            &( current->cmdnum ),
            &( current->startpos ) );
         }
      current->position = current->startpos;

      if ( current->cmdnum > -1 )
         {

         if ( bwb_cmdtable[ current->cmdnum ].vector == bwb_if )
            {
            ++i_level;

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in find_endif(): found IF at line %d, level %d",
               current->number, i_level );
            bwb_debug( bwb_ebuf );
#endif

            }
         else if ( is_endif( current ) == TRUE )
            {
            --i_level;

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in find_endif(): found END IF at line %d, level %d",
               current->number, i_level );
            bwb_debug( bwb_ebuf );
#endif

            if ( i_level == 0 )
               {
               return current;
               }
            }

         else if ( ( bwb_cmdtable[ current->cmdnum ].vector == bwb_else )
            || ( bwb_cmdtable[ current->cmdnum ].vector == bwb_elseif ))
            {

            /* we must only report the first ELSE or ELSE IF we encounter
               at level 1 */

            if ( ( i_level == 1 ) && ( *else_line == NULL ))
               {
               *else_line = current;
               }

            }
         }
      }

#if PROG_ERRORS
   sprintf( bwb_ebuf, "Multiline IF without END IF" );
   bwb_error( bwb_ebuf );
#else
   bwb_error( err_syntax  );
#endif

   return NULL;

   }

/***************************************************************

	FUNCTION:       is_endif()

	DESCRIPTION:    This C function attempts to determine if
			a given line contains an END IF statement.

***************************************************************/

#if ANSI_C
static int
is_endif( struct bwb_line *l )
#else
static int
is_endif( l )
   struct bwb_line *l;
#endif
   {
   int position;
   char tbuf[ MAXVARNAMESIZE + 1];

   if ( bwb_cmdtable[ l->cmdnum ].vector != bwb_xend )
      {
      return FALSE;
      }

   position = l->startpos;
   adv_ws( l->buffer, &position );
   adv_element( l->buffer, &position, tbuf );
   bwb_strtoupper( tbuf );

   if ( strcmp( tbuf, "IF" ) == 0 )
      {
      return TRUE;
      }

   return FALSE;

   }

/***************************************************************

        FUNCTION:       bwb_select()

        DESCRIPTION:    This C function handles the BASIC SELECT
                        statement.

	SYNTAX:		SELECT CASE expression

***************************************************************/

#if ANSI_C
struct bwb_line *
bwb_select( struct bwb_line *l )
#else
struct bwb_line *
bwb_select( l )
   struct bwb_line *l;
#endif
   {
   char tbuf[ MAXSTRINGSIZE + 1 ];
   struct exp_ese *e;

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_select(): entered function" );
   bwb_debug( bwb_ebuf );
#endif

   /* first element should be "CASE" */

   adv_element( l->buffer, &( l->position ), tbuf );
   bwb_strtoupper( tbuf );
   if ( strcmp( tbuf, "CASE" ) != 0 )
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "SELECT without CASE" );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_syntax );

      return bwb_zline( l );
#endif
      }

   /* increment the level and set to EXEC_SELFALSE */

   bwb_incexec();
   CURTASK excs[ CURTASK exsc ].code = EXEC_SELFALSE;

   /* evaluate the expression at this level */

   e = bwb_exp( l->buffer, FALSE, &( l->position ) );

#if OLDWAY
   memcpy( &( CURTASK excs[ CURTASK exsc ].expression ), e,
      sizeof( struct exp_ese ) );
#endif

   if ( e->type == STRING )
      {
      CURTASK excs[ CURTASK exsc ].expression.type = STRING;
      str_btob( &( CURTASK excs[ CURTASK exsc ].expression.sval ),
	 &( e->sval ) );
      }
   else
      {
      CURTASK excs[ CURTASK exsc ].expression.type = NUMBER;
      CURTASK excs[ CURTASK exsc ].expression.nval
	 = exp_getnval( e );
      }

   /* return */

#if MULTISEG_LINES
   adv_eos( l->buffer, &( l->position ));
#endif

   return bwb_zline( l );
   }

/***************************************************************

        FUNCTION:       bwb_case()

        DESCRIPTION:    This C function handles the BASIC CASE
                        statement.

	SYNTAX:		CASE constant | IF partial-expression | ELSE

***************************************************************/

#if ANSI_C
struct bwb_line *
bwb_case( struct bwb_line *l )
#else
struct bwb_line *
bwb_case( l )
   struct bwb_line *l;
#endif
   {
   char tbuf[ MAXSTRINGSIZE + 1 ];
   int oldpos;
   struct exp_ese minvalue;
   struct exp_ese *maxval, *minval;
   struct bwb_line *retline;
   char cbuf1[ MAXSTRINGSIZE + 1 ];
   char cbuf2[ MAXSTRINGSIZE + 1 ];

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_case(): entered function" );
   bwb_debug( bwb_ebuf );
#endif

   /* if code is EXEC_SELTRUE, then we should jump to the end */

   if ( CURTASK excs[ CURTASK exsc ].code == EXEC_SELTRUE )
      {
#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in bwb_case(): exit EXEC_SELTRUE" );
      bwb_debug( bwb_ebuf );
#endif
      retline = find_endselect( l );
      retline->position = 0;
      return retline;
      }

   /* read first element */

   oldpos = l->position;
   adv_element( l->buffer, &( l->position ), tbuf );
   bwb_strtoupper( tbuf );

   /* check for CASE IF */

   if ( strcmp( tbuf, CMD_IF ) == 0 )
      {
      return bwb_caseif( l );
      }

   /* check for CASE ELSE: if true, simply proceed to the next line,
      because other options should have been detected by now */

   else if ( strcmp( tbuf, CMD_ELSE ) == 0 )
      {
#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in bwb_case(): execute CASE ELSE" );
      bwb_debug( bwb_ebuf );
#endif

      return bwb_zline( l );
      }

   /* neither CASE ELSE nor CASE IF; presume constant here for min value */

   l->position = oldpos;
   minval = bwb_exp( l->buffer, FALSE, &( l->position ));
   memcpy( &minvalue, minval, sizeof( struct exp_ese ) );
   maxval = minval = &minvalue;

   /* check for string value */

   if ( minvalue.type == STRING )
      {

      str_btoc( cbuf1, &( CURTASK excs[ CURTASK exsc ].expression.sval ) );
      str_btoc( cbuf2, &( minvalue.sval ) );

#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in bwb_case(): compare strings <%s> and <%s>",
	 cbuf1, cbuf2 );
      bwb_debug( bwb_ebuf );
#endif

      if ( strncmp( cbuf1, cbuf2, MAXSTRINGSIZE ) == 0 )
	 {
#if INTENSIVE_DEBUG
	 sprintf( bwb_ebuf, "in bwb_case(): string comparison returns TRUE" );
	 bwb_debug( bwb_ebuf );
#endif
	 CURTASK excs[ CURTASK exsc ].code = EXEC_SELTRUE;

#if MULTISEG_LINES
         adv_eos( l->buffer, &( l->position ));
#endif
	 return bwb_zline( l );
	 }

      else
	 {
#if INTENSIVE_DEBUG
	 sprintf( bwb_ebuf, "in bwb_case(): string comparison returns FALSE" );
	 bwb_debug( bwb_ebuf );
#endif
	 retline = find_case( l );
	 retline->position = 0;
	 return retline;
	 }

      }

   /* not a string; advance */

   adv_ws( l->buffer, &( l->position ));

   /* check for TO */

   if ( is_eol( l->buffer, &( l->position )) != TRUE )
      {

      /* find the TO statement */

      adv_element( l->buffer, &( l->position ), tbuf );
      bwb_strtoupper( tbuf );
      if ( strcmp( tbuf, CMD_TO ) != 0 )
         {
#if PROG_ERRORS
         sprintf( bwb_ebuf, "CASE has inexplicable code following expression" );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_syntax );

#if MULTISEG_LINES
         adv_eos( l->buffer, &( l->position ));
#endif
         return bwb_zline( l );
#endif
         }

      /* now evaluate the MAX expression */

      maxval = bwb_exp( l->buffer, FALSE, &( l->position ));

      }

   /* evaluate the expression */

   if ( case_eval( &( CURTASK excs[ CURTASK exsc ].expression ),
      minval, maxval ) == TRUE )
      {
#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in bwb_case(): evaluation returns TRUE" );
      bwb_debug( bwb_ebuf );
#endif
      CURTASK excs[ CURTASK exsc ].code = EXEC_SELTRUE;

#if MULTISEG_LINES
      adv_eos( l->buffer, &( l->position ));
#endif
      return bwb_zline( l );
      }

   /* evaluation returns a FALSE value; find next CASE or END SELECT statement */

   else
      {
#if INTENSIVE_DEBUGb
      sprintf( bwb_ebuf, "in bwb_case(): evaluation returns FALSE" );
      bwb_debug( bwb_ebuf );
#endif
      retline = find_case( l );
      retline->position = 0;
      return retline;
      }

   }

/***************************************************************

        FUNCTION:       bwb_caseif()

        DESCRIPTION:    This C function handles the BASIC CASE IF
                        statement.

***************************************************************/

#if ANSI_C
static struct bwb_line *
bwb_caseif( struct bwb_line *l )
#else
static struct bwb_line *
bwb_caseif( l )
   struct bwb_line *l;
#endif
   {
   char tbuf[ MAXSTRINGSIZE + 1 ];
   int position;
   struct exp_ese *r;
   struct bwb_line *retline;

   if ( CURTASK excs[ CURTASK exsc ].expression.type == NUMBER )
      {
      sprintf( tbuf, "%f %s",
         (float) CURTASK excs[ CURTASK exsc ].expression.nval,
         &( l->buffer[ l->position ] ) );
      }
   else
      {
      bwb_error( err_mismatch );
#if MULTISEG_LINES
      adv_eos( l->buffer, &( l->position ));
#endif
      return bwb_zline( l );
      }

   position = 0;
   r = bwb_exp( tbuf, FALSE, &position );

   if ( r->nval == (bnumber) TRUE )
      {
      CURTASK excs[ CURTASK exsc ].code = EXEC_SELTRUE;

#if MULTISEG_LINES
      adv_eos( l->buffer, &( l->position ));
#endif
      return bwb_zline( l );
      }
   else
      {
      retline = find_case( l );
      retline->position = 0;
      return retline;
      }

   }

/***************************************************************

        FUNCTION:       case_eval()

        DESCRIPTION:    This function evaluates a case statement
			by comparing minimum and maximum values
			with a set expression. It returns either
			TRUE or FALSE

***************************************************************/

#if ANSI_C
static int
case_eval( struct exp_ese *expression, struct exp_ese *minval,
   struct exp_ese *maxval )
#else
static int
case_eval( expression, minval, maxval )
   struct exp_ese *expression;
   struct exp_ese *minval;
   struct exp_ese *maxval;
#endif
   {

   /* string value */

   if ( expression->type == STRING )
      {
      bwb_error( err_mismatch );
      return FALSE;
      }

   /* numerical value */

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in case_eval(): n <%f> min <%f> max <%f>",
      (float) expression->nval,
      (float) minval->nval,
      (float) maxval->nval );
   bwb_debug( bwb_ebuf );
#endif

   if (  ( expression->nval >= minval->nval )
      && ( expression->nval <= maxval->nval ))
      {
      return TRUE;
      }

   return FALSE;

   }

/***************************************************************

        FUNCTION:       find_case()

        DESCRIPTION:    This function searches for a line containing
                        a CASE statement corresponding to a previous
                        SELECT CASE statement.

***************************************************************/

#if ANSI_C
static struct bwb_line *
find_case( struct bwb_line *l )
#else
static struct bwb_line *
find_case( l )
   struct bwb_line *l;
#endif
   {
   struct bwb_line *current;
   register int c_level;
   int position;

   c_level = 1;
   for ( current = l->next; current != &CURTASK bwb_end; current = current->next )
      {
      position = 0;
      if ( current->marked != TRUE )
         {
         line_start( current->buffer, &position, &( current->lnpos ),
            &( current->lnum ),
            &( current->cmdpos ),
            &( current->cmdnum ),
            &( current->startpos ) );
         }
      current->position = current->startpos;

      if ( current->cmdnum > -1 )
         {

         if ( bwb_cmdtable[ current->cmdnum ].vector == bwb_select )
            {
            ++c_level;

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in find_case(): found SELECT at line %d, level %d",
               current->number, c_level );
            bwb_debug( bwb_ebuf );
#endif

            }
         else if ( is_endselect( current ) == TRUE )
            {
            --c_level;

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in find_endif(): found END SELECT at line %d, level %d",
               current->number, c_level );
            bwb_debug( bwb_ebuf );
#endif

            if ( c_level == 0 )
               {
               return current;
               }
            }

         else if ( bwb_cmdtable[ current->cmdnum ].vector == bwb_case )
            {
            --c_level;

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in find_case(): found CASE at line %d, level %d",
               current->number, c_level );
            bwb_debug( bwb_ebuf );
#endif

            if ( c_level == 0 )
               {
               return current;
               }
            }
         }
      }

#if PROG_ERRORS
   sprintf( bwb_ebuf, "SELECT without CASE" );
   bwb_error( bwb_ebuf );
#else
   bwb_error( err_syntax  );
#endif

   return NULL;

   }

/***************************************************************

        FUNCTION:       find_case()

        DESCRIPTION:    This function searches for a line containing
                        an END SELECT statement corresponding to a previous
                        SELECT CASE statement.

***************************************************************/

#if ANSI_C
static struct bwb_line *
find_endselect( struct bwb_line *l )
#else
static struct bwb_line *
find_endselect( l )
   struct bwb_line *l;
#endif
   {
   struct bwb_line *current;
   register int c_level;
   int position;

   c_level = 1;
   for ( current = l->next; current != &CURTASK bwb_end; current = current->next )
      {
      position = 0;
      if ( current->marked != TRUE )
         {
         line_start( current->buffer, &position, &( current->lnpos ),
            &( current->lnum ),
            &( current->cmdpos ),
            &( current->cmdnum ),
            &( current->startpos ) );
         }
      current->position = current->startpos;

      if ( current->cmdnum > -1 )
         {

         if ( bwb_cmdtable[ current->cmdnum ].vector == bwb_select )
            {
            ++c_level;

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in find_case(): found SELECT at line %d, level %d",
               current->number, c_level );
            bwb_debug( bwb_ebuf );
#endif

            }
         else if ( is_endselect( current ) == TRUE )
            {
            --c_level;

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in find_endif(): found END SELECT at line %d, level %d",
               current->number, c_level );
            bwb_debug( bwb_ebuf );
#endif

            if ( c_level == 0 )
               {
               return current;
               }
            }
         }
      }

#if PROG_ERRORS
   sprintf( bwb_ebuf, "SELECT without END SELECT" );
   bwb_error( bwb_ebuf );
#else
   bwb_error( err_syntax  );
#endif

   return NULL;

   }

/***************************************************************

	FUNCTION:       is_endselect()

	DESCRIPTION:    This C function attempts to determine if
			a given line contains an END SELECT statement.

***************************************************************/

#if ANSI_C
static int
is_endselect( struct bwb_line *l )
#else
static int
is_endselect( l )
   struct bwb_line *l;
#endif
   {
   int position;
   char tbuf[ MAXVARNAMESIZE + 1];

   if ( bwb_cmdtable[ l->cmdnum ].vector != bwb_xend )
      {
      return FALSE;
      }

   position = l->startpos;
   adv_ws( l->buffer, &position );
   adv_element( l->buffer, &position, tbuf );
   bwb_strtoupper( tbuf );

   if ( strcmp( tbuf, "SELECT" ) == 0 )
      {
      return TRUE;
      }

   return FALSE;

   }

/***************************************************************

        FUNCTION:       bwb_endselect()

        DESCRIPTION:    This function handles the BASIC END
                        SELECT statement.

	SYNTAX:		END SELECT

***************************************************************/

#if ANSI_C
struct bwb_line *
bwb_endselect( struct bwb_line *l )
#else
struct bwb_line *
bwb_endselect( l )
   struct bwb_line *l;
#endif
   {

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_endselect(): entered function" );
   bwb_debug( bwb_ebuf );
#endif

   if (  ( CURTASK excs[ CURTASK exsc ].code != EXEC_SELTRUE  )
      && ( CURTASK excs[ CURTASK exsc ].code != EXEC_SELFALSE ))
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "in bwb_endselect(): END SELECT without SELECT" );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_syntax );
#endif
      }

   bwb_decexec();


#if MULTISEG_LINES
   adv_eos( l->buffer, &( l->position ));
#endif
   return bwb_zline( l );
   }

#endif				/* STRUCT_CMDS */

#if COMMON_CMDS || STRUCT_CMDS

/***    WHILE-WEND ***/

/***************************************************************

        FUNCTION:       bwb_while()

        DESCRIPTION:    This function handles the BASIC WHILE
			statement and also the ANSI DO WHILE
			statement.

	SYNTAX:		WHILE expression
			DO WHILE expression

***************************************************************/

#if ANSI_C
struct bwb_line *
bwb_while( struct bwb_line *l )
#else
struct bwb_line *
bwb_while( l )
   struct bwb_line *l;
#endif
   {
   struct exp_ese *e;
   struct bwb_line *r;

   /* call bwb_exp() to interpret the expression */

   e = bwb_exp( l->buffer, FALSE, &( l->position ) );

   if ( (int) exp_getnval( e ) == TRUE )
      {

      /* if this is the first time at this WHILE statement, note it */

      if ( CURTASK excs[ CURTASK exsc ].while_line != l )
         {

         bwb_incexec();
         CURTASK excs[ CURTASK exsc ].while_line = l;

         /* find the WEND statement (or LOOP statement) */

#if STRUCT_CMDS
	 if ( l->cmdnum == getcmdnum( CMD_DO ))
            {
	    CURTASK excs[ CURTASK exsc ].wend_line = find_loop( l );
            }
         else
            {
	    CURTASK excs[ CURTASK exsc ].wend_line = find_wend( l );
	    }
#else
	 CURTASK excs[ CURTASK exsc ].wend_line = find_wend( l );
#endif

         if ( CURTASK excs[ CURTASK exsc ].wend_line == NULL )
            {
            return bwb_zline( l );
            }

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in bwb_while(): initialize WHILE loop, line <%d>",
            l->number );
         bwb_debug( bwb_ebuf );
#endif

         }
#if INTENSIVE_DEBUG
      else
         {
         sprintf( bwb_ebuf, "in bwb_while(): return to WHILE loop, line <%d>",
            l->number );
         bwb_debug( bwb_ebuf );
         }
#endif

      bwb_setexec( l, l->position, EXEC_WHILE );
      return bwb_zline( l );
      }
   else
      {
      CURTASK excs[ CURTASK exsc ].while_line = NULL;
      r = CURTASK excs[ CURTASK exsc ].wend_line;
      bwb_setexec( r, 0, CURTASK excs[ CURTASK exsc - 1 ].code );
      r->position = 0;
      bwb_decexec();
      return r;
      }

   }

/***************************************************************

        FUNCTION:       bwb_wend()

        DESCRIPTION:    This function handles the BASIC WEND
			statement and the LOOP statement ending
			a DO WHILE loop.

	SYNTAX:		WEND
			LOOP

***************************************************************/

#if ANSI_C
struct bwb_line *
bwb_wend( struct bwb_line *l )
#else
struct bwb_line *
bwb_wend( l )
   struct bwb_line *l;
#endif
   {

   /* check integrity of WHILE loop */

   if ( CURTASK excs[ CURTASK exsc ].code != EXEC_WHILE )
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "in bwb_wend(): exec stack code != EXEC_WHILE" );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_syntax );
#endif
      }

   if ( CURTASK excs[ CURTASK exsc ].while_line == NULL )
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "in bwb_wend(): exec stack while_line == NULL" );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_syntax );
#endif
      }

   /* reset to the top of the current WHILE loop */

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_wend() return to line <%d>",
      CURTASK excs[ CURTASK exsc ].while_line->number );
   bwb_debug( bwb_ebuf );
#endif

   CURTASK excs[ CURTASK exsc ].while_line->position = 0;
   bwb_setexec( CURTASK excs[ CURTASK exsc ].while_line, 0, EXEC_WHILE );

   return CURTASK excs[ CURTASK exsc ].while_line;

   }

/***************************************************************

        FUNCTION:       find_wend()

        DESCRIPTION:    This function searches for a line containing
                        a WEND statement corresponding to a previous
                        WHILE statement.

***************************************************************/

#if ANSI_C
static struct bwb_line *
find_wend( struct bwb_line *l )
#else
static struct bwb_line *
find_wend( l )
   struct bwb_line *l;
#endif
   {
   struct bwb_line *current;
   register int w_level;
   int position;

   w_level = 1;
   for ( current = l->next; current != &CURTASK bwb_end; current = current->next )
      {
      position = 0;
      if ( current->marked != TRUE )
         {
         line_start( current->buffer, &position, &( current->lnpos ),
            &( current->lnum ),
            &( current->cmdpos ),
            &( current->cmdnum ),
            &( current->startpos ) );
         }
      current->position = current->startpos;

      if ( current->cmdnum > -1 )
         {

         if ( bwb_cmdtable[ current->cmdnum ].vector == bwb_while )
            {
            ++w_level;

#if INTENSIVE_DEBUG
	    sprintf( bwb_ebuf, "in find_wend(): found WHILE at line %d, level %d",
               current->number, w_level );
            bwb_debug( bwb_ebuf );
#endif

            }
         else if ( bwb_cmdtable[ current->cmdnum ].vector == bwb_wend )
            {
            --w_level;

#if INTENSIVE_DEBUG
	    sprintf( bwb_ebuf, "in find_wend(): found WEND at line %d, level %d",
               current->number, w_level );
            bwb_debug( bwb_ebuf );
#endif

            if ( w_level == 0 )
               {
               return current->next;
               }
            }
         }
      }

#if PROG_ERRORS
   sprintf( bwb_ebuf, "in find_wend(): WHILE without WEND" );
   bwb_error( bwb_ebuf );
#else
   bwb_error( err_syntax  );
#endif

   return NULL;

   }

#if STRUCT_CMDS

/***************************************************************

	FUNCTION:       find_loop()

	DESCRIPTION:    This function searches for a line containing
			a LOOP statement corresponding to a previous
			DO statement.

***************************************************************/

#if ANSI_C
extern struct bwb_line *
find_loop( struct bwb_line *l )
#else
extern struct bwb_line *
find_loop( l )
   struct bwb_line *l;
#endif
   {
   struct bwb_line *current;
   register int w_level;
   int position;

   w_level = 1;
   for ( current = l->next; current != &CURTASK bwb_end; current = current->next )
      {
      position = 0;
      if ( current->marked != TRUE )
	 {
	 line_start( current->buffer, &position, &( current->lnpos ),
	    &( current->lnum ),
	    &( current->cmdpos ),
	    &( current->cmdnum ),
	    &( current->startpos ) );
	 }
      current->position = current->startpos;

      if ( current->cmdnum > -1 )
	 {

	 if ( bwb_cmdtable[ current->cmdnum ].vector == bwb_do )
	    {
	    ++w_level;

#if INTENSIVE_DEBUG
	    sprintf( bwb_ebuf, "in find_loop(): found DO at line %d, level %d",
	       current->number, w_level );
	    bwb_debug( bwb_ebuf );
#endif

	    }
	 else if ( bwb_cmdtable[ current->cmdnum ].vector == bwb_loop )
	    {
	    --w_level;

#if INTENSIVE_DEBUG
	    sprintf( bwb_ebuf, "in fnd_loop(): found LOOP at line %d, level %d",
	       current->number, w_level );
	    bwb_debug( bwb_ebuf );
#endif

	    if ( w_level == 0 )
	       {
	       return current->next;
	       }
	    }
	 }
      }

#if PROG_ERRORS
   sprintf( bwb_ebuf, "in find_loop(): DO without LOOP" );
   bwb_error( bwb_ebuf );
#else
   bwb_error( err_syntax  );
#endif

   return NULL;

   }

#endif                          /* STRUCT_CMDS */

#endif                          /* COMMON_CMDS || STRUCT_CMDS */

/***    FOR-NEXT ***/

/***************************************************************

        FUNCTION:       bwb_for()

        DESCRIPTION:    This function handles the BASIC FOR
                        statement.

	SYNTAX:		FOR counter = start TO finish [STEP increment]

***************************************************************/

#if ANSI_C
struct bwb_line *
bwb_for( struct bwb_line *l )
#else
struct bwb_line *
bwb_for( l )
   struct bwb_line *l;
#endif
   {
   register int n;
   int e, loop;
   int to, step, p;
   int for_step, for_target;
   struct exp_ese *exp;
   struct bwb_variable *v;
   char tbuf[ MAXSTRINGSIZE + 1 ];

   /* get the variable name */

   exp_getvfname( &( l->buffer[ l->position ] ), tbuf );
   l->position += strlen( tbuf );
   v = var_find( tbuf );

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_for(): variable name <%s>.", v->name );
   bwb_debug( bwb_ebuf );
#endif

   /*--------------------------------------------------------------*/
   /* Make sure we are in the right FOR-NEXT level!                */
   /* If we aren't (which could happen for legit reasons), fix the */
   /* exec stack.                                                  */
   /* JBV, 9/20/95                                                 */
   /*--------------------------------------------------------------*/
   if (v == CURTASK excs[ CURTASK exsc].local_variable) bwb_decexec();

   /* at this point one should find an equals sign ('=') */

   adv_ws( l->buffer, &( l->position ) );

   if ( l->buffer[ l->position ] != '=' )
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "in bwb_for(): failed to find equals sign, buf <%s>",
         &( l->buffer[ l->position ] ) );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_syntax );
#endif
      return bwb_zline( l );
      }
   else
      {
      ++( l->position );
      }

   /* Find the TO and STEP statements */

   cnd_tostep( l->buffer, l->position, &to, &step );

   /* if there is no TO statement, then an error has ocurred */

   if ( to < 1 )
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "FOR statement without TO" );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_syntax  );
#endif
      return bwb_zline( l );
      }

   /* copy initial value to buffer and evaluate it */

   tbuf[ 0 ] = '\0';
   p = 0;
   for ( n = l->position; n < to; ++n )
      {
      tbuf[ p ] = l->buffer[ n ];
      ++p;
      ++l->position;
      tbuf[ p ] = '\0';
      }

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_for(): initial value string <%s>",
      tbuf );
   bwb_debug( bwb_ebuf );
#endif

   p = 0;
   exp = bwb_exp( tbuf, FALSE, &p );
   var_setnval( v, exp_getnval( exp ) );

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_for(): initial value <%d> pos <%d>",
      exp_getnval( exp ), l->position );
   bwb_debug( bwb_ebuf );
#endif

   /* copy target value to small buffer and evaluate it */

   tbuf[ 0 ] = '\0';
   p = 0;
   l->position = to + 2;
   if ( step < 1 )
      {
      e = strlen( l->buffer );
      }
   else
      {
      e = step - 1;
      }

   loop = TRUE;
   n = l->position;
   while( loop == TRUE )
      {
      tbuf[ p ] = l->buffer[ n ];
      ++p;
      ++l->position;
      tbuf[ p ] = '\0';

      if ( n >= e )
          {
          loop = FALSE;
          }

      ++n;

      if ( l->buffer[ n ] == ':' )
         {
         loop = FALSE;
         }

      }

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_for(): target value string <%s>",
      tbuf );
   bwb_debug( bwb_ebuf );
#endif

   p = 0;
   exp = bwb_exp( tbuf, FALSE, &p );
   for_target = (int) exp_getnval( exp );

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_for(): target value <%d> pos <%d>",
      exp_getnval( exp ), l->position );
   bwb_debug( bwb_ebuf );
#endif

   /* If there is a STEP statement, copy it to a buffer
      and evaluate it */

   if ( step > 1 )
      {
      tbuf[ 0 ] = '\0';
      p = 0;
      l->position = step + 4;

      for ( n = l->position; n < (int) strlen( l->buffer ); ++n )
         {
         tbuf[ p ] = l->buffer[ n ];
         ++p;
         ++l->position;
         tbuf[ p ] = '\0';
         }

#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in bwb_for(): step value string <%s>",
         tbuf );
      bwb_debug( bwb_ebuf );
#endif

      p = 0;
      exp = bwb_exp( tbuf, FALSE, &p );
      for_step = (int) exp_getnval( exp );

      }
   else
      {
      for_step = 1;
      }


#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_for(): step value <%d>",
      for_step );
   bwb_debug( bwb_ebuf );
#endif

   /* set position in current line and increment EXEC counter */

   /* bwb_setexec( l, l->position, EXEC_NORM ); */  /* WRONG */
   bwb_incexec();

   CURTASK excs[ CURTASK exsc ].local_variable  = v;
   CURTASK excs[ CURTASK exsc ].for_step   = for_step;
   CURTASK excs[ CURTASK exsc ].for_target = for_target;

   /* set exit line to be used by EXIT FOR */

#if STRUCT_CMDS
   CURTASK excs[ CURTASK exsc ].wend_line  = find_next( l );
#endif

   /* set top line and position to be used in multisegmented FOR-NEXT loop */

#if MULTISEG_LINES
   CURTASK excs[ CURTASK exsc ].for_line     = l;
   CURTASK excs[ CURTASK exsc ].for_position = l->position;
#endif

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_for(): setting code to EXEC_FOR",
      l->position );
   bwb_debug( bwb_ebuf );
#endif

   bwb_setexec( l, l->position, EXEC_FOR );

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_for(): ready to exit, position <%d>",
      l->position );
   bwb_debug( bwb_ebuf );
#endif

   /* proceed with processing */

   return bwb_zline( l );

   }

/***************************************************************

        FUNCTION:       bwb_next()

        DESCRIPTION:    This function handles the BASIC NEXT
                        statement.

	SYNTAX:		NEXT counter

***************************************************************/

#if ANSI_C
struct bwb_line *
bwb_next( struct bwb_line *l )
#else
struct bwb_line *
bwb_next( l )
   struct bwb_line *l;
#endif
   {
   char tbuf[ MAXSTRINGSIZE + 1 ];
   struct bwb_variable *v; /* Relocated from INTENSIVE_DEBUG (JBV) */

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_next(): entered function, cmdnum <%d> exsc level <%d> code <%d>",
      l->cmdnum, CURTASK exsc, CURTASK excs[ CURTASK exsc ].code );
   bwb_debug( bwb_ebuf );
#endif

   /* Check the integrity of the FOR statement */

   if ( CURTASK excs[ CURTASK exsc ].code != EXEC_FOR )
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "in bwb_next(): NEXT without FOR; code is <%d> instead of <%d>",
	 CURTASK excs[ CURTASK exsc ].code, EXEC_FOR );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_syntax );
#endif
      }

   /* read the argument, if there is one */

   /* Relocated from MULTISEG_LINES (JBV) */
   exp_getvfname( &( l->buffer[ l->position ] ), tbuf );

   if (strlen(tbuf) != 0)
   {
   /* Relocated from INTENSIVE_DEBUG (JBV) */
   v = var_find( tbuf );

#if MULTISEG_LINES                   /* not currently needed otherwise */

   l->position += strlen( tbuf );

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_next(): variable name detected <%s>.", v->name );
   bwb_debug( bwb_ebuf );
#endif
#endif

   /* decrement or increment the value */

   /*--------------------------------------------------------------*/
   /* Make sure we are in the right FOR-NEXT level!                */
   /* If we aren't (which could happen for legit reasons), fix the */
   /* exec stack.                                                  */
   /* JBV, 9/20/95                                                 */
   /*--------------------------------------------------------------*/
   while (v != CURTASK excs[ CURTASK exsc].local_variable) bwb_decexec();
   }

   var_setnval( CURTASK excs[ CURTASK exsc ].local_variable,
      var_getnval( CURTASK excs[ CURTASK exsc ].local_variable )
      + (bnumber) CURTASK excs[ CURTASK exsc ].for_step );

   /* check for completion of the loop */

   if ( CURTASK excs[ CURTASK exsc ].for_step > 0 )            /* if step is positive */
      {
      if ( (int) var_getnval( CURTASK excs[ CURTASK exsc ].local_variable )
         > CURTASK excs[ CURTASK exsc ].for_target )
         {
         bwb_decexec();
#if MULTISEG_LINES
	 bwb_setexec( l, l->position, CURTASK excs[ CURTASK exsc ].code );
#else
	 bwb_setexec( l->next, 0, CURTASK excs[ CURTASK exsc ].code );
#endif

#if INTENSIVE_DEBUG
	 sprintf( bwb_ebuf, "in bwb_next(): end of loop" );
	 bwb_debug( bwb_ebuf );
#endif

#ifdef OLD_WAY
	 l->next->position = 0;
	 return l->next;
#else
	 return bwb_zline( l );
#endif
         }
      }
   else                                         /* if step is negative */
      {
      if ( (int) var_getnval( CURTASK excs[ CURTASK exsc ].local_variable )
         < CURTASK excs[ CURTASK exsc ].for_target )
         {
         bwb_decexec();
	 bwb_setexec( l->next, 0, CURTASK excs[ CURTASK exsc ].code );

#if INTENSIVE_DEBUG
	 sprintf( bwb_ebuf, "in bwb_next(): end of loop" );
	 bwb_debug( bwb_ebuf );
#endif

#ifdef OLD_WAY
	 l->next->position = 0;
	 return l->next;
#else
	 return bwb_zline( l );
#endif
         }
      }

   /* Target not reached: return to the top of the FOR loop */

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_next(): resetting code to EXEC_FOR",
      l->position );
   bwb_debug( bwb_ebuf );
#endif

#if MULTISEG_LINES
   CURTASK excs[ CURTASK exsc ].for_line->position
      = CURTASK excs[ CURTASK exsc ].for_position;
   bwb_setexec( CURTASK excs[ CURTASK exsc ].for_line,
      CURTASK excs[ CURTASK exsc ].for_position, EXEC_FOR );

   return CURTASK excs[ CURTASK exsc ].for_line; /* Added (JBV) */
#else
   bwb_setexec( CURTASK excs[ CURTASK exsc - 1 ].line,
      CURTASK excs[ CURTASK exsc - 1 ].position, EXEC_FOR );

   return CURTASK excs[ CURTASK exsc - 1 ].line; /* Relocated (JBV) */
#endif

   }

#if STRUCT_CMDS

/***************************************************************

	FUNCTION:       bwb_exitfor()

	DESCRIPTION:    This function handles the BASIC EXIT
			FOR statement.  This is a structured
			programming command compatible with ANSI
			BASIC. It is called from the bwb_exit()
			subroutine.

	SYNTAX:		EXIT FOR
			
***************************************************************/

#if ANSI_C
struct bwb_line *
bwb_exitfor( struct bwb_line *l )
#else
struct bwb_line *
bwb_exitfor( l )
   struct bwb_line *l;
#endif
   {
   struct bwb_line *next_line;
   int found;
   register int level;

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_exitfor(): entered subroutine" );
   bwb_debug( bwb_ebuf );
#endif

   /* Check the integrity of the FOR statement */

   found = FALSE;
   level = CURTASK exsc;
   do
      {
      if ( CURTASK excs[ level ].code == EXEC_FOR )
	 {
	 next_line = CURTASK excs[ CURTASK level ].wend_line;
	 found = TRUE;
	 }
      else
	 {
	 --level;
	 }
      }
   while ( ( level >= 0 ) && ( found == FALSE ) );

   if ( found != TRUE )
      {

#if PROG_ERRORS
      sprintf( bwb_ebuf, "in bwb_exitfor(): EXIT FOR without FOR" );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_syntax );
#endif

      return bwb_zline( l );

      }

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_exitfor(): level found is <%d>, current <%d>",
      level, CURTASK exsc );
   bwb_debug( bwb_ebuf );
#endif

   /* decrement below the level of the NEXT statement */

   while( CURTASK exsc >= level )
      {
      bwb_decexec();
      }

   /* set the next line in the exec stack */

   next_line->position = 0;
   /* bwb_setexec( next_line, 0, EXEC_NORM ); */  /* WRONG (JBV) */
   bwb_setexec( next_line, 0, CURTASK excs[ CURTASK exsc ].code ); /* JBV */

   return next_line;

   }

/***************************************************************

	FUNCTION:       find_next()

	DESCRIPTION:    This function searches for a line containing
			a NEXT statement corresponding to a previous
			FOR statement.

***************************************************************/

#if ANSI_C
static struct bwb_line *
find_next( struct bwb_line *l )
#else
static struct bwb_line *
find_next( l )
   struct bwb_line *l;
#endif
   {
   struct bwb_line *current;
   register int w_level;
   int position;

   w_level = 1;
   for ( current = l->next; current != &CURTASK bwb_end; current = current->next )
      {
      position = 0;
      if ( current->marked != TRUE )
	 {
	 line_start( current->buffer, &position, &( current->lnpos ),
	    &( current->lnum ),
	    &( current->cmdpos ),
	    &( current->cmdnum ),
	    &( current->startpos ) );
	 }
      current->position = current->startpos;

      if ( current->cmdnum > -1 )
	 {

	 if ( bwb_cmdtable[ current->cmdnum ].vector == bwb_for )
	    {
	    ++w_level;

#if INTENSIVE_DEBUG
	    sprintf( bwb_ebuf, "in find_next(): found FOR at line %d, level %d",
	       current->number, w_level );
	    bwb_debug( bwb_ebuf );
#endif

	    }
	 else if ( bwb_cmdtable[ current->cmdnum ].vector == bwb_next )
	    {
	    --w_level;

#if INTENSIVE_DEBUG
	    sprintf( bwb_ebuf, "in find_next(): found NEXT at line %d, level %d",
	       current->number, w_level );
	    bwb_debug( bwb_ebuf );
#endif

	    if ( w_level == 0 )
	       {

#if INTENSIVE_DEBUG
	       sprintf( bwb_ebuf, "in find_next(): found returning line <%d>",
		  current->next->number );
	       bwb_debug( bwb_ebuf );
#endif

	       return current->next;
	       }
	    }
	 }
      }

#if PROG_ERRORS
   sprintf( bwb_ebuf, "FOR without NEXT" );
   bwb_error( bwb_ebuf );
#else
   bwb_error( err_syntax  );
#endif

   return NULL;

   }

#endif				/* STRUCT_CMDS for EXIT FOR */

/***************************************************************

	FUNCTION:	cnd_tostep()

	DESCRIPTION:	This function searches through the
			<buffer> beginning at point <position>
			and attempts to find positions of TO
			and STEP statements.

***************************************************************/

#if ANSI_C
static int
cnd_tostep( char *buffer, int position, int *to, int *step )
#else
static int
cnd_tostep( buffer, position, to, step )
   char *buffer;
   int position;
   int *to;
   int *step;
#endif
   {
   int loop, t_pos, b_pos, p_word;
   char tbuf[ MAXSTRINGSIZE + 1 ];

   /* set then and els to FALSE initially */

   *to = *step = FALSE;

   /* loop to find words */

   p_word = b_pos = position;
   t_pos = 0;
   tbuf[ 0 ] = '\0';
   loop = TRUE;
   while ( loop == TRUE )
      {

      switch( buffer[ b_pos ] )
         {
         case '\0':                     /* end of string */
         case ':':			/* end of line segment */
            return TRUE;
         case ' ':                      /* whitespace = end of word */
         case '\t':

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in cnd_tostep(): word is <%s>", tbuf );
            bwb_debug( bwb_ebuf );
#endif

            if ( strncmp( tbuf, CMD_TO, (size_t) strlen( CMD_TO ) ) == 0 )
               {

#if INTENSIVE_DEBUG
               sprintf( bwb_ebuf, "in cnd_tostep(): TO found at position <%d>.",
                  p_word );
               bwb_debug( bwb_ebuf );
#endif

               *to = p_word;
               }
            else if ( strncmp( tbuf, CMD_STEP, (size_t) strlen( CMD_STEP ) ) == 0 )
               {

#if INTENSIVE_DEBUG
               sprintf( bwb_ebuf, "in cnd_tostep(): STEP found at position <%d>.",
                  p_word );
               bwb_debug( bwb_ebuf );
#endif

               *step = p_word;
               }
            ++b_pos;
            p_word = b_pos;
            t_pos = 0;
            tbuf[ 0 ] = '\0';
            break;

         default:
            if ( islower( buffer[ b_pos ] ) != FALSE )
               {
               tbuf[ t_pos ] = (char) toupper( buffer[ b_pos ] );
               }
            else
               {
               tbuf[ t_pos ] = buffer[ b_pos ];
               }
            ++b_pos;
            ++t_pos;
            tbuf[ t_pos ] = '\0';
            break;
         }

      }

   return TRUE;

   }

/***************************************************************

	FUNCTION:       var_setnval()

	DESCRIPTION:    This function sets the value of numerical
			variable v to the value of i.

***************************************************************/

#if ANSI_C
extern int
var_setnval( struct bwb_variable *v, bnumber i )
#else
int
var_setnval( v, i )
   struct bwb_variable *v;
   bnumber i;
#endif
   {

   switch( v->type )
      {
      case NUMBER:
         * var_findnval( v, v->array_pos ) = i;
         break;
      default:
#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in var_setnval(): variable <%s> is not a number",
            v->name );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_mismatch );
#endif
      }

   /* successful assignment */

   return TRUE;

   }

